diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-02-03 02:46:20 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-02-03 02:46:20 -0500 |
commit | 9ad62ec4f752c82b39aa5927f23d894b46ae10b9 (patch) | |
tree | 546f4572329478e849408fe129e44625881f9992 /arch/sh/drivers | |
parent | 37feecb0ae8c29b713b957c053b05ad10c42c73b (diff) |
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>
Diffstat (limited to 'arch/sh/drivers')
-rw-r--r-- | arch/sh/drivers/pci/common.c | 51 | ||||
-rw-r--r-- | arch/sh/drivers/pci/pci-sh7780.c | 2 | ||||
-rw-r--r-- | arch/sh/drivers/pci/pci.c | 51 |
3 files changed, 78 insertions, 26 deletions
diff --git a/arch/sh/drivers/pci/common.c b/arch/sh/drivers/pci/common.c index 25aec005da18..dbf138199871 100644 --- a/arch/sh/drivers/pci/common.c +++ b/arch/sh/drivers/pci/common.c | |||
@@ -3,29 +3,48 @@ | |||
3 | #include <linux/timer.h> | 3 | #include <linux/timer.h> |
4 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
5 | 5 | ||
6 | static int __init | 6 | /* |
7 | early_read_config_word(struct pci_channel *hose, | 7 | * These functions are used early on before PCI scanning is done |
8 | int top_bus, int bus, int devfn, int offset, u16 *value) | 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; | 13 | static struct pci_dev dev; |
11 | struct pci_bus fake_bus; | 14 | static struct pci_bus bus; |
12 | 15 | ||
13 | fake_dev.bus = &fake_bus; | 16 | dev.bus = &bus; |
14 | fake_dev.sysdata = hose; | 17 | dev.sysdata = hose; |
15 | fake_dev.devfn = devfn; | 18 | dev.devfn = devfn; |
16 | fake_bus.number = bus; | 19 | bus.number = busnr; |
17 | fake_bus.sysdata = hose; | 20 | bus.sysdata = hose; |
18 | fake_bus.ops = hose->pci_ops; | 21 | bus.ops = hose->pci_ops; |
19 | 22 | ||
20 | if (bus != top_bus) | 23 | if(busnr != top_bus) |
21 | /* Fake a parent bus structure. */ | 24 | /* Fake a parent bus structure. */ |
22 | fake_bus.parent = &fake_bus; | 25 | bus.parent = &bus; |
23 | else | 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 | } |
28 | 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) | ||
47 | |||
29 | int __init pci_is_66mhz_capable(struct pci_channel *hose, | 48 | int __init pci_is_66mhz_capable(struct pci_channel *hose, |
30 | int top_bus, int current_bus) | 49 | int top_bus, int current_bus) |
31 | { | 50 | { |
@@ -133,7 +152,7 @@ unsigned int pcibios_handle_status_errors(unsigned long addr, | |||
133 | 152 | ||
134 | /* Now back off of the IRQ for awhile */ | 153 | /* Now back off of the IRQ for awhile */ |
135 | if (hose->err_irq) { | 154 | if (hose->err_irq) { |
136 | disable_irq(hose->err_irq); | 155 | disable_irq_nosync(hose->err_irq); |
137 | hose->err_timer.expires = jiffies + HZ; | 156 | hose->err_timer.expires = jiffies + HZ; |
138 | add_timer(&hose->err_timer); | 157 | add_timer(&hose->err_timer); |
139 | } | 158 | } |
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c index 472f67aec337..1e147f445c1a 100644 --- a/arch/sh/drivers/pci/pci-sh7780.c +++ b/arch/sh/drivers/pci/pci-sh7780.c | |||
@@ -150,7 +150,7 @@ static irqreturn_t sh7780_pci_serr_irq(int irq, void *dev_id) | |||
150 | __raw_writel(SH4_PCIINTM_SDIM, hose->reg_base + SH4_PCIINTM); | 150 | __raw_writel(SH4_PCIINTM_SDIM, hose->reg_base + SH4_PCIINTM); |
151 | 151 | ||
152 | /* Back off the IRQ for awhile */ | 152 | /* Back off the IRQ for awhile */ |
153 | disable_irq(irq); | 153 | disable_irq_nosync(irq); |
154 | hose->serr_timer.expires = jiffies + HZ; | 154 | hose->serr_timer.expires = jiffies + HZ; |
155 | add_timer(&hose->serr_timer); | 155 | add_timer(&hose->serr_timer); |
156 | 156 | ||
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index f4a69833fce2..41d8f014f1df 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c | |||
@@ -204,7 +204,7 @@ void pcibios_align_resource(void *data, struct resource *res, | |||
204 | } | 204 | } |
205 | 205 | ||
206 | void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, | 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 | struct pci_channel *hose = dev->sysdata; | 209 | struct pci_channel *hose = dev->sysdata; |
210 | unsigned long offset = 0; | 210 | unsigned long offset = 0; |
@@ -218,9 +218,8 @@ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, | |||
218 | region->end = res->end - offset; | 218 | region->end = res->end - offset; |
219 | } | 219 | } |
220 | 220 | ||
221 | void __devinit | 221 | void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, |
222 | pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, | 222 | struct pci_bus_region *region) |
223 | struct pci_bus_region *region) | ||
224 | { | 223 | { |
225 | struct pci_channel *hose = dev->sysdata; | 224 | struct pci_channel *hose = dev->sysdata; |
226 | unsigned long offset = 0; | 225 | unsigned long offset = 0; |
@@ -303,12 +302,41 @@ char * __devinit pcibios_setup(char *str) | |||
303 | return str; | 302 | return str; |
304 | } | 303 | } |
305 | 304 | ||
305 | static void __init | ||
306 | pcibios_bus_report_status_early(struct pci_channel *hose, | ||
307 | int top_bus, int current_bus, | ||
308 | unsigned int status_mask, int warn) | ||
309 | { | ||
310 | unsigned int pci_devfn; | ||
311 | u16 status; | ||
312 | int ret; | ||
313 | |||
314 | for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { | ||
315 | if (PCI_FUNC(pci_devfn)) | ||
316 | continue; | ||
317 | ret = early_read_config_word(hose, top_bus, current_bus, | ||
318 | pci_devfn, PCI_STATUS, &status); | ||
319 | if (ret != PCIBIOS_SUCCESSFUL) | ||
320 | continue; | ||
321 | if (status == 0xffff) | ||
322 | continue; | ||
323 | |||
324 | early_write_config_word(hose, top_bus, current_bus, | ||
325 | pci_devfn, PCI_STATUS, | ||
326 | status & status_mask); | ||
327 | if (warn) | ||
328 | printk("(%02x:%02x: %04X) ", current_bus, | ||
329 | pci_devfn, status); | ||
330 | } | ||
331 | } | ||
332 | |||
306 | /* | 333 | /* |
307 | * We can't use pci_find_device() here since we are | 334 | * We can't use pci_find_device() here since we are |
308 | * called from interrupt context. | 335 | * called from interrupt context. |
309 | */ | 336 | */ |
310 | static void pcibios_bus_report_status(struct pci_bus *bus, | 337 | static void __init_refok |
311 | unsigned int status_mask, int warn) | 338 | pcibios_bus_report_status(struct pci_bus *bus, unsigned int status_mask, |
339 | int warn) | ||
312 | { | 340 | { |
313 | struct pci_dev *dev; | 341 | struct pci_dev *dev; |
314 | 342 | ||
@@ -341,12 +369,17 @@ static void pcibios_bus_report_status(struct pci_bus *bus, | |||
341 | pcibios_bus_report_status(dev->subordinate, status_mask, warn); | 369 | pcibios_bus_report_status(dev->subordinate, status_mask, warn); |
342 | } | 370 | } |
343 | 371 | ||
344 | void pcibios_report_status(unsigned int status_mask, int warn) | 372 | void __init_refok pcibios_report_status(unsigned int status_mask, int warn) |
345 | { | 373 | { |
346 | struct pci_channel *hose; | 374 | struct pci_channel *hose; |
347 | 375 | ||
348 | for (hose = hose_head; hose; hose = hose->next) | 376 | for (hose = hose_head; hose; hose = hose->next) { |
349 | pcibios_bus_report_status(hose->bus, status_mask, warn); | 377 | if (unlikely(!hose->bus)) |
378 | pcibios_bus_report_status_early(hose, hose_head->index, | ||
379 | hose->index, status_mask, warn); | ||
380 | else | ||
381 | pcibios_bus_report_status(hose->bus, status_mask, warn); | ||
382 | } | ||
350 | } | 383 | } |
351 | 384 | ||
352 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 385 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, |