lkml.org 
[lkml]   [2010]   [Apr]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    From
    SubjectRe: [PATCH] netdev/fec.c: add phylib supporting to enable carrier detection

    Hi Bryan,

    Bryan Wu wrote:
    > BugLink: http://bugs.launchpad.net/bugs/457878
    >
    > - removed old MII phy control code
    > - add phylib supporting
    > - add ethtool interface to make user space NetworkManager works
    >
    > Tested on Freescale i.MX51 Babbage board.
    >
    > This patch is based on a patch from Frederic Rodo <fred.rodo@gmail.com>
    >
    > Cc: Frederic Rodo <fred.rodo@gmail.com>
    > Signed-off-by: Bryan Wu <bryan.wu@canonical.com>

    I tested this on a ColdFire based M5208EVB board (which has internal
    FEC interface), and it seemed to work ok. Not all of the PHY's
    listed internal to this code are supported by phylib (though I think
    most will fall back to generic and be ok).

    So I am ok with it:

    Acked-by: Greg Ungerer <gerg@uclinux.org>

    Regards
    Greg



    > ---
    > drivers/net/Kconfig | 1 +
    > drivers/net/fec.c | 1125 ++++++++++++---------------------------------------
    > 2 files changed, 253 insertions(+), 873 deletions(-)
    >
    > diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
    > index 0ba5b8e..41f6a70 100644
    > --- a/drivers/net/Kconfig
    > +++ b/drivers/net/Kconfig
    > @@ -1916,6 +1916,7 @@ config FEC
    > bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
    > depends on M523x || M527x || M5272 || M528x || M520x || M532x || \
    > MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5
    > + select PHYLIB
    > help
    > Say Y here if you want to use the built-in 10/100 Fast ethernet
    > controller on some Motorola ColdFire and Freescale i.MX processors.
    > diff --git a/drivers/net/fec.c b/drivers/net/fec.c
    > index 9f98c1c..fca1f66 100644
    > --- a/drivers/net/fec.c
    > +++ b/drivers/net/fec.c
    > @@ -40,6 +40,7 @@
    > #include <linux/irq.h>
    > #include <linux/clk.h>
    > #include <linux/platform_device.h>
    > +#include <linux/phy.h>
    >
    > #include <asm/cacheflush.h>
    >
    > @@ -61,7 +62,6 @@
    > * Define the fixed address of the FEC hardware.
    > */
    > #if defined(CONFIG_M5272)
    > -#define HAVE_mii_link_interrupt
    >
    > static unsigned char fec_mac_default[] = {
    > 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    > @@ -86,23 +86,6 @@ static unsigned char fec_mac_default[] = {
    > #endif
    > #endif /* CONFIG_M5272 */
    >
    > -/* Forward declarations of some structures to support different PHYs */
    > -
    > -typedef struct {
    > - uint mii_data;
    > - void (*funct)(uint mii_reg, struct net_device *dev);
    > -} phy_cmd_t;
    > -
    > -typedef struct {
    > - uint id;
    > - char *name;
    > -
    > - const phy_cmd_t *config;
    > - const phy_cmd_t *startup;
    > - const phy_cmd_t *ack_int;
    > - const phy_cmd_t *shutdown;
    > -} phy_info_t;
    > -
    > /* The number of Tx and Rx buffers. These are allocated from the page
    > * pool. The code may assume these are power of two, so it it best
    > * to keep them that size.
    > @@ -189,29 +172,21 @@ struct fec_enet_private {
    > uint tx_full;
    > /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
    > spinlock_t hw_lock;
    > - /* hold while accessing the mii_list_t() elements */
    > - spinlock_t mii_lock;
    > -
    > - uint phy_id;
    > - uint phy_id_done;
    > - uint phy_status;
    > - uint phy_speed;
    > - phy_info_t const *phy;
    > - struct work_struct phy_task;
    >
    > - uint sequence_done;
    > - uint mii_phy_task_queued;
    > + struct platform_device *pdev;
    >
    > - uint phy_addr;
    > + int opened;
    >
    > + /* Phylib and MDIO interface */
    > + struct mii_bus *mii_bus;
    > + struct phy_device *phy_dev;
    > + int mii_timeout;
    > + uint phy_speed;
    > int index;
    > - int opened;
    > int link;
    > - int old_link;
    > int full_duplex;
    > };
    >
    > -static void fec_enet_mii(struct net_device *dev);
    > static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
    > static void fec_enet_tx(struct net_device *dev);
    > static void fec_enet_rx(struct net_device *dev);
    > @@ -219,67 +194,20 @@ static int fec_enet_close(struct net_device *dev);
    > static void fec_restart(struct net_device *dev, int duplex);
    > static void fec_stop(struct net_device *dev);
    >
    > +/* FEC MII MMFR bits definition */
    > +#define FEC_MMFR_ST (1 << 30)
    > +#define FEC_MMFR_OP_READ (2 << 28)
    > +#define FEC_MMFR_OP_WRITE (1 << 28)
    > +#define FEC_MMFR_PA(v) ((v & 0x1f) << 23)
    > +#define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
    > +#define FEC_MMFR_TA (2 << 16)
    > +#define FEC_MMFR_DATA(v) (v & 0xffff)
    >
    > -/* MII processing. We keep this as simple as possible. Requests are
    > - * placed on the list (if there is room). When the request is finished
    > - * by the MII, an optional function may be called.
    > - */
    > -typedef struct mii_list {
    > - uint mii_regval;
    > - void (*mii_func)(uint val, struct net_device *dev);
    > - struct mii_list *mii_next;
    > -} mii_list_t;
    > -
    > -#define NMII 20
    > -static mii_list_t mii_cmds[NMII];
    > -static mii_list_t *mii_free;
    > -static mii_list_t *mii_head;
    > -static mii_list_t *mii_tail;
    > -
    > -static int mii_queue(struct net_device *dev, int request,
    > - void (*func)(uint, struct net_device *));
    > -
    > -/* Make MII read/write commands for the FEC */
    > -#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
    > -#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \
    > - (VAL & 0xffff))
    > -#define mk_mii_end 0
    > +#define FEC_MII_TIMEOUT 10000
    >
    > /* Transmitter timeout */
    > #define TX_TIMEOUT (2 * HZ)
    >
    > -/* Register definitions for the PHY */
    > -
    > -#define MII_REG_CR 0 /* Control Register */
    > -#define MII_REG_SR 1 /* Status Register */
    > -#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */
    > -#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */
    > -#define MII_REG_ANAR 4 /* A-N Advertisement Register */
    > -#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */
    > -#define MII_REG_ANER 6 /* A-N Expansion Register */
    > -#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */
    > -#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */
    > -
    > -/* values for phy_status */
    > -
    > -#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */
    > -#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */
    > -#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */
    > -#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */
    > -#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */
    > -#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */
    > -#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */
    > -
    > -#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */
    > -#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */
    > -#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */
    > -#define PHY_STAT_SPMASK 0xf000 /* mask for speed */
    > -#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */
    > -#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */
    > -#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */
    > -#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */
    > -
    > -
    > static int
    > fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
    > {
    > @@ -406,12 +334,6 @@ fec_enet_interrupt(int irq, void * dev_id)
    > ret = IRQ_HANDLED;
    > fec_enet_tx(dev);
    > }
    > -
    > - if (int_events & FEC_ENET_MII) {
    > - ret = IRQ_HANDLED;
    > - fec_enet_mii(dev);
    > - }
    > -
    > } while (int_events);
    >
    > return ret;
    > @@ -607,827 +529,312 @@ rx_processing_done:
    > spin_unlock(&fep->hw_lock);
    > }
    >
    > -/* called from interrupt context */
    > -static void
    > -fec_enet_mii(struct net_device *dev)
    > -{
    > - struct fec_enet_private *fep;
    > - mii_list_t *mip;
    > -
    > - fep = netdev_priv(dev);
    > - spin_lock(&fep->mii_lock);
    > -
    > - if ((mip = mii_head) == NULL) {
    > - printk("MII and no head!\n");
    > - goto unlock;
    > - }
    > -
    > - if (mip->mii_func != NULL)
    > - (*(mip->mii_func))(readl(fep->hwp + FEC_MII_DATA), dev);
    > -
    > - mii_head = mip->mii_next;
    > - mip->mii_next = mii_free;
    > - mii_free = mip;
    > -
    > - if ((mip = mii_head) != NULL)
    > - writel(mip->mii_regval, fep->hwp + FEC_MII_DATA);
    > -
    > -unlock:
    > - spin_unlock(&fep->mii_lock);
    > -}
    > -
    > -static int
    > -mii_queue_unlocked(struct net_device *dev, int regval,
    > - void (*func)(uint, struct net_device *))
    > +/* ------------------------------------------------------------------------- */
    > +#ifdef CONFIG_M5272
    > +static void __inline__ fec_get_mac(struct net_device *dev)
    > {
    > - struct fec_enet_private *fep;
    > - mii_list_t *mip;
    > - int retval;
    > -
    > - /* Add PHY address to register command */
    > - fep = netdev_priv(dev);
    > + struct fec_enet_private *fep = netdev_priv(dev);
    > + unsigned char *iap, tmpaddr[ETH_ALEN];
    >
    > - regval |= fep->phy_addr << 23;
    > - retval = 0;
    > -
    > - if ((mip = mii_free) != NULL) {
    > - mii_free = mip->mii_next;
    > - mip->mii_regval = regval;
    > - mip->mii_func = func;
    > - mip->mii_next = NULL;
    > - if (mii_head) {
    > - mii_tail->mii_next = mip;
    > - mii_tail = mip;
    > - } else {
    > - mii_head = mii_tail = mip;
    > - writel(regval, fep->hwp + FEC_MII_DATA);
    > - }
    > + if (FEC_FLASHMAC) {
    > + /*
    > + * Get MAC address from FLASH.
    > + * If it is all 1's or 0's, use the default.
    > + */
    > + iap = (unsigned char *)FEC_FLASHMAC;
    > + if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
    > + (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
    > + iap = fec_mac_default;
    > + if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
    > + (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
    > + iap = fec_mac_default;
    > } else {
    > - retval = 1;
    > + *((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
    > + *((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
    > + iap = &tmpaddr[0];
    > }
    >
    > - return retval;
    > -}
    > -
    > -static int
    > -mii_queue(struct net_device *dev, int regval,
    > - void (*func)(uint, struct net_device *))
    > -{
    > - struct fec_enet_private *fep;
    > - unsigned long flags;
    > - int retval;
    > - fep = netdev_priv(dev);
    > - spin_lock_irqsave(&fep->mii_lock, flags);
    > - retval = mii_queue_unlocked(dev, regval, func);
    > - spin_unlock_irqrestore(&fep->mii_lock, flags);
    > - return retval;
    > -}
    > -
    > -static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
    > -{
    > - if(!c)
    > - return;
    > + memcpy(dev->dev_addr, iap, ETH_ALEN);
    >
    > - for (; c->mii_data != mk_mii_end; c++)
    > - mii_queue(dev, c->mii_data, c->funct);
    > + /* Adjust MAC if using default MAC address */
    > + if (iap == fec_mac_default)
    > + dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
    > }
    > +#endif
    >
    > -static void mii_parse_sr(uint mii_reg, struct net_device *dev)
    > -{
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > -
    > - status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
    > -
    > - if (mii_reg & 0x0004)
    > - status |= PHY_STAT_LINK;
    > - if (mii_reg & 0x0010)
    > - status |= PHY_STAT_FAULT;
    > - if (mii_reg & 0x0020)
    > - status |= PHY_STAT_ANC;
    > - *s = status;
    > -}
    > +/* ------------------------------------------------------------------------- */
    >
    > -static void mii_parse_cr(uint mii_reg, struct net_device *dev)
    > +/*
    > + * Phy section
    > + */
    > +static void fec_enet_adjust_link(struct net_device *dev)
    > {
    > struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > -
    > - status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP);
    > -
    > - if (mii_reg & 0x1000)
    > - status |= PHY_CONF_ANE;
    > - if (mii_reg & 0x4000)
    > - status |= PHY_CONF_LOOP;
    > - *s = status;
    > -}
    > + struct phy_device *phy_dev = fep->phy_dev;
    > + unsigned long flags;
    >
    > -static void mii_parse_anar(uint mii_reg, struct net_device *dev)
    > -{
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > -
    > - status = *s & ~(PHY_CONF_SPMASK);
    > -
    > - if (mii_reg & 0x0020)
    > - status |= PHY_CONF_10HDX;
    > - if (mii_reg & 0x0040)
    > - status |= PHY_CONF_10FDX;
    > - if (mii_reg & 0x0080)
    > - status |= PHY_CONF_100HDX;
    > - if (mii_reg & 0x00100)
    > - status |= PHY_CONF_100FDX;
    > - *s = status;
    > -}
    > + int status_change = 0;
    >
    > -/* ------------------------------------------------------------------------- */
    > -/* The Level one LXT970 is used by many boards */
    > + spin_lock_irqsave(&fep->hw_lock, flags);
    >
    > -#define MII_LXT970_MIRROR 16 /* Mirror register */
    > -#define MII_LXT970_IER 17 /* Interrupt Enable Register */
    > -#define MII_LXT970_ISR 18 /* Interrupt Status Register */
    > -#define MII_LXT970_CONFIG 19 /* Configuration Register */
    > -#define MII_LXT970_CSR 20 /* Chip Status Register */
    > + /* Prevent a state halted on mii error */
    > + if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
    > + phy_dev->state = PHY_RESUMING;
    > + goto spin_unlock;
    > + }
    >
    > -static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
    > -{
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > + /* Duplex link change */
    > + if (phy_dev->link) {
    > + if (fep->full_duplex != phy_dev->duplex) {
    > + fec_restart(dev, phy_dev->duplex);
    > + status_change = 1;
    > + }
    > + }
    >
    > - status = *s & ~(PHY_STAT_SPMASK);
    > - if (mii_reg & 0x0800) {
    > - if (mii_reg & 0x1000)
    > - status |= PHY_STAT_100FDX;
    > + /* Link on or off change */
    > + if (phy_dev->link != fep->link) {
    > + fep->link = phy_dev->link;
    > + if (phy_dev->link)
    > + fec_restart(dev, phy_dev->duplex);
    > else
    > - status |= PHY_STAT_100HDX;
    > - } else {
    > - if (mii_reg & 0x1000)
    > - status |= PHY_STAT_10FDX;
    > - else
    > - status |= PHY_STAT_10HDX;
    > + fec_stop(dev);
    > + status_change = 1;
    > }
    > - *s = status;
    > -}
    > -
    > -static phy_cmd_t const phy_cmd_lxt970_config[] = {
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */
    > - { mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_lxt970_ack_int[] = {
    > - /* read SR and ISR to acknowledge */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_read(MII_LXT970_ISR), NULL },
    > -
    > - /* find out the current status */
    > - { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */
    > - { mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_info_t const phy_info_lxt970 = {
    > - .id = 0x07810000,
    > - .name = "LXT970",
    > - .config = phy_cmd_lxt970_config,
    > - .startup = phy_cmd_lxt970_startup,
    > - .ack_int = phy_cmd_lxt970_ack_int,
    > - .shutdown = phy_cmd_lxt970_shutdown
    > -};
    >
    > -/* ------------------------------------------------------------------------- */
    > -/* The Level one LXT971 is used on some of my custom boards */
    > -
    > -/* register definitions for the 971 */
    > +spin_unlock:
    > + spin_unlock_irqrestore(&fep->hw_lock, flags);
    >
    > -#define MII_LXT971_PCR 16 /* Port Control Register */
    > -#define MII_LXT971_SR2 17 /* Status Register 2 */
    > -#define MII_LXT971_IER 18 /* Interrupt Enable Register */
    > -#define MII_LXT971_ISR 19 /* Interrupt Status Register */
    > -#define MII_LXT971_LCR 20 /* LED Control Register */
    > -#define MII_LXT971_TCR 30 /* Transmit Control Register */
    > + if (status_change)
    > + phy_print_status(phy_dev);
    > +}
    >
    > /*
    > - * I had some nice ideas of running the MDIO faster...
    > - * The 971 should support 8MHz and I tried it, but things acted really
    > - * weird, so 2.5 MHz ought to be enough for anyone...
    > + * NOTE: a MII transaction is during around 25 us, so polling it...
    > */
    > -
    > -static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
    > +static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
    > {
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > + struct fec_enet_private *fep = bus->priv;
    > + int timeout = FEC_MII_TIMEOUT;
    >
    > - status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
    > + fep->mii_timeout = 0;
    >
    > - if (mii_reg & 0x0400) {
    > - fep->link = 1;
    > - status |= PHY_STAT_LINK;
    > - } else {
    > - fep->link = 0;
    > - }
    > - if (mii_reg & 0x0080)
    > - status |= PHY_STAT_ANC;
    > - if (mii_reg & 0x4000) {
    > - if (mii_reg & 0x0200)
    > - status |= PHY_STAT_100FDX;
    > - else
    > - status |= PHY_STAT_100HDX;
    > - } else {
    > - if (mii_reg & 0x0200)
    > - status |= PHY_STAT_10FDX;
    > - else
    > - status |= PHY_STAT_10HDX;
    > + /* clear MII end of transfer bit*/
    > + writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
    > +
    > + /* start a read op */
    > + writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
    > + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
    > + FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
    > +
    > + /* wait for end of transfer */
    > + while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
    > + cpu_relax();
    > + if (timeout-- < 0) {
    > + fep->mii_timeout = 1;
    > + printk(KERN_ERR "FEC: MDIO read timeout\n");
    > + return -ETIMEDOUT;
    > + }
    > }
    > - if (mii_reg & 0x0008)
    > - status |= PHY_STAT_FAULT;
    >
    > - *s = status;
    > + /* return value */
    > + return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
    > }
    >
    > -static phy_cmd_t const phy_cmd_lxt971_config[] = {
    > - /* limit to 10MBit because my prototype board
    > - * doesn't work with 100. */
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_lxt971_startup[] = { /* enable interrupts */
    > - { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */
    > - /* Somehow does the 971 tell me that the link is down
    > - * the first read after power-up.
    > - * read here to get a valid value in ack_int */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_lxt971_ack_int[] = {
    > - /* acknowledge the int before reading status ! */
    > - { mk_mii_read(MII_LXT971_ISR), NULL },
    > - /* find out the current status */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */
    > - { mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_info_t const phy_info_lxt971 = {
    > - .id = 0x0001378e,
    > - .name = "LXT971",
    > - .config = phy_cmd_lxt971_config,
    > - .startup = phy_cmd_lxt971_startup,
    > - .ack_int = phy_cmd_lxt971_ack_int,
    > - .shutdown = phy_cmd_lxt971_shutdown
    > -};
    > -
    > -/* ------------------------------------------------------------------------- */
    > -/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
    > -
    > -/* register definitions */
    > -
    > -#define MII_QS6612_MCR 17 /* Mode Control Register */
    > -#define MII_QS6612_FTR 27 /* Factory Test Register */
    > -#define MII_QS6612_MCO 28 /* Misc. Control Register */
    > -#define MII_QS6612_ISR 29 /* Interrupt Source Register */
    > -#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
    > -#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
    > -
    > -static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
    > +static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
    > + u16 value)
    > {
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > + struct fec_enet_private *fep = bus->priv;
    > + int timeout = FEC_MII_TIMEOUT;
    >
    > - status = *s & ~(PHY_STAT_SPMASK);
    > + fep->mii_timeout = 0;
    >
    > - switch((mii_reg >> 2) & 7) {
    > - case 1: status |= PHY_STAT_10HDX; break;
    > - case 2: status |= PHY_STAT_100HDX; break;
    > - case 5: status |= PHY_STAT_10FDX; break;
    > - case 6: status |= PHY_STAT_100FDX; break;
    > -}
    > -
    > - *s = status;
    > -}
    > -
    > -static phy_cmd_t const phy_cmd_qs6612_config[] = {
    > - /* The PHY powers up isolated on the RPX,
    > - * so send a command to allow operation.
    > - */
    > - { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
    > -
    > - /* parse cr and anar to get some info */
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_qs6612_startup[] = { /* enable interrupts */
    > - { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
    > - /* we need to read ISR, SR and ANER to acknowledge */
    > - { mk_mii_read(MII_QS6612_ISR), NULL },
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_read(MII_REG_ANER), NULL },
    > -
    > - /* read pcr to get info */
    > - { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
    > - { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_info_t const phy_info_qs6612 = {
    > - .id = 0x00181440,
    > - .name = "QS6612",
    > - .config = phy_cmd_qs6612_config,
    > - .startup = phy_cmd_qs6612_startup,
    > - .ack_int = phy_cmd_qs6612_ack_int,
    > - .shutdown = phy_cmd_qs6612_shutdown
    > -};
    > -
    > -/* ------------------------------------------------------------------------- */
    > -/* AMD AM79C874 phy */
    > + /* clear MII end of transfer bit*/
    > + writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
    >
    > -/* register definitions for the 874 */
    > + /* start a read op */
    > + writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
    > + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
    > + FEC_MMFR_TA | FEC_MMFR_DATA(value),
    > + fep->hwp + FEC_MII_DATA);
    > +
    > + /* wait for end of transfer */
    > + while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
    > + cpu_relax();
    > + if (timeout-- < 0) {
    > + fep->mii_timeout = 1;
    > + printk(KERN_ERR "FEC: MDIO write timeout\n");
    > + return -ETIMEDOUT;
    > + }
    > + }
    >
    > -#define MII_AM79C874_MFR 16 /* Miscellaneous Feature Register */
    > -#define MII_AM79C874_ICSR 17 /* Interrupt/Status Register */
    > -#define MII_AM79C874_DR 18 /* Diagnostic Register */
    > -#define MII_AM79C874_PMLR 19 /* Power and Loopback Register */
    > -#define MII_AM79C874_MCR 21 /* ModeControl Register */
    > -#define MII_AM79C874_DC 23 /* Disconnect Counter */
    > -#define MII_AM79C874_REC 24 /* Recieve Error Counter */
    > + return 0;
    > +}
    >
    > -static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev)
    > +static int fec_enet_mdio_reset(struct mii_bus *bus)
    > {
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > - uint status;
    > -
    > - status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
    > -
    > - if (mii_reg & 0x0080)
    > - status |= PHY_STAT_ANC;
    > - if (mii_reg & 0x0400)
    > - status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
    > - else
    > - status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
    > -
    > - *s = status;
    > + return 0;
    > }
    >
    > -static phy_cmd_t const phy_cmd_am79c874_config[] = {
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_am79c874_startup[] = { /* enable interrupts */
    > - { mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL },
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
    > - /* find out the current status */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
    > - /* we only need to read ISR to acknowledge */
    > - { mk_mii_read(MII_AM79C874_ICSR), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
    > - { mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_info_t const phy_info_am79c874 = {
    > - .id = 0x00022561,
    > - .name = "AM79C874",
    > - .config = phy_cmd_am79c874_config,
    > - .startup = phy_cmd_am79c874_startup,
    > - .ack_int = phy_cmd_am79c874_ack_int,
    > - .shutdown = phy_cmd_am79c874_shutdown
    > -};
    > -
    > -
    > -/* ------------------------------------------------------------------------- */
    > -/* Kendin KS8721BL phy */
    > -
    > -/* register definitions for the 8721 */
    > -
    > -#define MII_KS8721BL_RXERCR 21
    > -#define MII_KS8721BL_ICSR 27
    > -#define MII_KS8721BL_PHYCR 31
    > -
    > -static phy_cmd_t const phy_cmd_ks8721bl_config[] = {
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_ks8721bl_startup[] = { /* enable interrupts */
    > - { mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL },
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
    > - /* find out the current status */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - /* we only need to read ISR to acknowledge */
    > - { mk_mii_read(MII_KS8721BL_ICSR), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */
    > - { mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL },
    > - { mk_mii_end, }
    > - };
    > -static phy_info_t const phy_info_ks8721bl = {
    > - .id = 0x00022161,
    > - .name = "KS8721BL",
    > - .config = phy_cmd_ks8721bl_config,
    > - .startup = phy_cmd_ks8721bl_startup,
    > - .ack_int = phy_cmd_ks8721bl_ack_int,
    > - .shutdown = phy_cmd_ks8721bl_shutdown
    > -};
    > -
    > -/* ------------------------------------------------------------------------- */
    > -/* register definitions for the DP83848 */
    > -
    > -#define MII_DP8384X_PHYSTST 16 /* PHY Status Register */
    > -
    > -static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
    > +static int fec_enet_mii_probe(struct net_device *dev)
    > {
    > struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > -
    > - *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
    > -
    > - /* Link up */
    > - if (mii_reg & 0x0001) {
    > - fep->link = 1;
    > - *s |= PHY_STAT_LINK;
    > - } else
    > - fep->link = 0;
    > - /* Status of link */
    > - if (mii_reg & 0x0010) /* Autonegotioation complete */
    > - *s |= PHY_STAT_ANC;
    > - if (mii_reg & 0x0002) { /* 10MBps? */
    > - if (mii_reg & 0x0004) /* Full Duplex? */
    > - *s |= PHY_STAT_10FDX;
    > - else
    > - *s |= PHY_STAT_10HDX;
    > - } else { /* 100 Mbps? */
    > - if (mii_reg & 0x0004) /* Full Duplex? */
    > - *s |= PHY_STAT_100FDX;
    > - else
    > - *s |= PHY_STAT_100HDX;
    > - }
    > - if (mii_reg & 0x0008)
    > - *s |= PHY_STAT_FAULT;
    > -}
    > -
    > -static phy_info_t phy_info_dp83848= {
    > - 0x020005c9,
    > - "DP83848",
    > + struct phy_device *phy_dev = NULL;
    > + int phy_addr;
    >
    > - (const phy_cmd_t []) { /* config */
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_read(MII_DP8384X_PHYSTST), mii_parse_dp8384x_sr2 },
    > - { mk_mii_end, }
    > - },
    > - (const phy_cmd_t []) { /* startup - enable interrupts */
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_end, }
    > - },
    > - (const phy_cmd_t []) { /* ack_int - never happens, no interrupt */
    > - { mk_mii_end, }
    > - },
    > - (const phy_cmd_t []) { /* shutdown */
    > - { mk_mii_end, }
    > - },
    > -};
    > + /* find the first phy */
    > + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
    > + if (fep->mii_bus->phy_map[phy_addr]) {
    > + phy_dev = fep->mii_bus->phy_map[phy_addr];
    > + break;
    > + }
    > + }
    >
    > -static phy_info_t phy_info_lan8700 = {
    > - 0x0007C0C,
    > - "LAN8700",
    > - (const phy_cmd_t []) { /* config */
    > - { mk_mii_read(MII_REG_CR), mii_parse_cr },
    > - { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
    > - { mk_mii_end, }
    > - },
    > - (const phy_cmd_t []) { /* startup */
    > - { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
    > - { mk_mii_read(MII_REG_SR), mii_parse_sr },
    > - { mk_mii_end, }
    > - },
    > - (const phy_cmd_t []) { /* act_int */
    > - { mk_mii_end, }
    > - },
    > - (const phy_cmd_t []) { /* shutdown */
    > - { mk_mii_end, }
    > - },
    > -};
    > -/* ------------------------------------------------------------------------- */
    > + if (!phy_dev) {
    > + printk(KERN_ERR "%s: no PHY found\n", dev->name);
    > + return -ENODEV;
    > + }
    >
    > -static phy_info_t const * const phy_info[] = {
    > - &phy_info_lxt970,
    > - &phy_info_lxt971,
    > - &phy_info_qs6612,
    > - &phy_info_am79c874,
    > - &phy_info_ks8721bl,
    > - &phy_info_dp83848,
    > - &phy_info_lan8700,
    > - NULL
    > -};
    > + /* attach the mac to the phy */
    > + phy_dev = phy_connect(dev, dev_name(&phy_dev->dev),
    > + &fec_enet_adjust_link, 0,
    > + PHY_INTERFACE_MODE_MII);
    > + if (IS_ERR(phy_dev)) {
    > + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
    > + return PTR_ERR(phy_dev);
    > + }
    >
    > -/* ------------------------------------------------------------------------- */
    > -#ifdef HAVE_mii_link_interrupt
    > -static irqreturn_t
    > -mii_link_interrupt(int irq, void * dev_id);
    > + /* mask with MAC supported features */
    > + phy_dev->supported &= PHY_BASIC_FEATURES;
    > + phy_dev->advertising = phy_dev->supported;
    >
    > -/*
    > - * This is specific to the MII interrupt setup of the M5272EVB.
    > - */
    > -static void __inline__ fec_request_mii_intr(struct net_device *dev)
    > -{
    > - if (request_irq(66, mii_link_interrupt, IRQF_DISABLED, "fec(MII)", dev) != 0)
    > - printk("FEC: Could not allocate fec(MII) IRQ(66)!\n");
    > -}
    > + fep->phy_dev = phy_dev;
    > + fep->link = 0;
    > + fep->full_duplex = 0;
    >
    > -static void __inline__ fec_disable_phy_intr(struct net_device *dev)
    > -{
    > - free_irq(66, dev);
    > + return 0;
    > }
    > -#endif
    >
    > -#ifdef CONFIG_M5272
    > -static void __inline__ fec_get_mac(struct net_device *dev)
    > +static int fec_enet_mii_init(struct platform_device *pdev)
    > {
    > + struct net_device *dev = platform_get_drvdata(pdev);
    > struct fec_enet_private *fep = netdev_priv(dev);
    > - unsigned char *iap, tmpaddr[ETH_ALEN];
    > + int err = -ENXIO, i;
    >
    > - if (FEC_FLASHMAC) {
    > - /*
    > - * Get MAC address from FLASH.
    > - * If it is all 1's or 0's, use the default.
    > - */
    > - iap = (unsigned char *)FEC_FLASHMAC;
    > - if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
    > - (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
    > - iap = fec_mac_default;
    > - if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
    > - (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
    > - iap = fec_mac_default;
    > - } else {
    > - *((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
    > - *((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
    > - iap = &tmpaddr[0];
    > - }
    > -
    > - memcpy(dev->dev_addr, iap, ETH_ALEN);
    > -
    > - /* Adjust MAC if using default MAC address */
    > - if (iap == fec_mac_default)
    > - dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
    > -}
    > -#endif
    > + fep->mii_timeout = 0;
    >
    > -/* ------------------------------------------------------------------------- */
    > -
    > -static void mii_display_status(struct net_device *dev)
    > -{
    > - struct fec_enet_private *fep = netdev_priv(dev);
    > - volatile uint *s = &(fep->phy_status);
    > + /*
    > + * Set MII speed to 2.5 MHz
    > + */
    > + fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
    > + / 2500000) / 2) & 0x3F) << 1;
    > + writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
    >
    > - if (!fep->link && !fep->old_link) {
    > - /* Link is still down - don't print anything */
    > - return;
    > + fep->mii_bus = mdiobus_alloc();
    > + if (fep->mii_bus == NULL) {
    > + err = -ENOMEM;
    > + goto err_out;
    > }
    >
    > - printk("%s: status: ", dev->name);
    > -
    > - if (!fep->link) {
    > - printk("link down");
    > - } else {
    > - printk("link up");
    > -
    > - switch(*s & PHY_STAT_SPMASK) {
    > - case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
    > - case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
    > - case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
    > - case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
    > - default:
    > - printk(", Unknown speed/duplex");
    > - }
    > -
    > - if (*s & PHY_STAT_ANC)
    > - printk(", auto-negotiation complete");
    > + fep->mii_bus->name = "fec_enet_mii_bus";
    > + fep->mii_bus->read = fec_enet_mdio_read;
    > + fep->mii_bus->write = fec_enet_mdio_write;
    > + fep->mii_bus->reset = fec_enet_mdio_reset;
    > + snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
    > + fep->mii_bus->priv = fep;
    > + fep->mii_bus->parent = &pdev->dev;
    > +
    > + fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
    > + if (!fep->mii_bus->irq) {
    > + err = -ENOMEM;
    > + goto err_out_free_mdiobus;
    > }
    >
    > - if (*s & PHY_STAT_FAULT)
    > - printk(", remote fault");
    > -
    > - printk(".\n");
    > -}
    > -
    > -static void mii_display_config(struct work_struct *work)
    > -{
    > - struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
    > - struct net_device *dev = fep->netdev;
    > - uint status = fep->phy_status;
    > + for (i = 0; i < PHY_MAX_ADDR; i++)
    > + fep->mii_bus->irq[i] = PHY_POLL;
    >
    > - /*
    > - ** When we get here, phy_task is already removed from
    > - ** the workqueue. It is thus safe to allow to reuse it.
    > - */
    > - fep->mii_phy_task_queued = 0;
    > - printk("%s: config: auto-negotiation ", dev->name);
    > -
    > - if (status & PHY_CONF_ANE)
    > - printk("on");
    > - else
    > - printk("off");
    > + platform_set_drvdata(dev, fep->mii_bus);
    >
    > - if (status & PHY_CONF_100FDX)
    > - printk(", 100FDX");
    > - if (status & PHY_CONF_100HDX)
    > - printk(", 100HDX");
    > - if (status & PHY_CONF_10FDX)
    > - printk(", 10FDX");
    > - if (status & PHY_CONF_10HDX)
    > - printk(", 10HDX");
    > - if (!(status & PHY_CONF_SPMASK))
    > - printk(", No speed/duplex selected?");
    > + if (mdiobus_register(fep->mii_bus))
    > + goto err_out_free_mdio_irq;
    >
    > - if (status & PHY_CONF_LOOP)
    > - printk(", loopback enabled");
    > + if (fec_enet_mii_probe(dev) != 0)
    > + goto err_out_unregister_bus;
    >
    > - printk(".\n");
    > + return 0;
    >
    > - fep->sequence_done = 1;
    > +err_out_unregister_bus:
    > + mdiobus_unregister(fep->mii_bus);
    > +err_out_free_mdio_irq:
    > + kfree(fep->mii_bus->irq);
    > +err_out_free_mdiobus:
    > + mdiobus_free(fep->mii_bus);
    > +err_out:
    > + return err;
    > }
    >
    > -static void mii_relink(struct work_struct *work)
    > +static void fec_enet_mii_remove(struct fec_enet_private *fep)
    > {
    > - struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
    > - struct net_device *dev = fep->netdev;
    > - int duplex;
    > -
    > - /*
    > - ** When we get here, phy_task is already removed from
    > - ** the workqueue. It is thus safe to allow to reuse it.
    > - */
    > - fep->mii_phy_task_queued = 0;
    > - fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
    > - mii_display_status(dev);
    > - fep->old_link = fep->link;
    > -
    > - if (fep->link) {
    > - duplex = 0;
    > - if (fep->phy_status
    > - & (PHY_STAT_100FDX | PHY_STAT_10FDX))
    > - duplex = 1;
    > - fec_restart(dev, duplex);
    > - } else
    > - fec_stop(dev);
    > + if (fep->phy_dev)
    > + phy_disconnect(fep->phy_dev);
    > + mdiobus_unregister(fep->mii_bus);
    > + kfree(fep->mii_bus->irq);
    > + mdiobus_free(fep->mii_bus);
    > }
    >
    > -/* mii_queue_relink is called in interrupt context from mii_link_interrupt */
    > -static void mii_queue_relink(uint mii_reg, struct net_device *dev)
    > +static int fec_enet_get_settings(struct net_device *dev,
    > + struct ethtool_cmd *cmd)
    > {
    > struct fec_enet_private *fep = netdev_priv(dev);
    > + struct phy_device *phydev = fep->phy_dev;
    >
    > - /*
    > - * We cannot queue phy_task twice in the workqueue. It
    > - * would cause an endless loop in the workqueue.
    > - * Fortunately, if the last mii_relink entry has not yet been
    > - * executed now, it will do the job for the current interrupt,
    > - * which is just what we want.
    > - */
    > - if (fep->mii_phy_task_queued)
    > - return;
    > + if (!phydev)
    > + return -ENODEV;
    >
    > - fep->mii_phy_task_queued = 1;
    > - INIT_WORK(&fep->phy_task, mii_relink);
    > - schedule_work(&fep->phy_task);
    > + return phy_ethtool_gset(phydev, cmd);
    > }
    >
    > -/* mii_queue_config is called in interrupt context from fec_enet_mii */
    > -static void mii_queue_config(uint mii_reg, struct net_device *dev)
    > +static int fec_enet_set_settings(struct net_device *dev,
    > + struct ethtool_cmd *cmd)
    > {
    > struct fec_enet_private *fep = netdev_priv(dev);
    > + struct phy_device *phydev = fep->phy_dev;
    >
    > - if (fep->mii_phy_task_queued)
    > - return;
    > + if (!phydev)
    > + return -ENODEV;
    >
    > - fep->mii_phy_task_queued = 1;
    > - INIT_WORK(&fep->phy_task, mii_display_config);
    > - schedule_work(&fep->phy_task);
    > + return phy_ethtool_sset(phydev, cmd);
    > }
    >
    > -phy_cmd_t const phy_cmd_relink[] = {
    > - { mk_mii_read(MII_REG_CR), mii_queue_relink },
    > - { mk_mii_end, }
    > - };
    > -phy_cmd_t const phy_cmd_config[] = {
    > - { mk_mii_read(MII_REG_CR), mii_queue_config },
    > - { mk_mii_end, }
    > - };
    > -
    > -/* Read remainder of PHY ID. */
    > -static void
    > -mii_discover_phy3(uint mii_reg, struct net_device *dev)
    > +static void fec_enet_get_drvinfo(struct net_device *dev,
    > + struct ethtool_drvinfo *info)
    > {
    > - struct fec_enet_private *fep;
    > - int i;
    > -
    > - fep = netdev_priv(dev);
    > - fep->phy_id |= (mii_reg & 0xffff);
    > - printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);
    > -
    > - for(i = 0; phy_info[i]; i++) {
    > - if(phy_info[i]->id == (fep->phy_id >> 4))
    > - break;
    > - }
    > -
    > - if (phy_info[i])
    > - printk(" -- %s\n", phy_info[i]->name);
    > - else
    > - printk(" -- unknown PHY!\n");
    > + struct fec_enet_private *fep = netdev_priv(dev);
    >
    > - fep->phy = phy_info[i];
    > - fep->phy_id_done = 1;
    > + strcpy(info->driver, fep->pdev->dev.driver->name);
    > + strcpy(info->version, "Revision: 1.0");
    > + strcpy(info->bus_info, dev_name(&dev->dev));
    > }
    >
    > -/* Scan all of the MII PHY addresses looking for someone to respond
    > - * with a valid ID. This usually happens quickly.
    > - */
    > -static void
    > -mii_discover_phy(uint mii_reg, struct net_device *dev)
    > -{
    > - struct fec_enet_private *fep;
    > - uint phytype;
    > -
    > - fep = netdev_priv(dev);
    > -
    > - if (fep->phy_addr < 32) {
    > - if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
    > -
    > - /* Got first part of ID, now get remainder */
    > - fep->phy_id = phytype << 16;
    > - mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR2),
    > - mii_discover_phy3);
    > - } else {
    > - fep->phy_addr++;
    > - mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR1),
    > - mii_discover_phy);
    > - }
    > - } else {
    > - printk("FEC: No PHY device found.\n");
    > - /* Disable external MII interface */
    > - writel(0, fep->hwp + FEC_MII_SPEED);
    > - fep->phy_speed = 0;
    > -#ifdef HAVE_mii_link_interrupt
    > - fec_disable_phy_intr(dev);
    > -#endif
    > - }
    > -}
    > +static struct ethtool_ops fec_enet_ethtool_ops = {
    > + .get_settings = fec_enet_get_settings,
    > + .set_settings = fec_enet_set_settings,
    > + .get_drvinfo = fec_enet_get_drvinfo,
    > + .get_link = ethtool_op_get_link,
    > +};
    >
    > -/* This interrupt occurs when the PHY detects a link change */
    > -#ifdef HAVE_mii_link_interrupt
    > -static irqreturn_t
    > -mii_link_interrupt(int irq, void * dev_id)
    > +static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
    > {
    > - struct net_device *dev = dev_id;
    > struct fec_enet_private *fep = netdev_priv(dev);
    > + struct phy_device *phydev = fep->phy_dev;
    >
    > - mii_do_cmd(dev, fep->phy->ack_int);
    > - mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */
    > + if (!netif_running(dev))
    > + return -EINVAL;
    >
    > - return IRQ_HANDLED;
    > + if (!phydev)
    > + return -ENODEV;
    > +
    > + return phy_mii_ioctl(phydev, if_mii(rq), cmd);
    > }
    > -#endif
    >
    > static void fec_enet_free_buffers(struct net_device *dev)
    > {
    > @@ -1509,35 +916,8 @@ fec_enet_open(struct net_device *dev)
    > if (ret)
    > return ret;
    >
    > - fep->sequence_done = 0;
    > - fep->link = 0;
    > -
    > - fec_restart(dev, 1);
    > -
    > - if (fep->phy) {
    > - mii_do_cmd(dev, fep->phy->ack_int);
    > - mii_do_cmd(dev, fep->phy->config);
    > - mii_do_cmd(dev, phy_cmd_config); /* display configuration */
    > -
    > - /* Poll until the PHY tells us its configuration
    > - * (not link state).
    > - * Request is initiated by mii_do_cmd above, but answer
    > - * comes by interrupt.
    > - * This should take about 25 usec per register at 2.5 MHz,
    > - * and we read approximately 5 registers.
    > - */
    > - while(!fep->sequence_done)
    > - schedule();
    > -
    > - mii_do_cmd(dev, fep->phy->startup);
    > - }
    > -
    > - /* Set the initial link state to true. A lot of hardware
    > - * based on this device does not implement a PHY interrupt,
    > - * so we are never notified of link change.
    > - */
    > - fep->link = 1;
    > -
    > + /* schedule a link state check */
    > + phy_start(fep->phy_dev);
    > netif_start_queue(dev);
    > fep->opened = 1;
    > return 0;
    > @@ -1550,6 +930,7 @@ fec_enet_close(struct net_device *dev)
    >
    > /* Don't know what to do yet. */
    > fep->opened = 0;
    > + phy_stop(fep->phy_dev);
    > netif_stop_queue(dev);
    > fec_stop(dev);
    >
    > @@ -1666,6 +1047,7 @@ static const struct net_device_ops fec_netdev_ops = {
    > .ndo_validate_addr = eth_validate_addr,
    > .ndo_tx_timeout = fec_timeout,
    > .ndo_set_mac_address = fec_set_mac_address,
    > + .ndo_do_ioctl = fec_enet_ioctl,
    > };
    >
    > /*
    > @@ -1689,7 +1071,6 @@ static int fec_enet_init(struct net_device *dev, int index)
    > }
    >
    > spin_lock_init(&fep->hw_lock);
    > - spin_lock_init(&fep->mii_lock);
    >
    > fep->index = index;
    > fep->hwp = (void __iomem *)dev->base_addr;
    > @@ -1716,16 +1097,10 @@ static int fec_enet_init(struct net_device *dev, int index)
    > fep->rx_bd_base = cbd_base;
    > fep->tx_bd_base = cbd_base + RX_RING_SIZE;
    >
    > -#ifdef HAVE_mii_link_interrupt
    > - fec_request_mii_intr(dev);
    > -#endif
    > /* The FEC Ethernet specific entries in the device structure */
    > dev->watchdog_timeo = TX_TIMEOUT;
    > dev->netdev_ops = &fec_netdev_ops;
    > -
    > - for (i=0; i<NMII-1; i++)
    > - mii_cmds[i].mii_next = &mii_cmds[i+1];
    > - mii_free = mii_cmds;
    > + dev->ethtool_ops = &fec_enet_ethtool_ops;
    >
    > /* Set MII speed to 2.5 MHz */
    > fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
    > @@ -1760,13 +1135,6 @@ static int fec_enet_init(struct net_device *dev, int index)
    >
    > fec_restart(dev, 0);
    >
    > - /* Queue up command to detect the PHY and initialize the
    > - * remainder of the interface.
    > - */
    > - fep->phy_id_done = 0;
    > - fep->phy_addr = 0;
    > - mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
    > -
    > return 0;
    > }
    >
    > @@ -1835,8 +1203,7 @@ fec_restart(struct net_device *dev, int duplex)
    > writel(0, fep->hwp + FEC_R_DES_ACTIVE);
    >
    > /* Enable interrupts we wish to service */
    > - writel(FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII,
    > - fep->hwp + FEC_IMASK);
    > + writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK);
    > }
    >
    > static void
    > @@ -1859,7 +1226,6 @@ fec_stop(struct net_device *dev)
    > /* Clear outstanding MII command interrupts. */
    > writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
    >
    > - writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
    > writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
    > }
    >
    > @@ -1891,6 +1257,7 @@ fec_probe(struct platform_device *pdev)
    > memset(fep, 0, sizeof(*fep));
    >
    > ndev->base_addr = (unsigned long)ioremap(r->start, resource_size(r));
    > + fep->pdev = pdev;
    >
    > if (!ndev->base_addr) {
    > ret = -ENOMEM;
    > @@ -1926,13 +1293,24 @@ fec_probe(struct platform_device *pdev)
    > if (ret)
    > goto failed_init;
    >
    > + ret = fec_enet_mii_init(pdev);
    > + if (ret)
    > + goto failed_mii_init;
    > +
    > ret = register_netdev(ndev);
    > if (ret)
    > goto failed_register;
    >
    > + printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
    > + "(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
    > + fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
    > + fep->phy_dev->irq);
    > +
    > return 0;
    >
    > failed_register:
    > + fec_enet_mii_remove(fep);
    > +failed_mii_init:
    > failed_init:
    > clk_disable(fep->clk);
    > clk_put(fep->clk);
    > @@ -1959,6 +1337,7 @@ fec_drv_remove(struct platform_device *pdev)
    > platform_set_drvdata(pdev, NULL);
    >
    > fec_stop(ndev);
    > + fec_enet_mii_remove(fep);
    > clk_disable(fep->clk);
    > clk_put(fep->clk);
    > iounmap((void __iomem *)ndev->base_addr);


    --
    ------------------------------------------------------------------------
    Greg Ungerer -- Principal Engineer EMAIL: gerg@snapgear.com
    SnapGear Group, McAfee PHONE: +61 7 3435 2888
    8 Gardner Close FAX: +61 7 3217 5323
    Milton, QLD, 4064, Australia WEB: http://www.SnapGear.com


    \
     
     \ /
      Last update: 2010-04-21 07:39    [W:0.156 / U:1.112 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site