blob: 013c1810ce72938c3bbe18a57f22664d1732f27b [file] [log] [blame]
Andi Kleena32073b2006-06-26 13:56:40 +02001/*
2 * Shared support code for AMD K8 northbridges and derivates.
3 * Copyright 2006 Andi Kleen, SUSE Labs. Subject to GPLv2.
4 */
Andi Kleena32073b2006-06-26 13:56:40 +02005#include <linux/types.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09006#include <linux/slab.h>
Andi Kleena32073b2006-06-26 13:56:40 +02007#include <linux/init.h>
8#include <linux/errno.h>
9#include <linux/module.h>
10#include <linux/spinlock.h>
Andreas Herrmann23ac4ae2010-09-17 18:03:43 +020011#include <asm/amd_nb.h>
Andi Kleena32073b2006-06-26 13:56:40 +020012
Andi Kleena32073b2006-06-26 13:56:40 +020013static u32 *flush_words;
14
Jan Beulich691269f2011-02-09 08:26:53 +000015const struct pci_device_id amd_nb_misc_ids[] = {
Joerg Roedelcf169702008-09-02 13:13:40 +020016 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
17 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
Borislav Petkovcb293252011-01-19 18:22:11 +010018 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
Andi Kleena32073b2006-06-26 13:56:40 +020019 {}
20};
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020021EXPORT_SYMBOL(amd_nb_misc_ids);
Andi Kleena32073b2006-06-26 13:56:40 +020022
Hans Rosenfeld41b26102011-01-24 16:05:42 +010023static struct pci_device_id amd_nb_link_ids[] = {
Borislav Petkovcb6c8522011-03-30 20:34:47 +020024 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
Hans Rosenfeld41b26102011-01-24 16:05:42 +010025 {}
26};
27
Jan Beulich24d9b702011-01-10 16:20:23 +000028const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = {
29 { 0x00, 0x18, 0x20 },
30 { 0xff, 0x00, 0x20 },
31 { 0xfe, 0x00, 0x20 },
32 { }
33};
34
Hans Rosenfeldeec1d4f2010-10-29 17:14:30 +020035struct amd_northbridge_info amd_northbridges;
36EXPORT_SYMBOL(amd_northbridges);
Andi Kleena32073b2006-06-26 13:56:40 +020037
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020038static struct pci_dev *next_northbridge(struct pci_dev *dev,
Jan Beulich691269f2011-02-09 08:26:53 +000039 const struct pci_device_id *ids)
Andi Kleena32073b2006-06-26 13:56:40 +020040{
41 do {
42 dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
43 if (!dev)
44 break;
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020045 } while (!pci_match_id(ids, dev));
Andi Kleena32073b2006-06-26 13:56:40 +020046 return dev;
47}
48
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020049int amd_cache_northbridges(void)
Andi Kleena32073b2006-06-26 13:56:40 +020050{
Borislav Petkov84fd1d32011-03-03 12:59:32 +010051 u16 i = 0;
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020052 struct amd_northbridge *nb;
Hans Rosenfeld41b26102011-01-24 16:05:42 +010053 struct pci_dev *misc, *link;
Ben Collins3c6df2a2007-05-23 13:57:43 -070054
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020055 if (amd_nb_num())
Andi Kleena32073b2006-06-26 13:56:40 +020056 return 0;
57
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020058 misc = NULL;
59 while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
60 i++;
61
62 if (i == 0)
63 return 0;
64
65 nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
66 if (!nb)
67 return -ENOMEM;
68
69 amd_northbridges.nb = nb;
70 amd_northbridges.num = i;
71
Hans Rosenfeld41b26102011-01-24 16:05:42 +010072 link = misc = NULL;
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020073 for (i = 0; i != amd_nb_num(); i++) {
74 node_to_amd_nb(i)->misc = misc =
75 next_northbridge(misc, amd_nb_misc_ids);
Hans Rosenfeld41b26102011-01-24 16:05:42 +010076 node_to_amd_nb(i)->link = link =
77 next_northbridge(link, amd_nb_link_ids);
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020078 }
Andi Kleena32073b2006-06-26 13:56:40 +020079
Andreas Herrmann900f9ac2010-09-17 18:02:54 +020080 /* some CPU families (e.g. family 0x11) do not support GART */
Andreas Herrmann5c80cc72010-09-30 14:43:16 +020081 if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
82 boot_cpu_data.x86 == 0x15)
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +020083 amd_northbridges.flags |= AMD_NB_GART;
Andreas Herrmann900f9ac2010-09-17 18:02:54 +020084
Hans Rosenfeldf658bcf2010-10-29 17:14:32 +020085 /*
86 * Some CPU families support L3 Cache Index Disable. There are some
87 * limitations because of E382 and E388 on family 0x10.
88 */
89 if (boot_cpu_data.x86 == 0x10 &&
90 boot_cpu_data.x86_model >= 0x8 &&
91 (boot_cpu_data.x86_model > 0x9 ||
92 boot_cpu_data.x86_mask >= 0x1))
93 amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
94
Hans Rosenfeldb453de02011-01-24 16:05:41 +010095 if (boot_cpu_data.x86 == 0x15)
96 amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
97
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +010098 /* L3 cache partitioning is supported on family 0x15 */
99 if (boot_cpu_data.x86 == 0x15)
100 amd_northbridges.flags |= AMD_NB_L3_PARTITIONING;
101
Andi Kleena32073b2006-06-26 13:56:40 +0200102 return 0;
103}
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200104EXPORT_SYMBOL_GPL(amd_cache_northbridges);
Andi Kleena32073b2006-06-26 13:56:40 +0200105
Borislav Petkov84fd1d32011-03-03 12:59:32 +0100106/*
107 * Ignores subdevice/subvendor but as far as I can figure out
108 * they're useless anyways
109 */
110bool __init early_is_amd_nb(u32 device)
Andi Kleena32073b2006-06-26 13:56:40 +0200111{
Jan Beulich691269f2011-02-09 08:26:53 +0000112 const struct pci_device_id *id;
Andi Kleena32073b2006-06-26 13:56:40 +0200113 u32 vendor = device & 0xffff;
Jan Beulich691269f2011-02-09 08:26:53 +0000114
Andi Kleena32073b2006-06-26 13:56:40 +0200115 device >>= 16;
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200116 for (id = amd_nb_misc_ids; id->vendor; id++)
Andi Kleena32073b2006-06-26 13:56:40 +0200117 if (vendor == id->vendor && device == id->device)
Borislav Petkov84fd1d32011-03-03 12:59:32 +0100118 return true;
119 return false;
Andi Kleena32073b2006-06-26 13:56:40 +0200120}
121
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100122int amd_get_subcaches(int cpu)
123{
124 struct pci_dev *link = node_to_amd_nb(amd_get_nb_id(cpu))->link;
125 unsigned int mask;
Kevin Winchester141168c2011-12-20 20:52:22 -0400126 int cuid;
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100127
128 if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING))
129 return 0;
130
131 pci_read_config_dword(link, 0x1d4, &mask);
132
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100133 cuid = cpu_data(cpu).compute_unit_id;
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100134 return (mask >> (4 * cuid)) & 0xf;
135}
136
137int amd_set_subcaches(int cpu, int mask)
138{
139 static unsigned int reset, ban;
140 struct amd_northbridge *nb = node_to_amd_nb(amd_get_nb_id(cpu));
141 unsigned int reg;
Kevin Winchester141168c2011-12-20 20:52:22 -0400142 int cuid;
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100143
144 if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING) || mask > 0xf)
145 return -EINVAL;
146
147 /* if necessary, collect reset state of L3 partitioning and BAN mode */
148 if (reset == 0) {
149 pci_read_config_dword(nb->link, 0x1d4, &reset);
150 pci_read_config_dword(nb->misc, 0x1b8, &ban);
151 ban &= 0x180000;
152 }
153
154 /* deactivate BAN mode if any subcaches are to be disabled */
155 if (mask != 0xf) {
156 pci_read_config_dword(nb->misc, 0x1b8, &reg);
157 pci_write_config_dword(nb->misc, 0x1b8, reg & ~0x180000);
158 }
159
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100160 cuid = cpu_data(cpu).compute_unit_id;
Hans Rosenfeldcabb5bd2011-02-07 18:10:39 +0100161 mask <<= 4 * cuid;
162 mask |= (0xf ^ (1 << cuid)) << 26;
163
164 pci_write_config_dword(nb->link, 0x1d4, mask);
165
166 /* reset BAN mode if L3 partitioning returned to reset state */
167 pci_read_config_dword(nb->link, 0x1d4, &reg);
168 if (reg == reset) {
169 pci_read_config_dword(nb->misc, 0x1b8, &reg);
170 reg &= ~0x180000;
171 pci_write_config_dword(nb->misc, 0x1b8, reg | ban);
172 }
173
174 return 0;
175}
176
Borislav Petkov84fd1d32011-03-03 12:59:32 +0100177static int amd_cache_gart(void)
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200178{
Borislav Petkov84fd1d32011-03-03 12:59:32 +0100179 u16 i;
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200180
181 if (!amd_nb_has_feature(AMD_NB_GART))
182 return 0;
183
184 flush_words = kmalloc(amd_nb_num() * sizeof(u32), GFP_KERNEL);
185 if (!flush_words) {
186 amd_northbridges.flags &= ~AMD_NB_GART;
187 return -ENOMEM;
188 }
189
190 for (i = 0; i != amd_nb_num(); i++)
191 pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c,
192 &flush_words[i]);
193
194 return 0;
195}
196
Hans Rosenfeldeec1d4f2010-10-29 17:14:30 +0200197void amd_flush_garts(void)
Andi Kleena32073b2006-06-26 13:56:40 +0200198{
199 int flushed, i;
200 unsigned long flags;
201 static DEFINE_SPINLOCK(gart_lock);
202
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200203 if (!amd_nb_has_feature(AMD_NB_GART))
Andreas Herrmann900f9ac2010-09-17 18:02:54 +0200204 return;
205
Andi Kleena32073b2006-06-26 13:56:40 +0200206 /* Avoid races between AGP and IOMMU. In theory it's not needed
207 but I'm not sure if the hardware won't lose flush requests
208 when another is pending. This whole thing is so expensive anyways
209 that it doesn't matter to serialize more. -AK */
210 spin_lock_irqsave(&gart_lock, flags);
211 flushed = 0;
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200212 for (i = 0; i < amd_nb_num(); i++) {
213 pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
214 flush_words[i] | 1);
Andi Kleena32073b2006-06-26 13:56:40 +0200215 flushed++;
216 }
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200217 for (i = 0; i < amd_nb_num(); i++) {
Andi Kleena32073b2006-06-26 13:56:40 +0200218 u32 w;
219 /* Make sure the hardware actually executed the flush*/
220 for (;;) {
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200221 pci_read_config_dword(node_to_amd_nb(i)->misc,
Andi Kleena32073b2006-06-26 13:56:40 +0200222 0x9c, &w);
223 if (!(w & 1))
224 break;
225 cpu_relax();
226 }
227 }
228 spin_unlock_irqrestore(&gart_lock, flags);
229 if (!flushed)
230 printk("nothing to flush?\n");
231}
Hans Rosenfeldeec1d4f2010-10-29 17:14:30 +0200232EXPORT_SYMBOL_GPL(amd_flush_garts);
Andi Kleena32073b2006-06-26 13:56:40 +0200233
Hans Rosenfeldeec1d4f2010-10-29 17:14:30 +0200234static __init int init_amd_nbs(void)
Borislav Petkov0e152cd2010-03-12 15:43:03 +0100235{
236 int err = 0;
237
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200238 err = amd_cache_northbridges();
Borislav Petkov0e152cd2010-03-12 15:43:03 +0100239
240 if (err < 0)
Hans Rosenfeldeec1d4f2010-10-29 17:14:30 +0200241 printk(KERN_NOTICE "AMD NB: Cannot enumerate AMD northbridges.\n");
Borislav Petkov0e152cd2010-03-12 15:43:03 +0100242
Hans Rosenfeld9653a5c2010-10-29 17:14:31 +0200243 if (amd_cache_gart() < 0)
244 printk(KERN_NOTICE "AMD NB: Cannot initialize GART flush words, "
245 "GART support disabled.\n");
246
Borislav Petkov0e152cd2010-03-12 15:43:03 +0100247 return err;
248}
249
250/* This has to go after the PCI subsystem */
Hans Rosenfeldeec1d4f2010-10-29 17:14:30 +0200251fs_initcall(init_amd_nbs);