1*b3dbf4a5SMacpaul Lin /* 2*b3dbf4a5SMacpaul Lin * Faraday FTGMAC100 Ethernet 3*b3dbf4a5SMacpaul Lin * 4*b3dbf4a5SMacpaul Lin * (C) Copyright 2009 Faraday Technology 5*b3dbf4a5SMacpaul Lin * Po-Yu Chuang <ratbert@faraday-tech.com> 6*b3dbf4a5SMacpaul Lin * 7*b3dbf4a5SMacpaul Lin * (C) Copyright 2010 Andes Technology 8*b3dbf4a5SMacpaul Lin * Macpaul Lin <macpaul@andestech.com> 9*b3dbf4a5SMacpaul Lin * 10*b3dbf4a5SMacpaul Lin * This program is free software; you can redistribute it and/or modify 11*b3dbf4a5SMacpaul Lin * it under the terms of the GNU General Public License as published by 12*b3dbf4a5SMacpaul Lin * the Free Software Foundation; either version 2 of the License, or 13*b3dbf4a5SMacpaul Lin * (at your option) any later version. 14*b3dbf4a5SMacpaul Lin * 15*b3dbf4a5SMacpaul Lin * This program is distributed in the hope that it will be useful, 16*b3dbf4a5SMacpaul Lin * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*b3dbf4a5SMacpaul Lin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*b3dbf4a5SMacpaul Lin * GNU General Public License for more details. 19*b3dbf4a5SMacpaul Lin * 20*b3dbf4a5SMacpaul Lin * You should have received a copy of the GNU General Public License 21*b3dbf4a5SMacpaul Lin * along with this program; if not, write to the Free Software 22*b3dbf4a5SMacpaul Lin * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23*b3dbf4a5SMacpaul Lin */ 24*b3dbf4a5SMacpaul Lin 25*b3dbf4a5SMacpaul Lin #include <config.h> 26*b3dbf4a5SMacpaul Lin #include <common.h> 27*b3dbf4a5SMacpaul Lin #include <malloc.h> 28*b3dbf4a5SMacpaul Lin #include <net.h> 29*b3dbf4a5SMacpaul Lin #include <asm/io.h> 30*b3dbf4a5SMacpaul Lin #include <linux/mii.h> 31*b3dbf4a5SMacpaul Lin 32*b3dbf4a5SMacpaul Lin #include "ftgmac100.h" 33*b3dbf4a5SMacpaul Lin 34*b3dbf4a5SMacpaul Lin #define ETH_ZLEN 60 35*b3dbf4a5SMacpaul Lin 36*b3dbf4a5SMacpaul Lin #define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) 37*b3dbf4a5SMacpaul Lin 38*b3dbf4a5SMacpaul Lin /* RBSR - hw default init value is also 0x640 */ 39*b3dbf4a5SMacpaul Lin #define RBSR_DEFAULT_VALUE 0x640 40*b3dbf4a5SMacpaul Lin 41*b3dbf4a5SMacpaul Lin /* PKTBUFSTX/PKTBUFSRX must both be power of 2 */ 42*b3dbf4a5SMacpaul Lin #define PKTBUFSTX 4 /* must be power of 2 */ 43*b3dbf4a5SMacpaul Lin 44*b3dbf4a5SMacpaul Lin struct ftgmac100_data { 45*b3dbf4a5SMacpaul Lin struct ftgmac100_txdes txdes[PKTBUFSTX]; 46*b3dbf4a5SMacpaul Lin struct ftgmac100_rxdes rxdes[PKTBUFSRX]; 47*b3dbf4a5SMacpaul Lin int tx_index; 48*b3dbf4a5SMacpaul Lin int rx_index; 49*b3dbf4a5SMacpaul Lin int phy_addr; 50*b3dbf4a5SMacpaul Lin }; 51*b3dbf4a5SMacpaul Lin 52*b3dbf4a5SMacpaul Lin /* 53*b3dbf4a5SMacpaul Lin * struct mii_bus functions 54*b3dbf4a5SMacpaul Lin */ 55*b3dbf4a5SMacpaul Lin static int ftgmac100_mdiobus_read(struct eth_device *dev, int phy_addr, 56*b3dbf4a5SMacpaul Lin int regnum) 57*b3dbf4a5SMacpaul Lin { 58*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 59*b3dbf4a5SMacpaul Lin int phycr; 60*b3dbf4a5SMacpaul Lin int i; 61*b3dbf4a5SMacpaul Lin 62*b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr); 63*b3dbf4a5SMacpaul Lin 64*b3dbf4a5SMacpaul Lin /* preserve MDC cycle threshold */ 65*b3dbf4a5SMacpaul Lin phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; 66*b3dbf4a5SMacpaul Lin 67*b3dbf4a5SMacpaul Lin phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) 68*b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_REGAD(regnum) 69*b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_MIIRD; 70*b3dbf4a5SMacpaul Lin 71*b3dbf4a5SMacpaul Lin writel(phycr, &ftgmac100->phycr); 72*b3dbf4a5SMacpaul Lin 73*b3dbf4a5SMacpaul Lin for (i = 0; i < 10; i++) { 74*b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr); 75*b3dbf4a5SMacpaul Lin 76*b3dbf4a5SMacpaul Lin if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) { 77*b3dbf4a5SMacpaul Lin int data; 78*b3dbf4a5SMacpaul Lin 79*b3dbf4a5SMacpaul Lin data = readl(&ftgmac100->phydata); 80*b3dbf4a5SMacpaul Lin return FTGMAC100_PHYDATA_MIIRDATA(data); 81*b3dbf4a5SMacpaul Lin } 82*b3dbf4a5SMacpaul Lin 83*b3dbf4a5SMacpaul Lin mdelay(10); 84*b3dbf4a5SMacpaul Lin } 85*b3dbf4a5SMacpaul Lin 86*b3dbf4a5SMacpaul Lin debug("mdio read timed out\n"); 87*b3dbf4a5SMacpaul Lin return -1; 88*b3dbf4a5SMacpaul Lin } 89*b3dbf4a5SMacpaul Lin 90*b3dbf4a5SMacpaul Lin static int ftgmac100_mdiobus_write(struct eth_device *dev, int phy_addr, 91*b3dbf4a5SMacpaul Lin int regnum, u16 value) 92*b3dbf4a5SMacpaul Lin { 93*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 94*b3dbf4a5SMacpaul Lin int phycr; 95*b3dbf4a5SMacpaul Lin int data; 96*b3dbf4a5SMacpaul Lin int i; 97*b3dbf4a5SMacpaul Lin 98*b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr); 99*b3dbf4a5SMacpaul Lin 100*b3dbf4a5SMacpaul Lin /* preserve MDC cycle threshold */ 101*b3dbf4a5SMacpaul Lin phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; 102*b3dbf4a5SMacpaul Lin 103*b3dbf4a5SMacpaul Lin phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) 104*b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_REGAD(regnum) 105*b3dbf4a5SMacpaul Lin | FTGMAC100_PHYCR_MIIWR; 106*b3dbf4a5SMacpaul Lin 107*b3dbf4a5SMacpaul Lin data = FTGMAC100_PHYDATA_MIIWDATA(value); 108*b3dbf4a5SMacpaul Lin 109*b3dbf4a5SMacpaul Lin writel(data, &ftgmac100->phydata); 110*b3dbf4a5SMacpaul Lin writel(phycr, &ftgmac100->phycr); 111*b3dbf4a5SMacpaul Lin 112*b3dbf4a5SMacpaul Lin for (i = 0; i < 10; i++) { 113*b3dbf4a5SMacpaul Lin phycr = readl(&ftgmac100->phycr); 114*b3dbf4a5SMacpaul Lin 115*b3dbf4a5SMacpaul Lin if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0) { 116*b3dbf4a5SMacpaul Lin debug("(phycr & FTGMAC100_PHYCR_MIIWR) == 0: " \ 117*b3dbf4a5SMacpaul Lin "phy_addr: %x\n", phy_addr); 118*b3dbf4a5SMacpaul Lin return 0; 119*b3dbf4a5SMacpaul Lin } 120*b3dbf4a5SMacpaul Lin 121*b3dbf4a5SMacpaul Lin mdelay(1); 122*b3dbf4a5SMacpaul Lin } 123*b3dbf4a5SMacpaul Lin 124*b3dbf4a5SMacpaul Lin debug("mdio write timed out\n"); 125*b3dbf4a5SMacpaul Lin return -1; 126*b3dbf4a5SMacpaul Lin } 127*b3dbf4a5SMacpaul Lin 128*b3dbf4a5SMacpaul Lin int ftgmac100_phy_read(struct eth_device *dev, int addr, int reg, u16 *value) 129*b3dbf4a5SMacpaul Lin { 130*b3dbf4a5SMacpaul Lin *value = ftgmac100_mdiobus_read(dev , addr, reg); 131*b3dbf4a5SMacpaul Lin 132*b3dbf4a5SMacpaul Lin if (*value == -1) 133*b3dbf4a5SMacpaul Lin return -1; 134*b3dbf4a5SMacpaul Lin 135*b3dbf4a5SMacpaul Lin return 0; 136*b3dbf4a5SMacpaul Lin } 137*b3dbf4a5SMacpaul Lin 138*b3dbf4a5SMacpaul Lin int ftgmac100_phy_write(struct eth_device *dev, int addr, int reg, u16 value) 139*b3dbf4a5SMacpaul Lin { 140*b3dbf4a5SMacpaul Lin if (ftgmac100_mdiobus_write(dev, addr, reg, value) == -1) 141*b3dbf4a5SMacpaul Lin return -1; 142*b3dbf4a5SMacpaul Lin 143*b3dbf4a5SMacpaul Lin return 0; 144*b3dbf4a5SMacpaul Lin } 145*b3dbf4a5SMacpaul Lin 146*b3dbf4a5SMacpaul Lin static int ftgmac100_phy_reset(struct eth_device *dev) 147*b3dbf4a5SMacpaul Lin { 148*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv; 149*b3dbf4a5SMacpaul Lin int i; 150*b3dbf4a5SMacpaul Lin u16 status, adv; 151*b3dbf4a5SMacpaul Lin 152*b3dbf4a5SMacpaul Lin adv = ADVERTISE_CSMA | ADVERTISE_ALL; 153*b3dbf4a5SMacpaul Lin 154*b3dbf4a5SMacpaul Lin ftgmac100_phy_write(dev, priv->phy_addr, MII_ADVERTISE, adv); 155*b3dbf4a5SMacpaul Lin 156*b3dbf4a5SMacpaul Lin printf("%s: Starting autonegotiation...\n", dev->name); 157*b3dbf4a5SMacpaul Lin 158*b3dbf4a5SMacpaul Lin ftgmac100_phy_write(dev, priv->phy_addr, 159*b3dbf4a5SMacpaul Lin MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)); 160*b3dbf4a5SMacpaul Lin 161*b3dbf4a5SMacpaul Lin for (i = 0; i < 100000 / 100; i++) { 162*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status); 163*b3dbf4a5SMacpaul Lin 164*b3dbf4a5SMacpaul Lin if (status & BMSR_ANEGCOMPLETE) 165*b3dbf4a5SMacpaul Lin break; 166*b3dbf4a5SMacpaul Lin mdelay(1); 167*b3dbf4a5SMacpaul Lin } 168*b3dbf4a5SMacpaul Lin 169*b3dbf4a5SMacpaul Lin if (status & BMSR_ANEGCOMPLETE) { 170*b3dbf4a5SMacpaul Lin printf("%s: Autonegotiation complete\n", dev->name); 171*b3dbf4a5SMacpaul Lin } else { 172*b3dbf4a5SMacpaul Lin printf("%s: Autonegotiation timed out (status=0x%04x)\n", 173*b3dbf4a5SMacpaul Lin dev->name, status); 174*b3dbf4a5SMacpaul Lin return 0; 175*b3dbf4a5SMacpaul Lin } 176*b3dbf4a5SMacpaul Lin 177*b3dbf4a5SMacpaul Lin return 1; 178*b3dbf4a5SMacpaul Lin } 179*b3dbf4a5SMacpaul Lin 180*b3dbf4a5SMacpaul Lin static int ftgmac100_phy_init(struct eth_device *dev) 181*b3dbf4a5SMacpaul Lin { 182*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv; 183*b3dbf4a5SMacpaul Lin 184*b3dbf4a5SMacpaul Lin int phy_addr; 185*b3dbf4a5SMacpaul Lin u16 phy_id, status, adv, lpa, stat_ge; 186*b3dbf4a5SMacpaul Lin int media, speed, duplex; 187*b3dbf4a5SMacpaul Lin int i; 188*b3dbf4a5SMacpaul Lin 189*b3dbf4a5SMacpaul Lin /* Check if the PHY is up to snuff... */ 190*b3dbf4a5SMacpaul Lin for (phy_addr = 0; phy_addr < CONFIG_PHY_MAX_ADDR; phy_addr++) { 191*b3dbf4a5SMacpaul Lin 192*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, phy_addr, MII_PHYSID1, &phy_id); 193*b3dbf4a5SMacpaul Lin 194*b3dbf4a5SMacpaul Lin /* 195*b3dbf4a5SMacpaul Lin * When it is unable to found PHY, 196*b3dbf4a5SMacpaul Lin * the interface usually return 0xffff or 0x0000 197*b3dbf4a5SMacpaul Lin */ 198*b3dbf4a5SMacpaul Lin if (phy_id != 0xffff && phy_id != 0x0) { 199*b3dbf4a5SMacpaul Lin printf("%s: found PHY at 0x%02x\n", 200*b3dbf4a5SMacpaul Lin dev->name, phy_addr); 201*b3dbf4a5SMacpaul Lin priv->phy_addr = phy_addr; 202*b3dbf4a5SMacpaul Lin break; 203*b3dbf4a5SMacpaul Lin } 204*b3dbf4a5SMacpaul Lin } 205*b3dbf4a5SMacpaul Lin 206*b3dbf4a5SMacpaul Lin if (phy_id == 0xffff || phy_id == 0x0) { 207*b3dbf4a5SMacpaul Lin printf("%s: no PHY present\n", dev->name); 208*b3dbf4a5SMacpaul Lin return 0; 209*b3dbf4a5SMacpaul Lin } 210*b3dbf4a5SMacpaul Lin 211*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status); 212*b3dbf4a5SMacpaul Lin 213*b3dbf4a5SMacpaul Lin if (!(status & BMSR_LSTATUS)) { 214*b3dbf4a5SMacpaul Lin /* Try to re-negotiate if we don't have link already. */ 215*b3dbf4a5SMacpaul Lin ftgmac100_phy_reset(dev); 216*b3dbf4a5SMacpaul Lin 217*b3dbf4a5SMacpaul Lin for (i = 0; i < 100000 / 100; i++) { 218*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, 219*b3dbf4a5SMacpaul Lin MII_BMSR, &status); 220*b3dbf4a5SMacpaul Lin if (status & BMSR_LSTATUS) 221*b3dbf4a5SMacpaul Lin break; 222*b3dbf4a5SMacpaul Lin udelay(100); 223*b3dbf4a5SMacpaul Lin } 224*b3dbf4a5SMacpaul Lin } 225*b3dbf4a5SMacpaul Lin 226*b3dbf4a5SMacpaul Lin if (!(status & BMSR_LSTATUS)) { 227*b3dbf4a5SMacpaul Lin printf("%s: link down\n", dev->name); 228*b3dbf4a5SMacpaul Lin return 0; 229*b3dbf4a5SMacpaul Lin } 230*b3dbf4a5SMacpaul Lin 231*b3dbf4a5SMacpaul Lin #ifdef CONFIG_FTGMAC100_EGIGA 232*b3dbf4a5SMacpaul Lin /* 1000 Base-T Status Register */ 233*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, 234*b3dbf4a5SMacpaul Lin MII_STAT1000, &stat_ge); 235*b3dbf4a5SMacpaul Lin 236*b3dbf4a5SMacpaul Lin speed = (stat_ge & (LPA_1000FULL | LPA_1000HALF) 237*b3dbf4a5SMacpaul Lin ? 1 : 0); 238*b3dbf4a5SMacpaul Lin 239*b3dbf4a5SMacpaul Lin duplex = ((stat_ge & LPA_1000FULL) 240*b3dbf4a5SMacpaul Lin ? 1 : 0); 241*b3dbf4a5SMacpaul Lin 242*b3dbf4a5SMacpaul Lin if (speed) { /* Speed is 1000 */ 243*b3dbf4a5SMacpaul Lin printf("%s: link up, 1000bps %s-duplex\n", 244*b3dbf4a5SMacpaul Lin dev->name, duplex ? "full" : "half"); 245*b3dbf4a5SMacpaul Lin return 0; 246*b3dbf4a5SMacpaul Lin } 247*b3dbf4a5SMacpaul Lin #endif 248*b3dbf4a5SMacpaul Lin 249*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_ADVERTISE, &adv); 250*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_LPA, &lpa); 251*b3dbf4a5SMacpaul Lin 252*b3dbf4a5SMacpaul Lin media = mii_nway_result(lpa & adv); 253*b3dbf4a5SMacpaul Lin speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 1 : 0); 254*b3dbf4a5SMacpaul Lin duplex = (media & ADVERTISE_FULL) ? 1 : 0; 255*b3dbf4a5SMacpaul Lin 256*b3dbf4a5SMacpaul Lin printf("%s: link up, %sMbps %s-duplex\n", 257*b3dbf4a5SMacpaul Lin dev->name, speed ? "100" : "10", duplex ? "full" : "half"); 258*b3dbf4a5SMacpaul Lin 259*b3dbf4a5SMacpaul Lin return 1; 260*b3dbf4a5SMacpaul Lin } 261*b3dbf4a5SMacpaul Lin 262*b3dbf4a5SMacpaul Lin static int ftgmac100_update_link_speed(struct eth_device *dev) 263*b3dbf4a5SMacpaul Lin { 264*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 265*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv; 266*b3dbf4a5SMacpaul Lin 267*b3dbf4a5SMacpaul Lin unsigned short stat_fe; 268*b3dbf4a5SMacpaul Lin unsigned short stat_ge; 269*b3dbf4a5SMacpaul Lin unsigned int maccr; 270*b3dbf4a5SMacpaul Lin 271*b3dbf4a5SMacpaul Lin #ifdef CONFIG_FTGMAC100_EGIGA 272*b3dbf4a5SMacpaul Lin /* 1000 Base-T Status Register */ 273*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_STAT1000, &stat_ge); 274*b3dbf4a5SMacpaul Lin #endif 275*b3dbf4a5SMacpaul Lin 276*b3dbf4a5SMacpaul Lin ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &stat_fe); 277*b3dbf4a5SMacpaul Lin 278*b3dbf4a5SMacpaul Lin if (!(stat_fe & BMSR_LSTATUS)) /* link status up? */ 279*b3dbf4a5SMacpaul Lin return 0; 280*b3dbf4a5SMacpaul Lin 281*b3dbf4a5SMacpaul Lin /* read MAC control register and clear related bits */ 282*b3dbf4a5SMacpaul Lin maccr = readl(&ftgmac100->maccr) & 283*b3dbf4a5SMacpaul Lin ~(FTGMAC100_MACCR_GIGA_MODE | 284*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_FAST_MODE | 285*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_FULLDUP); 286*b3dbf4a5SMacpaul Lin 287*b3dbf4a5SMacpaul Lin #ifdef CONFIG_FTGMAC100_EGIGA 288*b3dbf4a5SMacpaul Lin if (stat_ge & LPA_1000FULL) { 289*b3dbf4a5SMacpaul Lin /* set gmac for 1000BaseTX and Full Duplex */ 290*b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_GIGA_MODE | FTGMAC100_MACCR_FULLDUP; 291*b3dbf4a5SMacpaul Lin } 292*b3dbf4a5SMacpaul Lin 293*b3dbf4a5SMacpaul Lin if (stat_ge & LPA_1000HALF) { 294*b3dbf4a5SMacpaul Lin /* set gmac for 1000BaseTX and Half Duplex */ 295*b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_GIGA_MODE; 296*b3dbf4a5SMacpaul Lin } 297*b3dbf4a5SMacpaul Lin #endif 298*b3dbf4a5SMacpaul Lin 299*b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_100FULL) { 300*b3dbf4a5SMacpaul Lin /* set MII for 100BaseTX and Full Duplex */ 301*b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_FULLDUP; 302*b3dbf4a5SMacpaul Lin } 303*b3dbf4a5SMacpaul Lin 304*b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_10FULL) { 305*b3dbf4a5SMacpaul Lin /* set MII for 10BaseT and Full Duplex */ 306*b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_FULLDUP; 307*b3dbf4a5SMacpaul Lin } 308*b3dbf4a5SMacpaul Lin 309*b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_100HALF) { 310*b3dbf4a5SMacpaul Lin /* set MII for 100BaseTX and Half Duplex */ 311*b3dbf4a5SMacpaul Lin maccr |= FTGMAC100_MACCR_FAST_MODE; 312*b3dbf4a5SMacpaul Lin } 313*b3dbf4a5SMacpaul Lin 314*b3dbf4a5SMacpaul Lin if (stat_fe & BMSR_10HALF) { 315*b3dbf4a5SMacpaul Lin /* set MII for 10BaseT and Half Duplex */ 316*b3dbf4a5SMacpaul Lin /* we have already clear these bits, do nothing */ 317*b3dbf4a5SMacpaul Lin ; 318*b3dbf4a5SMacpaul Lin } 319*b3dbf4a5SMacpaul Lin 320*b3dbf4a5SMacpaul Lin /* update MII config into maccr */ 321*b3dbf4a5SMacpaul Lin writel(maccr, &ftgmac100->maccr); 322*b3dbf4a5SMacpaul Lin 323*b3dbf4a5SMacpaul Lin return 1; 324*b3dbf4a5SMacpaul Lin } 325*b3dbf4a5SMacpaul Lin 326*b3dbf4a5SMacpaul Lin /* 327*b3dbf4a5SMacpaul Lin * Reset MAC 328*b3dbf4a5SMacpaul Lin */ 329*b3dbf4a5SMacpaul Lin static void ftgmac100_reset(struct eth_device *dev) 330*b3dbf4a5SMacpaul Lin { 331*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 332*b3dbf4a5SMacpaul Lin 333*b3dbf4a5SMacpaul Lin debug("%s()\n", __func__); 334*b3dbf4a5SMacpaul Lin 335*b3dbf4a5SMacpaul Lin writel(FTGMAC100_MACCR_SW_RST, &ftgmac100->maccr); 336*b3dbf4a5SMacpaul Lin 337*b3dbf4a5SMacpaul Lin while (readl(&ftgmac100->maccr) & FTGMAC100_MACCR_SW_RST) 338*b3dbf4a5SMacpaul Lin ; 339*b3dbf4a5SMacpaul Lin } 340*b3dbf4a5SMacpaul Lin 341*b3dbf4a5SMacpaul Lin /* 342*b3dbf4a5SMacpaul Lin * Set MAC address 343*b3dbf4a5SMacpaul Lin */ 344*b3dbf4a5SMacpaul Lin static void ftgmac100_set_mac(struct eth_device *dev, 345*b3dbf4a5SMacpaul Lin const unsigned char *mac) 346*b3dbf4a5SMacpaul Lin { 347*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 348*b3dbf4a5SMacpaul Lin unsigned int maddr = mac[0] << 8 | mac[1]; 349*b3dbf4a5SMacpaul Lin unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; 350*b3dbf4a5SMacpaul Lin 351*b3dbf4a5SMacpaul Lin debug("%s(%x %x)\n", __func__, maddr, laddr); 352*b3dbf4a5SMacpaul Lin 353*b3dbf4a5SMacpaul Lin writel(maddr, &ftgmac100->mac_madr); 354*b3dbf4a5SMacpaul Lin writel(laddr, &ftgmac100->mac_ladr); 355*b3dbf4a5SMacpaul Lin } 356*b3dbf4a5SMacpaul Lin 357*b3dbf4a5SMacpaul Lin static void ftgmac100_set_mac_from_env(struct eth_device *dev) 358*b3dbf4a5SMacpaul Lin { 359*b3dbf4a5SMacpaul Lin eth_getenv_enetaddr("ethaddr", dev->enetaddr); 360*b3dbf4a5SMacpaul Lin 361*b3dbf4a5SMacpaul Lin ftgmac100_set_mac(dev, dev->enetaddr); 362*b3dbf4a5SMacpaul Lin } 363*b3dbf4a5SMacpaul Lin 364*b3dbf4a5SMacpaul Lin /* 365*b3dbf4a5SMacpaul Lin * disable transmitter, receiver 366*b3dbf4a5SMacpaul Lin */ 367*b3dbf4a5SMacpaul Lin static void ftgmac100_halt(struct eth_device *dev) 368*b3dbf4a5SMacpaul Lin { 369*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 370*b3dbf4a5SMacpaul Lin 371*b3dbf4a5SMacpaul Lin debug("%s()\n", __func__); 372*b3dbf4a5SMacpaul Lin 373*b3dbf4a5SMacpaul Lin writel(0, &ftgmac100->maccr); 374*b3dbf4a5SMacpaul Lin } 375*b3dbf4a5SMacpaul Lin 376*b3dbf4a5SMacpaul Lin static int ftgmac100_init(struct eth_device *dev, bd_t *bd) 377*b3dbf4a5SMacpaul Lin { 378*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 379*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv; 380*b3dbf4a5SMacpaul Lin struct ftgmac100_txdes *txdes = priv->txdes; 381*b3dbf4a5SMacpaul Lin struct ftgmac100_rxdes *rxdes = priv->rxdes; 382*b3dbf4a5SMacpaul Lin unsigned int maccr; 383*b3dbf4a5SMacpaul Lin int i; 384*b3dbf4a5SMacpaul Lin 385*b3dbf4a5SMacpaul Lin debug("%s()\n", __func__); 386*b3dbf4a5SMacpaul Lin 387*b3dbf4a5SMacpaul Lin ftgmac100_reset(dev); 388*b3dbf4a5SMacpaul Lin 389*b3dbf4a5SMacpaul Lin /* set the ethernet address */ 390*b3dbf4a5SMacpaul Lin ftgmac100_set_mac_from_env(dev); 391*b3dbf4a5SMacpaul Lin 392*b3dbf4a5SMacpaul Lin /* disable all interrupts */ 393*b3dbf4a5SMacpaul Lin writel(0, &ftgmac100->ier); 394*b3dbf4a5SMacpaul Lin 395*b3dbf4a5SMacpaul Lin /* initialize descriptors */ 396*b3dbf4a5SMacpaul Lin priv->tx_index = 0; 397*b3dbf4a5SMacpaul Lin priv->rx_index = 0; 398*b3dbf4a5SMacpaul Lin 399*b3dbf4a5SMacpaul Lin txdes[PKTBUFSTX - 1].txdes0 = FTGMAC100_TXDES0_EDOTR; 400*b3dbf4a5SMacpaul Lin rxdes[PKTBUFSRX - 1].rxdes0 = FTGMAC100_RXDES0_EDORR; 401*b3dbf4a5SMacpaul Lin 402*b3dbf4a5SMacpaul Lin for (i = 0; i < PKTBUFSTX; i++) { 403*b3dbf4a5SMacpaul Lin /* TXBUF_BADR */ 404*b3dbf4a5SMacpaul Lin txdes[i].txdes3 = 0; 405*b3dbf4a5SMacpaul Lin txdes[i].txdes1 = 0; 406*b3dbf4a5SMacpaul Lin } 407*b3dbf4a5SMacpaul Lin 408*b3dbf4a5SMacpaul Lin for (i = 0; i < PKTBUFSRX; i++) { 409*b3dbf4a5SMacpaul Lin /* RXBUF_BADR */ 410*b3dbf4a5SMacpaul Lin rxdes[i].rxdes3 = (unsigned int)NetRxPackets[i]; 411*b3dbf4a5SMacpaul Lin rxdes[i].rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY; 412*b3dbf4a5SMacpaul Lin } 413*b3dbf4a5SMacpaul Lin 414*b3dbf4a5SMacpaul Lin /* transmit ring */ 415*b3dbf4a5SMacpaul Lin writel((unsigned int)txdes, &ftgmac100->txr_badr); 416*b3dbf4a5SMacpaul Lin 417*b3dbf4a5SMacpaul Lin /* receive ring */ 418*b3dbf4a5SMacpaul Lin writel((unsigned int)rxdes, &ftgmac100->rxr_badr); 419*b3dbf4a5SMacpaul Lin 420*b3dbf4a5SMacpaul Lin /* poll receive descriptor automatically */ 421*b3dbf4a5SMacpaul Lin writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc); 422*b3dbf4a5SMacpaul Lin 423*b3dbf4a5SMacpaul Lin /* config receive buffer size register */ 424*b3dbf4a5SMacpaul Lin writel(FTGMAC100_RBSR_SIZE(RBSR_DEFAULT_VALUE), &ftgmac100->rbsr); 425*b3dbf4a5SMacpaul Lin 426*b3dbf4a5SMacpaul Lin /* enable transmitter, receiver */ 427*b3dbf4a5SMacpaul Lin maccr = FTGMAC100_MACCR_TXMAC_EN | 428*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RXMAC_EN | 429*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_TXDMA_EN | 430*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RXDMA_EN | 431*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_CRC_APD | 432*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_FULLDUP | 433*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RX_RUNT | 434*b3dbf4a5SMacpaul Lin FTGMAC100_MACCR_RX_BROADPKT; 435*b3dbf4a5SMacpaul Lin 436*b3dbf4a5SMacpaul Lin writel(maccr, &ftgmac100->maccr); 437*b3dbf4a5SMacpaul Lin 438*b3dbf4a5SMacpaul Lin if (!ftgmac100_phy_init(dev)) { 439*b3dbf4a5SMacpaul Lin if (!ftgmac100_update_link_speed(dev)) 440*b3dbf4a5SMacpaul Lin return -1; 441*b3dbf4a5SMacpaul Lin } 442*b3dbf4a5SMacpaul Lin 443*b3dbf4a5SMacpaul Lin return 0; 444*b3dbf4a5SMacpaul Lin } 445*b3dbf4a5SMacpaul Lin 446*b3dbf4a5SMacpaul Lin /* 447*b3dbf4a5SMacpaul Lin * Get a data block via Ethernet 448*b3dbf4a5SMacpaul Lin */ 449*b3dbf4a5SMacpaul Lin static int ftgmac100_recv(struct eth_device *dev) 450*b3dbf4a5SMacpaul Lin { 451*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv; 452*b3dbf4a5SMacpaul Lin struct ftgmac100_rxdes *curr_des; 453*b3dbf4a5SMacpaul Lin unsigned short rxlen; 454*b3dbf4a5SMacpaul Lin 455*b3dbf4a5SMacpaul Lin curr_des = &priv->rxdes[priv->rx_index]; 456*b3dbf4a5SMacpaul Lin 457*b3dbf4a5SMacpaul Lin if (!(curr_des->rxdes0 & FTGMAC100_RXDES0_RXPKT_RDY)) 458*b3dbf4a5SMacpaul Lin return -1; 459*b3dbf4a5SMacpaul Lin 460*b3dbf4a5SMacpaul Lin if (curr_des->rxdes0 & (FTGMAC100_RXDES0_RX_ERR | 461*b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_CRC_ERR | 462*b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_FTL | 463*b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_RUNT | 464*b3dbf4a5SMacpaul Lin FTGMAC100_RXDES0_RX_ODD_NB)) { 465*b3dbf4a5SMacpaul Lin return -1; 466*b3dbf4a5SMacpaul Lin } 467*b3dbf4a5SMacpaul Lin 468*b3dbf4a5SMacpaul Lin rxlen = FTGMAC100_RXDES0_VDBC(curr_des->rxdes0); 469*b3dbf4a5SMacpaul Lin 470*b3dbf4a5SMacpaul Lin debug("%s(): RX buffer %d, %x received\n", 471*b3dbf4a5SMacpaul Lin __func__, priv->rx_index, rxlen); 472*b3dbf4a5SMacpaul Lin 473*b3dbf4a5SMacpaul Lin /* pass the packet up to the protocol layers. */ 474*b3dbf4a5SMacpaul Lin NetReceive((void *)curr_des->rxdes3, rxlen); 475*b3dbf4a5SMacpaul Lin 476*b3dbf4a5SMacpaul Lin /* release buffer to DMA */ 477*b3dbf4a5SMacpaul Lin curr_des->rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY; 478*b3dbf4a5SMacpaul Lin 479*b3dbf4a5SMacpaul Lin priv->rx_index = (priv->rx_index + 1) % PKTBUFSRX; 480*b3dbf4a5SMacpaul Lin 481*b3dbf4a5SMacpaul Lin return 0; 482*b3dbf4a5SMacpaul Lin } 483*b3dbf4a5SMacpaul Lin 484*b3dbf4a5SMacpaul Lin /* 485*b3dbf4a5SMacpaul Lin * Send a data block via Ethernet 486*b3dbf4a5SMacpaul Lin */ 487*b3dbf4a5SMacpaul Lin static int 488*b3dbf4a5SMacpaul Lin ftgmac100_send(struct eth_device *dev, void *packet, int length) 489*b3dbf4a5SMacpaul Lin { 490*b3dbf4a5SMacpaul Lin struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase; 491*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv = dev->priv; 492*b3dbf4a5SMacpaul Lin struct ftgmac100_txdes *curr_des = &priv->txdes[priv->tx_index]; 493*b3dbf4a5SMacpaul Lin int start; 494*b3dbf4a5SMacpaul Lin 495*b3dbf4a5SMacpaul Lin if (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) { 496*b3dbf4a5SMacpaul Lin debug("%s(): no TX descriptor available\n", __func__); 497*b3dbf4a5SMacpaul Lin return -1; 498*b3dbf4a5SMacpaul Lin } 499*b3dbf4a5SMacpaul Lin 500*b3dbf4a5SMacpaul Lin debug("%s(%x, %x)\n", __func__, (int)packet, length); 501*b3dbf4a5SMacpaul Lin 502*b3dbf4a5SMacpaul Lin length = (length < ETH_ZLEN) ? ETH_ZLEN : length; 503*b3dbf4a5SMacpaul Lin 504*b3dbf4a5SMacpaul Lin /* initiate a transmit sequence */ 505*b3dbf4a5SMacpaul Lin curr_des->txdes3 = (unsigned int)packet; /* TXBUF_BADR */ 506*b3dbf4a5SMacpaul Lin 507*b3dbf4a5SMacpaul Lin /* only one descriptor on TXBUF */ 508*b3dbf4a5SMacpaul Lin curr_des->txdes0 &= FTGMAC100_TXDES0_EDOTR; 509*b3dbf4a5SMacpaul Lin curr_des->txdes0 |= FTGMAC100_TXDES0_FTS | 510*b3dbf4a5SMacpaul Lin FTGMAC100_TXDES0_LTS | 511*b3dbf4a5SMacpaul Lin FTGMAC100_TXDES0_TXBUF_SIZE(length) | 512*b3dbf4a5SMacpaul Lin FTGMAC100_TXDES0_TXDMA_OWN ; 513*b3dbf4a5SMacpaul Lin 514*b3dbf4a5SMacpaul Lin /* start transmit */ 515*b3dbf4a5SMacpaul Lin writel(1, &ftgmac100->txpd); 516*b3dbf4a5SMacpaul Lin 517*b3dbf4a5SMacpaul Lin /* wait for transfer to succeed */ 518*b3dbf4a5SMacpaul Lin start = get_timer(0); 519*b3dbf4a5SMacpaul Lin while (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) { 520*b3dbf4a5SMacpaul Lin if (get_timer(0) >= 5) { 521*b3dbf4a5SMacpaul Lin debug("%s(): timed out\n", __func__); 522*b3dbf4a5SMacpaul Lin return -1; 523*b3dbf4a5SMacpaul Lin } 524*b3dbf4a5SMacpaul Lin } 525*b3dbf4a5SMacpaul Lin 526*b3dbf4a5SMacpaul Lin debug("%s(): packet sent\n", __func__); 527*b3dbf4a5SMacpaul Lin 528*b3dbf4a5SMacpaul Lin priv->tx_index = (priv->tx_index + 1) % PKTBUFSTX; 529*b3dbf4a5SMacpaul Lin 530*b3dbf4a5SMacpaul Lin return 0; 531*b3dbf4a5SMacpaul Lin } 532*b3dbf4a5SMacpaul Lin 533*b3dbf4a5SMacpaul Lin int ftgmac100_initialize(bd_t *bd) 534*b3dbf4a5SMacpaul Lin { 535*b3dbf4a5SMacpaul Lin struct eth_device *dev; 536*b3dbf4a5SMacpaul Lin struct ftgmac100_data *priv; 537*b3dbf4a5SMacpaul Lin 538*b3dbf4a5SMacpaul Lin dev = malloc(sizeof *dev); 539*b3dbf4a5SMacpaul Lin if (!dev) { 540*b3dbf4a5SMacpaul Lin printf("%s(): failed to allocate dev\n", __func__); 541*b3dbf4a5SMacpaul Lin goto out; 542*b3dbf4a5SMacpaul Lin } 543*b3dbf4a5SMacpaul Lin 544*b3dbf4a5SMacpaul Lin /* Transmit and receive descriptors should align to 16 bytes */ 545*b3dbf4a5SMacpaul Lin priv = memalign(16, sizeof(struct ftgmac100_data)); 546*b3dbf4a5SMacpaul Lin if (!priv) { 547*b3dbf4a5SMacpaul Lin printf("%s(): failed to allocate priv\n", __func__); 548*b3dbf4a5SMacpaul Lin goto free_dev; 549*b3dbf4a5SMacpaul Lin } 550*b3dbf4a5SMacpaul Lin 551*b3dbf4a5SMacpaul Lin memset(dev, 0, sizeof(*dev)); 552*b3dbf4a5SMacpaul Lin memset(priv, 0, sizeof(*priv)); 553*b3dbf4a5SMacpaul Lin 554*b3dbf4a5SMacpaul Lin sprintf(dev->name, "FTGMAC100"); 555*b3dbf4a5SMacpaul Lin dev->iobase = CONFIG_FTGMAC100_BASE; 556*b3dbf4a5SMacpaul Lin dev->init = ftgmac100_init; 557*b3dbf4a5SMacpaul Lin dev->halt = ftgmac100_halt; 558*b3dbf4a5SMacpaul Lin dev->send = ftgmac100_send; 559*b3dbf4a5SMacpaul Lin dev->recv = ftgmac100_recv; 560*b3dbf4a5SMacpaul Lin dev->priv = priv; 561*b3dbf4a5SMacpaul Lin 562*b3dbf4a5SMacpaul Lin eth_register(dev); 563*b3dbf4a5SMacpaul Lin 564*b3dbf4a5SMacpaul Lin return 1; 565*b3dbf4a5SMacpaul Lin 566*b3dbf4a5SMacpaul Lin free_dev: 567*b3dbf4a5SMacpaul Lin free(dev); 568*b3dbf4a5SMacpaul Lin out: 569*b3dbf4a5SMacpaul Lin return 0; 570*b3dbf4a5SMacpaul Lin } 571