Merge branch 'upstream-fixes'
[mw/micromonitor-lm32.git] / umon_ports / bf537 / etherdev.c
1 /* etherdev.c:
2  * Hooks needed between uMon and BF537's ethernet MAC.
3  *
4  * General notice:
5  * This code is part of a boot-monitor package developed as a generic base
6  * platform for embedded system designs.  As such, it is likely to be
7  * distributed to various projects beyond the control of the original
8  * author.  Please notify the author of any enhancements made or bugs found
9  * so that all may benefit from the changes.  In addition, notification back
10  * to the author will allow the new user to pick up changes that may have
11  * been made by other users after this version of the code was distributed.
12  *
13  * Author:      Ed Sutter
14  * email:       esutter@lucent.com
15  * phone:       908-582-2351
16  */
17
18 #include "config.h"
19 #if INCLUDE_ETHERNET
20 #include "genlib.h"
21 #include "stddefs.h"
22 #include "warmstart.h"
23 #include "ether.h"
24 #include "phy.h"
25 #include "cpu.h"
26 #include "cdefBF537.h"
27 #include "timer.h"
28 #include "phyeeprom.h"
29
30 #define ENET_RX_ERR (RX_LONG | RX_ALIGN | RX_CRC | RX_LEN | RX_FRAG | \
31                                          RX_ADDR | RX_DMAO | RX_PHY | RX_LATE | RX_RANGE)
32
33 #define PHY_VERBOSE
34 #define AUTO_RX_CKSUM   0       /* If set to 1, Blackfin will compute incoming UDP
35                                                          * and TCP checksums (need to add code to take
36                                                          * advantage of this); otherwise set to 0.
37                                                          */
38 #define PHYAddr                 1
39 #define MDCFREQ                 2000000
40
41 #define RXTOT                   8
42 #define TBUFSIZE                1560 
43 #define RBUFSIZE                1560 
44
45 /* Structure from HW Reference Manual...
46  */
47 struct dma_descriptor {
48         struct dma_descriptor           *next;
49         ulong                                           *startaddr;
50         ushort                                          config;
51 };
52
53 /* Transmit & receive data buffers:
54  * TODO: consider using L1-resident ethernet buffers.
55  */
56
57 int rxidx;
58
59
60 #ifdef TX_STAT_BASE
61 vulong *pTxStat = (vulong *)TX_STAT_BASE;
62 vulong *pTxData = (vulong *)TX_DATA_BASE;
63 vulong (*pRxStat)[2] = RX_STAT_BASE;
64 vulong (*pRxData)[(RBUFSIZE/sizeof(long))] = RX_DATA_BASE;
65 #else
66 vulong tx_stat;
67 vulong tx_data[(TBUFSIZE/sizeof(long))];
68 vulong rx_stat[RXTOT][2];
69 vulong rx_data[RXTOT][(RBUFSIZE/sizeof(long))];
70 vulong *pTxStat = &tx_stat;
71 vulong *pTxData = tx_data;
72 vulong (*pRxStat)[2] = rx_stat;
73 vulong (*pRxData)[(RBUFSIZE/sizeof(long))] = rx_data;
74 #endif
75
76 #ifdef TX_DMA_DESCRIPTOR_BASE
77 struct dma_descriptor *txd = (struct dma_descriptor *)TX_DMA_DESCRIPTOR_BASE;
78 #else
79 struct dma_descriptor txd[2];
80 #endif
81
82 #ifdef RX_DMA_DESCRIPTOR_BASE
83 struct dma_descriptor *rxd = (struct dma_descriptor *)RX_DMA_DESCRIPTOR_BASE;
84 #else
85 struct dma_descriptor rxd[RXTOT*2];
86 #endif
87
88 /*
89  * enreset():
90  *      Reset the PHY and MAC.
91  */
92 void
93 enreset(void)
94 {
95         if (StateOfMonitor != INITIALIZE)
96                 return;
97
98         phy_reset(0);
99
100         *pEMAC_OPMODE = 0x00000000;
101         *pDMA1_CONFIG = 0x0000;
102         *pDMA2_CONFIG = 0x0000;
103 }
104
105 /*
106  * eninit():
107  * Initialize the PHY and MAC.
108  * These steps are based on the section titled "Programming Model"
109  * in chapter 8 (Ethernet MAC) of the hardware reference manual.
110  *
111  * Return 0 if successful; else -1.
112  */
113 static int
114 eninit(void)
115 {
116         static char beenhere;
117         ushort sval, phyreg;
118         ulong lval, tmp;
119
120         *pVR_CTL |= CLKBUFOE;           /* CLKBUF */
121
122         tmp = *pPORTH_FER;                      /* Read it once (bug work-around) */
123         *pPORTH_FER = 0xffff;           /* GPIO pin multiplexing (write it twice) */
124         *pPORTH_FER = 0xffff;
125         
126         /* Load MAC address into device...
127          */
128         memcpy((char *)&lval,(char *)BinEnetAddr,4);
129         *pEMAC_ADDRLO = lval;
130         memcpy((char *)&sval,(char *)BinEnetAddr+4,2);
131         *pEMAC_ADDRHI = sval;
132
133         /* Initialize SYSCTL:
134          */
135 #if AUTO_RX_CKSUM
136         *pEMAC_SYSCTL = (((SYSCLKFREQ/MDCFREQ)/2 - 1) << 8) | (RXDWA | RXCKS);
137 #else
138         *pEMAC_SYSCTL = (((SYSCLKFREQ/MDCFREQ)/2 - 1) << 8) | RXDWA;
139 #endif
140
141         /* PHY setup:
142          * The PHY is an SMSC LAN83C185 (on the board used for this port),
143          * the PHY ID should be 0x0007c0aX (X = revision).
144          */
145         if (phy_id(0,&lval) == -1) {
146                 printf("phy access failure\n");
147                 return(-1);
148         }
149
150         /* Can't get proper link status out of the PHY immediately after reset.
151          * So, always invoke auto-negotiation...
152          */
153         phy_autoneg(0);
154
155         /* Setup MAC:
156          * If PHY is configured full duplex, then we wanna setup the
157          * MAC for full duplex...
158          */
159         phy_read(0,PHY_CTRL,&phyreg);
160         if (phyreg & PHY_CTRL_FDX)
161                 *pEMAC_OPMODE = ASTP | DRO | FDMODE;
162         else
163                 *pEMAC_OPMODE = ASTP | DRO;
164
165         /* Establish/reset management counters:
166          */
167         *pEMAC_MMC_CTL = MMCE | CCOR | RSTC;
168         *pEMAC_MMC_CTL = MMCE | CCOR;
169         
170         /* Establish transmit and receive descriptors:
171          */
172         *pTxStat = 0;
173         txd[0].next = &txd[1];
174         txd[0].startaddr = (ulong *)pTxData;
175         txd[0].config = DMAEN | NDSIZE_5 | FLOW_LARGE | WDSIZE_32;
176         txd[1].next = &txd[0];
177         txd[1].startaddr = (ulong *)pTxStat;
178         txd[1].config = DMAEN | DI_EN | WNR | WDSIZE_32;
179         
180         for(rxidx=0;rxidx<RXTOT;rxidx++) {
181                 pRxStat[rxidx][0] = 0;
182                 pRxStat[rxidx][1] = 0;
183         }
184         for(rxidx=0;rxidx<RXTOT*2;rxidx+=2) {
185                 rxd[rxidx].next = &rxd[rxidx+1];
186                 rxd[rxidx].startaddr = (ulong *)pRxData[rxidx/2];
187                 rxd[rxidx].config = WNR | DMAEN | NDSIZE_5 | FLOW_LARGE | WDSIZE_32;
188                 rxd[rxidx+1].next = &rxd[rxidx+2];
189                 rxd[rxidx+1].startaddr = (ulong *)&pRxStat[rxidx/2][0];
190                 rxd[rxidx+1].config =
191                         WNR | DMAEN | DI_EN | NDSIZE_5 | FLOW_LARGE | WDSIZE_32;
192         }
193         rxd[(RXTOT*2)-1].next = &rxd[0];
194         rxidx = 0;
195
196         /* Init DMA:
197          * DMA_1 is for ethernet receive
198          * DMA_2 is for ethernet transmit
199          */
200         *pDMA1_X_COUNT  = 0;
201         *pDMA1_X_MODIFY = 4;
202         *pDMA1_Y_COUNT  = 0;
203         *pDMA1_Y_MODIFY = 0;
204         *pDMA2_X_COUNT  = 0;
205         *pDMA2_X_MODIFY = 4;
206         *pDMA2_Y_COUNT  = 0;
207         *pDMA2_Y_MODIFY = 0;
208         *pDMA1_NEXT_DESC_PTR = rxd;
209         *pDMA1_CONFIG = DMAEN | WNR | NDSIZE_5 | FLOW_LARGE | WDSIZE_32;
210
211         /* Once everything else is initialized, enable xmt & rcv...
212          */
213         *pEMAC_OPMODE |= (TE | RE);
214         beenhere = 1;
215         return (0);
216 }
217
218 int
219 EtherdevStartup(int verbose)
220 {
221         static char beenhere;
222
223         if (StateOfMonitor != INITIALIZE)
224                 return(0);
225
226         /* On the very first pass through this function we don't bring up
227          * the MAC.  Later in the initialization, polletherdev() will be
228          * called and it will detect the link-up status and automatically
229          * call eninit() to invoke auto-negotiation.
230          */
231         if (!beenhere) {
232                 beenhere = 1;
233                 return(0);
234         }
235
236         /* Put ethernet controller in reset: */
237         enreset();
238
239         /* Initialize controller and return the value returned by
240          * eninit().
241          */
242         return(eninit());
243 }
244
245 void
246 disableMulticastReception(void)
247 {
248         *pEMAC_OPMODE &= ~PAM;
249 }
250
251 void
252 enableMulticastReception(void)
253 {
254         *pEMAC_OPMODE |= PAM;
255 }
256
257 /* disablePromiscuousReception():
258  * Provide the code that disables promiscuous reception.
259  */
260 void
261 disablePromiscuousReception(void)
262 {
263         *pEMAC_OPMODE &= ~PR;
264 }
265
266 /* enablePromiscuousReception():
267  * Provide the code that enables promiscuous reception.
268  */
269 void
270 enablePromiscuousReception(void)
271 {
272         *pEMAC_OPMODE |= PR;
273 }
274
275 /* disableBroadcastReception():
276  * Provide the code that disables broadcast reception.
277  */
278 void
279 disableBroadcastReception(void)
280 {
281         *pEMAC_OPMODE |= DBF;
282 }
283
284 /* enableBroadcastReception():
285  * Provide the code that enables broadcast reception.
286  */
287 void
288 enableBroadcastReception(void)
289 {
290         *pEMAC_OPMODE &= ~DBF;
291 }
292
293 /* 
294  * enselftest():
295  *      Run a self test of the ethernet device(s).  This can be stubbed
296  *      with a return(1).
297  *      Return 1 if success; else -1 if failure.
298  */
299 int
300 enselftest(int verbose)
301 {
302         printf("Self-test not yet available for this driver.\n");
303         return(1);
304 }
305
306 /* ShowEtherdevStats():
307  * This function is used to display device-specific stats (error counts
308  * usually).
309  */
310 void
311 ShowEtherdevStats(void)
312 {
313         ushort phyreg;
314
315         /* Read the register twice because the STAT bit is sticky,
316          * and we want to make sure that we are showing the current status.
317          */
318         phy_read(0,PHY_STAT,&phyreg);
319         phy_read(0,PHY_STAT,&phyreg);
320         if (phyreg & PHY_STAT_LINKUP) {
321                 phy_read(0,PHY_CTRL,&phyreg);
322                 printf("PHY: %dMbps, %s-duplex\n",
323                         phyreg & PHY_CTRL_100MB ? 100 : 10,
324                         phyreg & PHY_CTRL_FDX ? "Full" : "Half");
325         }
326         else {
327                 printf("PHY: link is down\n");
328         }
329 }
330
331 /* getXmitBuffer():
332  * Return a pointer to the buffer that is to be used for transmission of
333  * the next packet.  The monitor will never need more than one packet at
334  * a time; hence, we just keep giving out the same pointer.
335  * Note that for the BF537, the first two bytes of the buffer are used
336  * by the DMA engine for the length of the packet; so this function actually
337  * hands the requestor a pointer incremented by two from the base of tx_data.
338  */
339 uchar *
340 getXmitBuffer(void)
341 {
342         uchar *cp;
343     struct elapsed_tmr tmr;
344
345         /* Since we're only using one transmit buffer, we need to make sure
346          * that the previous packet has been sent prior to giving the pointer
347          * up for re-use...
348          */
349     startElapsedTimer(&tmr,5000);
350         while(*pDMA2_IRQ_STATUS & DMA_RUN) {
351         if(msecElapsed(&tmr)) {
352                         printf("Wait-for-DMA_RUN timeout\n");
353                         break;
354                 }
355         }
356         cp = (uchar *)pTxData;
357         cp += 2;
358         return(cp);
359 }
360
361 /* sendBuffer():
362  * Send out the packet assumed to be built in the buffer returned by the
363  * previous call to getXmitBuffer() above.
364  *
365  * This driver uses register based DMA for ethernet transmit simply
366  * because uMon only needs one transmit buffer; hence, there's really
367  * no need for the added complexity of the descriptor model.
368  * Note that DMA_2 is for ethernet transmit.
369  */
370 int
371 sendBuffer(int length)
372 {
373     struct elapsed_tmr tmr;
374         int linkcheck(void);
375         static char beenhere;
376         int sentlen;
377         ushort *sp;
378
379         if (!beenhere) {
380                 beenhere = 1;
381                 if (linkcheck() == 0)
382                         return(0);
383         }
384
385         sp = (ushort *)pTxData;
386
387         asm("ssync;");
388 #if INCLUDE_ETHERVERBOSE
389         if (EtherVerbose &  SHOW_OUTGOING)
390                 printPkt((struct ether_header *)(sp+1),length,ETHER_OUTGOING);
391 #endif
392
393         *pTxStat = 0;
394         asm("ssync;");
395         *sp = length;
396         asm("ssync;");
397         *pDMA2_NEXT_DESC_PTR = txd;
398         asm("ssync;");
399         *pDMA2_CONFIG = DMAEN | NDSIZE_5 | FLOW_LARGE | WDSIZE_32;
400         asm("ssync;");
401
402     startElapsedTimer(&tmr,5000);
403         while((*pTxStat & TX_COMP) == 0) {
404                 asm("ssync;");
405         if(msecElapsed(&tmr)) {
406                         printf("EMAC: TX_COMP timeout\n");
407                         return(0);
408                 }
409         }
410         asm("ssync;");
411         if ((*pTxStat & TX_OK) == 0) 
412                 printf("EMAC txerr 0x%lx\n",*pTxStat);
413
414         sentlen = (*pTxStat & TX_FRLEN) >> 16;
415         
416         asm("ssync;");
417         if (sentlen < length) {
418                 printf("EMAC warning: sent count (%d) < send request (%d)\n",
419                         sentlen,length);
420         }
421
422         asm("ssync;");
423         *pTxStat = 0;
424         EtherXFRAMECnt++;
425         return(0);
426 }
427
428 /* DisableEtherdev():
429  */
430 void
431 DisableEtherdev(void)
432 {
433         enreset();
434 }
435
436 /* extGetIpAdd():
437  * If there was some external mechanism (other than just using the
438  * IPADD shell variable established in the monrc file) for retrieval of
439  * the board's IP address, then do it here...
440  */
441 char *
442 extGetIpAdd(void)
443 {
444         return((char *)0);
445 }
446
447 /* extGetEtherAdd():
448  * If there was some external mechanism (other than just using the
449  * ETHERADD shell variable established in the monrc file) for retrieval of
450  * the board's MAC address, then do it here...
451  */
452 char *
453 extGetEtherAdd(void)
454 {
455 #if PLATFORM_PCORE537
456         char *RetVal = DEFAULT_ETHERADD;                                // To return from.
457         static char MacAddress[18];
458
459         eeprom_init();
460         eeprom_getMAC(BinEnetAddr);
461         EtherToString(BinEnetAddr, MacAddress);
462         RetVal = MacAddress;
463         return RetVal;
464 #else
465         return((char *)0);
466 #endif
467 }
468
469 int
470 linkcheck(void)
471 {
472         ushort tmp;
473         static ushort phystat;
474
475         phy_read(0,PHY_STAT,&tmp);
476         if ((tmp & PHY_STAT_LINKUP) && ((phystat & PHY_STAT_LINKUP) == 0)) {
477 #if INCLUDE_ETHERVERBOSE
478                 if (EtherVerbose &  SHOW_DRIVER_DEBUG)
479                         printf("\nEMAC: link up\n");
480 #endif
481                 enreset();
482                 eninit();
483                 phystat = tmp;
484         }
485         else if (((tmp & PHY_STAT_LINKUP) == 0) && (phystat & PHY_STAT_LINKUP)) {
486 #if INCLUDE_ETHERVERBOSE
487                 if (EtherVerbose &  SHOW_DRIVER_DEBUG)
488                         printf("\nEMAC: link down\n");
489 #endif
490                 phystat = tmp;
491         }
492
493         /* Return non-zero if link is up...
494          */
495         return(phystat & PHY_STAT_LINKUP);
496 }
497
498 /* rxbuf_check():
499  * This function was written to help diagnose a problem with this driver
500  * that appears to occur when an input buffer overflow occurs.
501  */
502 void
503 rxbuf_check(void)
504 {
505         int i, idx;
506         volatile ulong stat;
507
508 #ifdef RXCHECK_VERBOSE
509         static int tag;
510
511         tag++;
512 #endif
513
514         idx = rxidx+1;
515         if (idx == RXTOT)
516                 idx = 0;
517
518         for(i=0;i<RXTOT;i++) {
519                 stat = pRxStat[idx][AUTO_RX_CKSUM];
520                 if (stat & RX_COMP) {
521 #ifdef RXCHECK_VERBOSE
522                         printf("(%08x/%d) rx_comp: %d\n",tag,rxidx,idx);
523 #endif
524                         if (idx != rxidx) {
525                                 if (++rxidx == RXTOT)
526                                         rxidx = 0;
527                                 return;
528                         }
529                 }
530 #ifdef RXCHECK_VERBOSE
531                 if (stat & ENET_RX_ERR) 
532                         printf("(%08x/%d) STAT (%d): 0x%lx\n",tag,rxidx,stat);
533 #endif
534                 if (++idx == RXTOT)
535                         idx = 0;
536         }
537 }
538
539 /*
540  * polletherdev():
541  * Called continuously by the monitor (ethernet.c) to determine if there
542  * is any incoming ethernet packets.
543  *
544  * NOTES:
545  * 1. This function must be reentrant, because there are a few cases in
546  *    processPACKET() where pollethernet() may be called.
547  * 2. It should only process one packet per call.  This is important
548  *    because if allowed to stay here to flush all available packets,
549  *    it may starve the rest of the system (especially in cases of heavy
550  *    network traffic).
551  * 3. There are cases in the monitor's execution that may cause the
552  *    polling polletherdev() to cease for several seconds.  Depending on
553  *    network traffic, this may cause the input buffering mechanism on
554  *    the ethernet device to overflow.  A robust polletherdev() function
555  *    should support this gracefully (i.e. when the error is detected,
556  *    attempt to pass all queued packets to processPACKET(), then do what
557  *    is necessary to clear the error).
558  *
559  * KNOWN PROBLEM with BF537:
560  * Apparently the DMA engine behind the EMAC does not deal with a buffer
561  * overflow condition on this device.  After several conversations (email)
562  * with the processor tech support at analog devices, I've been told that
563  * the DMA engine will just fill up the buffers pointed to by the circular
564  * queue of buffer descriptors, and then blindly overwrite a buffer if
565  * a burst of packets come in prior to the firmware being able to flush
566  * them from the queue.  IMHO, this is a poor design.  Typically, there is
567  * some kind of "in-use" bit in the buffer descriptor structure that will
568  * let the backend DMA engine know that it should not overwrite the buffer
569  * that the current descriptor is pointing to.
570  * Not the case with this device, so all we can do to reduce the likelihood
571  * that an overflow will occur is to increase the size of RXTOT (above).
572  */
573
574
575 int
576 polletherdev(void)
577 {
578         static int filter;
579         char *cp;
580         volatile ulong stat;
581         int     pktcnt = 0, this_rxidx;
582
583         stat = pRxStat[rxidx][AUTO_RX_CKSUM];
584
585         /* These error states are queried, but I don't expect them to occur;
586          * nevertheless, I need to add error-handler code.
587          */
588         if (*pDMA1_IRQ_STATUS & DMA_ERR)
589                 printf("polletherdev DMAERROR\n");
590
591         if (*pEMAC_SYSTAT &  (RXDMAERR | TXDMAERR))
592                 printf("polletherdev EMAC_SYSTAT: 0x%lx\n",*pEMAC_SYSTAT);
593
594         if (stat & ENET_RX_ERR) 
595                 printf("polletherdev STAT: 0x%lx\n",stat);
596         
597         /* Query for an incoming packet...
598          */
599         if (stat & RX_COMP) {
600                 if (stat & RX_OK) {
601                         cp = (char *)pRxData[rxidx];
602                         cp += 2;
603
604 #ifdef RXCHECK_VERBOSE
605                         printf("rx_ok: %d (cdp=0x%lx,ndp=0x%lx)\n",
606                                 rxidx, *pDMA1_CURR_DESC_PTR, *pDMA1_NEXT_DESC_PTR);
607 #endif
608
609                         /* FIX (hopefully): rxidx is incremented prior to calling
610                          * processPACKET() so that nested calls to polletherdev()
611                          * will deal with the correct rxbuffer index.
612                          */
613                         this_rxidx = rxidx;
614                         if (++rxidx == RXTOT)
615                                 rxidx = 0;
616
617                         processPACKET((struct ether_header *)cp, (stat & RX_FRLEN));
618
619                         *pDMA1_IRQ_STATUS |= (DMA_DONE | DMA_ERR);
620                         pRxStat[this_rxidx][AUTO_RX_CKSUM] = 0;
621
622                         pktcnt++;
623                         filter = 256;
624                 }
625                 else {
626                         printf("polletherdev COMPnotOK: 0x%lx\n",stat);
627
628                         *pDMA1_IRQ_STATUS |= (DMA_DONE | DMA_ERR);
629                         pRxStat[rxidx][AUTO_RX_CKSUM] = 0;
630
631                         if (++rxidx == RXTOT)
632                                 rxidx = 0;
633                 }
634         }
635         else {
636                 rxbuf_check();
637
638                 /* If there is an incoming packet, then there's no need to 
639                  * check the link for state (it must be up)...
640                  */
641                 if (filter == 0) {
642                         linkcheck();
643                         filter = 256;
644                 }
645         }
646
647         filter--;
648         return(pktcnt);
649 }
650
651 /*************************************************************************
652  * PHY interface:
653  *************************************************************************
654  */
655
656 /* phy_busy_poll():
657  * Wait in loop (with timeout) until PHY isn't busy.
658  */
659 int
660 phy_busy_poll(void)
661 {
662     struct elapsed_tmr tmr, tmr1;
663
664         /* Wait till previous MDC/MDIO transaction has completed.
665      */
666     startElapsedTimer(&tmr,5000);
667         while(*(vushort *)EMAC_STAADD & STABUSY) {
668         if(msecElapsed(&tmr)) {
669                         printf("phy_busy_poll timeout\n");
670                         return(-1);
671                 }
672         startElapsedTimer(&tmr1,10);
673         while(!msecElapsed(&tmr1));
674         }
675         return(0);
676 }
677
678 /* phy_read():
679  * Read an off-chip register in a PHY through the MDC/MDIO port:
680  */
681 int
682 phy_read(int phy, int reg, unsigned short *value)
683 {
684         if (phy_busy_poll() < 0)
685                 return(-1);
686
687         *(vulong *)EMAC_STAADD = (SET_PHYAD(PHYAddr) | SET_REGAD(reg) | STABUSY);
688
689         if (phy_busy_poll() < 0)
690                 return(-1);
691
692         *value = (ushort) *(vulong *)EMAC_STADAT;
693         return(0);
694 }
695
696
697 /* phy_write():
698  * Write an off-chip register in a PHY through the MDC/MDIO port:
699  */
700 int
701 phy_write(int phy, int reg, unsigned short value)
702 {
703         if (phy_busy_poll() < 0)
704                 return(-1);
705
706         *(vulong *)EMAC_STADAT = value;
707
708         *(vulong *)EMAC_STAADD =
709                 (SET_PHYAD(PHYAddr) | SET_REGAD(reg) | STAOP | STABUSY);
710
711         return(0);
712 }
713
714 /* phy_reset():
715  * Reset the phy.
716  */
717 int
718 phy_reset(int phy)
719 {
720         ushort ctrl;
721     struct elapsed_tmr tmr;
722
723         phy_read(phy,PHY_CTRL,&ctrl);
724         ctrl |= PHY_CTRL_RST;
725         phy_write(phy,PHY_CTRL,ctrl);
726     startElapsedTimer(&tmr,10);
727         while(1) {
728                 if (phy_read(phy,PHY_CTRL,&ctrl) < 0)
729                         return(-1);
730                 if ((ctrl & PHY_CTRL_RST) == 0)
731                         break;
732         if (msecElapsed(&tmr)) {
733                         printf("phy reset timeout\n");
734                         return(-1);
735                 }
736     }
737         return(0);
738 }
739
740 /* phy_autoneg():
741  * Go through the auto-negotiation process...
742  */
743 int
744 phy_autoneg(int phy)
745 {
746         volatile int retries;
747         ushort ctrl, stat, autoneg, par;
748
749 #if INCLUDE_ETHERVERBOSE
750         if (EtherVerbose &  SHOW_DRIVER_DEBUG)
751                 printf("\nPHY Autonegotiate... ");
752 #endif
753
754         retries = 1000;
755         autoneg = PHY_ANAD_802_3;
756
757         phy_read(phy,PHY_STAT,&stat);
758         if((stat & PHY_STAT_ANABLE) == 0) {
759                 printf("phy does not support autoneg\n");
760                 return (-1);
761         }
762         
763         /* Set appropriate advertisement bits based on this
764          * phy's capabilities...
765          */
766         if (stat & PHY_STAT_100FDX)
767                 autoneg |= PHY_ANAD_100FDX;
768         else if (stat & PHY_STAT_10FDX)
769                 autoneg |= PHY_ANAD_10FDX;
770         else if (stat & PHY_STAT_100HDX)
771                 autoneg |= PHY_ANAD_100HDX;
772         else if (stat & PHY_STAT_10HDX)
773                 autoneg |= PHY_ANAD_10HDX;
774         phy_write(phy,PHY_ANAD, autoneg);
775
776         phy_read(phy,PHY_CTRL,&ctrl);
777         ctrl &= ~(PHY_CTRL_LBK | PHY_CTRL_PWRDN);
778         ctrl &= ~(PHY_CTRL_ISOLATE | PHY_CTRL_COLTST);
779         ctrl |= (PHY_CTRL_ANE);
780
781         phy_write(phy,PHY_CTRL, ctrl);
782         ctrl |= PHY_CTRL_ANRESTART; 
783         phy_write(phy,PHY_CTRL, ctrl);
784
785         do {
786                 phy_read(phy,PHY_STAT,&stat);
787                 if((stat & PHY_STAT_REMFAULT) != 0) {
788                         printf("phy: remote fault\n");
789                         return -1;
790                 }
791                 if (--retries <= 0) {
792 #if INCLUDE_ETHERVERBOSE
793                         if (EtherVerbose &  SHOW_DRIVER_DEBUG)
794                                 printf("phy: AN timeout\n");
795 #endif
796                         return -1;
797                 }
798         } while((stat & PHY_STAT_ANDONE) == 0);
799
800         phy_read(phy,PHY_ANLPAR,&par);
801
802         if((par & PHY_ANLP_REMFAULT) != 0) {
803                 printf("phy: remote fault\n");
804                 return -1;
805         }
806
807         phy_read(phy,PHY_CTRL,&ctrl);
808         ctrl &= (~PHY_CTRL_FDX | PHY_CTRL_100MB);
809
810         phy_read(phy,PHY_STAT,&stat);
811         if ((stat & PHY_STAT_100FDX) && (par & PHY_ANLP_100FDX))
812                 ctrl |= (PHY_CTRL_100MB | PHY_CTRL_FDX);
813         else if ((stat & PHY_STAT_10FDX) && (par & PHY_ANLP_10FDX))
814                 ctrl |= PHY_CTRL_FDX;
815         else if ((stat & PHY_STAT_100HDX) && (par & PHY_ANLP_100HDX))
816                 ctrl |= PHY_CTRL_100MB;
817
818         phy_write(phy,PHY_CTRL, ctrl);
819
820 #if INCLUDE_ETHERVERBOSE
821         if (EtherVerbose &  SHOW_DRIVER_DEBUG) {
822                 printf("%dMbps %s-duplex\n",
823                         ctrl & PHY_CTRL_100MB ? 100 : 10,
824                         ctrl & PHY_CTRL_FDX ? "Full" : "Half");
825         }
826 #endif
827         return 0;
828 }
829
830 /* phy_id():
831  * Return the concatenation of the hi and lo phy ID registers (16 bits each)
832  * as a single 32-bit value.
833  */
834 int
835 phy_id(int phy, ulong *id)
836 {
837         ushort idhi, idlo;
838
839         if (phy_read(phy,PHY_IDHI,&idhi) < 0)
840                 return(-1);
841         if (phy_read(phy,PHY_IDLO,&idlo) < 0)
842                 return(-1);
843         *id = (((ulong)idhi << 16) | idlo);
844         //printf("phyid: 0x%lx\n",*id);
845         return(0);
846 }
847
848 #endif