[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