aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch')
-rw-r--r--target/linux/brcm47xx/patches-3.10/205-b44-add-phylib-support.patch312
1 files changed, 312 insertions, 0 deletions
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 <hauke@hauke-m.de>
+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 <hauke@hauke-m.de>
+---
+ 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 <m@bues.ch>
++ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Distribute under GPL.
+ */
+@@ -29,6 +30,7 @@
+ #include <linux/dma-mapping.h>
+ #include <linux/ssb/ssb.h>
+ #include <linux/slab.h>
++#include <linux/phy.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/io.h>
+@@ -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;
+ };
+