[OpenWrt-Devel] [PATCH 6/6] bcm53xx: R8000 handle PEX8603 switch
Ian Kent
raven at themaw.net
Sat Jun 27 23:37:39 EDT 2015
On Tue, 2015-06-23 at 07:58 +0800, Ian Kent wrote:
> On Mon, 2015-06-22 at 18:42 +0200, Hauke Mehrtens wrote:
> >
> > On 03/10/2015 04:30 AM, Ian Kent wrote:
> > > 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.
> >
> > Could you provide some diagram how this is connected?
>
> I could try, when I get a chance, ascii art isn't my strong suit.
>
> >
> > My current assumption is that on one of the 3 PCIe ports is this switch,
> > so the scan will find this switch as a PCIe device, bu how does it go
> > from there?
>
> I can only infer from looking at sysfs, which is what I have done.
>
> Perhaps I'm not correct but it looks to me that there are is a BCM53012
> connected with two legs, one has one wireless device, and the other has
> a PEX8603 bridge which has two legs, each with a wireless device.
I still think this is the correct topology and IIRC that was also
inferred by looking at sysfs with vendor firmware installed. But maybe
my recollection fails me, I'll need to check.
>
> > From the vendor driver I would assume that that the two devices behind
> > the switch are device function 0x08 and 0x10 on the switch device.
>
> I'll have to reacquaint myself with the code before I respond to the
> comments below, its been a while.
Yes, looking at the code again they are "upstream port bus number + 1,
devfn 0x08 (aka. slot 1)" and "upstream port bus number + 2, devfn 0x10
(aka. slot 2)" so the slot number can't be used alone.
>
> >
> > Can you also post the output of lspci on your device?
>
> Don't think I have lspci in the build I was using, I'll need to add it
> and rebuild. That probably won't be until the weekend.
I'll get this when the ncurses build breakage is fixed or perhaps by
deselecting ncurses dependencies if it takes too long for it to be
fixed.
>
> >
> > >
> > > 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.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
> >
> > Please remove the ifndef, it should fail if this gets added.
OK, I put the conditional in thinking this might be added to
linux/sizes.h at some point but looking at sizes.h again we probably
won't see the 48 variant being added.
> >
> >
> > > ++
> > > ++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);
> >
> > You should use msleep(50) here.
Will do.
> >
> > > ++
> > > ++ 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) {
> >
> > Are you sure this is correct?
Looking at this again, I think it isn't correct at all.
This is the first time I've looked at the PCI subsystem so I'm not
surprised I've made mistakes, fortunately you noticed something looked
wrong.
> >
> > Why does the vendor driver use the bus number and afterwards the device
> > function for the last two spaces and you are using only the slot number?
I think my usage causes initialization settings to be applied multiple
times most of which end with incorrect settings for the bridge. There
are other problems with it too.
I don't think the domain number can (should) be used to derive the
upstream port bus number as it is in the vendor code because it assumes
a topology of earlier connected devices that might not always be
accurate.
But I think the first time this function is called it will always be
called with the upstream bus number that's needed, due to the
enumeration that's being done, so I believe I can use that.
Let me rework this using the bus number as you recommend.
I'll repost my updated patch series once I've done that.
Thanks for reviewing the patch.
Ian
> >
> > > ++ 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
> > >
>
_______________________________________________
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