Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Florian Fainelli and committed by
David S. Miller
7b635da8 8e346e15

+51 -4
+1
drivers/net/ethernet/broadcom/genet/bcmgenet.h
··· 594 594 wait_queue_head_t wq; 595 595 struct phy_device *phydev; 596 596 struct device_node *phy_dn; 597 + struct device_node *mdio_dn; 597 598 struct mii_bus *mii_bus; 598 599 u16 gphy_rev; 599 600 struct clk *clk_eee;
+50 -4
drivers/net/ethernet/broadcom/genet/bcmmii.c
··· 408 408 return 0; 409 409 } 410 410 411 + /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with 412 + * their internal MDIO management controller making them fail to successfully 413 + * be read from or written to for the first transaction. We insert a dummy 414 + * BMSR read here to make sure that phy_get_device() and get_phy_id() can 415 + * correctly read the PHY MII_PHYSID1/2 registers and successfully register a 416 + * PHY device for this peripheral. 417 + * 418 + * Once the PHY driver is registered, we can workaround subsequent reads from 419 + * there (e.g: during system-wide power management). 420 + * 421 + * bus->reset is invoked before mdiobus_scan during mdiobus_register and is 422 + * therefore the right location to stick that workaround. Since we do not want 423 + * to read from non-existing PHYs, we either use bus->phy_mask or do a manual 424 + * Device Tree scan to limit the search area. 425 + */ 426 + static int bcmgenet_mii_bus_reset(struct mii_bus *bus) 427 + { 428 + struct net_device *dev = bus->priv; 429 + struct bcmgenet_priv *priv = netdev_priv(dev); 430 + struct device_node *np = priv->mdio_dn; 431 + struct device_node *child = NULL; 432 + u32 read_mask = 0; 433 + int addr = 0; 434 + 435 + if (!np) { 436 + read_mask = 1 << priv->phy_addr; 437 + } else { 438 + for_each_available_child_of_node(np, child) { 439 + addr = of_mdio_parse_addr(&dev->dev, child); 440 + if (addr < 0) 441 + continue; 442 + 443 + read_mask |= 1 << addr; 444 + } 445 + } 446 + 447 + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { 448 + if (read_mask & 1 << addr) { 449 + dev_dbg(&dev->dev, "Workaround for PHY @ %d\n", addr); 450 + mdiobus_read(bus, addr, MII_BMSR); 451 + } 452 + } 453 + 454 + return 0; 455 + } 456 + 411 457 static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) 412 458 { 413 459 struct mii_bus *bus; ··· 473 427 bus->parent = &priv->pdev->dev; 474 428 bus->read = bcmgenet_mii_read; 475 429 bus->write = bcmgenet_mii_write; 430 + bus->reset = bcmgenet_mii_bus_reset; 476 431 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", 477 432 priv->pdev->name, priv->pdev->id); 478 433 ··· 490 443 { 491 444 struct device_node *dn = priv->pdev->dev.of_node; 492 445 struct device *kdev = &priv->pdev->dev; 493 - struct device_node *mdio_dn; 494 446 char *compat; 495 447 int ret; 496 448 ··· 497 451 if (!compat) 498 452 return -ENOMEM; 499 453 500 - mdio_dn = of_find_compatible_node(dn, NULL, compat); 454 + priv->mdio_dn = of_find_compatible_node(dn, NULL, compat); 501 455 kfree(compat); 502 - if (!mdio_dn) { 456 + if (!priv->mdio_dn) { 503 457 dev_err(kdev, "unable to find MDIO bus node\n"); 504 458 return -ENODEV; 505 459 } 506 460 507 - ret = of_mdiobus_register(priv->mii_bus, mdio_dn); 461 + ret = of_mdiobus_register(priv->mii_bus, priv->mdio_dn); 508 462 if (ret) { 509 463 dev_err(kdev, "failed to register MDIO bus\n"); 510 464 return ret;