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 #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 
89 #define NS7520_MII_NEG_DELAY		(5*CONFIG_SYS_HZ)	/* in s */
90 #define TX_TIMEOUT			(5*CONFIG_SYS_HZ)	/* in s */
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 
eth_init(bd_t * pbis)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 
eth_send(volatile void * pPacket,int nLen)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 
eth_rx(void)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 
eth_halt(void)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 
ns7520_eth_reset(void)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 */
390 	ns7520_mii_write(PHY_BMCR, PHY_BMCR_RESET);
391 	ns7520_mii_write(PHY_BMCR, 0);
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 
ns7520_link_auto_negotiate(void)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 */
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);
447 	/* start auto-negotiation */
448 	ns7520_mii_write(PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG);
449 
450 	/* wait for completion */
451 
452 	ulStartJiffies = get_timer(0);
453 	while (get_timer(0) < ulStartJiffies + NS7520_MII_NEG_DELAY) {
454 		uiStatus = ns7520_mii_read(PHY_BMSR);
455 		if ((uiStatus &
456 		     (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) ==
457 		    (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) {
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 
ns7520_link_update_egcr(void)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 
ns7520_link_print_changed(void)511 static void ns7520_link_print_changed(void)
512 {
513 	unsigned short uiStatus;
514 	unsigned short uiControl;
515 
516 	DEBUG_FN(DEBUG_LINK);
517 
518 	uiControl = ns7520_mii_read(PHY_BMCR);
519 
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);
523 
524 		if (!(uiStatus & PHY_BMSR_LS)) {
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 
ns7520_mii_identify_phy(void)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 
585 	phyDetected = (PhyType) uiID1 = ns7520_mii_read(PHY_PHYIDR1);
586 
587 	switch (phyDetected) {
588 	case PHY_LXT971A:
589 		szName = "LXT971A";
590 		uiID2 = ns7520_mii_read(PHY_PHYIDR2);
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 
ns7520_mii_read(unsigned short uiRegister)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 
ns7520_mii_write(unsigned short uiRegister,unsigned short uiData)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 
ns7520_mii_get_clock_divisor(unsigned int unMaxMDIOClk)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 
ns7520_mii_poll_busy(void)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  */
ns7520_miiphy_read(char * devname,unsigned char const addr,unsigned char const reg,unsigned short * const value)764 extern int ns7520_miiphy_read(char *devname, unsigned char const addr,
765 		unsigned char const reg, unsigned short *const value)
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  */
ns7520_miiphy_write(char * devname,unsigned char const addr,unsigned char const reg,unsigned short const value)810 extern int ns7520_miiphy_write(char *devname, unsigned char const addr,
811 		unsigned char const reg, unsigned short const value)
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) */
843 
ns7520_miiphy_initialize(bd_t * bis)844 int ns7520_miiphy_initialize(bd_t *bis)
845 {
846 #if defined(CONFIG_MII)
847 	miiphy_register("ns7520phy", ns7520_miiphy_read, ns7520_miiphy_write);
848 #endif
849 	return 0;
850 }
851