From 2c5e4aa6add7eefb6663893a47cc6ffbb0241c68 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 6 Oct 2013 18:31:32 +0000 Subject: brcm47xx: b44: fix some problems with the phy * do not try initialize a unused phy * some improvements to the phylib patch * do not turn the phy off when mac is off Signed-off-by: Hauke Mehrtens SVN-Revision: 38306 --- .../patches-3.10/205-b44-add-phylib-support.patch | 312 +++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch (limited to 'target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch') diff --git a/target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch b/target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch new file mode 100644 index 0000000000..3c8e7a3714 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch @@ -0,0 +1,312 @@ +From 46e5460f446109565b3f4a0cb728171d74bce33b Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens +Date: Thu, 3 Oct 2013 22:07:11 +0200 +Subject: [PATCH 5/6] b44: add phylib support + +Most of the older home routers based on the Broadcom BCM47XX SoC series +are using a MAC that is supported by b44. On most of these routers not +the internal PHY of this MAC core is used, but a switch sometimes on an +external chip or integrated into the same SoC as the Ethernet core. +For this switch a special PHY driver is needed which should not be +integrated into b44 as the same switches are also used by other +Broadcom home networking SoCs which are using different Ethernet MAC +drivers. This was tested with the b53 switch driver which is currently +on its way to mainline. + +With this patch we scan the mdio bus when the sprom or nvram says that +the PHY address is 30, if a PHY was found at this address b44 uses it. + +This was tested with a BCM4704, BCM4712 and BCM5354. + +Signed-off-by: Hauke Mehrtens +--- + drivers/net/ethernet/broadcom/Kconfig | 1 + + drivers/net/ethernet/broadcom/b44.c | 183 ++++++++++++++++++++++++++++++++- + drivers/net/ethernet/broadcom/b44.h | 5 + + 3 files changed, 186 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/broadcom/Kconfig ++++ b/drivers/net/ethernet/broadcom/Kconfig +@@ -24,6 +24,7 @@ config B44 + select SSB + select NET_CORE + select MII ++ select PHYLIB + ---help--- + If you have a network (Ethernet) controller of this type, say Y + or M and read the Ethernet-HOWTO, available from +--- a/drivers/net/ethernet/broadcom/b44.c ++++ b/drivers/net/ethernet/broadcom/b44.c +@@ -6,6 +6,7 @@ + * Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org) + * Copyright (C) 2006 Broadcom Corporation. + * Copyright (C) 2007 Michael Buesch ++ * Copyright (C) 2013 Hauke Mehrtens + * + * Distribute under GPL. + */ +@@ -29,6 +30,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -316,6 +318,23 @@ static void b44_mdio_write_mii(struct ne + __b44_writephy(bp, phy_id, location, val); + } + ++static int b44_mdio_read_phylib(struct mii_bus *bus, int phy_id, int location) ++{ ++ u32 val; ++ struct b44 *bp = bus->priv; ++ int rc = __b44_readphy(bp, phy_id, location, &val); ++ if (rc) ++ return 0xffffffff; ++ return val; ++} ++ ++static int b44_mdio_write_phylib(struct mii_bus *bus, int phy_id, int location, ++ u16 val) ++{ ++ struct b44 *bp = bus->priv; ++ return __b44_writephy(bp, phy_id, location, val); ++} ++ + static int b44_phy_reset(struct b44 *bp) + { + u32 val; +@@ -1805,6 +1824,11 @@ static int b44_get_settings(struct net_d + { + struct b44 *bp = netdev_priv(dev); + ++ if (bp->has_phy) { ++ BUG_ON(!bp->phydev); ++ return phy_ethtool_gset(bp->phydev, cmd); ++ } ++ + cmd->supported = (SUPPORTED_Autoneg); + cmd->supported |= (SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | +@@ -1846,7 +1870,23 @@ static int b44_get_settings(struct net_d + static int b44_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) + { + struct b44 *bp = netdev_priv(dev); +- u32 speed = ethtool_cmd_speed(cmd); ++ u32 speed; ++ int ret; ++ ++ if (bp->has_phy) { ++ BUG_ON(!bp->phydev); ++ spin_lock_irq(&bp->lock); ++ if (netif_running(dev)) ++ b44_setup_phy(bp); ++ ++ ret = phy_ethtool_sset(bp->phydev, cmd); ++ ++ spin_unlock_irq(&bp->lock); ++ ++ return ret; ++ } ++ ++ speed = ethtool_cmd_speed(cmd); + + /* We do not support gigabit. */ + if (cmd->autoneg == AUTONEG_ENABLE) { +@@ -2076,7 +2116,6 @@ static const struct ethtool_ops b44_etht + + static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { +- struct mii_ioctl_data *data = if_mii(ifr); + struct b44 *bp = netdev_priv(dev); + int err = -EINVAL; + +@@ -2084,7 +2123,12 @@ static int b44_ioctl(struct net_device * + goto out; + + spin_lock_irq(&bp->lock); +- err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL); ++ if (bp->has_phy) { ++ BUG_ON(bp->phydev); ++ err = phy_mii_ioctl(bp->phydev, ifr, cmd); ++ } else { ++ err = generic_mii_ioctl(&bp->mii_if, if_mii(ifr), cmd, NULL); ++ } + spin_unlock_irq(&bp->lock); + out: + return err; +@@ -2146,6 +2190,124 @@ static const struct net_device_ops b44_n + #endif + }; + ++static void b44_adjust_link(struct net_device *dev) ++{ ++ struct b44 *bp = netdev_priv(dev); ++ struct phy_device *phydev = bp->phydev; ++ bool status_changed = 0; ++ ++ BUG_ON(!phydev); ++ ++ if (bp->old_link != phydev->link) { ++ status_changed = 1; ++ bp->old_link = phydev->link; ++ } ++ ++ /* reflect duplex change */ ++ if (phydev->link && (bp->old_duplex != phydev->duplex)) { ++ status_changed = 1; ++ bp->old_duplex = phydev->duplex; ++ } ++ ++ if (status_changed) ++ phy_print_status(phydev); ++} ++ ++static int b44_register_phy_one(struct b44 *bp) ++{ ++ struct mii_bus *mii_bus; ++ struct ssb_device *sdev = bp->sdev; ++ struct phy_device *phydev; ++ int err; ++ ++ mii_bus = mdiobus_alloc(); ++ if (!mii_bus) { ++ dev_err(sdev->dev, "mdiobus_alloc() failed\n"); ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ mii_bus->priv = bp; ++ mii_bus->read = b44_mdio_read_phylib; ++ mii_bus->write = b44_mdio_write_phylib; ++ mii_bus->name = "b44_eth_mii"; ++ mii_bus->parent = sdev->dev; ++ mii_bus->phy_mask = ~(1 << bp->phy_addr); ++ snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%x", instance); ++ mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); ++ if (!mii_bus->irq) { ++ dev_err(sdev->dev, "mii_bus irq allocation failed\n"); ++ err = -ENOMEM; ++ goto err_out_mdiobus; ++ } ++ ++ memset(mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR); ++ ++ bp->mii_bus = mii_bus; ++ ++ err = mdiobus_register(mii_bus); ++ if (err) { ++ dev_err(sdev->dev, "failed to register MII bus\n"); ++ goto err_out_mdiobus_irq; ++ } ++ ++ phydev = bp->mii_bus->phy_map[bp->phy_addr]; ++ if (!phydev) { ++ dev_err(sdev->dev, "could not find PHY at %i\n", bp->phy_addr); ++ err = -ENODEV; ++ goto err_out_mdiobus_unregister; ++ } ++ ++ err = phy_connect_direct(bp->dev, phydev, &b44_adjust_link, ++ PHY_INTERFACE_MODE_MII); ++ if (err < 0) { ++ dev_err(sdev->dev, "could not attach PHY at %i\n", ++ bp->phy_addr); ++ goto err_out_mdiobus_unregister; ++ } ++ ++ /* mask with MAC supported features */ ++ phydev->supported &= (SUPPORTED_10baseT_Half | ++ SUPPORTED_10baseT_Full | ++ SUPPORTED_100baseT_Half | ++ SUPPORTED_100baseT_Full | ++ SUPPORTED_Autoneg | ++ SUPPORTED_MII); ++ phydev->advertising = phydev->supported; ++ ++ bp->phydev = phydev; ++ bp->old_link = 0; ++ bp->old_duplex = -1; ++ bp->phy_addr = phydev->addr; ++ ++ dev_info(sdev->dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", ++ phydev->drv->name, dev_name(&phydev->dev)); ++ ++ return 0; ++ ++err_out_mdiobus_unregister: ++ mdiobus_unregister(mii_bus); ++ ++err_out_mdiobus_irq: ++ kfree(mii_bus->irq); ++ ++err_out_mdiobus: ++ mdiobus_free(mii_bus); ++ ++err_out: ++ return err; ++} ++ ++static void b44_unregister_phy_one(struct b44 *bp) ++{ ++ struct mii_bus *mii_bus = bp->mii_bus; ++ ++ phy_disconnect(bp->phydev); ++ mdiobus_unregister(mii_bus); ++ kfree(mii_bus->irq); ++ mdiobus_free(mii_bus); ++} ++ + static int b44_init_one(struct ssb_device *sdev, + const struct ssb_device_id *ent) + { +@@ -2246,10 +2408,22 @@ static int b44_init_one(struct ssb_devic + if (b44_phy_reset(bp) < 0) + bp->phy_addr = B44_PHY_ADDR_NO_LOACL_PHY; + ++ bp->has_phy = bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY; ++ ++ if (bp->has_phy) { ++ err = b44_register_phy_one(bp); ++ if (err) { ++ dev_err(sdev->dev, "Cannot register PHY, aborting\n"); ++ goto err_out_unregister_netdev; ++ } ++ } ++ + netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr); + + return 0; + ++err_out_unregister_netdev: ++ unregister_netdev(dev); + err_out_powerdown: + ssb_bus_may_powerdown(sdev->bus); + +@@ -2263,8 +2437,11 @@ out: + static void b44_remove_one(struct ssb_device *sdev) + { + struct net_device *dev = ssb_get_drvdata(sdev); ++ struct b44 *bp = netdev_priv(dev); + + unregister_netdev(dev); ++ if (bp->has_phy) ++ b44_unregister_phy_one(bp); + ssb_device_disable(sdev, 0); + ssb_bus_may_powerdown(sdev->bus); + free_netdev(dev); +--- a/drivers/net/ethernet/broadcom/b44.h ++++ b/drivers/net/ethernet/broadcom/b44.h +@@ -397,6 +397,11 @@ struct b44 { + u32 tx_pending; + u8 phy_addr; + u8 force_copybreak; ++ bool has_phy; ++ struct phy_device *phydev; ++ struct mii_bus *mii_bus; ++ int old_link; ++ int old_duplex; + struct mii_if_info mii_if; + }; + -- cgit v1.2.3