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 <hauke@hauke-m.de>

SVN-Revision: 38306
This commit is contained in:
Hauke Mehrtens 2013-10-06 18:31:32 +00:00
parent 048fc8f0c1
commit 2c5e4aa6ad
9 changed files with 526 additions and 354 deletions

View file

@ -1,9 +1,6 @@
From: Hauke Mehrtens <hauke@hauke-m.de>
b44: add support for Byte Queue Limits
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -622,6 +622,7 @@ static void b44_timer(unsigned long __op
@@ -596,6 +596,7 @@ static void b44_timer(unsigned long __op
static void b44_tx(struct b44 *bp)
{
u32 cur, cons;
@ -11,7 +8,7 @@ b44: add support for Byte Queue Limits
cur = br32(bp, B44_DMATX_STAT) & DMATX_STAT_CDMASK;
cur /= sizeof(struct dma_desc);
@@ -638,9 +639,14 @@ static void b44_tx(struct b44 *bp)
@@ -612,9 +613,14 @@ static void b44_tx(struct b44 *bp)
skb->len,
DMA_TO_DEVICE);
rp->skb = NULL;
@ -26,7 +23,7 @@ b44: add support for Byte Queue Limits
bp->tx_cons = cons;
if (netif_queue_stopped(bp->dev) &&
TX_BUFFS_AVAIL(bp) > B44_TX_WAKEUP_THRESH)
@@ -1044,6 +1050,8 @@ static netdev_tx_t b44_start_xmit(struct
@@ -1018,6 +1024,8 @@ static netdev_tx_t b44_start_xmit(struct
if (bp->flags & B44_FLAG_REORDER_BUG)
br32(bp, B44_DMATX_PTR);
@ -35,11 +32,11 @@ b44: add support for Byte Queue Limits
if (TX_BUFFS_AVAIL(bp) < 1)
netif_stop_queue(dev);
@@ -1442,6 +1450,8 @@ static void b44_init_hw(struct b44 *bp,
@@ -1416,6 +1424,8 @@ static void b44_init_hw(struct b44 *bp,
val = br32(bp, B44_ENET_CTRL);
bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE));
+
+
+ netdev_reset_queue(bp->dev);
}

View file

@ -0,0 +1,87 @@
From 991b6722fb727b6e2a98e7e8b57176ac68626110 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Thu, 3 Oct 2013 20:41:29 +0200
Subject: [PATCH 2/5] b44: rename B44_PHY_ADDR_NO_PHY to
B44_PHY_ADDR_NO_LOCAL_PHY
The PHY address 30 means there is no local PHY, but there could be an
external PHY like a switch connected via MII. This is the case on most
embedded home routers where this driver is used.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/net/ethernet/broadcom/b44.c | 12 ++++++------
drivers/net/ethernet/broadcom/b44.h | 6 +++---
2 files changed, 9 insertions(+), 9 deletions(-)
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -284,7 +284,7 @@ static int __b44_writephy(struct b44 *bp
static inline int b44_readphy(struct b44 *bp, int reg, u32 *val)
{
- if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ if (bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY)
return 0;
return __b44_readphy(bp, bp->phy_addr, reg, val);
@@ -292,7 +292,7 @@ static inline int b44_readphy(struct b44
static inline int b44_writephy(struct b44 *bp, int reg, u32 val)
{
- if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ if (bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY)
return 0;
return __b44_writephy(bp, bp->phy_addr, reg, val);
@@ -321,7 +321,7 @@ static int b44_phy_reset(struct b44 *bp)
u32 val;
int err;
- if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ if (bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY)
return 0;
err = b44_writephy(bp, MII_BMCR, BMCR_RESET);
if (err)
@@ -423,7 +423,7 @@ static int b44_setup_phy(struct b44 *bp)
b44_wap54g10_workaround(bp);
- if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
+ if (bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY)
return 0;
if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0)
goto out;
@@ -521,7 +521,7 @@ static void b44_check_phy(struct b44 *bp
{
u32 bmsr, aux;
- if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) {
+ if (bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY) {
bp->flags |= B44_FLAG_100_BASE_T;
bp->flags |= B44_FLAG_FULL_DUPLEX;
if (!netif_carrier_ok(bp->dev)) {
@@ -2238,7 +2238,7 @@ static int b44_init_one(struct ssb_devic
/* do a phy reset to test if there is an active phy */
if (b44_phy_reset(bp) < 0)
- bp->phy_addr = B44_PHY_ADDR_NO_PHY;
+ bp->phy_addr = B44_PHY_ADDR_NO_LOACL_PHY;
netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
--- a/drivers/net/ethernet/broadcom/b44.h
+++ b/drivers/net/ethernet/broadcom/b44.h
@@ -280,9 +280,9 @@ struct ring_info {
dma_addr_t mapping;
};
-#define B44_MCAST_TABLE_SIZE 32
-#define B44_PHY_ADDR_NO_PHY 30
-#define B44_MDC_RATIO 5000000
+#define B44_MCAST_TABLE_SIZE 32
+#define B44_PHY_ADDR_NO_LOACL_PHY 30 /* no local phy regs */
+#define B44_MDC_RATIO 5000000
#define B44_STAT_REG_DECLARE \
_B44(tx_good_octets) \

View file

@ -0,0 +1,40 @@
From 1bfdc259652abe22a587fd6d856c1b71168cccb2 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Thu, 3 Oct 2013 20:49:10 +0200
Subject: [PATCH 3/5] b44: abort when no PHY is available at all
When the phy address is 31, this means that there is no PHY connected
to this MAC at all, no internal and no external PHY. Reading these PHY
registers causes a system reset on some routers.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/net/ethernet/broadcom/b44.c | 6 ++++++
drivers/net/ethernet/broadcom/b44.h | 1 +
2 files changed, 7 insertions(+)
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -2207,6 +2207,12 @@ static int b44_init_one(struct ssb_devic
goto err_out_powerdown;
}
+ if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) {
+ dev_err(sdev->dev, "No PHY present on this MAC, aborting\n");
+ err = -ENODEV;
+ goto err_out_powerdown;
+ }
+
bp->mii_if.dev = dev;
bp->mii_if.mdio_read = b44_mii_read;
bp->mii_if.mdio_write = b44_mii_write;
--- a/drivers/net/ethernet/broadcom/b44.h
+++ b/drivers/net/ethernet/broadcom/b44.h
@@ -282,6 +282,7 @@ struct ring_info {
#define B44_MCAST_TABLE_SIZE 32
#define B44_PHY_ADDR_NO_LOACL_PHY 30 /* no local phy regs */
+#define B44_PHY_ADDR_NO_PHY 31 /* no phy present at all */
#define B44_MDC_RATIO 5000000
#define B44_STAT_REG_DECLARE \

View file

@ -0,0 +1,48 @@
From 6dcaccfc1e0046632dd54d91b6f679fee7f841bc Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 6 Oct 2013 15:31:04 +0200
Subject: [PATCH 4/5] b44: rename b44_mii_{read,write} to
b44_mdio_{read,write}_mii
The next patch will add these functions for phylib, and we should
rename the old ones before. This now indicates that these functions are
used for the mdio registers and on the mii interface.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/net/ethernet/broadcom/b44.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -299,7 +299,7 @@ static inline int b44_writephy(struct b4
}
/* miilib interface */
-static int b44_mii_read(struct net_device *dev, int phy_id, int location)
+static int b44_mdio_read_mii(struct net_device *dev, int phy_id, int location)
{
u32 val;
struct b44 *bp = netdev_priv(dev);
@@ -309,8 +309,8 @@ static int b44_mii_read(struct net_devic
return val;
}
-static void b44_mii_write(struct net_device *dev, int phy_id, int location,
- int val)
+static void b44_mdio_write_mii(struct net_device *dev, int phy_id, int location,
+ int val)
{
struct b44 *bp = netdev_priv(dev);
__b44_writephy(bp, phy_id, location, val);
@@ -2214,8 +2214,8 @@ static int b44_init_one(struct ssb_devic
}
bp->mii_if.dev = dev;
- bp->mii_if.mdio_read = b44_mii_read;
- bp->mii_if.mdio_write = b44_mii_write;
+ bp->mii_if.mdio_read = b44_mdio_read_mii;
+ bp->mii_if.mdio_write = b44_mdio_write_mii;
bp->mii_if.phy_id = bp->phy_addr;
bp->mii_if.phy_id_mask = 0x1f;
bp->mii_if.reg_num_mask = 0x1f;

View file

@ -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;
};

View file

@ -0,0 +1,28 @@
From 444044a410d4cf3b6dd462f2c9352d56039d9e07 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Sun, 6 Oct 2013 17:58:24 +0200
Subject: [PATCH 6/6] b44: activate PHY when MAC is off
Without this patch we can not access the PHY when the MAC is switched
off. This PHY access is needed to configure the switch, which is done
through PHY registers.
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
---
drivers/net/ethernet/broadcom/b44.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -1358,7 +1358,10 @@ static void b44_halt(struct b44 *bp)
bw32(bp, B44_MAC_CTRL, MAC_CTRL_PHY_PDOWN);
/* now reset the chip, but without enabling the MAC&PHY
* part of it. This has to be done _after_ we shut down the PHY */
- b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
+ if (bp->has_phy)
+ b44_chip_reset(bp, B44_CHIP_RESET_FULL);
+ else
+ b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL);
}
/* bp->lock is held. */

View file

@ -1,6 +1,6 @@
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -410,10 +410,34 @@ static void b44_wap54g10_workaround(stru
@@ -429,10 +429,34 @@ static void b44_wap54g10_workaround(stru
error:
pr_warning("PHY: cannot reset MII transceiver isolate bit\n");
}
@ -12,14 +12,14 @@
+
+ /* Toshiba WRC-1000, Siemens SE505 v1, Askey RT-210W, RT-220W */
+ if (sdev->bus->sprom.board_num == 100) {
+ bp->phy_addr = B44_PHY_ADDR_NO_PHY;
+ bp->phy_addr = B44_PHY_ADDR_NO_LOACL_PHY;
+ } else {
+ /* WL-HDD */
+ if (bcm47xx_nvram_getenv("hardware_version", buf, sizeof(buf)) >= 0 &&
+ !strncmp(buf, "WL300-", strlen("WL300-"))) {
+ if (sdev->bus->sprom.et0phyaddr == 0 &&
+ sdev->bus->sprom.et1phyaddr == 1)
+ bp->phy_addr = B44_PHY_ADDR_NO_PHY;
+ bp->phy_addr = B44_PHY_ADDR_NO_LOACL_PHY;
+ }
+ }
+ return;
@ -35,15 +35,15 @@
#endif
static int b44_setup_phy(struct b44 *bp)
@@ -422,6 +446,7 @@ static int b44_setup_phy(struct b44 *bp)
@@ -441,6 +465,7 @@ static int b44_setup_phy(struct b44 *bp)
int err;
b44_wap54g10_workaround(bp);
+ b44_bcm47xx_workarounds(bp);
if (bp->phy_addr == B44_PHY_ADDR_NO_PHY)
if (bp->phy_addr == B44_PHY_ADDR_NO_LOACL_PHY)
return 0;
@@ -2101,6 +2126,8 @@ static int b44_get_invariants(struct b44
@@ -2158,6 +2183,8 @@ static int b44_get_invariants(struct b44
* valid PHY address. */
bp->phy_addr &= 0x1F;

View file

@ -1,15 +0,0 @@
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -187,10 +187,11 @@ static int b44_wait_bit(struct b44 *bp,
udelay(10);
}
if (i == timeout) {
+#if 0
if (net_ratelimit())
netdev_err(bp->dev, "BUG! Timeout waiting for bit %08x of register %lx to %s\n",
bit, reg, clear ? "clear" : "set");
-
+#endif
return -ENODEV;
}
return 0;

View file

@ -1,325 +0,0 @@
--- 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
@@ -29,6 +29,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>
@@ -300,21 +301,23 @@ static inline int b44_writephy(struct b4
}
/* miilib interface */
-static int b44_mii_read(struct net_device *dev, int phy_id, int location)
+static int b44_mii_read(struct mii_bus *bus, int phy_id, int location)
{
u32 val;
- struct b44 *bp = netdev_priv(dev);
+ struct b44 *bp = bus->priv;
int rc = __b44_readphy(bp, phy_id, location, &val);
if (rc)
return 0xffffffff;
return val;
}
-static void b44_mii_write(struct net_device *dev, int phy_id, int location,
- int val)
+static int b44_mii_write(struct mii_bus *bus, int phy_id, int location,
+ u16 val)
{
- struct b44 *bp = netdev_priv(dev);
+ struct b44 *bp = bus->priv;
__b44_writephy(bp, phy_id, location, val);
+
+ return 0;
}
static int b44_phy_reset(struct b44 *bp)
@@ -1831,102 +1834,24 @@ static int b44_get_settings(struct net_d
{
struct b44 *bp = netdev_priv(dev);
- cmd->supported = (SUPPORTED_Autoneg);
- cmd->supported |= (SUPPORTED_100baseT_Half |
- SUPPORTED_100baseT_Full |
- SUPPORTED_10baseT_Half |
- SUPPORTED_10baseT_Full |
- SUPPORTED_MII);
-
- cmd->advertising = 0;
- if (bp->flags & B44_FLAG_ADV_10HALF)
- cmd->advertising |= ADVERTISED_10baseT_Half;
- if (bp->flags & B44_FLAG_ADV_10FULL)
- cmd->advertising |= ADVERTISED_10baseT_Full;
- if (bp->flags & B44_FLAG_ADV_100HALF)
- cmd->advertising |= ADVERTISED_100baseT_Half;
- if (bp->flags & B44_FLAG_ADV_100FULL)
- cmd->advertising |= ADVERTISED_100baseT_Full;
- cmd->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
- ethtool_cmd_speed_set(cmd, ((bp->flags & B44_FLAG_100_BASE_T) ?
- SPEED_100 : SPEED_10));
- cmd->duplex = (bp->flags & B44_FLAG_FULL_DUPLEX) ?
- DUPLEX_FULL : DUPLEX_HALF;
- cmd->port = 0;
- cmd->phy_address = bp->phy_addr;
- cmd->transceiver = (bp->flags & B44_FLAG_INTERNAL_PHY) ?
- XCVR_INTERNAL : XCVR_EXTERNAL;
- cmd->autoneg = (bp->flags & B44_FLAG_FORCE_LINK) ?
- AUTONEG_DISABLE : AUTONEG_ENABLE;
- if (cmd->autoneg == AUTONEG_ENABLE)
- cmd->advertising |= ADVERTISED_Autoneg;
- if (!netif_running(dev)){
- ethtool_cmd_speed_set(cmd, 0);
- cmd->duplex = 0xff;
- }
- cmd->maxtxpkt = 0;
- cmd->maxrxpkt = 0;
- return 0;
+ return phy_ethtool_gset(bp->phydev, cmd);
}
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);
-
- /* We do not support gigabit. */
- if (cmd->autoneg == AUTONEG_ENABLE) {
- if (cmd->advertising &
- (ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full))
- return -EINVAL;
- } else if ((speed != SPEED_100 &&
- speed != SPEED_10) ||
- (cmd->duplex != DUPLEX_HALF &&
- cmd->duplex != DUPLEX_FULL)) {
- return -EINVAL;
- }
+ int ret;
spin_lock_irq(&bp->lock);
- if (cmd->autoneg == AUTONEG_ENABLE) {
- bp->flags &= ~(B44_FLAG_FORCE_LINK |
- B44_FLAG_100_BASE_T |
- B44_FLAG_FULL_DUPLEX |
- B44_FLAG_ADV_10HALF |
- B44_FLAG_ADV_10FULL |
- B44_FLAG_ADV_100HALF |
- B44_FLAG_ADV_100FULL);
- if (cmd->advertising == 0) {
- bp->flags |= (B44_FLAG_ADV_10HALF |
- B44_FLAG_ADV_10FULL |
- B44_FLAG_ADV_100HALF |
- B44_FLAG_ADV_100FULL);
- } else {
- if (cmd->advertising & ADVERTISED_10baseT_Half)
- bp->flags |= B44_FLAG_ADV_10HALF;
- if (cmd->advertising & ADVERTISED_10baseT_Full)
- bp->flags |= B44_FLAG_ADV_10FULL;
- if (cmd->advertising & ADVERTISED_100baseT_Half)
- bp->flags |= B44_FLAG_ADV_100HALF;
- if (cmd->advertising & ADVERTISED_100baseT_Full)
- bp->flags |= B44_FLAG_ADV_100FULL;
- }
- } else {
- bp->flags |= B44_FLAG_FORCE_LINK;
- bp->flags &= ~(B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX);
- if (speed == SPEED_100)
- bp->flags |= B44_FLAG_100_BASE_T;
- if (cmd->duplex == DUPLEX_FULL)
- bp->flags |= B44_FLAG_FULL_DUPLEX;
- }
-
if (netif_running(dev))
b44_setup_phy(bp);
+ ret = phy_ethtool_sset(bp->phydev, cmd);
+
spin_unlock_irq(&bp->lock);
- return 0;
+ return ret;
}
static void b44_get_ringparam(struct net_device *dev,
@@ -2102,20 +2027,74 @@ 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;
if (!netif_running(dev))
goto out;
+ if (!bp->phydev)
+ return -EINVAL;
+
spin_lock_irq(&bp->lock);
- err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL);
+ err = phy_mii_ioctl(bp->phydev, ifr, cmd);
spin_unlock_irq(&bp->lock);
out:
return err;
}
+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_mii_probe(struct net_device *dev)
+{
+ struct b44 *bp = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ char phy_id[MII_BUS_ID_SIZE + 3];
+
+ /* connect to PHY */
+ snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
+ bp->mii_bus->id, bp->phy_addr);
+
+ phydev = phy_connect(dev, phy_id, &b44_adjust_link,
+ PHY_INTERFACE_MODE_MII);
+ if (IS_ERR(phydev)) {
+ netdev_err(dev, "could not attach PHY: %s\n", phy_id);
+ bp->phy_addr = B44_PHY_ADDR_NO_PHY;
+ return PTR_ERR(phydev);
+ }
+
+ bp->phydev = phydev;
+ bp->old_link = 0;
+ bp->old_duplex = -1;
+ bp->phy_addr = phydev->addr;
+
+ netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n",
+ phydev->drv->name, dev_name(&phydev->dev));
+
+ return 0;
+}
+
static int b44_get_invariants(struct b44 *bp)
{
struct ssb_device *sdev = bp->sdev;
@@ -2235,12 +2214,40 @@ static int b44_init_one(struct ssb_devic
goto err_out_powerdown;
}
- bp->mii_if.dev = dev;
- bp->mii_if.mdio_read = b44_mii_read;
- bp->mii_if.mdio_write = b44_mii_write;
- bp->mii_if.phy_id = bp->phy_addr;
- bp->mii_if.phy_id_mask = 0x1f;
- bp->mii_if.reg_num_mask = 0x1f;
+ bp->mii_bus = mdiobus_alloc();
+ if (!bp->mii_bus) {
+ dev_err(sdev->dev, "mdiobus_alloc() failed\n");
+ err = -ENOMEM;
+ goto err_out_powerdown;
+ }
+
+ bp->mii_bus->priv = bp;
+ bp->mii_bus->read = b44_mii_read;
+ bp->mii_bus->write = b44_mii_write;
+ bp->mii_bus->name = "b44_eth_mii";
+ bp->mii_bus->parent = sdev->dev;
+ bp->mii_bus->phy_mask = ~(1 << bp->phy_addr);
+ snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%x", instance);
+ bp->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!bp->mii_bus->irq) {
+ dev_err(sdev->dev, "mii_bus irq allocation failed\n");
+ err = -ENOMEM;
+ goto err_out_mdiobus;
+ }
+
+ memset(bp->mii_bus->irq, PHY_POLL, sizeof(int) * PHY_MAX_ADDR);
+
+ err = mdiobus_register(bp->mii_bus);
+ if (err) {
+ dev_err(sdev->dev, "failed to register MII bus\n");
+ goto err_out_mdiobus_irq;
+ }
+
+ err = b44_mii_probe(dev);
+ if (err) {
+ dev_err(sdev->dev, "failed to probe MII bus\n");
+ goto err_out_mdiobus_unregister;
+ }
/* By default, advertise all speed/duplex settings. */
bp->flags |= (B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL |
@@ -2272,6 +2279,16 @@ static int b44_init_one(struct ssb_devic
return 0;
+
+err_out_mdiobus_unregister:
+ mdiobus_unregister(bp->mii_bus);
+
+err_out_mdiobus_irq:
+ kfree(bp->mii_bus->irq);
+
+err_out_mdiobus:
+ mdiobus_free(bp->mii_bus);
+
err_out_powerdown:
ssb_bus_may_powerdown(sdev->bus);
@@ -2285,8 +2302,12 @@ 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);
+ mdiobus_unregister(bp->mii_bus);
+ kfree(bp->mii_bus->irq);
+ mdiobus_free(bp->mii_bus);
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
@@ -396,7 +396,10 @@ struct b44 {
u32 tx_pending;
u8 phy_addr;
u8 force_copybreak;
- struct mii_if_info mii_if;
+ struct phy_device *phydev;
+ struct mii_bus *mii_bus;
+ int old_link;
+ int old_duplex;
};
#endif /* _B44_H */