summaryrefslogtreecommitdiff
path: root/drivers/net/fec.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/fec.c')
-rw-r--r--drivers/net/fec.c409
1 files changed, 323 insertions, 86 deletions
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index edfff92a6d8e..d0e2e690b66e 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -17,6 +17,9 @@
*
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA.
+ *
+ * Support for FEC IEEE 1588.
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
*/
#include <linux/module.h>
@@ -40,34 +43,29 @@
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/swab.h>
#include <linux/phy.h>
#include <linux/fec.h>
#include <asm/cacheflush.h>
#ifndef CONFIG_ARCH_MXC
+#ifndef CONFIG_ARCH_MXS
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#endif
+#endif
#include "fec.h"
+#include "fec_1588.h"
-#ifdef CONFIG_ARCH_MXC
-#include <mach/hardware.h>
+#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS)
#define FEC_ALIGNMENT 0xf
#else
#define FEC_ALIGNMENT 0x3
#endif
-/*
- * Define the fixed address of the FEC hardware.
- */
#if defined(CONFIG_M5272)
-
-static unsigned char fec_mac_default[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
/*
* Some hardware gets it MAC address out of local flash memory.
* if this is non-zero then assume it is the address to get MAC from.
@@ -117,6 +115,15 @@ static unsigned char fec_mac_default[] = {
#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
+#define FEC_ENET_TS_AVAIL ((uint)0x00010000)
+#define FEC_ENET_TS_TIMER ((uint)0x00008000)
+
+#if defined(CONFIG_FEC_1588) && defined(CONFIG_ARCH_MX28)
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII | \
+ FEC_ENET_TS_AVAIL | FEC_ENET_TS_TIMER)
+#else
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
+#endif
/* The FEC stores dest/src/type, data, and checksum for receive packets.
*/
@@ -131,7 +138,8 @@ static unsigned char fec_mac_default[] = {
* account when setting it.
*/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x) || \
+ defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS)
#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
#else
#define OPT_FRAME_SIZE 0
@@ -187,8 +195,18 @@ struct fec_enet_private {
int index;
int link;
int full_duplex;
+ struct completion mdio_done;
+
+ struct fec_ptp_private *ptp_priv;
+ uint ptimer_present;
};
+/*
+ * Define the fixed address of the FEC hardware.
+ */
+static unsigned char fec_mac_default[ETH_ALEN];
+static struct mii_bus *fec_mii_bus;
+
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);
@@ -205,11 +223,24 @@ static void fec_stop(struct net_device *dev);
#define FEC_MMFR_TA (2 << 16)
#define FEC_MMFR_DATA(v) (v & 0xffff)
-#define FEC_MII_TIMEOUT 10000
+#define FEC_MII_TIMEOUT 1000
/* Transmitter timeout */
#define TX_TIMEOUT (2 * HZ)
+#ifdef CONFIG_ARCH_MXS
+static void *swap_buffer(void *bufaddr, int len)
+{
+ int i;
+ unsigned int *buf = bufaddr;
+
+ for (i = 0; i < (len + 3) / 4; i++, buf++)
+ *buf = __swab32(*buf);
+
+ return bufaddr;
+}
+#endif
+
static int
fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
@@ -217,10 +248,12 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct bufdesc *bdp;
void *bufaddr;
unsigned short status;
+ unsigned long estatus;
unsigned long flags;
if (!fep->link) {
/* Link is down or autonegotiation is in progress. */
+ netif_stop_queue(dev);
return NETDEV_TX_BUSY;
}
@@ -258,6 +291,21 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
bufaddr = fep->tx_bounce[index];
}
+ if (fep->ptimer_present) {
+ if (fec_ptp_do_txstamp(skb)) {
+ estatus = BD_ENET_TX_TS;
+ status |= BD_ENET_TX_PTP;
+ } else
+ estatus = 0;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = (estatus | BD_ENET_TX_INT);
+ bdp->cbd_bdu = 0;
+#endif
+ }
+
+#ifdef CONFIG_ARCH_MXS
+ swap_buffer(bufaddr, skb->len);
+#endif
/* Save skb pointer */
fep->tx_skbuff[fep->skb_cur] = skb;
@@ -314,6 +362,7 @@ fec_enet_interrupt(int irq, void * dev_id)
{
struct net_device *dev = dev_id;
struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec_ptp_private *fpp = fep->ptp_priv;
uint int_events;
irqreturn_t ret = IRQ_NONE;
@@ -334,6 +383,17 @@ fec_enet_interrupt(int irq, void * dev_id)
ret = IRQ_HANDLED;
fec_enet_tx(dev);
}
+
+ if (int_events & FEC_ENET_TS_TIMER) {
+ ret = IRQ_HANDLED;
+ if (fep->ptimer_present && fpp)
+ fpp->prtc++;
+ }
+
+ if (int_events & FEC_ENET_MII) {
+ ret = IRQ_HANDLED;
+ complete(&fep->mdio_done);
+ }
} while (int_events);
return ret;
@@ -344,11 +404,14 @@ static void
fec_enet_tx(struct net_device *dev)
{
struct fec_enet_private *fep;
+ struct fec_ptp_private *fpp;
struct bufdesc *bdp;
unsigned short status;
+ unsigned long estatus;
struct sk_buff *skb;
fep = netdev_priv(dev);
+ fpp = fep->ptp_priv;
spin_lock(&fep->hw_lock);
bdp = fep->dirty_tx;
@@ -388,6 +451,19 @@ fec_enet_tx(struct net_device *dev)
if (status & BD_ENET_TX_DEF)
dev->stats.collisions++;
+#if defined(CONFIG_ENHANCED_BD)
+ if (fep->ptimer_present) {
+ estatus = bdp->cbd_esc;
+ if (estatus & BD_ENET_TX_TS)
+ fec_ptp_store_txstamp(fpp, skb, bdp);
+ }
+#elif defined(CONFIG_IN_BAND)
+ if (fep->ptimer_present) {
+ if (status & BD_ENET_TX_PTP)
+ fec_ptp_store_txstamp(fpp, skb, bdp);
+ }
+#endif
+
/* Free the sk buffer associated with this last transmit */
dev_kfree_skb_any(skb);
fep->tx_skbuff[fep->skb_dirty] = NULL;
@@ -421,6 +497,7 @@ static void
fec_enet_rx(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
+ struct fec_ptp_private *fpp = fep->ptp_priv;
struct bufdesc *bdp;
unsigned short status;
struct sk_buff *skb;
@@ -483,7 +560,9 @@ fec_enet_rx(struct net_device *dev)
dma_unmap_single(NULL, bdp->cbd_bufaddr, bdp->cbd_datlen,
DMA_FROM_DEVICE);
-
+#ifdef CONFIG_ARCH_MXS
+ swap_buffer(data, pkt_len);
+#endif
/* This does 16 byte alignment, exactly what we need.
* The packet length includes FCS, but we don't want to
* include that when passing upstream as it messes up
@@ -499,6 +578,9 @@ fec_enet_rx(struct net_device *dev)
skb_reserve(skb, NET_IP_ALIGN);
skb_put(skb, pkt_len - 4); /* Make room */
skb_copy_to_linear_data(skb, data, pkt_len - 4);
+ /* 1588 messeage TS handle */
+ if (fep->ptimer_present)
+ fec_ptp_store_rxstamp(fpp, skb, bdp);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
}
@@ -512,6 +594,11 @@ rx_processing_done:
/* Mark the buffer empty */
status |= BD_ENET_RX_EMPTY;
bdp->cbd_sc = status;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = BD_ENET_RX_INT;
+ bdp->cbd_prot = 0;
+ bdp->cbd_bdu = 0;
+#endif
/* Update BD pointer to next entry */
if (status & BD_ENET_RX_WRAP)
@@ -530,12 +617,13 @@ rx_processing_done:
}
/* ------------------------------------------------------------------------- */
-#ifdef CONFIG_M5272
static void __inline__ fec_get_mac(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
unsigned char *iap, tmpaddr[ETH_ALEN];
+ static int index;
+#ifdef CONFIG_M5272
if (FEC_FLASHMAC) {
/*
* Get MAC address from FLASH.
@@ -548,19 +636,26 @@ static void __inline__ fec_get_mac(struct net_device *dev)
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);
+ }
+#else
+ if (is_valid_ether_addr(fec_mac_default)) {
+ iap = fec_mac_default;
+ }
+#endif
+ else {
+ *((unsigned long *) &tmpaddr[0]) = be32_to_cpu(readl(fep->hwp + FEC_ADDR_LOW));
+ *((unsigned short *) &tmpaddr[4]) = be16_to_cpu(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;
+ if (iap == fec_mac_default) {
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + index;
+ index++;
+}
}
-#endif
/* ------------------------------------------------------------------------- */
@@ -594,8 +689,15 @@ static void fec_enet_adjust_link(struct net_device *dev)
/* Link on or off change */
if (phy_dev->link != fep->link) {
fep->link = phy_dev->link;
- if (phy_dev->link)
+ if (phy_dev->link) {
fec_restart(dev, phy_dev->duplex);
+
+ /* if link becomes up and tx be stopped, start it */
+ if (netif_queue_stopped(dev)) {
+ netif_start_queue(dev);
+ netif_wake_queue(dev);
+ }
+ }
else
fec_stop(dev);
status_change = 1;
@@ -608,18 +710,13 @@ spin_unlock:
phy_print_status(phy_dev);
}
-/*
- * NOTE: a MII transaction is during around 25 us, so polling it...
- */
static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
struct fec_enet_private *fep = bus->priv;
- int timeout = FEC_MII_TIMEOUT;
+ unsigned long time_left;
fep->mii_timeout = 0;
-
- /* clear MII end of transfer bit*/
- writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
+ init_completion(&fep->mdio_done);
/* start a read op */
writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
@@ -627,15 +724,13 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int 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;
- }
+ time_left = wait_for_completion_timeout(&fep->mdio_done,
+ usecs_to_jiffies(FEC_MII_TIMEOUT));
+ if (time_left == 0) {
+ fep->mii_timeout = 1;
+ printk(KERN_ERR "FEC: MDIO read timeout\n");
+ return -ETIMEDOUT;
}
-
/* return value */
return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
}
@@ -644,27 +739,24 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
u16 value)
{
struct fec_enet_private *fep = bus->priv;
- int timeout = FEC_MII_TIMEOUT;
+ unsigned long time_left;
fep->mii_timeout = 0;
+ init_completion(&fep->mdio_done);
- /* 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 |
+ /* start a write op */
+ writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
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;
- }
+ time_left = wait_for_completion_timeout(&fep->mdio_done,
+ usecs_to_jiffies(FEC_MII_TIMEOUT));
+ if (time_left == 0) {
+ fep->mii_timeout = 1;
+ printk(KERN_ERR "FEC: MDIO write timeout\n");
+ return -ETIMEDOUT;
}
return 0;
@@ -680,12 +772,15 @@ static int fec_enet_mii_probe(struct net_device *dev)
struct fec_enet_private *fep = netdev_priv(dev);
struct phy_device *phy_dev = NULL;
int phy_addr;
+ int fec_index = fep->pdev->id > 0 ? fep->pdev->id : 0;
fep->phy_dev = NULL;
- /* find the first phy */
+ /* find the phy, assuming fec index corresponds to addr */
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
if (fep->mii_bus->phy_map[phy_addr]) {
+ if (fec_index--)
+ continue;
phy_dev = fep->mii_bus->phy_map[phy_addr];
break;
}
@@ -699,7 +794,7 @@ static int fec_enet_mii_probe(struct net_device *dev)
/* 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);
+ fep->phy_interface);
if (IS_ERR(phy_dev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
return PTR_ERR(phy_dev);
@@ -721,7 +816,7 @@ static int fec_enet_mii_probe(struct net_device *dev)
return 0;
}
-static int fec_enet_mii_init(struct platform_device *pdev)
+static struct mii_bus *fec_enet_mii_init(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(dev);
@@ -733,6 +828,10 @@ static int fec_enet_mii_init(struct platform_device *pdev)
* 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;
+#ifdef CONFIG_ARCH_MXS
+ /* Can't get phy(8720) ID when set to 2.5M on MX28, lower it*/
+ fep->phy_speed <<= 2;
+#endif
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
fep->mii_bus = mdiobus_alloc();
@@ -763,14 +862,14 @@ static int fec_enet_mii_init(struct platform_device *pdev)
if (mdiobus_register(fep->mii_bus))
goto err_out_free_mdio_irq;
- return 0;
+ return 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;
+ return ERR_PTR(err);
}
static void fec_enet_mii_remove(struct fec_enet_private *fep)
@@ -834,7 +933,7 @@ static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
if (!phydev)
return -ENODEV;
- return phy_mii_ioctl(phydev, if_mii(rq), cmd);
+ return phy_mii_ioctl(phydev, rq, cmd);
}
static void fec_enet_free_buffers(struct net_device *dev)
@@ -880,6 +979,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev)
bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data,
FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
bdp->cbd_sc = BD_ENET_RX_EMPTY;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = BD_ENET_RX_INT;
+#endif
bdp++;
}
@@ -893,6 +995,9 @@ static int fec_enet_alloc_buffers(struct net_device *dev)
bdp->cbd_sc = 0;
bdp->cbd_bufaddr = 0;
+#ifdef CONFIG_ENHANCED_BD
+ bdp->cbd_esc = BD_ENET_TX_INT;
+#endif
bdp++;
}
@@ -912,7 +1017,7 @@ fec_enet_open(struct net_device *dev)
/* I should reset the ring buffers here, but I don't yet know
* a simple way to do that.
*/
-
+ clk_enable(fep->clk);
ret = fec_enet_alloc_buffers(dev);
if (ret)
return ret;
@@ -924,7 +1029,7 @@ fec_enet_open(struct net_device *dev)
return ret;
}
phy_start(fep->phy_dev);
- netif_start_queue(dev);
+ fec_restart(dev, fep->phy_dev->duplex);
fep->opened = 1;
return 0;
}
@@ -936,13 +1041,14 @@ fec_enet_close(struct net_device *dev)
/* Don't know what to do yet. */
fep->opened = 0;
- netif_stop_queue(dev);
fec_stop(dev);
- if (fep->phy_dev)
+ if (fep->phy_dev) {
+ phy_stop(fep->phy_dev);
phy_disconnect(fep->phy_dev);
-
+ }
fec_enet_free_buffers(dev);
+ clk_disable(fep->clk);
return 0;
}
@@ -1058,6 +1164,31 @@ static const struct net_device_ops fec_netdev_ops = {
.ndo_do_ioctl = fec_enet_ioctl,
};
+static int fec_mac_addr_setup(char *mac_addr)
+{
+ char *ptr, *p = mac_addr;
+ unsigned long tmp;
+ int i = 0, ret = 0;
+
+ while (p && (*p) && i < 6) {
+ ptr = strchr(p, ':');
+ if (ptr)
+ *ptr++ = '\0';
+
+ if (strlen(p)) {
+ ret = strict_strtoul(p, 16, &tmp);
+ if (ret < 0 || tmp > 0xff)
+ break;
+ fec_mac_default[i++] = tmp;
+ }
+ p = ptr;
+ }
+
+ return 0;
+}
+
+__setup("fec_mac=", fec_mac_addr_setup);
+
/*
* XXX: We need to clean up on failure exits here.
*
@@ -1085,21 +1216,7 @@ static int fec_enet_init(struct net_device *dev, int index)
fep->netdev = dev;
/* Set the Ethernet address */
-#ifdef CONFIG_M5272
fec_get_mac(dev);
-#else
- {
- unsigned long l;
- l = readl(fep->hwp + FEC_ADDR_LOW);
- dev->dev_addr[0] = (unsigned char)((l & 0xFF000000) >> 24);
- dev->dev_addr[1] = (unsigned char)((l & 0x00FF0000) >> 16);
- dev->dev_addr[2] = (unsigned char)((l & 0x0000FF00) >> 8);
- dev->dev_addr[3] = (unsigned char)((l & 0x000000FF) >> 0);
- l = readl(fep->hwp + FEC_ADDR_HIGH);
- dev->dev_addr[4] = (unsigned char)((l & 0xFF000000) >> 24);
- dev->dev_addr[5] = (unsigned char)((l & 0x00FF0000) >> 16);
- }
-#endif
/* Set receive and transmit descriptor base. */
fep->rx_bd_base = cbd_base;
@@ -1151,11 +1268,20 @@ fec_restart(struct net_device *dev, int duplex)
{
struct fec_enet_private *fep = netdev_priv(dev);
int i;
+ uint ret = 0;
+ u32 temp_mac[2];
+ unsigned long reg;
+ int val;
/* Whack a reset. We should wait for this. */
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
+ /* Reset fec will reset MAC to zero, reconfig it again */
+ memcpy(&temp_mac, dev->dev_addr, ETH_ALEN);
+ writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW);
+ writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH);
+
/* Clear any outstanding interrupt. */
writel(0xffc00000, fep->hwp + FEC_IEVENT);
@@ -1199,9 +1325,46 @@ fec_restart(struct net_device *dev, int duplex)
}
fep->full_duplex = duplex;
+#ifdef CONFIG_ARCH_MXS
+
+ reg = readl(fep->hwp + FEC_R_CNTRL);
+
+ /* Enable flow control and length check */
+ reg |= (0x40000000 | 0x00000020);
+
+ /* Check MII or RMII */
+ if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+ reg |= 0x00000100;
+ else
+ reg &= ~0x00000100;
+
+ /* Check 10M or 100M */
+ if (fep->phy_dev && fep->phy_dev->speed == SPEED_100)
+ reg &= ~0x00000200; /* 100M */
+ else
+ reg |= 0x00000200; /* 10M */
+
+ writel(reg, fep->hwp + FEC_R_CNTRL);
+
+#endif
/* Set MII speed */
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+ if (fep->ptimer_present) {
+ /* Set Timer count */
+ ret = fec_ptp_start(fep->ptp_priv);
+ if (ret) {
+ fep->ptimer_present = 0;
+ reg = 0x0;
+ } else
+#ifdef CONFIG_ARCH_MX28
+ reg = 0x00000010;
+#else
+ reg = 0x0;
+#endif
+ } else
+ reg = 0x0;
+
#ifdef FEC_MIIGSK_ENR
if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
/* disable the gasket and wait */
@@ -1210,7 +1373,10 @@ fec_restart(struct net_device *dev, int duplex)
udelay(1);
/* configure the gasket: RMII, 50 MHz, no loopback, no echo */
- writel(1, fep->hwp + FEC_MIIGSK_CFGR);
+ val = 1;
+ if (fep->phy_dev && fep->phy_dev->speed == SPEED_10)
+ val |= 1 << 6;
+ writel(val, fep->hwp + FEC_MIIGSK_CFGR);
/* re-enable the gasket */
writel(2, fep->hwp + FEC_MIIGSK_ENR);
@@ -1218,11 +1384,12 @@ fec_restart(struct net_device *dev, int duplex)
#endif
/* And last, enable the transmit and receive processing */
- writel(2, fep->hwp + FEC_ECNTRL);
+ reg |= 0x00000002;
+ writel(reg, fep->hwp + FEC_ECNTRL);
writel(0, fep->hwp + FEC_R_DES_ACTIVE);
/* Enable interrupts we wish to service */
- writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK);
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
}
static void
@@ -1242,10 +1409,24 @@ fec_stop(struct net_device *dev)
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
+#ifdef CONFIG_ARCH_MXS
+ /* Check MII or RMII */
+ if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
+ writel(readl(fep->hwp + FEC_R_CNTRL) | 0x100,
+ fep->hwp + FEC_R_CNTRL);
+ else
+ writel(readl(fep->hwp + FEC_R_CNTRL) & ~0x100,
+ fep->hwp + FEC_R_CNTRL);
+#endif
/* Clear outstanding MII command interrupts. */
writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
-
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
+ if (fep->ptimer_present)
+ fec_ptp_stop(fep->ptp_priv);
+ writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+
+ netif_stop_queue(dev);
+ fep->link = 0;
}
static int __devinit
@@ -1290,6 +1471,9 @@ fec_probe(struct platform_device *pdev)
if (pdata)
fep->phy_interface = pdata->phy;
+ if (pdata && !is_valid_ether_addr(fec_mac_default))
+ memcpy(fec_mac_default, pdata->mac, sizeof(fec_mac_default));
+
/* This device has up to three irqs on some platforms */
for (i = 0; i < 3; i++) {
irq = platform_get_irq(pdev, i);
@@ -1313,22 +1497,63 @@ fec_probe(struct platform_device *pdev)
}
clk_enable(fep->clk);
+ /* PHY reset should be done during clock on */
+ if (pdata && pdata->init)
+ ret = pdata->init();
+ if (ret)
+ goto failed_platform_init;
+ /*
+ * The priority for getting MAC address is:
+ * (1) kernel command line fec_mac = xx:xx:xx...
+ * (2) platform data mac field got from fuse etc
+ * (3) bootloader set the FEC mac register
+ */
+
+ if (pdata && !is_valid_ether_addr(fec_mac_default) &&
+ pdata->mac && is_valid_ether_addr(pdata->mac))
+ memcpy(fec_mac_default, pdata->mac,
+ sizeof(fec_mac_default));
+
ret = fec_enet_init(ndev, 0);
if (ret)
goto failed_init;
- ret = fec_enet_mii_init(pdev);
- if (ret)
+ if (pdev->id == 0) {
+ fec_mii_bus = fec_enet_mii_init(pdev);
+ if (IS_ERR(fec_mii_bus)) {
+ ret = -ENOMEM;
goto failed_mii_init;
+ }
+ } else {
+ fep->mii_bus = fec_mii_bus;
+ }
+
+ if (fec_ptp_malloc_priv(&(fep->ptp_priv))) {
+ if (fep->ptp_priv) {
+ fep->ptp_priv->hwp = fep->hwp;
+ ret = fec_ptp_init(fep->ptp_priv, pdev->id);
+ if (ret)
+ printk(KERN_WARNING
+ "IEEE1588: ptp-timer is unavailable\n");
+ else
+ fep->ptimer_present = 1;
+ } else
+ printk(KERN_ERR "IEEE1588: failed to malloc memory\n");
+ }
ret = register_netdev(ndev);
if (ret)
goto failed_register;
+ clk_disable(fep->clk);
+
return 0;
failed_register:
fec_enet_mii_remove(fep);
+ if (fep->ptimer_present)
+ fec_ptp_cleanup(fep->ptp_priv);
+ kfree(fep->ptp_priv);
failed_mii_init:
failed_init:
clk_disable(fep->clk);
@@ -1342,6 +1567,7 @@ failed_clk:
failed_irq:
iounmap((void __iomem *)ndev->base_addr);
failed_ioremap:
+failed_platform_init:
free_netdev(ndev);
return ret;
@@ -1352,14 +1578,20 @@ fec_drv_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
+ struct fec_platform_data *pdata = pdev->dev.platform_data;
platform_set_drvdata(pdev, NULL);
fec_stop(ndev);
fec_enet_mii_remove(fep);
+ if (pdata && pdata->uninit)
+ pdata->uninit();
clk_disable(fep->clk);
clk_put(fep->clk);
iounmap((void __iomem *)ndev->base_addr);
+ if (fep->ptimer_present)
+ fec_ptp_cleanup(fep->ptp_priv);
+ kfree(fep->ptp_priv);
unregister_netdev(ndev);
free_netdev(ndev);
return 0;
@@ -1373,9 +1605,12 @@ fec_suspend(struct platform_device *dev, pm_message_t state)
if (ndev) {
fep = netdev_priv(ndev);
- if (netif_running(ndev))
- fec_enet_close(ndev);
- clk_disable(fep->clk);
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ fec_stop(ndev);
+ netif_carrier_off(ndev);
+ clk_disable(fep->clk);
+ }
}
return 0;
}
@@ -1388,9 +1623,11 @@ fec_resume(struct platform_device *dev)
if (ndev) {
fep = netdev_priv(ndev);
- clk_enable(fep->clk);
- if (netif_running(ndev))
- fec_enet_open(ndev);
+ if (netif_running(ndev)) {
+ clk_enable(fep->clk);
+ fec_restart(ndev, fep->full_duplex);
+ netif_device_attach(ndev);
+ }
}
return 0;
}