[PATCH] ep93xx-eth: convert to phylib
Florian Fainelli
florian at openwrt.org
Sun Jun 5 13:57:36 EDT 2011
ep93xx-eth lacked support for monitoring link status changes, with this
patch, link changes are now watched and reported correctly.
Signed-off-by: Florian Fainelli <florian at openwrt.org>
---
diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
index 39e1c0d..53d075e 100644
--- a/drivers/net/arm/Kconfig
+++ b/drivers/net/arm/Kconfig
@@ -51,7 +51,7 @@ config ARM_KS8695_ETHER
config EP93XX_ETH
tristate "EP93xx Ethernet support"
depends on ARM && ARCH_EP93XX
- select MII
+ select PHYLIB
help
This is a driver for the ethernet hardware included in EP93xx CPUs.
Say Y if you are building a kernel for EP93xx based devices.
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
index 5a77001..4669b6c 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/arm/ep93xx_eth.c
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/phy.h>
#include <mach/hardware.h>
@@ -175,7 +176,11 @@ struct ep93xx_priv
struct net_device *dev;
struct napi_struct napi;
- struct mii_if_info mii;
+ struct mii_bus *mii_bus;
+ struct phy_device *phydev;
+ int old_link;
+ int old_duplex;
+ unsigned int phy_addr;
u8 mdc_divisor;
};
@@ -186,8 +191,9 @@ struct ep93xx_priv
#define wrw(ep, off, val) __raw_writew((val), (ep)->base_addr + (off))
#define wrl(ep, off, val) __raw_writel((val), (ep)->base_addr + (off))
-static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg)
+static int ep93xx_mdiobus_read(struct mii_bus *bus, int phy_id, int reg)
{
+ struct net_device *dev = bus->priv;
struct ep93xx_priv *ep = netdev_priv(dev);
int data;
int i;
@@ -210,8 +216,9 @@ static int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg)
return data;
}
-static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data)
+static int ep93xx_mdiobus_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
{
+ struct net_device *dev = bus->priv;
struct ep93xx_priv *ep = netdev_priv(dev);
int i;
@@ -224,8 +231,84 @@ static void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int d
msleep(1);
}
- if (i == 10)
+ if (i == 10) {
pr_info("mdio write timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ep93xx_mdiobus_reset(struct mii_bus *bus)
+{
+ return 0;
+}
+
+static void ep93xx_adjust_link(struct net_device *dev)
+{
+ struct ep93xx_priv * ep = netdev_priv(dev);
+ struct phy_device *phydev = ep->phydev;
+ int status_changed = 0;
+
+ BUG_ON(!phydev);
+
+ if (ep->old_link != phydev->link) {
+ status_changed = 1;
+ ep->old_link = phydev->link;
+ }
+
+ if (phydev->link && (ep->old_duplex != phydev->duplex)) {
+ status_changed = 1;
+ ep->old_duplex = phydev->duplex;
+ }
+
+ if (status_changed) {
+ pr_info("%s: link %s", dev->name, phydev->link ?
+ "UP" : "DOWN");
+ if (phydev->link)
+ pr_cont(" - %d/%s", phydev->speed,
+ DUPLEX_FULL == phydev->duplex ? "full" : "half");
+ pr_cont("\n");
+ }
+}
+
+static int ep93xx_mii_probe(struct net_device *dev)
+{
+ struct ep93xx_priv * ep = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+
+ /* use platform supplied PHY address if valid */
+ if (ep->phy_addr)
+ phydev = ep->mii_bus->phy_map[ep->phy_addr];
+ else
+ phydev = phy_find_first(ep->mii_bus);
+
+ if (!phydev) {
+ pr_err("no PHY found\n");
+ return -ENODEV;
+ }
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev), &ep93xx_adjust_link,
+ 0, PHY_INTERFACE_MODE_MII);
+ if (IS_ERR(phydev)) {
+ pr_err("could not attach to PHY\n");
+ return PTR_ERR(phydev);
+ }
+
+ /* mask with MAC supported features */
+ phydev->supported &= PHY_BASIC_FEATURES;
+ phydev->advertising = phydev->supported;
+ ep->phydev = phydev;
+ ep->old_link = 0;
+ ep->old_duplex = -1;
+
+ pr_info("attached PHY driver [%s] "
+ "(mii_bus:phy_addr=%s)\n",
+ phydev->drv->name, dev_name(&phydev->dev));
+
+ phy_start(phydev);
+
+ return 0;
}
static int ep93xx_rx(struct net_device *dev, int processed, int budget)
@@ -570,7 +653,7 @@ static int ep93xx_start_hw(struct net_device *dev)
wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9));
/* Does the PHY support preamble suppress? */
- if ((ep93xx_mdio_read(dev, ep->mii.phy_id, MII_BMSR) & 0x0040) != 0)
+ if ((ep93xx_mdiobus_read(ep->mii_bus, ep->phy_addr, MII_BMSR) & 0x0040) != 0)
wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9) | (1 << 8));
/* Receive descriptor ring. */
@@ -703,9 +786,11 @@ static int ep93xx_close(struct net_device *dev)
static int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct ep93xx_priv *ep = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(ifr);
- return generic_mii_ioctl(&ep->mii, data, cmd, NULL);
+ if (!ep->phydev)
+ return -ENODEV;
+
+ return phy_mii_ioctl(ep->phydev, ifr, cmd);
}
static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
@@ -717,25 +802,19 @@ static void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i
static int ep93xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct ep93xx_priv *ep = netdev_priv(dev);
- return mii_ethtool_gset(&ep->mii, cmd);
+ return phy_ethtool_gset(ep->phydev, cmd);
}
static int ep93xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct ep93xx_priv *ep = netdev_priv(dev);
- return mii_ethtool_sset(&ep->mii, cmd);
+ return phy_ethtool_sset(ep->phydev, cmd);
}
static int ep93xx_nway_reset(struct net_device *dev)
{
struct ep93xx_priv *ep = netdev_priv(dev);
- return mii_nway_restart(&ep->mii);
-}
-
-static u32 ep93xx_get_link(struct net_device *dev)
-{
- struct ep93xx_priv *ep = netdev_priv(dev);
- return mii_link_ok(&ep->mii);
+ return phy_start_aneg(ep->phydev);
}
static const struct ethtool_ops ep93xx_ethtool_ops = {
@@ -743,7 +822,7 @@ static const struct ethtool_ops ep93xx_ethtool_ops = {
.get_settings = ep93xx_get_settings,
.set_settings = ep93xx_set_settings,
.nway_reset = ep93xx_nway_reset,
- .get_link = ep93xx_get_link,
+ .get_link = ethtool_op_get_link,
};
static const struct net_device_ops ep93xx_netdev_ops = {
@@ -789,6 +868,11 @@ static int ep93xx_eth_remove(struct platform_device *pdev)
/* @@@ Force down. */
unregister_netdev(dev);
+
+ mdiobus_unregister(ep->mii_bus);
+ kfree(ep->mii_bus->irq);
+ mdiobus_free(ep->mii_bus);
+
ep93xx_free_buffers(ep);
if (ep->base_addr != NULL)
@@ -811,7 +895,8 @@ static int ep93xx_eth_probe(struct platform_device *pdev)
struct ep93xx_priv *ep;
struct resource *mem;
int irq;
- int err;
+ int err = 0;
+ int i;
if (pdev == NULL)
return -ENODEV;
@@ -849,13 +934,41 @@ static int ep93xx_eth_probe(struct platform_device *pdev)
}
ep->irq = irq;
- ep->mii.phy_id = data->phy_id;
- ep->mii.phy_id_mask = 0x1f;
- ep->mii.reg_num_mask = 0x1f;
- ep->mii.dev = dev;
- ep->mii.mdio_read = ep93xx_mdio_read;
- ep->mii.mdio_write = ep93xx_mdio_write;
+ ep->mii_bus = mdiobus_alloc();
+ if (!ep->mii_bus) {
+ dev_err(&pdev->dev, "Failed to allocate mdiobus\n");
+ goto err_out;
+ }
+
+ ep->phy_addr = data->phy_id;
+ ep->mii_bus->priv = dev;
+ ep->mii_bus->read = ep93xx_mdiobus_read;
+ ep->mii_bus->write = ep93xx_mdiobus_write;
+ ep->mii_bus->reset = ep93xx_mdiobus_reset;
+ ep->mii_bus->name = "ep93xx_eth_mii";
ep->mdc_divisor = 40; /* Max HCLK 100 MHz, min MDIO clk 2.5 MHz. */
+ snprintf(ep->mii_bus->id, MII_BUS_ID_SIZE, "%x",
+ (pdev->id != -1) ? pdev->id : 0);
+ ep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+ if (!ep->mii_bus->irq) {
+ dev_err(&pdev->dev, "mii_bus irq allocation failed\n");
+ goto err_out;
+ }
+
+ for (i = 0; i < PHY_MAX_ADDR; i++)
+ ep->mii_bus->irq[i] = PHY_POLL;
+
+ err = mdiobus_register(ep->mii_bus);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register MII bus\n");
+ goto err_out;
+ }
+
+ err = ep93xx_mii_probe(dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to probe MII bus\n");
+ goto err_out;
+ }
if (is_zero_ether_addr(dev->dev_addr))
random_ether_addr(dev->dev_addr);
--
1.7.4.1
More information about the linux-arm-kernel
mailing list