183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 20b23fb36SIlya Yanok /* 30b23fb36SIlya Yanok * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com> 40b23fb36SIlya Yanok * (C) Copyright 2008,2009 Eric Jarrige <eric.jarrige@armadeus.org> 50b23fb36SIlya Yanok * (C) Copyright 2008 Armadeus Systems nc 60b23fb36SIlya Yanok * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> 70b23fb36SIlya Yanok * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de> 80b23fb36SIlya Yanok */ 90b23fb36SIlya Yanok 100b23fb36SIlya Yanok #include <common.h> 1160752ca8SJagan Teki #include <dm.h> 129925f1dbSAlex Kiernan #include <environment.h> 130b23fb36SIlya Yanok #include <malloc.h> 14cf92e05cSSimon Glass #include <memalign.h> 15567173a6SJagan Teki #include <miiphy.h> 160b23fb36SIlya Yanok #include <net.h> 1784f64c8bSJeroen Hofstee #include <netdev.h> 18ad8c43cbSMartin Fuzzey #include <power/regulator.h> 190b23fb36SIlya Yanok 20567173a6SJagan Teki #include <asm/io.h> 21567173a6SJagan Teki #include <linux/errno.h> 22567173a6SJagan Teki #include <linux/compiler.h> 23567173a6SJagan Teki 240b23fb36SIlya Yanok #include <asm/arch/clock.h> 250b23fb36SIlya Yanok #include <asm/arch/imx-regs.h> 26552a848eSStefano Babic #include <asm/mach-imx/sys_proto.h> 27efd0b791SMichael Trimarchi #include <asm-generic/gpio.h> 28efd0b791SMichael Trimarchi 29efd0b791SMichael Trimarchi #include "fec_mxc.h" 300b23fb36SIlya Yanok 310b23fb36SIlya Yanok DECLARE_GLOBAL_DATA_PTR; 320b23fb36SIlya Yanok 33bc1ce150SMarek Vasut /* 34bc1ce150SMarek Vasut * Timeout the transfer after 5 mS. This is usually a bit more, since 35bc1ce150SMarek Vasut * the code in the tightloops this timeout is used in adds some overhead. 36bc1ce150SMarek Vasut */ 37bc1ce150SMarek Vasut #define FEC_XFER_TIMEOUT 5000 38bc1ce150SMarek Vasut 39db5b7f56SFabio Estevam /* 40db5b7f56SFabio Estevam * The standard 32-byte DMA alignment does not work on mx6solox, which requires 41db5b7f56SFabio Estevam * 64-byte alignment in the DMA RX FEC buffer. 42db5b7f56SFabio Estevam * Introduce the FEC_DMA_RX_MINALIGN which can cover mx6solox needs and also 43db5b7f56SFabio Estevam * satisfies the alignment on other SoCs (32-bytes) 44db5b7f56SFabio Estevam */ 45db5b7f56SFabio Estevam #define FEC_DMA_RX_MINALIGN 64 46db5b7f56SFabio Estevam 470b23fb36SIlya Yanok #ifndef CONFIG_MII 480b23fb36SIlya Yanok #error "CONFIG_MII has to be defined!" 490b23fb36SIlya Yanok #endif 500b23fb36SIlya Yanok 51392b8502SMarek Vasut #ifndef CONFIG_FEC_XCV_TYPE 52392b8502SMarek Vasut #define CONFIG_FEC_XCV_TYPE MII100 53392b8502SMarek Vasut #endif 54392b8502SMarek Vasut 55be7e87e2SMarek Vasut /* 56be7e87e2SMarek Vasut * The i.MX28 operates with packets in big endian. We need to swap them before 57be7e87e2SMarek Vasut * sending and after receiving. 58be7e87e2SMarek Vasut */ 59be7e87e2SMarek Vasut #ifdef CONFIG_MX28 60be7e87e2SMarek Vasut #define CONFIG_FEC_MXC_SWAP_PACKET 61be7e87e2SMarek Vasut #endif 62be7e87e2SMarek Vasut 635c1ad3e6SEric Nelson #define RXDESC_PER_CACHELINE (ARCH_DMA_MINALIGN/sizeof(struct fec_bd)) 645c1ad3e6SEric Nelson 655c1ad3e6SEric Nelson /* Check various alignment issues at compile time */ 665c1ad3e6SEric Nelson #if ((ARCH_DMA_MINALIGN < 16) || (ARCH_DMA_MINALIGN % 16 != 0)) 675c1ad3e6SEric Nelson #error "ARCH_DMA_MINALIGN must be multiple of 16!" 685c1ad3e6SEric Nelson #endif 695c1ad3e6SEric Nelson 705c1ad3e6SEric Nelson #if ((PKTALIGN < ARCH_DMA_MINALIGN) || \ 715c1ad3e6SEric Nelson (PKTALIGN % ARCH_DMA_MINALIGN != 0)) 725c1ad3e6SEric Nelson #error "PKTALIGN must be multiple of ARCH_DMA_MINALIGN!" 735c1ad3e6SEric Nelson #endif 745c1ad3e6SEric Nelson 750b23fb36SIlya Yanok #undef DEBUG 760b23fb36SIlya Yanok 77be7e87e2SMarek Vasut #ifdef CONFIG_FEC_MXC_SWAP_PACKET 78be7e87e2SMarek Vasut static void swap_packet(uint32_t *packet, int length) 79be7e87e2SMarek Vasut { 80be7e87e2SMarek Vasut int i; 81be7e87e2SMarek Vasut 82be7e87e2SMarek Vasut for (i = 0; i < DIV_ROUND_UP(length, 4); i++) 83be7e87e2SMarek Vasut packet[i] = __swab32(packet[i]); 84be7e87e2SMarek Vasut } 85be7e87e2SMarek Vasut #endif 86be7e87e2SMarek Vasut 87567173a6SJagan Teki /* MII-interface related functions */ 88567173a6SJagan Teki static int fec_mdio_read(struct ethernet_regs *eth, uint8_t phyaddr, 89567173a6SJagan Teki uint8_t regaddr) 900b23fb36SIlya Yanok { 910b23fb36SIlya Yanok uint32_t reg; /* convenient holder for the PHY register */ 920b23fb36SIlya Yanok uint32_t phy; /* convenient holder for the PHY */ 930b23fb36SIlya Yanok uint32_t start; 9413947f43STroy Kisky int val; 950b23fb36SIlya Yanok 960b23fb36SIlya Yanok /* 970b23fb36SIlya Yanok * reading from any PHY's register is done by properly 980b23fb36SIlya Yanok * programming the FEC's MII data register. 990b23fb36SIlya Yanok */ 100d133b881SMarek Vasut writel(FEC_IEVENT_MII, ð->ievent); 101567173a6SJagan Teki reg = regaddr << FEC_MII_DATA_RA_SHIFT; 102567173a6SJagan Teki phy = phyaddr << FEC_MII_DATA_PA_SHIFT; 1030b23fb36SIlya Yanok 1040b23fb36SIlya Yanok writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | 105d133b881SMarek Vasut phy | reg, ð->mii_data); 1060b23fb36SIlya Yanok 107567173a6SJagan Teki /* wait for the related interrupt */ 108a60d1e5bSGraeme Russ start = get_timer(0); 109d133b881SMarek Vasut while (!(readl(ð->ievent) & FEC_IEVENT_MII)) { 1100b23fb36SIlya Yanok if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { 1110b23fb36SIlya Yanok printf("Read MDIO failed...\n"); 1120b23fb36SIlya Yanok return -1; 1130b23fb36SIlya Yanok } 1140b23fb36SIlya Yanok } 1150b23fb36SIlya Yanok 116567173a6SJagan Teki /* clear mii interrupt bit */ 117d133b881SMarek Vasut writel(FEC_IEVENT_MII, ð->ievent); 1180b23fb36SIlya Yanok 119567173a6SJagan Teki /* it's now safe to read the PHY's register */ 12013947f43STroy Kisky val = (unsigned short)readl(ð->mii_data); 121567173a6SJagan Teki debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phyaddr, 122567173a6SJagan Teki regaddr, val); 12313947f43STroy Kisky return val; 1240b23fb36SIlya Yanok } 1250b23fb36SIlya Yanok 12658ec4d33SAnatolij Gustschin static int fec_get_clk_rate(void *udev, int idx) 12758ec4d33SAnatolij Gustschin { 12858ec4d33SAnatolij Gustschin #if IS_ENABLED(CONFIG_IMX8) 12958ec4d33SAnatolij Gustschin struct fec_priv *fec; 13058ec4d33SAnatolij Gustschin struct udevice *dev; 13158ec4d33SAnatolij Gustschin int ret; 13258ec4d33SAnatolij Gustschin 13358ec4d33SAnatolij Gustschin dev = udev; 13458ec4d33SAnatolij Gustschin if (!dev) { 13558ec4d33SAnatolij Gustschin ret = uclass_get_device(UCLASS_ETH, idx, &dev); 13658ec4d33SAnatolij Gustschin if (ret < 0) { 13758ec4d33SAnatolij Gustschin debug("Can't get FEC udev: %d\n", ret); 13858ec4d33SAnatolij Gustschin return ret; 13958ec4d33SAnatolij Gustschin } 14058ec4d33SAnatolij Gustschin } 14158ec4d33SAnatolij Gustschin 14258ec4d33SAnatolij Gustschin fec = dev_get_priv(dev); 14358ec4d33SAnatolij Gustschin if (fec) 14458ec4d33SAnatolij Gustschin return fec->clk_rate; 14558ec4d33SAnatolij Gustschin 14658ec4d33SAnatolij Gustschin return -EINVAL; 14758ec4d33SAnatolij Gustschin #else 14858ec4d33SAnatolij Gustschin return imx_get_fecclk(); 14958ec4d33SAnatolij Gustschin #endif 15058ec4d33SAnatolij Gustschin } 15158ec4d33SAnatolij Gustschin 152575c5cc0STroy Kisky static void fec_mii_setspeed(struct ethernet_regs *eth) 1534294b248SStefano Babic { 1544294b248SStefano Babic /* 1554294b248SStefano Babic * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock 1564294b248SStefano Babic * and do not drop the Preamble. 157843a3e58SMåns Rullgård * 158843a3e58SMåns Rullgård * The i.MX28 and i.MX6 types have another field in the MSCR (aka 159843a3e58SMåns Rullgård * MII_SPEED) register that defines the MDIO output hold time. Earlier 160843a3e58SMåns Rullgård * versions are RAZ there, so just ignore the difference and write the 161843a3e58SMåns Rullgård * register always. 162843a3e58SMåns Rullgård * The minimal hold time according to IEE802.3 (clause 22) is 10 ns. 163843a3e58SMåns Rullgård * HOLDTIME + 1 is the number of clk cycles the fec is holding the 164843a3e58SMåns Rullgård * output. 165843a3e58SMåns Rullgård * The HOLDTIME bitfield takes values between 0 and 7 (inclusive). 166843a3e58SMåns Rullgård * Given that ceil(clkrate / 5000000) <= 64, the calculation for 167843a3e58SMåns Rullgård * holdtime cannot result in a value greater than 3. 1684294b248SStefano Babic */ 16958ec4d33SAnatolij Gustschin u32 pclk; 17058ec4d33SAnatolij Gustschin u32 speed; 17158ec4d33SAnatolij Gustschin u32 hold; 17258ec4d33SAnatolij Gustschin int ret; 17358ec4d33SAnatolij Gustschin 17458ec4d33SAnatolij Gustschin ret = fec_get_clk_rate(NULL, 0); 17558ec4d33SAnatolij Gustschin if (ret < 0) { 17658ec4d33SAnatolij Gustschin printf("Can't find FEC0 clk rate: %d\n", ret); 17758ec4d33SAnatolij Gustschin return; 17858ec4d33SAnatolij Gustschin } 17958ec4d33SAnatolij Gustschin pclk = ret; 18058ec4d33SAnatolij Gustschin speed = DIV_ROUND_UP(pclk, 5000000); 18158ec4d33SAnatolij Gustschin hold = DIV_ROUND_UP(pclk, 100000000) - 1; 18258ec4d33SAnatolij Gustschin 1836ba45cc0SMarkus Niebel #ifdef FEC_QUIRK_ENET_MAC 1846ba45cc0SMarkus Niebel speed--; 1856ba45cc0SMarkus Niebel #endif 186843a3e58SMåns Rullgård writel(speed << 1 | hold << 8, ð->mii_speed); 187575c5cc0STroy Kisky debug("%s: mii_speed %08x\n", __func__, readl(ð->mii_speed)); 1884294b248SStefano Babic } 1890b23fb36SIlya Yanok 190567173a6SJagan Teki static int fec_mdio_write(struct ethernet_regs *eth, uint8_t phyaddr, 191567173a6SJagan Teki uint8_t regaddr, uint16_t data) 19213947f43STroy Kisky { 1930b23fb36SIlya Yanok uint32_t reg; /* convenient holder for the PHY register */ 1940b23fb36SIlya Yanok uint32_t phy; /* convenient holder for the PHY */ 1950b23fb36SIlya Yanok uint32_t start; 1960b23fb36SIlya Yanok 197567173a6SJagan Teki reg = regaddr << FEC_MII_DATA_RA_SHIFT; 198567173a6SJagan Teki phy = phyaddr << FEC_MII_DATA_PA_SHIFT; 1990b23fb36SIlya Yanok 2000b23fb36SIlya Yanok writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | 201d133b881SMarek Vasut FEC_MII_DATA_TA | phy | reg | data, ð->mii_data); 2020b23fb36SIlya Yanok 203567173a6SJagan Teki /* wait for the MII interrupt */ 204a60d1e5bSGraeme Russ start = get_timer(0); 205d133b881SMarek Vasut while (!(readl(ð->ievent) & FEC_IEVENT_MII)) { 2060b23fb36SIlya Yanok if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { 2070b23fb36SIlya Yanok printf("Write MDIO failed...\n"); 2080b23fb36SIlya Yanok return -1; 2090b23fb36SIlya Yanok } 2100b23fb36SIlya Yanok } 2110b23fb36SIlya Yanok 212567173a6SJagan Teki /* clear MII interrupt bit */ 213d133b881SMarek Vasut writel(FEC_IEVENT_MII, ð->ievent); 214567173a6SJagan Teki debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phyaddr, 215567173a6SJagan Teki regaddr, data); 2160b23fb36SIlya Yanok 2170b23fb36SIlya Yanok return 0; 2180b23fb36SIlya Yanok } 2190b23fb36SIlya Yanok 220567173a6SJagan Teki static int fec_phy_read(struct mii_dev *bus, int phyaddr, int dev_addr, 221567173a6SJagan Teki int regaddr) 22213947f43STroy Kisky { 223567173a6SJagan Teki return fec_mdio_read(bus->priv, phyaddr, regaddr); 22413947f43STroy Kisky } 22513947f43STroy Kisky 226567173a6SJagan Teki static int fec_phy_write(struct mii_dev *bus, int phyaddr, int dev_addr, 227567173a6SJagan Teki int regaddr, u16 data) 22813947f43STroy Kisky { 229567173a6SJagan Teki return fec_mdio_write(bus->priv, phyaddr, regaddr, data); 23013947f43STroy Kisky } 23113947f43STroy Kisky 23213947f43STroy Kisky #ifndef CONFIG_PHYLIB 2330b23fb36SIlya Yanok static int miiphy_restart_aneg(struct eth_device *dev) 2340b23fb36SIlya Yanok { 235b774fe9dSStefano Babic int ret = 0; 236b774fe9dSStefano Babic #if !defined(CONFIG_FEC_MXC_NO_ANEG) 2379e27e9dcSMarek Vasut struct fec_priv *fec = (struct fec_priv *)dev->priv; 23813947f43STroy Kisky struct ethernet_regs *eth = fec->bus->priv; 2399e27e9dcSMarek Vasut 2400b23fb36SIlya Yanok /* 2410b23fb36SIlya Yanok * Wake up from sleep if necessary 2420b23fb36SIlya Yanok * Reset PHY, then delay 300ns 2430b23fb36SIlya Yanok */ 244cb17b92dSJohn Rigby #ifdef CONFIG_MX27 24513947f43STroy Kisky fec_mdio_write(eth, fec->phy_id, MII_DCOUNTER, 0x00FF); 246cb17b92dSJohn Rigby #endif 24713947f43STroy Kisky fec_mdio_write(eth, fec->phy_id, MII_BMCR, BMCR_RESET); 2480b23fb36SIlya Yanok udelay(1000); 2490b23fb36SIlya Yanok 250567173a6SJagan Teki /* Set the auto-negotiation advertisement register bits */ 25113947f43STroy Kisky fec_mdio_write(eth, fec->phy_id, MII_ADVERTISE, 2528ef583a0SMike Frysinger LPA_100FULL | LPA_100HALF | LPA_10FULL | 2538ef583a0SMike Frysinger LPA_10HALF | PHY_ANLPAR_PSB_802_3); 25413947f43STroy Kisky fec_mdio_write(eth, fec->phy_id, MII_BMCR, 2558ef583a0SMike Frysinger BMCR_ANENABLE | BMCR_ANRESTART); 2562e5f4421SMarek Vasut 2572e5f4421SMarek Vasut if (fec->mii_postcall) 2582e5f4421SMarek Vasut ret = fec->mii_postcall(fec->phy_id); 2592e5f4421SMarek Vasut 260b774fe9dSStefano Babic #endif 2612e5f4421SMarek Vasut return ret; 2620b23fb36SIlya Yanok } 2630b23fb36SIlya Yanok 2640750701aSHannes Schmelzer #ifndef CONFIG_FEC_FIXED_SPEED 2650b23fb36SIlya Yanok static int miiphy_wait_aneg(struct eth_device *dev) 2660b23fb36SIlya Yanok { 2670b23fb36SIlya Yanok uint32_t start; 26813947f43STroy Kisky int status; 2699e27e9dcSMarek Vasut struct fec_priv *fec = (struct fec_priv *)dev->priv; 27013947f43STroy Kisky struct ethernet_regs *eth = fec->bus->priv; 2710b23fb36SIlya Yanok 272567173a6SJagan Teki /* Wait for AN completion */ 273a60d1e5bSGraeme Russ start = get_timer(0); 2740b23fb36SIlya Yanok do { 2750b23fb36SIlya Yanok if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { 2760b23fb36SIlya Yanok printf("%s: Autonegotiation timeout\n", dev->name); 2770b23fb36SIlya Yanok return -1; 2780b23fb36SIlya Yanok } 2790b23fb36SIlya Yanok 28013947f43STroy Kisky status = fec_mdio_read(eth, fec->phy_id, MII_BMSR); 28113947f43STroy Kisky if (status < 0) { 28213947f43STroy Kisky printf("%s: Autonegotiation failed. status: %d\n", 2830b23fb36SIlya Yanok dev->name, status); 2840b23fb36SIlya Yanok return -1; 2850b23fb36SIlya Yanok } 2868ef583a0SMike Frysinger } while (!(status & BMSR_LSTATUS)); 2870b23fb36SIlya Yanok 2880b23fb36SIlya Yanok return 0; 2890b23fb36SIlya Yanok } 2900750701aSHannes Schmelzer #endif /* CONFIG_FEC_FIXED_SPEED */ 29113947f43STroy Kisky #endif 29213947f43STroy Kisky 2930b23fb36SIlya Yanok static int fec_rx_task_enable(struct fec_priv *fec) 2940b23fb36SIlya Yanok { 295c0b5a3bbSMarek Vasut writel(FEC_R_DES_ACTIVE_RDAR, &fec->eth->r_des_active); 2960b23fb36SIlya Yanok return 0; 2970b23fb36SIlya Yanok } 2980b23fb36SIlya Yanok 2990b23fb36SIlya Yanok static int fec_rx_task_disable(struct fec_priv *fec) 3000b23fb36SIlya Yanok { 3010b23fb36SIlya Yanok return 0; 3020b23fb36SIlya Yanok } 3030b23fb36SIlya Yanok 3040b23fb36SIlya Yanok static int fec_tx_task_enable(struct fec_priv *fec) 3050b23fb36SIlya Yanok { 306c0b5a3bbSMarek Vasut writel(FEC_X_DES_ACTIVE_TDAR, &fec->eth->x_des_active); 3070b23fb36SIlya Yanok return 0; 3080b23fb36SIlya Yanok } 3090b23fb36SIlya Yanok 3100b23fb36SIlya Yanok static int fec_tx_task_disable(struct fec_priv *fec) 3110b23fb36SIlya Yanok { 3120b23fb36SIlya Yanok return 0; 3130b23fb36SIlya Yanok } 3140b23fb36SIlya Yanok 3150b23fb36SIlya Yanok /** 3160b23fb36SIlya Yanok * Initialize receive task's buffer descriptors 3170b23fb36SIlya Yanok * @param[in] fec all we know about the device yet 3180b23fb36SIlya Yanok * @param[in] count receive buffer count to be allocated 3195c1ad3e6SEric Nelson * @param[in] dsize desired size of each receive buffer 3200b23fb36SIlya Yanok * @return 0 on success 3210b23fb36SIlya Yanok * 32279e5f27bSMarek Vasut * Init all RX descriptors to default values. 3230b23fb36SIlya Yanok */ 32479e5f27bSMarek Vasut static void fec_rbd_init(struct fec_priv *fec, int count, int dsize) 3250b23fb36SIlya Yanok { 3265c1ad3e6SEric Nelson uint32_t size; 327f24e482aSYe Li ulong data; 3285c1ad3e6SEric Nelson int i; 3290b23fb36SIlya Yanok 3300b23fb36SIlya Yanok /* 33179e5f27bSMarek Vasut * Reload the RX descriptors with default values and wipe 33279e5f27bSMarek Vasut * the RX buffers. 3330b23fb36SIlya Yanok */ 3345c1ad3e6SEric Nelson size = roundup(dsize, ARCH_DMA_MINALIGN); 3355c1ad3e6SEric Nelson for (i = 0; i < count; i++) { 336f24e482aSYe Li data = fec->rbd_base[i].data_pointer; 337f24e482aSYe Li memset((void *)data, 0, dsize); 338f24e482aSYe Li flush_dcache_range(data, data + size); 33979e5f27bSMarek Vasut 34079e5f27bSMarek Vasut fec->rbd_base[i].status = FEC_RBD_EMPTY; 34179e5f27bSMarek Vasut fec->rbd_base[i].data_length = 0; 3425c1ad3e6SEric Nelson } 3435c1ad3e6SEric Nelson 3445c1ad3e6SEric Nelson /* Mark the last RBD to close the ring. */ 34579e5f27bSMarek Vasut fec->rbd_base[i - 1].status = FEC_RBD_WRAP | FEC_RBD_EMPTY; 3460b23fb36SIlya Yanok fec->rbd_index = 0; 3470b23fb36SIlya Yanok 348f24e482aSYe Li flush_dcache_range((ulong)fec->rbd_base, 349f24e482aSYe Li (ulong)fec->rbd_base + size); 3500b23fb36SIlya Yanok } 3510b23fb36SIlya Yanok 3520b23fb36SIlya Yanok /** 3530b23fb36SIlya Yanok * Initialize transmit task's buffer descriptors 3540b23fb36SIlya Yanok * @param[in] fec all we know about the device yet 3550b23fb36SIlya Yanok * 3560b23fb36SIlya Yanok * Transmit buffers are created externally. We only have to init the BDs here.\n 3570b23fb36SIlya Yanok * Note: There is a race condition in the hardware. When only one BD is in 3580b23fb36SIlya Yanok * use it must be marked with the WRAP bit to use it for every transmitt. 3590b23fb36SIlya Yanok * This bit in combination with the READY bit results into double transmit 3600b23fb36SIlya Yanok * of each data buffer. It seems the state machine checks READY earlier then 3610b23fb36SIlya Yanok * resetting it after the first transfer. 3620b23fb36SIlya Yanok * Using two BDs solves this issue. 3630b23fb36SIlya Yanok */ 3640b23fb36SIlya Yanok static void fec_tbd_init(struct fec_priv *fec) 3650b23fb36SIlya Yanok { 366f24e482aSYe Li ulong addr = (ulong)fec->tbd_base; 3675c1ad3e6SEric Nelson unsigned size = roundup(2 * sizeof(struct fec_bd), 3685c1ad3e6SEric Nelson ARCH_DMA_MINALIGN); 36979e5f27bSMarek Vasut 37079e5f27bSMarek Vasut memset(fec->tbd_base, 0, size); 37179e5f27bSMarek Vasut fec->tbd_base[0].status = 0; 37279e5f27bSMarek Vasut fec->tbd_base[1].status = FEC_TBD_WRAP; 3730b23fb36SIlya Yanok fec->tbd_index = 0; 3745c1ad3e6SEric Nelson flush_dcache_range(addr, addr + size); 3750b23fb36SIlya Yanok } 3760b23fb36SIlya Yanok 3770b23fb36SIlya Yanok /** 3780b23fb36SIlya Yanok * Mark the given read buffer descriptor as free 3790b23fb36SIlya Yanok * @param[in] last 1 if this is the last buffer descriptor in the chain, else 0 380567173a6SJagan Teki * @param[in] prbd buffer descriptor to mark free again 3810b23fb36SIlya Yanok */ 382567173a6SJagan Teki static void fec_rbd_clean(int last, struct fec_bd *prbd) 3830b23fb36SIlya Yanok { 3845c1ad3e6SEric Nelson unsigned short flags = FEC_RBD_EMPTY; 3850b23fb36SIlya Yanok if (last) 3865c1ad3e6SEric Nelson flags |= FEC_RBD_WRAP; 387567173a6SJagan Teki writew(flags, &prbd->status); 388567173a6SJagan Teki writew(0, &prbd->data_length); 3890b23fb36SIlya Yanok } 3900b23fb36SIlya Yanok 391f54183e6SJagan Teki static int fec_get_hwaddr(int dev_id, unsigned char *mac) 3920b23fb36SIlya Yanok { 393be252b65SFabio Estevam imx_get_mac_from_fuse(dev_id, mac); 3940adb5b76SJoe Hershberger return !is_valid_ethaddr(mac); 3950b23fb36SIlya Yanok } 3960b23fb36SIlya Yanok 39760752ca8SJagan Teki #ifdef CONFIG_DM_ETH 39860752ca8SJagan Teki static int fecmxc_set_hwaddr(struct udevice *dev) 39960752ca8SJagan Teki #else 4004294b248SStefano Babic static int fec_set_hwaddr(struct eth_device *dev) 40160752ca8SJagan Teki #endif 4020b23fb36SIlya Yanok { 40360752ca8SJagan Teki #ifdef CONFIG_DM_ETH 40460752ca8SJagan Teki struct fec_priv *fec = dev_get_priv(dev); 40560752ca8SJagan Teki struct eth_pdata *pdata = dev_get_platdata(dev); 40660752ca8SJagan Teki uchar *mac = pdata->enetaddr; 40760752ca8SJagan Teki #else 4084294b248SStefano Babic uchar *mac = dev->enetaddr; 4090b23fb36SIlya Yanok struct fec_priv *fec = (struct fec_priv *)dev->priv; 41060752ca8SJagan Teki #endif 4110b23fb36SIlya Yanok 4120b23fb36SIlya Yanok writel(0, &fec->eth->iaddr1); 4130b23fb36SIlya Yanok writel(0, &fec->eth->iaddr2); 4140b23fb36SIlya Yanok writel(0, &fec->eth->gaddr1); 4150b23fb36SIlya Yanok writel(0, &fec->eth->gaddr2); 4160b23fb36SIlya Yanok 417567173a6SJagan Teki /* Set physical address */ 4180b23fb36SIlya Yanok writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], 4190b23fb36SIlya Yanok &fec->eth->paddr1); 4200b23fb36SIlya Yanok writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, &fec->eth->paddr2); 4210b23fb36SIlya Yanok 4220b23fb36SIlya Yanok return 0; 4230b23fb36SIlya Yanok } 4240b23fb36SIlya Yanok 425567173a6SJagan Teki /* Do initial configuration of the FEC registers */ 426a5990b26SMarek Vasut static void fec_reg_setup(struct fec_priv *fec) 427a5990b26SMarek Vasut { 428a5990b26SMarek Vasut uint32_t rcntrl; 429a5990b26SMarek Vasut 430567173a6SJagan Teki /* Set interrupt mask register */ 431a5990b26SMarek Vasut writel(0x00000000, &fec->eth->imask); 432a5990b26SMarek Vasut 433567173a6SJagan Teki /* Clear FEC-Lite interrupt event register(IEVENT) */ 434a5990b26SMarek Vasut writel(0xffffffff, &fec->eth->ievent); 435a5990b26SMarek Vasut 436567173a6SJagan Teki /* Set FEC-Lite receive control register(R_CNTRL): */ 437a5990b26SMarek Vasut 438a5990b26SMarek Vasut /* Start with frame length = 1518, common for all modes. */ 439a5990b26SMarek Vasut rcntrl = PKTSIZE << FEC_RCNTRL_MAX_FL_SHIFT; 4409d2d924aSbenoit.thebaudeau@advans if (fec->xcv_type != SEVENWIRE) /* xMII modes */ 4419d2d924aSbenoit.thebaudeau@advans rcntrl |= FEC_RCNTRL_FCE | FEC_RCNTRL_MII_MODE; 4429d2d924aSbenoit.thebaudeau@advans if (fec->xcv_type == RGMII) 443a5990b26SMarek Vasut rcntrl |= FEC_RCNTRL_RGMII; 444a5990b26SMarek Vasut else if (fec->xcv_type == RMII) 445a5990b26SMarek Vasut rcntrl |= FEC_RCNTRL_RMII; 446a5990b26SMarek Vasut 447a5990b26SMarek Vasut writel(rcntrl, &fec->eth->r_cntrl); 448a5990b26SMarek Vasut } 449a5990b26SMarek Vasut 4500b23fb36SIlya Yanok /** 4510b23fb36SIlya Yanok * Start the FEC engine 4520b23fb36SIlya Yanok * @param[in] dev Our device to handle 4530b23fb36SIlya Yanok */ 45460752ca8SJagan Teki #ifdef CONFIG_DM_ETH 45560752ca8SJagan Teki static int fec_open(struct udevice *dev) 45660752ca8SJagan Teki #else 4570b23fb36SIlya Yanok static int fec_open(struct eth_device *edev) 45860752ca8SJagan Teki #endif 4590b23fb36SIlya Yanok { 46060752ca8SJagan Teki #ifdef CONFIG_DM_ETH 46160752ca8SJagan Teki struct fec_priv *fec = dev_get_priv(dev); 46260752ca8SJagan Teki #else 4630b23fb36SIlya Yanok struct fec_priv *fec = (struct fec_priv *)edev->priv; 46460752ca8SJagan Teki #endif 46528774cbaSTroy Kisky int speed; 466f24e482aSYe Li ulong addr, size; 4675c1ad3e6SEric Nelson int i; 4680b23fb36SIlya Yanok 4690b23fb36SIlya Yanok debug("fec_open: fec_open(dev)\n"); 4700b23fb36SIlya Yanok /* full-duplex, heartbeat disabled */ 4710b23fb36SIlya Yanok writel(1 << 2, &fec->eth->x_cntrl); 4720b23fb36SIlya Yanok fec->rbd_index = 0; 4730b23fb36SIlya Yanok 4745c1ad3e6SEric Nelson /* Invalidate all descriptors */ 4755c1ad3e6SEric Nelson for (i = 0; i < FEC_RBD_NUM - 1; i++) 4765c1ad3e6SEric Nelson fec_rbd_clean(0, &fec->rbd_base[i]); 4775c1ad3e6SEric Nelson fec_rbd_clean(1, &fec->rbd_base[i]); 4785c1ad3e6SEric Nelson 4795c1ad3e6SEric Nelson /* Flush the descriptors into RAM */ 4805c1ad3e6SEric Nelson size = roundup(FEC_RBD_NUM * sizeof(struct fec_bd), 4815c1ad3e6SEric Nelson ARCH_DMA_MINALIGN); 482f24e482aSYe Li addr = (ulong)fec->rbd_base; 4835c1ad3e6SEric Nelson flush_dcache_range(addr, addr + size); 4845c1ad3e6SEric Nelson 48528774cbaSTroy Kisky #ifdef FEC_QUIRK_ENET_MAC 4862ef2b950SJason Liu /* Enable ENET HW endian SWAP */ 4872ef2b950SJason Liu writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_DBSWAP, 4882ef2b950SJason Liu &fec->eth->ecntrl); 4892ef2b950SJason Liu /* Enable ENET store and forward mode */ 4902ef2b950SJason Liu writel(readl(&fec->eth->x_wmrk) | FEC_X_WMRK_STRFWD, 4912ef2b950SJason Liu &fec->eth->x_wmrk); 4922ef2b950SJason Liu #endif 493567173a6SJagan Teki /* Enable FEC-Lite controller */ 494cb17b92dSJohn Rigby writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_ETHER_EN, 495cb17b92dSJohn Rigby &fec->eth->ecntrl); 496567173a6SJagan Teki 4977df51fd8SFabio Estevam #if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL) 498740d6ae5SJohn Rigby udelay(100); 499740d6ae5SJohn Rigby 500567173a6SJagan Teki /* setup the MII gasket for RMII mode */ 501740d6ae5SJohn Rigby /* disable the gasket */ 502740d6ae5SJohn Rigby writew(0, &fec->eth->miigsk_enr); 503740d6ae5SJohn Rigby 504740d6ae5SJohn Rigby /* wait for the gasket to be disabled */ 505740d6ae5SJohn Rigby while (readw(&fec->eth->miigsk_enr) & MIIGSK_ENR_READY) 506740d6ae5SJohn Rigby udelay(2); 507740d6ae5SJohn Rigby 508740d6ae5SJohn Rigby /* configure gasket for RMII, 50 MHz, no loopback, and no echo */ 509740d6ae5SJohn Rigby writew(MIIGSK_CFGR_IF_MODE_RMII, &fec->eth->miigsk_cfgr); 510740d6ae5SJohn Rigby 511740d6ae5SJohn Rigby /* re-enable the gasket */ 512740d6ae5SJohn Rigby writew(MIIGSK_ENR_EN, &fec->eth->miigsk_enr); 513740d6ae5SJohn Rigby 514740d6ae5SJohn Rigby /* wait until MII gasket is ready */ 515740d6ae5SJohn Rigby int max_loops = 10; 516740d6ae5SJohn Rigby while ((readw(&fec->eth->miigsk_enr) & MIIGSK_ENR_READY) == 0) { 517740d6ae5SJohn Rigby if (--max_loops <= 0) { 518740d6ae5SJohn Rigby printf("WAIT for MII Gasket ready timed out\n"); 519740d6ae5SJohn Rigby break; 520740d6ae5SJohn Rigby } 521740d6ae5SJohn Rigby } 522740d6ae5SJohn Rigby #endif 5230b23fb36SIlya Yanok 52413947f43STroy Kisky #ifdef CONFIG_PHYLIB 5254dc27eedSTroy Kisky { 52613947f43STroy Kisky /* Start up the PHY */ 52711af8d65STimur Tabi int ret = phy_startup(fec->phydev); 52811af8d65STimur Tabi 52911af8d65STimur Tabi if (ret) { 53011af8d65STimur Tabi printf("Could not initialize PHY %s\n", 53111af8d65STimur Tabi fec->phydev->dev->name); 53211af8d65STimur Tabi return ret; 53311af8d65STimur Tabi } 53413947f43STroy Kisky speed = fec->phydev->speed; 53513947f43STroy Kisky } 5360750701aSHannes Schmelzer #elif CONFIG_FEC_FIXED_SPEED 5370750701aSHannes Schmelzer speed = CONFIG_FEC_FIXED_SPEED; 53813947f43STroy Kisky #else 5390b23fb36SIlya Yanok miiphy_wait_aneg(edev); 54028774cbaSTroy Kisky speed = miiphy_speed(edev->name, fec->phy_id); 5419e27e9dcSMarek Vasut miiphy_duplex(edev->name, fec->phy_id); 54213947f43STroy Kisky #endif 5430b23fb36SIlya Yanok 54428774cbaSTroy Kisky #ifdef FEC_QUIRK_ENET_MAC 54528774cbaSTroy Kisky { 54628774cbaSTroy Kisky u32 ecr = readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_SPEED; 547bcb6e902SAlison Wang u32 rcr = readl(&fec->eth->r_cntrl) & ~FEC_RCNTRL_RMII_10T; 54828774cbaSTroy Kisky if (speed == _1000BASET) 54928774cbaSTroy Kisky ecr |= FEC_ECNTRL_SPEED; 55028774cbaSTroy Kisky else if (speed != _100BASET) 55128774cbaSTroy Kisky rcr |= FEC_RCNTRL_RMII_10T; 55228774cbaSTroy Kisky writel(ecr, &fec->eth->ecntrl); 55328774cbaSTroy Kisky writel(rcr, &fec->eth->r_cntrl); 55428774cbaSTroy Kisky } 55528774cbaSTroy Kisky #endif 55628774cbaSTroy Kisky debug("%s:Speed=%i\n", __func__, speed); 55728774cbaSTroy Kisky 558567173a6SJagan Teki /* Enable SmartDMA receive task */ 5590b23fb36SIlya Yanok fec_rx_task_enable(fec); 5600b23fb36SIlya Yanok 5610b23fb36SIlya Yanok udelay(100000); 5620b23fb36SIlya Yanok return 0; 5630b23fb36SIlya Yanok } 5640b23fb36SIlya Yanok 56560752ca8SJagan Teki #ifdef CONFIG_DM_ETH 56660752ca8SJagan Teki static int fecmxc_init(struct udevice *dev) 56760752ca8SJagan Teki #else 5680b23fb36SIlya Yanok static int fec_init(struct eth_device *dev, bd_t *bd) 56960752ca8SJagan Teki #endif 5700b23fb36SIlya Yanok { 57160752ca8SJagan Teki #ifdef CONFIG_DM_ETH 57260752ca8SJagan Teki struct fec_priv *fec = dev_get_priv(dev); 57360752ca8SJagan Teki #else 5740b23fb36SIlya Yanok struct fec_priv *fec = (struct fec_priv *)dev->priv; 57560752ca8SJagan Teki #endif 576f24e482aSYe Li u8 *mib_ptr = (uint8_t *)&fec->eth->rmon_t_drop; 577f24e482aSYe Li u8 *i; 578f24e482aSYe Li ulong addr; 5790b23fb36SIlya Yanok 580e9319f11SJohn Rigby /* Initialize MAC address */ 58160752ca8SJagan Teki #ifdef CONFIG_DM_ETH 58260752ca8SJagan Teki fecmxc_set_hwaddr(dev); 58360752ca8SJagan Teki #else 584e9319f11SJohn Rigby fec_set_hwaddr(dev); 58560752ca8SJagan Teki #endif 586e9319f11SJohn Rigby 587567173a6SJagan Teki /* Setup transmit descriptors, there are two in total. */ 5885c1ad3e6SEric Nelson fec_tbd_init(fec); 5890b23fb36SIlya Yanok 59079e5f27bSMarek Vasut /* Setup receive descriptors. */ 59179e5f27bSMarek Vasut fec_rbd_init(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE); 5920b23fb36SIlya Yanok 593a5990b26SMarek Vasut fec_reg_setup(fec); 5949eb3770bSMarek Vasut 595f41471e6Sbenoit.thebaudeau@advans if (fec->xcv_type != SEVENWIRE) 596575c5cc0STroy Kisky fec_mii_setspeed(fec->bus->priv); 5979eb3770bSMarek Vasut 598567173a6SJagan Teki /* Set Opcode/Pause Duration Register */ 5990b23fb36SIlya Yanok writel(0x00010020, &fec->eth->op_pause); /* FIXME 0xffff0020; */ 6000b23fb36SIlya Yanok writel(0x2, &fec->eth->x_wmrk); 601567173a6SJagan Teki 602567173a6SJagan Teki /* Set multicast address filter */ 6030b23fb36SIlya Yanok writel(0x00000000, &fec->eth->gaddr1); 6040b23fb36SIlya Yanok writel(0x00000000, &fec->eth->gaddr2); 6050b23fb36SIlya Yanok 606238a53c7SPeng Fan /* Do not access reserved register */ 607cd357ad1SPeng Fan if (!is_mx6ul() && !is_mx6ull() && !is_imx8m()) { 6080b23fb36SIlya Yanok /* clear MIB RAM */ 6099e27e9dcSMarek Vasut for (i = mib_ptr; i <= mib_ptr + 0xfc; i += 4) 6109e27e9dcSMarek Vasut writel(0, i); 6110b23fb36SIlya Yanok 6120b23fb36SIlya Yanok /* FIFO receive start register */ 6130b23fb36SIlya Yanok writel(0x520, &fec->eth->r_fstart); 614fbecbaa1SPeng Fan } 6150b23fb36SIlya Yanok 6160b23fb36SIlya Yanok /* size and address of each buffer */ 6170b23fb36SIlya Yanok writel(FEC_MAX_PKT_SIZE, &fec->eth->emrbr); 618f24e482aSYe Li 619f24e482aSYe Li addr = (ulong)fec->tbd_base; 620f24e482aSYe Li writel((uint32_t)addr, &fec->eth->etdsr); 621f24e482aSYe Li 622f24e482aSYe Li addr = (ulong)fec->rbd_base; 623f24e482aSYe Li writel((uint32_t)addr, &fec->eth->erdsr); 6240b23fb36SIlya Yanok 62513947f43STroy Kisky #ifndef CONFIG_PHYLIB 6260b23fb36SIlya Yanok if (fec->xcv_type != SEVENWIRE) 6270b23fb36SIlya Yanok miiphy_restart_aneg(dev); 62813947f43STroy Kisky #endif 6290b23fb36SIlya Yanok fec_open(dev); 6300b23fb36SIlya Yanok return 0; 6310b23fb36SIlya Yanok } 6320b23fb36SIlya Yanok 6330b23fb36SIlya Yanok /** 6340b23fb36SIlya Yanok * Halt the FEC engine 6350b23fb36SIlya Yanok * @param[in] dev Our device to handle 6360b23fb36SIlya Yanok */ 63760752ca8SJagan Teki #ifdef CONFIG_DM_ETH 63860752ca8SJagan Teki static void fecmxc_halt(struct udevice *dev) 63960752ca8SJagan Teki #else 6400b23fb36SIlya Yanok static void fec_halt(struct eth_device *dev) 64160752ca8SJagan Teki #endif 6420b23fb36SIlya Yanok { 64360752ca8SJagan Teki #ifdef CONFIG_DM_ETH 64460752ca8SJagan Teki struct fec_priv *fec = dev_get_priv(dev); 64560752ca8SJagan Teki #else 6469e27e9dcSMarek Vasut struct fec_priv *fec = (struct fec_priv *)dev->priv; 64760752ca8SJagan Teki #endif 6480b23fb36SIlya Yanok int counter = 0xffff; 6490b23fb36SIlya Yanok 650567173a6SJagan Teki /* issue graceful stop command to the FEC transmitter if necessary */ 651cb17b92dSJohn Rigby writel(FEC_TCNTRL_GTS | readl(&fec->eth->x_cntrl), 6520b23fb36SIlya Yanok &fec->eth->x_cntrl); 6530b23fb36SIlya Yanok 6540b23fb36SIlya Yanok debug("eth_halt: wait for stop regs\n"); 655567173a6SJagan Teki /* wait for graceful stop to register */ 6560b23fb36SIlya Yanok while ((counter--) && (!(readl(&fec->eth->ievent) & FEC_IEVENT_GRA))) 657cb17b92dSJohn Rigby udelay(1); 6580b23fb36SIlya Yanok 659567173a6SJagan Teki /* Disable SmartDMA tasks */ 6600b23fb36SIlya Yanok fec_tx_task_disable(fec); 6610b23fb36SIlya Yanok fec_rx_task_disable(fec); 6620b23fb36SIlya Yanok 6630b23fb36SIlya Yanok /* 6640b23fb36SIlya Yanok * Disable the Ethernet Controller 6650b23fb36SIlya Yanok * Note: this will also reset the BD index counter! 6660b23fb36SIlya Yanok */ 667740d6ae5SJohn Rigby writel(readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_ETHER_EN, 668740d6ae5SJohn Rigby &fec->eth->ecntrl); 6690b23fb36SIlya Yanok fec->rbd_index = 0; 6700b23fb36SIlya Yanok fec->tbd_index = 0; 6710b23fb36SIlya Yanok debug("eth_halt: done\n"); 6720b23fb36SIlya Yanok } 6730b23fb36SIlya Yanok 6740b23fb36SIlya Yanok /** 6750b23fb36SIlya Yanok * Transmit one frame 6760b23fb36SIlya Yanok * @param[in] dev Our ethernet device to handle 6770b23fb36SIlya Yanok * @param[in] packet Pointer to the data to be transmitted 6780b23fb36SIlya Yanok * @param[in] length Data count in bytes 6790b23fb36SIlya Yanok * @return 0 on success 6800b23fb36SIlya Yanok */ 68160752ca8SJagan Teki #ifdef CONFIG_DM_ETH 68260752ca8SJagan Teki static int fecmxc_send(struct udevice *dev, void *packet, int length) 68360752ca8SJagan Teki #else 684442dac4cSJoe Hershberger static int fec_send(struct eth_device *dev, void *packet, int length) 68560752ca8SJagan Teki #endif 6860b23fb36SIlya Yanok { 6870b23fb36SIlya Yanok unsigned int status; 688f24e482aSYe Li u32 size; 689f24e482aSYe Li ulong addr, end; 690bc1ce150SMarek Vasut int timeout = FEC_XFER_TIMEOUT; 691bc1ce150SMarek Vasut int ret = 0; 6920b23fb36SIlya Yanok 6930b23fb36SIlya Yanok /* 6940b23fb36SIlya Yanok * This routine transmits one frame. This routine only accepts 6950b23fb36SIlya Yanok * 6-byte Ethernet addresses. 6960b23fb36SIlya Yanok */ 69760752ca8SJagan Teki #ifdef CONFIG_DM_ETH 69860752ca8SJagan Teki struct fec_priv *fec = dev_get_priv(dev); 69960752ca8SJagan Teki #else 7000b23fb36SIlya Yanok struct fec_priv *fec = (struct fec_priv *)dev->priv; 70160752ca8SJagan Teki #endif 7020b23fb36SIlya Yanok 7030b23fb36SIlya Yanok /* 7040b23fb36SIlya Yanok * Check for valid length of data. 7050b23fb36SIlya Yanok */ 7060b23fb36SIlya Yanok if ((length > 1500) || (length <= 0)) { 7074294b248SStefano Babic printf("Payload (%d) too large\n", length); 7080b23fb36SIlya Yanok return -1; 7090b23fb36SIlya Yanok } 7100b23fb36SIlya Yanok 7110b23fb36SIlya Yanok /* 7125c1ad3e6SEric Nelson * Setup the transmit buffer. We are always using the first buffer for 7135c1ad3e6SEric Nelson * transmission, the second will be empty and only used to stop the DMA 7145c1ad3e6SEric Nelson * engine. We also flush the packet to RAM here to avoid cache trouble. 7150b23fb36SIlya Yanok */ 716be7e87e2SMarek Vasut #ifdef CONFIG_FEC_MXC_SWAP_PACKET 717be7e87e2SMarek Vasut swap_packet((uint32_t *)packet, length); 718be7e87e2SMarek Vasut #endif 7195c1ad3e6SEric Nelson 720f24e482aSYe Li addr = (ulong)packet; 721efe24d2eSMarek Vasut end = roundup(addr + length, ARCH_DMA_MINALIGN); 722efe24d2eSMarek Vasut addr &= ~(ARCH_DMA_MINALIGN - 1); 723efe24d2eSMarek Vasut flush_dcache_range(addr, end); 7245c1ad3e6SEric Nelson 7250b23fb36SIlya Yanok writew(length, &fec->tbd_base[fec->tbd_index].data_length); 726f24e482aSYe Li writel((uint32_t)addr, &fec->tbd_base[fec->tbd_index].data_pointer); 7275c1ad3e6SEric Nelson 7280b23fb36SIlya Yanok /* 7290b23fb36SIlya Yanok * update BD's status now 7300b23fb36SIlya Yanok * This block: 7310b23fb36SIlya Yanok * - is always the last in a chain (means no chain) 7320b23fb36SIlya Yanok * - should transmitt the CRC 7330b23fb36SIlya Yanok * - might be the last BD in the list, so the address counter should 7340b23fb36SIlya Yanok * wrap (-> keep the WRAP flag) 7350b23fb36SIlya Yanok */ 7360b23fb36SIlya Yanok status = readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_WRAP; 7370b23fb36SIlya Yanok status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; 7380b23fb36SIlya Yanok writew(status, &fec->tbd_base[fec->tbd_index].status); 7390b23fb36SIlya Yanok 7400b23fb36SIlya Yanok /* 7415c1ad3e6SEric Nelson * Flush data cache. This code flushes both TX descriptors to RAM. 7425c1ad3e6SEric Nelson * After this code, the descriptors will be safely in RAM and we 7435c1ad3e6SEric Nelson * can start DMA. 7445c1ad3e6SEric Nelson */ 7455c1ad3e6SEric Nelson size = roundup(2 * sizeof(struct fec_bd), ARCH_DMA_MINALIGN); 746f24e482aSYe Li addr = (ulong)fec->tbd_base; 7475c1ad3e6SEric Nelson flush_dcache_range(addr, addr + size); 7485c1ad3e6SEric Nelson 7495c1ad3e6SEric Nelson /* 750ab94cd49SMarek Vasut * Below we read the DMA descriptor's last four bytes back from the 751ab94cd49SMarek Vasut * DRAM. This is important in order to make sure that all WRITE 752ab94cd49SMarek Vasut * operations on the bus that were triggered by previous cache FLUSH 753ab94cd49SMarek Vasut * have completed. 754ab94cd49SMarek Vasut * 755ab94cd49SMarek Vasut * Otherwise, on MX28, it is possible to observe a corruption of the 756ab94cd49SMarek Vasut * DMA descriptors. Please refer to schematic "Figure 1-2" in MX28RM 757ab94cd49SMarek Vasut * for the bus structure of MX28. The scenario is as follows: 758ab94cd49SMarek Vasut * 759ab94cd49SMarek Vasut * 1) ARM core triggers a series of WRITEs on the AHB_ARB2 bus going 760ab94cd49SMarek Vasut * to DRAM due to flush_dcache_range() 761ab94cd49SMarek Vasut * 2) ARM core writes the FEC registers via AHB_ARB2 762ab94cd49SMarek Vasut * 3) FEC DMA starts reading/writing from/to DRAM via AHB_ARB3 763ab94cd49SMarek Vasut * 764ab94cd49SMarek Vasut * Note that 2) does sometimes finish before 1) due to reordering of 765ab94cd49SMarek Vasut * WRITE accesses on the AHB bus, therefore triggering 3) before the 766ab94cd49SMarek Vasut * DMA descriptor is fully written into DRAM. This results in occasional 767ab94cd49SMarek Vasut * corruption of the DMA descriptor. 768ab94cd49SMarek Vasut */ 769ab94cd49SMarek Vasut readl(addr + size - 4); 770ab94cd49SMarek Vasut 771567173a6SJagan Teki /* Enable SmartDMA transmit task */ 7720b23fb36SIlya Yanok fec_tx_task_enable(fec); 7730b23fb36SIlya Yanok 7740b23fb36SIlya Yanok /* 7755c1ad3e6SEric Nelson * Wait until frame is sent. On each turn of the wait cycle, we must 7765c1ad3e6SEric Nelson * invalidate data cache to see what's really in RAM. Also, we need 7775c1ad3e6SEric Nelson * barrier here. 7780b23fb36SIlya Yanok */ 77967449098SMarek Vasut while (--timeout) { 780c0b5a3bbSMarek Vasut if (!(readl(&fec->eth->x_des_active) & FEC_X_DES_ACTIVE_TDAR)) 781bc1ce150SMarek Vasut break; 782bc1ce150SMarek Vasut } 7835c1ad3e6SEric Nelson 784f599288dSFabio Estevam if (!timeout) { 785f599288dSFabio Estevam ret = -EINVAL; 786f599288dSFabio Estevam goto out; 787f599288dSFabio Estevam } 788f599288dSFabio Estevam 789f599288dSFabio Estevam /* 790f599288dSFabio Estevam * The TDAR bit is cleared when the descriptors are all out from TX 791f599288dSFabio Estevam * but on mx6solox we noticed that the READY bit is still not cleared 792f599288dSFabio Estevam * right after TDAR. 793f599288dSFabio Estevam * These are two distinct signals, and in IC simulation, we found that 794f599288dSFabio Estevam * TDAR always gets cleared prior than the READY bit of last BD becomes 795f599288dSFabio Estevam * cleared. 796f599288dSFabio Estevam * In mx6solox, we use a later version of FEC IP. It looks like that 797f599288dSFabio Estevam * this intrinsic behaviour of TDAR bit has changed in this newer FEC 798f599288dSFabio Estevam * version. 799f599288dSFabio Estevam * 800f599288dSFabio Estevam * Fix this by polling the READY bit of BD after the TDAR polling, 801f599288dSFabio Estevam * which covers the mx6solox case and does not harm the other SoCs. 802f599288dSFabio Estevam */ 803f599288dSFabio Estevam timeout = FEC_XFER_TIMEOUT; 804f599288dSFabio Estevam while (--timeout) { 805f599288dSFabio Estevam invalidate_dcache_range(addr, addr + size); 806f599288dSFabio Estevam if (!(readw(&fec->tbd_base[fec->tbd_index].status) & 807f599288dSFabio Estevam FEC_TBD_READY)) 808f599288dSFabio Estevam break; 809f599288dSFabio Estevam } 810f599288dSFabio Estevam 81167449098SMarek Vasut if (!timeout) 81267449098SMarek Vasut ret = -EINVAL; 81367449098SMarek Vasut 814f599288dSFabio Estevam out: 81567449098SMarek Vasut debug("fec_send: status 0x%x index %d ret %i\n", 8160b23fb36SIlya Yanok readw(&fec->tbd_base[fec->tbd_index].status), 81767449098SMarek Vasut fec->tbd_index, ret); 8180b23fb36SIlya Yanok /* for next transmission use the other buffer */ 8190b23fb36SIlya Yanok if (fec->tbd_index) 8200b23fb36SIlya Yanok fec->tbd_index = 0; 8210b23fb36SIlya Yanok else 8220b23fb36SIlya Yanok fec->tbd_index = 1; 8230b23fb36SIlya Yanok 824bc1ce150SMarek Vasut return ret; 8250b23fb36SIlya Yanok } 8260b23fb36SIlya Yanok 8270b23fb36SIlya Yanok /** 8280b23fb36SIlya Yanok * Pull one frame from the card 8290b23fb36SIlya Yanok * @param[in] dev Our ethernet device to handle 8300b23fb36SIlya Yanok * @return Length of packet read 8310b23fb36SIlya Yanok */ 83260752ca8SJagan Teki #ifdef CONFIG_DM_ETH 83360752ca8SJagan Teki static int fecmxc_recv(struct udevice *dev, int flags, uchar **packetp) 83460752ca8SJagan Teki #else 8350b23fb36SIlya Yanok static int fec_recv(struct eth_device *dev) 83660752ca8SJagan Teki #endif 8370b23fb36SIlya Yanok { 83860752ca8SJagan Teki #ifdef CONFIG_DM_ETH 83960752ca8SJagan Teki struct fec_priv *fec = dev_get_priv(dev); 84060752ca8SJagan Teki #else 8410b23fb36SIlya Yanok struct fec_priv *fec = (struct fec_priv *)dev->priv; 84260752ca8SJagan Teki #endif 8430b23fb36SIlya Yanok struct fec_bd *rbd = &fec->rbd_base[fec->rbd_index]; 8440b23fb36SIlya Yanok unsigned long ievent; 8450b23fb36SIlya Yanok int frame_length, len = 0; 8460b23fb36SIlya Yanok uint16_t bd_status; 847f24e482aSYe Li ulong addr, size, end; 8485c1ad3e6SEric Nelson int i; 84907763ac9SYe Li 85007763ac9SYe Li #ifdef CONFIG_DM_ETH 85107763ac9SYe Li *packetp = memalign(ARCH_DMA_MINALIGN, FEC_MAX_PKT_SIZE); 85207763ac9SYe Li if (*packetp == 0) { 85307763ac9SYe Li printf("%s: error allocating packetp\n", __func__); 85407763ac9SYe Li return -ENOMEM; 85507763ac9SYe Li } 85607763ac9SYe Li #else 857fd37f195SFabio Estevam ALLOC_CACHE_ALIGN_BUFFER(uchar, buff, FEC_MAX_PKT_SIZE); 85807763ac9SYe Li #endif 8590b23fb36SIlya Yanok 860567173a6SJagan Teki /* Check if any critical events have happened */ 8610b23fb36SIlya Yanok ievent = readl(&fec->eth->ievent); 8620b23fb36SIlya Yanok writel(ievent, &fec->eth->ievent); 863eda959f3SMarek Vasut debug("fec_recv: ievent 0x%lx\n", ievent); 8640b23fb36SIlya Yanok if (ievent & FEC_IEVENT_BABR) { 86560752ca8SJagan Teki #ifdef CONFIG_DM_ETH 86660752ca8SJagan Teki fecmxc_halt(dev); 86760752ca8SJagan Teki fecmxc_init(dev); 86860752ca8SJagan Teki #else 8690b23fb36SIlya Yanok fec_halt(dev); 8700b23fb36SIlya Yanok fec_init(dev, fec->bd); 87160752ca8SJagan Teki #endif 8720b23fb36SIlya Yanok printf("some error: 0x%08lx\n", ievent); 8730b23fb36SIlya Yanok return 0; 8740b23fb36SIlya Yanok } 8750b23fb36SIlya Yanok if (ievent & FEC_IEVENT_HBERR) { 8760b23fb36SIlya Yanok /* Heartbeat error */ 8770b23fb36SIlya Yanok writel(0x00000001 | readl(&fec->eth->x_cntrl), 8780b23fb36SIlya Yanok &fec->eth->x_cntrl); 8790b23fb36SIlya Yanok } 8800b23fb36SIlya Yanok if (ievent & FEC_IEVENT_GRA) { 8810b23fb36SIlya Yanok /* Graceful stop complete */ 8820b23fb36SIlya Yanok if (readl(&fec->eth->x_cntrl) & 0x00000001) { 88360752ca8SJagan Teki #ifdef CONFIG_DM_ETH 88460752ca8SJagan Teki fecmxc_halt(dev); 88560752ca8SJagan Teki #else 8860b23fb36SIlya Yanok fec_halt(dev); 88760752ca8SJagan Teki #endif 8880b23fb36SIlya Yanok writel(~0x00000001 & readl(&fec->eth->x_cntrl), 8890b23fb36SIlya Yanok &fec->eth->x_cntrl); 89060752ca8SJagan Teki #ifdef CONFIG_DM_ETH 89160752ca8SJagan Teki fecmxc_init(dev); 89260752ca8SJagan Teki #else 8930b23fb36SIlya Yanok fec_init(dev, fec->bd); 89460752ca8SJagan Teki #endif 8950b23fb36SIlya Yanok } 8960b23fb36SIlya Yanok } 8970b23fb36SIlya Yanok 8980b23fb36SIlya Yanok /* 8995c1ad3e6SEric Nelson * Read the buffer status. Before the status can be read, the data cache 9005c1ad3e6SEric Nelson * must be invalidated, because the data in RAM might have been changed 9015c1ad3e6SEric Nelson * by DMA. The descriptors are properly aligned to cachelines so there's 9025c1ad3e6SEric Nelson * no need to worry they'd overlap. 9035c1ad3e6SEric Nelson * 9045c1ad3e6SEric Nelson * WARNING: By invalidating the descriptor here, we also invalidate 9055c1ad3e6SEric Nelson * the descriptors surrounding this one. Therefore we can NOT change the 9065c1ad3e6SEric Nelson * contents of this descriptor nor the surrounding ones. The problem is 9075c1ad3e6SEric Nelson * that in order to mark the descriptor as processed, we need to change 9085c1ad3e6SEric Nelson * the descriptor. The solution is to mark the whole cache line when all 9095c1ad3e6SEric Nelson * descriptors in the cache line are processed. 9100b23fb36SIlya Yanok */ 911f24e482aSYe Li addr = (ulong)rbd; 9125c1ad3e6SEric Nelson addr &= ~(ARCH_DMA_MINALIGN - 1); 9135c1ad3e6SEric Nelson size = roundup(sizeof(struct fec_bd), ARCH_DMA_MINALIGN); 9145c1ad3e6SEric Nelson invalidate_dcache_range(addr, addr + size); 9155c1ad3e6SEric Nelson 9160b23fb36SIlya Yanok bd_status = readw(&rbd->status); 9170b23fb36SIlya Yanok debug("fec_recv: status 0x%x\n", bd_status); 9180b23fb36SIlya Yanok 9190b23fb36SIlya Yanok if (!(bd_status & FEC_RBD_EMPTY)) { 9200b23fb36SIlya Yanok if ((bd_status & FEC_RBD_LAST) && !(bd_status & FEC_RBD_ERR) && 9210b23fb36SIlya Yanok ((readw(&rbd->data_length) - 4) > 14)) { 922567173a6SJagan Teki /* Get buffer address and size */ 923b189584bSAlbert ARIBAUD \(3ADEV\) addr = readl(&rbd->data_pointer); 9240b23fb36SIlya Yanok frame_length = readw(&rbd->data_length) - 4; 925567173a6SJagan Teki /* Invalidate data cache over the buffer */ 926efe24d2eSMarek Vasut end = roundup(addr + frame_length, ARCH_DMA_MINALIGN); 927efe24d2eSMarek Vasut addr &= ~(ARCH_DMA_MINALIGN - 1); 928efe24d2eSMarek Vasut invalidate_dcache_range(addr, end); 9295c1ad3e6SEric Nelson 930567173a6SJagan Teki /* Fill the buffer and pass it to upper layers */ 931be7e87e2SMarek Vasut #ifdef CONFIG_FEC_MXC_SWAP_PACKET 932b189584bSAlbert ARIBAUD \(3ADEV\) swap_packet((uint32_t *)addr, frame_length); 933be7e87e2SMarek Vasut #endif 93407763ac9SYe Li 93507763ac9SYe Li #ifdef CONFIG_DM_ETH 93607763ac9SYe Li memcpy(*packetp, (char *)addr, frame_length); 93707763ac9SYe Li #else 938b189584bSAlbert ARIBAUD \(3ADEV\) memcpy(buff, (char *)addr, frame_length); 9391fd92db8SJoe Hershberger net_process_received_packet(buff, frame_length); 94007763ac9SYe Li #endif 9410b23fb36SIlya Yanok len = frame_length; 9420b23fb36SIlya Yanok } else { 9430b23fb36SIlya Yanok if (bd_status & FEC_RBD_ERR) 944f24e482aSYe Li debug("error frame: 0x%08lx 0x%08x\n", 945b189584bSAlbert ARIBAUD \(3ADEV\) addr, bd_status); 9460b23fb36SIlya Yanok } 9475c1ad3e6SEric Nelson 9480b23fb36SIlya Yanok /* 9495c1ad3e6SEric Nelson * Free the current buffer, restart the engine and move forward 9505c1ad3e6SEric Nelson * to the next buffer. Here we check if the whole cacheline of 9515c1ad3e6SEric Nelson * descriptors was already processed and if so, we mark it free 9525c1ad3e6SEric Nelson * as whole. 9530b23fb36SIlya Yanok */ 9545c1ad3e6SEric Nelson size = RXDESC_PER_CACHELINE - 1; 9555c1ad3e6SEric Nelson if ((fec->rbd_index & size) == size) { 9565c1ad3e6SEric Nelson i = fec->rbd_index - size; 957f24e482aSYe Li addr = (ulong)&fec->rbd_base[i]; 9585c1ad3e6SEric Nelson for (; i <= fec->rbd_index ; i++) { 9595c1ad3e6SEric Nelson fec_rbd_clean(i == (FEC_RBD_NUM - 1), 9605c1ad3e6SEric Nelson &fec->rbd_base[i]); 9615c1ad3e6SEric Nelson } 9625c1ad3e6SEric Nelson flush_dcache_range(addr, 9635c1ad3e6SEric Nelson addr + ARCH_DMA_MINALIGN); 9645c1ad3e6SEric Nelson } 9655c1ad3e6SEric Nelson 9660b23fb36SIlya Yanok fec_rx_task_enable(fec); 9670b23fb36SIlya Yanok fec->rbd_index = (fec->rbd_index + 1) % FEC_RBD_NUM; 9680b23fb36SIlya Yanok } 9690b23fb36SIlya Yanok debug("fec_recv: stop\n"); 9700b23fb36SIlya Yanok 9710b23fb36SIlya Yanok return len; 9720b23fb36SIlya Yanok } 9730b23fb36SIlya Yanok 974ef8e3a3bSTroy Kisky static void fec_set_dev_name(char *dest, int dev_id) 975ef8e3a3bSTroy Kisky { 976ef8e3a3bSTroy Kisky sprintf(dest, (dev_id == -1) ? "FEC" : "FEC%i", dev_id); 977ef8e3a3bSTroy Kisky } 978ef8e3a3bSTroy Kisky 97979e5f27bSMarek Vasut static int fec_alloc_descs(struct fec_priv *fec) 98079e5f27bSMarek Vasut { 98179e5f27bSMarek Vasut unsigned int size; 98279e5f27bSMarek Vasut int i; 98379e5f27bSMarek Vasut uint8_t *data; 984f24e482aSYe Li ulong addr; 98579e5f27bSMarek Vasut 98679e5f27bSMarek Vasut /* Allocate TX descriptors. */ 98779e5f27bSMarek Vasut size = roundup(2 * sizeof(struct fec_bd), ARCH_DMA_MINALIGN); 98879e5f27bSMarek Vasut fec->tbd_base = memalign(ARCH_DMA_MINALIGN, size); 98979e5f27bSMarek Vasut if (!fec->tbd_base) 99079e5f27bSMarek Vasut goto err_tx; 99179e5f27bSMarek Vasut 99279e5f27bSMarek Vasut /* Allocate RX descriptors. */ 99379e5f27bSMarek Vasut size = roundup(FEC_RBD_NUM * sizeof(struct fec_bd), ARCH_DMA_MINALIGN); 99479e5f27bSMarek Vasut fec->rbd_base = memalign(ARCH_DMA_MINALIGN, size); 99579e5f27bSMarek Vasut if (!fec->rbd_base) 99679e5f27bSMarek Vasut goto err_rx; 99779e5f27bSMarek Vasut 99879e5f27bSMarek Vasut memset(fec->rbd_base, 0, size); 99979e5f27bSMarek Vasut 100079e5f27bSMarek Vasut /* Allocate RX buffers. */ 100179e5f27bSMarek Vasut 100279e5f27bSMarek Vasut /* Maximum RX buffer size. */ 1003db5b7f56SFabio Estevam size = roundup(FEC_MAX_PKT_SIZE, FEC_DMA_RX_MINALIGN); 100479e5f27bSMarek Vasut for (i = 0; i < FEC_RBD_NUM; i++) { 1005db5b7f56SFabio Estevam data = memalign(FEC_DMA_RX_MINALIGN, size); 100679e5f27bSMarek Vasut if (!data) { 100779e5f27bSMarek Vasut printf("%s: error allocating rxbuf %d\n", __func__, i); 100879e5f27bSMarek Vasut goto err_ring; 100979e5f27bSMarek Vasut } 101079e5f27bSMarek Vasut 101179e5f27bSMarek Vasut memset(data, 0, size); 101279e5f27bSMarek Vasut 1013f24e482aSYe Li addr = (ulong)data; 1014f24e482aSYe Li fec->rbd_base[i].data_pointer = (uint32_t)addr; 101579e5f27bSMarek Vasut fec->rbd_base[i].status = FEC_RBD_EMPTY; 101679e5f27bSMarek Vasut fec->rbd_base[i].data_length = 0; 101779e5f27bSMarek Vasut /* Flush the buffer to memory. */ 1018f24e482aSYe Li flush_dcache_range(addr, addr + size); 101979e5f27bSMarek Vasut } 102079e5f27bSMarek Vasut 102179e5f27bSMarek Vasut /* Mark the last RBD to close the ring. */ 102279e5f27bSMarek Vasut fec->rbd_base[i - 1].status = FEC_RBD_WRAP | FEC_RBD_EMPTY; 102379e5f27bSMarek Vasut 102479e5f27bSMarek Vasut fec->rbd_index = 0; 102579e5f27bSMarek Vasut fec->tbd_index = 0; 102679e5f27bSMarek Vasut 102779e5f27bSMarek Vasut return 0; 102879e5f27bSMarek Vasut 102979e5f27bSMarek Vasut err_ring: 1030f24e482aSYe Li for (; i >= 0; i--) { 1031f24e482aSYe Li addr = fec->rbd_base[i].data_pointer; 1032f24e482aSYe Li free((void *)addr); 1033f24e482aSYe Li } 103479e5f27bSMarek Vasut free(fec->rbd_base); 103579e5f27bSMarek Vasut err_rx: 103679e5f27bSMarek Vasut free(fec->tbd_base); 103779e5f27bSMarek Vasut err_tx: 103879e5f27bSMarek Vasut return -ENOMEM; 103979e5f27bSMarek Vasut } 104079e5f27bSMarek Vasut 104179e5f27bSMarek Vasut static void fec_free_descs(struct fec_priv *fec) 104279e5f27bSMarek Vasut { 104379e5f27bSMarek Vasut int i; 1044f24e482aSYe Li ulong addr; 104579e5f27bSMarek Vasut 1046f24e482aSYe Li for (i = 0; i < FEC_RBD_NUM; i++) { 1047f24e482aSYe Li addr = fec->rbd_base[i].data_pointer; 1048f24e482aSYe Li free((void *)addr); 1049f24e482aSYe Li } 105079e5f27bSMarek Vasut free(fec->rbd_base); 105179e5f27bSMarek Vasut free(fec->tbd_base); 105279e5f27bSMarek Vasut } 105379e5f27bSMarek Vasut 10541bcabd79SPeng Fan struct mii_dev *fec_get_miibus(ulong base_addr, int dev_id) 105560752ca8SJagan Teki { 10561bcabd79SPeng Fan struct ethernet_regs *eth = (struct ethernet_regs *)base_addr; 105760752ca8SJagan Teki struct mii_dev *bus; 105860752ca8SJagan Teki int ret; 105960752ca8SJagan Teki 106060752ca8SJagan Teki bus = mdio_alloc(); 106160752ca8SJagan Teki if (!bus) { 106260752ca8SJagan Teki printf("mdio_alloc failed\n"); 106360752ca8SJagan Teki return NULL; 106460752ca8SJagan Teki } 106560752ca8SJagan Teki bus->read = fec_phy_read; 106660752ca8SJagan Teki bus->write = fec_phy_write; 106760752ca8SJagan Teki bus->priv = eth; 106860752ca8SJagan Teki fec_set_dev_name(bus->name, dev_id); 106960752ca8SJagan Teki 107060752ca8SJagan Teki ret = mdio_register(bus); 107160752ca8SJagan Teki if (ret) { 107260752ca8SJagan Teki printf("mdio_register failed\n"); 107360752ca8SJagan Teki free(bus); 107460752ca8SJagan Teki return NULL; 107560752ca8SJagan Teki } 107660752ca8SJagan Teki fec_mii_setspeed(eth); 107760752ca8SJagan Teki return bus; 107860752ca8SJagan Teki } 107960752ca8SJagan Teki 108060752ca8SJagan Teki #ifndef CONFIG_DM_ETH 1081fe428b90STroy Kisky #ifdef CONFIG_PHYLIB 1082fe428b90STroy Kisky int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr, 1083fe428b90STroy Kisky struct mii_dev *bus, struct phy_device *phydev) 1084fe428b90STroy Kisky #else 1085fe428b90STroy Kisky static int fec_probe(bd_t *bd, int dev_id, uint32_t base_addr, 1086fe428b90STroy Kisky struct mii_dev *bus, int phy_id) 1087fe428b90STroy Kisky #endif 10880b23fb36SIlya Yanok { 10890b23fb36SIlya Yanok struct eth_device *edev; 10909e27e9dcSMarek Vasut struct fec_priv *fec; 10910b23fb36SIlya Yanok unsigned char ethaddr[6]; 1092979a5893SAndy Duan char mac[16]; 1093e382fb48SMarek Vasut uint32_t start; 1094e382fb48SMarek Vasut int ret = 0; 10950b23fb36SIlya Yanok 10960b23fb36SIlya Yanok /* create and fill edev struct */ 10970b23fb36SIlya Yanok edev = (struct eth_device *)malloc(sizeof(struct eth_device)); 10980b23fb36SIlya Yanok if (!edev) { 10999e27e9dcSMarek Vasut puts("fec_mxc: not enough malloc memory for eth_device\n"); 1100e382fb48SMarek Vasut ret = -ENOMEM; 1101e382fb48SMarek Vasut goto err1; 11020b23fb36SIlya Yanok } 11039e27e9dcSMarek Vasut 11049e27e9dcSMarek Vasut fec = (struct fec_priv *)malloc(sizeof(struct fec_priv)); 11059e27e9dcSMarek Vasut if (!fec) { 11069e27e9dcSMarek Vasut puts("fec_mxc: not enough malloc memory for fec_priv\n"); 1107e382fb48SMarek Vasut ret = -ENOMEM; 1108e382fb48SMarek Vasut goto err2; 11099e27e9dcSMarek Vasut } 11109e27e9dcSMarek Vasut 1111de0b9576SNobuhiro Iwamatsu memset(edev, 0, sizeof(*edev)); 11129e27e9dcSMarek Vasut memset(fec, 0, sizeof(*fec)); 11139e27e9dcSMarek Vasut 111479e5f27bSMarek Vasut ret = fec_alloc_descs(fec); 111579e5f27bSMarek Vasut if (ret) 111679e5f27bSMarek Vasut goto err3; 111779e5f27bSMarek Vasut 11180b23fb36SIlya Yanok edev->priv = fec; 11190b23fb36SIlya Yanok edev->init = fec_init; 11200b23fb36SIlya Yanok edev->send = fec_send; 11210b23fb36SIlya Yanok edev->recv = fec_recv; 11220b23fb36SIlya Yanok edev->halt = fec_halt; 1123fb57ec97SHeiko Schocher edev->write_hwaddr = fec_set_hwaddr; 11240b23fb36SIlya Yanok 1125f24e482aSYe Li fec->eth = (struct ethernet_regs *)(ulong)base_addr; 11260b23fb36SIlya Yanok fec->bd = bd; 11270b23fb36SIlya Yanok 1128392b8502SMarek Vasut fec->xcv_type = CONFIG_FEC_XCV_TYPE; 11290b23fb36SIlya Yanok 11300b23fb36SIlya Yanok /* Reset chip. */ 1131cb17b92dSJohn Rigby writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_RESET, &fec->eth->ecntrl); 1132e382fb48SMarek Vasut start = get_timer(0); 1133e382fb48SMarek Vasut while (readl(&fec->eth->ecntrl) & FEC_ECNTRL_RESET) { 1134e382fb48SMarek Vasut if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { 11353450a859SVagrant Cascadian printf("FEC MXC: Timeout resetting chip\n"); 113679e5f27bSMarek Vasut goto err4; 1137e382fb48SMarek Vasut } 11380b23fb36SIlya Yanok udelay(10); 1139e382fb48SMarek Vasut } 11400b23fb36SIlya Yanok 1141a5990b26SMarek Vasut fec_reg_setup(fec); 1142ef8e3a3bSTroy Kisky fec_set_dev_name(edev->name, dev_id); 1143ef8e3a3bSTroy Kisky fec->dev_id = (dev_id == -1) ? 0 : dev_id; 114413947f43STroy Kisky fec->bus = bus; 1145fe428b90STroy Kisky fec_mii_setspeed(bus->priv); 1146fe428b90STroy Kisky #ifdef CONFIG_PHYLIB 1147fe428b90STroy Kisky fec->phydev = phydev; 1148fe428b90STroy Kisky phy_connect_dev(phydev, edev); 1149fe428b90STroy Kisky /* Configure phy */ 1150fe428b90STroy Kisky phy_config(phydev); 1151fe428b90STroy Kisky #else 1152fe428b90STroy Kisky fec->phy_id = phy_id; 1153fe428b90STroy Kisky #endif 11540b23fb36SIlya Yanok eth_register(edev); 1155979a5893SAndy Duan /* only support one eth device, the index number pointed by dev_id */ 1156979a5893SAndy Duan edev->index = fec->dev_id; 11570b23fb36SIlya Yanok 1158f01e4e1eSAndy Duan if (fec_get_hwaddr(fec->dev_id, ethaddr) == 0) { 1159f01e4e1eSAndy Duan debug("got MAC%d address from fuse: %pM\n", fec->dev_id, ethaddr); 11600b23fb36SIlya Yanok memcpy(edev->enetaddr, ethaddr, 6); 1161979a5893SAndy Duan if (fec->dev_id) 1162979a5893SAndy Duan sprintf(mac, "eth%daddr", fec->dev_id); 1163979a5893SAndy Duan else 1164979a5893SAndy Duan strcpy(mac, "ethaddr"); 116500caae6dSSimon Glass if (!env_get(mac)) 1166fd1e959eSSimon Glass eth_env_set_enetaddr(mac, ethaddr); 11674294b248SStefano Babic } 1168e382fb48SMarek Vasut return ret; 116979e5f27bSMarek Vasut err4: 117079e5f27bSMarek Vasut fec_free_descs(fec); 1171e382fb48SMarek Vasut err3: 1172e382fb48SMarek Vasut free(fec); 1173e382fb48SMarek Vasut err2: 1174e382fb48SMarek Vasut free(edev); 1175e382fb48SMarek Vasut err1: 1176e382fb48SMarek Vasut return ret; 11770b23fb36SIlya Yanok } 11780b23fb36SIlya Yanok 1179eef24480STroy Kisky int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr) 1180eef24480STroy Kisky { 1181fe428b90STroy Kisky uint32_t base_mii; 1182fe428b90STroy Kisky struct mii_dev *bus = NULL; 1183fe428b90STroy Kisky #ifdef CONFIG_PHYLIB 1184fe428b90STroy Kisky struct phy_device *phydev = NULL; 1185fe428b90STroy Kisky #endif 1186fe428b90STroy Kisky int ret; 1187fe428b90STroy Kisky 1188fbada485SPeng Fan #ifdef CONFIG_FEC_MXC_MDIO_BASE 1189fe428b90STroy Kisky /* 1190fe428b90STroy Kisky * The i.MX28 has two ethernet interfaces, but they are not equal. 1191fe428b90STroy Kisky * Only the first one can access the MDIO bus. 1192fe428b90STroy Kisky */ 1193fbada485SPeng Fan base_mii = CONFIG_FEC_MXC_MDIO_BASE; 1194fe428b90STroy Kisky #else 1195fe428b90STroy Kisky base_mii = addr; 1196fe428b90STroy Kisky #endif 1197eef24480STroy Kisky debug("eth_init: fec_probe(bd, %i, %i) @ %08x\n", dev_id, phy_id, addr); 1198fe428b90STroy Kisky bus = fec_get_miibus(base_mii, dev_id); 1199fe428b90STroy Kisky if (!bus) 1200fe428b90STroy Kisky return -ENOMEM; 1201fe428b90STroy Kisky #ifdef CONFIG_PHYLIB 1202fe428b90STroy Kisky phydev = phy_find_by_mask(bus, 1 << phy_id, PHY_INTERFACE_MODE_RGMII); 1203fe428b90STroy Kisky if (!phydev) { 1204845a57b4SMåns Rullgård mdio_unregister(bus); 1205fe428b90STroy Kisky free(bus); 1206fe428b90STroy Kisky return -ENOMEM; 1207fe428b90STroy Kisky } 1208fe428b90STroy Kisky ret = fec_probe(bd, dev_id, addr, bus, phydev); 1209fe428b90STroy Kisky #else 1210fe428b90STroy Kisky ret = fec_probe(bd, dev_id, addr, bus, phy_id); 1211fe428b90STroy Kisky #endif 1212fe428b90STroy Kisky if (ret) { 1213fe428b90STroy Kisky #ifdef CONFIG_PHYLIB 1214fe428b90STroy Kisky free(phydev); 1215fe428b90STroy Kisky #endif 1216845a57b4SMåns Rullgård mdio_unregister(bus); 1217fe428b90STroy Kisky free(bus); 1218fe428b90STroy Kisky } 1219fe428b90STroy Kisky return ret; 1220eef24480STroy Kisky } 1221eef24480STroy Kisky 122209439c31STroy Kisky #ifdef CONFIG_FEC_MXC_PHYADDR 12230b23fb36SIlya Yanok int fecmxc_initialize(bd_t *bd) 12240b23fb36SIlya Yanok { 1225eef24480STroy Kisky return fecmxc_initialize_multi(bd, -1, CONFIG_FEC_MXC_PHYADDR, 1226eef24480STroy Kisky IMX_FEC_BASE); 12279e27e9dcSMarek Vasut } 12289e27e9dcSMarek Vasut #endif 12299e27e9dcSMarek Vasut 123013947f43STroy Kisky #ifndef CONFIG_PHYLIB 12312e5f4421SMarek Vasut int fecmxc_register_mii_postcall(struct eth_device *dev, int (*cb)(int)) 12322e5f4421SMarek Vasut { 12332e5f4421SMarek Vasut struct fec_priv *fec = (struct fec_priv *)dev->priv; 12342e5f4421SMarek Vasut fec->mii_postcall = cb; 12352e5f4421SMarek Vasut return 0; 12362e5f4421SMarek Vasut } 123713947f43STroy Kisky #endif 123860752ca8SJagan Teki 123960752ca8SJagan Teki #else 124060752ca8SJagan Teki 12411ed2570fSJagan Teki static int fecmxc_read_rom_hwaddr(struct udevice *dev) 12421ed2570fSJagan Teki { 12431ed2570fSJagan Teki struct fec_priv *priv = dev_get_priv(dev); 12441ed2570fSJagan Teki struct eth_pdata *pdata = dev_get_platdata(dev); 12451ed2570fSJagan Teki 12461ed2570fSJagan Teki return fec_get_hwaddr(priv->dev_id, pdata->enetaddr); 12471ed2570fSJagan Teki } 12481ed2570fSJagan Teki 124907763ac9SYe Li static int fecmxc_free_pkt(struct udevice *dev, uchar *packet, int length) 125007763ac9SYe Li { 125107763ac9SYe Li if (packet) 125207763ac9SYe Li free(packet); 125307763ac9SYe Li 125407763ac9SYe Li return 0; 125507763ac9SYe Li } 125607763ac9SYe Li 125760752ca8SJagan Teki static const struct eth_ops fecmxc_ops = { 125860752ca8SJagan Teki .start = fecmxc_init, 125960752ca8SJagan Teki .send = fecmxc_send, 126060752ca8SJagan Teki .recv = fecmxc_recv, 126107763ac9SYe Li .free_pkt = fecmxc_free_pkt, 126260752ca8SJagan Teki .stop = fecmxc_halt, 126360752ca8SJagan Teki .write_hwaddr = fecmxc_set_hwaddr, 12641ed2570fSJagan Teki .read_rom_hwaddr = fecmxc_read_rom_hwaddr, 126560752ca8SJagan Teki }; 126660752ca8SJagan Teki 1267774ec60bSMartyn Welch static int device_get_phy_addr(struct udevice *dev) 1268774ec60bSMartyn Welch { 1269774ec60bSMartyn Welch struct ofnode_phandle_args phandle_args; 1270774ec60bSMartyn Welch int reg; 1271774ec60bSMartyn Welch 1272774ec60bSMartyn Welch if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0, 1273774ec60bSMartyn Welch &phandle_args)) { 1274774ec60bSMartyn Welch debug("Failed to find phy-handle"); 1275774ec60bSMartyn Welch return -ENODEV; 1276774ec60bSMartyn Welch } 1277774ec60bSMartyn Welch 1278774ec60bSMartyn Welch reg = ofnode_read_u32_default(phandle_args.node, "reg", 0); 1279774ec60bSMartyn Welch 1280774ec60bSMartyn Welch return reg; 1281774ec60bSMartyn Welch } 1282774ec60bSMartyn Welch 128360752ca8SJagan Teki static int fec_phy_init(struct fec_priv *priv, struct udevice *dev) 128460752ca8SJagan Teki { 128560752ca8SJagan Teki struct phy_device *phydev; 1286774ec60bSMartyn Welch int addr; 128760752ca8SJagan Teki 1288774ec60bSMartyn Welch addr = device_get_phy_addr(dev); 1289178d4f00SLukasz Majewski #ifdef CONFIG_FEC_MXC_PHYADDR 1290*b882005aSHannes Schmelzer addr = CONFIG_FEC_MXC_PHYADDR; 129160752ca8SJagan Teki #endif 129260752ca8SJagan Teki 1293*b882005aSHannes Schmelzer phydev = phy_connect(priv->bus, addr, dev, priv->interface); 129460752ca8SJagan Teki if (!phydev) 129560752ca8SJagan Teki return -ENODEV; 129660752ca8SJagan Teki 129760752ca8SJagan Teki priv->phydev = phydev; 129860752ca8SJagan Teki phy_config(phydev); 129960752ca8SJagan Teki 130060752ca8SJagan Teki return 0; 130160752ca8SJagan Teki } 130260752ca8SJagan Teki 1303efd0b791SMichael Trimarchi #ifdef CONFIG_DM_GPIO 1304efd0b791SMichael Trimarchi /* FEC GPIO reset */ 1305efd0b791SMichael Trimarchi static void fec_gpio_reset(struct fec_priv *priv) 1306efd0b791SMichael Trimarchi { 1307efd0b791SMichael Trimarchi debug("fec_gpio_reset: fec_gpio_reset(dev)\n"); 1308efd0b791SMichael Trimarchi if (dm_gpio_is_valid(&priv->phy_reset_gpio)) { 1309efd0b791SMichael Trimarchi dm_gpio_set_value(&priv->phy_reset_gpio, 1); 13109b8b9188SMartin Fuzzey mdelay(priv->reset_delay); 1311efd0b791SMichael Trimarchi dm_gpio_set_value(&priv->phy_reset_gpio, 0); 1312efd0b791SMichael Trimarchi } 1313efd0b791SMichael Trimarchi } 1314efd0b791SMichael Trimarchi #endif 1315efd0b791SMichael Trimarchi 131660752ca8SJagan Teki static int fecmxc_probe(struct udevice *dev) 131760752ca8SJagan Teki { 131860752ca8SJagan Teki struct eth_pdata *pdata = dev_get_platdata(dev); 131960752ca8SJagan Teki struct fec_priv *priv = dev_get_priv(dev); 132060752ca8SJagan Teki struct mii_dev *bus = NULL; 132160752ca8SJagan Teki uint32_t start; 132260752ca8SJagan Teki int ret; 132360752ca8SJagan Teki 132458ec4d33SAnatolij Gustschin if (IS_ENABLED(CONFIG_IMX8)) { 132558ec4d33SAnatolij Gustschin ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk); 132658ec4d33SAnatolij Gustschin if (ret < 0) { 132758ec4d33SAnatolij Gustschin debug("Can't get FEC ipg clk: %d\n", ret); 132858ec4d33SAnatolij Gustschin return ret; 132958ec4d33SAnatolij Gustschin } 133058ec4d33SAnatolij Gustschin ret = clk_enable(&priv->ipg_clk); 133158ec4d33SAnatolij Gustschin if (ret < 0) { 133258ec4d33SAnatolij Gustschin debug("Can't enable FEC ipg clk: %d\n", ret); 133358ec4d33SAnatolij Gustschin return ret; 133458ec4d33SAnatolij Gustschin } 133558ec4d33SAnatolij Gustschin 133658ec4d33SAnatolij Gustschin priv->clk_rate = clk_get_rate(&priv->ipg_clk); 133758ec4d33SAnatolij Gustschin } 133858ec4d33SAnatolij Gustschin 133960752ca8SJagan Teki ret = fec_alloc_descs(priv); 134060752ca8SJagan Teki if (ret) 134160752ca8SJagan Teki return ret; 134260752ca8SJagan Teki 1343ad8c43cbSMartin Fuzzey #ifdef CONFIG_DM_REGULATOR 1344ad8c43cbSMartin Fuzzey if (priv->phy_supply) { 13458f1a5ac7SAdam Ford ret = regulator_set_enable(priv->phy_supply, true); 1346ad8c43cbSMartin Fuzzey if (ret) { 1347ad8c43cbSMartin Fuzzey printf("%s: Error enabling phy supply\n", dev->name); 1348ad8c43cbSMartin Fuzzey return ret; 1349ad8c43cbSMartin Fuzzey } 1350ad8c43cbSMartin Fuzzey } 1351ad8c43cbSMartin Fuzzey #endif 1352ad8c43cbSMartin Fuzzey 1353efd0b791SMichael Trimarchi #ifdef CONFIG_DM_GPIO 1354efd0b791SMichael Trimarchi fec_gpio_reset(priv); 1355efd0b791SMichael Trimarchi #endif 135660752ca8SJagan Teki /* Reset chip. */ 1357567173a6SJagan Teki writel(readl(&priv->eth->ecntrl) | FEC_ECNTRL_RESET, 1358567173a6SJagan Teki &priv->eth->ecntrl); 135960752ca8SJagan Teki start = get_timer(0); 136060752ca8SJagan Teki while (readl(&priv->eth->ecntrl) & FEC_ECNTRL_RESET) { 136160752ca8SJagan Teki if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { 136260752ca8SJagan Teki printf("FEC MXC: Timeout reseting chip\n"); 136360752ca8SJagan Teki goto err_timeout; 136460752ca8SJagan Teki } 136560752ca8SJagan Teki udelay(10); 136660752ca8SJagan Teki } 136760752ca8SJagan Teki 136860752ca8SJagan Teki fec_reg_setup(priv); 136960752ca8SJagan Teki 13708b203863SPeng Fan priv->dev_id = dev->seq; 1371fbada485SPeng Fan #ifdef CONFIG_FEC_MXC_MDIO_BASE 1372fbada485SPeng Fan bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq); 1373fbada485SPeng Fan #else 13748b203863SPeng Fan bus = fec_get_miibus((ulong)priv->eth, dev->seq); 1375fbada485SPeng Fan #endif 1376306dd7daSLothar Waßmann if (!bus) { 1377306dd7daSLothar Waßmann ret = -ENOMEM; 1378306dd7daSLothar Waßmann goto err_mii; 1379306dd7daSLothar Waßmann } 1380306dd7daSLothar Waßmann 1381306dd7daSLothar Waßmann priv->bus = bus; 1382306dd7daSLothar Waßmann priv->interface = pdata->phy_interface; 13830126c641SMartin Fuzzey switch (priv->interface) { 13840126c641SMartin Fuzzey case PHY_INTERFACE_MODE_MII: 13850126c641SMartin Fuzzey priv->xcv_type = MII100; 13860126c641SMartin Fuzzey break; 13870126c641SMartin Fuzzey case PHY_INTERFACE_MODE_RMII: 13880126c641SMartin Fuzzey priv->xcv_type = RMII; 13890126c641SMartin Fuzzey break; 13900126c641SMartin Fuzzey case PHY_INTERFACE_MODE_RGMII: 13910126c641SMartin Fuzzey case PHY_INTERFACE_MODE_RGMII_ID: 13920126c641SMartin Fuzzey case PHY_INTERFACE_MODE_RGMII_RXID: 13930126c641SMartin Fuzzey case PHY_INTERFACE_MODE_RGMII_TXID: 13940126c641SMartin Fuzzey priv->xcv_type = RGMII; 13950126c641SMartin Fuzzey break; 13960126c641SMartin Fuzzey default: 13970126c641SMartin Fuzzey priv->xcv_type = CONFIG_FEC_XCV_TYPE; 13980126c641SMartin Fuzzey printf("Unsupported interface type %d defaulting to %d\n", 13990126c641SMartin Fuzzey priv->interface, priv->xcv_type); 14000126c641SMartin Fuzzey break; 14010126c641SMartin Fuzzey } 14020126c641SMartin Fuzzey 1403306dd7daSLothar Waßmann ret = fec_phy_init(priv, dev); 1404306dd7daSLothar Waßmann if (ret) 1405306dd7daSLothar Waßmann goto err_phy; 1406306dd7daSLothar Waßmann 140760752ca8SJagan Teki return 0; 140860752ca8SJagan Teki 140960752ca8SJagan Teki err_phy: 141060752ca8SJagan Teki mdio_unregister(bus); 141160752ca8SJagan Teki free(bus); 141260752ca8SJagan Teki err_mii: 14132087eac2SYe Li err_timeout: 141460752ca8SJagan Teki fec_free_descs(priv); 141560752ca8SJagan Teki return ret; 141660752ca8SJagan Teki } 141760752ca8SJagan Teki 141860752ca8SJagan Teki static int fecmxc_remove(struct udevice *dev) 141960752ca8SJagan Teki { 142060752ca8SJagan Teki struct fec_priv *priv = dev_get_priv(dev); 142160752ca8SJagan Teki 142260752ca8SJagan Teki free(priv->phydev); 142360752ca8SJagan Teki fec_free_descs(priv); 142460752ca8SJagan Teki mdio_unregister(priv->bus); 142560752ca8SJagan Teki mdio_free(priv->bus); 142660752ca8SJagan Teki 1427ad8c43cbSMartin Fuzzey #ifdef CONFIG_DM_REGULATOR 1428ad8c43cbSMartin Fuzzey if (priv->phy_supply) 1429ad8c43cbSMartin Fuzzey regulator_set_enable(priv->phy_supply, false); 1430ad8c43cbSMartin Fuzzey #endif 1431ad8c43cbSMartin Fuzzey 143260752ca8SJagan Teki return 0; 143360752ca8SJagan Teki } 143460752ca8SJagan Teki 143560752ca8SJagan Teki static int fecmxc_ofdata_to_platdata(struct udevice *dev) 143660752ca8SJagan Teki { 1437efd0b791SMichael Trimarchi int ret = 0; 143860752ca8SJagan Teki struct eth_pdata *pdata = dev_get_platdata(dev); 143960752ca8SJagan Teki struct fec_priv *priv = dev_get_priv(dev); 144060752ca8SJagan Teki const char *phy_mode; 144160752ca8SJagan Teki 1442a821c4afSSimon Glass pdata->iobase = (phys_addr_t)devfdt_get_addr(dev); 144360752ca8SJagan Teki priv->eth = (struct ethernet_regs *)pdata->iobase; 144460752ca8SJagan Teki 144560752ca8SJagan Teki pdata->phy_interface = -1; 1446e160f7d4SSimon Glass phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode", 1447e160f7d4SSimon Glass NULL); 144860752ca8SJagan Teki if (phy_mode) 144960752ca8SJagan Teki pdata->phy_interface = phy_get_interface_by_name(phy_mode); 145060752ca8SJagan Teki if (pdata->phy_interface == -1) { 145160752ca8SJagan Teki debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); 145260752ca8SJagan Teki return -EINVAL; 145360752ca8SJagan Teki } 145460752ca8SJagan Teki 1455ad8c43cbSMartin Fuzzey #ifdef CONFIG_DM_REGULATOR 1456ad8c43cbSMartin Fuzzey device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply); 1457ad8c43cbSMartin Fuzzey #endif 1458ad8c43cbSMartin Fuzzey 1459efd0b791SMichael Trimarchi #ifdef CONFIG_DM_GPIO 1460efd0b791SMichael Trimarchi ret = gpio_request_by_name(dev, "phy-reset-gpios", 0, 1461efd0b791SMichael Trimarchi &priv->phy_reset_gpio, GPIOD_IS_OUT); 1462331fcabeSMartin Fuzzey if (ret < 0) 1463331fcabeSMartin Fuzzey return 0; /* property is optional, don't return error! */ 146460752ca8SJagan Teki 1465331fcabeSMartin Fuzzey priv->reset_delay = dev_read_u32_default(dev, "phy-reset-duration", 1); 1466efd0b791SMichael Trimarchi if (priv->reset_delay > 1000) { 1467331fcabeSMartin Fuzzey printf("FEC MXC: phy reset duration should be <= 1000ms\n"); 1468331fcabeSMartin Fuzzey /* property value wrong, use default value */ 1469331fcabeSMartin Fuzzey priv->reset_delay = 1; 1470efd0b791SMichael Trimarchi } 1471efd0b791SMichael Trimarchi #endif 1472efd0b791SMichael Trimarchi 1473331fcabeSMartin Fuzzey return 0; 147460752ca8SJagan Teki } 147560752ca8SJagan Teki 147660752ca8SJagan Teki static const struct udevice_id fecmxc_ids[] = { 147760752ca8SJagan Teki { .compatible = "fsl,imx6q-fec" }, 1478979e0fc8SPeng Fan { .compatible = "fsl,imx6sl-fec" }, 1479979e0fc8SPeng Fan { .compatible = "fsl,imx6sx-fec" }, 1480979e0fc8SPeng Fan { .compatible = "fsl,imx6ul-fec" }, 1481948239eaSLukasz Majewski { .compatible = "fsl,imx53-fec" }, 148258ec4d33SAnatolij Gustschin { .compatible = "fsl,imx7d-fec" }, 148360752ca8SJagan Teki { } 148460752ca8SJagan Teki }; 148560752ca8SJagan Teki 148660752ca8SJagan Teki U_BOOT_DRIVER(fecmxc_gem) = { 148760752ca8SJagan Teki .name = "fecmxc", 148860752ca8SJagan Teki .id = UCLASS_ETH, 148960752ca8SJagan Teki .of_match = fecmxc_ids, 149060752ca8SJagan Teki .ofdata_to_platdata = fecmxc_ofdata_to_platdata, 149160752ca8SJagan Teki .probe = fecmxc_probe, 149260752ca8SJagan Teki .remove = fecmxc_remove, 149360752ca8SJagan Teki .ops = &fecmxc_ops, 149460752ca8SJagan Teki .priv_auto_alloc_size = sizeof(struct fec_priv), 149560752ca8SJagan Teki .platdata_auto_alloc_size = sizeof(struct eth_pdata), 149660752ca8SJagan Teki }; 149760752ca8SJagan Teki #endif 1498