|
@@ -1,850 +0,0 @@
|
|
-/***********************************************************************
|
|
|
|
- *
|
|
|
|
- * Copyright (C) 2005 by Videon Central, Inc.
|
|
|
|
- *
|
|
|
|
- * $Id$
|
|
|
|
- * @Author: Arthur Shipkowski
|
|
|
|
- * @Descr: Ethernet driver for the NS7520. Uses polled Ethernet, like
|
|
|
|
- * the older netarmeth driver. Note that attempting to filter
|
|
|
|
- * broadcast and multicast out in the SAFR register will cause
|
|
|
|
- * bad things due to released errata.
|
|
|
|
- * @References: [1] NS7520 Hardware Reference, December 2003
|
|
|
|
- * [2] Intel LXT971 Datasheet #249414 Rev. 02
|
|
|
|
- *
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-#include <common.h>
|
|
|
|
-
|
|
|
|
-#include <net.h> /* NetSendPacket */
|
|
|
|
-#include <asm/arch/netarm_registers.h>
|
|
|
|
-#include <asm/arch/netarm_dma_module.h>
|
|
|
|
-
|
|
|
|
-#include "ns7520_eth.h" /* for Ethernet and PHY */
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Send an error message to the terminal.
|
|
|
|
- */
|
|
|
|
-#define ERROR(x) \
|
|
|
|
-do { \
|
|
|
|
- char *__foo = strrchr(__FILE__, '/'); \
|
|
|
|
- \
|
|
|
|
- printf("%s: %d: %s(): ", (__foo == NULL ? __FILE__ : (__foo + 1)), \
|
|
|
|
- __LINE__, __FUNCTION__); \
|
|
|
|
- printf x; printf("\n"); \
|
|
|
|
-} while (0);
|
|
|
|
-
|
|
|
|
-/* some definition to make transistion to linux easier */
|
|
|
|
-
|
|
|
|
-#define NS7520_DRIVER_NAME "eth"
|
|
|
|
-#define KERN_WARNING "Warning:"
|
|
|
|
-#define KERN_ERR "Error:"
|
|
|
|
-#define KERN_INFO "Info:"
|
|
|
|
-
|
|
|
|
-#if 1
|
|
|
|
-# define DEBUG
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#ifdef DEBUG
|
|
|
|
-# define printk printf
|
|
|
|
-
|
|
|
|
-# define DEBUG_INIT 0x0001
|
|
|
|
-# define DEBUG_MINOR 0x0002
|
|
|
|
-# define DEBUG_RX 0x0004
|
|
|
|
-# define DEBUG_TX 0x0008
|
|
|
|
-# define DEBUG_INT 0x0010
|
|
|
|
-# define DEBUG_POLL 0x0020
|
|
|
|
-# define DEBUG_LINK 0x0040
|
|
|
|
-# define DEBUG_MII 0x0100
|
|
|
|
-# define DEBUG_MII_LOW 0x0200
|
|
|
|
-# define DEBUG_MEM 0x0400
|
|
|
|
-# define DEBUG_ERROR 0x4000
|
|
|
|
-# define DEBUG_ERROR_CRIT 0x8000
|
|
|
|
-
|
|
|
|
-static int nDebugLvl = DEBUG_ERROR_CRIT;
|
|
|
|
-
|
|
|
|
-# define DEBUG_ARGS0( FLG, a0 ) if( ( nDebugLvl & (FLG) ) == (FLG) ) \
|
|
|
|
- printf("%s: " a0, __FUNCTION__, 0, 0, 0, 0, 0, 0 )
|
|
|
|
-# define DEBUG_ARGS1( FLG, a0, a1 ) if( ( nDebugLvl & (FLG) ) == (FLG)) \
|
|
|
|
- printf("%s: " a0, __FUNCTION__, (int)(a1), 0, 0, 0, 0, 0 )
|
|
|
|
-# define DEBUG_ARGS2( FLG, a0, a1, a2 ) if( (nDebugLvl & (FLG)) ==(FLG))\
|
|
|
|
- printf("%s: " a0, __FUNCTION__, (int)(a1), (int)(a2), 0, 0,0,0 )
|
|
|
|
-# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) if((nDebugLvl &(FLG))==(FLG))\
|
|
|
|
- printf("%s: "a0,__FUNCTION__,(int)(a1),(int)(a2),(int)(a3),0,0,0)
|
|
|
|
-# define DEBUG_FN( FLG ) if( (nDebugLvl & (FLG)) == (FLG) ) \
|
|
|
|
- printf("\r%s:line %d\n", (int)__FUNCTION__, __LINE__, 0,0,0,0);
|
|
|
|
-# define ASSERT( expr, func ) if( !( expr ) ) { \
|
|
|
|
- printf( "Assertion failed! %s:line %d %s\n", \
|
|
|
|
- (int)__FUNCTION__,__LINE__,(int)(#expr),0,0,0); \
|
|
|
|
- func }
|
|
|
|
-#else /* DEBUG */
|
|
|
|
-# define printk(...)
|
|
|
|
-# define DEBUG_ARGS0( FLG, a0 )
|
|
|
|
-# define DEBUG_ARGS1( FLG, a0, a1 )
|
|
|
|
-# define DEBUG_ARGS2( FLG, a0, a1, a2 )
|
|
|
|
-# define DEBUG_ARGS3( FLG, a0, a1, a2, a3 )
|
|
|
|
-# define DEBUG_FN( n )
|
|
|
|
-# define ASSERT(expr, func)
|
|
|
|
-#endif /* DEBUG */
|
|
|
|
-
|
|
|
|
-#define NS7520_MII_NEG_DELAY (5*CONFIG_SYS_HZ) /* in s */
|
|
|
|
-#define TX_TIMEOUT (5*CONFIG_SYS_HZ) /* in s */
|
|
|
|
-#define RX_STALL_WORKAROUND_CNT 100
|
|
|
|
-
|
|
|
|
-static int ns7520_eth_reset(void);
|
|
|
|
-
|
|
|
|
-static void ns7520_link_auto_negotiate(void);
|
|
|
|
-static void ns7520_link_update_egcr(void);
|
|
|
|
-static void ns7520_link_print_changed(void);
|
|
|
|
-
|
|
|
|
-/* the PHY stuff */
|
|
|
|
-
|
|
|
|
-static char ns7520_mii_identify_phy(void);
|
|
|
|
-static unsigned short ns7520_mii_read(unsigned short uiRegister);
|
|
|
|
-static void ns7520_mii_write(unsigned short uiRegister,
|
|
|
|
- unsigned short uiData);
|
|
|
|
-static unsigned int ns7520_mii_get_clock_divisor(unsigned int
|
|
|
|
- unMaxMDIOClk);
|
|
|
|
-static unsigned int ns7520_mii_poll_busy(void);
|
|
|
|
-
|
|
|
|
-static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
|
|
|
|
-static unsigned int uiLastLinkStatus;
|
|
|
|
-static PhyType phyDetected = PHY_NONE;
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: eth_init
|
|
|
|
- * @Return: -1 on failure otherwise 0
|
|
|
|
- * @Descr: Initializes the ethernet engine and uses either FS Forth's default
|
|
|
|
- * MAC addr or the one in environment
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-int eth_init(bd_t * pbis)
|
|
|
|
-{
|
|
|
|
- unsigned char aucMACAddr[6];
|
|
|
|
- char *pcTmp = getenv("ethaddr");
|
|
|
|
- char *pcEnd;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_INIT);
|
|
|
|
-
|
|
|
|
- /* no need to check for hardware */
|
|
|
|
-
|
|
|
|
- if (!ns7520_eth_reset())
|
|
|
|
- return -1;
|
|
|
|
-
|
|
|
|
- if (NULL == pcTmp)
|
|
|
|
- return -1;
|
|
|
|
-
|
|
|
|
- for (i = 0; i < 6; i++) {
|
|
|
|
- aucMACAddr[i] =
|
|
|
|
- pcTmp ? simple_strtoul(pcTmp, &pcEnd, 16) : 0;
|
|
|
|
- pcTmp = (*pcTmp) ? pcEnd + 1 : pcEnd;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* configure ethernet address */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_SA1) =
|
|
|
|
- aucMACAddr[5] << 8 | aucMACAddr[4];
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_SA2) =
|
|
|
|
- aucMACAddr[3] << 8 | aucMACAddr[2];
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_SA3) =
|
|
|
|
- aucMACAddr[1] << 8 | aucMACAddr[0];
|
|
|
|
-
|
|
|
|
- /* enable hardware */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN;
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_SUPP) = NS7520_ETH_SUPP_JABBER;
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC1) = NS7520_ETH_MAC1_RXEN;
|
|
|
|
-
|
|
|
|
- /* the linux kernel may give packets < 60 bytes, for example arp */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC2) = NS7520_ETH_MAC2_CRCEN |
|
|
|
|
- NS7520_ETH_MAC2_PADEN | NS7520_ETH_MAC2_HUGE;
|
|
|
|
-
|
|
|
|
- /* Broadcast/multicast allowed; if you don't set this even unicast chokes */
|
|
|
|
- /* Based on NS7520 errata documentation */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_SAFR) =
|
|
|
|
- NS7520_ETH_SAFR_BROAD | NS7520_ETH_SAFR_PRM;
|
|
|
|
-
|
|
|
|
- /* enable receive and transmit FIFO, use 10/100 Mbps MII */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) |=
|
|
|
|
- NS7520_ETH_EGCR_ETXWM_75 |
|
|
|
|
- NS7520_ETH_EGCR_ERX |
|
|
|
|
- NS7520_ETH_EGCR_ERXREG |
|
|
|
|
- NS7520_ETH_EGCR_ERXBR | NS7520_ETH_EGCR_ETX;
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: eth_send
|
|
|
|
- * @Return: -1 on timeout otherwise 1
|
|
|
|
- * @Descr: sends one frame by DMA
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-int eth_send(volatile void *pPacket, int nLen)
|
|
|
|
-{
|
|
|
|
- int i, length32, retval = 1;
|
|
|
|
- char *pa;
|
|
|
|
- unsigned int *pa32, lastp = 0, rest;
|
|
|
|
- unsigned int status;
|
|
|
|
-
|
|
|
|
- pa = (char *) pPacket;
|
|
|
|
- pa32 = (unsigned int *) pPacket;
|
|
|
|
- length32 = nLen / 4;
|
|
|
|
- rest = nLen % 4;
|
|
|
|
-
|
|
|
|
- /* make sure there's no garbage in the last word */
|
|
|
|
- switch (rest) {
|
|
|
|
- case 0:
|
|
|
|
- lastp = pa32[length32 - 1];
|
|
|
|
- length32--;
|
|
|
|
- break;
|
|
|
|
- case 1:
|
|
|
|
- lastp = pa32[length32] & 0x000000ff;
|
|
|
|
- break;
|
|
|
|
- case 2:
|
|
|
|
- lastp = pa32[length32] & 0x0000ffff;
|
|
|
|
- break;
|
|
|
|
- case 3:
|
|
|
|
- lastp = pa32[length32] & 0x00ffffff;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
|
|
|
|
- NS7520_ETH_EGSR_TXREGE)
|
|
|
|
- == 0) {
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* write to the fifo */
|
|
|
|
- for (i = 0; i < length32; i++)
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_FIFO) = pa32[i];
|
|
|
|
-
|
|
|
|
- /* the last word is written to an extra register, this
|
|
|
|
- starts the transmission */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_FIFOL) = lastp;
|
|
|
|
-
|
|
|
|
- /* Wait for it to be done */
|
|
|
|
- while ((*get_eth_reg_addr(NS7520_ETH_EGSR) & NS7520_ETH_EGSR_TXBC)
|
|
|
|
- == 0) {
|
|
|
|
- }
|
|
|
|
- status = (*get_eth_reg_addr(NS7520_ETH_ETSR));
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_TXBC; /* Clear it now */
|
|
|
|
-
|
|
|
|
- if (status & NS7520_ETH_ETSR_TXOK) {
|
|
|
|
- retval = 0; /* We're OK! */
|
|
|
|
- } else if (status & NS7520_ETH_ETSR_TXDEF) {
|
|
|
|
- printf("Deferred, we'll see.\n");
|
|
|
|
- retval = 0;
|
|
|
|
- } else if (status & NS7520_ETH_ETSR_TXAL) {
|
|
|
|
- printf("Late collision error, %d collisions.\n",
|
|
|
|
- (*get_eth_reg_addr(NS7520_ETH_ETSR)) &
|
|
|
|
- NS7520_ETH_ETSR_TXCOLC);
|
|
|
|
- } else if (status & NS7520_ETH_ETSR_TXAEC) {
|
|
|
|
- printf("Excessive collisions: %d\n",
|
|
|
|
- (*get_eth_reg_addr(NS7520_ETH_ETSR)) &
|
|
|
|
- NS7520_ETH_ETSR_TXCOLC);
|
|
|
|
- } else if (status & NS7520_ETH_ETSR_TXAED) {
|
|
|
|
- printf("Excessive deferral on xmit.\n");
|
|
|
|
- } else if (status & NS7520_ETH_ETSR_TXAUR) {
|
|
|
|
- printf("Packet underrun.\n");
|
|
|
|
- } else if (status & NS7520_ETH_ETSR_TXAJ) {
|
|
|
|
- printf("Jumbo packet error.\n");
|
|
|
|
- } else {
|
|
|
|
- printf("Error: Should never get here.\n");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return (retval);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: eth_rx
|
|
|
|
- * @Return: size of last frame in bytes or 0 if no frame available
|
|
|
|
- * @Descr: gives one frame to U-Boot which has been copied by DMA engine already
|
|
|
|
- * to NetRxPackets[ 0 ].
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-int eth_rx(void)
|
|
|
|
-{
|
|
|
|
- int i;
|
|
|
|
- unsigned short rxlen;
|
|
|
|
- unsigned short totrxlen = 0;
|
|
|
|
- unsigned int *addr;
|
|
|
|
- unsigned int rxstatus, lastrxlen;
|
|
|
|
- char *pa;
|
|
|
|
-
|
|
|
|
- /* If RXBR is 1, data block was received */
|
|
|
|
- while (((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
|
|
|
|
- NS7520_ETH_EGSR_RXBR) == NS7520_ETH_EGSR_RXBR) {
|
|
|
|
-
|
|
|
|
- /* get status register and the length of received block */
|
|
|
|
- rxstatus = *get_eth_reg_addr(NS7520_ETH_ERSR);
|
|
|
|
- rxlen = (rxstatus & NS7520_ETH_ERSR_RXSIZE) >> 16;
|
|
|
|
-
|
|
|
|
- /* clear RXBR to make fifo available */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGSR) = NS7520_ETH_EGSR_RXBR;
|
|
|
|
-
|
|
|
|
- if (rxstatus & NS7520_ETH_ERSR_ROVER) {
|
|
|
|
- printf("Receive overrun, resetting FIFO.\n");
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) &=
|
|
|
|
- ~NS7520_ETH_EGCR_ERX;
|
|
|
|
- udelay(20);
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) |=
|
|
|
|
- NS7520_ETH_EGCR_ERX;
|
|
|
|
- }
|
|
|
|
- if (rxlen == 0) {
|
|
|
|
- printf("Nothing.\n");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- addr = (unsigned int *) NetRxPackets[0];
|
|
|
|
- pa = (char *) NetRxPackets[0];
|
|
|
|
-
|
|
|
|
- /* read the fifo */
|
|
|
|
- for (i = 0; i < rxlen / 4; i++) {
|
|
|
|
- *addr = *get_eth_reg_addr(NS7520_ETH_FIFO);
|
|
|
|
- addr++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
|
|
|
|
- NS7520_ETH_EGSR_RXREGR) {
|
|
|
|
- /* RXFDB indicates wether the last word is 1,2,3 or 4 bytes long */
|
|
|
|
- lastrxlen =
|
|
|
|
- ((*get_eth_reg_addr(NS7520_ETH_EGSR)) &
|
|
|
|
- NS7520_ETH_EGSR_RXFDB_MA) >> 28;
|
|
|
|
- *addr = *get_eth_reg_addr(NS7520_ETH_FIFO);
|
|
|
|
- switch (lastrxlen) {
|
|
|
|
- case 1:
|
|
|
|
- *addr &= 0xff000000;
|
|
|
|
- break;
|
|
|
|
- case 2:
|
|
|
|
- *addr &= 0xffff0000;
|
|
|
|
- break;
|
|
|
|
- case 3:
|
|
|
|
- *addr &= 0xffffff00;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Pass the packet up to the protocol layers. */
|
|
|
|
- NetReceive(NetRxPackets[0], rxlen - 4);
|
|
|
|
- totrxlen += rxlen - 4;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return totrxlen;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: eth_halt
|
|
|
|
- * @Return: n/a
|
|
|
|
- * @Descr: stops the ethernet engine
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-void eth_halt(void)
|
|
|
|
-{
|
|
|
|
- DEBUG_FN(DEBUG_INIT);
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_RXEN;
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) &= ~(NS7520_ETH_EGCR_ERX |
|
|
|
|
- NS7520_ETH_EGCR_ERXDMA |
|
|
|
|
- NS7520_ETH_EGCR_ERXREG |
|
|
|
|
- NS7520_ETH_EGCR_ERXBR |
|
|
|
|
- NS7520_ETH_EGCR_ETX |
|
|
|
|
- NS7520_ETH_EGCR_ETXDMA);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_eth_reset
|
|
|
|
- * @Return: 0 on failure otherwise 1
|
|
|
|
- * @Descr: resets the ethernet interface and the PHY,
|
|
|
|
- * performs auto negotiation or fixed modes
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static int ns7520_eth_reset(void)
|
|
|
|
-{
|
|
|
|
- DEBUG_FN(DEBUG_MINOR);
|
|
|
|
-
|
|
|
|
- /* Reset important registers */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) = 0; /* Null it out! */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC1) &= NS7520_ETH_MAC1_SRST;
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC2) = 0;
|
|
|
|
- /* Reset MAC */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) |= NS7520_ETH_EGCR_MAC_RES;
|
|
|
|
- udelay(5);
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) &= ~NS7520_ETH_EGCR_MAC_RES;
|
|
|
|
-
|
|
|
|
- /* reset and initialize PHY */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC1) &= ~NS7520_ETH_MAC1_SRST;
|
|
|
|
-
|
|
|
|
- /* we don't support hot plugging of PHY, therefore we don't reset
|
|
|
|
- phyDetected and nPhyMaxMdioClock here. The risk is if the setting is
|
|
|
|
- incorrect the first open
|
|
|
|
- may detect the PHY correctly but succeding will fail
|
|
|
|
- For reseting the PHY and identifying we have to use the standard
|
|
|
|
- MDIO CLOCK value 2.5 MHz only after hardware reset
|
|
|
|
- After having identified the PHY we will do faster */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MCFG) =
|
|
|
|
- ns7520_mii_get_clock_divisor(nPhyMaxMdioClock);
|
|
|
|
-
|
|
|
|
- /* reset PHY */
|
|
|
|
- ns7520_mii_write(MII_BMCR, BMCR_RESET);
|
|
|
|
- ns7520_mii_write(MII_BMCR, 0);
|
|
|
|
-
|
|
|
|
- udelay(3000); /* [2] p.70 says at least 300us reset recovery time. */
|
|
|
|
-
|
|
|
|
- /* MII clock has been setup to default, ns7520_mii_identify_phy should
|
|
|
|
- work for all */
|
|
|
|
-
|
|
|
|
- if (!ns7520_mii_identify_phy()) {
|
|
|
|
- printk(KERN_ERR NS7520_DRIVER_NAME
|
|
|
|
- ": Unsupported PHY, aborting\n");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* now take the highest MDIO clock possible after detection */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MCFG) =
|
|
|
|
- ns7520_mii_get_clock_divisor(nPhyMaxMdioClock);
|
|
|
|
-
|
|
|
|
- /* PHY has been detected, so there can be no abort reason and we can
|
|
|
|
- finish initializing ethernet */
|
|
|
|
-
|
|
|
|
- uiLastLinkStatus = 0xff; /* undefined */
|
|
|
|
-
|
|
|
|
- ns7520_link_auto_negotiate();
|
|
|
|
-
|
|
|
|
- if (phyDetected == PHY_LXT971A)
|
|
|
|
- /* set LED2 to link mode */
|
|
|
|
- ns7520_mii_write(PHY_LXT971_LED_CFG,
|
|
|
|
- (PHY_LXT971_LED_CFG_LINK_ACT <<
|
|
|
|
- PHY_LXT971_LED_CFG_SHIFT_LED2) |
|
|
|
|
- (PHY_LXT971_LED_CFG_TRANSMIT <<
|
|
|
|
- PHY_LXT971_LED_CFG_SHIFT_LED1));
|
|
|
|
-
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_link_auto_negotiate
|
|
|
|
- * @Return: void
|
|
|
|
- * @Descr: performs auto-negotation of link.
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static void ns7520_link_auto_negotiate(void)
|
|
|
|
-{
|
|
|
|
- unsigned long ulStartJiffies;
|
|
|
|
- unsigned short uiStatus;
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_LINK);
|
|
|
|
-
|
|
|
|
- /* run auto-negotation */
|
|
|
|
- /* define what we are capable of */
|
|
|
|
- ns7520_mii_write(MII_ADVERTISE,
|
|
|
|
- LPA_100FULL |
|
|
|
|
- LPA_100HALF |
|
|
|
|
- LPA_10FULL |
|
|
|
|
- LPA_10HALF |
|
|
|
|
- PHY_ANLPAR_PSB_802_3);
|
|
|
|
- /* start auto-negotiation */
|
|
|
|
- ns7520_mii_write(MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
|
|
|
|
-
|
|
|
|
- /* wait for completion */
|
|
|
|
-
|
|
|
|
- ulStartJiffies = get_timer(0);
|
|
|
|
- while (get_timer(0) < ulStartJiffies + NS7520_MII_NEG_DELAY) {
|
|
|
|
- uiStatus = ns7520_mii_read(MII_BMSR);
|
|
|
|
- if ((uiStatus &
|
|
|
|
- (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) ==
|
|
|
|
- (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) {
|
|
|
|
- /* lucky we are, auto-negotiation succeeded */
|
|
|
|
- ns7520_link_print_changed();
|
|
|
|
- ns7520_link_update_egcr();
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- DEBUG_ARGS0(DEBUG_LINK, "auto-negotiation timed out\n");
|
|
|
|
- /* ignore invalid link settings */
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_link_update_egcr
|
|
|
|
- * @Return: void
|
|
|
|
- * @Descr: updates the EGCR and MAC2 link status after mode change or
|
|
|
|
- * auto-negotation
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static void ns7520_link_update_egcr(void)
|
|
|
|
-{
|
|
|
|
- unsigned int unEGCR;
|
|
|
|
- unsigned int unMAC2;
|
|
|
|
- unsigned int unIPGT;
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_LINK);
|
|
|
|
-
|
|
|
|
- unEGCR = *get_eth_reg_addr(NS7520_ETH_EGCR);
|
|
|
|
- unMAC2 = *get_eth_reg_addr(NS7520_ETH_MAC2);
|
|
|
|
- unIPGT =
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_IPGT) & ~NS7520_ETH_IPGT_IPGT;
|
|
|
|
-
|
|
|
|
- unEGCR &= ~NS7520_ETH_EGCR_EFULLD;
|
|
|
|
- unMAC2 &= ~NS7520_ETH_MAC2_FULLD;
|
|
|
|
- if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE)
|
|
|
|
- == PHY_LXT971_STAT2_DUPLEX_MODE) {
|
|
|
|
- unEGCR |= NS7520_ETH_EGCR_EFULLD;
|
|
|
|
- unMAC2 |= NS7520_ETH_MAC2_FULLD;
|
|
|
|
- unIPGT |= 0x15; /* see [1] p. 167 */
|
|
|
|
- } else
|
|
|
|
- unIPGT |= 0x12; /* see [1] p. 167 */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MAC2) = unMAC2;
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_EGCR) = unEGCR;
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_IPGT) = unIPGT;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_link_print_changed
|
|
|
|
- * @Return: void
|
|
|
|
- * @Descr: checks whether the link status has changed and if so prints
|
|
|
|
- * the new mode
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static void ns7520_link_print_changed(void)
|
|
|
|
-{
|
|
|
|
- unsigned short uiStatus;
|
|
|
|
- unsigned short uiControl;
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_LINK);
|
|
|
|
-
|
|
|
|
- uiControl = ns7520_mii_read(MII_BMCR);
|
|
|
|
-
|
|
|
|
- if ((uiControl & BMCR_ANENABLE) == BMCR_ANENABLE) {
|
|
|
|
- /* BMSR_LSTATUS is only set on autonegotiation */
|
|
|
|
- uiStatus = ns7520_mii_read(MII_BMSR);
|
|
|
|
-
|
|
|
|
- if (!(uiStatus & BMSR_LSTATUS)) {
|
|
|
|
- printk(KERN_WARNING NS7520_DRIVER_NAME
|
|
|
|
- ": link down\n");
|
|
|
|
- /* @TODO Linux: carrier_off */
|
|
|
|
- } else {
|
|
|
|
- /* @TODO Linux: carrier_on */
|
|
|
|
- if (phyDetected == PHY_LXT971A) {
|
|
|
|
- uiStatus =
|
|
|
|
- ns7520_mii_read(PHY_LXT971_STAT2);
|
|
|
|
- uiStatus &=
|
|
|
|
- (PHY_LXT971_STAT2_100BTX |
|
|
|
|
- PHY_LXT971_STAT2_DUPLEX_MODE |
|
|
|
|
- PHY_LXT971_STAT2_AUTO_NEG);
|
|
|
|
-
|
|
|
|
- /* mask out all uninteresting parts */
|
|
|
|
- }
|
|
|
|
- /* other PHYs must store there link information in
|
|
|
|
- uiStatus as PHY_LXT971 */
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- /* mode has been forced, so uiStatus should be the same as the
|
|
|
|
- last link status, enforce printing */
|
|
|
|
- uiStatus = uiLastLinkStatus;
|
|
|
|
- uiLastLinkStatus = 0xff;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (uiStatus != uiLastLinkStatus) {
|
|
|
|
- /* save current link status */
|
|
|
|
- uiLastLinkStatus = uiStatus;
|
|
|
|
-
|
|
|
|
- /* print new link status */
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO NS7520_DRIVER_NAME
|
|
|
|
- ": link mode %i Mbps %s duplex %s\n",
|
|
|
|
- (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10,
|
|
|
|
- (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" :
|
|
|
|
- "half",
|
|
|
|
- (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" :
|
|
|
|
- "");
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * the MII low level stuff
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_mii_identify_phy
|
|
|
|
- * @Return: 1 if supported PHY has been detected otherwise 0
|
|
|
|
- * @Descr: checks for supported PHY and prints the IDs.
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static char ns7520_mii_identify_phy(void)
|
|
|
|
-{
|
|
|
|
- unsigned short uiID1;
|
|
|
|
- unsigned short uiID2;
|
|
|
|
- unsigned char *szName;
|
|
|
|
- char cRes = 0;
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_MII);
|
|
|
|
-
|
|
|
|
- phyDetected = (PhyType) uiID1 = ns7520_mii_read(MII_PHYSID1);
|
|
|
|
-
|
|
|
|
- switch (phyDetected) {
|
|
|
|
- case PHY_LXT971A:
|
|
|
|
- szName = "LXT971A";
|
|
|
|
- uiID2 = ns7520_mii_read(MII_PHYSID2);
|
|
|
|
- nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK;
|
|
|
|
- cRes = 1;
|
|
|
|
- break;
|
|
|
|
- case PHY_NONE:
|
|
|
|
- default:
|
|
|
|
- /* in case uiID1 == 0 && uiID2 == 0 we may have the wrong
|
|
|
|
- address or reset sets the wrong NS7520_ETH_MCFG_CLKS */
|
|
|
|
-
|
|
|
|
- uiID2 = 0;
|
|
|
|
- szName = "unknown";
|
|
|
|
- nPhyMaxMdioClock = PHY_MDIO_MAX_CLK;
|
|
|
|
- phyDetected = PHY_NONE;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO NS7520_DRIVER_NAME
|
|
|
|
- ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName);
|
|
|
|
-
|
|
|
|
- return cRes;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_mii_read
|
|
|
|
- * @Return: the data read from PHY register uiRegister
|
|
|
|
- * @Descr: the data read may be invalid if timed out. If so, a message
|
|
|
|
- * is printed but the invalid data is returned.
|
|
|
|
- * The fixed device address is being used.
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static unsigned short ns7520_mii_read(unsigned short uiRegister)
|
|
|
|
-{
|
|
|
|
- DEBUG_FN(DEBUG_MII_LOW);
|
|
|
|
-
|
|
|
|
- /* write MII register to be read */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MADR) =
|
|
|
|
- CONFIG_PHY_ADDR << 8 | uiRegister;
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ;
|
|
|
|
-
|
|
|
|
- if (!ns7520_mii_poll_busy())
|
|
|
|
- printk(KERN_WARNING NS7520_DRIVER_NAME
|
|
|
|
- ": MII still busy in read\n");
|
|
|
|
- /* continue to read */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MCMD) = 0;
|
|
|
|
-
|
|
|
|
- return (unsigned short) (*get_eth_reg_addr(NS7520_ETH_MRDD));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_mii_write
|
|
|
|
- * @Return: nothing
|
|
|
|
- * @Descr: writes the data to the PHY register. In case of a timeout,
|
|
|
|
- * no special handling is performed but a message printed
|
|
|
|
- * The fixed device address is being used.
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static void ns7520_mii_write(unsigned short uiRegister,
|
|
|
|
- unsigned short uiData)
|
|
|
|
-{
|
|
|
|
- DEBUG_FN(DEBUG_MII_LOW);
|
|
|
|
-
|
|
|
|
- /* write MII register to be written */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MADR) =
|
|
|
|
- CONFIG_PHY_ADDR << 8 | uiRegister;
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MWTD) = uiData;
|
|
|
|
-
|
|
|
|
- if (!ns7520_mii_poll_busy()) {
|
|
|
|
- printf(KERN_WARNING NS7520_DRIVER_NAME
|
|
|
|
- ": MII still busy in write\n");
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_mii_get_clock_divisor
|
|
|
|
- * @Return: the clock divisor that should be used in NS7520_ETH_MCFG_CLKS
|
|
|
|
- * @Descr: if no clock divisor can be calculated for the
|
|
|
|
- * current SYSCLK and the maximum MDIO Clock, a warning is printed
|
|
|
|
- * and the greatest divisor is taken
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static unsigned int ns7520_mii_get_clock_divisor(unsigned int unMaxMDIOClk)
|
|
|
|
-{
|
|
|
|
- struct {
|
|
|
|
- unsigned int unSysClkDivisor;
|
|
|
|
- unsigned int unClks; /* field for NS7520_ETH_MCFG_CLKS */
|
|
|
|
- } PHYClockDivisors[] = {
|
|
|
|
- {
|
|
|
|
- 4, NS7520_ETH_MCFG_CLKS_4}, {
|
|
|
|
- 6, NS7520_ETH_MCFG_CLKS_6}, {
|
|
|
|
- 8, NS7520_ETH_MCFG_CLKS_8}, {
|
|
|
|
- 10, NS7520_ETH_MCFG_CLKS_10}, {
|
|
|
|
- 14, NS7520_ETH_MCFG_CLKS_14}, {
|
|
|
|
- 20, NS7520_ETH_MCFG_CLKS_20}, {
|
|
|
|
- 28, NS7520_ETH_MCFG_CLKS_28}
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- int nIndexSysClkDiv;
|
|
|
|
- int nArraySize =
|
|
|
|
- sizeof(PHYClockDivisors) / sizeof(PHYClockDivisors[0]);
|
|
|
|
- unsigned int unClks = NS7520_ETH_MCFG_CLKS_28; /* defaults to
|
|
|
|
- greatest div */
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_INIT);
|
|
|
|
-
|
|
|
|
- for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize;
|
|
|
|
- nIndexSysClkDiv++) {
|
|
|
|
- /* find first sysclock divisor that isn't higher than 2.5 MHz
|
|
|
|
- clock */
|
|
|
|
- if (NETARM_XTAL_FREQ /
|
|
|
|
- PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <=
|
|
|
|
- unMaxMDIOClk) {
|
|
|
|
- unClks = PHYClockDivisors[nIndexSysClkDiv].unClks;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- DEBUG_ARGS2(DEBUG_INIT,
|
|
|
|
- "Taking MDIO Clock bit mask 0x%0x for max clock %i\n",
|
|
|
|
- unClks, unMaxMDIOClk);
|
|
|
|
-
|
|
|
|
- /* return greatest divisor */
|
|
|
|
- return unClks;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/***********************************************************************
|
|
|
|
- * @Function: ns7520_mii_poll_busy
|
|
|
|
- * @Return: 0 if timed out otherwise the remaing timeout
|
|
|
|
- * @Descr: waits until the MII has completed a command or it times out
|
|
|
|
- * code may be interrupted by hard interrupts.
|
|
|
|
- * It is not checked what happens on multiple actions when
|
|
|
|
- * the first is still being busy and we timeout.
|
|
|
|
- ***********************************************************************/
|
|
|
|
-
|
|
|
|
-static unsigned int ns7520_mii_poll_busy(void)
|
|
|
|
-{
|
|
|
|
- unsigned int unTimeout = 1000;
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_MII_LOW);
|
|
|
|
-
|
|
|
|
- while (((*get_eth_reg_addr(NS7520_ETH_MIND) & NS7520_ETH_MIND_BUSY)
|
|
|
|
- == NS7520_ETH_MIND_BUSY) && unTimeout)
|
|
|
|
- unTimeout--;
|
|
|
|
-
|
|
|
|
- return unTimeout;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/* ----------------------------------------------------------------------------
|
|
|
|
- * Net+ARM ethernet MII functionality.
|
|
|
|
- */
|
|
|
|
-#if defined(CONFIG_MII)
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Maximum MII address we support
|
|
|
|
- */
|
|
|
|
-#define MII_ADDRESS_MAX (31)
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Maximum MII register address we support
|
|
|
|
- */
|
|
|
|
-#define MII_REGISTER_MAX (31)
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Ethernet MII interface return values for public functions.
|
|
|
|
- */
|
|
|
|
-enum mii_status {
|
|
|
|
- MII_STATUS_SUCCESS = 0,
|
|
|
|
- MII_STATUS_FAILURE = 1,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Read a 16-bit value from an MII register.
|
|
|
|
- */
|
|
|
|
-extern int ns7520_miiphy_read(const char *devname, unsigned char const addr,
|
|
|
|
- unsigned char const reg, unsigned short *const value)
|
|
|
|
-{
|
|
|
|
- int ret = MII_STATUS_FAILURE;
|
|
|
|
-
|
|
|
|
- /* Parameter checks */
|
|
|
|
- if (addr > MII_ADDRESS_MAX) {
|
|
|
|
- ERROR(("invalid addr, 0x%02X", addr));
|
|
|
|
- goto miiphy_read_failed_0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (reg > MII_REGISTER_MAX) {
|
|
|
|
- ERROR(("invalid reg, 0x%02X", reg));
|
|
|
|
- goto miiphy_read_failed_0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (value == NULL) {
|
|
|
|
- ERROR(("NULL value"));
|
|
|
|
- goto miiphy_read_failed_0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- DEBUG_FN(DEBUG_MII_LOW);
|
|
|
|
-
|
|
|
|
- /* write MII register to be read */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg;
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MCMD) = NS7520_ETH_MCMD_READ;
|
|
|
|
-
|
|
|
|
- if (!ns7520_mii_poll_busy())
|
|
|
|
- printk(KERN_WARNING NS7520_DRIVER_NAME
|
|
|
|
- ": MII still busy in read\n");
|
|
|
|
- /* continue to read */
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MCMD) = 0;
|
|
|
|
-
|
|
|
|
- *value = (*get_eth_reg_addr(NS7520_ETH_MRDD));
|
|
|
|
- ret = MII_STATUS_SUCCESS;
|
|
|
|
- /* Fall through */
|
|
|
|
-
|
|
|
|
- miiphy_read_failed_0:
|
|
|
|
- return (ret);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * Write a 16-bit value to an MII register.
|
|
|
|
- */
|
|
|
|
-extern int ns7520_miiphy_write(const char *devname, unsigned char const addr,
|
|
|
|
- unsigned char const reg, unsigned short const value)
|
|
|
|
-{
|
|
|
|
- int ret = MII_STATUS_FAILURE;
|
|
|
|
-
|
|
|
|
- /* Parameter checks */
|
|
|
|
- if (addr > MII_ADDRESS_MAX) {
|
|
|
|
- ERROR(("invalid addr, 0x%02X", addr));
|
|
|
|
- goto miiphy_write_failed_0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (reg > MII_REGISTER_MAX) {
|
|
|
|
- ERROR(("invalid reg, 0x%02X", reg));
|
|
|
|
- goto miiphy_write_failed_0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* write MII register to be written */
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MADR) = (addr << 8) | reg;
|
|
|
|
-
|
|
|
|
- *get_eth_reg_addr(NS7520_ETH_MWTD) = value;
|
|
|
|
-
|
|
|
|
- if (!ns7520_mii_poll_busy()) {
|
|
|
|
- printf(KERN_WARNING NS7520_DRIVER_NAME
|
|
|
|
- ": MII still busy in write\n");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ret = MII_STATUS_SUCCESS;
|
|
|
|
- /* Fall through */
|
|
|
|
-
|
|
|
|
- miiphy_write_failed_0:
|
|
|
|
- return (ret);
|
|
|
|
-}
|
|
|
|
-#endif /* defined(CONFIG_MII) */
|
|
|
|
-
|
|
|
|
-int ns7520_miiphy_initialize(bd_t *bis)
|
|
|
|
-{
|
|
|
|
-#if defined(CONFIG_MII)
|
|
|
|
- miiphy_register("ns7520phy", ns7520_miiphy_read, ns7520_miiphy_write);
|
|
|
|
-#endif
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|