Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c80c3af authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bcm7xxx_workaround'



Florian Fainelli says:

====================
net: phy: bcm7xxx initial read/write workaround

This patch series fixes occasional BCM7xxx PHY driver binding failure due
to a harware bug where the first read or write does not come out of the PHY
MDIO management controller.

Since we have two different MDIO controllers using this PHY, a similar
need to be replicated in GENET and UniMAC MDIO.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8031612d d8e704e4
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -594,6 +594,7 @@ struct bcmgenet_priv {
	wait_queue_head_t wq;
	wait_queue_head_t wq;
	struct phy_device *phydev;
	struct phy_device *phydev;
	struct device_node *phy_dn;
	struct device_node *phy_dn;
	struct device_node *mdio_dn;
	struct mii_bus *mii_bus;
	struct mii_bus *mii_bus;
	u16 gphy_rev;
	u16 gphy_rev;
	struct clk *clk_eee;
	struct clk *clk_eee;
+50 −4
Original line number Original line Diff line number Diff line
@@ -408,6 +408,52 @@ static int bcmgenet_mii_probe(struct net_device *dev)
	return 0;
	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)
static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv)
{
{
	struct mii_bus *bus;
	struct mii_bus *bus;
@@ -427,6 +473,7 @@ static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv)
	bus->parent = &priv->pdev->dev;
	bus->parent = &priv->pdev->dev;
	bus->read = bcmgenet_mii_read;
	bus->read = bcmgenet_mii_read;
	bus->write = bcmgenet_mii_write;
	bus->write = bcmgenet_mii_write;
	bus->reset = bcmgenet_mii_bus_reset;
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d",
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d",
		 priv->pdev->name, priv->pdev->id);
		 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_node *dn = priv->pdev->dev.of_node;
	struct device *kdev = &priv->pdev->dev;
	struct device *kdev = &priv->pdev->dev;
	struct device_node *mdio_dn;
	char *compat;
	char *compat;
	int ret;
	int ret;


@@ -451,14 +497,14 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
	if (!compat)
	if (!compat)
		return -ENOMEM;
		return -ENOMEM;


	mdio_dn = of_find_compatible_node(dn, NULL, compat);
	priv->mdio_dn = of_find_compatible_node(dn, NULL, compat);
	kfree(compat);
	kfree(compat);
	if (!mdio_dn) {
	if (!priv->mdio_dn) {
		dev_err(kdev, "unable to find MDIO bus node\n");
		dev_err(kdev, "unable to find MDIO bus node\n");
		return -ENODEV;
		return -ENODEV;
	}
	}


	ret = of_mdiobus_register(priv->mii_bus, mdio_dn);
	ret = of_mdiobus_register(priv->mii_bus, priv->mdio_dn);
	if (ret) {
	if (ret) {
		dev_err(kdev, "failed to register MDIO bus\n");
		dev_err(kdev, "failed to register MDIO bus\n");
		return ret;
		return ret;
+7 −0
Original line number Original line Diff line number Diff line
@@ -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",
	pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
		     dev_name(&phydev->dev), phydev->drv->name, rev, patch);
		     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) {
	switch (rev) {
	case 0xb0:
	case 0xb0:
		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
		ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
+43 −0
Original line number Original line Diff line number Diff line
@@ -120,6 +120,48 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
	return 0;
	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)
static int unimac_mdio_probe(struct platform_device *pdev)
{
{
	struct unimac_mdio_priv *priv;
	struct unimac_mdio_priv *priv;
@@ -155,6 +197,7 @@ static int unimac_mdio_probe(struct platform_device *pdev)
	bus->parent = &pdev->dev;
	bus->parent = &pdev->dev;
	bus->read = unimac_mdio_read;
	bus->read = unimac_mdio_read;
	bus->write = unimac_mdio_write;
	bus->write = unimac_mdio_write;
	bus->reset = unimac_mdio_reset;
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);


	bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
	bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);