tsec: fix link detection for the RTL8211B PHY
[platform/kernel/u-boot.git] / drivers / net / 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 }