diff options
author | Shawn Guo <shawn.guo@linaro.org> | 2011-09-22 22:12:48 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-09-23 13:55:26 -0400 |
commit | 230dec61313dc5f5720311d0b492f69f5466b0a4 (patch) | |
tree | 6d67c1ee8106752a156468d339e5ae8f1848e16e /drivers/net/ethernet/freescale/fec.c | |
parent | c828827f8426f2cd8e37315c59ae406534a16d48 (diff) |
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: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale/fec.c')
-rw-r--r-- | drivers/net/ethernet/freescale/fec.c | 66 |
1 files changed, 53 insertions, 13 deletions
diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 2bbe6a519bf4..cce78ceb63ed 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c | |||
@@ -18,7 +18,7 @@ | |||
18 | * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) | 18 | * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) |
19 | * Copyright (c) 2004-2006 Macq Electronique SA. | 19 | * Copyright (c) 2004-2006 Macq Electronique SA. |
20 | * | 20 | * |
21 | * Copyright (C) 2010 Freescale Semiconductor, Inc. | 21 | * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
@@ -72,6 +72,8 @@ | |||
72 | #define FEC_QUIRK_SWAP_FRAME (1 << 1) | 72 | #define FEC_QUIRK_SWAP_FRAME (1 << 1) |
73 | /* Controller uses gasket */ | 73 | /* Controller uses gasket */ |
74 | #define FEC_QUIRK_USE_GASKET (1 << 2) | 74 | #define FEC_QUIRK_USE_GASKET (1 << 2) |
75 | /* Controller has GBIT support */ | ||
76 | #define FEC_QUIRK_HAS_GBIT (1 << 3) | ||
75 | 77 | ||
76 | static struct platform_device_id fec_devtype[] = { | 78 | static struct platform_device_id fec_devtype[] = { |
77 | { | 79 | { |
@@ -88,6 +90,9 @@ static struct platform_device_id fec_devtype[] = { | |||
88 | .name = "imx28-fec", | 90 | .name = "imx28-fec", |
89 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, | 91 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, |
90 | }, { | 92 | }, { |
93 | .name = "imx6q-fec", | ||
94 | .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT, | ||
95 | }, { | ||
91 | /* sentinel */ | 96 | /* sentinel */ |
92 | } | 97 | } |
93 | }; | 98 | }; |
@@ -97,12 +102,14 @@ enum imx_fec_type { | |||
97 | IMX25_FEC = 1, /* runs on i.mx25/50/53 */ | 102 | IMX25_FEC = 1, /* runs on i.mx25/50/53 */ |
98 | IMX27_FEC, /* runs on i.mx27/35/51 */ | 103 | IMX27_FEC, /* runs on i.mx27/35/51 */ |
99 | IMX28_FEC, | 104 | IMX28_FEC, |
105 | IMX6Q_FEC, | ||
100 | }; | 106 | }; |
101 | 107 | ||
102 | static const struct of_device_id fec_dt_ids[] = { | 108 | static const struct of_device_id fec_dt_ids[] = { |
103 | { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, | 109 | { .compatible = "fsl,imx25-fec", .data = &fec_devtype[IMX25_FEC], }, |
104 | { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, | 110 | { .compatible = "fsl,imx27-fec", .data = &fec_devtype[IMX27_FEC], }, |
105 | { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, | 111 | { .compatible = "fsl,imx28-fec", .data = &fec_devtype[IMX28_FEC], }, |
112 | { .compatible = "fsl,imx6q-fec", .data = &fec_devtype[IMX6Q_FEC], }, | ||
106 | { /* sentinel */ } | 113 | { /* sentinel */ } |
107 | }; | 114 | }; |
108 | MODULE_DEVICE_TABLE(of, fec_dt_ids); | 115 | MODULE_DEVICE_TABLE(of, fec_dt_ids); |
@@ -373,6 +380,7 @@ fec_restart(struct net_device *ndev, int duplex) | |||
373 | int i; | 380 | int i; |
374 | u32 temp_mac[2]; | 381 | u32 temp_mac[2]; |
375 | u32 rcntl = OPT_FRAME_SIZE | 0x04; | 382 | u32 rcntl = OPT_FRAME_SIZE | 0x04; |
383 | u32 ecntl = 0x2; /* ETHEREN */ | ||
376 | 384 | ||
377 | /* Whack a reset. We should wait for this. */ | 385 | /* Whack a reset. We should wait for this. */ |
378 | writel(1, fep->hwp + FEC_ECNTRL); | 386 | writel(1, fep->hwp + FEC_ECNTRL); |
@@ -442,18 +450,23 @@ fec_restart(struct net_device *ndev, int duplex) | |||
442 | /* Enable flow control and length check */ | 450 | /* Enable flow control and length check */ |
443 | rcntl |= 0x40000000 | 0x00000020; | 451 | rcntl |= 0x40000000 | 0x00000020; |
444 | 452 | ||
445 | /* MII or RMII */ | 453 | /* RGMII, RMII or MII */ |
446 | if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) | 454 | if (fep->phy_interface == PHY_INTERFACE_MODE_RGMII) |
455 | rcntl |= (1 << 6); | ||
456 | else if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) | ||
447 | rcntl |= (1 << 8); | 457 | rcntl |= (1 << 8); |
448 | else | 458 | else |
449 | rcntl &= ~(1 << 8); | 459 | rcntl &= ~(1 << 8); |
450 | 460 | ||
451 | /* 10M or 100M */ | 461 | /* 1G, 100M or 10M */ |
452 | if (fep->phy_dev && fep->phy_dev->speed == SPEED_100) | 462 | if (fep->phy_dev) { |
453 | rcntl &= ~(1 << 9); | 463 | if (fep->phy_dev->speed == SPEED_1000) |
454 | else | 464 | ecntl |= (1 << 5); |
455 | rcntl |= (1 << 9); | 465 | else if (fep->phy_dev->speed == SPEED_100) |
456 | 466 | rcntl &= ~(1 << 9); | |
467 | else | ||
468 | rcntl |= (1 << 9); | ||
469 | } | ||
457 | } else { | 470 | } else { |
458 | #ifdef FEC_MIIGSK_ENR | 471 | #ifdef FEC_MIIGSK_ENR |
459 | if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) { | 472 | if (id_entry->driver_data & FEC_QUIRK_USE_GASKET) { |
@@ -478,8 +491,15 @@ fec_restart(struct net_device *ndev, int duplex) | |||
478 | } | 491 | } |
479 | writel(rcntl, fep->hwp + FEC_R_CNTRL); | 492 | writel(rcntl, fep->hwp + FEC_R_CNTRL); |
480 | 493 | ||
494 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | ||
495 | /* enable ENET endian swap */ | ||
496 | ecntl |= (1 << 8); | ||
497 | /* enable ENET store and forward mode */ | ||
498 | writel(1 << 8, fep->hwp + FEC_X_WMRK); | ||
499 | } | ||
500 | |||
481 | /* And last, enable the transmit and receive processing */ | 501 | /* And last, enable the transmit and receive processing */ |
482 | writel(2, fep->hwp + FEC_ECNTRL); | 502 | writel(ecntl, fep->hwp + FEC_ECNTRL); |
483 | writel(0, fep->hwp + FEC_R_DES_ACTIVE); | 503 | writel(0, fep->hwp + FEC_R_DES_ACTIVE); |
484 | 504 | ||
485 | /* Enable interrupts we wish to service */ | 505 | /* Enable interrupts we wish to service */ |
@@ -490,6 +510,8 @@ static void | |||
490 | fec_stop(struct net_device *ndev) | 510 | fec_stop(struct net_device *ndev) |
491 | { | 511 | { |
492 | struct fec_enet_private *fep = netdev_priv(ndev); | 512 | struct fec_enet_private *fep = netdev_priv(ndev); |
513 | const struct platform_device_id *id_entry = | ||
514 | platform_get_device_id(fep->pdev); | ||
493 | 515 | ||
494 | /* We cannot expect a graceful transmit stop without link !!! */ | 516 | /* We cannot expect a graceful transmit stop without link !!! */ |
495 | if (fep->link) { | 517 | if (fep->link) { |
@@ -504,6 +526,10 @@ fec_stop(struct net_device *ndev) | |||
504 | udelay(10); | 526 | udelay(10); |
505 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | 527 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
506 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); | 528 | writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); |
529 | |||
530 | /* We have to keep ENET enabled to have MII interrupt stay working */ | ||
531 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | ||
532 | writel(2, fep->hwp + FEC_ECNTRL); | ||
507 | } | 533 | } |
508 | 534 | ||
509 | 535 | ||
@@ -918,6 +944,8 @@ static int fec_enet_mdio_reset(struct mii_bus *bus) | |||
918 | static int fec_enet_mii_probe(struct net_device *ndev) | 944 | static int fec_enet_mii_probe(struct net_device *ndev) |
919 | { | 945 | { |
920 | struct fec_enet_private *fep = netdev_priv(ndev); | 946 | struct fec_enet_private *fep = netdev_priv(ndev); |
947 | const struct platform_device_id *id_entry = | ||
948 | platform_get_device_id(fep->pdev); | ||
921 | struct phy_device *phy_dev = NULL; | 949 | struct phy_device *phy_dev = NULL; |
922 | char mdio_bus_id[MII_BUS_ID_SIZE]; | 950 | char mdio_bus_id[MII_BUS_ID_SIZE]; |
923 | char phy_name[MII_BUS_ID_SIZE + 3]; | 951 | char phy_name[MII_BUS_ID_SIZE + 3]; |
@@ -949,14 +977,18 @@ static int fec_enet_mii_probe(struct net_device *ndev) | |||
949 | 977 | ||
950 | snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); | 978 | snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); |
951 | phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, | 979 | phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link, 0, |
952 | PHY_INTERFACE_MODE_MII); | 980 | fep->phy_interface); |
953 | if (IS_ERR(phy_dev)) { | 981 | if (IS_ERR(phy_dev)) { |
954 | printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); | 982 | printk(KERN_ERR "%s: could not attach to PHY\n", ndev->name); |
955 | return PTR_ERR(phy_dev); | 983 | return PTR_ERR(phy_dev); |
956 | } | 984 | } |
957 | 985 | ||
958 | /* mask with MAC supported features */ | 986 | /* mask with MAC supported features */ |
959 | phy_dev->supported &= PHY_BASIC_FEATURES; | 987 | if (id_entry->driver_data & FEC_QUIRK_HAS_GBIT) |
988 | phy_dev->supported &= PHY_GBIT_FEATURES; | ||
989 | else | ||
990 | phy_dev->supported &= PHY_BASIC_FEATURES; | ||
991 | |||
960 | phy_dev->advertising = phy_dev->supported; | 992 | phy_dev->advertising = phy_dev->supported; |
961 | 993 | ||
962 | fep->phy_dev = phy_dev; | 994 | fep->phy_dev = phy_dev; |
@@ -1006,8 +1038,16 @@ static int fec_enet_mii_init(struct platform_device *pdev) | |||
1006 | 1038 | ||
1007 | /* | 1039 | /* |
1008 | * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) | 1040 | * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed) |
1041 | * | ||
1042 | * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while | ||
1043 | * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'. The i.MX28 | ||
1044 | * Reference Manual has an error on this, and gets fixed on i.MX6Q | ||
1045 | * document. | ||
1009 | */ | 1046 | */ |
1010 | fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 1; | 1047 | fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000); |
1048 | if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | ||
1049 | fep->phy_speed--; | ||
1050 | fep->phy_speed <<= 1; | ||
1011 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); | 1051 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
1012 | 1052 | ||
1013 | fep->mii_bus = mdiobus_alloc(); | 1053 | fep->mii_bus = mdiobus_alloc(); |