| /******************************************************************************* |
| 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 <common.h> |
| #include <command.h> |
| #include <net.h> |
| #include <malloc.h> |
| |
| #if defined (MV_INCLUDE_GIG_ETH) |
| #if defined(MV_ETH_LEGACY) |
| //#include "sys/mvSysGbe.h" |
| #include "mvOs.h" |
| #include "mvSysHwConfig.h" |
| #include "eth/mvEth.h" |
| #include "gpp/mvGppRegs.h" |
| #include "eth/gbe/mvEthGbe.h" |
| #include "eth-phy/mvEthPhy.h" |
| #include "ethSwitch/mvSwitch.h" |
| #include "mvBoardEnvLib.h" |
| #include "mvSysEthApi.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]; |
| |
| mvSysEthInit(); |
| |
| for (port = 0; port < mvCtrlEthMaxPortGet(); port++) { |
| if (MV_FALSE == mvBoardIsGbEPortConnected(port)) continue; |
| |
| 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); |
| |
| mvEthPortPowerUp(port); |
| |
| MV_REG_WRITE(ETH_TX_QUEUE_COMMAND1_REG(port), 0x8); |
| mvEgigaLoad(port, name, enet_addr); |
| } |
| |
| 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_U8*)(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->index = port; |
| dev->priv = priv; |
| dev->iobase = 0; |
| dev->port = port; |
| 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->ownerId = -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; i < EGIGA_RXQ_LEN; i++) { |
| pktInfo = mvEgigaRxFill(); |
| if (pktInfo == NULL) |
| goto error; |
| |
| /* give the buffer to hal */ |
| status = mvEthPortRxDone(priv->halPriv, 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); |
| |
| /* Clear Cause registers */ |
| 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; |
| MV_U32 timeout = 0; |
| |
| 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) { |
| if (timeout++ > 100) |
| break; |
| 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->status ) );*/ |
| 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 /* legacy */ |
| #endif /* #if defined (MV_INCLUDE_GIG_ETH) */ |