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