From 8e346e1594bab6c06b6b4d2938c881729b03041d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 26 Jun 2015 10:39:04 -0700 Subject: [PATCH 1/3] net: phy: bcm7xxx: workaround MDIO management controller initial read The initial MDIO read or write towards the BCM7xxx integrated PHY may fail, workaround this by inserting a dummy MII_BMSR read to force the MDIO management controller to see at least one valid transaction and get out of stuck state out of reset. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 4dea85bfc545b..6b701b3ded749 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -246,6 +246,13 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", dev_name(&phydev->dev), phydev->drv->name, rev, patch); + /* Dummy read to a register to workaround an issue upon reset where the + * internal inverter may not allow the first MDIO transaction to pass + * the MDIO management controller and make us return 0xffff for such + * reads. + */ + phy_read(phydev, MII_BMSR); + switch (rev) { case 0xb0: ret = bcm7xxx_28nm_b0_afe_config_init(phydev); From 7b635da86821005cd131369b7a4268df8067cc6c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 26 Jun 2015 10:39:05 -0700 Subject: [PATCH 2/3] net: bcmgenet: workaround initial read failures for integrated PHYs All BCM7xxx integrated Gigabit PHYs have an issue in their MDIO management controller which will make the initial read or write to them to fail and return 0xffff. This is a real issue as the typical first thing we do is read from MII_PHYSID1 and MII_PHYSID2 from get_phy_id() to register a driver for these PHYs. Coupled with the workaround in drivers/net/phy/bcm7xxx.c, this workaround for the MDIO bus controller consists in scanning the list of PHYs to do this initial read workaround for as part of the MDIO bus reset routine which is invoked prior to mdiobus_scan(). Once we have a proper PHY driver/device registered, all workarounds are located there (e.g: power management suspend/resume calls). Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/genet/bcmgenet.h | 1 + drivers/net/ethernet/broadcom/genet/bcmmii.c | 54 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 6f2887a5e0be6..6159deab8c985 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -594,6 +594,7 @@ struct bcmgenet_priv { wait_queue_head_t wq; struct phy_device *phydev; struct device_node *phy_dn; + struct device_node *mdio_dn; struct mii_bus *mii_bus; u16 gphy_rev; struct clk *clk_eee; diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 6bef04e2f7354..adf23d2ac4888 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -408,6 +408,52 @@ static int bcmgenet_mii_probe(struct net_device *dev) return 0; } +/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with + * their internal MDIO management controller making them fail to successfully + * be read from or written to for the first transaction. We insert a dummy + * BMSR read here to make sure that phy_get_device() and get_phy_id() can + * correctly read the PHY MII_PHYSID1/2 registers and successfully register a + * PHY device for this peripheral. + * + * Once the PHY driver is registered, we can workaround subsequent reads from + * there (e.g: during system-wide power management). + * + * bus->reset is invoked before mdiobus_scan during mdiobus_register and is + * therefore the right location to stick that workaround. Since we do not want + * to read from non-existing PHYs, we either use bus->phy_mask or do a manual + * Device Tree scan to limit the search area. + */ +static int bcmgenet_mii_bus_reset(struct mii_bus *bus) +{ + struct net_device *dev = bus->priv; + struct bcmgenet_priv *priv = netdev_priv(dev); + struct device_node *np = priv->mdio_dn; + struct device_node *child = NULL; + u32 read_mask = 0; + int addr = 0; + + if (!np) { + read_mask = 1 << priv->phy_addr; + } else { + for_each_available_child_of_node(np, child) { + addr = of_mdio_parse_addr(&dev->dev, child); + if (addr < 0) + continue; + + read_mask |= 1 << addr; + } + } + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { + if (read_mask & 1 << addr) { + dev_dbg(&dev->dev, "Workaround for PHY @ %d\n", addr); + mdiobus_read(bus, addr, MII_BMSR); + } + } + + return 0; +} + static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) { struct mii_bus *bus; @@ -427,6 +473,7 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) bus->parent = &priv->pdev->dev; bus->read = bcmgenet_mii_read; bus->write = bcmgenet_mii_write; + bus->reset = bcmgenet_mii_bus_reset; snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", priv->pdev->name, priv->pdev->id); @@ -443,7 +490,6 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) { struct device_node *dn = priv->pdev->dev.of_node; struct device *kdev = &priv->pdev->dev; - struct device_node *mdio_dn; char *compat; int ret; @@ -451,14 +497,14 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) if (!compat) return -ENOMEM; - mdio_dn = of_find_compatible_node(dn, NULL, compat); + priv->mdio_dn = of_find_compatible_node(dn, NULL, compat); kfree(compat); - if (!mdio_dn) { + if (!priv->mdio_dn) { dev_err(kdev, "unable to find MDIO bus node\n"); return -ENODEV; } - ret = of_mdiobus_register(priv->mii_bus, mdio_dn); + ret = of_mdiobus_register(priv->mii_bus, priv->mdio_dn); if (ret) { dev_err(kdev, "failed to register MDIO bus\n"); return ret; From d8e704e461c217918cf236f49276e469580afbef Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 26 Jun 2015 10:39:06 -0700 Subject: [PATCH 3/3] net: phy: mdio-bcm-unimac: workaround initial read failures for integrated PHYs All BCM7xxx integrated Gigabit PHYs have an issue in their MDIO management controller which will make the initial read or write to them to fail and return 0xffff. This is a real issue as the typical first thing we do is read from MII_PHYSID1 and MII_PHYSID2 from get_phy_id() to register a driver for these PHYs. Coupled with the workaround in drivers/net/phy/bcm7xxx.c, this workaround for the MDIO bus controller consists in scanning the list of PHYs to do this initial read workaround for as part of the MDIO bus reset routine which is invoked prior to mdiobus_scan(). Once we have a proper PHY driver/device registered, all workarounds are located there (e.g: power management suspend/resume calls). Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio-bcm-unimac.c | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index fc7abc50b4f17..6a52a7f0fa0dc 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -120,6 +120,48 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, return 0; } +/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with + * their internal MDIO management controller making them fail to successfully + * be read from or written to for the first transaction. We insert a dummy + * BMSR read here to make sure that phy_get_device() and get_phy_id() can + * correctly read the PHY MII_PHYSID1/2 registers and successfully register a + * PHY device for this peripheral. + * + * Once the PHY driver is registered, we can workaround subsequent reads from + * there (e.g: during system-wide power management). + * + * bus->reset is invoked before mdiobus_scan during mdiobus_register and is + * therefore the right location to stick that workaround. Since we do not want + * to read from non-existing PHYs, we either use bus->phy_mask or do a manual + * Device Tree scan to limit the search area. + */ +static int unimac_mdio_reset(struct mii_bus *bus) +{ + struct device_node *np = bus->dev.of_node; + struct device_node *child; + u32 read_mask = 0; + int addr; + + if (!np) { + read_mask = ~bus->phy_mask; + } else { + for_each_available_child_of_node(np, child) { + addr = of_mdio_parse_addr(&bus->dev, child); + if (addr < 0) + continue; + + read_mask |= 1 << addr; + } + } + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { + if (read_mask & 1 << addr) + mdiobus_read(bus, addr, MII_BMSR); + } + + return 0; +} + static int unimac_mdio_probe(struct platform_device *pdev) { struct unimac_mdio_priv *priv; @@ -155,6 +197,7 @@ static int unimac_mdio_probe(struct platform_device *pdev) bus->parent = &pdev->dev; bus->read = unimac_mdio_read; bus->write = unimac_mdio_write; + bus->reset = unimac_mdio_reset; snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);