Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sh: Fix up early PCI PERR/SERR IRQ handling.

This adds support for handling early PERR/SERR triggering in between
controller registration and the initial bus scan. Buggy cards end up
asserting these as soon as the M66EN scan is undertaken, resulting in
an early crash.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>

+90 -26
+35 -16
arch/sh/drivers/pci/common.c
··· 3 3 #include <linux/timer.h> 4 4 #include <linux/kernel.h> 5 5 6 - static int __init 7 - early_read_config_word(struct pci_channel *hose, 8 - int top_bus, int bus, int devfn, int offset, u16 *value) 6 + /* 7 + * These functions are used early on before PCI scanning is done 8 + * and all of the pci_dev and pci_bus structures have been created. 9 + */ 10 + static struct pci_dev *fake_pci_dev(struct pci_channel *hose, 11 + int top_bus, int busnr, int devfn) 9 12 { 10 - struct pci_dev fake_dev; 11 - struct pci_bus fake_bus; 13 + static struct pci_dev dev; 14 + static struct pci_bus bus; 12 15 13 - fake_dev.bus = &fake_bus; 14 - fake_dev.sysdata = hose; 15 - fake_dev.devfn = devfn; 16 - fake_bus.number = bus; 17 - fake_bus.sysdata = hose; 18 - fake_bus.ops = hose->pci_ops; 16 + dev.bus = &bus; 17 + dev.sysdata = hose; 18 + dev.devfn = devfn; 19 + bus.number = busnr; 20 + bus.sysdata = hose; 21 + bus.ops = hose->pci_ops; 19 22 20 - if (bus != top_bus) 23 + if(busnr != top_bus) 21 24 /* Fake a parent bus structure. */ 22 - fake_bus.parent = &fake_bus; 25 + bus.parent = &bus; 23 26 else 24 - fake_bus.parent = NULL; 27 + bus.parent = NULL; 25 28 26 - return pci_read_config_word(&fake_dev, offset, value); 29 + return &dev; 27 30 } 31 + 32 + #define EARLY_PCI_OP(rw, size, type) \ 33 + int __init early_##rw##_config_##size(struct pci_channel *hose, \ 34 + int top_bus, int bus, int devfn, int offset, type value) \ 35 + { \ 36 + return pci_##rw##_config_##size( \ 37 + fake_pci_dev(hose, top_bus, bus, devfn), \ 38 + offset, value); \ 39 + } 40 + 41 + EARLY_PCI_OP(read, byte, u8 *) 42 + EARLY_PCI_OP(read, word, u16 *) 43 + EARLY_PCI_OP(read, dword, u32 *) 44 + EARLY_PCI_OP(write, byte, u8) 45 + EARLY_PCI_OP(write, word, u16) 46 + EARLY_PCI_OP(write, dword, u32) 28 47 29 48 int __init pci_is_66mhz_capable(struct pci_channel *hose, 30 49 int top_bus, int current_bus) ··· 152 133 153 134 /* Now back off of the IRQ for awhile */ 154 135 if (hose->err_irq) { 155 - disable_irq(hose->err_irq); 136 + disable_irq_nosync(hose->err_irq); 156 137 hose->err_timer.expires = jiffies + HZ; 157 138 add_timer(&hose->err_timer); 158 139 }
+1 -1
arch/sh/drivers/pci/pci-sh7780.c
··· 150 150 __raw_writel(SH4_PCIINTM_SDIM, hose->reg_base + SH4_PCIINTM); 151 151 152 152 /* Back off the IRQ for awhile */ 153 - disable_irq(irq); 153 + disable_irq_nosync(irq); 154 154 hose->serr_timer.expires = jiffies + HZ; 155 155 add_timer(&hose->serr_timer); 156 156
+42 -9
arch/sh/drivers/pci/pci.c
··· 204 204 } 205 205 206 206 void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, 207 - struct resource *res) 207 + struct resource *res) 208 208 { 209 209 struct pci_channel *hose = dev->sysdata; 210 210 unsigned long offset = 0; ··· 218 218 region->end = res->end - offset; 219 219 } 220 220 221 - void __devinit 222 - pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, 223 - struct pci_bus_region *region) 221 + void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, 222 + struct pci_bus_region *region) 224 223 { 225 224 struct pci_channel *hose = dev->sysdata; 226 225 unsigned long offset = 0; ··· 302 303 return str; 303 304 } 304 305 306 + static void __init 307 + pcibios_bus_report_status_early(struct pci_channel *hose, 308 + int top_bus, int current_bus, 309 + unsigned int status_mask, int warn) 310 + { 311 + unsigned int pci_devfn; 312 + u16 status; 313 + int ret; 314 + 315 + for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { 316 + if (PCI_FUNC(pci_devfn)) 317 + continue; 318 + ret = early_read_config_word(hose, top_bus, current_bus, 319 + pci_devfn, PCI_STATUS, &status); 320 + if (ret != PCIBIOS_SUCCESSFUL) 321 + continue; 322 + if (status == 0xffff) 323 + continue; 324 + 325 + early_write_config_word(hose, top_bus, current_bus, 326 + pci_devfn, PCI_STATUS, 327 + status & status_mask); 328 + if (warn) 329 + printk("(%02x:%02x: %04X) ", current_bus, 330 + pci_devfn, status); 331 + } 332 + } 333 + 305 334 /* 306 335 * We can't use pci_find_device() here since we are 307 336 * called from interrupt context. 308 337 */ 309 - static void pcibios_bus_report_status(struct pci_bus *bus, 310 - unsigned int status_mask, int warn) 338 + static void __init_refok 339 + pcibios_bus_report_status(struct pci_bus *bus, unsigned int status_mask, 340 + int warn) 311 341 { 312 342 struct pci_dev *dev; 313 343 ··· 369 341 pcibios_bus_report_status(dev->subordinate, status_mask, warn); 370 342 } 371 343 372 - void pcibios_report_status(unsigned int status_mask, int warn) 344 + void __init_refok pcibios_report_status(unsigned int status_mask, int warn) 373 345 { 374 346 struct pci_channel *hose; 375 347 376 - for (hose = hose_head; hose; hose = hose->next) 377 - pcibios_bus_report_status(hose->bus, status_mask, warn); 348 + for (hose = hose_head; hose; hose = hose->next) { 349 + if (unlikely(!hose->bus)) 350 + pcibios_bus_report_status_early(hose, hose_head->index, 351 + hose->index, status_mask, warn); 352 + else 353 + pcibios_bus_report_status(hose->bus, status_mask, warn); 354 + } 378 355 } 379 356 380 357 int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+12
arch/sh/include/asm/pci.h
··· 41 41 extern void pcibios_report_status(unsigned int status_mask, int warn); 42 42 43 43 /* arch/sh/drivers/pci/common.c */ 44 + extern int early_read_config_byte(struct pci_channel *hose, int top_bus, 45 + int bus, int devfn, int offset, u8 *value); 46 + extern int early_read_config_word(struct pci_channel *hose, int top_bus, 47 + int bus, int devfn, int offset, u16 *value); 48 + extern int early_read_config_dword(struct pci_channel *hose, int top_bus, 49 + int bus, int devfn, int offset, u32 *value); 50 + extern int early_write_config_byte(struct pci_channel *hose, int top_bus, 51 + int bus, int devfn, int offset, u8 value); 52 + extern int early_write_config_word(struct pci_channel *hose, int top_bus, 53 + int bus, int devfn, int offset, u16 value); 54 + extern int early_write_config_dword(struct pci_channel *hose, int top_bus, 55 + int bus, int devfn, int offset, u32 value); 44 56 extern void pcibios_enable_timers(struct pci_channel *hose); 45 57 extern unsigned int pcibios_handle_status_errors(unsigned long addr, 46 58 unsigned int status, struct pci_channel *hose);