1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 2de1b686bSSascha Hauer /* 3de1b686bSSascha Hauer * SMSC LAN9[12]1[567] Network driver 4de1b686bSSascha Hauer * 5cce9cfdaSStelian Pop * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> 6de1b686bSSascha Hauer */ 7de1b686bSSascha Hauer 8de1b686bSSascha Hauer #include <common.h> 9de1b686bSSascha Hauer #include <command.h> 10736fead8SBen Warren #include <malloc.h> 11de1b686bSSascha Hauer #include <net.h> 12de1b686bSSascha Hauer #include <miiphy.h> 13de1b686bSSascha Hauer 1475ba6d69SMike Frysinger #include "smc911x.h" 15de1b686bSSascha Hauer 16736fead8SBen Warren u32 pkt_data_pull(struct eth_device *dev, u32 addr) \ 17890a02e8SStefan Roese __attribute__ ((weak, alias ("smc911x_reg_read"))); 18736fead8SBen Warren void pkt_data_push(struct eth_device *dev, u32 addr, u32 val) \ 19890a02e8SStefan Roese __attribute__ ((weak, alias ("smc911x_reg_write"))); 2033314470SNobuhiro Iwamatsu 2145b6b65cSMike Rapoport static void smc911x_handle_mac_address(struct eth_device *dev) 22de1b686bSSascha Hauer { 23de1b686bSSascha Hauer unsigned long addrh, addrl; 24736fead8SBen Warren uchar *m = dev->enetaddr; 25de1b686bSSascha Hauer 262c0234faSDaniel Mack addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24); 272c0234faSDaniel Mack addrh = m[4] | (m[5] << 8); 28736fead8SBen Warren smc911x_set_mac_csr(dev, ADDRL, addrl); 29736fead8SBen Warren smc911x_set_mac_csr(dev, ADDRH, addrh); 30de1b686bSSascha Hauer 3103f3d8d3SMike Frysinger printf(DRIVERNAME ": MAC %pM\n", m); 32736fead8SBen Warren } 33736fead8SBen Warren 346af1d41aSHelmut Raiger static int smc911x_eth_phy_read(struct eth_device *dev, 35736fead8SBen Warren u8 phy, u8 reg, u16 *val) 36736fead8SBen Warren { 37736fead8SBen Warren while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) 38736fead8SBen Warren ; 39736fead8SBen Warren 40736fead8SBen Warren smc911x_set_mac_csr(dev, MII_ACC, phy << 11 | reg << 6 | 41736fead8SBen Warren MII_ACC_MII_BUSY); 42736fead8SBen Warren 43736fead8SBen Warren while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) 44736fead8SBen Warren ; 45736fead8SBen Warren 46736fead8SBen Warren *val = smc911x_get_mac_csr(dev, MII_DATA); 47de1b686bSSascha Hauer 48de1b686bSSascha Hauer return 0; 49de1b686bSSascha Hauer } 50de1b686bSSascha Hauer 516af1d41aSHelmut Raiger static int smc911x_eth_phy_write(struct eth_device *dev, 52736fead8SBen Warren u8 phy, u8 reg, u16 val) 53de1b686bSSascha Hauer { 54736fead8SBen Warren while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) 553e0f331cSGuennadi Liakhovetski ; 56de1b686bSSascha Hauer 57736fead8SBen Warren smc911x_set_mac_csr(dev, MII_DATA, val); 58736fead8SBen Warren smc911x_set_mac_csr(dev, MII_ACC, 59de1b686bSSascha Hauer phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE); 60de1b686bSSascha Hauer 61736fead8SBen Warren while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY) 623e0f331cSGuennadi Liakhovetski ; 63de1b686bSSascha Hauer return 0; 64de1b686bSSascha Hauer } 65de1b686bSSascha Hauer 66736fead8SBen Warren static int smc911x_phy_reset(struct eth_device *dev) 67de1b686bSSascha Hauer { 68de1b686bSSascha Hauer u32 reg; 69de1b686bSSascha Hauer 70736fead8SBen Warren reg = smc911x_reg_read(dev, PMT_CTRL); 71de1b686bSSascha Hauer reg &= ~0xfffff030; 72de1b686bSSascha Hauer reg |= PMT_CTRL_PHY_RST; 73736fead8SBen Warren smc911x_reg_write(dev, PMT_CTRL, reg); 74de1b686bSSascha Hauer 75de1b686bSSascha Hauer mdelay(100); 76de1b686bSSascha Hauer 77de1b686bSSascha Hauer return 0; 78de1b686bSSascha Hauer } 79de1b686bSSascha Hauer 80736fead8SBen Warren static void smc911x_phy_configure(struct eth_device *dev) 81de1b686bSSascha Hauer { 82de1b686bSSascha Hauer int timeout; 83de1b686bSSascha Hauer u16 status; 84de1b686bSSascha Hauer 85736fead8SBen Warren smc911x_phy_reset(dev); 86de1b686bSSascha Hauer 876af1d41aSHelmut Raiger smc911x_eth_phy_write(dev, 1, MII_BMCR, BMCR_RESET); 88de1b686bSSascha Hauer mdelay(1); 896af1d41aSHelmut Raiger smc911x_eth_phy_write(dev, 1, MII_ADVERTISE, 0x01e1); 906af1d41aSHelmut Raiger smc911x_eth_phy_write(dev, 1, MII_BMCR, BMCR_ANENABLE | 918ef583a0SMike Frysinger BMCR_ANRESTART); 92de1b686bSSascha Hauer 93de1b686bSSascha Hauer timeout = 5000; 94de1b686bSSascha Hauer do { 95de1b686bSSascha Hauer mdelay(1); 96de1b686bSSascha Hauer if ((timeout--) == 0) 97de1b686bSSascha Hauer goto err_out; 98de1b686bSSascha Hauer 996af1d41aSHelmut Raiger if (smc911x_eth_phy_read(dev, 1, MII_BMSR, &status) != 0) 100de1b686bSSascha Hauer goto err_out; 1018ef583a0SMike Frysinger } while (!(status & BMSR_LSTATUS)); 102de1b686bSSascha Hauer 103de1b686bSSascha Hauer printf(DRIVERNAME ": phy initialized\n"); 104de1b686bSSascha Hauer 105de1b686bSSascha Hauer return; 106de1b686bSSascha Hauer 107de1b686bSSascha Hauer err_out: 108de1b686bSSascha Hauer printf(DRIVERNAME ": autonegotiation timed out\n"); 109de1b686bSSascha Hauer } 110de1b686bSSascha Hauer 111736fead8SBen Warren static void smc911x_enable(struct eth_device *dev) 112de1b686bSSascha Hauer { 113de1b686bSSascha Hauer /* Enable TX */ 114736fead8SBen Warren smc911x_reg_write(dev, HW_CFG, 8 << 16 | HW_CFG_SF); 115de1b686bSSascha Hauer 116736fead8SBen Warren smc911x_reg_write(dev, GPT_CFG, GPT_CFG_TIMER_EN | 10000); 117de1b686bSSascha Hauer 118736fead8SBen Warren smc911x_reg_write(dev, TX_CFG, TX_CFG_TX_ON); 119de1b686bSSascha Hauer 120de1b686bSSascha Hauer /* no padding to start of packets */ 121736fead8SBen Warren smc911x_reg_write(dev, RX_CFG, 0); 122de1b686bSSascha Hauer 123736fead8SBen Warren smc911x_set_mac_csr(dev, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | 124736fead8SBen Warren MAC_CR_HBDIS); 125de1b686bSSascha Hauer 126de1b686bSSascha Hauer } 127de1b686bSSascha Hauer 128736fead8SBen Warren static int smc911x_init(struct eth_device *dev, bd_t * bd) 129de1b686bSSascha Hauer { 1302a6cc97bSOlof Johansson struct chip_id *id = dev->priv; 131de1b686bSSascha Hauer 1322a6cc97bSOlof Johansson printf(DRIVERNAME ": detected %s controller\n", id->name); 133de1b686bSSascha Hauer 134736fead8SBen Warren smc911x_reset(dev); 135de1b686bSSascha Hauer 136de1b686bSSascha Hauer /* Configure the PHY, initialize the link state */ 137736fead8SBen Warren smc911x_phy_configure(dev); 138de1b686bSSascha Hauer 13945b6b65cSMike Rapoport smc911x_handle_mac_address(dev); 140de1b686bSSascha Hauer 141de1b686bSSascha Hauer /* Turn on Tx + Rx */ 142736fead8SBen Warren smc911x_enable(dev); 143de1b686bSSascha Hauer 144de1b686bSSascha Hauer return 0; 145de1b686bSSascha Hauer } 146de1b686bSSascha Hauer 147fd89b61bSAnatolij Gustschin static int smc911x_send(struct eth_device *dev, void *packet, int length) 148de1b686bSSascha Hauer { 149de1b686bSSascha Hauer u32 *data = (u32*)packet; 150de1b686bSSascha Hauer u32 tmplen; 151de1b686bSSascha Hauer u32 status; 152de1b686bSSascha Hauer 153736fead8SBen Warren smc911x_reg_write(dev, TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | 154736fead8SBen Warren TX_CMD_A_INT_LAST_SEG | length); 155736fead8SBen Warren smc911x_reg_write(dev, TX_DATA_FIFO, length); 156de1b686bSSascha Hauer 157de1b686bSSascha Hauer tmplen = (length + 3) / 4; 158de1b686bSSascha Hauer 159de1b686bSSascha Hauer while (tmplen--) 160736fead8SBen Warren pkt_data_push(dev, TX_DATA_FIFO, *data++); 161de1b686bSSascha Hauer 162de1b686bSSascha Hauer /* wait for transmission */ 163736fead8SBen Warren while (!((smc911x_reg_read(dev, TX_FIFO_INF) & 164736fead8SBen Warren TX_FIFO_INF_TSUSED) >> 16)); 165de1b686bSSascha Hauer 166de1b686bSSascha Hauer /* get status. Ignore 'no carrier' error, it has no meaning for 167de1b686bSSascha Hauer * full duplex operation 168de1b686bSSascha Hauer */ 169736fead8SBen Warren status = smc911x_reg_read(dev, TX_STATUS_FIFO) & 170736fead8SBen Warren (TX_STS_LOC | TX_STS_LATE_COLL | TX_STS_MANY_COLL | 171736fead8SBen Warren TX_STS_MANY_DEFER | TX_STS_UNDERRUN); 172de1b686bSSascha Hauer 173de1b686bSSascha Hauer if (!status) 174de1b686bSSascha Hauer return 0; 175de1b686bSSascha Hauer 176de1b686bSSascha Hauer printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n", 177de1b686bSSascha Hauer status & TX_STS_LOC ? "TX_STS_LOC " : "", 178de1b686bSSascha Hauer status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "", 179de1b686bSSascha Hauer status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "", 180de1b686bSSascha Hauer status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "", 181de1b686bSSascha Hauer status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : ""); 182de1b686bSSascha Hauer 183de1b686bSSascha Hauer return -1; 184de1b686bSSascha Hauer } 185de1b686bSSascha Hauer 186736fead8SBen Warren static void smc911x_halt(struct eth_device *dev) 187de1b686bSSascha Hauer { 188736fead8SBen Warren smc911x_reset(dev); 18999dd6ab4SMarek Vasut smc911x_handle_mac_address(dev); 190de1b686bSSascha Hauer } 191de1b686bSSascha Hauer 192736fead8SBen Warren static int smc911x_rx(struct eth_device *dev) 193de1b686bSSascha Hauer { 1941fd92db8SJoe Hershberger u32 *data = (u32 *)net_rx_packets[0]; 195de1b686bSSascha Hauer u32 pktlen, tmplen; 196de1b686bSSascha Hauer u32 status; 197de1b686bSSascha Hauer 198736fead8SBen Warren if ((smc911x_reg_read(dev, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) { 199736fead8SBen Warren status = smc911x_reg_read(dev, RX_STATUS_FIFO); 200de1b686bSSascha Hauer pktlen = (status & RX_STS_PKT_LEN) >> 16; 201de1b686bSSascha Hauer 202736fead8SBen Warren smc911x_reg_write(dev, RX_CFG, 0); 203de1b686bSSascha Hauer 204bd75db3fSValentin Yakovenkov tmplen = (pktlen + 3) / 4; 205de1b686bSSascha Hauer while (tmplen--) 206736fead8SBen Warren *data++ = pkt_data_pull(dev, RX_DATA_FIFO); 207de1b686bSSascha Hauer 208de1b686bSSascha Hauer if (status & RX_STS_ES) 209de1b686bSSascha Hauer printf(DRIVERNAME 210de1b686bSSascha Hauer ": dropped bad packet. Status: 0x%08x\n", 211de1b686bSSascha Hauer status); 212de1b686bSSascha Hauer else 2131fd92db8SJoe Hershberger net_process_received_packet(net_rx_packets[0], pktlen); 214de1b686bSSascha Hauer } 215de1b686bSSascha Hauer 216de1b686bSSascha Hauer return 0; 217de1b686bSSascha Hauer } 218736fead8SBen Warren 2196af1d41aSHelmut Raiger #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) 2206af1d41aSHelmut Raiger /* wrapper for smc911x_eth_phy_read */ 2215a49f174SJoe Hershberger static int smc911x_miiphy_read(struct mii_dev *bus, int phy, int devad, 2225a49f174SJoe Hershberger int reg) 2236af1d41aSHelmut Raiger { 2245a49f174SJoe Hershberger u16 val = 0; 2255a49f174SJoe Hershberger struct eth_device *dev = eth_get_dev_by_name(bus->name); 2265a49f174SJoe Hershberger if (dev) { 2275a49f174SJoe Hershberger int retval = smc911x_eth_phy_read(dev, phy, reg, &val); 2285a49f174SJoe Hershberger if (retval < 0) 2295a49f174SJoe Hershberger return retval; 2305a49f174SJoe Hershberger return val; 2315a49f174SJoe Hershberger } 232875e0bc6SJoe Hershberger return -ENODEV; 2336af1d41aSHelmut Raiger } 2346af1d41aSHelmut Raiger /* wrapper for smc911x_eth_phy_write */ 2355a49f174SJoe Hershberger static int smc911x_miiphy_write(struct mii_dev *bus, int phy, int devad, 2365a49f174SJoe Hershberger int reg, u16 val) 2376af1d41aSHelmut Raiger { 2385a49f174SJoe Hershberger struct eth_device *dev = eth_get_dev_by_name(bus->name); 2396af1d41aSHelmut Raiger if (dev) 2406af1d41aSHelmut Raiger return smc911x_eth_phy_write(dev, phy, reg, val); 241875e0bc6SJoe Hershberger return -ENODEV; 2426af1d41aSHelmut Raiger } 2436af1d41aSHelmut Raiger #endif 2446af1d41aSHelmut Raiger 245736fead8SBen Warren int smc911x_initialize(u8 dev_num, int base_addr) 246736fead8SBen Warren { 247736fead8SBen Warren unsigned long addrl, addrh; 248736fead8SBen Warren struct eth_device *dev; 249736fead8SBen Warren 250736fead8SBen Warren dev = malloc(sizeof(*dev)); 251736fead8SBen Warren if (!dev) { 252fbd47b67SMike Rapoport return -1; 253736fead8SBen Warren } 254736fead8SBen Warren memset(dev, 0, sizeof(*dev)); 255736fead8SBen Warren 256736fead8SBen Warren dev->iobase = base_addr; 257736fead8SBen Warren 2584bc3d2afSSteve Sakoman /* Try to detect chip. Will fail if not present. */ 2594bc3d2afSSteve Sakoman if (smc911x_detect_chip(dev)) { 2604bc3d2afSSteve Sakoman free(dev); 2614bc3d2afSSteve Sakoman return 0; 2624bc3d2afSSteve Sakoman } 2634bc3d2afSSteve Sakoman 264736fead8SBen Warren addrh = smc911x_get_mac_csr(dev, ADDRH); 265736fead8SBen Warren addrl = smc911x_get_mac_csr(dev, ADDRL); 26676771e59SSeunghyeon Rhee if (!(addrl == 0xffffffff && addrh == 0x0000ffff)) { 26776771e59SSeunghyeon Rhee /* address is obtained from optional eeprom */ 268736fead8SBen Warren dev->enetaddr[0] = addrl; 269736fead8SBen Warren dev->enetaddr[1] = addrl >> 8; 270736fead8SBen Warren dev->enetaddr[2] = addrl >> 16; 271736fead8SBen Warren dev->enetaddr[3] = addrl >> 24; 272736fead8SBen Warren dev->enetaddr[4] = addrh; 273736fead8SBen Warren dev->enetaddr[5] = addrh >> 8; 27476771e59SSeunghyeon Rhee } 275736fead8SBen Warren 276736fead8SBen Warren dev->init = smc911x_init; 277736fead8SBen Warren dev->halt = smc911x_halt; 278736fead8SBen Warren dev->send = smc911x_send; 279736fead8SBen Warren dev->recv = smc911x_rx; 280736fead8SBen Warren sprintf(dev->name, "%s-%hu", DRIVERNAME, dev_num); 281736fead8SBen Warren 282736fead8SBen Warren eth_register(dev); 2836af1d41aSHelmut Raiger 2846af1d41aSHelmut Raiger #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) 2855a49f174SJoe Hershberger int retval; 2865a49f174SJoe Hershberger struct mii_dev *mdiodev = mdio_alloc(); 2875a49f174SJoe Hershberger if (!mdiodev) 2885a49f174SJoe Hershberger return -ENOMEM; 2895a49f174SJoe Hershberger strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN); 2905a49f174SJoe Hershberger mdiodev->read = smc911x_miiphy_read; 2915a49f174SJoe Hershberger mdiodev->write = smc911x_miiphy_write; 2925a49f174SJoe Hershberger 2935a49f174SJoe Hershberger retval = mdio_register(mdiodev); 2945a49f174SJoe Hershberger if (retval < 0) 2955a49f174SJoe Hershberger return retval; 2966af1d41aSHelmut Raiger #endif 2976af1d41aSHelmut Raiger 298fbd47b67SMike Rapoport return 1; 299736fead8SBen Warren } 300