Message ID | 1270037444-8470-1-git-send-email-bryan.wu@canonical.com |
---|---|
State | Awaiting Upstream |
Headers | show |
On 10 Mar 31, Bryan Wu wrote: > BugLink: http://bugs.launchpad.net/bugs/457878 > > v2: > - remove duplicated phy_speed caculation > - fix the phy_speed caculation according to the DataSheet > > v1: > - 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> Acked-by: Amit Kucheria <amit.kucheria@canonical.com> > --- > drivers/net/Kconfig | 1 + > drivers/net/fec.c | 1128 ++++++++++++--------------------------------------- > 2 files changed, 252 insertions(+), 877 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..848eb19 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,311 @@ 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 (= clk_get_rate() / 2 * phy_speed) > + */ > + fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 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; > + > + if (!netif_running(dev)) > + return -EINVAL; > > - mii_do_cmd(dev, fep->phy->ack_int); > - mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ > + if (!phydev) > + return -ENODEV; > > - return IRQ_HANDLED; > + return phy_mii_ioctl(phydev, if_mii(rq), cmd); > } > -#endif > > static void fec_enet_free_buffers(struct net_device *dev) > { > @@ -1509,35 +915,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 +929,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 +1046,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 +1070,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,20 +1096,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; > - > - /* Set MII speed to 2.5 MHz */ > - fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999) > - / 2500000) / 2) & 0x3F) << 1; > + dev->ethtool_ops = &fec_enet_ethtool_ops; > > /* Initialize the receive buffer descriptors. */ > bdp = fep->rx_bd_base; > @@ -1760,13 +1130,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 +1198,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 +1221,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 +1252,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 +1288,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 +1332,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); > -- > 1.7.0.1 >
On Wed, Mar 31, 2010 at 08:10:44PM +0800, Bryan Wu wrote: > BugLink: http://bugs.launchpad.net/bugs/457878 > > v2: > - remove duplicated phy_speed caculation > - fix the phy_speed caculation according to the DataSheet > > v1: > - 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> Acked-by: Sascha Hauer <s.hauer@pengutronix.de> Sascha > --- > drivers/net/Kconfig | 1 + > drivers/net/fec.c | 1128 ++++++++++++--------------------------------------- > 2 files changed, 252 insertions(+), 877 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..848eb19 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,311 @@ 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 (= clk_get_rate() / 2 * phy_speed) > + */ > + fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 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; > + > + if (!netif_running(dev)) > + return -EINVAL; > > - mii_do_cmd(dev, fep->phy->ack_int); > - mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ > + if (!phydev) > + return -ENODEV; > > - return IRQ_HANDLED; > + return phy_mii_ioctl(phydev, if_mii(rq), cmd); > } > -#endif > > static void fec_enet_free_buffers(struct net_device *dev) > { > @@ -1509,35 +915,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 +929,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 +1046,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 +1070,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,20 +1096,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; > - > - /* Set MII speed to 2.5 MHz */ > - fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999) > - / 2500000) / 2) & 0x3F) << 1; > + dev->ethtool_ops = &fec_enet_ethtool_ops; > > /* Initialize the receive buffer descriptors. */ > bdp = fep->rx_bd_base; > @@ -1760,13 +1130,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 +1198,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 +1221,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 +1252,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 +1288,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 +1332,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); > -- > 1.7.0.1 > >
From: Sascha Hauer <s.hauer@pengutronix.de> Date: Wed, 31 Mar 2010 14:26:04 +0200 > On Wed, Mar 31, 2010 at 08:10:44PM +0800, Bryan Wu wrote: >> BugLink: http://bugs.launchpad.net/bugs/457878 >> >> v2: >> - remove duplicated phy_speed caculation >> - fix the phy_speed caculation according to the DataSheet >> >> v1: >> - 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> > > Acked-by: Sascha Hauer <s.hauer@pengutronix.de> Applied to net-next-2.6, thanks.
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..848eb19 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,311 @@ 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 (= clk_get_rate() / 2 * phy_speed) + */ + fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 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; + + if (!netif_running(dev)) + return -EINVAL; - mii_do_cmd(dev, fep->phy->ack_int); - mii_do_cmd(dev, phy_cmd_relink); /* restart and display status */ + if (!phydev) + return -ENODEV; - return IRQ_HANDLED; + return phy_mii_ioctl(phydev, if_mii(rq), cmd); } -#endif static void fec_enet_free_buffers(struct net_device *dev) { @@ -1509,35 +915,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 +929,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 +1046,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 +1070,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,20 +1096,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; - - /* Set MII speed to 2.5 MHz */ - fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999) - / 2500000) / 2) & 0x3F) << 1; + dev->ethtool_ops = &fec_enet_ethtool_ops; /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; @@ -1760,13 +1130,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 +1198,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 +1221,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 +1252,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 +1288,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 +1332,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);
BugLink: http://bugs.launchpad.net/bugs/457878 v2: - remove duplicated phy_speed caculation - fix the phy_speed caculation according to the DataSheet v1: - 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> --- drivers/net/Kconfig | 1 + drivers/net/fec.c | 1128 ++++++++++++--------------------------------------- 2 files changed, 252 insertions(+), 877 deletions(-)