]>
Commit | Line | Data |
---|---|---|
3df5bea0 WD |
1 | /*********************************************************************** |
2 | * | |
3 | * Copyright (C) 2005 by Videon Central, Inc. | |
4 | * | |
5 | * $Id$ | |
6 | * @Author: Arthur Shipkowski | |
7 | * @Descr: Ethernet driver for the NS7520. Uses polled Ethernet, like | |
8 | * the older netarmeth driver. Note that attempting to filter | |
9 | * broadcast and multicast out in the SAFR register will cause | |
10 | * bad things due to released errata. | |
11 | * @References: [1] NS7520 Hardware Reference, December 2003 | |
12 | * [2] Intel LXT971 Datasheet #249414 Rev. 02 | |
13 | * | |
14 | ***********************************************************************/ | |
15 | ||
16 | #include <common.h> | |
17 | ||
3df5bea0 WD |
18 | #include <net.h> /* NetSendPacket */ |
19 | #include <asm/arch/netarm_registers.h> | |
20 | #include <asm/arch/netarm_dma_module.h> | |
21 | ||
22 | #include "ns7520_eth.h" /* for Ethernet and PHY */ | |
23 | ||
24 | /** | |
25 | * Send an error message to the terminal. | |
26 | */ | |
27 | #define ERROR(x) \ | |
28 | do { \ | |
29 | char *__foo = strrchr(__FILE__, '/'); \ | |
30 | \ | |
31 | printf("%s: %d: %s(): ", (__foo == NULL ? __FILE__ : (__foo + 1)), \ | |
32 | __LINE__, __FUNCTION__); \ | |
33 | printf x; printf("\n"); \ | |
34 | } while (0); | |
35 | ||
36 | /* some definition to make transistion to linux easier */ | |
37 | ||
38 | #define NS7520_DRIVER_NAME "eth" | |
39 | #define KERN_WARNING "Warning:" | |
40 | #define KERN_ERR "Error:" | |
41 | #define KERN_INFO "Info:" | |
42 | ||
43 | #if 1 | |
44 | # define DEBUG | |
45 | #endif | |
46 | ||
47 | #ifdef DEBUG | |
48 | # define printk printf | |
49 | ||
50 | # define DEBUG_INIT 0x0001 | |
51 | # define DEBUG_MINOR 0x0002 | |
52 | # define DEBUG_RX 0x0004 | |
53 | # define DEBUG_TX 0x0008 | |
54 | # define DEBUG_INT 0x0010 | |
55 | # define DEBUG_POLL 0x0020 | |
56 | # define DEBUG_LINK 0x0040 | |
57 | # define DEBUG_MII 0x0100 | |
58 | # define DEBUG_MII_LOW 0x0200 | |
59 | # define DEBUG_MEM 0x0400 | |
60 | # define DEBUG_ERROR 0x4000 | |
61 | # define DEBUG_ERROR_CRIT 0x8000 | |
62 | ||
63 | static int nDebugLvl = DEBUG_ERROR_CRIT; | |
64 | ||
65 | # define DEBUG_ARGS0( FLG, a0 ) if( ( nDebugLvl & (FLG) ) == (FLG) ) \ | |
66 | printf("%s: " a0, __FUNCTION__, 0, 0, 0, 0, 0, 0 ) | |
67 | # define DEBUG_ARGS1( FLG, a0, a1 ) if( ( nDebugLvl & (FLG) ) == (FLG)) \ | |
68 | printf("%s: " a0, __FUNCTION__, (int)(a1), 0, 0, 0, 0, 0 ) | |
69 | # define DEBUG_ARGS2( FLG, a0, a1, a2 ) if( (nDebugLvl & (FLG)) ==(FLG))\ | |
70 | printf("%s: " a0, __FUNCTION__, (int)(a1), (int)(a2), 0, 0,0,0 ) | |
71 | # define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) if((nDebugLvl &(FLG))==(FLG))\ | |
72 | printf("%s: "a0,__FUNCTION__,(int)(a1),(int)(a2),(int)(a3),0,0,0) | |
73 | # define DEBUG_FN( FLG ) if( (nDebugLvl & (FLG)) == (FLG) ) \ | |
74 | printf("\r%s:line %d\n", (int)__FUNCTION__, __LINE__, 0,0,0,0); | |
75 | # define ASSERT( expr, func ) if( !( expr ) ) { \ | |
76 | printf( "Assertion failed! %s:line %d %s\n", \ | |
77 | (int)__FUNCTION__,__LINE__,(int)(#expr),0,0,0); \ | |
78 | func } | |
79 | #else /* DEBUG */ | |
80 | # define printk(...) | |
81 | # define DEBUG_ARGS0( FLG, a0 ) | |
82 | # define DEBUG_ARGS1( FLG, a0, a1 ) | |
83 | # define DEBUG_ARGS2( FLG, a0, a1, a2 ) | |
84 | # define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) | |
85 | # define DEBUG_FN( n ) | |
86 | # define ASSERT(expr, func) | |
87 | #endif /* DEBUG */ | |
88 | ||
6d0f6bcf JCPV |
89 | #define NS7520_MII_NEG_DELAY (5*CONFIG_SYS_HZ) /* in s */ |
90 | #define TX_TIMEOUT (5*CONFIG_SYS_HZ) /* in s */ | |
3df5bea0 WD |
91 | #define RX_STALL_WORKAROUND_CNT 100 |
92 | ||
93 | static int ns7520_eth_reset(void); | |
94 | ||
95 | static void ns7520_link_auto_negotiate(void); | |
96 | static void ns7520_link_update_egcr(void); | |
97 | static void ns7520_link_print_changed(void); | |
98 | ||
99 | /* the PHY stuff */ | |
100 | ||
101 | static char ns7520_mii_identify_phy(void); | |
102 | static unsigned short ns7520_mii_read(unsigned short uiRegister); | |
103 | static void ns7520_mii_write(unsigned short uiRegister, | |
104 | unsigned short uiData); | |
105 | static unsigned int ns7520_mii_get_clock_divisor(unsigned int | |
106 | unMaxMDIOClk); | |
107 | static unsigned int ns7520_mii_poll_busy(void); | |
108 | ||
109 | static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; | |
110 | static unsigned int uiLastLinkStatus; | |
111 | static PhyType phyDetected = PHY_NONE; | |
112 | ||
113 | /*********************************************************************** | |
114 | * @Function: eth_init | |
115 | * @Return: -1 on failure otherwise 0 | |
116 | * @Descr: Initializes the ethernet engine and uses either FS Forth's default | |
117 | * MAC addr or the one in environment | |
118 | ***********************************************************************/ | |
119 | ||
120 | int eth_init(bd_t * pbis) | |
121 | { | |
122 | unsigned char aucMACAddr[6]; | |
123 | char *pcTmp = getenv("ethaddr"); | |
124 | char *pcEnd; | |
125 | int i; | |
126 | ||
127 | DEBUG_FN(DEBUG_INIT); | |
128 | ||
129 | /* no need to check for hardware */ | |
130 | ||
131 | if (!ns7520_eth_reset()) | |
132 | return -1; | |
133 | ||
134 | if (NULL == pcTmp) | |
135 | return -1; | |
136 | ||
137 | for (i = 0; i < 6; i++) { | |
138 | aucMACAddr[i] = | |
139 | pcTmp ? simple_strtoul(pcTmp, &pcEnd, 16) : 0; | |
140 | pcTmp = (*pcTmp) ? pcEnd + 1 : pcEnd; | |
141 | } | |
142 | ||
143 | /* configure ethernet address */ | |
144 | ||
145 | *get_eth_reg_addr(NS7520_ETH_SA1) = | |
146 | aucMACAddr[5] << 8 | aucMACAddr[4]; | |
147 | *get_eth_reg_addr(NS7520_ETH_SA2) = | |
148 | aucMACAddr[3] << 8 | aucMACAddr[2]; | |
149 | *get_eth_reg_addr(NS7520_ETH_SA3) = | |
150 | aucMACAddr[1] << 8 | aucMACAddr[0]; | |
151 | ||
152 | /* enable hardware */ | |
153 | ||
154 | *get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN; | |
155 | *get_eth_reg_addr(NS7520_ETH_SUPP) = NS7520_ETH_SUPP_JABBER; | |
156 | *get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN; | |
157 | ||
158 | /* the linux kernel may give packets < 60 bytes, for example arp */ | |
159 | *get_eth_reg_addr(NS7520_ETH_MAC2) = NS7520_ETH_MAC2_CRCEN | | |
160 | NS7520_ETH_MAC2_PADEN | NS7520_ETH_MAC2_HUGE; | |
161 | ||
162 | /* Broadcast/multicast allowed; if you don't set this even unicast chokes */ | |
163 | /* Based on NS7520 errata documentation */ | |
164 | *get_eth_reg_addr(NS7520_ETH_SAFR) = | |
165 | NS7520_ETH_SAFR_BROAD | NS7520_ETH_SAFR_PRM; | |
166 | ||
167 | /* enable receive and transmit FIFO, use 10/100 Mbps MII */ | |
168 | *get_eth_reg_addr(NS7520_ETH_EGCR) |= | |
169 | NS7520_ETH_EGCR_ETXWM_75 | | |
170 | NS7520_ETH_EGCR_ERX | | |
171 | NS7520_ETH_EGCR_ERXREG | | |
172 | NS7520_ETH_EGCR_ERXBR | NS7520_ETH_EGCR_ETX; | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | /*********************************************************************** | |
178 | * @Function: eth_send | |
179 | * @Return: -1 on timeout otherwise 1 | |
180 | * @Descr: sends one frame by DMA | |
181 | ***********************************************************************/ | |
182 | ||
183 | int eth_send(volatile void *pPacket, int nLen) | |
184 | { | |
185 | int i, length32, retval = 1; | |
186 | char *pa; | |
187 | unsigned int *pa32, lastp = 0, rest; | |
188 | unsigned int status; | |
189 | ||
190 | pa = (char *) pPacket; | |
191 | pa32 = (unsigned int *) pPacket; | |
192 | length32 = nLen / 4; | |
193 | rest = nLen % 4; | |
194 | ||
195 | /* make sure there's no garbage in the last word */ | |
196 | switch (rest) { | |
197 | case 0: | |
198 | lastp = pa32[length32 - 1]; | |
199 | length32--; | |
200 | break; | |
201 | case 1: | |
202 | lastp = pa32[length32] & 0x000000ff; | |
203 | break; | |
204 | case 2: | |
205 | lastp = pa32[length32] & 0x0000ffff; | |
206 | break; | |
207 | case 3: | |
208 | lastp = pa32[length32] & 0x00ffffff; | |
209 | break; | |
210 | } | |
211 | ||
212 | while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) & | |
213 | NS7520_ETH_EGSR_TXREGE) | |
214 | == 0) { | |
215 | } | |
216 | ||
217 | /* write to the fifo */ | |
218 | for (i = 0; i < length32; i++) | |
219 | *get_eth_reg_addr(NS7520_ETH_FIFO) = pa32[i]; | |
220 | ||
221 | /* the last word is written to an extra register, this | |
222 | starts the transmission */ | |
223 | *get_eth_reg_addr(NS7520_ETH_FIFOL) = lastp; | |
224 | ||
225 | /* Wait for it to be done */ | |
226 | while ((*get_eth_reg_addr(NS7520_ETH_EGSR) & NS7520_ETH_EGSR_TXBC) | |
227 | == 0) { | |
228 | } | |
229 | status = (*get_eth_reg_addr(NS7520_ETH_ETSR)); | |
230 | *get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_TXBC; /* Clear it now */ | |
231 | ||
232 | if (status & NS7520_ETH_ETSR_TXOK) { | |
233 | retval = 0; /* We're OK! */ | |
234 | } else if (status & NS7520_ETH_ETSR_TXDEF) { | |
235 | printf("Deferred, we'll see.\n"); | |
236 | retval = 0; | |
237 | } else if (status & NS7520_ETH_ETSR_TXAL) { | |
238 | printf("Late collision error, %d collisions.\n", | |
239 | (*get_eth_reg_addr(NS7520_ETH_ETSR)) & | |
240 | NS7520_ETH_ETSR_TXCOLC); | |
241 | } else if (status & NS7520_ETH_ETSR_TXAEC) { | |
242 | printf("Excessive collisions: %d\n", | |
243 | (*get_eth_reg_addr(NS7520_ETH_ETSR)) & | |
244 | NS7520_ETH_ETSR_TXCOLC); | |
245 | } else if (status & NS7520_ETH_ETSR_TXAED) { | |
246 | printf("Excessive deferral on xmit.\n"); | |
247 | } else if (status & NS7520_ETH_ETSR_TXAUR) { | |
248 | printf("Packet underrun.\n"); | |
249 | } else if (status & NS7520_ETH_ETSR_TXAJ) { | |
250 | printf("Jumbo packet error.\n"); | |
251 | } else { | |
252 | printf("Error: Should never get here.\n"); | |
253 | } | |
254 | ||
255 | return (retval); | |
256 | } | |
257 | ||
258 | /*********************************************************************** | |
259 | * @Function: eth_rx | |
260 | * @Return: size of last frame in bytes or 0 if no frame available | |
261 | * @Descr: gives one frame to U-Boot which has been copied by DMA engine already | |
262 | * to NetRxPackets[ 0 ]. | |
263 | ***********************************************************************/ | |
264 | ||
265 | int eth_rx(void) | |
266 | { | |
267 | int i; | |
268 | unsigned short rxlen; | |
269 | unsigned short totrxlen = 0; | |
270 | unsigned int *addr; | |
271 | unsigned int rxstatus, lastrxlen; | |
272 | char *pa; | |
273 | ||
274 | /* If RXBR is 1, data block was received */ | |
275 | while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) & | |
276 | NS7520_ETH_EGSR_RXBR) == NS7520_ETH_EGSR_RXBR) { | |
277 | ||
278 | /* get status register and the length of received block */ | |
279 | rxstatus = *get_eth_reg_addr(NS7520_ETH_ERSR); | |
280 | rxlen = (rxstatus & NS7520_ETH_ERSR_RXSIZE) >> 16; | |
281 | ||
282 | /* clear RXBR to make fifo available */ | |
283 | *get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_RXBR; | |
284 | ||
285 | if (rxstatus & NS7520_ETH_ERSR_ROVER) { | |
286 | printf("Receive overrun, resetting FIFO.\n"); | |
287 | *get_eth_reg_addr(NS7520_ETH_EGCR) &= | |
288 | ~NS7520_ETH_EGCR_ERX; | |
289 | udelay(20); | |
290 | *get_eth_reg_addr(NS7520_ETH_EGCR) |= | |
291 | NS7520_ETH_EGCR_ERX; | |
292 | } | |
293 | if (rxlen == 0) { | |
294 | printf("Nothing.\n"); | |
295 | return 0; | |
296 | } | |
297 | ||
298 | addr = (unsigned int *) NetRxPackets[0]; | |
299 | pa = (char *) NetRxPackets[0]; | |
300 | ||
301 | /* read the fifo */ | |
302 | for (i = 0; i < rxlen / 4; i++) { | |
303 | *addr = *get_eth_reg_addr(NS7520_ETH_FIFO); | |
304 | addr++; | |
305 | } | |
306 | ||
307 | if ((*get_eth_reg_addr(NS7520_ETH_EGSR)) & | |
308 | NS7520_ETH_EGSR_RXREGR) { | |
309 | /* RXFDB indicates wether the last word is 1,2,3 or 4 bytes long */ | |
310 | lastrxlen = | |
311 | ((*get_eth_reg_addr(NS7520_ETH_EGSR)) & | |
312 | NS7520_ETH_EGSR_RXFDB_MA) >> 28; | |
313 | *addr = *get_eth_reg_addr(NS7520_ETH_FIFO); | |
314 | switch (lastrxlen) { | |
315 | case 1: | |
316 | *addr &= 0xff000000; | |
317 | break; | |
318 | case 2: | |
319 | *addr &= 0xffff0000; | |
320 | break; | |
321 | case 3: | |
322 | *addr &= 0xffffff00; | |
323 | break; | |
324 | } | |
325 | } | |
326 | ||
327 | /* Pass the packet up to the protocol layers. */ | |
328 | NetReceive(NetRxPackets[0], rxlen - 4); | |
329 | totrxlen += rxlen - 4; | |
330 | } | |
331 | ||
332 | return totrxlen; | |
333 | } | |
334 | ||
335 | /*********************************************************************** | |
336 | * @Function: eth_halt | |
337 | * @Return: n/a | |
338 | * @Descr: stops the ethernet engine | |
339 | ***********************************************************************/ | |
340 | ||
341 | void eth_halt(void) | |
342 | { | |
343 | DEBUG_FN(DEBUG_INIT); | |
344 | ||
345 | *get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_RXEN; | |
346 | *get_eth_reg_addr(NS7520_ETH_EGCR) &= ~(NS7520_ETH_EGCR_ERX | | |
347 | NS7520_ETH_EGCR_ERXDMA | | |
348 | NS7520_ETH_EGCR_ERXREG | | |
349 | NS7520_ETH_EGCR_ERXBR | | |
350 | NS7520_ETH_EGCR_ETX | | |
351 | NS7520_ETH_EGCR_ETXDMA); | |
352 | } | |
353 | ||
354 | /*********************************************************************** | |
355 | * @Function: ns7520_eth_reset | |
356 | * @Return: 0 on failure otherwise 1 | |
357 | * @Descr: resets the ethernet interface and the PHY, | |
358 | * performs auto negotiation or fixed modes | |
359 | ***********************************************************************/ | |
360 | ||
361 | static int ns7520_eth_reset(void) | |
362 | { | |
363 | DEBUG_FN(DEBUG_MINOR); | |
364 | ||
365 | /* Reset important registers */ | |
366 | *get_eth_reg_addr(NS7520_ETH_EGCR) = 0; /* Null it out! */ | |
367 | *get_eth_reg_addr(NS7520_ETH_MAC1) &= NS7520_ETH_MAC1_SRST; | |
368 | *get_eth_reg_addr(NS7520_ETH_MAC2) = 0; | |
369 | /* Reset MAC */ | |
370 | *get_eth_reg_addr(NS7520_ETH_EGCR) |= NS7520_ETH_EGCR_MAC_RES; | |
371 | udelay(5); | |
372 | *get_eth_reg_addr(NS7520_ETH_EGCR) &= ~NS7520_ETH_EGCR_MAC_RES; | |
373 | ||
374 | /* reset and initialize PHY */ | |
375 | ||
376 | *get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_SRST; | |
377 | ||
378 | /* we don't support hot plugging of PHY, therefore we don't reset | |
379 | phyDetected and nPhyMaxMdioClock here. The risk is if the setting is | |
380 | incorrect the first open | |
381 | may detect the PHY correctly but succeding will fail | |
382 | For reseting the PHY and identifying we have to use the standard | |
383 | MDIO CLOCK value 2.5 MHz only after hardware reset | |
384 | After having identified the PHY we will do faster */ | |
385 | ||
386 | *get_eth_reg_addr(NS7520_ETH_MCFG) = | |
387 | ns7520_mii_get_clock_divisor(nPhyMaxMdioClock); | |
388 | ||
389 | /* reset PHY */ | |
fec61431 HV |
390 | ns7520_mii_write(PHY_BMCR, PHY_BMCR_RESET); |
391 | ns7520_mii_write(PHY_BMCR, 0); | |
3df5bea0 WD |
392 | |
393 | udelay(3000); /* [2] p.70 says at least 300us reset recovery time. */ | |
394 | ||
395 | /* MII clock has been setup to default, ns7520_mii_identify_phy should | |
396 | work for all */ | |
397 | ||
398 | if (!ns7520_mii_identify_phy()) { | |
399 | printk(KERN_ERR NS7520_DRIVER_NAME | |
400 | ": Unsupported PHY, aborting\n"); | |
401 | return 0; | |
402 | } | |
403 | ||
404 | /* now take the highest MDIO clock possible after detection */ | |
405 | *get_eth_reg_addr(NS7520_ETH_MCFG) = | |
406 | ns7520_mii_get_clock_divisor(nPhyMaxMdioClock); | |
407 | ||
408 | /* PHY has been detected, so there can be no abort reason and we can | |
409 | finish initializing ethernet */ | |
410 | ||
411 | uiLastLinkStatus = 0xff; /* undefined */ | |
412 | ||
413 | ns7520_link_auto_negotiate(); | |
414 | ||
415 | if (phyDetected == PHY_LXT971A) | |
416 | /* set LED2 to link mode */ | |
417 | ns7520_mii_write(PHY_LXT971_LED_CFG, | |
418 | (PHY_LXT971_LED_CFG_LINK_ACT << | |
419 | PHY_LXT971_LED_CFG_SHIFT_LED2) | | |
420 | (PHY_LXT971_LED_CFG_TRANSMIT << | |
421 | PHY_LXT971_LED_CFG_SHIFT_LED1)); | |
422 | ||
423 | return 1; | |
424 | } | |
425 | ||
426 | /*********************************************************************** | |
427 | * @Function: ns7520_link_auto_negotiate | |
428 | * @Return: void | |
429 | * @Descr: performs auto-negotation of link. | |
430 | ***********************************************************************/ | |
431 | ||
432 | static void ns7520_link_auto_negotiate(void) | |
433 | { | |
434 | unsigned long ulStartJiffies; | |
435 | unsigned short uiStatus; | |
436 | ||
437 | DEBUG_FN(DEBUG_LINK); | |
438 | ||
439 | /* run auto-negotation */ | |
440 | /* define what we are capable of */ | |
fec61431 HV |
441 | ns7520_mii_write(PHY_ANAR, |
442 | PHY_ANLPAR_TXFD | | |
443 | PHY_ANLPAR_TX | | |
444 | PHY_ANLPAR_10FD | | |
445 | PHY_ANLPAR_10 | | |
446 | PHY_ANLPAR_PSB_802_3); | |
3df5bea0 | 447 | /* start auto-negotiation */ |
fec61431 | 448 | ns7520_mii_write(PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); |
3df5bea0 WD |
449 | |
450 | /* wait for completion */ | |
451 | ||
452 | ulStartJiffies = get_timer(0); | |
453 | while (get_timer(0) < ulStartJiffies + NS7520_MII_NEG_DELAY) { | |
fec61431 | 454 | uiStatus = ns7520_mii_read(PHY_BMSR); |
3df5bea0 | 455 | if ((uiStatus & |
fec61431 HV |
456 | (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) == |
457 | (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) { | |
3df5bea0 WD |
458 | /* lucky we are, auto-negotiation succeeded */ |
459 | ns7520_link_print_changed(); | |
460 | ns7520_link_update_egcr(); | |
461 | return; | |
462 | } | |
463 | } | |
464 | ||
465 | DEBUG_ARGS0(DEBUG_LINK, "auto-negotiation timed out\n"); | |
466 | /* ignore invalid link settings */ | |
467 | } | |
468 | ||
469 | /*********************************************************************** | |
470 | * @Function: ns7520_link_update_egcr | |
471 | * @Return: void | |
472 | * @Descr: updates the EGCR and MAC2 link status after mode change or | |
473 | * auto-negotation | |
474 | ***********************************************************************/ | |
475 | ||
476 | static void ns7520_link_update_egcr(void) | |
477 | { | |
478 | unsigned int unEGCR; | |
479 | unsigned int unMAC2; | |
480 | unsigned int unIPGT; | |
481 | ||
482 | DEBUG_FN(DEBUG_LINK); | |
483 | ||
484 | unEGCR = *get_eth_reg_addr(NS7520_ETH_EGCR); | |
485 | unMAC2 = *get_eth_reg_addr(NS7520_ETH_MAC2); | |
486 | unIPGT = | |
487 | *get_eth_reg_addr(NS7520_ETH_IPGT) & ~NS7520_ETH_IPGT_IPGT; | |
488 | ||
489 | unEGCR &= ~NS7520_ETH_EGCR_EFULLD; | |
490 | unMAC2 &= ~NS7520_ETH_MAC2_FULLD; | |
491 | if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE) | |
492 | == PHY_LXT971_STAT2_DUPLEX_MODE) { | |
493 | unEGCR |= NS7520_ETH_EGCR_EFULLD; | |
494 | unMAC2 |= NS7520_ETH_MAC2_FULLD; | |
495 | unIPGT |= 0x15; /* see [1] p. 167 */ | |
496 | } else | |
497 | unIPGT |= 0x12; /* see [1] p. 167 */ | |
498 | ||
499 | *get_eth_reg_addr(NS7520_ETH_MAC2) = unMAC2; | |
500 | *get_eth_reg_addr(NS7520_ETH_EGCR) = unEGCR; | |
501 | *get_eth_reg_addr(NS7520_ETH_IPGT) = unIPGT; | |
502 | } | |
503 | ||
504 | /*********************************************************************** | |
505 | * @Function: ns7520_link_print_changed | |
506 | * @Return: void | |
507 | * @Descr: checks whether the link status has changed and if so prints | |
508 | * the new mode | |
509 | ***********************************************************************/ | |
510 | ||
511 | static void ns7520_link_print_changed(void) | |
512 | { | |
513 | unsigned short uiStatus; | |
514 | unsigned short uiControl; | |
515 | ||
516 | DEBUG_FN(DEBUG_LINK); | |
517 | ||
fec61431 | 518 | uiControl = ns7520_mii_read(PHY_BMCR); |
3df5bea0 | 519 | |
fec61431 HV |
520 | if ((uiControl & PHY_BMCR_AUTON) == PHY_BMCR_AUTON) { |
521 | /* PHY_BMSR_LS is only set on autonegotiation */ | |
522 | uiStatus = ns7520_mii_read(PHY_BMSR); | |
3df5bea0 | 523 | |
fec61431 | 524 | if (!(uiStatus & PHY_BMSR_LS)) { |
3df5bea0 WD |
525 | printk(KERN_WARNING NS7520_DRIVER_NAME |
526 | ": link down\n"); | |
527 | /* @TODO Linux: carrier_off */ | |
528 | } else { | |
529 | /* @TODO Linux: carrier_on */ | |
530 | if (phyDetected == PHY_LXT971A) { | |
531 | uiStatus = | |
532 | ns7520_mii_read(PHY_LXT971_STAT2); | |
533 | uiStatus &= | |
534 | (PHY_LXT971_STAT2_100BTX | | |
535 | PHY_LXT971_STAT2_DUPLEX_MODE | | |
536 | PHY_LXT971_STAT2_AUTO_NEG); | |
537 | ||
538 | /* mask out all uninteresting parts */ | |
539 | } | |
540 | /* other PHYs must store there link information in | |
541 | uiStatus as PHY_LXT971 */ | |
542 | } | |
543 | } else { | |
544 | /* mode has been forced, so uiStatus should be the same as the | |
545 | last link status, enforce printing */ | |
546 | uiStatus = uiLastLinkStatus; | |
547 | uiLastLinkStatus = 0xff; | |
548 | } | |
549 | ||
550 | if (uiStatus != uiLastLinkStatus) { | |
551 | /* save current link status */ | |
552 | uiLastLinkStatus = uiStatus; | |
553 | ||
554 | /* print new link status */ | |
555 | ||
556 | printk(KERN_INFO NS7520_DRIVER_NAME | |
557 | ": link mode %i Mbps %s duplex %s\n", | |
558 | (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10, | |
559 | (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" : | |
560 | "half", | |
561 | (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" : | |
562 | ""); | |
563 | } | |
564 | } | |
565 | ||
566 | /*********************************************************************** | |
567 | * the MII low level stuff | |
568 | ***********************************************************************/ | |
569 | ||
570 | /*********************************************************************** | |
571 | * @Function: ns7520_mii_identify_phy | |
572 | * @Return: 1 if supported PHY has been detected otherwise 0 | |
573 | * @Descr: checks for supported PHY and prints the IDs. | |
574 | ***********************************************************************/ | |
575 | ||
576 | static char ns7520_mii_identify_phy(void) | |
577 | { | |
578 | unsigned short uiID1; | |
579 | unsigned short uiID2; | |
580 | unsigned char *szName; | |
581 | char cRes = 0; | |
582 | ||
583 | DEBUG_FN(DEBUG_MII); | |
584 | ||
fec61431 | 585 | phyDetected = (PhyType) uiID1 = ns7520_mii_read(PHY_PHYIDR1); |
3df5bea0 WD |
586 | |
587 | switch (phyDetected) { | |
588 | case PHY_LXT971A: | |
589 | szName = "LXT971A"; | |
fec61431 | 590 | uiID2 = ns7520_mii_read(PHY_PHYIDR2); |
3df5bea0 WD |
591 | nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK; |
592 | cRes = 1; | |
593 | break; | |
594 | case PHY_NONE: | |
595 | default: | |
596 | /* in case uiID1 == 0 && uiID2 == 0 we may have the wrong | |
597 | address or reset sets the wrong NS7520_ETH_MCFG_CLKS */ | |
598 | ||
599 | uiID2 = 0; | |
600 | szName = "unknown"; | |
601 | nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; | |
602 | phyDetected = PHY_NONE; | |
603 | } | |
604 | ||
605 | printk(KERN_INFO NS7520_DRIVER_NAME | |
606 | ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName); | |
607 | ||
608 | return cRes; | |
609 | } | |
610 | ||
611 | /*********************************************************************** | |
612 | * @Function: ns7520_mii_read | |
613 | * @Return: the data read from PHY register uiRegister | |
614 | * @Descr: the data read may be invalid if timed out. If so, a message | |
615 | * is printed but the invalid data is returned. | |
616 | * The fixed device address is being used. | |
617 | ***********************************************************************/ | |
618 | ||
619 | static unsigned short ns7520_mii_read(unsigned short uiRegister) | |
620 | { | |
621 | DEBUG_FN(DEBUG_MII_LOW); | |
622 | ||
623 | /* write MII register to be read */ | |
624 | *get_eth_reg_addr(NS7520_ETH_MADR) = | |
625 | CONFIG_PHY_ADDR << 8 | uiRegister; | |
626 | ||
627 | *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ; | |
628 | ||
629 | if (!ns7520_mii_poll_busy()) | |
630 | printk(KERN_WARNING NS7520_DRIVER_NAME | |
631 | ": MII still busy in read\n"); | |
632 | /* continue to read */ | |
633 | ||
634 | *get_eth_reg_addr(NS7520_ETH_MCMD) = 0; | |
635 | ||
636 | return (unsigned short) (*get_eth_reg_addr(NS7520_ETH_MRDD)); | |
637 | } | |
638 | ||
639 | /*********************************************************************** | |
640 | * @Function: ns7520_mii_write | |
641 | * @Return: nothing | |
642 | * @Descr: writes the data to the PHY register. In case of a timeout, | |
643 | * no special handling is performed but a message printed | |
644 | * The fixed device address is being used. | |
645 | ***********************************************************************/ | |
646 | ||
647 | static void ns7520_mii_write(unsigned short uiRegister, | |
648 | unsigned short uiData) | |
649 | { | |
650 | DEBUG_FN(DEBUG_MII_LOW); | |
651 | ||
652 | /* write MII register to be written */ | |
653 | *get_eth_reg_addr(NS7520_ETH_MADR) = | |
654 | CONFIG_PHY_ADDR << 8 | uiRegister; | |
655 | ||
656 | *get_eth_reg_addr(NS7520_ETH_MWTD) = uiData; | |
657 | ||
658 | if (!ns7520_mii_poll_busy()) { | |
659 | printf(KERN_WARNING NS7520_DRIVER_NAME | |
660 | ": MII still busy in write\n"); | |
661 | } | |
662 | } | |
663 | ||
664 | /*********************************************************************** | |
665 | * @Function: ns7520_mii_get_clock_divisor | |
666 | * @Return: the clock divisor that should be used in NS7520_ETH_MCFG_CLKS | |
667 | * @Descr: if no clock divisor can be calculated for the | |
668 | * current SYSCLK and the maximum MDIO Clock, a warning is printed | |
669 | * and the greatest divisor is taken | |
670 | ***********************************************************************/ | |
671 | ||
672 | static unsigned int ns7520_mii_get_clock_divisor(unsigned int unMaxMDIOClk) | |
673 | { | |
674 | struct { | |
675 | unsigned int unSysClkDivisor; | |
676 | unsigned int unClks; /* field for NS7520_ETH_MCFG_CLKS */ | |
677 | } PHYClockDivisors[] = { | |
678 | { | |
679 | 4, NS7520_ETH_MCFG_CLKS_4}, { | |
680 | 6, NS7520_ETH_MCFG_CLKS_6}, { | |
681 | 8, NS7520_ETH_MCFG_CLKS_8}, { | |
682 | 10, NS7520_ETH_MCFG_CLKS_10}, { | |
683 | 14, NS7520_ETH_MCFG_CLKS_14}, { | |
684 | 20, NS7520_ETH_MCFG_CLKS_20}, { | |
685 | 28, NS7520_ETH_MCFG_CLKS_28} | |
686 | }; | |
687 | ||
688 | int nIndexSysClkDiv; | |
689 | int nArraySize = | |
690 | sizeof(PHYClockDivisors) / sizeof(PHYClockDivisors[0]); | |
691 | unsigned int unClks = NS7520_ETH_MCFG_CLKS_28; /* defaults to | |
692 | greatest div */ | |
693 | ||
694 | DEBUG_FN(DEBUG_INIT); | |
695 | ||
696 | for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize; | |
697 | nIndexSysClkDiv++) { | |
698 | /* find first sysclock divisor that isn't higher than 2.5 MHz | |
699 | clock */ | |
700 | if (NETARM_XTAL_FREQ / | |
701 | PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <= | |
702 | unMaxMDIOClk) { | |
703 | unClks = PHYClockDivisors[nIndexSysClkDiv].unClks; | |
704 | break; | |
705 | } | |
706 | } | |
707 | ||
708 | DEBUG_ARGS2(DEBUG_INIT, | |
709 | "Taking MDIO Clock bit mask 0x%0x for max clock %i\n", | |
710 | unClks, unMaxMDIOClk); | |
711 | ||
712 | /* return greatest divisor */ | |
713 | return unClks; | |
714 | } | |
715 | ||
716 | /*********************************************************************** | |
717 | * @Function: ns7520_mii_poll_busy | |
718 | * @Return: 0 if timed out otherwise the remaing timeout | |
719 | * @Descr: waits until the MII has completed a command or it times out | |
720 | * code may be interrupted by hard interrupts. | |
721 | * It is not checked what happens on multiple actions when | |
722 | * the first is still being busy and we timeout. | |
723 | ***********************************************************************/ | |
724 | ||
725 | static unsigned int ns7520_mii_poll_busy(void) | |
726 | { | |
727 | unsigned int unTimeout = 1000; | |
728 | ||
729 | DEBUG_FN(DEBUG_MII_LOW); | |
730 | ||
731 | while (((*get_eth_reg_addr(NS7520_ETH_MIND) & NS7520_ETH_MIND_BUSY) | |
732 | == NS7520_ETH_MIND_BUSY) && unTimeout) | |
733 | unTimeout--; | |
734 | ||
735 | return unTimeout; | |
736 | } | |
737 | ||
738 | /* ---------------------------------------------------------------------------- | |
739 | * Net+ARM ethernet MII functionality. | |
740 | */ | |
741 | #if defined(CONFIG_MII) | |
742 | ||
743 | /** | |
744 | * Maximum MII address we support | |
745 | */ | |
746 | #define MII_ADDRESS_MAX (31) | |
747 | ||
748 | /** | |
749 | * Maximum MII register address we support | |
750 | */ | |
751 | #define MII_REGISTER_MAX (31) | |
752 | ||
753 | /** | |
754 | * Ethernet MII interface return values for public functions. | |
755 | */ | |
756 | enum mii_status { | |
757 | MII_STATUS_SUCCESS = 0, | |
758 | MII_STATUS_FAILURE = 1, | |
759 | }; | |
760 | ||
761 | /** | |
762 | * Read a 16-bit value from an MII register. | |
763 | */ | |
63ff004c MB |
764 | extern int ns7520_miiphy_read(char *devname, unsigned char const addr, |
765 | unsigned char const reg, unsigned short *const value) | |
3df5bea0 WD |
766 | { |
767 | int ret = MII_STATUS_FAILURE; | |
768 | ||
769 | /* Parameter checks */ | |
770 | if (addr > MII_ADDRESS_MAX) { | |
771 | ERROR(("invalid addr, 0x%02X", addr)); | |
772 | goto miiphy_read_failed_0; | |
773 | } | |
774 | ||
775 | if (reg > MII_REGISTER_MAX) { | |
776 | ERROR(("invalid reg, 0x%02X", reg)); | |
777 | goto miiphy_read_failed_0; | |
778 | } | |
779 | ||
780 | if (value == NULL) { | |
781 | ERROR(("NULL value")); | |
782 | goto miiphy_read_failed_0; | |
783 | } | |
784 | ||
785 | DEBUG_FN(DEBUG_MII_LOW); | |
786 | ||
787 | /* write MII register to be read */ | |
788 | *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg; | |
789 | ||
790 | *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ; | |
791 | ||
792 | if (!ns7520_mii_poll_busy()) | |
793 | printk(KERN_WARNING NS7520_DRIVER_NAME | |
794 | ": MII still busy in read\n"); | |
795 | /* continue to read */ | |
796 | ||
797 | *get_eth_reg_addr(NS7520_ETH_MCMD) = 0; | |
798 | ||
799 | *value = (*get_eth_reg_addr(NS7520_ETH_MRDD)); | |
800 | ret = MII_STATUS_SUCCESS; | |
801 | /* Fall through */ | |
802 | ||
803 | miiphy_read_failed_0: | |
804 | return (ret); | |
805 | } | |
806 | ||
807 | /** | |
808 | * Write a 16-bit value to an MII register. | |
809 | */ | |
63ff004c MB |
810 | extern int ns7520_miiphy_write(char *devname, unsigned char const addr, |
811 | unsigned char const reg, unsigned short const value) | |
3df5bea0 WD |
812 | { |
813 | int ret = MII_STATUS_FAILURE; | |
814 | ||
815 | /* Parameter checks */ | |
816 | if (addr > MII_ADDRESS_MAX) { | |
817 | ERROR(("invalid addr, 0x%02X", addr)); | |
818 | goto miiphy_write_failed_0; | |
819 | } | |
820 | ||
821 | if (reg > MII_REGISTER_MAX) { | |
822 | ERROR(("invalid reg, 0x%02X", reg)); | |
823 | goto miiphy_write_failed_0; | |
824 | } | |
825 | ||
826 | /* write MII register to be written */ | |
827 | *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg; | |
828 | ||
829 | *get_eth_reg_addr(NS7520_ETH_MWTD) = value; | |
830 | ||
831 | if (!ns7520_mii_poll_busy()) { | |
832 | printf(KERN_WARNING NS7520_DRIVER_NAME | |
833 | ": MII still busy in write\n"); | |
834 | } | |
835 | ||
836 | ret = MII_STATUS_SUCCESS; | |
837 | /* Fall through */ | |
838 | ||
839 | miiphy_write_failed_0: | |
840 | return (ret); | |
841 | } | |
842 | #endif /* defined(CONFIG_MII) */ | |
63ff004c MB |
843 | |
844 | int ns7520_miiphy_initialize(bd_t *bis) | |
845 | { | |
63ff004c MB |
846 | #if defined(CONFIG_MII) |
847 | miiphy_register("ns7520phy", ns7520_miiphy_read, ns7520_miiphy_write); | |
63ff004c MB |
848 | #endif |
849 | return 0; | |
850 | } |