amd64_edac: Improve DRAM address mapping
[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
24d9b70b
JB
23const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = {
24 { 0x00, 0x18, 0x20 },
25 { 0xff, 0x00, 0x20 },
26 { 0xfe, 0x00, 0x20 },
27 { }
28};
29
eec1d4fa
HR
30struct amd_northbridge_info amd_northbridges;
31EXPORT_SYMBOL(amd_northbridges);
a32073bf 32
9653a5c7
HR
33static struct pci_dev *next_northbridge(struct pci_dev *dev,
34 struct pci_device_id *ids)
a32073bf
AK
35{
36 do {
37 dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
38 if (!dev)
39 break;
9653a5c7 40 } while (!pci_match_id(ids, dev));
a32073bf
AK
41 return dev;
42}
43
9653a5c7 44int amd_cache_northbridges(void)
a32073bf 45{
9653a5c7
HR
46 int i = 0;
47 struct amd_northbridge *nb;
48 struct pci_dev *misc;
3c6df2a9 49
9653a5c7 50 if (amd_nb_num())
a32073bf
AK
51 return 0;
52
9653a5c7
HR
53 misc = NULL;
54 while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
55 i++;
900f9ac9 56
9653a5c7
HR
57 if (i == 0)
58 return 0;
a32073bf 59
9653a5c7
HR
60 nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
61 if (!nb)
a32073bf
AK
62 return -ENOMEM;
63
9653a5c7
HR
64 amd_northbridges.nb = nb;
65 amd_northbridges.num = i;
3c6df2a9 66
9653a5c7
HR
67 misc = NULL;
68 for (i = 0; i != amd_nb_num(); i++) {
69 node_to_amd_nb(i)->misc = misc =
70 next_northbridge(misc, amd_nb_misc_ids);
71 }
72
73 /* some CPU families (e.g. family 0x11) do not support GART */
74 if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
75 boot_cpu_data.x86 == 0x15)
76 amd_northbridges.flags |= AMD_NB_GART;
a32073bf 77
f658bcfb
HR
78 /*
79 * Some CPU families support L3 Cache Index Disable. There are some
80 * limitations because of E382 and E388 on family 0x10.
81 */
82 if (boot_cpu_data.x86 == 0x10 &&
83 boot_cpu_data.x86_model >= 0x8 &&
84 (boot_cpu_data.x86_model > 0x9 ||
85 boot_cpu_data.x86_mask >= 0x1))
86 amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE;
87
a32073bf
AK
88 return 0;
89}
9653a5c7 90EXPORT_SYMBOL_GPL(amd_cache_northbridges);
a32073bf
AK
91
92/* Ignores subdevice/subvendor but as far as I can figure out
93 they're useless anyways */
eec1d4fa 94int __init early_is_amd_nb(u32 device)
a32073bf
AK
95{
96 struct pci_device_id *id;
97 u32 vendor = device & 0xffff;
98 device >>= 16;
9653a5c7 99 for (id = amd_nb_misc_ids; id->vendor; id++)
a32073bf
AK
100 if (vendor == id->vendor && device == id->device)
101 return 1;
102 return 0;
103}
104
9653a5c7
HR
105int amd_cache_gart(void)
106{
107 int i;
108
109 if (!amd_nb_has_feature(AMD_NB_GART))
110 return 0;
111
112 flush_words = kmalloc(amd_nb_num() * sizeof(u32), GFP_KERNEL);
113 if (!flush_words) {
114 amd_northbridges.flags &= ~AMD_NB_GART;
115 return -ENOMEM;
116 }
117
118 for (i = 0; i != amd_nb_num(); i++)
119 pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c,
120 &flush_words[i]);
121
122 return 0;
123}
124
eec1d4fa 125void amd_flush_garts(void)
a32073bf
AK
126{
127 int flushed, i;
128 unsigned long flags;
129 static DEFINE_SPINLOCK(gart_lock);
130
9653a5c7 131 if (!amd_nb_has_feature(AMD_NB_GART))
900f9ac9
AH
132 return;
133
a32073bf
AK
134 /* Avoid races between AGP and IOMMU. In theory it's not needed
135 but I'm not sure if the hardware won't lose flush requests
136 when another is pending. This whole thing is so expensive anyways
137 that it doesn't matter to serialize more. -AK */
138 spin_lock_irqsave(&gart_lock, flags);
139 flushed = 0;
9653a5c7
HR
140 for (i = 0; i < amd_nb_num(); i++) {
141 pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c,
142 flush_words[i] | 1);
a32073bf
AK
143 flushed++;
144 }
9653a5c7 145 for (i = 0; i < amd_nb_num(); i++) {
a32073bf
AK
146 u32 w;
147 /* Make sure the hardware actually executed the flush*/
148 for (;;) {
9653a5c7 149 pci_read_config_dword(node_to_amd_nb(i)->misc,
a32073bf
AK
150 0x9c, &w);
151 if (!(w & 1))
152 break;
153 cpu_relax();
154 }
155 }
156 spin_unlock_irqrestore(&gart_lock, flags);
157 if (!flushed)
158 printk("nothing to flush?\n");
159}
eec1d4fa 160EXPORT_SYMBOL_GPL(amd_flush_garts);
a32073bf 161
eec1d4fa 162static __init int init_amd_nbs(void)
0e152cd7
BP
163{
164 int err = 0;
165
9653a5c7 166 err = amd_cache_northbridges();
0e152cd7
BP
167
168 if (err < 0)
eec1d4fa 169 printk(KERN_NOTICE "AMD NB: Cannot enumerate AMD northbridges.\n");
0e152cd7 170
9653a5c7
HR
171 if (amd_cache_gart() < 0)
172 printk(KERN_NOTICE "AMD NB: Cannot initialize GART flush words, "
173 "GART support disabled.\n");
174
0e152cd7
BP
175 return err;
176}
177
178/* This has to go after the PCI subsystem */
eec1d4fa 179fs_initcall(init_amd_nbs);