]>
Commit | Line | Data |
---|---|---|
de1b686b SH |
1 | /* |
2 | * SMSC LAN9[12]1[567] Network driver | |
3 | * | |
cce9cfda | 4 | * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> |
de1b686b SH |
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> | |
de1b686b SH |
26 | #include <command.h> |
27 | #include <net.h> | |
28 | #include <miiphy.h> | |
29 | ||
75ba6d69 | 30 | #include "smc911x.h" |
de1b686b | 31 | |
33314470 | 32 | u32 pkt_data_pull(u32 addr) \ |
890a02e8 | 33 | __attribute__ ((weak, alias ("smc911x_reg_read"))); |
33314470 | 34 | void pkt_data_push(u32 addr, u32 val) \ |
890a02e8 | 35 | __attribute__ ((weak, alias ("smc911x_reg_write"))); |
33314470 | 36 | |
3e0f331c | 37 | #define mdelay(n) udelay((n)*1000) |
de1b686b | 38 | |
de1b686b SH |
39 | static int smx911x_handle_mac_address(bd_t *bd) |
40 | { | |
41 | unsigned long addrh, addrl; | |
03f3d8d3 | 42 | uchar m[6]; |
de1b686b | 43 | |
2c0234fa DM |
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 { | |
de1b686b SH |
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 | } | |
03f3d8d3 MF |
68 | |
69 | eth_setenv_enetaddr("ethaddr", m); | |
de1b686b SH |
70 | } |
71 | ||
03f3d8d3 | 72 | printf(DRIVERNAME ": MAC %pM\n", m); |
de1b686b SH |
73 | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static int smc911x_miiphy_read(u8 phy, u8 reg, u16 *val) | |
78 | { | |
3e0f331c GL |
79 | while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) |
80 | ; | |
de1b686b | 81 | |
3e0f331c | 82 | smc911x_set_mac_csr(MII_ACC, phy << 11 | reg << 6 | MII_ACC_MII_BUSY); |
de1b686b | 83 | |
3e0f331c GL |
84 | while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) |
85 | ; | |
de1b686b SH |
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 | { | |
3e0f331c GL |
94 | while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) |
95 | ; | |
de1b686b SH |
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 | ||
3e0f331c GL |
101 | while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY) |
102 | ; | |
de1b686b SH |
103 | return 0; |
104 | } | |
105 | ||
106 | static int smc911x_phy_reset(void) | |
107 | { | |
108 | u32 reg; | |
109 | ||
890a02e8 | 110 | reg = smc911x_reg_read(PMT_CTRL); |
de1b686b SH |
111 | reg &= ~0xfffff030; |
112 | reg |= PMT_CTRL_PHY_RST; | |
890a02e8 | 113 | smc911x_reg_write(PMT_CTRL, reg); |
de1b686b SH |
114 | |
115 | mdelay(100); | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | static void smc911x_phy_configure(void) | |
121 | { | |
122 | int timeout; | |
123 | u16 status; | |
124 | ||
125 | smc911x_phy_reset(); | |
126 | ||
127 | smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_RESET); | |
128 | mdelay(1); | |
129 | smc911x_miiphy_write(1, PHY_ANAR, 0x01e1); | |
130 | smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); | |
131 | ||
132 | timeout = 5000; | |
133 | do { | |
134 | mdelay(1); | |
135 | if ((timeout--) == 0) | |
136 | goto err_out; | |
137 | ||
138 | if (smc911x_miiphy_read(1, PHY_BMSR, &status) != 0) | |
139 | goto err_out; | |
140 | } while (!(status & PHY_BMSR_LS)); | |
141 | ||
142 | printf(DRIVERNAME ": phy initialized\n"); | |
143 | ||
144 | return; | |
145 | ||
146 | err_out: | |
147 | printf(DRIVERNAME ": autonegotiation timed out\n"); | |
148 | } | |
149 | ||
de1b686b SH |
150 | static void smc911x_enable(void) |
151 | { | |
152 | /* Enable TX */ | |
890a02e8 | 153 | smc911x_reg_write(HW_CFG, 8 << 16 | HW_CFG_SF); |
de1b686b | 154 | |
890a02e8 | 155 | smc911x_reg_write(GPT_CFG, GPT_CFG_TIMER_EN | 10000); |
de1b686b | 156 | |
890a02e8 | 157 | smc911x_reg_write(TX_CFG, TX_CFG_TX_ON); |
de1b686b SH |
158 | |
159 | /* no padding to start of packets */ | |
890a02e8 | 160 | smc911x_reg_write(RX_CFG, 0); |
de1b686b SH |
161 | |
162 | smc911x_set_mac_csr(MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS); | |
163 | ||
164 | } | |
165 | ||
166 | int eth_init(bd_t *bd) | |
167 | { | |
de1b686b SH |
168 | printf(DRIVERNAME ": initializing\n"); |
169 | ||
75ba6d69 | 170 | if (smc911x_detect_chip()) |
de1b686b | 171 | goto err_out; |
de1b686b SH |
172 | |
173 | smc911x_reset(); | |
174 | ||
175 | /* Configure the PHY, initialize the link state */ | |
176 | smc911x_phy_configure(); | |
177 | ||
178 | if (smx911x_handle_mac_address(bd)) | |
179 | goto err_out; | |
180 | ||
181 | /* Turn on Tx + Rx */ | |
182 | smc911x_enable(); | |
183 | ||
184 | return 0; | |
185 | ||
186 | err_out: | |
187 | return -1; | |
188 | } | |
189 | ||
190 | int eth_send(volatile void *packet, int length) | |
191 | { | |
192 | u32 *data = (u32*)packet; | |
193 | u32 tmplen; | |
194 | u32 status; | |
195 | ||
890a02e8 SR |
196 | smc911x_reg_write(TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | TX_CMD_A_INT_LAST_SEG | length); |
197 | smc911x_reg_write(TX_DATA_FIFO, length); | |
de1b686b SH |
198 | |
199 | tmplen = (length + 3) / 4; | |
200 | ||
3e0f331c | 201 | while (tmplen--) |
33314470 | 202 | pkt_data_push(TX_DATA_FIFO, *data++); |
de1b686b SH |
203 | |
204 | /* wait for transmission */ | |
890a02e8 | 205 | while (!((smc911x_reg_read(TX_FIFO_INF) & TX_FIFO_INF_TSUSED) >> 16)); |
de1b686b SH |
206 | |
207 | /* get status. Ignore 'no carrier' error, it has no meaning for | |
208 | * full duplex operation | |
209 | */ | |
890a02e8 | 210 | status = smc911x_reg_read(TX_STATUS_FIFO) & (TX_STS_LOC | TX_STS_LATE_COLL | |
de1b686b SH |
211 | TX_STS_MANY_COLL | TX_STS_MANY_DEFER | TX_STS_UNDERRUN); |
212 | ||
3e0f331c | 213 | if (!status) |
de1b686b SH |
214 | return 0; |
215 | ||
216 | printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n", | |
217 | status & TX_STS_LOC ? "TX_STS_LOC " : "", | |
218 | status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "", | |
219 | status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "", | |
220 | status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "", | |
221 | status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : ""); | |
222 | ||
223 | return -1; | |
224 | } | |
225 | ||
226 | void eth_halt(void) | |
227 | { | |
c3147c17 | 228 | smc911x_reset(); |
de1b686b SH |
229 | } |
230 | ||
231 | int eth_rx(void) | |
232 | { | |
233 | u32 *data = (u32 *)NetRxPackets[0]; | |
234 | u32 pktlen, tmplen; | |
235 | u32 status; | |
236 | ||
890a02e8 SR |
237 | if ((smc911x_reg_read(RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) { |
238 | status = smc911x_reg_read(RX_STATUS_FIFO); | |
de1b686b SH |
239 | pktlen = (status & RX_STS_PKT_LEN) >> 16; |
240 | ||
890a02e8 | 241 | smc911x_reg_write(RX_CFG, 0); |
de1b686b SH |
242 | |
243 | tmplen = (pktlen + 2+ 3) / 4; | |
3e0f331c | 244 | while (tmplen--) |
33314470 | 245 | *data++ = pkt_data_pull(RX_DATA_FIFO); |
de1b686b | 246 | |
3e0f331c | 247 | if (status & RX_STS_ES) |
de1b686b SH |
248 | printf(DRIVERNAME |
249 | ": dropped bad packet. Status: 0x%08x\n", | |
250 | status); | |
251 | else | |
252 | NetReceive(NetRxPackets[0], pktlen); | |
253 | } | |
254 | ||
255 | return 0; | |
256 | } |