Skip to content

Commit

Permalink
net: Rework mpc5200 fec driver to use of_mdio infrastructure.
Browse files Browse the repository at this point in the history
The patch reworks the MPC5200 Fast Ethernet Controller (FEC) driver to
use the of_mdio infrastructure for registering PHY devices from data out
openfirmware device tree, and eliminates the assumption that the PHY
for the FEC is always attached to the FEC's own MDIO bus.  With this
patch, the FEC can use a PHY attached to any MDIO bus if it is described
in the device tree.

Tested on Freescale Lite5200b eval board

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Andy Fleming <afleming@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Grant Likely authored and David S. Miller committed Apr 27, 2009
1 parent 8bc487d commit ca816d9
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 147 deletions.
180 changes: 53 additions & 127 deletions drivers/net/fec_mpc52xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/hardirq.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>

#include <linux/netdevice.h>
Expand All @@ -43,11 +44,9 @@

#define DRIVER_NAME "mpc52xx-fec"

#define FEC5200_PHYADDR_NONE (-1)
#define FEC5200_PHYADDR_7WIRE (-2)

/* Private driver data structure */
struct mpc52xx_fec_priv {
struct net_device *ndev;
int duplex;
int speed;
int r_irq;
Expand All @@ -59,10 +58,11 @@ struct mpc52xx_fec_priv {
int msg_enable;

/* MDIO link details */
int phy_addr;
unsigned int phy_speed;
unsigned int mdio_speed;
struct device_node *phy_node;
struct phy_device *phydev;
enum phy_state link;
int seven_wire_mode;
};


Expand Down Expand Up @@ -211,85 +211,25 @@ static void mpc52xx_fec_adjust_link(struct net_device *dev)
phy_print_status(phydev);
}

static int mpc52xx_fec_init_phy(struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
struct phy_device *phydev;
char phy_id[BUS_ID_SIZE];

snprintf(phy_id, sizeof(phy_id), "%x:%02x",
(unsigned int)dev->base_addr, priv->phy_addr);

priv->link = PHY_DOWN;
priv->speed = 0;
priv->duplex = -1;

phydev = phy_connect(dev, phy_id, &mpc52xx_fec_adjust_link, 0, PHY_INTERFACE_MODE_MII);
if (IS_ERR(phydev)) {
dev_err(&dev->dev, "phy_connect failed\n");
return PTR_ERR(phydev);
}
dev_info(&dev->dev, "attached phy %i to driver %s\n",
phydev->addr, phydev->drv->name);

priv->phydev = phydev;

return 0;
}

static int mpc52xx_fec_phy_start(struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
int err;

if (priv->phy_addr < 0)
return 0;

err = mpc52xx_fec_init_phy(dev);
if (err) {
dev_err(&dev->dev, "mpc52xx_fec_init_phy failed\n");
return err;
}

/* reset phy - this also wakes it from PDOWN */
phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
phy_start(priv->phydev);

return 0;
}

static void mpc52xx_fec_phy_stop(struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);

if (!priv->phydev)
return;

phy_disconnect(priv->phydev);
/* power down phy */
phy_stop(priv->phydev);
phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN);
}

static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv)
{
struct mpc52xx_fec __iomem *fec = priv->fec;

if (priv->phydev)
return;

out_be32(&fec->mii_speed, priv->phy_speed);
}

static int mpc52xx_fec_open(struct net_device *dev)
{
struct mpc52xx_fec_priv *priv = netdev_priv(dev);
int err = -EBUSY;

if (priv->phy_node) {
priv->phydev = of_phy_connect(priv->ndev, priv->phy_node,
mpc52xx_fec_adjust_link, 0, 0);
if (!priv->phydev) {
dev_err(&dev->dev, "of_phy_connect failed\n");
return -ENODEV;
}
phy_start(priv->phydev);
}

if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_SHARED,
DRIVER_NAME "_ctrl", dev)) {
dev_err(&dev->dev, "ctrl interrupt request failed\n");
goto out;
goto free_phy;
}
if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, 0,
DRIVER_NAME "_rx", dev)) {
Expand All @@ -311,10 +251,6 @@ static int mpc52xx_fec_open(struct net_device *dev)
goto free_irqs;
}

err = mpc52xx_fec_phy_start(dev);
if (err)
goto free_skbs;

bcom_enable(priv->rx_dmatsk);
bcom_enable(priv->tx_dmatsk);

Expand All @@ -324,16 +260,18 @@ static int mpc52xx_fec_open(struct net_device *dev)

return 0;

free_skbs:
mpc52xx_fec_free_rx_buffers(dev, priv->rx_dmatsk);

free_irqs:
free_irq(priv->t_irq, dev);
free_2irqs:
free_irq(priv->r_irq, dev);
free_ctrl_irq:
free_irq(dev->irq, dev);
out:
free_phy:
if (priv->phydev) {
phy_stop(priv->phydev);
phy_disconnect(priv->phydev);
priv->phydev = NULL;
}

return err;
}
Expand All @@ -352,7 +290,12 @@ static int mpc52xx_fec_close(struct net_device *dev)
free_irq(priv->r_irq, dev);
free_irq(priv->t_irq, dev);

mpc52xx_fec_phy_stop(dev);
if (priv->phydev) {
/* power down phy */
phy_stop(priv->phydev);
phy_disconnect(priv->phydev);
priv->phydev = NULL;
}

return 0;
}
Expand Down Expand Up @@ -696,7 +639,7 @@ static void mpc52xx_fec_hw_init(struct net_device *dev)
/* set phy speed.
* this can't be done in phy driver, since it needs to be called
* before fec stuff (even on resume) */
mpc52xx_fec_phy_hw_init(priv);
out_be32(&fec->mii_speed, priv->mdio_speed);
}

/**
Expand Down Expand Up @@ -732,7 +675,7 @@ static void mpc52xx_fec_start(struct net_device *dev)
rcntrl = FEC_RX_BUFFER_SIZE << 16; /* max frame length */
rcntrl |= FEC_RCNTRL_FCE;

if (priv->phy_addr != FEC5200_PHYADDR_7WIRE)
if (!priv->seven_wire_mode)
rcntrl |= FEC_RCNTRL_MII_MODE;

if (priv->duplex == DUPLEX_FULL)
Expand Down Expand Up @@ -798,8 +741,6 @@ static void mpc52xx_fec_stop(struct net_device *dev)

/* Stop FEC */
out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN);

return;
}

/* reset fec and bestcomm tasks */
Expand All @@ -817,9 +758,11 @@ static void mpc52xx_fec_reset(struct net_device *dev)

mpc52xx_fec_hw_init(dev);

phy_stop(priv->phydev);
phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
phy_start(priv->phydev);
if (priv->phydev) {
phy_stop(priv->phydev);
phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
phy_start(priv->phydev);
}

bcom_fec_rx_reset(priv->rx_dmatsk);
bcom_fec_tx_reset(priv->tx_dmatsk);
Expand Down Expand Up @@ -919,8 +862,6 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
struct net_device *ndev;
struct mpc52xx_fec_priv *priv = NULL;
struct resource mem;
struct device_node *phy_node;
const phandle *phy_handle;
const u32 *prop;
int prop_size;

Expand All @@ -933,6 +874,7 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
return -ENOMEM;

priv = netdev_priv(ndev);
priv->ndev = ndev;

/* Reserve FEC control zone */
rv = of_address_to_resource(op->node, 0, &mem);
Expand All @@ -956,6 +898,7 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
ndev->ethtool_ops = &mpc52xx_fec_ethtool_ops;
ndev->watchdog_timeo = FEC_WATCHDOG_TIMEOUT;
ndev->base_addr = mem.start;
SET_NETDEV_DEV(ndev, &op->dev);

spin_lock_init(&priv->lock);

Expand Down Expand Up @@ -1003,14 +946,9 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
*/

/* Start with safe defaults for link connection */
priv->phy_addr = FEC5200_PHYADDR_NONE;
priv->speed = 100;
priv->duplex = DUPLEX_HALF;
priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;

/* the 7-wire property means don't use MII mode */
if (of_find_property(op->node, "fsl,7-wire-mode", NULL))
priv->phy_addr = FEC5200_PHYADDR_7WIRE;
priv->mdio_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;

/* The current speed preconfigures the speed of the MII link */
prop = of_get_property(op->node, "current-speed", &prop_size);
Expand All @@ -1019,43 +957,23 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
priv->duplex = prop[1] ? DUPLEX_FULL : DUPLEX_HALF;
}

/* If there is a phy handle, setup link to that phy */
phy_handle = of_get_property(op->node, "phy-handle", &prop_size);
if (phy_handle && (prop_size >= sizeof(phandle))) {
phy_node = of_find_node_by_phandle(*phy_handle);
prop = of_get_property(phy_node, "reg", &prop_size);
if (prop && (prop_size >= sizeof(u32)))
if ((*prop >= 0) && (*prop < PHY_MAX_ADDR))
priv->phy_addr = *prop;
of_node_put(phy_node);
/* If there is a phy handle, then get the PHY node */
priv->phy_node = of_parse_phandle(op->node, "phy-handle", 0);

/* the 7-wire property means don't use MII mode */
if (of_find_property(op->node, "fsl,7-wire-mode", NULL)) {
priv->seven_wire_mode = 1;
dev_info(&ndev->dev, "using 7-wire PHY mode\n");
}

/* Hardware init */
mpc52xx_fec_hw_init(ndev);

mpc52xx_fec_reset_stats(ndev);

SET_NETDEV_DEV(ndev, &op->dev);

/* Register the new network device */
rv = register_netdev(ndev);
if (rv < 0)
goto probe_error;

/* Now report the link setup */
switch (priv->phy_addr) {
case FEC5200_PHYADDR_NONE:
dev_info(&ndev->dev, "Fixed speed MII link: %i%cD\n",
priv->speed, priv->duplex ? 'F' : 'H');
break;
case FEC5200_PHYADDR_7WIRE:
dev_info(&ndev->dev, "using 7-wire PHY mode\n");
break;
default:
dev_info(&ndev->dev, "Using PHY at MDIO address %i\n",
priv->phy_addr);
}

/* We're done ! */
dev_set_drvdata(&op->dev, ndev);

Expand All @@ -1065,6 +983,10 @@ mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
/* Error handling - free everything that might be allocated */
probe_error:

if (priv->phy_node)
of_node_put(priv->phy_node);
priv->phy_node = NULL;

irq_dispose_mapping(ndev->irq);

if (priv->rx_dmatsk)
Expand Down Expand Up @@ -1093,6 +1015,10 @@ mpc52xx_fec_remove(struct of_device *op)

unregister_netdev(ndev);

if (priv->phy_node)
of_node_put(priv->phy_node);
priv->phy_node = NULL;

irq_dispose_mapping(ndev->irq);

bcom_fec_rx_release(priv->rx_dmatsk);
Expand Down
Loading

0 comments on commit ca816d9

Please sign in to comment.