Commit 230dec61 authored by Shawn Guo's avatar Shawn Guo Committed by David S. Miller

net/fec: add imx6q enet support

The imx6q enet is a derivative of imx28 enet controller.  It fixed
the frame endian issue found on imx28, and added 1 Gbps support.

It also fixes a typo on vendor name in Kconfig.
Signed-off-by: default avatarShawn Guo <shawn.guo@linaro.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c828827f
...@@ -7,7 +7,7 @@ config NET_VENDOR_FREESCALE ...@@ -7,7 +7,7 @@ config NET_VENDOR_FREESCALE
default y default y
depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \ depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \
M523x || M527x || M5272 || M528x || M520x || M532x || \ M523x || M527x || M5272 || M528x || M520x || M532x || \
IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC || \ ARCH_MXC || ARCH_MXS || \
(PPC_MPC52xx && PPC_BESTCOMM) (PPC_MPC52xx && PPC_BESTCOMM)
---help--- ---help---
If you have a network (Ethernet) card belonging to this class, say Y If you have a network (Ethernet) card belonging to this class, say Y
...@@ -16,16 +16,15 @@ config NET_VENDOR_FREESCALE ...@@ -16,16 +16,15 @@ config NET_VENDOR_FREESCALE
Note that the answer to this question doesn't directly affect the Note that the answer to this question doesn't directly affect the
kernel: saying N will just cause the configurator to skip all kernel: saying N will just cause the configurator to skip all
the questions about IBM devices. If you say Y, you will be asked for the questions about Freescale devices. If you say Y, you will be
your specific card in the following questions. asked for your specific card in the following questions.
if NET_VENDOR_FREESCALE if NET_VENDOR_FREESCALE
config FEC config FEC
bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)" bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)"
depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \ depends on (M523x || M527x || M5272 || M528x || M520x || M532x || \
IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC) ARCH_MXC || ARCH_MXS)
default IMX_HAVE_PLATFORM_FEC || MXS_HAVE_PLATFORM_FEC if ARM
select PHYLIB select PHYLIB
---help--- ---help---
Say Y here if you want to use the built-in 10/100 Fast ethernet Say Y here if you want to use the built-in 10/100 Fast ethernet
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA. * Copyright (c) 2004-2006 Macq Electronique SA.
* *
* Copyright (C) 2010 Freescale Semiconductor, Inc. * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -72,6 +72,8 @@ ...@@ -72,6 +72,8 @@
#define FEC_QUIRK_SWAP_FRAME (1 << 1) #define FEC_QUIRK_SWAP_FRAME (1 << 1)
/* Controller uses gasket */ /* Controller uses gasket */
#define FEC_QUIRK_USE_GASKET (1 << 2) #define FEC_QUIRK_USE_GASKET (1 << 2)
/* Controller has GBIT support */
#define FEC_QUIRK_HAS_GBIT (1 << 3)
static struct platform_device_id fec_devtype[] = { static struct platform_device_id fec_devtype[] = {
{ {
...@@ -87,6 +89,9 @@ static struct platform_device_id fec_devtype[] = { ...@@ -87,6 +89,9 @@ static struct platform_device_id fec_devtype[] = {
}, { }, {
.name = "imx28-fec", .name = "imx28-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME,
}, {
.name = "imx6q-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT,
}, { }, {
/* sentinel */ /* sentinel */
} }
...@@ -97,12 +102,14 @@ enum imx_fec_type { ...@@ -97,12 +102,14 @@ enum imx_fec_type {
IMX25_FEC = 1, /* runs on i.mx25/50/53 */ IMX25_FEC = 1, /* runs on i.mx25/50/53 */
IMX27_FEC, /* runs on i.mx27/35/51 */ IMX27_FEC, /* runs on i.mx27/35/51 */
IMX28_FEC, IMX28_FEC,
IMX6Q_FEC,
}; };
static const struct of_device_id fec_dt_ids[] = { static const struct of_device_id fec_dt_ids[] = {
{ .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], },
{ .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], },
{ .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], },
{ .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, fec_dt_ids); MODULE_DEVICE_TABLE(of, fec_dt_ids);
...@@ -373,6 +380,7 @@ fec_restart(struct net_device *ndev, int duplex) ...@@ -373,6 +380,7 @@ fec_restart(struct net_device *ndev, int duplex)
int i; int i;
u32 temp_mac[2]; u32 temp_mac[2];
u32 rcntl = OPT_FRAME_SIZE | 0x04; u32 rcntl = OPT_FRAME_SIZE | 0x04;
u32 ecntl = 0x2; /* ETHEREN */
/* Whack a reset. We should wait for this. */ /* Whack a reset. We should wait for this. */
writel(1, fep->hwp + FEC_ECNTRL); writel(1, fep->hwp + FEC_ECNTRL);
...@@ -442,18 +450,23 @@ fec_restart(struct net_device *ndev, int duplex) ...@@ -442,18 +450,23 @@ fec_restart(struct net_device *ndev, int duplex)
/* Enable flow control and length check */ /* Enable flow control and length check */
rcntl |= 0x40000000 | 0x00000020; rcntl |= 0x40000000 | 0x00000020;
/* MII or RMII */ /* RGMII, RMII or MII */
if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII)
rcntl |= (1 << 6);
else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
rcntl |= (1 << 8); rcntl |= (1 << 8);
else else
rcntl &= ~(1 << 8); rcntl &= ~(1 << 8);
/* 10M or 100M */ /* 1G, 100M or 10M */
if (fep->phy_dev && fep->phy_dev->speed == SPEED_100) if (fep->phy_dev) {
rcntl &= ~(1 << 9); if (fep->phy_dev->speed == SPEED_1000)
else ecntl |= (1 << 5);
rcntl |= (1 << 9); else if (fep->phy_dev->speed == SPEED_100)
rcntl &= ~(1 << 9);
else
rcntl |= (1 << 9);
}
} else { } else {
#ifdef FEC_MIIGSK_ENR #ifdef FEC_MIIGSK_ENR
if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) { if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) {
...@@ -478,8 +491,15 @@ fec_restart(struct net_device *ndev, int duplex) ...@@ -478,8 +491,15 @@ fec_restart(struct net_device *ndev, int duplex)
} }
writel(rcntl, fep->hwp + FEC_R_CNTRL); writel(rcntl, fep->hwp + FEC_R_CNTRL);
if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
/* enable ENET endian swap */
ecntl |= (1 << 8);
/* enable ENET store and forward mode */
writel(1 << 8, fep->hwp + FEC_X_WMRK);
}
/* And last, enable the transmit and receive processing */ /* And last, enable the transmit and receive processing */
writel(2, fep->hwp + FEC_ECNTRL); writel(ecntl, fep->hwp + FEC_ECNTRL);
writel(0, fep->hwp + FEC_R_DES_ACTIVE); writel(0, fep->hwp + FEC_R_DES_ACTIVE);
/* Enable interrupts we wish to service */ /* Enable interrupts we wish to service */
...@@ -490,6 +510,8 @@ static void ...@@ -490,6 +510,8 @@ static void
fec_stop(struct net_device *ndev) fec_stop(struct net_device *ndev)
{ {
struct fec_enet_private *fep = netdev_priv(ndev); struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
/* We cannot expect a graceful transmit stop without link !!! */ /* We cannot expect a graceful transmit stop without link !!! */
if (fep->link) { if (fep->link) {
...@@ -504,6 +526,10 @@ fec_stop(struct net_device *ndev) ...@@ -504,6 +526,10 @@ fec_stop(struct net_device *ndev)
udelay(10); udelay(10);
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
/* We have to keep ENET enabled to have MII interrupt stay working */
if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
writel(2, fep->hwp + FEC_ECNTRL);
} }
...@@ -918,6 +944,8 @@ static int fec_enet_mdio_reset(struct mii_bus *bus) ...@@ -918,6 +944,8 @@ static int fec_enet_mdio_reset(struct mii_bus *bus)
static int fec_enet_mii_probe(struct net_device *ndev) static int fec_enet_mii_probe(struct net_device *ndev)
{ {
struct fec_enet_private *fep = netdev_priv(ndev); struct fec_enet_private *fep = netdev_priv(ndev);
const struct platform_device_id *id_entry =
platform_get_device_id(fep->pdev);
struct phy_device *phy_dev = NULL; struct phy_device *phy_dev = NULL;
char mdio_bus_id[MII_BUS_ID_SIZE]; char mdio_bus_id[MII_BUS_ID_SIZE];
char phy_name[MII_BUS_ID_SIZE + 3]; char phy_name[MII_BUS_ID_SIZE + 3];
...@@ -949,14 +977,18 @@ static int fec_enet_mii_probe(struct net_device *ndev) ...@@ -949,14 +977,18 @@ static int fec_enet_mii_probe(struct net_device *ndev)
snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id);
phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0,
PHY_INTERFACE_MODE_MII); fep->phy_interface);
if (IS_ERR(phy_dev)) { if (IS_ERR(phy_dev)) {
printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name);
return PTR_ERR(phy_dev); return PTR_ERR(phy_dev);
} }
/* mask with MAC supported features */ /* mask with MAC supported features */
phy_dev->supported &= PHY_BASIC_FEATURES; if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT)
phy_dev->supported &= PHY_GBIT_FEATURES;
else
phy_dev->supported &= PHY_BASIC_FEATURES;
phy_dev->advertising = phy_dev->supported; phy_dev->advertising = phy_dev->supported;
fep->phy_dev = phy_dev; fep->phy_dev = phy_dev;
...@@ -1006,8 +1038,16 @@ static int fec_enet_mii_init(struct platform_device *pdev) ...@@ -1006,8 +1038,16 @@ static int fec_enet_mii_init(struct platform_device *pdev)
/* /*
* Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
*
* The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
* for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28
* Reference Manual has an error on this, and gets fixed on i.MX6Q
* document.
*/ */
fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 1; fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000);
if (id_entry->driver_data & FEC_QUIRK_ENET_MAC)
fep->phy_speed--;
fep->phy_speed <<= 1;
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
fep->mii_bus = mdiobus_alloc(); fep->mii_bus = mdiobus_alloc();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment