1de1b686bSSascha Hauer /* 2de1b686bSSascha Hauer * SMSC LAN9[12]1[567] Network driver 3de1b686bSSascha Hauer * 4cce9cfdaSStelian Pop * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> 5de1b686bSSascha Hauer * 6de1b686bSSascha Hauer * See file CREDITS for list of people who contributed to this 7de1b686bSSascha Hauer * project. 8de1b686bSSascha Hauer * 9de1b686bSSascha Hauer * This program is free software; you can redistribute it and/or 10de1b686bSSascha Hauer * modify it under the terms of the GNU General Public License as 11de1b686bSSascha Hauer * published by the Free Software Foundation; either version 2 of 12de1b686bSSascha Hauer * the License, or (at your option) any later version. 13de1b686bSSascha Hauer * 14de1b686bSSascha Hauer * This program is distributed in the hope that it will be useful, 15de1b686bSSascha Hauer * but WITHOUT ANY WARRANTY; without even the implied warranty of 16de1b686bSSascha Hauer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17de1b686bSSascha Hauer * GNU General Public License for more details. 18de1b686bSSascha Hauer * 19de1b686bSSascha Hauer * You should have received a copy of the GNU General Public License 20de1b686bSSascha Hauer * along with this program; if not, write to the Free Software 21de1b686bSSascha Hauer * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 22de1b686bSSascha Hauer * MA 02111-1307 USA 23de1b686bSSascha Hauer */ 24de1b686bSSascha Hauer 25de1b686bSSascha Hauer #include <common.h> 26de1b686bSSascha Hauer #include <command.h> 27de1b686bSSascha Hauer #include <net.h> 28de1b686bSSascha Hauer #include <miiphy.h> 29de1b686bSSascha Hauer 3075ba6d69SMike Frysinger #include "smc911x.h" 31de1b686bSSascha Hauer 3233314470SNobuhiro Iwamatsu u32 pkt_data_pull(u32 addr) \ 33890a02e8SStefan Roese __attribute__ ((weak, alias ("smc911x_reg_read"))); 3433314470SNobuhiro Iwamatsu void pkt_data_push(u32 addr, u32 val) \ 35890a02e8SStefan Roese __attribute__ ((weak, alias ("smc911x_reg_write"))); 3633314470SNobuhiro Iwamatsu 373e0f331cSGuennadi Liakhovetski #define mdelay(n) udelay((n)*1000) 38de1b686bSSascha Hauer 39de1b686bSSascha Hauer static int smx911x_handle_mac_address(bd_t *bd) 40de1b686bSSascha Hauer { 41de1b686bSSascha Hauer unsigned long addrh, addrl; 4203f3d8d3SMike Frysinger uchar m[6]; 43de1b686bSSascha Hauer 44*2c0234faSDaniel Mack if (eth_getenv_enetaddr("ethaddr", m)) { 45de1b686bSSascha Hauer /* if the environment has a valid mac address then use it */ 46*2c0234faSDaniel Mack addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24); 47*2c0234faSDaniel Mack addrh = m[4] | (m[5] << 8); 48*2c0234faSDaniel Mack smc911x_set_mac_csr(ADDRL, addrl); 49*2c0234faSDaniel Mack smc911x_set_mac_csr(ADDRH, addrh); 50*2c0234faSDaniel Mack } else { 51de1b686bSSascha Hauer /* if not, try to get one from the eeprom */ 52de1b686bSSascha Hauer addrh = smc911x_get_mac_csr(ADDRH); 53de1b686bSSascha Hauer addrl = smc911x_get_mac_csr(ADDRL); 54de1b686bSSascha Hauer 55de1b686bSSascha Hauer m[0] = (addrl ) & 0xff; 56de1b686bSSascha Hauer m[1] = (addrl >> 8 ) & 0xff; 57de1b686bSSascha Hauer m[2] = (addrl >> 16 ) & 0xff; 58de1b686bSSascha Hauer m[3] = (addrl >> 24 ) & 0xff; 59de1b686bSSascha Hauer m[4] = (addrh ) & 0xff; 60de1b686bSSascha Hauer m[5] = (addrh >> 8 ) & 0xff; 61de1b686bSSascha Hauer 62de1b686bSSascha Hauer /* we get 0xff when there is no eeprom connected */ 63de1b686bSSascha Hauer if ((m[0] & m[1] & m[2] & m[3] & m[4] & m[5]) == 0xff) { 64de1b686bSSascha Hauer printf(DRIVERNAME ": no valid mac address in environment " 65de1b686bSSascha Hauer "and no eeprom found\n"); 66de1b686bSSascha Hauer return -1; 67de1b686bSSascha Hauer } 6803f3d8d3SMike Frysinger 6903f3d8d3SMike Frysinger eth_setenv_enetaddr("ethaddr", m); 70de1b686bSSascha Hauer } 71de1b686bSSascha Hauer 7203f3d8d3SMike Frysinger printf(DRIVERNAME ": MAC %pM\n", m); 73de1b686bSSascha Hauer 74de1b686bSSascha Hauer return 0; 75de1b686bSSascha Hauer } 76de1b686bSSascha Hauer 77de1b686bSSascha Hauer static int smc911x_miiphy_read(u8 phy, u8 reg, u16 *val) 78de1b686bSSascha Hauer { 793e0f331cSGuennadi Liakhovetski while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) 803e0f331cSGuennadi Liakhovetski ; 81de1b686bSSascha Hauer 82de1b686bSSascha Hauer smc911x_set_mac_csr(MII_ACC, phy << 11 | reg << 6 | MII_ACC_MII_BUSY); 83de1b686bSSascha Hauer 843e0f331cSGuennadi Liakhovetski while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) 853e0f331cSGuennadi Liakhovetski ; 86de1b686bSSascha Hauer 87de1b686bSSascha Hauer *val = smc911x_get_mac_csr(MII_DATA); 88de1b686bSSascha Hauer 89de1b686bSSascha Hauer return 0; 90de1b686bSSascha Hauer } 91de1b686bSSascha Hauer 92de1b686bSSascha Hauer static int smc911x_miiphy_write(u8 phy, u8 reg, u16 val) 93de1b686bSSascha Hauer { 943e0f331cSGuennadi Liakhovetski while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) 953e0f331cSGuennadi Liakhovetski ; 96de1b686bSSascha Hauer 97de1b686bSSascha Hauer smc911x_set_mac_csr(MII_DATA, val); 98de1b686bSSascha Hauer smc911x_set_mac_csr(MII_ACC, 99de1b686bSSascha Hauer phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE); 100de1b686bSSascha Hauer 1013e0f331cSGuennadi Liakhovetski while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) 1023e0f331cSGuennadi Liakhovetski ; 103de1b686bSSascha Hauer return 0; 104de1b686bSSascha Hauer } 105de1b686bSSascha Hauer 106de1b686bSSascha Hauer static int smc911x_phy_reset(void) 107de1b686bSSascha Hauer { 108de1b686bSSascha Hauer u32 reg; 109de1b686bSSascha Hauer 110890a02e8SStefan Roese reg = smc911x_reg_read(PMT_CTRL); 111de1b686bSSascha Hauer reg &= ~0xfffff030; 112de1b686bSSascha Hauer reg |= PMT_CTRL_PHY_RST; 113890a02e8SStefan Roese smc911x_reg_write(PMT_CTRL, reg); 114de1b686bSSascha Hauer 115de1b686bSSascha Hauer mdelay(100); 116de1b686bSSascha Hauer 117de1b686bSSascha Hauer return 0; 118de1b686bSSascha Hauer } 119de1b686bSSascha Hauer 120de1b686bSSascha Hauer static void smc911x_phy_configure(void) 121de1b686bSSascha Hauer { 122de1b686bSSascha Hauer int timeout; 123de1b686bSSascha Hauer u16 status; 124de1b686bSSascha Hauer 125de1b686bSSascha Hauer smc911x_phy_reset(); 126de1b686bSSascha Hauer 127de1b686bSSascha Hauer smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_RESET); 128de1b686bSSascha Hauer mdelay(1); 129de1b686bSSascha Hauer smc911x_miiphy_write(1, PHY_ANAR, 0x01e1); 130de1b686bSSascha Hauer smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); 131de1b686bSSascha Hauer 132de1b686bSSascha Hauer timeout = 5000; 133de1b686bSSascha Hauer do { 134de1b686bSSascha Hauer mdelay(1); 135de1b686bSSascha Hauer if ((timeout--) == 0) 136de1b686bSSascha Hauer goto err_out; 137de1b686bSSascha Hauer 138de1b686bSSascha Hauer if (smc911x_miiphy_read(1, PHY_BMSR, &status) != 0) 139de1b686bSSascha Hauer goto err_out; 140de1b686bSSascha Hauer } while (!(status & PHY_BMSR_LS)); 141de1b686bSSascha Hauer 142de1b686bSSascha Hauer printf(DRIVERNAME ": phy initialized\n"); 143de1b686bSSascha Hauer 144de1b686bSSascha Hauer return; 145de1b686bSSascha Hauer 146de1b686bSSascha Hauer err_out: 147de1b686bSSascha Hauer printf(DRIVERNAME ": autonegotiation timed out\n"); 148de1b686bSSascha Hauer } 149de1b686bSSascha Hauer 150de1b686bSSascha Hauer static void smc911x_enable(void) 151de1b686bSSascha Hauer { 152de1b686bSSascha Hauer /* Enable TX */ 153890a02e8SStefan Roese smc911x_reg_write(HW_CFG, 8 << 16 | HW_CFG_SF); 154de1b686bSSascha Hauer 155890a02e8SStefan Roese smc911x_reg_write(GPT_CFG, GPT_CFG_TIMER_EN | 10000); 156de1b686bSSascha Hauer 157890a02e8SStefan Roese smc911x_reg_write(TX_CFG, TX_CFG_TX_ON); 158de1b686bSSascha Hauer 159de1b686bSSascha Hauer /* no padding to start of packets */ 160890a02e8SStefan Roese smc911x_reg_write(RX_CFG, 0); 161de1b686bSSascha Hauer 162de1b686bSSascha Hauer smc911x_set_mac_csr(MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS); 163de1b686bSSascha Hauer 164de1b686bSSascha Hauer } 165de1b686bSSascha Hauer 166de1b686bSSascha Hauer int eth_init(bd_t *bd) 167de1b686bSSascha Hauer { 168de1b686bSSascha Hauer printf(DRIVERNAME ": initializing\n"); 169de1b686bSSascha Hauer 17075ba6d69SMike Frysinger if (smc911x_detect_chip()) 171de1b686bSSascha Hauer goto err_out; 172de1b686bSSascha Hauer 173de1b686bSSascha Hauer smc911x_reset(); 174de1b686bSSascha Hauer 175de1b686bSSascha Hauer /* Configure the PHY, initialize the link state */ 176de1b686bSSascha Hauer smc911x_phy_configure(); 177de1b686bSSascha Hauer 178de1b686bSSascha Hauer if (smx911x_handle_mac_address(bd)) 179de1b686bSSascha Hauer goto err_out; 180de1b686bSSascha Hauer 181de1b686bSSascha Hauer /* Turn on Tx + Rx */ 182de1b686bSSascha Hauer smc911x_enable(); 183de1b686bSSascha Hauer 184de1b686bSSascha Hauer return 0; 185de1b686bSSascha Hauer 186de1b686bSSascha Hauer err_out: 187de1b686bSSascha Hauer return -1; 188de1b686bSSascha Hauer } 189de1b686bSSascha Hauer 190de1b686bSSascha Hauer int eth_send(volatile void *packet, int length) 191de1b686bSSascha Hauer { 192de1b686bSSascha Hauer u32 *data = (u32*)packet; 193de1b686bSSascha Hauer u32 tmplen; 194de1b686bSSascha Hauer u32 status; 195de1b686bSSascha Hauer 196890a02e8SStefan Roese smc911x_reg_write(TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | TX_CMD_A_INT_LAST_SEG | length); 197890a02e8SStefan Roese smc911x_reg_write(TX_DATA_FIFO, length); 198de1b686bSSascha Hauer 199de1b686bSSascha Hauer tmplen = (length + 3) / 4; 200de1b686bSSascha Hauer 201de1b686bSSascha Hauer while (tmplen--) 20233314470SNobuhiro Iwamatsu pkt_data_push(TX_DATA_FIFO, *data++); 203de1b686bSSascha Hauer 204de1b686bSSascha Hauer /* wait for transmission */ 205890a02e8SStefan Roese while (!((smc911x_reg_read(TX_FIFO_INF) & TX_FIFO_INF_TSUSED) >> 16)); 206de1b686bSSascha Hauer 207de1b686bSSascha Hauer /* get status. Ignore 'no carrier' error, it has no meaning for 208de1b686bSSascha Hauer * full duplex operation 209de1b686bSSascha Hauer */ 210890a02e8SStefan Roese status = smc911x_reg_read(TX_STATUS_FIFO) & (TX_STS_LOC | TX_STS_LATE_COLL | 211de1b686bSSascha Hauer TX_STS_MANY_COLL | TX_STS_MANY_DEFER | TX_STS_UNDERRUN); 212de1b686bSSascha Hauer 213de1b686bSSascha Hauer if (!status) 214de1b686bSSascha Hauer return 0; 215de1b686bSSascha Hauer 216de1b686bSSascha Hauer printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n", 217de1b686bSSascha Hauer status & TX_STS_LOC ? "TX_STS_LOC " : "", 218de1b686bSSascha Hauer status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "", 219de1b686bSSascha Hauer status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "", 220de1b686bSSascha Hauer status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "", 221de1b686bSSascha Hauer status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : ""); 222de1b686bSSascha Hauer 223de1b686bSSascha Hauer return -1; 224de1b686bSSascha Hauer } 225de1b686bSSascha Hauer 226de1b686bSSascha Hauer void eth_halt(void) 227de1b686bSSascha Hauer { 228de1b686bSSascha Hauer smc911x_reset(); 229de1b686bSSascha Hauer } 230de1b686bSSascha Hauer 231de1b686bSSascha Hauer int eth_rx(void) 232de1b686bSSascha Hauer { 233de1b686bSSascha Hauer u32 *data = (u32 *)NetRxPackets[0]; 234de1b686bSSascha Hauer u32 pktlen, tmplen; 235de1b686bSSascha Hauer u32 status; 236de1b686bSSascha Hauer 237890a02e8SStefan Roese if ((smc911x_reg_read(RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) { 238890a02e8SStefan Roese status = smc911x_reg_read(RX_STATUS_FIFO); 239de1b686bSSascha Hauer pktlen = (status & RX_STS_PKT_LEN) >> 16; 240de1b686bSSascha Hauer 241890a02e8SStefan Roese smc911x_reg_write(RX_CFG, 0); 242de1b686bSSascha Hauer 243de1b686bSSascha Hauer tmplen = (pktlen + 2+ 3) / 4; 244de1b686bSSascha Hauer while (tmplen--) 24533314470SNobuhiro Iwamatsu *data++ = pkt_data_pull(RX_DATA_FIFO); 246de1b686bSSascha Hauer 247de1b686bSSascha Hauer if (status & RX_STS_ES) 248de1b686bSSascha Hauer printf(DRIVERNAME 249de1b686bSSascha Hauer ": dropped bad packet. Status: 0x%08x\n", 250de1b686bSSascha Hauer status); 251de1b686bSSascha Hauer else 252de1b686bSSascha Hauer NetReceive(NetRxPackets[0], pktlen); 253de1b686bSSascha Hauer } 254de1b686bSSascha Hauer 255de1b686bSSascha Hauer return 0; 256de1b686bSSascha Hauer } 257