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