summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJason <r64343@freescale.com>2010-01-26 22:39:46 +0800
committerAlejandro Gonzalez <alex.gonzalez@digi.com>2010-05-25 11:13:32 +0200
commit018e46003434997ab3b9a9515185faa0ba762018 (patch)
tree8868be63e53fc97930113cd33b411a9a21633a68 /drivers
parente90da503e6f2a81b7b42666f8544b197e5b489c8 (diff)
ENGR00117744-2 i.MX28 EVK ENET(Single) support
FEC driver support for i.MX28 Signed-off-by:Jason Liu <r64343@freescale.com> Signed-off-by: Alejandro Gonzalez <alex.gonzalez@digi.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--drivers/net/fec.c201
-rw-r--r--drivers/net/fec.h5
3 files changed, 192 insertions, 16 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index de871061b221..26612275d21f 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1884,7 +1884,7 @@ config 68360_ENET
config FEC
bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
- depends on M523x || M527x || M5272 || M528x || M520x || M532x || ARCH_MX25 || MACH_MX27 || ARCH_MX35 || ARCH_MX5
+ depends on M523x || M527x || M5272 || M528x || M520x || M532x || ARCH_MX25 || MACH_MX27 || ARCH_MX35 || ARCH_MX5 || ARCH_MX28
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 43b6cf2d491c..76a3f005fe29 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -40,19 +40,27 @@
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/swab.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"
-#ifdef CONFIG_ARCH_MXC
+#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS)
#include <mach/hardware.h>
#define FEC_ALIGNMENT 0xf
+#ifdef CONFIG_ARCH_MXS
+#include <mach/device.h>
+#endif
+static unsigned char fec_mac_default[6];
#else
#define FEC_ALIGNMENT 0x3
#endif
@@ -160,7 +168,8 @@ typedef struct {
* 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
@@ -211,6 +220,7 @@ struct fec_enet_private {
uint phy_speed;
phy_info_t const *phy;
struct work_struct phy_task;
+ phy_interface_t phy_interface;
uint sequence_done;
uint mii_phy_task_queued;
@@ -222,6 +232,7 @@ struct fec_enet_private {
int link;
int old_link;
int full_duplex;
+ struct completion anc_done;
};
static void fec_enet_mii(struct net_device *dev);
@@ -292,6 +303,18 @@ static int mii_queue(struct net_device *dev, int request,
#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */
#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */
+#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)
@@ -341,6 +364,9 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
bufaddr = fep->tx_bounce[index];
}
+#ifdef CONFIG_ARCH_MXS
+ swap_buffer(bufaddr, skb->len);
+#endif
/* Save skb pointer */
fep->tx_skbuff[fep->skb_cur] = skb;
@@ -574,7 +600,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
@@ -1141,6 +1169,40 @@ static phy_info_t phy_info_dp83848= {
},
};
+#define MII_LAN872X_PHYSCSR 31 /* Sepecial PHY Status Register */
+
+static void mii_parse_lan872x_scsr(uint mii_reg, struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ uint *s = &(fep->phy_status);
+
+ *s &= ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
+
+ if (!(mii_reg & 0x1000)) {
+ mii_queue_unlocked(dev,
+ mk_mii_read(MII_LAN872X_PHYSCSR), mii_parse_lan872x_scsr);
+ return ;
+ }
+
+ *s |= PHY_STAT_ANC;
+
+ if (mii_reg & 0x0004) { /* 10MBps? */
+ if (mii_reg & 0x0010) /* Full Duplex? */
+ *s |= PHY_STAT_10FDX;
+ else
+ *s |= PHY_STAT_10HDX;
+ } else { /* 100 Mbps? */
+ if (mii_reg & 0x0010) /* Full Duplex? */
+ *s |= PHY_STAT_100FDX;
+ else
+ *s |= PHY_STAT_100HDX;
+ }
+
+ complete(&fep->anc_done);
+
+ fep->full_duplex =
+ !!(fep->phy_status & (PHY_STAT_10FDX | PHY_STAT_100FDX));
+}
static phy_info_t phy_info_lan8700 = {
0x0007C0C,
"LAN8700",
@@ -1183,6 +1245,28 @@ static phy_info_t phy_info_lan8710 = {
},
};
+static phy_info_t phy_info_lan8720 = {
+ 0x0007C0F,
+ "LAN8720",
+ (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_read(MII_LAN872X_PHYSCSR), mii_parse_lan872x_scsr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* act_int */
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+
/* ------------------------------------------------------------------------- */
static phy_info_t const * const phy_info[] = {
@@ -1194,6 +1278,7 @@ static phy_info_t const * const phy_info[] = {
&phy_info_dp83848,
&phy_info_lan8700,
&phy_info_lan8710,
+ &phy_info_lan8720,
NULL
};
@@ -1424,6 +1509,8 @@ mii_discover_phy3(uint mii_reg, struct net_device *dev)
fep->phy = phy_info[i];
fep->phy_id_done = 1;
+
+ clk_disable(fep->clk);
}
/* Scan all of the MII PHY addresses looking for someone to respond
@@ -1445,7 +1532,10 @@ mii_discover_phy(uint mii_reg, struct net_device *dev)
mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR2),
mii_discover_phy3);
} else {
+#ifndef CONFIG_ARCH_MXS
+ /* PHY not working with first access on MX28 evk*/
fep->phy_addr++;
+#endif
mii_queue_unlocked(dev, mk_mii_read(MII_REG_PHYIR1),
mii_discover_phy);
}
@@ -1564,6 +1654,7 @@ fec_enet_open(struct net_device *dev)
fec_restart(dev, 1);
if (fep->phy) {
+ init_completion(&fep->anc_done);
mii_do_cmd(dev, fep->phy->ack_int);
mii_do_cmd(dev, fep->phy->config);
mii_do_cmd(dev, phy_cmd_config); /* display configuration */
@@ -1579,7 +1670,10 @@ fec_enet_open(struct net_device *dev)
schedule();
mii_do_cmd(dev, fep->phy->startup);
+ wait_for_completion_timeout(&fep->anc_done,
+ msecs_to_jiffies(10000));
}
+ fec_restart(dev, fep->full_duplex);
/* Set the initial link state to true. A lot of hardware
* based on this device does not implement a PHY interrupt,
@@ -1719,6 +1813,51 @@ static const struct net_device_ops fec_netdev_ops = {
.ndo_tx_timeout = fec_timeout,
.ndo_set_mac_address = fec_set_mac_address,
};
+#ifdef CONFIG_ARCH_MXS
+
+static int fec_set_hw_mac(struct net_device *dev, char *mac_addr)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ char *addr = mac_addr;
+
+ if (!is_valid_ether_addr(addr))
+ return -EADDRNOTAVAIL;
+
+ writel(addr[3] | (addr[2] << 8) | (addr[1] << 16) | (addr[0] << 24),
+ fep->hwp + FEC_ADDR_LOW);
+
+ writel((addr[5] << 16) | (addr[4] << 24) | 0x8808,
+ fep->hwp + FEC_ADDR_HIGH);
+
+ return 0;
+
+}
+
+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);
+#endif
/*
* XXX: We need to clean up on failure exits here.
@@ -1753,6 +1892,10 @@ int __init fec_enet_init(struct net_device *dev, int index)
#else
{
unsigned long l;
+#ifdef CONFIG_ARCH_MXS
+ if (fec_set_hw_mac(dev, fec_mac_default))
+ printk(KERN_WARNING"Invalid MAC address....\n");
+#endif
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);
@@ -1783,6 +1926,10 @@ int __init fec_enet_init(struct net_device *dev, int index)
fep->phy_speed = ((((clk_get_rate(fep->clk) / 2 + 4999999)
/ 2500000) / 2) & 0x3F) << 1;
+#ifdef CONFIG_ARCH_MXS
+ /* Can't get phy(8720) ID when set to 2.5M, lower it*/
+ fep->phy_speed <<= 2;
+#endif
/* Initialize the receive buffer descriptors. */
bdp = fep->rx_bd_base;
for (i = 0; i < RX_RING_SIZE; i++) {
@@ -1831,11 +1978,16 @@ fec_restart(struct net_device *dev, int duplex)
{
struct fec_enet_private *fep = netdev_priv(dev);
int i;
-
+#ifdef CONFIG_ARCH_MXS
+ unsigned long reg;
+#endif
/* Whack a reset. We should wait for this. */
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
-
+#ifdef CONFIG_ARCH_MXS
+ /* Reset fec will reset MAC to zero, reconfig it again */
+ fec_set_hw_mac(dev, fec_mac_default);
+#endif
/* Clear any outstanding interrupt. */
writel(0xffc00000, fep->hwp + FEC_IEVENT);
@@ -1846,7 +1998,7 @@ fec_restart(struct net_device *dev, int duplex)
writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
#endif
-
+#ifdef CONFIG_ARCH_MXC
if (cpu_is_mx25()) {
/*
* Set up the MII gasket for RMII mode
@@ -1864,7 +2016,7 @@ fec_restart(struct net_device *dev, int duplex)
/* re-enable the gasket */
__raw_writel(FEC_MIIGSK_ENR_EN, fep->hwp + FEC_MIIGSK_ENR);
}
-
+#endif
/* Set maximum receive buffer size. */
writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
@@ -1897,6 +2049,28 @@ 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_status & 0x0004)
+ reg |= 0x00000200;
+ else
+ reg &= ~0x00000200;
+
+ writel(reg, fep->hwp + FEC_R_CNTRL);
+
+#endif
/* Set MII speed */
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
@@ -1970,6 +2144,12 @@ fec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ndev);
+ if (pdata) {
+ fep->phy_interface = pdata->phy;
+ if (pdata->init && pdata->init())
+ goto failed_platform_init;
+ }
+
/* This device has up to three irqs on some platforms */
for (i = 0; i < 3; i++) {
irq = platform_get_irq(pdev, i);
@@ -1986,10 +2166,6 @@ fec_probe(struct platform_device *pdev)
}
}
- if (pdata && pdata->init)
- if (pdata->init())
- goto failed_platform_init;
-
fep->clk = clk_get(&pdev->dev, "fec_clk");
if (IS_ERR(fep->clk)) {
ret = PTR_ERR(fep->clk);
@@ -2005,7 +2181,6 @@ fec_probe(struct platform_device *pdev)
if (ret)
goto failed_register;
- clk_disable(fep->clk);
return 0;
failed_register:
@@ -2013,7 +2188,6 @@ failed_init:
clk_disable(fep->clk);
clk_put(fep->clk);
failed_clk:
-failed_platform_init:
for (i = 0; i < 3; i++) {
irq = platform_get_irq(pdev, i);
if (irq > 0)
@@ -2022,6 +2196,7 @@ failed_platform_init:
failed_irq:
iounmap((void __iomem *)ndev->base_addr);
failed_ioremap:
+failed_platform_init:
free_netdev(ndev);
return ret;
diff --git a/drivers/net/fec.h b/drivers/net/fec.h
index 0e8dbba9f817..5f04d4c00c4f 100644
--- a/drivers/net/fec.h
+++ b/drivers/net/fec.h
@@ -14,7 +14,8 @@
/****************************************************************************/
#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)
/*
* Just figures, Motorola would have to change the offsets for
* registers in the same peripheral device on different models
@@ -80,7 +81,7 @@
/*
* Define the buffer descriptor structure.
*/
-#ifdef CONFIG_ARCH_MXC
+#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_MXS)
struct bufdesc {
unsigned short cbd_datlen; /* Data length */
unsigned short cbd_sc; /* Control and status info */