diff options
| author | Paul Mundt <lethal@linux-sh.org> | 2010-02-01 02:39:46 -0500 |
|---|---|---|
| committer | Paul Mundt <lethal@linux-sh.org> | 2010-02-01 02:39:46 -0500 |
| commit | ef407beefbd9928792ccc93857e408e0057bc17b (patch) | |
| tree | f98fc1e6eaa7d00b578d759f612d815cd7a7391a | |
| parent | bcf39352eb9e9026f7a1028d4bce3707b65f104b (diff) | |
sh: Hook up ERR/PERR/SERR detection for SH7780 PCI host controllers.
These were never handled before, so implement some common infrastructure
to support them, then make use of that in the SH7780-specific code. In
practice there is little here that can not be generalized for SH4 parts,
which will be an incremental change as the 7780/7751 code is gradually
unified.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
| -rw-r--r-- | arch/sh/drivers/pci/common.c | 79 | ||||
| -rw-r--r-- | arch/sh/drivers/pci/ops-sh4.c | 2 | ||||
| -rw-r--r-- | arch/sh/drivers/pci/pci-sh7780.c | 203 | ||||
| -rw-r--r-- | arch/sh/drivers/pci/pci.c | 51 | ||||
| -rw-r--r-- | arch/sh/include/asm/pci.h | 11 |
5 files changed, 317 insertions, 29 deletions
diff --git a/arch/sh/drivers/pci/common.c b/arch/sh/drivers/pci/common.c index f67c946a8612..25aec005da18 100644 --- a/arch/sh/drivers/pci/common.c +++ b/arch/sh/drivers/pci/common.c | |||
| @@ -1,4 +1,6 @@ | |||
| 1 | #include <linux/pci.h> | 1 | #include <linux/pci.h> |
| 2 | #include <linux/interrupt.h> | ||
| 3 | #include <linux/timer.h> | ||
| 2 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
| 3 | 5 | ||
| 4 | static int __init | 6 | static int __init |
| @@ -62,3 +64,80 @@ int __init pci_is_66mhz_capable(struct pci_channel *hose, | |||
| 62 | 64 | ||
| 63 | return cap66 > 0; | 65 | return cap66 > 0; |
| 64 | } | 66 | } |
| 67 | |||
| 68 | static void pcibios_enable_err(unsigned long __data) | ||
| 69 | { | ||
| 70 | struct pci_channel *hose = (struct pci_channel *)__data; | ||
| 71 | |||
| 72 | del_timer(&hose->err_timer); | ||
| 73 | printk(KERN_DEBUG "PCI: re-enabling error IRQ.\n"); | ||
| 74 | enable_irq(hose->err_irq); | ||
| 75 | } | ||
| 76 | |||
| 77 | static void pcibios_enable_serr(unsigned long __data) | ||
| 78 | { | ||
| 79 | struct pci_channel *hose = (struct pci_channel *)__data; | ||
| 80 | |||
| 81 | del_timer(&hose->serr_timer); | ||
| 82 | printk(KERN_DEBUG "PCI: re-enabling system error IRQ.\n"); | ||
| 83 | enable_irq(hose->serr_irq); | ||
| 84 | } | ||
| 85 | |||
| 86 | void pcibios_enable_timers(struct pci_channel *hose) | ||
| 87 | { | ||
| 88 | if (hose->err_irq) { | ||
| 89 | init_timer(&hose->err_timer); | ||
| 90 | hose->err_timer.data = (unsigned long)hose; | ||
| 91 | hose->err_timer.function = pcibios_enable_err; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (hose->serr_irq) { | ||
| 95 | init_timer(&hose->serr_timer); | ||
| 96 | hose->serr_timer.data = (unsigned long)hose; | ||
| 97 | hose->serr_timer.function = pcibios_enable_serr; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | /* | ||
| 102 | * A simple handler for the regular PCI status errors, called from IRQ | ||
| 103 | * context. | ||
| 104 | */ | ||
| 105 | unsigned int pcibios_handle_status_errors(unsigned long addr, | ||
| 106 | unsigned int status, | ||
| 107 | struct pci_channel *hose) | ||
| 108 | { | ||
| 109 | unsigned int cmd = 0; | ||
| 110 | |||
| 111 | if (status & PCI_STATUS_REC_MASTER_ABORT) { | ||
| 112 | printk(KERN_DEBUG "PCI: master abort, pc=0x%08lx\n", addr); | ||
| 113 | cmd |= PCI_STATUS_REC_MASTER_ABORT; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (status & PCI_STATUS_REC_TARGET_ABORT) { | ||
| 117 | printk(KERN_DEBUG "PCI: target abort: "); | ||
| 118 | pcibios_report_status(PCI_STATUS_REC_TARGET_ABORT | | ||
| 119 | PCI_STATUS_SIG_TARGET_ABORT | | ||
| 120 | PCI_STATUS_REC_MASTER_ABORT, 1); | ||
| 121 | printk("\n"); | ||
| 122 | |||
| 123 | cmd |= PCI_STATUS_REC_TARGET_ABORT; | ||
| 124 | } | ||
| 125 | |||
| 126 | if (status & (PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY)) { | ||
| 127 | printk(KERN_DEBUG "PCI: parity error detected: "); | ||
| 128 | pcibios_report_status(PCI_STATUS_PARITY | | ||
| 129 | PCI_STATUS_DETECTED_PARITY, 1); | ||
| 130 | printk("\n"); | ||
| 131 | |||
| 132 | cmd |= PCI_STATUS_PARITY | PCI_STATUS_DETECTED_PARITY; | ||
| 133 | |||
| 134 | /* Now back off of the IRQ for awhile */ | ||
| 135 | if (hose->err_irq) { | ||
| 136 | disable_irq(hose->err_irq); | ||
| 137 | hose->err_timer.expires = jiffies + HZ; | ||
| 138 | add_timer(&hose->err_timer); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | return cmd; | ||
| 143 | } | ||
diff --git a/arch/sh/drivers/pci/ops-sh4.c b/arch/sh/drivers/pci/ops-sh4.c index e55e81a71727..0b81999fb88b 100644 --- a/arch/sh/drivers/pci/ops-sh4.c +++ b/arch/sh/drivers/pci/ops-sh4.c | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | * Direct access to PCI hardware... | 16 | * Direct access to PCI hardware... |
| 17 | */ | 17 | */ |
| 18 | #define CONFIG_CMD(bus, devfn, where) \ | 18 | #define CONFIG_CMD(bus, devfn, where) \ |
| 19 | (P1SEG | (bus->number << 16) | (devfn << 8) | (where & ~3)) | 19 | (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) |
| 20 | 20 | ||
| 21 | static DEFINE_SPINLOCK(sh4_pci_lock); | 21 | static DEFINE_SPINLOCK(sh4_pci_lock); |
| 22 | 22 | ||
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c index 0e0ddd67e6e1..86373314f458 100644 --- a/arch/sh/drivers/pci/pci-sh7780.c +++ b/arch/sh/drivers/pci/pci-sh7780.c | |||
| @@ -11,6 +11,9 @@ | |||
| 11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
| 12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
| 13 | #include <linux/pci.h> | 13 | #include <linux/pci.h> |
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/timer.h> | ||
| 16 | #include <linux/irq.h> | ||
| 14 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
| 15 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
| 16 | #include <linux/log2.h> | 19 | #include <linux/log2.h> |
| @@ -39,8 +42,165 @@ static struct pci_channel sh7780_pci_controller = { | |||
| 39 | .io_resource = &sh7785_io_resource, | 42 | .io_resource = &sh7785_io_resource, |
| 40 | .io_offset = 0x00000000, | 43 | .io_offset = 0x00000000, |
| 41 | .io_map_base = SH7780_PCI_IO_BASE, | 44 | .io_map_base = SH7780_PCI_IO_BASE, |
| 45 | .serr_irq = evt2irq(0xa00), | ||
| 46 | .err_irq = evt2irq(0xaa0), | ||
| 42 | }; | 47 | }; |
| 43 | 48 | ||
| 49 | struct pci_errors { | ||
| 50 | unsigned int mask; | ||
| 51 | const char *str; | ||
| 52 | } pci_arbiter_errors[] = { | ||
| 53 | { SH4_PCIAINT_MBKN, "master broken" }, | ||
| 54 | { SH4_PCIAINT_TBTO, "target bus time out" }, | ||
| 55 | { SH4_PCIAINT_MBTO, "master bus time out" }, | ||
| 56 | { SH4_PCIAINT_TABT, "target abort" }, | ||
| 57 | { SH4_PCIAINT_MABT, "master abort" }, | ||
| 58 | { SH4_PCIAINT_RDPE, "read data parity error" }, | ||
| 59 | { SH4_PCIAINT_WDPE, "write data parity error" }, | ||
| 60 | }, pci_interrupt_errors[] = { | ||
| 61 | { SH4_PCIINT_MLCK, "master lock error" }, | ||
| 62 | { SH4_PCIINT_TABT, "target-target abort" }, | ||
| 63 | { SH4_PCIINT_TRET, "target retry time out" }, | ||
| 64 | { SH4_PCIINT_MFDE, "master function disable erorr" }, | ||
| 65 | { SH4_PCIINT_PRTY, "address parity error" }, | ||
| 66 | { SH4_PCIINT_SERR, "SERR" }, | ||
| 67 | { SH4_PCIINT_TWDP, "data parity error for target write" }, | ||
| 68 | { SH4_PCIINT_TRDP, "PERR detected for target read" }, | ||
| 69 | { SH4_PCIINT_MTABT, "target abort for master" }, | ||
| 70 | { SH4_PCIINT_MMABT, "master abort for master" }, | ||
| 71 | { SH4_PCIINT_MWPD, "master write data parity error" }, | ||
| 72 | { SH4_PCIINT_MRPD, "master read data parity error" }, | ||
| 73 | }; | ||
| 74 | |||
| 75 | static irqreturn_t sh7780_pci_err_irq(int irq, void *dev_id) | ||
| 76 | { | ||
| 77 | struct pci_channel *hose = dev_id; | ||
| 78 | unsigned long addr; | ||
| 79 | unsigned int status; | ||
| 80 | unsigned int cmd; | ||
| 81 | int i; | ||
| 82 | |||
| 83 | addr = __raw_readl(hose->reg_base + SH4_PCIALR); | ||
| 84 | |||
| 85 | /* | ||
| 86 | * Handle status errors. | ||
| 87 | */ | ||
| 88 | status = __raw_readw(hose->reg_base + PCI_STATUS); | ||
| 89 | if (status & (PCI_STATUS_PARITY | | ||
| 90 | PCI_STATUS_DETECTED_PARITY | | ||
| 91 | PCI_STATUS_SIG_TARGET_ABORT | | ||
| 92 | PCI_STATUS_REC_TARGET_ABORT | | ||
| 93 | PCI_STATUS_REC_MASTER_ABORT)) { | ||
| 94 | cmd = pcibios_handle_status_errors(addr, status, hose); | ||
| 95 | if (likely(cmd)) | ||
| 96 | __raw_writew(cmd, hose->reg_base + PCI_STATUS); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* | ||
| 100 | * Handle arbiter errors. | ||
| 101 | */ | ||
| 102 | status = __raw_readl(hose->reg_base + SH4_PCIAINT); | ||
| 103 | for (i = cmd = 0; i < ARRAY_SIZE(pci_arbiter_errors); i++) { | ||
| 104 | if (status & pci_arbiter_errors[i].mask) { | ||
| 10 | |||
