diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-01 12:47:12 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-01 12:47:12 -0400 |
commit | e76e5b2c663ac74ae6a542ac20795c625e36a5cd (patch) | |
tree | 2e7271be1f3a26832f4b121839fc4044fbbf27a6 /arch | |
parent | 32527bc0e4b4fa7711ad1c923cf64ae72a7ffd9d (diff) | |
parent | eeafda70bf2807544e96fa4e52b2433cd470ff46 (diff) |
Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: (88 commits)
PCI: fix HT MSI mapping fix
PCI: don't enable too much HT MSI mapping
x86/PCI: make pci=lastbus=255 work when acpi is on
PCI: save and restore PCIe 2.0 registers
PCI: update fakephp for bus_id removal
PCI: fix kernel oops on bridge removal
PCI: fix conflict between SR-IOV and config space sizing
powerpc/PCI: include pci.h in powerpc MSI implementation
PCI Hotplug: schedule fakephp for feature removal
PCI Hotplug: rename legacy_fakephp to fakephp
PCI Hotplug: restore fakephp interface with complete reimplementation
PCI: Introduce /sys/bus/pci/devices/.../rescan
PCI: Introduce /sys/bus/pci/devices/.../remove
PCI: Introduce /sys/bus/pci/rescan
PCI: Introduce pci_rescan_bus()
PCI: do not enable bridges more than once
PCI: do not initialize bridges more than once
PCI: always scan child buses
PCI: pci_scan_slot() returns newly found devices
PCI: don't scan existing devices
...
Fix trivial append-only conflict in Documentation/feature-removal-schedule.txt
Diffstat (limited to 'arch')
-rw-r--r-- | arch/alpha/include/asm/pci.h | 14 | ||||
-rw-r--r-- | arch/alpha/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/alpha/kernel/pci-sysfs.c | 366 | ||||
-rw-r--r-- | arch/powerpc/include/asm/pci.h | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/msi.c | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/pci.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/apic/io_apic.c | 4 | ||||
-rw-r--r-- | arch/x86/kernel/pci-dma.c | 3 | ||||
-rw-r--r-- | arch/x86/pci/early.c | 19 | ||||
-rw-r--r-- | arch/x86/pci/fixup.c | 20 | ||||
-rw-r--r-- | arch/x86/pci/legacy.c | 3 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 227 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig_64.c | 17 |
13 files changed, 594 insertions, 93 deletions
diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index 2a14302c17a3..cb04eaa6ba33 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h | |||
@@ -273,4 +273,18 @@ struct pci_dev *alpha_gendev_to_pci(struct device *dev); | |||
273 | 273 | ||
274 | extern struct pci_dev *isa_bridge; | 274 | extern struct pci_dev *isa_bridge; |
275 | 275 | ||
276 | extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, | ||
277 | size_t count); | ||
278 | extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, | ||
279 | size_t count); | ||
280 | extern int pci_mmap_legacy_page_range(struct pci_bus *bus, | ||
281 | struct vm_area_struct *vma, | ||
282 | enum pci_mmap_state mmap_state); | ||
283 | extern void pci_adjust_legacy_attr(struct pci_bus *bus, | ||
284 | enum pci_mmap_state mmap_type); | ||
285 | #define HAVE_PCI_LEGACY 1 | ||
286 | |||
287 | extern int pci_create_resource_files(struct pci_dev *dev); | ||
288 | extern void pci_remove_resource_files(struct pci_dev *dev); | ||
289 | |||
276 | #endif /* __ALPHA_PCI_H */ | 290 | #endif /* __ALPHA_PCI_H */ |
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index b4697759a123..a427538252f8 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile | |||
@@ -12,7 +12,7 @@ obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \ | |||
12 | 12 | ||
13 | obj-$(CONFIG_VGA_HOSE) += console.o | 13 | obj-$(CONFIG_VGA_HOSE) += console.o |
14 | obj-$(CONFIG_SMP) += smp.o | 14 | obj-$(CONFIG_SMP) += smp.o |
15 | obj-$(CONFIG_PCI) += pci.o pci_iommu.o | 15 | obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o |
16 | obj-$(CONFIG_SRM_ENV) += srm_env.o | 16 | obj-$(CONFIG_SRM_ENV) += srm_env.o |
17 | obj-$(CONFIG_MODULES) += module.o | 17 | obj-$(CONFIG_MODULES) += module.o |
18 | 18 | ||
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c new file mode 100644 index 000000000000..6ea822e7f724 --- /dev/null +++ b/arch/alpha/kernel/pci-sysfs.c | |||
@@ -0,0 +1,366 @@ | |||
1 | /* | ||
2 | * arch/alpha/kernel/pci-sysfs.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Ivan Kokshaysky | ||
5 | * | ||
6 | * Alpha PCI resource files. | ||
7 | * | ||
8 | * Loosely based on generic HAVE_PCI_MMAP implementation in | ||
9 | * drivers/pci/pci-sysfs.c | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include <linux/pci.h> | ||
14 | |||
15 | static int hose_mmap_page_range(struct pci_controller *hose, | ||
16 | struct vm_area_struct *vma, | ||
17 | enum pci_mmap_state mmap_type, int sparse) | ||
18 | { | ||
19 | unsigned long base; | ||
20 | |||
21 | if (mmap_type == pci_mmap_mem) | ||
22 | base = sparse ? hose->sparse_mem_base : hose->dense_mem_base; | ||
23 | else | ||
24 | base = sparse ? hose->sparse_io_base : hose->dense_io_base; | ||
25 | |||
26 | vma->vm_pgoff += base >> PAGE_SHIFT; | ||
27 | vma->vm_flags |= (VM_IO | VM_RESERVED); | ||
28 | |||
29 | return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | ||
30 | vma->vm_end - vma->vm_start, | ||
31 | vma->vm_page_prot); | ||
32 | } | ||
33 | |||
34 | static int __pci_mmap_fits(struct pci_dev *pdev, int num, | ||
35 | struct vm_area_struct *vma, int sparse) | ||
36 | { | ||
37 | unsigned long nr, start, size; | ||
38 | int shift = sparse ? 5 : 0; | ||
39 | |||
40 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
41 | start = vma->vm_pgoff; | ||
42 | size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1; | ||
43 | |||
44 | if (start < size && size - start >= nr) | ||
45 | return 1; | ||
46 | WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d " | ||
47 | "(size 0x%08lx)\n", | ||
48 | current->comm, sparse ? " sparse" : "", start, start + nr, | ||
49 | pci_name(pdev), num, size); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * pci_mmap_resource - map a PCI resource into user memory space | ||
55 | * @kobj: kobject for mapping | ||
56 | * @attr: struct bin_attribute for the file being mapped | ||
57 | * @vma: struct vm_area_struct passed into the mmap | ||
58 | * @sparse: address space type | ||
59 | * | ||
60 | * Use the bus mapping routines to map a PCI resource into userspace. | ||
61 | */ | ||
62 | static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | ||
63 | struct vm_area_struct *vma, int sparse) | ||
64 | { | ||
65 | struct pci_dev *pdev = to_pci_dev(container_of(kobj, | ||
66 | struct device, kobj)); | ||
67 | struct resource *res = (struct resource *)attr->private; | ||
68 | enum pci_mmap_state mmap_type; | ||
69 | struct pci_bus_region bar; | ||
70 | int i; | ||
71 | |||
72 | for (i = 0; i < PCI_ROM_RESOURCE; i++) | ||
73 | if (res == &pdev->resource[i]) | ||
74 | break; | ||
75 | if (i >= PCI_ROM_RESOURCE) | ||
76 | return -ENODEV; | ||
77 | |||
78 | if (!__pci_mmap_fits(pdev, i, vma, sparse)) | ||
79 | return -EINVAL; | ||
80 | |||
81 | if (iomem_is_exclusive(res->start)) | ||
82 | return -EINVAL; | ||
83 | |||
84 | pcibios_resource_to_bus(pdev, &bar, res); | ||
85 | vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0)); | ||
86 | mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; | ||
87 | |||
88 | return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse); | ||
89 | } | ||
90 | |||
91 | static int pci_mmap_resource_sparse(struct kobject *kobj, | ||
92 | struct bin_attribute *attr, | ||
93 | struct vm_area_struct *vma) | ||
94 | { | ||
95 | return pci_mmap_resource(kobj, attr, vma, 1); | ||
96 | } | ||
97 | |||
98 | static int pci_mmap_resource_dense(struct kobject *kobj, | ||
99 | struct bin_attribute *attr, | ||
100 | struct vm_area_struct *vma) | ||
101 | { | ||
102 | return pci_mmap_resource(kobj, attr, vma, 0); | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * pci_remove_resource_files - cleanup resource files | ||
107 | * @dev: dev to cleanup | ||
108 | * | ||
109 | * If we created resource files for @dev, remove them from sysfs and | ||
110 | * free their resources. | ||
111 | */ | ||
112 | void pci_remove_resource_files(struct pci_dev *pdev) | ||
113 | { | ||
114 | int i; | ||
115 | |||
116 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
117 | struct bin_attribute *res_attr; | ||
118 | |||
119 | res_attr = pdev->res_attr[i]; | ||
120 | if (res_attr) { | ||
121 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
122 | kfree(res_attr); | ||
123 | } | ||
124 | |||
125 | res_attr = pdev->res_attr_wc[i]; | ||
126 | if (res_attr) { | ||
127 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
128 | kfree(res_attr); | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) | ||
134 | { | ||
135 | struct pci_bus_region bar; | ||
136 | struct pci_controller *hose = pdev->sysdata; | ||
137 | long dense_offset; | ||
138 | unsigned long sparse_size; | ||
139 | |||
140 | pcibios_resource_to_bus(pdev, &bar, &pdev->resource[num]); | ||
141 | |||
142 | /* All core logic chips have 4G sparse address space, except | ||
143 | CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM | ||
144 | definitions in asm/core_xxx.h files). This corresponds | ||
145 | to 128M or 512M of the bus space. */ | ||
146 | dense_offset = (long)(hose->dense_mem_base - hose->sparse_mem_base); | ||
147 | sparse_size = dense_offset >= 0x400000000UL ? 0x20000000 : 0x8000000; | ||
148 | |||
149 | return bar.end < sparse_size; | ||
150 | } | ||
151 | |||
152 | static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name, | ||
153 | char *suffix, struct bin_attribute *res_attr, | ||
154 | unsigned long sparse) | ||
155 | { | ||
156 | size_t size = pci_resource_len(pdev, num); | ||
157 | |||
158 | sprintf(name, "resource%d%s", num, suffix); | ||
159 | res_attr->mmap = sparse ? pci_mmap_resource_sparse : | ||
160 | pci_mmap_resource_dense; | ||
161 | res_attr->attr.name = name; | ||
162 | res_attr->attr.mode = S_IRUSR | S_IWUSR; | ||
163 | res_attr->size = sparse ? size << 5 : size; | ||
164 | res_attr->private = &pdev->resource[num]; | ||
165 | return sysfs_create_bin_file(&pdev->dev.kobj, res_attr); | ||
166 | } | ||
167 | |||
168 | static int pci_create_attr(struct pci_dev *pdev, int num) | ||
169 | { | ||
170 | /* allocate attribute structure, piggyback attribute name */ | ||
171 | int retval, nlen1, nlen2 = 0, res_count = 1; | ||
172 | unsigned long sparse_base, dense_base; | ||
173 | struct bin_attribute *attr; | ||
174 | struct pci_controller *hose = pdev->sysdata; | ||
175 | char *suffix, *attr_name; | ||
176 | |||
177 | suffix = ""; /* Assume bwx machine, normal resourceN files. */ | ||
178 | nlen1 = 10; | ||
179 | |||
180 | if (pdev->resource[num].flags & IORESOURCE_MEM) { | ||
181 | sparse_base = hose->sparse_mem_base; | ||
182 | dense_base = hose->dense_mem_base; | ||
183 | if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) { | ||
184 | sparse_base = 0; | ||
185 | suffix = "_dense"; | ||
186 | nlen1 = 16; /* resourceN_dense */ | ||
187 | } | ||
188 | } else { | ||
189 | sparse_base = hose->sparse_io_base; | ||
190 | dense_base = hose->dense_io_base; | ||
191 | } | ||
192 | |||
193 | if (sparse_base) { | ||
194 | suffix = "_sparse"; | ||
195 | nlen1 = 17; | ||
196 | if (dense_base) { | ||
197 | nlen2 = 16; /* resourceN_dense */ | ||
198 | res_count = 2; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC); | ||
203 | if (!attr) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | /* Create bwx, sparse or single dense file */ | ||
207 | attr_name = (char *)(attr + res_count); | ||
208 | pdev->res_attr[num] = attr; | ||
209 | retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr, | ||
210 | sparse_base); | ||
211 | if (retval || res_count == 1) | ||
212 | return retval; | ||
213 | |||
214 | /* Create dense file */ | ||
215 | attr_name += nlen1; | ||
216 | attr++; | ||
217 | pdev->res_attr_wc[num] = attr; | ||
218 | return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0); | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * pci_create_resource_files - create resource files in sysfs for @dev | ||
223 | * @dev: dev in question | ||
224 | * | ||
225 | * Walk the resources in @dev creating files for each resource available. | ||
226 | */ | ||
227 | int pci_create_resource_files(struct pci_dev *pdev) | ||
228 | { | ||
229 | int i; | ||
230 | int retval; | ||
231 | |||
232 | /* Expose the PCI resources from this device as files */ | ||
233 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
234 | |||
235 | /* skip empty resources */ | ||
236 | if (!pci_resource_len(pdev, i)) | ||
237 | continue; | ||
238 | |||
239 | retval = pci_create_attr(pdev, i); | ||
240 | if (retval) { | ||
241 | pci_remove_resource_files(pdev); | ||
242 | return retval; | ||
243 | } | ||
244 | } | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | /* Legacy I/O bus mapping stuff. */ | ||
249 | |||
250 | static int __legacy_mmap_fits(struct pci_controller *hose, | ||
251 | struct vm_area_struct *vma, | ||
252 | unsigned long res_size, int sparse) | ||
253 | { | ||
254 | unsigned long nr, start, size; | ||
255 | |||
256 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
257 | start = vma->vm_pgoff; | ||
258 | size = ((res_size - 1) >> PAGE_SHIFT) + 1; | ||
259 | |||
260 | if (start < size && size - start >= nr) | ||
261 | return 1; | ||
262 | WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d " | ||
263 | "(size 0x%08lx)\n", | ||
264 | current->comm, sparse ? " sparse" : "", start, start + nr, | ||
265 | hose->index, size); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static inline int has_sparse(struct pci_controller *hose, | ||
270 | enum pci_mmap_state mmap_type) | ||
271 | { | ||
272 | unsigned long base; | ||
273 | |||
274 | base = (mmap_type == pci_mmap_mem) ? hose->sparse_mem_base : | ||
275 | hose->sparse_io_base; | ||
276 | |||
277 | return base != 0; | ||
278 | } | ||
279 | |||
280 | int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, | ||
281 | enum pci_mmap_state mmap_type) | ||
282 | { | ||
283 | struct pci_controller *hose = bus->sysdata; | ||
284 | int sparse = has_sparse(hose, mmap_type); | ||
285 | unsigned long res_size; | ||
286 | |||
287 | res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size : | ||
288 | bus->legacy_io->size; | ||
289 | if (!__legacy_mmap_fits(hose, vma, res_size, sparse)) | ||
290 | return -EINVAL; | ||
291 | |||
292 | return hose_mmap_page_range(hose, vma, mmap_type, sparse); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * pci_adjust_legacy_attr - adjustment of legacy file attributes | ||
297 | * @b: bus to create files under | ||
298 | * @mmap_type: I/O port or memory | ||
299 | * | ||
300 | * Adjust file name and size for sparse mappings. | ||
301 | */ | ||
302 | void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type) | ||
303 | { | ||
304 | struct pci_controller *hose = bus->sysdata; | ||
305 | |||
306 | if (!has_sparse(hose, mmap_type)) | ||
307 | return; | ||
308 | |||
309 | if (mmap_type == pci_mmap_mem) { | ||
310 | bus->legacy_mem->attr.name = "legacy_mem_sparse"; | ||
311 | bus->legacy_mem->size <<= 5; | ||
312 | } else { | ||
313 | bus->legacy_io->attr.name = "legacy_io_sparse"; | ||
314 | bus->legacy_io->size <<= 5; | ||
315 | } | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | /* Legacy I/O bus read/write functions */ | ||
320 | int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size) | ||
321 | { | ||
322 | struct pci_controller *hose = bus->sysdata; | ||
323 | |||
324 | port += hose->io_space->start; | ||
325 | |||
326 | switch(size) { | ||
327 | case 1: | ||
328 | *((u8 *)val) = inb(port); | ||
329 | return 1; | ||
330 | case 2: | ||
331 | if (port & 1) | ||
332 | return -EINVAL; | ||
333 | *((u16 *)val) = inw(port); | ||
334 | return 2; | ||
335 | case 4: | ||
336 | if (port & 3) | ||
337 | return -EINVAL; | ||
338 | *((u32 *)val) = inl(port); | ||
339 | return 4; | ||
340 | } | ||
341 | return -EINVAL; | ||
342 | } | ||
343 | |||
344 | int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) | ||
345 | { | ||
346 | struct pci_controller *hose = bus->sysdata; | ||
347 | |||
348 | port += hose->io_space->start; | ||
349 | |||
350 | switch(size) { | ||
351 | case 1: | ||
352 | outb(port, val); | ||
353 | return 1; | ||
354 | case 2: | ||
355 | if (port & 1) | ||
356 | return -EINVAL; | ||
357 | outw(port, val); | ||
358 | return 2; | ||
359 | case 4: | ||
360 | if (port & 3) | ||
361 | return -EINVAL; | ||
362 | outl(port, val); | ||
363 | return 4; | ||
364 | } | ||
365 | return -EINVAL; | ||
366 | } | ||
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 3548159a1beb..ba17d5d90a49 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h | |||
@@ -114,6 +114,10 @@ extern int pci_domain_nr(struct pci_bus *bus); | |||
114 | /* Decide whether to display the domain number in /proc */ | 114 | /* Decide whether to display the domain number in /proc */ |
115 | extern int pci_proc_domain(struct pci_bus *bus); | 115 | extern int pci_proc_domain(struct pci_bus *bus); |
116 | 116 | ||
117 | /* MSI arch hooks */ | ||
118 | #define arch_setup_msi_irqs arch_setup_msi_irqs | ||
119 | #define arch_teardown_msi_irqs arch_teardown_msi_irqs | ||
120 | #define arch_msi_check_device arch_msi_check_device | ||
117 | 121 | ||
118 | struct vm_area_struct; | 122 | struct vm_area_struct; |
119 | /* Map a range of PCI memory or I/O space for a device into user space */ | 123 | /* Map a range of PCI memory or I/O space for a device into user space */ |
diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c index 3bb7d3dd28be..8bbc12d20f5c 100644 --- a/arch/powerpc/kernel/msi.c +++ b/arch/powerpc/kernel/msi.c | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
11 | #include <linux/msi.h> | 11 | #include <linux/msi.h> |
12 | #include <linux/pci.h> | ||
12 | 13 | ||
13 | #include <asm/machdep.h> | 14 | #include <asm/machdep.h> |
14 | 15 | ||
@@ -19,6 +20,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type) | |||
19 | return -ENOSYS; | 20 | return -ENOSYS; |
20 | } | 21 | } |
21 | 22 | ||
23 | /* PowerPC doesn't support multiple MSI yet */ | ||
24 | if (type == PCI_CAP_ID_MSI && nvec > 1) | ||
25 | return 1; | ||
26 | |||
22 | if (ppc_md.msi_check_device) { | 27 | if (ppc_md.msi_check_device) { |
23 | pr_debug("msi: Using platform check routine.\n"); | 28 | pr_debug("msi: Using platform check routine.\n"); |
24 | return ppc_md.msi_check_device(dev, nvec, type); | 29 | return ppc_md.msi_check_device(dev, nvec, type); |
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index a977de23cb4d..a0301bfeb954 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h | |||
@@ -86,6 +86,9 @@ static inline void early_quirks(void) { } | |||
86 | 86 | ||
87 | extern void pci_iommu_alloc(void); | 87 | extern void pci_iommu_alloc(void); |
88 | 88 | ||
89 | /* MSI arch hook */ | ||
90 | #define arch_setup_msi_irqs arch_setup_msi_irqs | ||
91 | |||
89 | #endif /* __KERNEL__ */ | 92 | #endif /* __KERNEL__ */ |
90 | 93 | ||
91 | #ifdef CONFIG_X86_32 | 94 | #ifdef CONFIG_X86_32 |
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index da99ffcdfde6..1bb5c6cee3eb 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c | |||
@@ -3468,6 +3468,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | |||
3468 | struct intel_iommu *iommu = NULL; | 3468 | struct intel_iommu *iommu = NULL; |
3469 | int index = 0; | 3469 | int index = 0; |
3470 | 3470 | ||
3471 | /* x86 doesn't support multiple MSI yet */ | ||
3472 | if (type == PCI_CAP_ID_MSI && nvec > 1) | ||
3473 | return 1; | ||
3474 | |||
3471 | irq_want = nr_irqs_gsi; | 3475 | irq_want = nr_irqs_gsi; |
3472 | sub_handle = 0; | 3476 | sub_handle = 0; |
3473 | list_for_each_entry(msidesc, &dev->msi_list, list) { | 3477 | list_for_each_entry(msidesc, &dev->msi_list, list) { |
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index c7c4776ff630..90f5b9ef5def 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c | |||
@@ -300,8 +300,7 @@ fs_initcall(pci_iommu_init); | |||
300 | static __devinit void via_no_dac(struct pci_dev *dev) | 300 | static __devinit void via_no_dac(struct pci_dev *dev) |
301 | { | 301 | { |
302 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) { | 302 | if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) { |
303 | printk(KERN_INFO | 303 | dev_info(&dev->dev, "disabling DAC on VIA PCI bridge\n"); |
304 | "PCI: VIA PCI bridge detected. Disabling DAC.\n"); | ||
305 | forbid_dac = 1; | 304 | forbid_dac = 1; |
306 | } | 305 | } |
307 | } | 306 | } |
diff --git a/arch/x86/pci/early.c b/arch/x86/pci/early.c index f6adf2c6d751..aaf26ae58cd5 100644 --- a/arch/x86/pci/early.c +++ b/arch/x86/pci/early.c | |||
@@ -69,11 +69,12 @@ void early_dump_pci_device(u8 bus, u8 slot, u8 func) | |||
69 | int j; | 69 | int j; |
70 | u32 val; | 70 | u32 val; |
71 | 71 | ||
72 | printk(KERN_INFO "PCI: %02x:%02x:%02x", bus, slot, func); | 72 | printk(KERN_INFO "pci 0000:%02x:%02x.%d config space:", |
73 | bus, slot, func); | ||
73 | 74 | ||
74 | for (i = 0; i < 256; i += 4) { | 75 | for (i = 0; i < 256; i += 4) { |
75 | if (!(i & 0x0f)) | 76 | if (!(i & 0x0f)) |
76 | printk("\n%04x:",i); | 77 | printk("\n %02x:",i); |
77 | 78 | ||
78 | val = read_pci_config(bus, slot, func, i); | 79 | val = read_pci_config(bus, slot, func, i); |
79 | for (j = 0; j < 4; j++) { | 80 | for (j = 0; j < 4; j++) { |
@@ -96,20 +97,22 @@ void early_dump_pci_devices(void) | |||
96 | for (func = 0; func < 8; func++) { | 97 | for (func = 0; func < 8; func++) { |
97 | u32 class; | 98 | u32 class; |
98 | u8 type; | 99 | u8 type; |
100 | |||
99 | class = read_pci_config(bus, slot, func, | 101 | class = read_pci_config(bus, slot, func, |
100 | PCI_CLASS_REVISION); | 102 | PCI_CLASS_REVISION); |
101 | if (class == 0xffffffff) | 103 | if (class == 0xffffffff) |
102 | break; | 104 | continue; |
103 | 105 | ||
104 | early_dump_pci_device(bus, slot, func); | 106 | early_dump_pci_device(bus, slot, func); |
105 | 107 | ||
106 | /* No multi-function device? */ | 108 | if (func == 0) { |
107 | type = read_pci_config_byte(bus, slot, func, | 109 | type = read_pci_config_byte(bus, slot, |
110 | func, | ||
108 | PCI_HEADER_TYPE); | 111 | PCI_HEADER_TYPE); |
109 | if (!(type & 0x80)) | 112 | if (!(type & 0x80)) |
110 | break; | 113 | break; |
114 | } | ||
111 | } | 115 | } |
112 | } | 116 | } |
113 | } | 117 | } |
114 | } | 118 | } |
115 | |||
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 9c49919e4d1c..6dd89555fbfa 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c | |||
@@ -495,26 +495,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SIEMENS, 0x0015, | |||
495 | pci_siemens_interrupt_controller); | 495 | pci_siemens_interrupt_controller); |
496 | 496 | ||
497 | /* | 497 | /* |
498 | * Regular PCI devices have 256 bytes, but AMD Family 10h/11h CPUs have | ||
499 | * 4096 bytes configuration space for each function of their processor | ||
500 | * configuration space. | ||
501 | */ | ||
502 | static void amd_cpu_pci_cfg_space_size(struct pci_dev *dev) | ||
503 | { | ||
504 | dev->cfg_size = pci_cfg_space_size_ext(dev); | ||
505 | } | ||
506 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1200, amd_cpu_pci_cfg_space_size); | ||
507 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1201, amd_cpu_pci_cfg_space_size); | ||
508 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, amd_cpu_pci_cfg_space_size); | ||
509 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, amd_cpu_pci_cfg_space_size); | ||
510 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, amd_cpu_pci_cfg_space_size); | ||
511 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1300, amd_cpu_pci_cfg_space_size); | ||
512 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1301, amd_cpu_pci_cfg_space_size); | ||
513 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1302, amd_cpu_pci_cfg_space_size); | ||
514 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1303, amd_cpu_pci_cfg_space_size); | ||
515 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1304, amd_cpu_pci_cfg_space_size); | ||
516 | |||
517 | /* | ||
518 | * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from | 498 | * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from |
519 | * confusing the PCI engine: | 499 | * confusing the PCI engine: |
520 | */ | 500 | */ |
diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index f1065b129e9c..4061bb0f267d 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c | |||
@@ -50,8 +50,6 @@ static int __init pci_legacy_init(void) | |||
50 | if (pci_root_bus) | 50 | if (pci_root_bus) |
51 | pci_bus_add_devices(pci_root_bus); | 51 | pci_bus_add_devices(pci_root_bus); |
52 | 52 | ||
53 | pcibios_fixup_peer_bridges(); | ||
54 | |||
55 | return 0; | 53 | return 0; |
56 | } | 54 | } |
57 | 55 | ||
@@ -67,6 +65,7 @@ int __init pci_subsys_init(void) | |||
67 | pci_visws_init(); | 65 | pci_visws_init(); |
68 | #endif | 66 | #endif |
69 | pci_legacy_init(); | 67 | pci_legacy_init(); |
68 | pcibios_fixup_peer_bridges(); | ||
70 | pcibios_irq_init(); | 69 | pcibios_irq_init(); |
71 | pcibios_init(); | 70 | pcibios_init(); |
72 | 71 | ||
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 89bf9242c80a..905bb526b133 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/acpi.h> | 15 | #include <linux/acpi.h> |
16 | #include <linux/bitmap.h> | 16 | #include <linux/bitmap.h> |
17 | #include <linux/sort.h> | ||
17 | #include <asm/e820.h> | 18 | #include <asm/e820.h> |
18 | #include <asm/pci_x86.h> | 19 | #include <asm/pci_x86.h> |
19 | 20 | ||
@@ -24,24 +25,49 @@ | |||
24 | /* Indicate if the mmcfg resources have been placed into the resource table. */ | 25 | /* Indicate if the mmcfg resources have been placed into the resource table. */ |
25 | static int __initdata pci_mmcfg_resources_inserted; | 26 | static int __initdata pci_mmcfg_resources_inserted; |
26 | 27 | ||
28 | static __init int extend_mmcfg(int num) | ||
29 | { | ||
30 | struct acpi_mcfg_allocation *new; | ||
31 | int new_num = pci_mmcfg_config_num + num; | ||
32 | |||
33 | new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL); | ||
34 | if (!new) | ||
35 | return -1; | ||
36 | |||
37 | if (pci_mmcfg_config) { | ||
38 | memcpy(new, pci_mmcfg_config, | ||
39 | sizeof(pci_mmcfg_config[0]) * new_num); | ||
40 | kfree(pci_mmcfg_config); | ||
41 | } | ||
42 | pci_mmcfg_config = new; | ||
43 | |||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end) | ||
48 | { | ||
49 | int i = pci_mmcfg_config_num; | ||
50 | |||
51 | pci_mmcfg_config_num++; | ||
52 | pci_mmcfg_config[i].address = addr; | ||
53 | pci_mmcfg_config[i].pci_segment = segment; | ||
54 | pci_mmcfg_config[i].start_bus_number = start; | ||
55 | pci_mmcfg_config[i].end_bus_number = end; | ||
56 | } | ||
57 | |||
27 | static const char __init *pci_mmcfg_e7520(void) | 58 | static const char __init *pci_mmcfg_e7520(void) |
28 | { | 59 | { |
29 | u32 win; | 60 | u32 win; |
30 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); | 61 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); |
31 | 62 | ||
32 | win = win & 0xf000; | 63 | win = win & 0xf000; |
33 | if(win == 0x0000 || win == 0xf000) | 64 | if (win == 0x0000 || win == 0xf000) |
34 | pci_mmcfg_config_num = 0; | 65 | return NULL; |
35 | else { | 66 | |
36 | pci_mmcfg_config_num = 1; | 67 | if (extend_mmcfg(1) == -1) |
37 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL); | 68 | return NULL; |
38 | if (!pci_mmcfg_config) | 69 | |
39 | return NULL; | 70 | fill_one_mmcfg(win << 16, 0, 0, 255); |
40 | pci_mmcfg_config[0].address = win << 16; | ||
41 | pci_mmcfg_config[0].pci_segment = 0; | ||
42 | pci_mmcfg_config[0].start_bus_number = 0; | ||
43 | pci_mmcfg_config[0].end_bus_number = 255; | ||
44 | } | ||
45 | 71 | ||
46 | return "Intel Corporation E7520 Memory Controller Hub"; | 72 | return "Intel Corporation E7520 Memory Controller Hub"; |
47 | } | 73 | } |
@@ -50,13 +76,11 @@ static const char __init *pci_mmcfg_intel_945(void) | |||
50 | { | 76 | { |
51 | u32 pciexbar, mask = 0, len = 0; | 77 | u32 pciexbar, mask = 0, len = 0; |
52 | 78 | ||
53 | pci_mmcfg_config_num = 1; | ||
54 | |||
55 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); | 79 | raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); |
56 | 80 | ||
57 | /* Enable bit */ | 81 | /* Enable bit */ |
58 | if (!(pciexbar & 1)) | 82 | if (!(pciexbar & 1)) |
59 | pci_mmcfg_config_num = 0; | 83 | return NULL; |
60 | 84 | ||
61 | /* Size bits */ | 85 | /* Size bits */ |
62 | switch ((pciexbar >> 1) & 3) { | 86 | switch ((pciexbar >> 1) & 3) { |
@@ -73,28 +97,23 @@ static const char __init *pci_mmcfg_intel_945(void) | |||
73 | len = 0x04000000U; | 97 | len = 0x04000000U; |
74 | break; | 98 | break; |
75 | default: | 99 | default: |
76 | pci_mmcfg_config_num = 0; | 100 | return NULL; |
77 | } | 101 | } |
78 | 102 | ||
79 | /* Errata #2, things break when not aligned on a 256Mb boundary */ | 103 | /* Errata #2, things break when not aligned on a 256Mb boundary */ |
80 | /* Can only happen in 64M/128M mode */ | 104 | /* Can only happen in 64M/128M mode */ |
81 | 105 | ||
82 | if ((pciexbar & mask) & 0x0fffffffU) | 106 | if ((pciexbar & mask) & 0x0fffffffU) |
83 | pci_mmcfg_config_num = 0; | 107 | return NULL; |
84 | 108 | ||
85 | /* Don't hit the APIC registers and their friends */ | 109 | /* Don't hit the APIC registers and their friends */ |
86 | if ((pciexbar & mask) >= 0xf0000000U) | 110 | if ((pciexbar & mask) >= 0xf0000000U) |
87 | pci_mmcfg_config_num = 0; | 111 | return NULL; |
88 | 112 | ||
89 | if (pci_mmcfg_config_num) { | 113 | if (extend_mmcfg(1) == -1) |
90 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL); | 114 | return NULL; |
91 | if (!pci_mmcfg_config) | 115 | |
92 | return NULL; | 116 | fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1); |
93 | pci_mmcfg_config[0].address = pciexbar & mask; | ||
94 | pci_mmcfg_config[0].pci_segment = 0; | ||
95 | pci_mmcfg_config[0].start_bus_number = 0; | ||
96 | pci_mmcfg_config[0].end_bus_number = (len >> 20) - 1; | ||
97 | } | ||
98 | 117 | ||
99 | return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; | 118 | return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; |
100 | } | 119 | } |
@@ -138,22 +157,77 @@ static const char __init *pci_mmcfg_amd_fam10h(void) | |||
138 | busnbits = 8; | 157 | busnbits = 8; |
139 | } | 158 | } |
140 | 159 | ||
141 | pci_mmcfg_config_num = (1 << segnbits); | 160 | if (extend_mmcfg(1 << segnbits) == -1) |
142 | pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]) * | ||
143 | pci_mmcfg_config_num, GFP_KERNEL); | ||
144 | if (!pci_mmcfg_config) | ||
145 | return NULL; | 161 | return NULL; |
146 | 162 | ||
147 | for (i = 0; i < (1 << segnbits); i++) { | 163 | for (i = 0; i < (1 << segnbits); i++) |
148 | pci_mmcfg_config[i].address = base + (1<<28) * i; | 164 | fill_one_mmcfg(base + (1<<28) * i, i, 0, (1 << busnbits) - 1); |
149 | pci_mmcfg_config[i].pci_segment = i; | ||
150 | pci_mmcfg_config[i].start_bus_number = 0; | ||
151 | pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1; | ||
152 | } | ||
153 | 165 | ||
154 | return "AMD Family 10h NB"; | 166 | return "AMD Family 10h NB"; |
155 | } | 167 | } |
156 | 168 | ||
169 | static bool __initdata mcp55_checked; | ||
170 | static const char __init *pci_mmcfg_nvidia_mcp55(void) | ||
171 | { | ||
172 | int bus; | ||
173 | int mcp55_mmconf_found = 0; | ||
174 | |||
175 | static const u32 extcfg_regnum = 0x90; | ||
176 | static const u32 extcfg_regsize = 4; | ||
177 | static const u32 extcfg_enable_mask = 1<<31; | ||
178 | static const u32 extcfg_start_mask = 0xff<<16; | ||
179 | static const int extcfg_start_shift = 16; | ||
180 | static const u32 extcfg_size_mask = 0x3<<28; | ||
181 | static const int extcfg_size_shift = 28; | ||
182 | static const int extcfg_sizebus[] = {0x100, 0x80, 0x40, 0x20}; | ||
183 | static const u32 extcfg_base_mask[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff}; | ||
184 | static const int extcfg_base_lshift = 25; | ||
185 | |||
186 | /* | ||
187 | * do check if amd fam10h already took over | ||
188 | */ | ||
189 | if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked) | ||
190 | return NULL; | ||
191 | |||
192 | mcp55_checked = true; | ||
193 | for (bus = 0; bus < 256; bus++) { | ||
194 | u64 base; | ||
195 | u32 l, extcfg; | ||
196 | u16 vendor, device; | ||
197 | int start, size_index, end; | ||
198 | |||
199 | raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l); | ||
200 | vendor = l & 0xffff; | ||
201 | device = (l >> 16) & 0xffff; | ||
202 | |||
203 | if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device) | ||
204 | continue; | ||
205 | |||
206 | raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum, | ||
207 | extcfg_regsize, &extcfg); | ||
208 | |||
209 | if (!(extcfg & extcfg_enable_mask)) | ||
210 | continue; | ||
211 | |||
212 | if (extend_mmcfg(1) == -1) | ||
213 | continue; | ||
214 | |||
215 | size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift; | ||
216 | base = extcfg & extcfg_base_mask[size_index]; | ||
217 | /* base could > 4G */ | ||
218 | base <<= extcfg_base_lshift; | ||
219 | start = (extcfg & extcfg_start_mask) >> extcfg_start_shift; | ||
220 | end = start + extcfg_sizebus[size_index] - 1; | ||
221 | fill_one_mmcfg(base, 0, start, end); | ||
222 | mcp55_mmconf_found++; | ||
223 | } | ||
224 | |||
225 | if (!mcp55_mmconf_found) | ||
226 | return NULL; | ||
227 | |||
228 | return "nVidia MCP55"; | ||
229 | } | ||
230 | |||
157 | struct pci_mmcfg_hostbridge_probe { | 231 | struct pci_mmcfg_hostbridge_probe { |
158 | u32 bus; | 232 | u32 bus; |
159 | u32 devfn; | 233 | u32 devfn; |
@@ -171,8 +245,52 @@ static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = { | |||
171 | 0x1200, pci_mmcfg_amd_fam10h }, | 245 | 0x1200, pci_mmcfg_amd_fam10h }, |
172 | { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD, | 246 | { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD, |
173 | 0x1200, pci_mmcfg_amd_fam10h }, | 247 | 0x1200, pci_mmcfg_amd_fam10h }, |
248 | { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA, | ||
249 | 0x0369, pci_mmcfg_nvidia_mcp55 }, | ||
174 | }; | 250 | }; |
175 | 251 | ||
252 | static int __init cmp_mmcfg(const void *x1, const void *x2) | ||
253 | { | ||
254 | const typeof(pci_mmcfg_config[0]) *m1 = x1; | ||
255 | const typeof(pci_mmcfg_config[0]) *m2 = x2; | ||
256 | int start1, start2; | ||
257 | |||
258 | start1 = m1->start_bus_number; | ||
259 | start2 = m2->start_bus_number; | ||
260 | |||
261 | return start1 - start2; | ||
262 | } | ||
263 | |||
264 | static void __init pci_mmcfg_check_end_bus_number(void) | ||
265 | { | ||
266 | int i; | ||
267 | typeof(pci_mmcfg_config[0]) *cfg, *cfgx; | ||
268 | |||
269 | /* sort them at first */ | ||
270 | sort(pci_mmcfg_config, pci_mmcfg_config_num, | ||
271 | sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL); | ||
272 | |||
273 | /* last one*/ | ||
274 | if (pci_mmcfg_config_num > 0) { | ||
275 | i = pci_mmcfg_config_num - 1; | ||
276 | cfg = &pci_mmcfg_config[i]; | ||
277 | if (cfg->end_bus_number < cfg->start_bus_number) | ||
278 | cfg->end_bus_number = 255; | ||
279 | } | ||
280 | |||
281 | /* don't overlap please */ | ||
282 | for (i = 0; i < pci_mmcfg_config_num - 1; i++) { | ||
283 | cfg = &pci_mmcfg_config[i]; | ||
284 | cfgx = &pci_mmcfg_config[i+1]; | ||
285 | |||
286 | if (cfg->end_bus_number < cfg->start_bus_number) | ||
287 | cfg->end_bus_number = 255; | ||
288 | |||
289 | if (cfg->end_bus_number >= cfgx->start_bus_number) | ||
290 | cfg->end_bus_number = cfgx->start_bus_number - 1; | ||
291 | } | ||
292 | } | ||
293 | |||
176 | static int __init pci_mmcfg_check_hostbridge(void) | 294 | static int __init pci_mmcfg_check_hostbridge(void) |
177 | { | 295 | { |
178 | u32 l; | 296 | u32 l; |
@@ -186,31 +304,33 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
186 | 304 | ||
187 | pci_mmcfg_config_num = 0; | 305 | pci_mmcfg_config_num = 0; |
188 | pci_mmcfg_config = NULL; | 306 | pci_mmcfg_config = NULL; |
189 | name = NULL; | ||
190 | 307 | ||
191 | for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) { | 308 | for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) { |
192 | bus = pci_mmcfg_probes[i].bus; | 309 | bus = pci_mmcfg_probes[i].bus; |
193 | devfn = pci_mmcfg_probes[i].devfn; | 310 | devfn = pci_mmcfg_probes[i].devfn; |
194 | raw_pci_ops->read(0, bus, devfn, 0, 4, &l); | 311 | raw_pci_ops->read(0, bus, devfn, 0, 4, &l); |
195 | vendor = l & 0xffff; | 312 | vendor = l & 0xffff; |
196 | device = (l >> 16) & 0xffff; | 313 | device = (l >> 16) & 0xffff; |
197 | 314 | ||
315 | name = NULL; | ||
198 | if (pci_mmcfg_probes[i].vendor == vendor && | 316 | if (pci_mmcfg_probes[i].vendor == vendor && |
199 | pci_mmcfg_probes[i].device == device) | 317 | pci_mmcfg_probes[i].device == device) |
200 | name = pci_mmcfg_probes[i].probe(); | 318 | name = pci_mmcfg_probes[i].probe(); |
201 | } | ||
202 | 319 | ||
203 | if (name) { | 320 | if (name) |
204 | printk(KERN_INFO "PCI: Found %s %s MMCONFIG support.\n", | 321 | printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n", |
205 | name, pci_mmcfg_config_num ? "with" : "without"); | 322 | name); |
206 | } | 323 | } |
207 | 324 | ||
208 | return name != NULL; | 325 | /* some end_bus_number is crazy, fix it */ |
326 | pci_mmcfg_check_end_bus_number(); | ||
327 | |||
328 | return pci_mmcfg_config_num != 0; | ||
209 | } | 329 | } |
210 | 330 | ||
211 | static void __init pci_mmcfg_insert_resources(void) | 331 | static void __init pci_mmcfg_insert_resources(void) |
212 | { | 332 | { |
213 | #define PCI_MMCFG_RESOURCE_NAME_LEN 19 | 333 | #define PCI_MMCFG_RESOURCE_NAME_LEN 24 |
214 | int i; | 334 | int i; |
215 | struct resource *res; | 335 | struct resource *res; |
216 | char *names; | 336 | char *names; |
@@ -228,9 +348,10 @@ static void __init pci_mmcfg_insert_resources(void) | |||
228 | struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i]; | 348 | struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i]; |
229 | num_buses = cfg->end_bus_number - cfg->start_bus_number + 1; | 349 | num_buses = cfg->end_bus_number - cfg->start_bus_number + 1; |
230 | res->name = names; | 350 | res->name = names; |
231 | snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, "PCI MMCONFIG %u", | 351 | snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, |
232 | cfg->pci_segment); | 352 | "PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment, |
233 | res->start = cfg->address; | 353 | cfg->start_bus_number, cfg->end_bus_number); |
354 | res->start = cfg->address + (cfg->start_bus_number << 20); | ||
234 | res->end = res->start + (num_buses << 20) - 1; | 355 | res->end = res->start + (num_buses << 20) - 1; |
235 | res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; | 356 | res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; |
236 | insert_resource(&iomem_resource, res); | 357 | insert_resource(&iomem_resource, res); |
@@ -354,8 +475,6 @@ static void __init pci_mmcfg_reject_broken(int early) | |||
354 | (pci_mmcfg_config[0].address == 0)) | 475 | (pci_mmcfg_config[0].address == 0)) |
355 | return; | 476 | return; |
356 | 477 | ||
357 | cfg = &pci_mmcfg_config[0]; | ||
358 | |||
359 | for (i = 0; i < pci_mmcfg_config_num; i++) { | 478 | for (i = 0; i < pci_mmcfg_config_num; i++) { |
360 | int valid = 0; | 479 | int valid = 0; |
361 | u64 addr, size; | 480 | u64 addr, size; |
@@ -423,10 +542,10 @@ static void __init __pci_mmcfg_init(int early) | |||
423 | known_bridge = 1; | 542 | known_bridge = 1; |
424 | } | 543 | } |
425 | 544 | ||
426 | if (!known_bridge) { | 545 | if (!known_bridge) |
427 | acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); | 546 | acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); |
428 | pci_mmcfg_reject_broken(early); | 547 | |
429 | } | 548 | pci_mmcfg_reject_broken(early); |
430 | 549 | ||
431 | if ((pci_mmcfg_config_num == 0) || | 550 | if ((pci_mmcfg_config_num == 0) || |
432 | (pci_mmcfg_config == NULL) || | 551 | (pci_mmcfg_config == NULL) || |
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index 30007ffc8e11..94349f8b2f96 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c | |||
@@ -112,13 +112,18 @@ static struct pci_raw_ops pci_mmcfg = { | |||
112 | static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg) | 112 | static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg) |
113 | { | 113 | { |
114 | void __iomem *addr; | 114 | void __iomem *addr; |
115 | u32 size; | 115 | u64 start, size; |
116 | 116 | ||
117 | size = (cfg->end_bus_number + 1) << 20; | 117 | start = cfg->start_bus_number; |
118 | addr = ioremap_nocache(cfg->address, size); | 118 | start <<= 20; |
119 | start += cfg->address; | ||
120 | size = cfg->end_bus_number + 1 - cfg->start_bus_number; | ||
121 | size <<= 20; | ||
122 | addr = ioremap_nocache(start, size); | ||
119 | if (addr) { | 123 | if (addr) { |
120 | printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", | 124 | printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", |
121 | cfg->address, cfg->address + size - 1); | 125 | start, start + size - 1); |
126 | addr -= cfg->start_bus_number << 20; | ||
122 | } | 127 | } |
123 | return addr; | 128 | return addr; |
124 | } | 129 | } |
@@ -157,7 +162,7 @@ void __init pci_mmcfg_arch_free(void) | |||
157 | 162 | ||
158 | for (i = 0; i < pci_mmcfg_config_num; ++i) { | 163 | for (i = 0; i < pci_mmcfg_config_num; ++i) { |
159 | if (pci_mmcfg_virt[i].virt) { | 164 | if (pci_mmcfg_virt[i].virt) { |
160 | iounmap(pci_mmcfg_virt[i].virt); | 165 | iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20)); |
161 | pci_mmcfg_virt[i].virt = NULL; | 166 | pci_mmcfg_virt[i].virt = NULL; |
162 | pci_mmcfg_virt[i].cfg = NULL; | 167 | pci_mmcfg_virt[i].cfg = NULL; |
163 | } | 168 | } |