|
@@ -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 @@
|
|
#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[] = {
|
|
}, {
|
|
}, {
|
|
.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 {
|
|
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)
|
|
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)
|
|
/* Enable flow control and length check */
|
|
/* Enable flow control and length check */
|
|
rcntl |= 0x40000000 | 0x00000020;
|
|
rcntl |= 0x40000000 | 0x00000020;
|
|
|
|
|
|
- /* MII or RMII */
|
|
|
|
- if (fep->phy_interface == PHY_INTERFACE_MODE_RMII)
|
|
|
|
|
|
+ /* RGMII, RMII or MII */
|
|
|
|
+ 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 */
|
|
|
|
- if (fep->phy_dev && fep->phy_dev->speed == SPEED_100)
|
|
|
|
- rcntl &= ~(1 << 9);
|
|
|
|
- else
|
|
|
|
- rcntl |= (1 << 9);
|
|
|
|
-
|
|
|
|
|
|
+ /* 1G, 100M or 10M */
|
|
|
|
+ if (fep->phy_dev) {
|
|
|
|
+ if (fep->phy_dev->speed == SPEED_1000)
|
|
|
|
+ ecntl |= (1 << 5);
|
|
|
|
+ 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)
|
|
}
|
|
}
|
|
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
|
|
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)
|
|
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)
|
|
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)
|
|
|
|
|
|
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)
|
|
|
|
|
|
/*
|
|
/*
|
|
* 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();
|