[OpenWrt-Devel] [PATCH 6/6] bcm53xx: R8000 handle PEX8603 switch
Ian Kent
raven at themaw.net
Mon Mar 9 23:30:38 EDT 2015
The Netgear R8000 has a PEX8603 connected to the BCM53012 and if
it isn't configured during the bus scan the PCI layer goes crazy
trying to configure phantom devices.
Signed-off-by: Ian Kent <raven at themaw.net>
---
.../172-bcm5301x-R8000-handle-PEX8603-switch.patch | 225 ++++++++++++++++++++
.../172-bcm5301x-R8000-handle-PEX8603-switch.patch | 225 ++++++++++++++++++++
2 files changed, 450 insertions(+)
create mode 100644 target/linux/bcm53xx/patches-3.14/172-bcm5301x-R8000-handle-PEX8603-switch.patch
create mode 100644 target/linux/bcm53xx/patches-3.18/172-bcm5301x-R8000-handle-PEX8603-switch.patch
diff --git a/target/linux/bcm53xx/patches-3.14/172-bcm5301x-R8000-handle-PEX8603-switch.patch b/target/linux/bcm53xx/patches-3.14/172-bcm5301x-R8000-handle-PEX8603-switch.patch
new file mode 100644
index 0000000..fc606b4
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.14/172-bcm5301x-R8000-handle-PEX8603-switch.patch
@@ -0,0 +1,225 @@
+bcm53xx: R8000 handle PEX8603 switch
+
+The Netgear R8000 has a PEX8603 which, if not configured at
+bus probe results in quite a few phantom devices as it doesn't
+respond properly to configuration requests. The device needs
+to be configured when seen.
+
+Signed-off-by: Ian Kent <raven at themaw.net>
+
+--- a/drivers/pci/host/pci-host-bcm5301x.c
++++ b/drivers/pci/host/pci-host-bcm5301x.c
+@@ -29,6 +29,21 @@
+ #define PCI_TARGET_LINK_SPEED_GEN2 0x2
+ #define PCI_TARGET_LINK_SPEED_GEN1 0x1
+
++#define PCI_MAX_BUS 4
++#define PLX_PRIM_SEC_BUS_NUM (0x00000201 | (PCI_MAX_BUS << 16))
++
++#ifndef SZ_48M
++#define SZ_48M (SZ_32M + SZ_16M)
++#endif
++
++struct pex86xx_info {
++ u8 busno; /* Upstream bus PEX is on */
++ u8 slot; /* Upstream slot PEX is at */
++ u16 active; /* Active port count */
++ u16 ports; /* Active port bit map */
++};
++struct pex86xx_info pex8603;
++
+ static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+ {
+ struct pci_sys_data *sys = pdev->sysdata;
+@@ -115,6 +130,39 @@ static int bcma_pcie2_read_config_pci(st
+ struct pci_sys_data *sys = bus->sysdata;
+ struct bcma_device *bdev = sys->private_data;
+
++ /* The PEX8603 won't return sensible values to the PCI layer so
++ * we have to do that ourselves.
++ */
++ if (pex8603.busno) {
++ u16 slot = PCI_SLOT(devfn);
++
++ /* Not the PEX upstream slot */
++ if (pex8603.busno == bus->number && pex8603.slot != slot)
++ goto done;
++
++ /* Not the PEX downstream bus? */
++ if (bus->number < pex8603.busno ||
++ bus->number > pex8603.busno + 1)
++ goto done;
++
++ switch (bus->number - pex8603.busno) {
++ case 0:
++ /* Upstream port */
++ break;
++
++ case 1:
++ /* PEX8603, not present for slots other than 1 or 2 */
++ if (!(slot == 1 || slot == 2)) {
++ *val = 0xffffffff;
++ return PCIBIOS_SUCCESSFUL;
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++done:
+ *val = bcma_pcie2_read_config(bdev, bus->number, devfn, where, size);
+
+ return PCIBIOS_SUCCESSFUL;
+@@ -126,6 +174,37 @@ static int bcma_pcie2_write_config_pci(s
+ struct pci_sys_data *sys = bus->sysdata;
+ struct bcma_device *bdev = sys->private_data;
+
++ /* Don't try and set anything on the PEX8603 if it isn't
++ * valid.
++ */
++ if (pex8603.busno) {
++ u16 slot = PCI_SLOT(devfn);
++
++ /* Not the PEX upstream slot */
++ if (pex8603.busno == bus->number && pex8603.slot != slot)
++ goto done;
++
++ /* Not the PEX downstream bus? */
++ if (bus->number < pex8603.busno ||
++ bus->number > pex8603.busno + 1)
++ goto done;
++
++ switch (bus->number - pex8603.busno) {
++ case 0:
++ /* Upstream port */
++ break;
++
++ case 1:
++ /* PEX8603 slots only slots 1 and 2 present */
++ if (!(slot == 1 || slot == 2))
++ return PCIBIOS_SUCCESSFUL;
++ break;
++
++ default:
++ break;
++ }
++ }
++done:
+ bcma_pcie2_write_config(bdev, bus->number, devfn, where, size, val);
+
+ return PCIBIOS_SUCCESSFUL;
+@@ -147,6 +226,113 @@ static void bcma_pcie2_fixup_class(struc
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class);
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
+
++static void bcma_pcie2_pex_switch_setup(struct pci_dev *dev)
++{
++ struct pci_sys_data *sys = dev->sysdata;
++ struct bcma_device *bdev = sys->private_data;
++ unsigned char busno = dev->bus->number;
++ unsigned int devfn = dev->devfn;
++ unsigned int slot = PCI_SLOT(devfn);
++ u32 addr = bdev->addr_s[0];
++ u32 tmp32;
++ u16 tmp16;
++
++ tmp32 = bcma_pcie2_read_config(bdev, busno, devfn, 0x100, 4);
++ if (!tmp32) {
++ dev_info(&bdev->dev, "failed to read PEX switch\n");
++ return;
++ }
++
++ /* Debug control register. */
++ tmp32 = bcma_pcie2_read_config(bdev, busno, devfn, 0x1dc, 4);
++ tmp32 &= ~(1<<22);
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x1dc, 4, tmp32);
++
++ /* Set GPIO enable. */
++ tmp32 = bcma_pcie2_read_config(bdev, busno, devfn, 0x62c, 4);
++ tmp32 &= ~((1 << 0) | (1 << 1) | (1 << 3));
++ tmp32 |= ((1 << 4) | (1 << 5) | (1 << 7));
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x62c, 4, tmp32);
++
++ mdelay(50);
++
++ tmp32 |= ((1<<0)|(1<<1));
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x62c, 4, tmp32);
++
++ /* Bus master */
++ tmp16 = bcma_pcie2_read_config(bdev, busno, devfn, 0x4, 2);
++ tmp16 |= 0x06;
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x4, 2, tmp16);
++
++ switch (slot) {
++ case 0:
++ /* Upstream port busno and slot */
++ pex8603.busno = busno;
++ pex8603.slot = slot;
++
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, 0x18, 4, PLX_PRIM_SEC_BUS_NUM);
++
++ /* TODO: We need to scan all outgoing windows,
++ * to look for a base limit pair for this register.
++ */
++ /* MEM_BASE, MEM_LIM require 1MB alignment */
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_BASE, 2,
++ addr >> 16);
++ BUG_ON(((addr + SZ_32M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_LIMIT, 2,
++ (addr + SZ_32M) >> 16);
++ break;
++
++ case 1:
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, 0x18, 4,
++ (((busno + slot) << 16) |
++ ((busno + slot) << 8) | busno));
++ BUG_ON(((addr + SZ_48M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_BASE, 4,
++ (addr + SZ_48M) >> 16);
++ BUG_ON(((addr + SZ_48M + SZ_32M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_LIMIT, 4,
++ (addr + SZ_48M + SZ_32M) >> 16);
++
++ /* Mark port bit number as active if successful */
++ tmp16 = bcma_pcie2_read_config(bdev, busno, devfn, 0x7A, 2);
++ if (tmp16 & PCI_EXP_LNKSTA_DLLLA) {
++ pex8603.ports |= ((1 << (slot - 1)) & 0xffff);
++ pex8603.active++;
++ }
++ break;
++
++ case 2:
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, 0x18, 4,
++ (((busno + slot) << 16) |
++ ((busno + slot) << 8) | busno));
++ BUG_ON(((addr + (SZ_48M * 2)) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_BASE, 4,
++ (addr + (SZ_48M * 2)) >> 16);
++ BUG_ON(((addr + (SZ_48M * 2) + SZ_32M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_LIMIT, 4,
++ (addr + (SZ_48M * 2) + SZ_32M) >> 16);
++
++ /* Mark port bit number as active if successful */
++ tmp16 = bcma_pcie2_read_config(bdev, busno, devfn, 0x7A, 2);
++ if (tmp16 & PCI_EXP_LNKSTA_DLLLA) {
++ pex8603.ports |= ((1 << (slot - 1)) & 0xffff);
++ pex8603.active++;
++ }
++ break;
++ }
++}
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8603, bcma_pcie2_pex_switch_setup);
++
+ /*
+ * Check link status, return 0 if link is up in RC mode,
+ * otherwise return non-zero
diff --git a/target/linux/bcm53xx/patches-3.18/172-bcm5301x-R8000-handle-PEX8603-switch.patch b/target/linux/bcm53xx/patches-3.18/172-bcm5301x-R8000-handle-PEX8603-switch.patch
new file mode 100644
index 0000000..fc606b4
--- /dev/null
+++ b/target/linux/bcm53xx/patches-3.18/172-bcm5301x-R8000-handle-PEX8603-switch.patch
@@ -0,0 +1,225 @@
+bcm53xx: R8000 handle PEX8603 switch
+
+The Netgear R8000 has a PEX8603 which, if not configured at
+bus probe results in quite a few phantom devices as it doesn't
+respond properly to configuration requests. The device needs
+to be configured when seen.
+
+Signed-off-by: Ian Kent <raven at themaw.net>
+
+--- a/drivers/pci/host/pci-host-bcm5301x.c
++++ b/drivers/pci/host/pci-host-bcm5301x.c
+@@ -29,6 +29,21 @@
+ #define PCI_TARGET_LINK_SPEED_GEN2 0x2
+ #define PCI_TARGET_LINK_SPEED_GEN1 0x1
+
++#define PCI_MAX_BUS 4
++#define PLX_PRIM_SEC_BUS_NUM (0x00000201 | (PCI_MAX_BUS << 16))
++
++#ifndef SZ_48M
++#define SZ_48M (SZ_32M + SZ_16M)
++#endif
++
++struct pex86xx_info {
++ u8 busno; /* Upstream bus PEX is on */
++ u8 slot; /* Upstream slot PEX is at */
++ u16 active; /* Active port count */
++ u16 ports; /* Active port bit map */
++};
++struct pex86xx_info pex8603;
++
+ static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+ {
+ struct pci_sys_data *sys = pdev->sysdata;
+@@ -115,6 +130,39 @@ static int bcma_pcie2_read_config_pci(st
+ struct pci_sys_data *sys = bus->sysdata;
+ struct bcma_device *bdev = sys->private_data;
+
++ /* The PEX8603 won't return sensible values to the PCI layer so
++ * we have to do that ourselves.
++ */
++ if (pex8603.busno) {
++ u16 slot = PCI_SLOT(devfn);
++
++ /* Not the PEX upstream slot */
++ if (pex8603.busno == bus->number && pex8603.slot != slot)
++ goto done;
++
++ /* Not the PEX downstream bus? */
++ if (bus->number < pex8603.busno ||
++ bus->number > pex8603.busno + 1)
++ goto done;
++
++ switch (bus->number - pex8603.busno) {
++ case 0:
++ /* Upstream port */
++ break;
++
++ case 1:
++ /* PEX8603, not present for slots other than 1 or 2 */
++ if (!(slot == 1 || slot == 2)) {
++ *val = 0xffffffff;
++ return PCIBIOS_SUCCESSFUL;
++ }
++ break;
++
++ default:
++ break;
++ }
++ }
++done:
+ *val = bcma_pcie2_read_config(bdev, bus->number, devfn, where, size);
+
+ return PCIBIOS_SUCCESSFUL;
+@@ -126,6 +174,37 @@ static int bcma_pcie2_write_config_pci(s
+ struct pci_sys_data *sys = bus->sysdata;
+ struct bcma_device *bdev = sys->private_data;
+
++ /* Don't try and set anything on the PEX8603 if it isn't
++ * valid.
++ */
++ if (pex8603.busno) {
++ u16 slot = PCI_SLOT(devfn);
++
++ /* Not the PEX upstream slot */
++ if (pex8603.busno == bus->number && pex8603.slot != slot)
++ goto done;
++
++ /* Not the PEX downstream bus? */
++ if (bus->number < pex8603.busno ||
++ bus->number > pex8603.busno + 1)
++ goto done;
++
++ switch (bus->number - pex8603.busno) {
++ case 0:
++ /* Upstream port */
++ break;
++
++ case 1:
++ /* PEX8603 slots only slots 1 and 2 present */
++ if (!(slot == 1 || slot == 2))
++ return PCIBIOS_SUCCESSFUL;
++ break;
++
++ default:
++ break;
++ }
++ }
++done:
+ bcma_pcie2_write_config(bdev, bus->number, devfn, where, size, val);
+
+ return PCIBIOS_SUCCESSFUL;
+@@ -147,6 +226,113 @@ static void bcma_pcie2_fixup_class(struc
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class);
+ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
+
++static void bcma_pcie2_pex_switch_setup(struct pci_dev *dev)
++{
++ struct pci_sys_data *sys = dev->sysdata;
++ struct bcma_device *bdev = sys->private_data;
++ unsigned char busno = dev->bus->number;
++ unsigned int devfn = dev->devfn;
++ unsigned int slot = PCI_SLOT(devfn);
++ u32 addr = bdev->addr_s[0];
++ u32 tmp32;
++ u16 tmp16;
++
++ tmp32 = bcma_pcie2_read_config(bdev, busno, devfn, 0x100, 4);
++ if (!tmp32) {
++ dev_info(&bdev->dev, "failed to read PEX switch\n");
++ return;
++ }
++
++ /* Debug control register. */
++ tmp32 = bcma_pcie2_read_config(bdev, busno, devfn, 0x1dc, 4);
++ tmp32 &= ~(1<<22);
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x1dc, 4, tmp32);
++
++ /* Set GPIO enable. */
++ tmp32 = bcma_pcie2_read_config(bdev, busno, devfn, 0x62c, 4);
++ tmp32 &= ~((1 << 0) | (1 << 1) | (1 << 3));
++ tmp32 |= ((1 << 4) | (1 << 5) | (1 << 7));
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x62c, 4, tmp32);
++
++ mdelay(50);
++
++ tmp32 |= ((1<<0)|(1<<1));
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x62c, 4, tmp32);
++
++ /* Bus master */
++ tmp16 = bcma_pcie2_read_config(bdev, busno, devfn, 0x4, 2);
++ tmp16 |= 0x06;
++ bcma_pcie2_write_config(bdev, busno, devfn, 0x4, 2, tmp16);
++
++ switch (slot) {
++ case 0:
++ /* Upstream port busno and slot */
++ pex8603.busno = busno;
++ pex8603.slot = slot;
++
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, 0x18, 4, PLX_PRIM_SEC_BUS_NUM);
++
++ /* TODO: We need to scan all outgoing windows,
++ * to look for a base limit pair for this register.
++ */
++ /* MEM_BASE, MEM_LIM require 1MB alignment */
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_BASE, 2,
++ addr >> 16);
++ BUG_ON(((addr + SZ_32M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_LIMIT, 2,
++ (addr + SZ_32M) >> 16);
++ break;
++
++ case 1:
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, 0x18, 4,
++ (((busno + slot) << 16) |
++ ((busno + slot) << 8) | busno));
++ BUG_ON(((addr + SZ_48M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_BASE, 4,
++ (addr + SZ_48M) >> 16);
++ BUG_ON(((addr + SZ_48M + SZ_32M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_LIMIT, 4,
++ (addr + SZ_48M + SZ_32M) >> 16);
++
++ /* Mark port bit number as active if successful */
++ tmp16 = bcma_pcie2_read_config(bdev, busno, devfn, 0x7A, 2);
++ if (tmp16 & PCI_EXP_LNKSTA_DLLLA) {
++ pex8603.ports |= ((1 << (slot - 1)) & 0xffff);
++ pex8603.active++;
++ }
++ break;
++
++ case 2:
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, 0x18, 4,
++ (((busno + slot) << 16) |
++ ((busno + slot) << 8) | busno));
++ BUG_ON(((addr + (SZ_48M * 2)) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_BASE, 4,
++ (addr + (SZ_48M * 2)) >> 16);
++ BUG_ON(((addr + (SZ_48M * 2) + SZ_32M) >> 16) & 0xf);
++ bcma_pcie2_write_config(bdev, busno,
++ devfn, PCI_MEMORY_LIMIT, 4,
++ (addr + (SZ_48M * 2) + SZ_32M) >> 16);
++
++ /* Mark port bit number as active if successful */
++ tmp16 = bcma_pcie2_read_config(bdev, busno, devfn, 0x7A, 2);
++ if (tmp16 & PCI_EXP_LNKSTA_DLLLA) {
++ pex8603.ports |= ((1 << (slot - 1)) & 0xffff);
++ pex8603.active++;
++ }
++ break;
++ }
++}
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8603, bcma_pcie2_pex_switch_setup);
++
+ /*
+ * Check link status, return 0 if link is up in RC mode,
+ * otherwise return non-zero
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list