x86, amd: Extend AMD northbridge caching code to support "Link Control" devices
[linux-2.6-block.git] / arch / x86 / kernel / amd_nb.c
CommitLineData
a32073bf
AK
1/*
2 * Shared support code for AMD K8 northbridges and derivates.
3 * Copyright 2006 Andi Kleen, SUSE Labs. Subject to GPLv2.
4 */
a32073bf 5#include <linux/types.h>
5a0e3ad6 6#include <linux/slab.h>
a32073bf
AK
7#include <linux/init.h>
8#include <linux/errno.h>
9#include <linux/module.h>
10#include <linux/spinlock.h>
23ac4ae8 11#include <asm/amd_nb.h>
a32073bf 12
a32073bf
AK
13static u32 *flush_words;
14
9653a5c7 15struct pci_device_id amd_nb_misc_ids[] = {
cf169702
JR
16 { 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) },
5c80cc78 18 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_MISC) },
a32073bf
AK
19 {}
20};
9653a5c7 21EXPORT_SYMBOL(amd_nb_misc_ids);
a32073bf 22
41b2610c
HR
23static struct pci_device_id amd_nb_link_ids[] = {
24 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_LINK) },
25 {}
26};
27
24d9b70b
JB
28const 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
eec1d4fa
HR
35struct amd_northbridge_info amd_northbridges;
36EXPORT_SYMBOL(amd_northbridges);
a32073bf 37
9653a5c7
HR
38static struct pci_dev *next_northbridge(struct pci_dev *dev,
39 struct pci_device_id *ids)
a32073bf
AK
40{
41 do {
42 dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
43 if (!dev)
44 break;
9653a5c7 45 } while (!pci_match_id(ids, dev));
a32073bf
AK
46 return dev;
47}
48
9653a5c7 49int amd_cache_northbridges(void)
a32073bf 50{
9653a5c7
HR
51 int i = 0;
52 struct amd_northbridge *nb;
41b2610c 53 struct pci_dev *misc, *link;
3c6df2a9 54
9653a5c7 55 if (amd_nb_num())
a32073bf
AK
56 return 0;
57
9653a5c7
HR
58 misc = NULL;
59 while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
60 i++;
900f9ac9 61
9653a5c7
HR
62 if (i == 0)
63 return 0;
a32073bf 64
9653a5c7
HR
65 nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
66 if (!nb)
a32073bf
AK
67 return -ENOMEM;
68
9653a5c7
HR
69 amd_northbridges.nb = nb;
70 amd_northbridges.num = i;
3c6df2a9 71
41b2610c 72 link = misc = NULL;
9653a5c7
HR
73 for (i = 0; i != amd_nb_num(); i++) {
74 node_to_amd_nb(i)->misc = misc =
75 next_northbridge(misc, amd_nb_misc_ids);
41b2610c
HR
76 node_to_amd_nb(i)->link = link =
77 next_northbridge(link, amd_nb_link_ids);
9653a5c7
HR
78 }
79
80 /* some CPU families (e.g. family 0x11) do not support GART */
81 if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
82 boot_cpu_data.x86 == 0x15)
83 amd_northbridges.flags |= AMD_NB_GART;
a32073bf 84
f658bcfb
HR
85 /*
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
b453de02
HR
95 if (boot_cpu_data.x86 == 0x15)
96 amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
97
a32073bf
AK
98 return 0;
99}
9653a5c7 100EXPORT_SYMBOL_GPL(amd_cache_northbridges);
a32073bf
AK
101
102/* Ignores subdevice/subvendor but as far as I can figure out
103 they're useless anyways */
eec1d4fa 104int __init early_is_amd_nb(u32 device)
a32073bf
AK
105{
106 struct pci_device_id *id;
107 u32 vendor = device & 0xffff;
108 device >>= 16;
9653a5c7 109 for (id = amd_nb_misc_ids; id->vendor; id++)
a32073bf
AK
110 if (vendor == id->vendor && device == id->device)
111 return 1;
112 return 0;
113}
114
9653a5c7
HR
115int amd_cache_gart(void)
116{
117 int i;
118
119 if (!amd_nb_has_feature(AMD_NB_GART))
120 return 0;
121
122 flush_words = kmalloc(amd_nb_num() * sizeof(u32), GFP_KERNEL);
123 if (!flush_words) {
124 amd_northbridges.flags &= ~AMD_NB_GART;
125 return -ENOMEM;
126 }
127
128 for (i = 0; i != amd_nb_num(); i++)
129 pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c,
130 &flush_words[i]);
131
132 return 0;
133}
134
eec1d4fa 135void amd_flush_garts(void)
a32073bf
AK
136{
137 int flushed, i;
138 unsigned long flags;
139 static DEFINE_SPINLOCK(gart_lock);
140
9653a5c7 141 if (!amd_nb_has_feature(AMD_NB_GART))
900f9ac9
AH
142 return;
143
a32073bf
AK
144 /* Avoid races between AGP and IOMMU. In theory it's not needed
145 but I'm not sure if the hardware won't lose flush requests
146 when another is pending. This whole thing is so expensive anyways
147 that it doesn't matter to serialize more. -AK */
148 spin_lock_irqsave(&gart_lock, flags);
149 flushed = 0;
9653a5c7
HR
150 for (i = 0; i < amd_nb_num(); i++) {
151 pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
152 flush_words[i] | 1);
a32073bf
AK
153 flushed++;
154 }
9653a5c7 155 for (i = 0; i < amd_nb_num(); i++) {
a32073bf
AK
156 u32 w;
157 /* Make sure the hardware actually executed the flush*/
158 for (;;) {
9653a5c7 159 pci_read_config_dword(node_to_amd_nb(i)->misc,
a32073bf
AK
160 0x9c, &w);
161 if (!(w & 1))
162 break;
163 cpu_relax();
164 }
165 }
166 spin_unlock_irqrestore(&gart_lock, flags);
167 if (!flushed)
168 printk("nothing to flush?\n");
169}
eec1d4fa 170EXPORT_SYMBOL_GPL(amd_flush_garts);
a32073bf 171
eec1d4fa 172static __init int init_amd_nbs(void)
0e152cd7
BP
173{
174 int err = 0;
175
9653a5c7 176 err = amd_cache_northbridges();
0e152cd7
BP
177
178 if (err < 0)
eec1d4fa 179 printk(KERN_NOTICE "AMD NB: Cannot enumerate AMD northbridges.\n");
0e152cd7 180
9653a5c7
HR
181 if (amd_cache_gart() < 0)
182 printk(KERN_NOTICE "AMD NB: Cannot initialize GART flush words, "
183 "GART support disabled.\n");
184
0e152cd7
BP
185 return err;
186}
187
188/* This has to go after the PCI subsystem */
eec1d4fa 189fs_initcall(init_amd_nbs);