/******************************************************************************* Copyright (C) Marvell International Ltd. and its affiliates ******************************************************************************** Marvell GPL License Option If you received this File from Marvell, you may opt to use, redistribute and/or modify this File in accordance with the terms and conditions of the General Public License Version 2, June 1991 (the "GPL License"), a copy of which is available along with the File in the license.txt file or by writing to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. The GPL License provides additional details about this warranty disclaimer. *******************************************************************************/ #include #include #include #include #if defined (MV_INCLUDE_GIG_ETH) #include "sys/mvSysGbe.h" #include "mvOs.h" #include "mvSysHwConfig.h" #include "eth/mvEth.h" #include "eth/gbe/mvEthGbe.h" #include "eth-phy/mvEthPhy.h" #include "ethswitch/mvSwitch.h" #include "mvBoardEnvLib.h" /* #define MV_DEBUG */ #ifdef MV_DEBUG #define DB(x) x #else #define DB(x) #endif /****************************************************** * driver internal definitions -- * ******************************************************/ /* use only tx-queue0 and rx-queue0 */ #define EGIGA_DEF_TXQ 0 #define EGIGA_DEF_RXQ 0 /* rx buffer size */ #define ETH_HLEN 14 #define WRAP (2 + ETH_HLEN + 4 + 32) /* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(extra for cache prefetch)*/ #define MTU 1500 #define RX_BUFFER_SIZE (MTU + WRAP) /* rings length */ #define EGIGA_TXQ_LEN 20 #define EGIGA_RXQ_LEN 20 typedef struct _egigaPriv { int port; MV_VOID *halPriv; MV_U32 rxqCount; MV_U32 txqCount; MV_BOOL devInit; } egigaPriv; /****************************************************** * functions prototype -- * ******************************************************/ static int mvEgigaLoad( int port, char *name, char *enet_addr ); static int mvEgigaInit( struct eth_device *dev, bd_t *p ); static int mvEgigaHalt( struct eth_device *dev ); static int mvEgigaTx( struct eth_device *dev, volatile MV_VOID *packet, int len ); static int mvEgigaRx( struct eth_device *dev ); static MV_PKT_INFO* mvEgigaRxFill(MV_VOID); /*********************************************************** * mv_eth_initialize -- * * main driver initialization. loading the interfaces. * ***********************************************************/ int mv_eth_initialize( bd_t *bis ) { int port; MV_8 *enet_addr; MV_8 name[NAMESIZE+1]; MV_8 enetvar[9]; MV_8 ethPortNum = mvCtrlEthMaxPortGet(); MV_U32 ctrlId = mvCtrlModelGet(); mvEthInit(); /* load interface(s) */ for( port = BOARD_ETH_START_PORT_NUM; port < ethPortNum; port++ ) { if (MV_FALSE == mvCtrlPwrClckGet(ETH_GIG_UNIT_ID, port)) continue; /* interface name */ sprintf( name, "egiga%d", port ); /* interface MAC addr extract */ sprintf( enetvar, port ? "eth%daddr" : "ethaddr", port ); enet_addr = getenv( enetvar ); if ( (MV_78100_DEV_ID == ctrlId) || (MV_78200_DEV_ID == ctrlId) || (MV_6281_DEV_ID == ctrlId) || (MV_6192_DEV_ID == ctrlId) || (MV_6190_DEV_ID == ctrlId) || (MV_6180_DEV_ID == ctrlId)) mvEthPortPowerUp(port); MV_REG_WRITE(ETH_TX_QUEUE_COMMAND1_REG(port), 0x8); mvEgigaLoad( port, name, enet_addr ); } #ifdef RD_MV78XX0_AMC /* Light on RDY led */ mvEthPhyRegWrite(0, 0xb, 0x19, 0xc00); #endif return 0; } /*********************************************************** * mvEgigaLoad -- * * load a network interface into uboot network core. * * initialize sw structures e.g. private, rings, etc. * ***********************************************************/ static int mvEgigaLoad( int port, char *name, char *enet_addr ) { struct eth_device *dev = NULL; egigaPriv *priv = NULL; ETH_PORT_CTRL dummy_port_handle; DB( printf( "%s: %s load - ", __FUNCTION__, name ) ); dev = malloc( sizeof(struct eth_device) ); priv = malloc( sizeof(egigaPriv) ); if( !dev ) { DB( printf( "%s: %s falied to alloc eth_device (error)\n", __FUNCTION__, name ) ); goto error; } if( !priv ) { DB( printf( "%s: %s falied to alloc egiga_priv (error)\n", __FUNCTION__, name ) ); goto error; } memset( priv, 0, sizeof(egigaPriv) ); /* init device methods */ memcpy( dev->name, name, NAMESIZE ); mvMacStrToHex(enet_addr, (MV_8 *)(dev->enetaddr)); /* set MAC addres even if port was not used yet. */ dummy_port_handle.portNo = port; mvEthMacAddrSet( &dummy_port_handle, dev->enetaddr, EGIGA_DEF_RXQ); dev->init = (void *)mvEgigaInit; dev->halt = (void *)mvEgigaHalt; dev->send = (void *)mvEgigaTx; dev->recv = (void *)mvEgigaRx; dev->priv = priv; dev->iobase = 0; priv->port = port; /* register the interface */ eth_register(dev); DB( printf( "%s: %s load ok\n", __FUNCTION__, name ) ); return 0; error: printf( "%s: %s load failed\n", __FUNCTION__, name ); if( priv ) free( dev->priv ); if( dev ) free( dev ); return -1; } static MV_PKT_INFO* mvEgigaRxFill(MV_VOID) { MV_BUF_INFO *pBufInfo; MV_PKT_INFO *pPktInfo; MV_U8 *buf = (MV_U8 *)memalign( 32, RX_BUFFER_SIZE ); /* align on 32B */ if( !buf ) { DB(printf("failed to alloc buffer\n")); return NULL; } if( ((MV_U32)buf) & 0xf ) printf( "un-align rx buffer %x\n", (MV_U32)buf ); pPktInfo = malloc(sizeof(MV_PKT_INFO)); if (pPktInfo == NULL) { printf("Error: cannot allocate memory for pktInfo\n"); free(buf); return NULL; } pBufInfo = malloc(sizeof(MV_BUF_INFO)); if (pBufInfo == NULL) { printf("Error: cannot allocate memory for bufInfo\n"); free(buf); free(pPktInfo); return NULL; } pBufInfo->bufPhysAddr = mvOsIoVirtToPhy(NULL, buf); pBufInfo->bufVirtPtr = buf; pBufInfo->bufSize = RX_BUFFER_SIZE; pBufInfo->dataSize = 0; pPktInfo->osInfo = (MV_ULONG)buf; pPktInfo->pFrags = pBufInfo; pPktInfo->pktSize = RX_BUFFER_SIZE; /* how much to invalidate */ pPktInfo->numFrags = 1; pPktInfo->status = 0; pPktInfo->srcIdx = -1; return pPktInfo; } unsigned int egiga_init=0; static int mvEgigaInit( struct eth_device *dev, bd_t *p ) { egigaPriv *priv = dev->priv; MV_ETH_PORT_INIT halInitStruct; MV_PKT_INFO *pktInfo; MV_STATUS status; int i; DB( printf( "%s: %s init - ", __FUNCTION__, dev->name ) ); /* egiga not ready */ DB( printf ("mvBoardPhyAddrGet()=0x%x , priv->port =0x%x\n",mvBoardPhyAddrGet(priv->port),priv->port)); /* If speed is not auto then link is force */ if (BOARD_MAC_SPEED_AUTO == mvBoardMacSpeedGet(priv->port)) { /* Check Link status on phy */ if( mvEthPhyCheckLink( mvBoardPhyAddrGet(priv->port) ) == MV_FALSE ) { printf( "%s no link\n", dev->name ); return 0; } else DB( printf( "link up\n" ) ); } egiga_init = 1; /* init the hal -- create internal port control structure and descriptor rings, */ /* open address decode windows, disable rx and tx operations. mask interrupts. */ halInitStruct.maxRxPktSize = RX_BUFFER_SIZE; halInitStruct.rxDefQ = EGIGA_DEF_RXQ; halInitStruct.txDescrNum[0] = EGIGA_TXQ_LEN; halInitStruct.rxDescrNum[0] = EGIGA_RXQ_LEN; halInitStruct.osHandle = NULL; priv->halPriv = mvEthPortInit( priv->port, &halInitStruct ); if( !priv->halPriv ) { DB( printf( "falied to init eth port (error)\n" ) ); goto error; } /* set new addr in hw */ if( mvEthMacAddrSet( priv->halPriv, dev->enetaddr, EGIGA_DEF_RXQ) != MV_OK ) { printf("%s: ethSetMacAddr failed\n", dev->name ); goto error; } priv->devInit = MV_TRUE; /* fill rx ring with buffers */ for( i=0; ihalPriv, EGIGA_DEF_RXQ, pktInfo ); if( status == MV_OK ) { priv->rxqCount++; } else if( status == MV_FULL ) { /* the ring is full */ priv->rxqCount++; DB( printf( "ring full\n" ) ); break; } else { printf( "error\n" ); goto error; } } #ifdef MV_DEBUG ethPortQueues(priv->port, EGIGA_DEF_RXQ, EGIGA_DEF_TXQ, 1); printf("%s : after calling ethPortQueues\n",__FUNCTION__); #endif /* start the hal - rx/tx activity */ /* Check if link is up for 2 Sec */ for (i = 1; i < 100 ; i ++) { status = mvEthPortEnable( priv->halPriv ); if (status == MV_OK) break; mvOsDelay(20); } if( status != MV_OK ) { printf( "%s: %s mvEthPortEnable failed (error)\n", __FUNCTION__, dev->name ); goto error; } #ifdef MV_DEBUG ethRegs(priv->port); ethPortRegs(priv->port); ethPortStatus(priv->port); ethPortQueues(priv->port, EGIGA_DEF_RXQ, -1/*EGIGA_DEF_TXQ*/, 0); #endif DB( printf( "%s: %s complete ok\n", __FUNCTION__, dev->name ) ); return 1; error: if( priv->devInit ) mvEgigaHalt( dev ); printf( "%s: %s failed\n", __FUNCTION__, dev->name ); return 0; } static int mvEgigaHalt( struct eth_device *dev ) { egigaPriv *priv = dev->priv; MV_PKT_INFO *pktInfo; DB( printf( "%s: %s halt - ", __FUNCTION__, dev->name ) ); if( priv->devInit == MV_TRUE ) { /* stop the port activity, mask all interrupts */ if( mvEthPortDisable( priv->halPriv ) != MV_OK ) printf( "mvEthPortDisable failed (error)\n" ); /* free the buffs in the rx ring */ while( (pktInfo = mvEthPortForceRx( priv->halPriv, EGIGA_DEF_RXQ )) != NULL ) { priv->rxqCount--; if( pktInfo->osInfo ) free( (void *)pktInfo->osInfo ); else printf( "mvEthPortForceRx failed (error)\n" ); if( pktInfo->pFrags ) free( (void *)pktInfo->pFrags ); else printf( "mvEthPortForceRx failed (error)\n" ); free( (void *)pktInfo ); } /* Clear Cause registers (must come before mvEthPortFinish) */ MV_REG_WRITE(ETH_INTR_CAUSE_REG(((ETH_PORT_CTRL*)(priv->halPriv))->portNo),0); MV_REG_WRITE(ETH_INTR_CAUSE_EXT_REG(((ETH_PORT_CTRL*)(priv->halPriv))->portNo),0); mvEthPortFinish( priv->halPriv ); priv->devInit = MV_FALSE; } egiga_init = 0; DB( printf( "%s: %s complete\n", __FUNCTION__, dev->name ) ); return 0; } static int mvEgigaTx( struct eth_device *dev, volatile void *buf, int len ) { egigaPriv *priv = dev->priv; MV_BUF_INFO bufInfo; MV_PKT_INFO pktInfo; MV_PKT_INFO *pPktInfo; MV_STATUS status; DB( printf( "mvEgigaTx start\n" ) ); /* if no link exist */ if(!egiga_init) return 0; pktInfo.osInfo = (MV_ULONG)0x44CAFE44; pktInfo.pktSize = len; pktInfo.pFrags = &bufInfo; pktInfo.status = 0; pktInfo.numFrags = 1; bufInfo.bufVirtPtr = (MV_U8*)buf; bufInfo.bufPhysAddr = mvOsIoVirtToPhy(NULL, buf); bufInfo.dataSize = len; /* send the packet */ status = mvEthPortTx( priv->halPriv, EGIGA_DEF_TXQ, &pktInfo ); if( status != MV_OK ) { if( status == MV_NO_RESOURCE ) DB( printf( "ring is full (error)\n" ) ); else if( status == MV_ERROR ) printf( "error\n" ); else printf( "unrecognize status (error) ethPortSend\n" ); goto error; } else DB( printf( "ok\n" ) ); priv->txqCount++; /* release the transmitted packet(s) */ while( 1 ) { DB( printf( "%s: %s tx-done - ", __FUNCTION__, dev->name ) ); /* get a packet */ pPktInfo = mvEthPortTxDone( priv->halPriv, EGIGA_DEF_TXQ); if( pPktInfo != NULL ) { priv->txqCount--; /* validate skb */ if( (pPktInfo != &pktInfo) || (pPktInfo->osInfo != 0x44CAFE44 ) ) { printf( "error\n" ); goto error; } /* handle tx error */ if( pPktInfo->status & (ETH_ERROR_SUMMARY_BIT) ) { printf( "bad status (error)\n" ); goto error; } DB( printf( "ok\n" ) ); break; } else DB(printf( "NULL pPktInfo\n" )); } DB( printf( "%s: %s complete ok\n", __FUNCTION__, dev->name ) ); return 0; error: printf( "%s: %s failed\n", __FUNCTION__, dev->name ); return 1; } static int mvEgigaRx( struct eth_device *dev ) { egigaPriv* priv = dev->priv; MV_PKT_INFO *pktInfo; MV_STATUS status; /* if no link exist */ if(!egiga_init) return 0; while( 1 ) { /* get rx packet from hal */ pktInfo = mvEthPortRx( priv->halPriv, EGIGA_DEF_RXQ); if( pktInfo != NULL ) { /*DB( printf( "good rx\n" ) );*/ priv->rxqCount--; /* check rx error status */ if( pktInfo->status & (ETH_ERROR_SUMMARY_MASK) ) { MV_U32 err = pktInfo->status & ETH_RX_ERROR_CODE_MASK; /*DB( printf( "bad rx status %08x, ", (MV_U32)pktInfo->cmdSts ) );*/ if( err == ETH_RX_RESOURCE_ERROR ) DB( printf( "(resource error)" ) ); else if( err == ETH_RX_MAX_FRAME_LEN_ERROR ) DB( printf( "(max frame length error)" ) ); else if( err == ETH_RX_OVERRUN_ERROR ) DB( printf( "(overrun error)" ) ); else if( err == ETH_RX_CRC_ERROR ) DB( printf( "(crc error)" ) ); else { DB( printf( "(unknown error)" ) ); goto error; } DB( printf( "\n" ) ); } else { DB( printf( "%s: %s calling NetRecieve ", __FUNCTION__, dev->name) ); DB( printf( "%s: calling NetRecieve pkInfo = 0x%x\n", __FUNCTION__, pktInfo) ); DB( printf( "%s: calling NetRecieve osInfo = 0x%x\n", __FUNCTION__, pktInfo->osInfo) ); DB( printf( "%s: calling NetRecieve pktSize = 0x%x\n", __FUNCTION__, pktInfo->pFrags->dataSize) ); /* good rx - push the packet up (skip on two first empty bytes) */ NetReceive( ((MV_U8 *)pktInfo->osInfo) + 2, (int)pktInfo->pFrags->dataSize); } DB( printf( "%s: %s refill rx buffer - ", __FUNCTION__, dev->name) ); /* give the buffer back to hal (re-init the buffer address) */ pktInfo->pktSize = RX_BUFFER_SIZE; /* how much to invalidate */ status = mvEthPortRxDone( priv->halPriv, EGIGA_DEF_RXQ, pktInfo ); if( status == MV_OK ) { priv->rxqCount++; } else if( status == MV_FULL ) { /* this buffer made the ring full */ priv->rxqCount++; DB( printf( "ring full\n" ) ); break; } else { printf( "error\n" ); goto error; } } else { /* no more rx packets ready */ /* DB( printf( "no more work\n" ) ); */ break; } } /*DB( printf( "%s: %s complete ok\n", __FUNCTION__, dev->name ) );*/ return 0; error: DB( printf( "%s: %s failed\n", __FUNCTION__, dev->name ) ); return 1; } #endif /* #if defined (MV_INCLUDE_GIG_ETH) */