aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/pci/mmconfig-shared.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-14 12:46:06 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-14 12:46:06 -0500
commit414f827c46973ba39320cfb43feb55a0eeb9b4e8 (patch)
tree45e860974ef698e71370a0ebdddcff4f14fbdf9e /arch/i386/pci/mmconfig-shared.c
parent86a71dbd3e81e8870d0f0e56b87875f57e58222b (diff)
parent126b1922367fbe5513daa675a2abd13ed3917f4e (diff)
Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6
* 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6: (94 commits) [PATCH] x86-64: Remove mk_pte_phys() [PATCH] i386: Fix broken CONFIG_COMPAT_VDSO on i386 [PATCH] i386: fix 32-bit ioctls on x64_32 [PATCH] x86: Unify pcspeaker platform device code between i386/x86-64 [PATCH] i386: Remove extern declaration from mm/discontig.c, put in header. [PATCH] i386: Rename cpu_gdt_descr and remove extern declaration from smpboot.c [PATCH] i386: Move mce_disabled to asm/mce.h [PATCH] i386: paravirt unhandled fallthrough [PATCH] x86_64: Wire up compat epoll_pwait [PATCH] x86: Don't require the vDSO for handling a.out signals [PATCH] i386: Fix Cyrix MediaGX detection [PATCH] i386: Fix warning in cpu initialization [PATCH] i386: Fix warning in microcode.c [PATCH] x86: Enable NMI watchdog for AMD Family 0x10 CPUs [PATCH] x86: Add new CPUID bits for AMD Family 10 CPUs in /proc/cpuinfo [PATCH] i386: Remove fastcall in paravirt.[ch] [PATCH] x86-64: Fix wrong gcc check in bitops.h [PATCH] x86-64: survive having no irq mapping for a vector [PATCH] i386: geode configuration fixes [PATCH] i386: add option to show more code in oops reports ...
Diffstat (limited to 'arch/i386/pci/mmconfig-shared.c')
-rw-r--r--arch/i386/pci/mmconfig-shared.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/arch/i386/pci/mmconfig-shared.c b/arch/i386/pci/mmconfig-shared.c
new file mode 100644
index 000000000000..747d8c63b0c4
--- /dev/null
+++ b/arch/i386/pci/mmconfig-shared.c
@@ -0,0 +1,264 @@
1/*
2 * mmconfig-shared.c - Low-level direct PCI config space access via
3 * MMCONFIG - common code between i386 and x86-64.
4 *
5 * This code does:
6 * - known chipset handling
7 * - ACPI decoding and validation
8 *
9 * Per-architecture code takes care of the mappings and accesses
10 * themselves.
11 */
12
13#include <linux/pci.h>
14#include <linux/init.h>
15#include <linux/acpi.h>
16#include <linux/bitmap.h>
17#include <asm/e820.h>
18
19#include "pci.h"
20
21/* aperture is up to 256MB but BIOS may reserve less */
22#define MMCONFIG_APER_MIN (2 * 1024*1024)
23#define MMCONFIG_APER_MAX (256 * 1024*1024)
24
25DECLARE_BITMAP(pci_mmcfg_fallback_slots, 32*PCI_MMCFG_MAX_CHECK_BUS);
26
27/* K8 systems have some devices (typically in the builtin northbridge)
28 that are only accessible using type1
29 Normally this can be expressed in the MCFG by not listing them
30 and assigning suitable _SEGs, but this isn't implemented in some BIOS.
31 Instead try to discover all devices on bus 0 that are unreachable using MM
32 and fallback for them. */
33static void __init unreachable_devices(void)
34{
35 int i, bus;
36 /* Use the max bus number from ACPI here? */
37 for (bus = 0; bus < PCI_MMCFG_MAX_CHECK_BUS; bus++) {
38 for (i = 0; i < 32; i++) {
39 unsigned int devfn = PCI_DEVFN(i, 0);
40 u32 val1, val2;
41
42 pci_conf1_read(0, bus, devfn, 0, 4, &val1);
43 if (val1 == 0xffffffff)
44 continue;
45
46 if (pci_mmcfg_arch_reachable(0, bus, devfn)) {
47 raw_pci_ops->read(0, bus, devfn, 0, 4, &val2);
48 if (val1 == val2)
49 continue;
50 }
51 set_bit(i + 32 * bus, pci_mmcfg_fallback_slots);
52 printk(KERN_NOTICE "PCI: No mmconfig possible on device"
53 " %02x:%02x\n", bus, i);
54 }
55 }
56}
57
58static const char __init *pci_mmcfg_e7520(void)
59{
60 u32 win;
61 pci_conf1_read(0, 0, PCI_DEVFN(0,0), 0xce, 2, &win);
62
63 pci_mmcfg_config_num = 1;
64 pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL);
65 if (!pci_mmcfg_config)
66 return NULL;
67 pci_mmcfg_config[0].address = (win & 0xf000) << 16;
68 pci_mmcfg_config[0].pci_segment = 0;
69 pci_mmcfg_config[0].start_bus_number = 0;
70 pci_mmcfg_config[0].end_bus_number = 255;
71
72 return "Intel Corporation E7520 Memory Controller Hub";
73}
74
75static const char __init *pci_mmcfg_intel_945(void)
76{
77 u32 pciexbar, mask = 0, len = 0;
78
79 pci_mmcfg_config_num = 1;
80
81 pci_conf1_read(0, 0, PCI_DEVFN(0,0), 0x48, 4, &pciexbar);
82
83 /* Enable bit */
84 if (!(pciexbar & 1))
85 pci_mmcfg_config_num = 0;
86
87 /* Size bits */
88 switch ((pciexbar >> 1) & 3) {
89 case 0:
90 mask = 0xf0000000U;
91 len = 0x10000000U;
92 break;
93 case 1:
94 mask = 0xf8000000U;
95 len = 0x08000000U;
96 break;
97 case 2:
98 mask = 0xfc000000U;
99 len = 0x04000000U;
100 break;
101 default:
102 pci_mmcfg_config_num = 0;
103 }
104
105 /* Errata #2, things break when not aligned on a 256Mb boundary */
106 /* Can only happen in 64M/128M mode */
107
108 if ((pciexbar & mask) & 0x0fffffffU)
109 pci_mmcfg_config_num = 0;
110
111 if (pci_mmcfg_config_num) {
112 pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL);
113 if (!pci_mmcfg_config)
114 return NULL;
115 pci_mmcfg_config[0].address = pciexbar & mask;
116 pci_mmcfg_config[0].pci_segment = 0;
117 pci_mmcfg_config[0].start_bus_number = 0;
118 pci_mmcfg_config[0].end_bus_number = (len >> 20) - 1;
119 }
120
121 return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
122}
123
124struct pci_mmcfg_hostbridge_probe {
125 u32 vendor;
126 u32 device;
127 const char *(*probe)(void);
128};
129
130static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
131 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
132 { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 },
133};
134
135static int __init pci_mmcfg_check_hostbridge(void)
136{
137 u32 l;
138 u16 vendor, device;
139 int i;
140 const char *name;
141
142 pci_conf1_read(0, 0, PCI_DEVFN(0,0), 0, 4, &l);
143 vendor = l & 0xffff;
144 device = (l >> 16) & 0xffff;
145
146 pci_mmcfg_config_num = 0;
147 pci_mmcfg_config = NULL;
148 name = NULL;
149
150 for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
151 if (pci_mmcfg_probes[i].vendor == vendor &&
152 pci_mmcfg_probes[i].device == device)
153 name = pci_mmcfg_probes[i].probe();
154 }
155
156 if (name) {
157 printk(KERN_INFO "PCI: Found %s %s MMCONFIG support.\n",
158 name, pci_mmcfg_config_num ? "with" : "without");
159 }
160
161 return name != NULL;
162}
163
164static void __init pci_mmcfg_insert_resources(void)
165{
166#define PCI_MMCFG_RESOURCE_NAME_LEN 19
167 int i;
168 struct resource *res;
169 char *names;
170 unsigned num_buses;
171
172 res = kcalloc(PCI_MMCFG_RESOURCE_NAME_LEN + sizeof(*res),
173 pci_mmcfg_config_num, GFP_KERNEL);
174 if (!res) {
175 printk(KERN_ERR "PCI: Unable to allocate MMCONFIG resources\n");
176 return;
177 }
178
179 names = (void *)&res[pci_mmcfg_config_num];
180 for (i = 0; i < pci_mmcfg_config_num; i++, res++) {
181 struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i];
182 num_buses = cfg->end_bus_number - cfg->start_bus_number + 1;
183 res->name = names;
184 snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, "PCI MMCONFIG %u",
185 cfg->pci_segment);
186 res->start = cfg->address;
187 res->end = res->start + (num_buses << 20) - 1;
188 res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
189 insert_resource(&iomem_resource, res);
190 names += PCI_MMCFG_RESOURCE_NAME_LEN;
191 }
192}
193
194static void __init pci_mmcfg_reject_broken(int type)
195{
196 typeof(pci_mmcfg_config[0]) *cfg;
197
198 if ((pci_mmcfg_config_num == 0) ||
199 (pci_mmcfg_config == NULL) ||
200 (pci_mmcfg_config[0].address == 0))
201 return;
202
203 cfg = &pci_mmcfg_config[0];
204
205 /*
206 * Handle more broken MCFG tables on Asus etc.
207 * They only contain a single entry for bus 0-0.
208 */
209 if (pci_mmcfg_config_num == 1 &&
210 cfg->pci_segment == 0 &&
211 (cfg->start_bus_number | cfg->end_bus_number) == 0) {
212 printk(KERN_ERR "PCI: start and end of bus number is 0. "
213 "Rejected as broken MCFG.\n");
214 goto reject;
215 }
216
217 /*
218 * Only do this check when type 1 works. If it doesn't work
219 * assume we run on a Mac and always use MCFG
220 */
221 if (type == 1 && !e820_all_mapped(cfg->address,
222 cfg->address + MMCONFIG_APER_MIN,
223 E820_RESERVED)) {
224 printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not"
225 " E820-reserved\n", cfg->address);
226 goto reject;
227 }
228 return;
229
230reject:
231 printk(KERN_ERR "PCI: Not using MMCONFIG.\n");
232 kfree(pci_mmcfg_config);
233 pci_mmcfg_config = NULL;
234 pci_mmcfg_config_num = 0;
235}
236
237void __init pci_mmcfg_init(int type)
238{
239 int known_bridge = 0;
240
241 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
242 return;
243
244 if (type == 1 && pci_mmcfg_check_hostbridge())
245 known_bridge = 1;
246
247 if (!known_bridge) {
248 acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
249 pci_mmcfg_reject_broken(type);
250 }
251
252 if ((pci_mmcfg_config_num == 0) ||
253 (pci_mmcfg_config == NULL) ||
254 (pci_mmcfg_config[0].address == 0))
255 return;
256
257 if (pci_mmcfg_arch_init()) {
258 if (type == 1)
259 unreachable_devices();
260 if (known_bridge)
261 pci_mmcfg_insert_resources();
262 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
263 }
264}