uboot/drivers/net/ns7520_eth.c
<<
>>
Prefs
   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) \
  28do { \
  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
  63static 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
  93static int ns7520_eth_reset(void);
  94
  95static void ns7520_link_auto_negotiate(void);
  96static void ns7520_link_update_egcr(void);
  97static void ns7520_link_print_changed(void);
  98
  99/* the PHY stuff */
 100
 101static char ns7520_mii_identify_phy(void);
 102static unsigned short ns7520_mii_read(unsigned short uiRegister);
 103static void ns7520_mii_write(unsigned short uiRegister,
 104                             unsigned short uiData);
 105static unsigned int ns7520_mii_get_clock_divisor(unsigned int
 106                                                 unMaxMDIOClk);
 107static unsigned int ns7520_mii_poll_busy(void);
 108
 109static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
 110static unsigned int uiLastLinkStatus;
 111static 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
 120int 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
 183int 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
 265int 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
 341void 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
 361static 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
 432static 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
 476static 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
 511static 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
 576static 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
 619static 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
 647static 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
 672static 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
 725static 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 */
 756enum 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 */
 764extern int ns7520_miiphy_read(const 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 */
 810extern int ns7520_miiphy_write(const 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
 844int 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