Commit b5680e0b591f2701c5ba7d5fc8f96b55414073c8
Committed by
David S. Miller
1 parent
bcc67771ed
Exists in
master
and in
7 other branches
net/fec: add dual fec support for mx28
This patch is to add mx28 dual fec support. Here are some key notes for mx28 fec controller. - The mx28 fec controller naming ENET-MAC is a different IP from FEC used on other i.mx variants. But they are basically compatible on software interface, so it's possible to share the same driver. - ENET-MAC design on mx28 made an improper assumption that it runs on a big-endian system. As the result, driver has to swap every frame going to and coming from the controller. - The external phys can only be configured by fec0, which means fec1 can not work independently and both phys need to be configured by mii_bus attached on fec0. - ENET-MAC reset will get mac address registers reset too. - ENET-MAC MII/RMII mode and 10M/100M speed are configured differently FEC. - ETHER_EN bit must be set to get ENET-MAC interrupt work. Signed-off-by: Shawn Guo <shawn.guo@freescale.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 139 additions and 21 deletions Side-by-side Diff
drivers/net/Kconfig
... | ... | @@ -1944,18 +1944,19 @@ |
1944 | 1944 | config FEC |
1945 | 1945 | bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)" |
1946 | 1946 | depends on M523x || M527x || M5272 || M528x || M520x || M532x || \ |
1947 | - MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5 | |
1947 | + MACH_MX27 || ARCH_MX35 || ARCH_MX25 || ARCH_MX5 || SOC_IMX28 | |
1948 | 1948 | select PHYLIB |
1949 | 1949 | help |
1950 | 1950 | Say Y here if you want to use the built-in 10/100 Fast ethernet |
1951 | 1951 | controller on some Motorola ColdFire and Freescale i.MX processors. |
1952 | 1952 | |
1953 | 1953 | config FEC2 |
1954 | - bool "Second FEC ethernet controller (on some ColdFire CPUs)" | |
1954 | + bool "Second FEC ethernet controller" | |
1955 | 1955 | depends on FEC |
1956 | 1956 | help |
1957 | 1957 | Say Y here if you want to use the second built-in 10/100 Fast |
1958 | - ethernet controller on some Motorola ColdFire processors. | |
1958 | + ethernet controller on some Motorola ColdFire and Freescale | |
1959 | + i.MX processors. | |
1959 | 1960 | |
1960 | 1961 | config FEC_MPC52xx |
1961 | 1962 | tristate "MPC52xx FEC driver" |
drivers/net/fec.c
... | ... | @@ -17,6 +17,8 @@ |
17 | 17 | * |
18 | 18 | * Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be) |
19 | 19 | * Copyright (c) 2004-2006 Macq Electronique SA. |
20 | + * | |
21 | + * Copyright (C) 2010 Freescale Semiconductor, Inc. | |
20 | 22 | */ |
21 | 23 | |
22 | 24 | #include <linux/module.h> |
23 | 25 | |
24 | 26 | |
... | ... | @@ -45,20 +47,36 @@ |
45 | 47 | |
46 | 48 | #include <asm/cacheflush.h> |
47 | 49 | |
48 | -#ifndef CONFIG_ARCH_MXC | |
50 | +#ifndef CONFIG_ARM | |
49 | 51 | #include <asm/coldfire.h> |
50 | 52 | #include <asm/mcfsim.h> |
51 | 53 | #endif |
52 | 54 | |
53 | 55 | #include "fec.h" |
54 | 56 | |
55 | -#ifdef CONFIG_ARCH_MXC | |
56 | -#include <mach/hardware.h> | |
57 | +#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) | |
57 | 58 | #define FEC_ALIGNMENT 0xf |
58 | 59 | #else |
59 | 60 | #define FEC_ALIGNMENT 0x3 |
60 | 61 | #endif |
61 | 62 | |
63 | +#define DRIVER_NAME "fec" | |
64 | + | |
65 | +/* Controller is ENET-MAC */ | |
66 | +#define FEC_QUIRK_ENET_MAC (1 << 0) | |
67 | +/* Controller needs driver to swap frame */ | |
68 | +#define FEC_QUIRK_SWAP_FRAME (1 << 1) | |
69 | + | |
70 | +static struct platform_device_id fec_devtype[] = { | |
71 | + { | |
72 | + .name = DRIVER_NAME, | |
73 | + .driver_data = 0, | |
74 | + }, { | |
75 | + .name = "imx28-fec", | |
76 | + .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, | |
77 | + } | |
78 | +}; | |
79 | + | |
62 | 80 | static unsigned char macaddr[ETH_ALEN]; |
63 | 81 | module_param_array(macaddr, byte, NULL, 0); |
64 | 82 | MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); |
... | ... | @@ -129,7 +147,8 @@ |
129 | 147 | * account when setting it. |
130 | 148 | */ |
131 | 149 | #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ |
132 | - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC) | |
150 | + defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ | |
151 | + defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) | |
133 | 152 | #define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16) |
134 | 153 | #else |
135 | 154 | #define OPT_FRAME_SIZE 0 |
136 | 155 | |
... | ... | @@ -208,10 +227,23 @@ |
208 | 227 | /* Transmitter timeout */ |
209 | 228 | #define TX_TIMEOUT (2 * HZ) |
210 | 229 | |
230 | +static void *swap_buffer(void *bufaddr, int len) | |
231 | +{ | |
232 | + int i; | |
233 | + unsigned int *buf = bufaddr; | |
234 | + | |
235 | + for (i = 0; i < (len + 3) / 4; i++, buf++) | |
236 | + *buf = cpu_to_be32(*buf); | |
237 | + | |
238 | + return bufaddr; | |
239 | +} | |
240 | + | |
211 | 241 | static netdev_tx_t |
212 | 242 | fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) |
213 | 243 | { |
214 | 244 | struct fec_enet_private *fep = netdev_priv(dev); |
245 | + const struct platform_device_id *id_entry = | |
246 | + platform_get_device_id(fep->pdev); | |
215 | 247 | struct bufdesc *bdp; |
216 | 248 | void *bufaddr; |
217 | 249 | unsigned short status; |
... | ... | @@ -256,6 +288,14 @@ |
256 | 288 | bufaddr = fep->tx_bounce[index]; |
257 | 289 | } |
258 | 290 | |
291 | + /* | |
292 | + * Some design made an incorrect assumption on endian mode of | |
293 | + * the system that it's running on. As the result, driver has to | |
294 | + * swap every frame going to and coming from the controller. | |
295 | + */ | |
296 | + if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) | |
297 | + swap_buffer(bufaddr, skb->len); | |
298 | + | |
259 | 299 | /* Save skb pointer */ |
260 | 300 | fep->tx_skbuff[fep->skb_cur] = skb; |
261 | 301 | |
... | ... | @@ -424,6 +464,8 @@ |
424 | 464 | fec_enet_rx(struct net_device *dev) |
425 | 465 | { |
426 | 466 | struct fec_enet_private *fep = netdev_priv(dev); |
467 | + const struct platform_device_id *id_entry = | |
468 | + platform_get_device_id(fep->pdev); | |
427 | 469 | struct bufdesc *bdp; |
428 | 470 | unsigned short status; |
429 | 471 | struct sk_buff *skb; |
... | ... | @@ -487,6 +529,9 @@ |
487 | 529 | dma_unmap_single(NULL, bdp->cbd_bufaddr, bdp->cbd_datlen, |
488 | 530 | DMA_FROM_DEVICE); |
489 | 531 | |
532 | + if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) | |
533 | + swap_buffer(data, pkt_len); | |
534 | + | |
490 | 535 | /* This does 16 byte alignment, exactly what we need. |
491 | 536 | * The packet length includes FCS, but we don't want to |
492 | 537 | * include that when passing upstream as it messes up |
... | ... | @@ -689,6 +734,7 @@ |
689 | 734 | char mdio_bus_id[MII_BUS_ID_SIZE]; |
690 | 735 | char phy_name[MII_BUS_ID_SIZE + 3]; |
691 | 736 | int phy_id; |
737 | + int dev_id = fep->pdev->id; | |
692 | 738 | |
693 | 739 | fep->phy_dev = NULL; |
694 | 740 | |
... | ... | @@ -700,6 +746,8 @@ |
700 | 746 | continue; |
701 | 747 | if (fep->mii_bus->phy_map[phy_id]->phy_id == 0) |
702 | 748 | continue; |
749 | + if (dev_id--) | |
750 | + continue; | |
703 | 751 | strncpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE); |
704 | 752 | break; |
705 | 753 | } |
706 | 754 | |
707 | 755 | |
... | ... | @@ -737,10 +785,35 @@ |
737 | 785 | |
738 | 786 | static int fec_enet_mii_init(struct platform_device *pdev) |
739 | 787 | { |
788 | + static struct mii_bus *fec0_mii_bus; | |
740 | 789 | struct net_device *dev = platform_get_drvdata(pdev); |
741 | 790 | struct fec_enet_private *fep = netdev_priv(dev); |
791 | + const struct platform_device_id *id_entry = | |
792 | + platform_get_device_id(fep->pdev); | |
742 | 793 | int err = -ENXIO, i; |
743 | 794 | |
795 | + /* | |
796 | + * The dual fec interfaces are not equivalent with enet-mac. | |
797 | + * Here are the differences: | |
798 | + * | |
799 | + * - fec0 supports MII & RMII modes while fec1 only supports RMII | |
800 | + * - fec0 acts as the 1588 time master while fec1 is slave | |
801 | + * - external phys can only be configured by fec0 | |
802 | + * | |
803 | + * That is to say fec1 can not work independently. It only works | |
804 | + * when fec0 is working. The reason behind this design is that the | |
805 | + * second interface is added primarily for Switch mode. | |
806 | + * | |
807 | + * Because of the last point above, both phys are attached on fec0 | |
808 | + * mdio interface in board design, and need to be configured by | |
809 | + * fec0 mii_bus. | |
810 | + */ | |
811 | + if ((id_entry->driver_data & FEC_QUIRK_ENET_MAC) && pdev->id) { | |
812 | + /* fec1 uses fec0 mii_bus */ | |
813 | + fep->mii_bus = fec0_mii_bus; | |
814 | + return 0; | |
815 | + } | |
816 | + | |
744 | 817 | fep->mii_timeout = 0; |
745 | 818 | |
746 | 819 | /* |
... | ... | @@ -777,6 +850,10 @@ |
777 | 850 | if (mdiobus_register(fep->mii_bus)) |
778 | 851 | goto err_out_free_mdio_irq; |
779 | 852 | |
853 | + /* save fec0 mii_bus */ | |
854 | + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) | |
855 | + fec0_mii_bus = fep->mii_bus; | |
856 | + | |
780 | 857 | return 0; |
781 | 858 | |
782 | 859 | err_out_free_mdio_irq: |
783 | 860 | |
784 | 861 | |
... | ... | @@ -1148,12 +1225,25 @@ |
1148 | 1225 | fec_restart(struct net_device *dev, int duplex) |
1149 | 1226 | { |
1150 | 1227 | struct fec_enet_private *fep = netdev_priv(dev); |
1228 | + const struct platform_device_id *id_entry = | |
1229 | + platform_get_device_id(fep->pdev); | |
1151 | 1230 | int i; |
1231 | + u32 val, temp_mac[2]; | |
1152 | 1232 | |
1153 | 1233 | /* Whack a reset. We should wait for this. */ |
1154 | 1234 | writel(1, fep->hwp + FEC_ECNTRL); |
1155 | 1235 | udelay(10); |
1156 | 1236 | |
1237 | + /* | |
1238 | + * enet-mac reset will reset mac address registers too, | |
1239 | + * so need to reconfigure it. | |
1240 | + */ | |
1241 | + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | |
1242 | + memcpy(&temp_mac, dev->dev_addr, ETH_ALEN); | |
1243 | + writel(cpu_to_be32(temp_mac[0]), fep->hwp + FEC_ADDR_LOW); | |
1244 | + writel(cpu_to_be32(temp_mac[1]), fep->hwp + FEC_ADDR_HIGH); | |
1245 | + } | |
1246 | + | |
1157 | 1247 | /* Clear any outstanding interrupt. */ |
1158 | 1248 | writel(0xffc00000, fep->hwp + FEC_IEVENT); |
1159 | 1249 | |
1160 | 1250 | |
1161 | 1251 | |
1162 | 1252 | |
1163 | 1253 | |
... | ... | @@ -1200,20 +1290,45 @@ |
1200 | 1290 | /* Set MII speed */ |
1201 | 1291 | writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); |
1202 | 1292 | |
1293 | + /* | |
1294 | + * The phy interface and speed need to get configured | |
1295 | + * differently on enet-mac. | |
1296 | + */ | |
1297 | + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { | |
1298 | + val = readl(fep->hwp + FEC_R_CNTRL); | |
1299 | + | |
1300 | + /* MII or RMII */ | |
1301 | + if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) | |
1302 | + val |= (1 << 8); | |
1303 | + else | |
1304 | + val &= ~(1 << 8); | |
1305 | + | |
1306 | + /* 10M or 100M */ | |
1307 | + if (fep->phy_dev && fep->phy_dev->speed == SPEED_100) | |
1308 | + val &= ~(1 << 9); | |
1309 | + else | |
1310 | + val |= (1 << 9); | |
1311 | + | |
1312 | + writel(val, fep->hwp + FEC_R_CNTRL); | |
1313 | + } else { | |
1203 | 1314 | #ifdef FEC_MIIGSK_ENR |
1204 | - if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { | |
1205 | - /* disable the gasket and wait */ | |
1206 | - writel(0, fep->hwp + FEC_MIIGSK_ENR); | |
1207 | - while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) | |
1208 | - udelay(1); | |
1315 | + if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) { | |
1316 | + /* disable the gasket and wait */ | |
1317 | + writel(0, fep->hwp + FEC_MIIGSK_ENR); | |
1318 | + while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4) | |
1319 | + udelay(1); | |
1209 | 1320 | |
1210 | - /* configure the gasket: RMII, 50 MHz, no loopback, no echo */ | |
1211 | - writel(1, fep->hwp + FEC_MIIGSK_CFGR); | |
1321 | + /* | |
1322 | + * configure the gasket: | |
1323 | + * RMII, 50 MHz, no loopback, no echo | |
1324 | + */ | |
1325 | + writel(1, fep->hwp + FEC_MIIGSK_CFGR); | |
1212 | 1326 | |
1213 | - /* re-enable the gasket */ | |
1214 | - writel(2, fep->hwp + FEC_MIIGSK_ENR); | |
1215 | - } | |
1327 | + /* re-enable the gasket */ | |
1328 | + writel(2, fep->hwp + FEC_MIIGSK_ENR); | |
1329 | + } | |
1216 | 1330 | #endif |
1331 | + } | |
1217 | 1332 | |
1218 | 1333 | /* And last, enable the transmit and receive processing */ |
1219 | 1334 | writel(2, fep->hwp + FEC_ECNTRL); |
1220 | 1335 | |
... | ... | @@ -1410,12 +1525,13 @@ |
1410 | 1525 | |
1411 | 1526 | static struct platform_driver fec_driver = { |
1412 | 1527 | .driver = { |
1413 | - .name = "fec", | |
1528 | + .name = DRIVER_NAME, | |
1414 | 1529 | .owner = THIS_MODULE, |
1415 | 1530 | #ifdef CONFIG_PM |
1416 | 1531 | .pm = &fec_pm_ops, |
1417 | 1532 | #endif |
1418 | 1533 | }, |
1534 | + .id_table = fec_devtype, | |
1419 | 1535 | .probe = fec_probe, |
1420 | 1536 | .remove = __devexit_p(fec_drv_remove), |
1421 | 1537 | }; |
drivers/net/fec.h
... | ... | @@ -14,7 +14,8 @@ |
14 | 14 | /****************************************************************************/ |
15 | 15 | |
16 | 16 | #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ |
17 | - defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARCH_MXC) | |
17 | + defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ | |
18 | + defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) | |
18 | 19 | /* |
19 | 20 | * Just figures, Motorola would have to change the offsets for |
20 | 21 | * registers in the same peripheral device on different models |
... | ... | @@ -78,7 +79,7 @@ |
78 | 79 | /* |
79 | 80 | * Define the buffer descriptor structure. |
80 | 81 | */ |
81 | -#ifdef CONFIG_ARCH_MXC | |
82 | +#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_SOC_IMX28) | |
82 | 83 | struct bufdesc { |
83 | 84 | unsigned short cbd_datlen; /* Data length */ |
84 | 85 | unsigned short cbd_sc; /* Control and status info */ |