mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-25 00:11:13 +00:00
130 lines
4.2 KiB
Diff
130 lines
4.2 KiB
Diff
|
--- a/arch/arm/mach-cns3xxx/pcie.c
|
||
|
+++ b/arch/arm/mach-cns3xxx/pcie.c
|
||
|
@@ -88,6 +88,79 @@ static void __iomem *cns3xxx_pci_cfg_bas
|
||
|
return base + (where & 0xffc) + (devfn << 12);
|
||
|
}
|
||
|
|
||
|
+static inline int check_master_abort(struct pci_bus *bus, unsigned int devfn, int where)
|
||
|
+{
|
||
|
+ struct cns3xxx_pcie *cnspci = pbus_to_cnspci(bus);
|
||
|
+
|
||
|
+ /* check PCI-compatible status register after access */
|
||
|
+ if (cnspci->linked) {
|
||
|
+ void __iomem *host_base;
|
||
|
+ u32 sreg, ereg;
|
||
|
+
|
||
|
+ host_base = (void __iomem *) cnspci->cfg_bases[CNS3XXX_HOST_TYPE].virtual;
|
||
|
+ sreg = __raw_readw(host_base + 0x6) & 0xF900;
|
||
|
+ ereg = __raw_readl(host_base + 0x104); // Uncorrectable Error Status Reg
|
||
|
+
|
||
|
+ if (sreg | ereg) {
|
||
|
+ /* SREG:
|
||
|
+ * BIT15 - Detected Parity Error
|
||
|
+ * BIT14 - Signaled System Error
|
||
|
+ * BIT13 - Received Master Abort
|
||
|
+ * BIT12 - Received Target Abort
|
||
|
+ * BIT11 - Signaled Target Abort
|
||
|
+ * BIT08 - Master Data Parity Error
|
||
|
+ *
|
||
|
+ * EREG:
|
||
|
+ * BIT20 - Unsupported Request
|
||
|
+ * BIT19 - ECRC
|
||
|
+ * BIT18 - Malformed TLP
|
||
|
+ * BIT17 - Receiver Overflow
|
||
|
+ * BIT16 - Unexpected Completion
|
||
|
+ * BIT15 - Completer Abort
|
||
|
+ * BIT14 - Completion Timeout
|
||
|
+ * BIT13 - Flow Control Protocol Error
|
||
|
+ * BIT12 - Poisoned TLP
|
||
|
+ * BIT04 - Data Link Protocol Error
|
||
|
+ *
|
||
|
+ * TODO: see Documentation/pci-error-recovery.txt
|
||
|
+ * implement error_detected handler
|
||
|
+ */
|
||
|
+/*
|
||
|
+ printk("pci error: %04d:%02x:%02x.%02x sreg=0x%04x ereg=0x%08x", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), sreg, ereg);
|
||
|
+ if (sreg & BIT(15)) printk(" <PERR");
|
||
|
+ if (sreg & BIT(14)) printk(" >SERR");
|
||
|
+ if (sreg & BIT(13)) printk(" <MABRT");
|
||
|
+ if (sreg & BIT(12)) printk(" <TABRT");
|
||
|
+ if (sreg & BIT(11)) printk(" >TABRT");
|
||
|
+ if (sreg & BIT( 8)) printk(" MPERR");
|
||
|
+
|
||
|
+ if (ereg & BIT(20)) printk(" Unsup");
|
||
|
+ if (ereg & BIT(19)) printk(" ECRC");
|
||
|
+ if (ereg & BIT(18)) printk(" MTLP");
|
||
|
+ if (ereg & BIT(17)) printk(" OFLOW");
|
||
|
+ if (ereg & BIT(16)) printk(" Unex");
|
||
|
+ if (ereg & BIT(15)) printk(" ABRT");
|
||
|
+ if (ereg & BIT(14)) printk(" COMPTO");
|
||
|
+ if (ereg & BIT(13)) printk(" FLOW");
|
||
|
+ if (ereg & BIT(12)) printk(" PTLP");
|
||
|
+ if (ereg & BIT( 4)) printk(" DLINK");
|
||
|
+ printk("\n");
|
||
|
+*/
|
||
|
+ pr_debug("%s failed port%d sreg=0x%04x\n", __func__,
|
||
|
+ cnspci->hw_pci.domain, sreg);
|
||
|
+
|
||
|
+ /* make sure the status bits are reset */
|
||
|
+ __raw_writew(sreg, host_base + 6);
|
||
|
+ __raw_writel(ereg, host_base + 0x104);
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else
|
||
|
+ return 1;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
|
||
|
int where, int size, u32 *val)
|
||
|
{
|
||
|
@@ -104,6 +177,11 @@ static int cns3xxx_pci_read_config(struc
|
||
|
|
||
|
v = __raw_readl(base);
|
||
|
|
||
|
+ if (check_master_abort(bus, devfn, where)) {
|
||
|
+ printk(KERN_ERR "pci error: %04d:%02x:%02x.%02x %02x(%d)= master_abort on read\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);
|
||
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
||
|
+ }
|
||
|
+
|
||
|
if (bus->number == 0 && devfn == 0 &&
|
||
|
(where & 0xffc) == PCI_CLASS_REVISION) {
|
||
|
/*
|
||
|
@@ -133,11 +211,19 @@ static int cns3xxx_pci_write_config(stru
|
||
|
return PCIBIOS_SUCCESSFUL;
|
||
|
|
||
|
v = __raw_readl(base);
|
||
|
+ if (check_master_abort(bus, devfn, where)) {
|
||
|
+ printk(KERN_ERR "pci error: %04d:%02x:%02x.%02x %02x(%d)=0x%08x master_abort on read\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
|
||
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
||
|
+ }
|
||
|
|
||
|
v &= ~(mask << shift);
|
||
|
v |= (val & mask) << shift;
|
||
|
|
||
|
__raw_writel(v, base);
|
||
|
+ if (check_master_abort(bus, devfn, where)) {
|
||
|
+ printk(KERN_ERR "pci error: %04d:%02x:%02x.%02x %02x(%d)=0x%08x master_abort on write\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
|
||
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
||
|
+ }
|
||
|
|
||
|
return PCIBIOS_SUCCESSFUL;
|
||
|
}
|
||
|
@@ -315,8 +401,14 @@ static void __init cns3xxx_pcie_hw_init(
|
||
|
static int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr,
|
||
|
struct pt_regs *regs)
|
||
|
{
|
||
|
+#if 0
|
||
|
+/* R14_ABORT = PC+4 for XSCALE but not ARM11MPCORE
|
||
|
+ * ignore imprecise aborts and use PCI-compatible Status register to
|
||
|
+ * determine errors instead
|
||
|
+ */
|
||
|
if (fsr & (1 << 10))
|
||
|
regs->ARM_pc += 4;
|
||
|
+#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|