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