Tool/software:
Hi:
1) When our device communicates with a PLC through a network port, there is no problem with normal communication. However, if the network cable is unplugged or the PLC is powered off, and we wait for about a minute to restore it, the communication packet between the device and the PLC will be disrupted;
2) There are two messages in the attachment, one is the message that other products and this PLC can recover normally (GP4000), and the other is the message that our product and this PLC cannot recover normally after communication is restored. (192.168.1.1 corresponds to the address of the PLC, and the upper layer application processing of the two products uses the same WindRiver vxworks system, only the lower layer CPU is different)
3) From the confused messages, it can be seen that when reconnecting, the HMI sent three consecutive messages to the PLC with almost no time interval, and the subsequent PLC replies were confused.
I would like to know if there are any relevant configurations for the network port driver to not send cached messages when the link is disconnected in response to this situation,I have attached the code file for our current network port driver in the attachment.
/* vxbCpswEnd.c - TI 3 port gigabit switch VxBus END driver */ /* * Copyright (c) 2011-2016 Wind River Systems, Inc. * * The right to copy, distribute, modify or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. */ /* DESCRIPTION This module implements an END driver for the TI 3 port gigabit switch (CPSW) network interface. The TI CPSW is a layer 2 ethernet switch. The TI CPSW combines two(maybe more in future) 10/100/1000 MAC ports and a CPU(host) port. This driver doesn't support CPSW operating in switch mode. It will configure the CPSW to operate in independent port mode by disabling address learning on the all GMAC ports and constructing the MAC address look-up table manually during intialization. To VxBus, each cpsw port will has a device instance and each port has private device contexts. Also each port can be loaded/unloaded independently. This driver supports promisc mode by setting ALE (Address Lookup Engine) into bypass mode. In bypass mode, all packets received will be forwarded only to host port and ingress checking is ignored. Because there are two ethernet port inside the CPSW, so there are two END objects showing up when calling ifconfig shell command and because ALE is a shared resouce so if one port is put into promisc mode then the other will also be put into promisc mode automatically, but interface capability flags don't change automactically. For example, calling: \cs ifconfig "cpsw0 inet promisc" \ce will put both cpsw0 and cpsw1 into promisc mode, but only cpsw0's capability flags will show: \cs UP RUNNING SIMPLEX BROADCAST MULTICAST PROMISC ALLMULTI \ce cpsw1's capability flags will not be changed, the following command must be called for cpsw1 to show up correctly: \cs ifconfig "cpsw1 inet promisc" \ce This same is true when removing promisc mode. BOARD LAYOUT The network interfaces are internal to the CPU. All configurations are jumperless. See target.ref for connector locations. EXTERNAL INTERFACE The driver provides the standard VxBus external interface, vxbCpswEndRegister(). This function registers the driver with the VxBus subsystem, and instances will be created as needed. Since this is a processor local bus device, each device instance must be specified in the hwconf.c file in a BSP. The hwconf entry must specify the following parameters (NOTE: this driver does require many sub-module register offset specified as the offfset may be different on other boards, eg. on TI DM647/DM648 the offset of various sub-modules and port number assignments are different from this one): \is \i <cpswBase> Specifies the base address where Network control module registers are mapped into the host's address space. All register offsets are computed relative to this address \i <portOffset> Specifies the offset of host port register set. \i <cpdmaOffset> Specifies the offset of DMA register set. \i <statsOffset> Specifies the offset of statictics register set. \i <aleOffset> Specifies the offset of ALE register set. \i <mdioOffset> Specifies the offset of MDIO register set. \i <wrOffset> Specifies the offset of WR register set. \i <cpdmaHdpOffset> Specifies the offset of CPDMA register set. \i <workMode> Specifies word mode of cpsw, only independent port is supportted. \i <sysGetChipVer> Speficies the function that returns SoC chip version. This information is used to set different features of different SoC. \i <phyAddr> Specifies the phy address of port. \i <portIndex> Specifies the port index of port, the CPU(host) port which connects to CPU is 0, other ports start from 1. \i <portOffset> Specifies the offset of PORT register set. \i <gmacOffset> Specifies the offset of GMAC register set. \i <switchData> Specifies the address of switch common resource, all the ports share same switchData. \ie An example hwconf entry is shown below: \cs LOCAL CPSW_SWITCH_DATA cpswSwitchData = { AM335X_EMAC0_BASE , /@ cpswBase @/ 0x108 , /@ portOffset @/ 0x800 , /@ cpdmaOffset @/ 0x900 , /@ statsOffset @/ 0xd00 , /@ aleOffset @/ 0x1000 , /@ mdioOffset @/ 0x1200 , /@ wrOffset @/ 0x200 , /@ cpdmaHdpOffset @/ CPSW_MODE_INDEPENDENT_PORT , /@ work mode @/ sysGetChipVer , /@ chipVersion function @/ }; struct hcfResource am335xCpsw0Resources[] = { { "phyAddr", HCF_RES_INT, {(void *)0} }, { "portIndex", HCF_RES_INT, {(void *)1} }, { "portOffset", HCF_RES_INT, {(void *)0x208} }, { "gmacOffset", HCF_RES_INT, {(void *)0xd80} }, { "switchData", HCF_RES_ADDR, {(void *)&cpswSwitchData} }, }; #define am335xCpsw0Num NELEMENTS(am335xCpsw0Resources) struct hcfResource am335xCpsw1Resources[] = { { "phyAddr", HCF_RES_INT, {(void *)1} }, { "portIndex", HCF_RES_INT, {(void *)2} }, { "portOffset", HCF_RES_INT, {(void *)0x308} }, { "gmacOffset", HCF_RES_INT, {(void *)0xdc0} }, { "switchData", HCF_RES_ADDR, {(void *)&cpswSwitchData} }, }; #define am335xCpsw1Num NELEMENTS(am335xCpsw1Resources) \ce \cs This driver requires one external support function from the BSP. \ce \is \i sysNetMacNVRamAddrGet() \ie \cs STATUS sysNetMacNVRamAddrGet (ifName, ifUnit, ifMacAddr, ifMacAddrLen) \ce \cs This routine queries the BSP to provide the ethernet address for a given MAC. \ce Note: This driver only supports dual emac mode, switch mode is not supported. RESTRICTIONS SEE ALSO: VxBus, miiBus, ifLib */ #include <vxWorks.h> #include <string.h> #include <intLib.h> #include <stdio.h> #include <logLib.h> #include <muxLib.h> #include <netLib.h> #include <netBufLib.h> #include <semLib.h> #include <sysLib.h> #include <vxBusLib.h> #include <wdLib.h> #include <etherMultiLib.h> #include <end.h> #include "errno.h" #define END_MACROS #include <endLib.h> #include <endMedia.h> #include <cacheLib.h> #include <spinLockLib.h> #include <vxAtomicLib.h> #include <hwif/vxbus/vxBus.h> #include <hwif/vxbus/hwConf.h> #include <hwif/util/vxbDmaBufLib.h> #include <hwif/util/vxbParamSys.h> #include <../src/hwif/h/mii/miiBus.h> #include <../src/hwif/h/vxbus/vxbAccess.h> #include <../src/hwif/h/hEnd/hEnd.h> #include "vxbCpswEnd.h" #include "errno.h" #include "sysNet.h" #include "Dp83822Phy.h" #include "LanExtFilter.h" #include "am335xEndDrv.h" #undef CPSW_DBG #ifdef CPSW_DBG /* NOTE: to use kprintf, INCLUDE_DEBUG_KPRINTF should be included */ #include <private/kwriteLibP.h> #define DBG_MSG(...) \ (*_func_kprintf)("%s,%d, ", __FUNCTION__, __LINE__); \ (*_func_kprintf)(__VA_ARGS__) #else #define DBG_MSG(...) #endif /* CPSW_DBG */ #undef CPSWDRV_DEBUG #ifdef CPSWDRV_DEBUG #include "usrLib.h" /* d()-display memory */ #define CPSWDRV_DEBUG_OFF 0x0000 #define CPSWDRV_DEBUG_RX 0x0001 #define CPSWDRV_DEBUG_TX 0x0002 #define CPSWDRV_DEBUG_INT 0x0004 #define CPSWDRV_DEBUG_POLL (CPSWDRV_DEBUG_POLL_RX | CPSWDRV_DEBUG_POLL_TX) #define CPSWDRV_DEBUG_POLL_RX 0x0008 #define CPSWDRV_DEBUG_POLL_TX 0x0010 #define CPSWDRV_DEBUG_LOAD 0x0020 #define CPSWDRV_DEBUG_IOCTL 0x0040 #define CPSWDRV_DEBUG_TRACE 0x0080 #define CPSWDRV_DEBUG_INFO 0x0100 #define CPSWDRV_DEBUG_DUMP 0x4000 #define CPSWDRV_DEBUG_ERROR 0x8000 int cpswDbg = 0xFFFF; /*CPSWDRV_DEBUG_OFF;*/ /* This one is specifically for use in the ISR */ #define CPSWDRV_LOGMSG(FLG, X0, X1, X2, X3, X4, X5, X6) \ do { \ if (cpswDbg & FLG) \ logMsg(X0, X1, X2, X3, X4, X5, X6); \ } while (0) #else /* DRV_DEBUG */ #define CPSWDRV_LOGMSG(FLG, X0, X1, X2, X3, X4, X5, X6) #endif /* DRV_DEBUG */ /* accessor definitions */ #define CPSW_BAR(pSwCtrl) pSwCtrl->cpswBase #define CPSW_DMA_BAR(pSwCtrl) pSwCtrl->cpdmaOffset #define ECSR_READ_4(pSwCtrl, addr) \ *(volatile UINT32 *)((UINT32)CPSW_BAR(pSwCtrl) + (addr)) #define ECSR_WRITE_4(pSwCtrl, addr, data) \ *(volatile UINT32 *)((UINT32)CPSW_BAR(pSwCtrl) + (addr)) = (data) #define CSR_SET_BIT(pSwCtrl, offset, val) \ ECSR_WRITE_4(pSwCtrl, offset, ECSR_READ_4(pSwCtrl, offset) | (val)) #define CSR_CLR_BIT(pSwCtrl, offset, val) \ ECSR_WRITE_4(pSwCtrl, offset, ECSR_READ_4(pSwCtrl, offset) & ~(val)) /* DMA manipulation */ #define CPDMA_REG_READ(pSwCtrl, addr) \ *(volatile UINT32 *)((UINT32)CPSW_BAR(pSwCtrl) + \ (UINT32)CPSW_DMA_BAR(pSwCtrl) + (addr)) #define CPDMA_REG_WRITE(pSwCtrl, addr, data) \ *(volatile UINT32 *)((UINT32)CPSW_BAR(pSwCtrl) + \ (UINT32)CPSW_DMA_BAR(pSwCtrl) + (addr)) = (data) #define CPDMA_TX_EN(pSwCtrl) \ ECSR_WRITE_4(pSwCtrl, CPSW_DMA_BAR(pSwCtrl) + CPDMA_TX_CONTROL, 0x1) #define CPDMA_RX_EN(pSwCtrl) \ ECSR_WRITE_4(pSwCtrl, CPSW_DMA_BAR(pSwCtrl) + CPDMA_RX_CONTROL, 0x1) #define CPDMA_RX_DIS(pSwCtrl) \ ECSR_WRITE_4(pSwCtrl, CPSW_DMA_BAR(pSwCtrl) + CPDMA_RX_CONTROL, 0x0) #define CPDMA_TX_DIS(pSwCtrl) \ ECSR_WRITE_4(pSwCtrl, CPSW_DMA_BAR(pSwCtrl) + CPDMA_TX_CONTROL, 0x0) LOCAL char * txChanErrMsg[] = { "NO error", "SOP errpr", "Ownership bit not set in SOP buffer", "Zero next buffer descriptor pointer without EOP", "Zero buffer pointer", "Zero buffer length", "packet length error" }; LOCAL char * rxChanErrMsg[] = { "NO error", "reserved", "Ownership bit not set in input buffer", "reserved", "Zero buffer pointer", "Zero buffer on non-SOP descriptor", "SOP buffer length not greater than offset" }; /* common resource which is shared by all port */ LOCAL CPSW_SW_CTRL *pSwCtrl = NULL; LOCAL short G_cpsw0IpDetectPermission=0; /* 0: enable, 1:disable */ LOCAL short G_cpsw1IpDetectPermission=0; /* 0: enable, 1:disable */ LOCAL LanPktFilterF G_cpswIpdetect; /* Filter funtion */ SEM_ID GnSemId_cpswIpDetect = (SEM_ID) NULL; /* Semaphore for IP detect */ static BOOL gnLBTflag2 = FALSE; /* LoopBack Test flag */ #define CPSW_TIMEOUT 1000 /* import functions */ IMPORT FUNCPTR _func_m2PollStatsIfPoll; IMPORT STATUS sysNetMacNVRamAddrGet(char *, int, UINT8 *, int); IMPORT void sysUsDelay(int); /* VxBus interfaces */ LOCAL void cpswInstInit( VXB_DEVICE_ID); LOCAL void cpswInstInit2( VXB_DEVICE_ID); LOCAL void cpswInstConnect( VXB_DEVICE_ID); LOCAL STATUS cpswInstUnlink( VXB_DEVICE_ID, void *); LOCAL void cpswMuxConnect( VXB_DEVICE_ID, void *); STATUS cpswPhyRead( VXB_DEVICE_ID, UINT8, UINT8, UINT16 *); STATUS cpswPhyWrite( VXB_DEVICE_ID, UINT8, UINT8, UINT16); LOCAL STATUS cpswLinkUpdate( VXB_DEVICE_ID); /* driver utility functions */ LOCAL struct drvBusFuncs cpswFuncs = { cpswInstInit, /* devInstanceInit */ cpswInstInit2, /* devInstanceInit2 */ cpswInstConnect /* devConnect */ }; LOCAL struct vxbDeviceMethod cpswMethods[] = { DEVMETHOD(miiRead, cpswPhyRead), DEVMETHOD(miiWrite, cpswPhyWrite), DEVMETHOD(miiMediaUpdate,cpswLinkUpdate), DEVMETHOD(muxDevConnect, cpswMuxConnect), DEVMETHOD(vxbDrvUnlink, cpswInstUnlink), { 0, 0 } }; /* default queue parameters */ LOCAL HEND_RX_QUEUE_PARAM cpswRxQueueDefault = { NULL, /* jobQueId */ 0, /* priority */ 0, /* rbdNum */ 0, /* rbdTupleRatio */ 0, /* rxBufSize */ NULL, /* pBufMemBase */ 0, /* rxBufMemSize */ 0, /* rxBufMemAttributes */ NULL, /* rxBufMemFreeMethod */ NULL, /* pRxBdBase */ 0, /* rxBdMemSize */ 0, /* rxBdMemAttributes */ NULL /* rxBdMemFreeMethod */ }; LOCAL HEND_TX_QUEUE_PARAM cpswTxQueueDefault = { NULL, /* jobQueId */ 0, /* priority */ 0, /* tbdNum */ 0, /* allowedFrags */ NULL, /* pTxBdBase */ 0, /* txBdMemSize */ 0, /* txBdMemAttributes */ NULL /* txBdMemFreeMethod */ }; LOCAL VXB_PARAMETERS cpswParamDefaults[] = { { "rxQueue00", VXB_PARAM_POINTER, { (void *) &cpswRxQueueDefault } }, { "txQueue00", VXB_PARAM_POINTER, { (void *) &cpswTxQueueDefault } }, { NULL, VXB_PARAM_END_OF_LIST, { NULL } } }; LOCAL struct vxbDevRegInfo cpswDevPlbRegistration = { NULL, /* pNext */ VXB_DEVID_DEVICE, /* devID */ VXB_BUSID_PLB, /* busID = PLB */ VXB_VER_4_0_0, /* VxBus Version */ CPSW_NAME, /* drvName */ &cpswFuncs, /* pDrvBusFuncs */ cpswMethods, /* pMethods */ NULL, /* devProbe */ cpswParamDefaults /* pParamDefaults */ }; /* END functions */ END_OBJ *cpswEndLoad(char *, void *); LOCAL STATUS cpswEndUnload(END_OBJ *); LOCAL int cpswEndIoctl(END_OBJ *, int, caddr_t); LOCAL STATUS cpswEndMCastAddrAdd(END_OBJ *, char *); LOCAL STATUS cpswEndMCastAddrDel(END_OBJ *, char *); LOCAL STATUS cpswEndMCastAddrGet(END_OBJ *, MULTI_TABLE *); LOCAL STATUS cpswEndStart(END_OBJ *); LOCAL STATUS cpswEndStop(END_OBJ *); LOCAL int cpswEndEncap(CPSW_DRV_CTRL *, M_BLK_ID); LOCAL int cpswEndSend(END_OBJ *, M_BLK_ID); LOCAL STATUS cpswEndPollSend(END_OBJ *, M_BLK_ID); LOCAL int cpswEndPollReceive(END_OBJ *, M_BLK_ID); LOCAL void cpswEndRxHandle(void *); LOCAL void cpswEndTxHandle(void *); LOCAL void cpswEndMiscHandle(void *); LOCAL STATUS cpswSwitchInit(CPSW_SWITCH_DATA *); LOCAL STATUS cpswHostPortInit(CPSW_SW_CTRL *); LOCAL STATUS cpswGmacPortInit(CPSW_DRV_CTRL *); LOCAL void cpswEndTxInt(CPSW_SW_CTRL *); LOCAL void cpswEndRxInt(CPSW_SW_CTRL *); LOCAL void cpswEndMiscInt(CPSW_SW_CTRL *); LOCAL STATUS cpswSoftReset(CPSW_SW_CTRL *, UINT32, UINT32); LOCAL void cpswReset(VXB_DEVICE_ID pDev); /* CPDMA routines */ LOCAL STATUS cpswDmaInit(CPSW_SW_CTRL *); LOCAL STATUS cpswDmaChanRequest(CPSW_SW_CTRL *, BOOL, int); LOCAL STATUS cpswDmaChanRelease(CPSW_SW_CTRL *, BOOL, int); /* ALE routines */ LOCAL int cpswAleAddMultiCast(CPSW_SW_CTRL *, unsigned char *, int, int, int, int); LOCAL int cpswAleAddUniCast(CPSW_SW_CTRL *, unsigned char *, int, int, int, int); LOCAL void cpswAleRead(CPSW_SW_CTRL *, CPSW_ALE_TBL *, UINT32); LOCAL void cpswAleWrite(CPSW_SW_CTRL *, CPSW_ALE_TBL *, UINT32); LOCAL int cpswAleFind(CPSW_SW_CTRL *); LOCAL int cpswAleMatch(CPSW_SW_CTRL *, unsigned char *); LOCAL int cpswAleMatchVlan(CPSW_SW_CTRL *, int); LOCAL int cpswAleAddVlan(CPSW_SW_CTRL *, int, int, int, int, int); void cpswAleEntryShow(CPSW_ALE_TBL *); void cpswAleDump(void); LOCAL NET_FUNCS cpswNetFuncs = { cpswEndStart, /* start func. */ cpswEndStop, /* stop func. */ cpswEndUnload, /* unload func. */ cpswEndIoctl, /* ioctl func. */ cpswEndSend, /* send func. */ cpswEndMCastAddrAdd, /* multicast add func. */ cpswEndMCastAddrDel, /* multicast delete func. */ cpswEndMCastAddrGet, /* multicast get fun. */ cpswEndPollSend, /* cpswPolling send func. */ cpswEndPollReceive, /* cpswPolling receive func. */ endEtherAddressForm, /* put address info into a NET_BUFFER */ endEtherPacketDataGet, /* get pointer to data in NET_BUFFER */ endEtherPacketAddrGet /* Get packet addresses */ }; /******************************************************************************** * cpswWriteMac - write mac address. * * This function write MAC address to the serial flush NOR. * * RETURNS: OK/ERROR ********************************************************************************/ STATUS cpswWriteMac(UINT8 *pBuf, int ifUnit) /* bffer for mac address */ { /* In Kernel side, it does not allow to write MAC address */ /* This function is used for developer */ if((ifUnit == 0)||(ifUnit ==1)) { return sysNetMacAddrSet("cpsw", ifUnit, pBuf,CPSW_SFN_MAC_DATA_LEN); } else { return ERROR; } } /******************************************************************************** * cpswReadMac - read mac address. * * This function read MAC address from the serial flush NOR. * * RETURNS: OK/ERROR ********************************************************************************/ STATUS cpswReadMac(UINT8 *pBuf,int ifUnit /* (o) read buffer for MAC address */ ) { if((ifUnit == 0)||(ifUnit ==1)) { return sysEnetAddrGet(ifUnit, pBuf); } else { return ERROR; } } /******************************************************************************** * STATUS cpswCheckSum - check serial flush NOR checksum. * * This function checks the checksum in the serial flush NOR. * * RETURNS: OK/ERROR ********************************************************************************/ STATUS cpswCheckSum(int ifUnit) { UINT8 buf[MAC_ADRS_LEN]; if (cpswReadMac(buf, ifUnit)) { return (ERROR); } else { return (OK); } } /***************************************************************************** * * cpswReset - reset the controller * * This function issues a reset command to the controller and waits * for it to complete. This routine is always used to place the * controller into a known state prior to configuration. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswReset(VXB_DEVICE_ID pDev) { CPSW_DRV_CTRL *pDrvCtrl = pDev->pDrvCtrl; /* Reset the CPSW */ cpswSoftReset(pSwCtrl, CPSW_SOFT_RESET, CPSW_TIMEOUT_VAL); cpswSoftReset(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_SOFT_RESET,CPSW_TIMEOUT_VAL); cpswSoftReset(pSwCtrl, CPSW_WR_SOFTRESET, CPSW_TIMEOUT_VAL); cpswSoftReset(pSwCtrl, pSwCtrl->cpdmaOffset + CPDMA_SOFT_RESET, CPSW_TIMEOUT_VAL); } /******************************************************************************* * cpswEndPollStart - start polled mode operations * * RETURNS: OK only. *******************************************************************************/ STATUS cpswEndPollStart(CPSW_DRV_CTRL *pDrvCtrl /* device to be initialized */ ) { pDrvCtrl->cpswPolling = TRUE; /* stop tx dma channel interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_CLR, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_CLR_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); /* stop rx dma channel interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_CLR, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_CLR_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); /* * We may have been asked to enter polled mode while there are * transmissions pending. This is a problem, because the polled * transmit routine expects that the TX ring will be empty when * it's called. In order to guarantee this, we have to drain * the TX ring here. We could also just plain reset and * reinitialize the transmitter, but this is faster. */ while (pDrvCtrl->cpswTxFree < CPSW_DESC_CNT) { M_BLK_ID pMblk; volatile CPSW_DESC *desc; desc = &pDrvCtrl->cpswTxDescMem[pDrvCtrl->cpswTxQHead]; if (desc->flags & CPSW_EOQ) { ECSR_WRITE_4(pSwCtrl, CPDMA_TX_CP(pDrvCtrl->cpswTxDmaChan), desc->phys); if (desc->link) { ECSR_WRITE_4(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan), desc->link); } } while (desc->flags & CPSW_OWNERSHIP) ; pMblk = pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead]; if (pMblk != NULL) { endPoolTupleFree(pMblk); pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead] = NULL; } pDrvCtrl->cpswTxFree++; CPSW_DESC_INC(pDrvCtrl->cpswTxQHead, CPSW_DESC_CNT); } return ( OK); } /******************************************************************************* * cpswEndPollStop - start polled mode operations * * This function start polling mode. * * RETURNS: OK only. *******************************************************************************/ STATUS cpswEndPollStop(CPSW_DRV_CTRL *pDrvCtrl /* device to be initialized */ ) { pDrvCtrl->cpswPolling = FALSE; /* enable relative tx channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_SET, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); /* enable relative rx channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_SET, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); return ( OK); } /******************************************************************************** * STATUS cpswProcLbTest - LoopBack mode test * * This function tests the sending and receiving of the packet in the loop back mode. * * RETURNS: OK/ERROR ********************************************************************************/ STATUS cpswProcLbTest(CPSW_DRV_CTRL *pDrvCtrl){ int Loop1, Loop2; M_BLK_ID pMblk= NULL; M_BLK_ID pRMblk= NULL; char *SendTestData = NULL; int rtnVal = OK; STATUS result; if (pDrvCtrl == NULL) { return (ERROR); } SendTestData = malloc(CPSW_LPBK_TEST_LEN); if (SendTestData == NULL) { rtnVal = ERROR; return (rtnVal); } /******************************** * create send data. Data length is CPSW_LPBK_TEST_LEN(default 1514). * Sending data is flowing : * [dist address] * 0xff 0xff 0xff 0xff 0xff 0xff * * [source address] * 0x06 0x07 0x08 0x09 0x0a 0x0b * * [length type field] * 0x00 0x00 * * [data field] * 0xa5 0xa5 ............ ********************************/ /* set distination address */ for (Loop1 = 0; Loop1 < 6; Loop1++) { SendTestData[Loop1] = (UINT8) 0xFF; } /* set source address */ for (Loop1 = 6; Loop1 < 12; Loop1++) { SendTestData[Loop1] = Loop1; } /*Set length type field */ SendTestData[12] = 0x00; SendTestData[13] = 0x00; /* fill data field */ for (Loop1 = 14; Loop1 < CPSW_LPBK_TEST_LEN; Loop1++) { SendTestData[Loop1] = CPSW_LPBK_TEST_DAT; } cpswEndPollStart(pDrvCtrl); /* if Rx Descritpor not empty */ if (vxAtomicCas(&pDrvCtrl->cpswRxIntPend, FALSE, TRUE) == TRUE) { /* post the rx done job to task */ (void) jobQueuePost(pDrvCtrl->cpswJobQueue, &pDrvCtrl->cpswRxQJob); } /* Copy data from DMA buffer to the cluster */ pMblk = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool); if (pMblk == NULL) { rtnVal = ERROR; logMsg("fail to get buffer\n", 0, 0, 0, 0, 0, 0); goto error; } pMblk->mBlkHdr.mData = SendTestData; pMblk->mBlkHdr.mLen = CPSW_LPBK_TEST_LEN; pMblk->mBlkHdr.mFlags |= (M_PKTHDR | M_EXT); pMblk->mBlkPktHdr.len = CPSW_LPBK_TEST_LEN; pRMblk = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool); if (pRMblk == NULL) { rtnVal = ERROR; goto error; } pRMblk->mBlkHdr.mFlags |= M_EXT; pRMblk->m_len = pRMblk->m_pkthdr.len = CPSW_CLSIZE; pRMblk->m_next = NULL; for (Loop1 = 0; Loop1 < CPSW_LPBK_TEST_CNT; Loop1++) { gnLBTflag2 = TRUE; /* send data */ result = cpswEndPollSend(&pDrvCtrl->cpswEndObj, pMblk); gnLBTflag2 = FALSE; if ((result != OK) || (pMblk->mBlkHdr.mLen != CPSW_LPBK_TEST_LEN)) { CPSWDRV_LOGMSG(CPSWDRV_DEBUG_ERROR, "(:%d) Error cpswEndPollSend() ...\n", __LINE__, 2, 3, 4, 5, 6); logMsg("fail to sendpi \n", 0, 0, 0, 0, 0, 0); rtnVal = ERROR; break; } sysUsDelay(500); /* recv data */ result = cpswEndPollReceive(&pDrvCtrl->cpswEndObj, pRMblk); if ((result != OK) || (pRMblk->mBlkHdr.mLen != CPSW_LPBK_TEST_LEN)) { CPSWDRV_LOGMSG(CPSWDRV_DEBUG_ERROR, "(:%d) Error cpswEndPollReceive() ...\n", __LINE__, 2, 3, 4, 5, 6); rtnVal = ERROR; logMsg("fail to recieve \n", 0, 0, 0, 0, 0, 0); break; } /* check data */ for (Loop2 = 0; Loop2 < CPSW_LPBK_TEST_LEN; Loop2++) { if (SendTestData[Loop2] != pRMblk->mBlkHdr.mData[Loop2]) { logMsg("CPSWDRV_DEBUG_Error [%d]: S:0x%02x R:0x%02x\n", Loop2, (UINT8) SendTestData[Loop2], (UINT8) pRMblk->mBlkHdr.mData[Loop2], 0, 0, 0); #ifdef CPSWDRV_DEBUG if (cpswDbg != 0) { d(SendTestData, 128, 1); d(pRMblk->mBlkHdr.mData, 128, 1); } #endif break; } } CPSWDRV_LOGMSG(CPSWDRV_DEBUG_POLL, "%d done ok\n", Loop2, 2, 3, 4, 5, 6); if (Loop2 < CPSW_LPBK_TEST_LEN) { CPSWDRV_LOGMSG(CPSWDRV_DEBUG_ERROR, "(:%d) Error data miss match(%d byte) ...\n", __LINE__, Loop2, 3, 4, 5, 6); logMsg("Error data miss matchr\n", 0, 0, 0, 0, 0, 0); rtnVal = ERROR; break; } } error: /* free Mblk */ if (pRMblk != NULL) netMblkClChainFree(pRMblk); if (pMblk != NULL) netMblkClChainFree(pMblk); cpswEndPollStop(pDrvCtrl); free(SendTestData); SendTestData=NULL; return (rtnVal); } /******************************************************************************** * STATUS cpswTestLoopBack - LoopBack mode test * * This function tests the sending and receiving of the packet in the loop back mode. * * RETURNS: OK/ERROR ********************************************************************************/ STATUS cpswTestLoopBack(int ifUnit) { CPSW_DRV_CTRL *pDrvCtrl =NULL; UINT32 miiPhyFlags_saved, ctl_retval; int rtnresult = 0; UINT32 val; if(ifUnit == 0) { pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, CPSW_DEV_UNIT_NUM0); } else if(ifUnit == 1) { pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, CPSW_DEV_UNIT_NUM1); } else { return ( ERROR); } if (pDrvCtrl == NULL) { DBG_MSG ("Please run muxDevLoad.\n"); return ( ERROR); } if (!(pDrvCtrl->cpswEndObj.flags & IFF_UP)) return (ERROR); /* save configuration */ cpswPhyRead(pDrvCtrl->pDev, (UINT8)pDrvCtrl->cpswPhyAddr, (UINT8)MII_CTRL_REG,(UINT16 *)&ctl_retval); miiPhyFlags_saved = ctl_retval; /* set loopback mode with 100base full-duplex */ if ((pDrvCtrl->cpswEndObj.flags & IFF_UP)== IFF_UP ) { cpswEndStop(&pDrvCtrl->cpswEndObj); } /* pDrvCtrl->cpswMiiBus.flags = CPSW_USR_MII_100MB | CPSW_USR_MII_FD;*/ cpswEndStart(&pDrvCtrl->cpswEndObj); cpswPhyWrite(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_CTRL_REG, MII_CR_LOOPBACK | MII_CR_100 | MII_CR_FDX); val = ECSR_READ_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL); val &= ~CPSW_EXT_EN; val |= (CPSW_RMII_EN_100 | GMAC_CTL_FULLDUPLEX); ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val); /* run loopback test */ /* add loopback setting */ { UINT16 retval; { cpswPhyRead(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_PHY_BISTCNT_REG_DP83822, &retval); retval &= (~MII_PHY_BIST_LOOPBACKMODE_MASK); retval |= (MII_PHY_BIST_TRANMIILOOPBACK_bit | MII_PHY_BIST_LOOPBACKMODE_DIGITAL); cpswPhyWrite(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_PHY_BISTCNT_REG_DP83822, retval); } } rtnresult = cpswProcLbTest(pDrvCtrl); /* del loopback setting */ { UINT16 retval; { /* DP83822 */ cpswPhyRead(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_PHY_BISTCNT_REG_DP83822, &retval); retval &= (~(MII_PHY_BIST_TRANMIILOOPBACK_bit | MII_PHY_BIST_LOOPBACKMODE_MASK)); cpswPhyWrite(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_PHY_BISTCNT_REG_DP83822, retval); cpswPhyRead(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_PHY_BISTCNT_REG_DP83822, &retval); /* Dummy read for writing */ } } /* restore configuration */ cpswEndStop(&pDrvCtrl->cpswEndObj); cpswPhyWrite(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_CTRL_REG, miiPhyFlags_saved); cpswEndStart(&pDrvCtrl->cpswEndObj); return (rtnresult); } /****************************************************************************** * cpswGetLinkStat - get cable link status * * This function get LAN cable link status. * * RETURNS: ON(1) or OFF(0) ******************************************************************************/ int cpswGetLinkStat(int ifUnit) { UINT16 regVal; CPSW_DRV_CTRL *pDrvCtrl =NULL; if(ifUnit == 0) { pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, CPSW_DEV_UNIT_NUM0); } else if(ifUnit == 1) { pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, CPSW_DEV_UNIT_NUM1); } else { return ( ERROR); } if (pDrvCtrl == NULL) { DBG_MSG("Please run muxDevLoad.\n"); return (VxD_LANLINK_NG); } /* get PHY link stat */ if (cpswPhyRead(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_STAT_REG, ®Val) != OK) { /* MII read error */ return (VxD_LANLINK_NG); } /* check link up stat */ if ((regVal & MII_SR_LINK_STATUS) == MII_SR_LINK_STATUS) { /* cable is connected */ return (VxD_LANLINK_ON); } else { /* cable is unconnected */ return (VxD_LANLINK_OFF); } } /****************************************************************************** * cpswGetLinkSpeed - get link speed * * This function get LAN link speed. * * RETURNS: ******************************************************************************/ int cpswGetLinkSpeed(int ifUnit) { UINT16 regVal; CPSW_DRV_CTRL *pDrvCtrl =NULL; if(ifUnit == 0) { pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, CPSW_DEV_UNIT_NUM0); } else if(ifUnit == 1) { pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, CPSW_DEV_UNIT_NUM1); } else { return ( ERROR); } if (pDrvCtrl == NULL) { return (LNKSPEED_10BASET_HALF); } cpswPhyRead(pDrvCtrl->pDev, pDrvCtrl->cpswPhyAddr, MII_PHY_STS_REG_DP83822, ®Val); if ((regVal & MII_PHY_STS_SPEED_bit) == MII_PHY_STS_SPEED_bit) { /* 10Mbps */ if ((regVal & MII_PHY_STS_DUPLEX_bit) == MII_PHY_STS_DUPLEX_bit) { /* Full-Duplex */ return (LNKSPEED_10BASET_FULL); } else { /* Half-Duplex */ return (LNKSPEED_10BASET_HALF); } } else { /* 100Mbps */ if ((regVal & MII_PHY_STS_DUPLEX_bit) == MII_PHY_STS_DUPLEX_bit) { /* Full-Duplex */ return (LNKSPEED_100BASETX_FULL); } else { /* Half-Duplex */ return (LNKSPEED_100BASETX_HALF); } } /* still in auto-negotiation : 10Base-T half-duplex */ return (LNKSPEED_10BASET_HALF); } /******************************************************************************* * cpswSendDataDirect - the driver send routine * * This routine takes a M_BLK_ID sends off the data in the M_BLK_ID. * The buffer must already have the addressing information properly installed * in it. This is done by a higher layer. * * Please note that this procedure updates 'tmdLastIndex' and test 'tmdIndex' * * RETURNS: OK, ERROR or END_ERR_BLOCK when not enough descriptors are available. *******************************************************************************/ /* This macro is convert virtual the address to physical address */ STATUS cpswSendDataDirect(CPSW_DRV_CTRL *pDrvCtrl, /* driver structure pointer */ char *pBuf, UINT32 len) { CPSW_DRV_CTRL *pDev; M_BLK_ID pMblk; M_BLK_ID pTemp; pDev = pDrvCtrl; CPSW_DESC *head = NULL; int i = 0; if (pDev == NULL) return (ERROR); if (pDrvCtrl->cpswPolling == TRUE) { return (ERROR); } (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); END_TX_SEM_TAKE(&pDrvCtrl->cpswEndObj, WAIT_FOREVER); if (!pDrvCtrl->cpswTxFree || !(pDrvCtrl->cpswEndObj.flags & IFF_UP)) goto blocked; pMblk = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool); if (pMblk == NULL) { DBG_MSG("cpswSendDataDirect(:%d) (pMblk)blocked. \n", __LINE__); goto blocked; } pMblk->mBlkHdr.mData = pBuf; pMblk->mBlkHdr.mLen = pMblk->mBlkPktHdr.len=len; pMblk->mBlkHdr.mFlags |= (M_PKTHDR | M_EXT); if (cpswEndEncap(pDrvCtrl, pMblk) != OK) { netMblkClChainFree (pMblk); endPoolTupleFree(pMblk); #if 0 END_TX_SEM_GIVE(&pDrvCtrl->cpswEndObj); DBG_MSG("(:%d) cpswEndTxHandle cpswEndEncap Error!!! \n", __LINE__); return (ERROR); #else goto blocked; #endif } for (i = 0; i < CPSW_TIMEOUT_VAL; i++) { if (CPDMA_REG_READ(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan))==0) break; } if (i >= CPSW_TIMEOUT_VAL) { goto blocked; } head = &pDrvCtrl->cpswTxDescMem[pDrvCtrl->cpswTxQHead]; pTemp = pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead]; if (pTemp) { pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead] = NULL; } pDrvCtrl->cpswTxFree++; CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_CP(pDrvCtrl->cpswTxDmaChan), head->phys); CPSW_DESC_INC(pDrvCtrl->cpswTxQHead, CPSW_DESC_CNT); END_TX_SEM_GIVE(&pDrvCtrl->cpswEndObj); (void) semGive(pSwCtrl->cpswDevSem); return (OK); blocked: pDrvCtrl->cpswTxstall = TRUE; END_TX_SEM_GIVE(&pDrvCtrl->cpswEndObj); (void) semGive(pSwCtrl->cpswDevSem); DBG_MSG("(:%d) cpswSendDataDirect Error\n", __LINE__); return (END_ERR_BLOCK); } /********************************************************** * info : Send Direct function. * arguments : char *Data * pointer to send data. * int Len * send data length. * return value : ERROR transmission fail. * OK transmission success. **********************************************************/ long cpswSendDirect_2(struct LanPktFilterST *Filter, char *Data, UINT32 Len,UINT16 sUnitNo) { CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, sUnitNo); return cpswSendDataDirect(pDrvCtrl, Data, Len); } long cpswSendDirect(struct LanPktFilterST *Filter, char *Data, UINT32 Len) { return cpswSendDirect_2(Filter, Data, Len,0); } /********************************************************** * cpswEnableFilter - Regist/Unregist ip Direct function * * This function is Regist and Unregist the ip Direct functions. * The Direct send function is registed cpswSendDirect. * The Direct hook function is registed Arbitrary function. * * return value : OK, ERROR **********************************************************/ UINT32 cpswEnableFilter_2(struct LanPktFilterST *Filter, UINT32 IsAdd,UINT16 sUnitNo ) { CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName(CPSW_NAME, sUnitNo); if (IsAdd == 1) { if (pDrvCtrl == NULL) { DBG_MSG("Please run muxDevLoad.\n"); return (ERROR); } /* registration */ if (semTake(GnSemId_cpswIpDetect, WAIT_FOREVER) == ERROR) { return (ERROR); } G_cpswIpdetect = Filter->PktFilterF; /* filter function */ if (semGive(GnSemId_cpswIpDetect) == ERROR) { return (ERROR); } /* filled by LAN driver */ Filter->LocalAddr = (char *) pDrvCtrl->macaddr; /* MAC address */ Filter->PktSendF = cpswSendDirect_2; /* direct send function */ } else if (IsAdd == 0) { /* unregistration */ if (semTake(GnSemId_cpswIpDetect, WAIT_FOREVER) == ERROR) { return (ERROR); } G_cpswIpdetect = 0; /* filter function */ if (semGive(GnSemId_cpswIpDetect) == ERROR) { return (ERROR); } /* cleared by LAN driver */ Filter->LocalAddr = 0; /* MAC address */ Filter->PktSendF = 0; /* direct send function */ } else { /* IsAdd value error.*/ return (ERROR); } return (OK); } UINT32 cpswEnableFilter(struct LanPktFilterST *Filter, UINT32 IsAdd) { return cpswEnableFilter_2(Filter,IsAdd, 0); } /********************************************************** * cpswCtrlNomSend - Send Direct function. * * This function control the normal sending. * * return value : OK only. **********************************************************/ STATUS cpswCtrlNomSend_2(UINT32 option,INT16 sUnitNo) { /* set enable/disable flag */ if( sUnitNo == 0 ) { G_cpsw0IpDetectPermission = option; } else { G_cpsw1IpDetectPermission = option; } return (OK); } STATUS cpswCtrlNomSend(UINT32 option) { /* set enable/disable flag */ G_cpsw0IpDetectPermission = option; return (OK); } /****************************************************************************** * * vxbCpswEndRegister - register with the VxBus subsystem * * This routine registers the CPSW driver with VxBus as a * PLB bus type. * * RETURNS: N/A * * ERRNO: N/A */ void vxbCpswEndRegister(void) { vxbDevRegister((struct vxbDevRegInfo *) &cpswDevPlbRegistration); } /******************************************************************************* * * cpswInstInit - VxBus instInit handler * * This function implements the VxBus instInit handler for an CPSW * device instance. The only thing done here is to select a unit * number for the device. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswInstInit(VXB_DEVICE_ID pDev) { const struct hcfDevice *pHcf; /* Always use the unit number allocated to us in the hwconf file. */ pHcf = hcfDeviceGet(pDev); if (pHcf == NULL) { return; } (void) vxbInstUnitSet(pDev, pHcf->devUnit); } /******************************************************************************* * * cpswInstInit2 - VxBus instInit2 handler * * This function implements the VxBus instInit2 handler for a CPSW * device instance. Once we reach this stage of initialization, it's * safe for us to allocate memory, so we can create our pDrvCtrl * structure and do some initial hardware setup. The important * steps we do here are to create a child miiBus instance, get the station * address from the BSP, and set up our vxbDma tags and memory * regions. We need to allocate a memory region for the RX DMA window * here. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswInstInit2(VXB_DEVICE_ID pDev) { int cnt; CPSW_DRV_CTRL *pDrvCtrl = NULL; CPSW_SWITCH_DATA *pSwData; const struct hcfDevice *pHcf = hcfDeviceGet(pDev); if (pHcf == NULL) { return; } /* get switch common resource info */ if (devResourceGet(pHcf, "switchData", HCF_RES_ADDR, (void *) &pSwData) != OK) { DBG_MSG("can not get pSwData resource\n"); goto failed; } if (cpswSwitchInit(pSwData) != OK) { DBG_MSG("cpswSwitchInit() failed\n"); return; } pDrvCtrl = malloc(sizeof(CPSW_DRV_CTRL)); if (!pDrvCtrl) { DBG_MSG("allocate pDrvCtrl failed\n"); goto failed; } memset(pDrvCtrl, 0, sizeof(CPSW_DRV_CTRL)); /* make connection between pDev and pDrvCtrl */ pDev->pDrvCtrl = pDrvCtrl; pDrvCtrl->pDev = pDev; /* make connection between pSwCtrl and pDrvCtrl */ pSwCtrl->port[pDev->unitNumber] = pDrvCtrl; pDrvCtrl->pSwCtrl = pSwCtrl; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); pSwCtrl->refCount++; (void) semGive(pSwCtrl->cpswDevSem); DBG_MSG("%s%d pSwCtrl->refCount = %d\n", pDev->pName, pDev->unitNumber, pSwCtrl->refCount); /* fetch parameter from bsp */ if (devResourceGet(pHcf, "phyAddr", HCF_RES_INT, (void *) &pDrvCtrl->cpswPhyAddr) != OK) { DBG_MSG("%s%d can not get phy address\n", pDev->pName, pDev->unitNumber); goto failed; } DBG_MSG("%s%d get phy address: %d\n", pDev->pName, pDev->unitNumber, pDrvCtrl->cpswPhyAddr); if (devResourceGet(pHcf, "portIndex", HCF_RES_INT, (void *) &pDrvCtrl->portIndex) != OK) { DBG_MSG("%s%d can not get portIndex\n", pDev->pName, pDev->unitNumber); goto failed; } DBG_MSG("%s%d get portIndex: %d\n", pDev->pName, pDev->unitNumber, pDrvCtrl->portIndex); if (devResourceGet(pHcf, "portOffset", HCF_RES_INT, (void *) &pDrvCtrl->portOffset) != OK) { DBG_MSG("%s%d can not get parameter portOffset\n", pDev->pName, pDev->unitNumber); goto failed; } DBG_MSG("%s%d get parameter portOffset: 0x%08x\n", pDev->pName, pDev->unitNumber, pDrvCtrl->portOffset); if (devResourceGet(pHcf, "gmacOffset", HCF_RES_INT, (void *) &pDrvCtrl->gmacOffset) != OK) { DBG_MSG("%s%d can not get parameter gmacOffset\n", pDev->pName, pDev->unitNumber); goto failed; } DBG_MSG("%s%d get parameter gmacOffset: 0x%08x\n", pDev->pName, pDev->unitNumber, pDrvCtrl->gmacOffset); /* allocating TX DMA buffer */ pDrvCtrl->cpswTxDescMem = cacheDmaMalloc(sizeof(CPSW_DESC) * CPSW_DESC_CNT); if (pDrvCtrl->cpswTxDescMem == NULL) { DBG_MSG("%s%d can not create tx desc buffer\n", pDev->pName, pDev->unitNumber); goto failed; } memset(pDrvCtrl->cpswTxDescMem, 0, sizeof(CPSW_DESC) * CPSW_DESC_CNT); for (cnt = 0; cnt < CPSW_DESC_CNT; cnt++) { /* save the physical address into desc */ pDrvCtrl->cpswTxDescMem[cnt].phys = (UINT32) &(pDrvCtrl->cpswTxDescMem[cnt]); } /* allocating RX DMA buffer */ pDrvCtrl->cpswRxDescMem = cacheDmaMalloc(sizeof(CPSW_DESC) * CPSW_DESC_CNT); if (!pDrvCtrl->cpswRxDescMem) { DBG_MSG("%s%d can not create rx desc buffer\n", pDev->pName, pDev->unitNumber); goto failed; } memset(pDrvCtrl->cpswRxDescMem, 0, sizeof(CPSW_DESC) * CPSW_DESC_CNT); for (cnt = 0; cnt < CPSW_DESC_CNT; cnt++) { /* save the physical address into desc */ pDrvCtrl->cpswRxDescMem[cnt].phys = (UINT32) &(pDrvCtrl->cpswRxDescMem[cnt]); } /* attach ISR */ vxbIntConnect(pDev, 0, cpswEndTxInt, pSwCtrl); vxbIntConnect(pDev, 1, cpswEndRxInt, pSwCtrl); vxbIntConnect(pDev, 2, cpswEndMiscInt, pSwCtrl); /* create the MII bus */ if (miiBusCreate(pDev, &pDrvCtrl->cpswMiiBus) != OK) { DBG_MSG("%s%d miiBusCreate() failed\n", pDev->pName, pDev->unitNumber); goto failed; } miiBusMediaListGet(pDrvCtrl->cpswMiiBus, &pDrvCtrl->cpswMediaList); miiBusModeSet(pDrvCtrl->cpswMiiBus, pDrvCtrl->cpswMediaList->endMediaListDefault); return; failed: if (pDrvCtrl) { if (pDrvCtrl->cpswTxDescMem) cacheDmaFree(pDrvCtrl->cpswTxDescMem); if (pDrvCtrl->cpswRxDescMem) cacheDmaFree(pDrvCtrl->cpswRxDescMem); free(pDrvCtrl); pDrvCtrl=NULL; } (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); pSwCtrl->refCount--; pSwCtrl->port[pDev->unitNumber] = NULL; (void) semGive(pSwCtrl->cpswDevSem); if (pSwCtrl->refCount == 0) { (void) semDelete(pSwCtrl->cpswDevSem); free(pSwCtrl); pSwCtrl = NULL; DBG_MSG("%s%d pSwCtrl is freed\n", pDev->pName, pDev->unitNumber); } } /******************************************************************************* * * cpswInstConnect - VxBus instConnect handler * * This function implements the VxBus instConnect handler for an CPSW * device instance. This routine connects the CPSW ISR routines here. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswInstConnect(VXB_DEVICE_ID pDev) { return; } /******************************************************************************* * * cpswSwitchInit - initialize CPSW switch common resource * * This routine initializes the CPSW switch common resource. * * RETURNS: OK or ERROR if failed. * * ERRNO: N/A */ LOCAL STATUS cpswSwitchInit(CPSW_SWITCH_DATA *pSwData) { FUNCPTR chipVerGetFunc; /* check if switch common resource already been initialized */ if (pSwCtrl != NULL) return OK; pSwCtrl = malloc(sizeof(CPSW_SW_CTRL)); if (!pSwCtrl) { DBG_MSG("malloc space for CPSW_SW_CTRL failed\n"); return ERROR; } memset(pSwCtrl, 0, sizeof(CPSW_SW_CTRL)); pSwCtrl->cpswBase = pSwData->cpswBase; pSwCtrl->portOffset = pSwData->portOffset; pSwCtrl->cpdmaOffset = pSwData->cpdmaOffset; pSwCtrl->statsOffset = pSwData->statsOffset; pSwCtrl->aleOffset = pSwData->aleOffset; pSwCtrl->mdioOffset = pSwData->mdioOffset; pSwCtrl->wrOffset = pSwData->wrOffset; pSwCtrl->cpdmaHdpOffset = pSwData->cpdmaHdpOffset; pSwCtrl->workMode = pSwData->workMode; chipVerGetFunc = (FUNCPTR) pSwData->chipVerGetFunc; DBG_MSG("pSwCtrl->cpswBase = 0x%08x\n", pSwCtrl->cpswBase); DBG_MSG("pSwCtrl->portOffset = 0x%08x\n", pSwCtrl->portOffset); DBG_MSG("pSwCtrl->cpdmaOffset = 0x%08x\n", pSwCtrl->cpdmaOffset); DBG_MSG("pSwCtrl->statsOffset = 0x%08x\n", pSwCtrl->statsOffset); DBG_MSG("pSwCtrl->aleOffset = 0x%08x\n", pSwCtrl->aleOffset); DBG_MSG("pSwCtrl->mdioOffset = 0x%08x\n", pSwCtrl->mdioOffset); DBG_MSG("pSwCtrl->wrOffset = 0x%08x\n", pSwCtrl->wrOffset); DBG_MSG("pSwCtrl->cpdmaHdpOffset = 0x%08x\n", pSwCtrl->cpdmaHdpOffset); DBG_MSG("pSwCtrl->workMode = 0x%08x\n", pSwCtrl->workMode); if (chipVerGetFunc != NULL) { pSwCtrl->chipVersion = chipVerGetFunc(); } if (pSwCtrl->workMode != CPSW_MODE_INDEPENDENT_PORT) { DBG_MSG("workMode is not independent mode\n"); goto failed; } /* * NOTE: there are many resource are shared by multi port, * cpswDevSem is used to protect these resource */ pSwCtrl->cpswDevSem = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); if (pSwCtrl->cpswDevSem == NULL) { DBG_MSG("can not create semaphore cpswDevSem\n"); goto failed; } /* initialize host(cpu) port */ if (cpswHostPortInit(pSwCtrl) != OK) goto failed; /* initialize DMA module */ if (cpswDmaInit(pSwCtrl) != OK) goto failed; return OK; failed: if (pSwCtrl->cpswDevSem) { (void) semDelete(pSwCtrl->cpswDevSem); } free(pSwCtrl); pSwCtrl = NULL; return ERROR; } /******************************************************************************* * * cpswHostPortInit - initialize CPSW host port * * This routine initializes the CPSW host port. It performs a soft reset, clears & * enables the ALE module (NOTE: ALE must be enabled otherwise all packets will be * dropped), and puts port in forward state. MDIO module is enabled in this routine * (NOTE: if soft reset fails, the routine won't proceed. An error message will be * print out if debug mode is enabled) * * RETURNS: OK or ERROR if reset timeout * * ERRNO: N/A */ LOCAL STATUS cpswHostPortInit(CPSW_SW_CTRL *pSwCtrl) { UINT32 base, val; if (cpswSoftReset(pSwCtrl, CPSW_SOFT_RESET, CPSW_TIMEOUT_VAL) != OK) { DBG_MSG("host port soft reset failed\n"); return ERROR; } DBG_MSG("cpswSoftReset done\n"); base = pSwCtrl->aleOffset; val = CPSW_ALE_EN_TABLE | CPSW_ALE_CLR_TABLE; if (pSwCtrl->workMode == CPSW_MODE_INDEPENDENT_PORT) val |= CPSW_ALE_VLAN_AWARE; ECSR_WRITE_4(pSwCtrl, base + CPSW_ALE_CONTROL, val); ECSR_WRITE_4(pSwCtrl, base + CPSW_ALE_PORTCTL(pSwCtrl->hostPortIndex), CPSW_ALE_PORT_FW | CPSW_ALE_CTL_NO_LEARN); /* enable statistics for all port */ ECSR_WRITE_4(pSwCtrl, CPSW_STAT_PORT_EN, 7); base = pSwCtrl->portOffset; ECSR_WRITE_4(pSwCtrl, base + CPSW_TX_PRI_MAP, CPSW_TX_PRI_MAP_DFTL); ECSR_WRITE_4(pSwCtrl, base + CPSW_CPDMA_TX_PRI_MAP, CPDMA_TX_PRI_MAP_DFTL); if (pSwCtrl->workMode == CPSW_MODE_INDEPENDENT_PORT) { /* set tx fifo into dual emac mode */ val = ECSR_READ_4(pSwCtrl, base + CPSW_TX_IN_CTL); val = val & ~(CPSW_TX_IN_SEL_MSK << CPSW_TX_IN_SEL_SHIFT); ECSR_WRITE_4(pSwCtrl, base + CPSW_TX_IN_CTL, val | (CPSW_TX_FIFO_DUAL_EMAC << CPSW_TX_IN_SEL_SHIFT)); } base = pSwCtrl->mdioOffset; ECSR_WRITE_4(pSwCtrl, base + CPSW_MDIO_CONTROL, CPSW_MDIO_EN | CPSW_MDIO_CLK_DIV); return OK; } /******************************************************************************* * * cpswMacPortInit - initialize CPSW GMAC port * * This routine initializes the CPSW GMAC port. It performs a soft reset, sets up * neccessary reigsters, and finally get MAC address from BSP. (NOTE: if soft * reset fails, the routine won't proceed. An error message will be print out if * debug mode is enabled) * * RETURNS: OK or ERROR if reset timeout * * ERRNO: N/A */ LOCAL STATUS cpswGmacPortInit(CPSW_DRV_CTRL *pDrvCtrl) { UINT32 lo, hi; UINT32 base; VXB_DEVICE_ID pDev = pDrvCtrl->pDev; /* * put MAC port into forward state and disable learning on this port, * because we are operating in independent port mode, not switch mode. * We add our MAC address manually. */ base = pSwCtrl->aleOffset; ECSR_WRITE_4(pSwCtrl, base + CPSW_ALE_PORTCTL(pDrvCtrl->portIndex), CPSW_ALE_PORT_FW | CPSW_ALE_CTL_NO_LEARN); base = pDrvCtrl->portOffset; ECSR_WRITE_4(pSwCtrl, base + CPSW_TX_PRI_MAP, CPSW_TX_PRI_MAP_DFTL); /* * get our MAC address from BSP. In dual emac mode * every gmac port has its own MAC address */ sysNetMacNVRamAddrGet(pDev->pName, pDev->unitNumber, pDrvCtrl->macaddr, ETHER_ADDR_LEN); lo = (pDrvCtrl->macaddr[0] << 8) | pDrvCtrl->macaddr[1]; hi = (pDrvCtrl->macaddr[2] << 24) | (pDrvCtrl->macaddr[3] << 16) | (pDrvCtrl->macaddr[4] << 8) | pDrvCtrl->macaddr[5]; ECSR_WRITE_4(pSwCtrl, pDrvCtrl->portOffset + CPSW_SL_SA_L0, lo); ECSR_WRITE_4(pSwCtrl, pDrvCtrl->portOffset + CPSW_SL_SA_HI, hi); /* reset the gmac module */ base = pDrvCtrl->gmacOffset; if (cpswSoftReset(pSwCtrl, base + CPSW_SL_MAC_SOFT_RESET, CPSW_TIMEOUT_VAL) != OK) { DBG_MSG("%s%d gmac port soft reset timeout\n", pDev->pName, pDev->unitNumber); return ERROR; } ECSR_WRITE_4(pSwCtrl, base + CPSW_SL_RX_PRI_MAP, CPDMA_TX_PRI_MAP_DFTL); ECSR_WRITE_4(pSwCtrl, base + CPSW_SL_RX_MAXLEN, CPSW_MTU); ECSR_WRITE_4(pSwCtrl, base + CPSW_SL_MAC_CTL, CPSW_GMII_EN); if (pSwCtrl->workMode == CPSW_MODE_INDEPENDENT_PORT) { ECSR_WRITE_4(pSwCtrl, pDrvCtrl->portOffset + CPSW_PORT_VLAN, pDrvCtrl->portVlan); cpswAleAddVlan(pSwCtrl, (0x1 << pSwCtrl->hostPortIndex) | (0x1 << pDrvCtrl->portIndex), 0, 0, 0, pDrvCtrl->portVlan); cpswAleAddUniCast(pSwCtrl, pDrvCtrl->macaddr, pDrvCtrl->portVlan, 1, 0, pSwCtrl->hostPortIndex); } return OK; } /******************************************************************************* * * cpswDmaChanRequest - request a DMA channel * * This routine allocates a DMA resource for a given request. The requested * channel number is specified through the parameter "idx". If "rx" is TRUE, then * it is asssumed the allocation is for rx channel otherwise it is assumed for * tx channle. DMA is shared resource, so this routine must be called with * semaphore taken. * * RETURNS: OK or ERROR if allocation fails. * * ERRNO: N/A */ LOCAL STATUS cpswDmaChanRequest(CPSW_SW_CTRL *pSwCtrl, BOOL rx, int chanNum) { UINT8 *pChanArray; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); if (rx == TRUE) pChanArray = pSwCtrl->cpswRxDmaChans; else pChanArray = pSwCtrl->cpswTxDmaChans; if (0 <= chanNum && chanNum < NR_DMA_CHANS) { if (!pChanArray[chanNum]) { pChanArray[chanNum] = 1; (void) semGive(pSwCtrl->cpswDevSem); return OK; } } (void) semGive(pSwCtrl->cpswDevSem); return ERROR; } /******************************************************************************* * * cpswDmaChanRelease - release a DMA channel * * This routine releases a DMA resource for a given request. The requested * channel number is specified through the parameter "idx". If "rx" is TRUE, then * it is asssumed the release operation is for rx channel otherwise it is assumed * for tx channle. If a already-free channel is given for release, this routine * also returns OK. DMA is shared resource, so this routine must be called with * semaphore taken. * * RETURNS: OK or ERROR if release operation fails. * * ERRNO: N/A */ LOCAL STATUS cpswDmaChanRelease(CPSW_SW_CTRL *pSwCtrl, BOOL rx, int chanNum) { UINT8 *pChanArray; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); if (rx == TRUE) pChanArray = pSwCtrl->cpswRxDmaChans; else pChanArray = pSwCtrl->cpswTxDmaChans; if (0 <= chanNum && chanNum < NR_DMA_CHANS) { if (!pChanArray[chanNum]) { (void) semGive(pSwCtrl->cpswDevSem); return OK; } else { pChanArray[chanNum] = 0; (void) semGive(pSwCtrl->cpswDevSem); return OK; } } (void) semGive(pSwCtrl->cpswDevSem); return ERROR; } /******************************************************************************* * * cpswDmaInit - initialize CPDMA module * * This routine initializes the CPDMA module inside the NIC. This routine will * perform a soft reset, clear all the rx/tx header decriptor pointer/completion * header pointer, disable interrupts and set the CPDMA control register to * default value: * * frames containing overrun error are filtered * DMA writes offset/len field in the descritpor * DMA clears the owership bit * All 8 DMA queue uses the round-robin scheduling algorithm * * RETURNS: N/A * * ERRNO: N/A */ LOCAL STATUS cpswDmaInit(CPSW_SW_CTRL *pSwCtrl) { int i; #ifdef CPSW_DBG UINT32 val; #endif /* CPSW_DBG */ /* reset the dma module */ if (cpswSoftReset(pSwCtrl, pSwCtrl->cpdmaOffset + CPDMA_SOFT_RESET, CPSW_TIMEOUT_VAL) != OK) { DBG_MSG("cpdma module soft reset timeout\n"); return ERROR; } /* initialize dma queue header pointers */ for (i = 0; i < NR_DMA_CHANS; i++) { CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_HDP(i), 0); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_HDP(i), 0); CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_CP(i), 0); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_CP(i), 0); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_FREEBUF(i), 0); } CPDMA_REG_WRITE(pSwCtrl, CPDMA_DMACONTROL, CPDMA_CONTROL_DEFAULT); /* disable channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_BUFOFFSET, 0x0); CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_CLR, 0xffff); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_CLR, 0xffff); CPDMA_REG_WRITE(pSwCtrl, CPDMA_INTMASK_CLR, 0x3); #ifdef CPSW_DBG val = CPDMA_REG_READ(pSwCtrl, CPDMA_RX_INTSTAT_MASKED); if (val & 0xffff) { DBG_MSG("rx mask(0x%08x) is not zero\n", val); } val = CPDMA_REG_READ(pSwCtrl, CPDMA_TX_INTSTAT_MASKED); if (val & 0xffff) { DBG_MSG("tx mask(0x%08x) is not zero\n", val); } #endif /* CPSW_DBG */ /* enable global tx rx DMA */ CPDMA_TX_EN(pSwCtrl); CPDMA_RX_EN(pSwCtrl); return OK; } /******************************************************************************* * * cpswSoftReset - perform soft reset on a given module * * This routine performs soft reset on a given module. * * RETURNS: OK or ERROR if timeout * * ERRNO: N/A */ LOCAL STATUS cpswSoftReset(CPSW_SW_CTRL *pSwCtrl, UINT32 moduleReg, UINT32 timeout) { UINT32 i = 0; UINT32 ret; ECSR_WRITE_4(pSwCtrl, moduleReg, 0x1); do { ret = ECSR_READ_4(pSwCtrl, moduleReg); } while ((ret & 0x1) && (i++ < timeout)); if (i >= timeout) return ERROR; return OK; } /******************************************************************************* * * cpswInstUnlink - unlink the device instance from VxBus subsystem * * This routine unlinks the device instance from VxBus subsystem * * RETURNS: OK or ERROR if operation failed. * * ERRNO: N/A */ LOCAL STATUS cpswInstUnlink(VXB_DEVICE_ID pDev, void *unused) { CPSW_DRV_CTRL *pDrvCtrl = pDev->pDrvCtrl; if (pDrvCtrl->cookie != NULL) { if (muxDevStop(pDrvCtrl->cookie) != OK) { DBG_MSG("%s%d muxDevStop return ERROR\n", pDev->pName, pDev->unitNumber); return ERROR; } if (muxDevUnload(pDev->pName, pDev->unitNumber) != OK) { DBG_MSG("%s%d muxDevUnload return ERROR\n", pDev->pName, pDev->unitNumber); return ERROR; } } /* free the memory of ring descriptor */ if (pDrvCtrl->cpswTxDescMem) cacheDmaFree(pDrvCtrl->cpswTxDescMem); if (pDrvCtrl->cpswRxDescMem) cacheDmaFree(pDrvCtrl->cpswRxDescMem); /* destroy our MII bus and child PHYs */ if (pDrvCtrl->cpswMiiBus != NULL) { miiBusDelete(pDrvCtrl->cpswMiiBus); } /* disconnect the ISR handle */ (void) vxbIntDisconnect(pDev, 0, cpswEndTxInt, pSwCtrl); (void) vxbIntDisconnect(pDev, 1, cpswEndRxInt, pSwCtrl); (void) vxbIntDisconnect(pDev, 2, cpswEndMiscInt, pSwCtrl); /* destroy the adapter context */ free(pDrvCtrl); pDev->pDrvCtrl = NULL; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); pSwCtrl->refCount--; pSwCtrl->port[pDev->unitNumber] = NULL; (void) semGive(pSwCtrl->cpswDevSem); if (pSwCtrl->refCount == 0) { /* disable global tx rx DMA */ CPDMA_TX_DIS(pSwCtrl); CPDMA_RX_DIS(pSwCtrl); (void) semDelete(pSwCtrl->cpswDevSem); free(pSwCtrl); pSwCtrl = NULL; DBG_MSG("%s%d: pSwCtrl is freed for all the port are freed\n", pDev->pName, pDev->unitNumber); } return OK; } /******************************************************************************* * * cpswAleRead - read an ALE entry * * This routine reads an ALE entry from the given index * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswAleRead(CPSW_SW_CTRL *pSwCtrl, CPSW_ALE_TBL *tbl, UINT32 entry) { ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_TBLCTL, entry & CPSW_ALE_ENTRY_IDX_MASK); tbl->word0 = ECSR_READ_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_WORD0); tbl->word1 = ECSR_READ_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_WORD1); tbl->word2 = ECSR_READ_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_WORD2) & 0xf; } /******************************************************************************* * * cpswAleWrite - write an ALE entry * * This routine writes data to an ALE entry with the given index * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswAleWrite(CPSW_SW_CTRL *pSwCtrl, CPSW_ALE_TBL *tbl, UINT32 entry) { ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_WORD0, tbl->word0); ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_WORD1, tbl->word1); ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_WORD2, tbl->word2 & 0xf); ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_TBLCTL, (entry & CPSW_ALE_ENTRY_IDX_MASK) | CPSW_ALE_WRITE); } /******************************************************************************* * * cpswAleFind - find an emtpy ALE entry * * This routine finds an emtpy ALE entry. * * RETURNS: empty entry index or -1 if ALE is full * * ERRNO: N/A */ LOCAL int cpswAleFind(CPSW_SW_CTRL *pSwCtrl) { int i; CPSW_ALE_TBL tbl; for (i = 0; i < CPSW_ALE_ENTRY_NR; i++) { cpswAleRead(pSwCtrl, &tbl, i); if ((tbl.word1 & CPSW_ALE_ENTRY_MASK) == 0x0) return i; } return -1; } /******************************************************************************* * * cpswAleMatch - find the entry with the given MAC address * * This routine finds the entry with the given MAC address * * RETURNS: entry found or -1 if the MAC address has not entered into ALE yet. * * ERRNO: N/A */ LOCAL int cpswAleMatch(CPSW_SW_CTRL *pSwCtrl, unsigned char *mac) { int i; CPSW_ALE_TBL t; unsigned int word0, word1; word0 = (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | (mac[5]); word1 = (mac[0] << 8) | (mac[1]); for (i = 0; i < CPSW_ALE_ENTRY_NR; i++) { cpswAleRead(pSwCtrl, &t, i); if ((t.word0 == word0) && ((t.word1 & 0xffff) == word1)) return i; } return -1; } /******************************************************************************* * * cpswAleEntryShow - show an ALE entry * * This routine outputs verbose message of an ALE entry * * RETURNS: N/A * * ERRNO: N/A */ void cpswAleEntryShow(CPSW_ALE_TBL *tbl) { unsigned char *macaddr; unsigned char *macaddr1; int block = 0, secure = 0, port = 0, portmask = 0; int fwstate = 0, super = 0; int unicasttype = 0; int vmem = 0, umf = 0, rmf = 0, fue = 0; char *str = NULL; static char *unicast[] = { "u/na", "u/a/nt", "oui", "u/a/t" }; macaddr = (unsigned char *) &tbl->word0; macaddr1 = (unsigned char *) &tbl->word1; int vid = (tbl->word1 >> 16) & 0xfff; int type = (tbl->word1 >> 28) & 0x3; int multicast = tbl->word1 & 0x100; if (type == 0x0) return; if (type == 1) { if (multicast) str = "multicast"; else str = "unicast"; } else if (type == 0x3) { if (multicast) str = "multi/vlan"; else str = "uni/vlan"; } if (type == 0x2) { str = "vlan"; fue = (tbl->word0 >> 24) & 0x7; rmf = (tbl->word0 >> 16) & 0x7; umf = (tbl->word0 >> 8) & 0x7; vmem = tbl->word0 & 0x7; } else { if (!multicast) { unicasttype = (tbl->word1 >> 30) & 0x3; secure = (tbl->word2 & 0x1); block = (tbl->word2 & 0x2) >> 1; port = (tbl->word2 >> 0x2) & 0x3; } else { fwstate = (tbl->word1 >> 30) & 0x3; super = (tbl->word2 >> 1) & 0x1; portmask = (tbl->word2 >> 2) & 0x7; } } if (type == 0x3 || type == 0x1) { /* multicast entry */ if (multicast) { printf("%-20s%-20s%-20s%-20s%-20s%-20s\n", "mac", "ent type", "state", "super", "portmask", "vid"); printf("---------------------------------------------" "---------------------------------------------" "-----------------------------------\n"); printf("%02x:%02x:%02x:%02x:%02x:%02x %-20s%" "-20d%-20d%-20d%-20d\n\n", macaddr1[1], macaddr1[0], macaddr[3], macaddr[2], macaddr[1], macaddr[0], str, fwstate, super, portmask, vid); } /* unicast entry */ else { printf("%-20s%-20s%-20s%-20s%-20s%-20s%-20s\n", "mac", "ent type", "type", "secure", "block", "port", "vid"); printf("---------------------------------------------" "---------------------------------------------" "-----------------------------------\n"); printf("%02x:%02x:%02x:%02x:%02x:%02x %-20s%" "-20s%-20d%-20d%-20d%-20d\n\n", macaddr1[1], macaddr1[0], macaddr[3], macaddr[2], macaddr[1], macaddr[0], str, unicast[unicasttype], secure, block, port, vid); } /* vlan entry */ /* } else if (type == 0x2) {*/ } else { printf("%-20s%-20s%-20s%-20s%-20s%-20s%-20s\n", "mac", "ent type", "fue", "rmf", "umf", "vmem", "vid"); printf("---------------------------------------------" "---------------------------------------------" "-----------------------------------\n"); printf("%-20s%-20s%-20d%-20d%-20d%-20d%-20d\n\n", "N/A", str, fue, rmf, umf, vmem, vid); } } /******************************************************************************* * * cpswAleDump - show all the ALE entries * * This routine outputs verbose message of all the ALE entries * * RETURNS: N/A * * ERRNO: N/A */ void cpswAleDump(void) { int i; CPSW_ALE_TBL tbl; for (i = 0; i < CPSW_ALE_ENTRY_NR; i++) { cpswAleRead(pSwCtrl, &tbl, i); cpswAleEntryShow(&tbl); } } /******************************************************************************* * * cpswAleAddMultiCast - add a multicast MAC address into ALE table * * This routine adds a multicast MAC address into ALE table. First, this routine * will try to find a match. If the MAC address is already in the table, it * overwrites the entry content with new values. If it can not find a match, a empty * entry will be allocated and the MAC address will be added. * * RETURNS: 0 on success, ENOMEM if can not find a emtpy entry * * ERRNO: N/A */ LOCAL int cpswAleAddMultiCast(CPSW_SW_CTRL *pSwCtrl, unsigned char *mac, int vid, int fwstate, int super, int portmask) { unsigned int word0, word1; CPSW_ALE_TBL tbl; int entry; word0 = (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | (mac[5]); word1 = (mac[0] << 8) | (mac[1]); entry = cpswAleMatch(pSwCtrl, mac); if (entry < 0) { entry = cpswAleFind(pSwCtrl); if (entry < 0) return ENOMEM; } tbl.word0 = word0; /* multicast with vlan */ if (vid > 0) tbl.word1 = word1 | ((vid & 0xfff) << 16) | ((fwstate & 0x3) << 30) | (0x3 << 28); else tbl.word1 = word1 | ((fwstate & 0x3) << 30) | (0x1 << 28); tbl.word2 = ((super & 0x1) << 1) | ((portmask & 0x7) << 2); cpswAleWrite(pSwCtrl, &tbl, entry); return 0; } /******************************************************************************* * * cpswAleAddUniCast - add a unicast MAC address into ALE table * * This routine adds a unicast MAC address into ALE table. First, this routine * will try to find a match. If the MAC address is already in the table, it overwrites * that entry content with new values. If it can not find a match, * a empty entry will be allocated and the MAC address will be added. * * RETURNS: 0 on success or ENOMEM if can not find a emtpy entry * * ERRNO: N/A */ LOCAL int cpswAleAddUniCast(CPSW_SW_CTRL *pSwCtrl, unsigned char *mac, int vid, int secure, int block, int port) { unsigned int word0, word1; CPSW_ALE_TBL tbl; int entry; word0 = (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | (mac[5]); word1 = (mac[0] << 8) | (mac[1]); tbl.word0 = word0; tbl.word1 = word1; tbl.word2 = 0x0; entry = cpswAleMatch(pSwCtrl, mac); if (entry < 0) { entry = cpswAleFind(pSwCtrl); if (entry < 0) return ENOMEM; } tbl.word0 = word0; /* unicast with vlan*/ if (vid > 0) tbl.word1 = word1 | (0x3 << 28) | CPSW_ALE_UNICAST_AGEABLE_NOT | ((vid & 0xfff) << 16); else tbl.word1 = word1 | (0x1 << 28) | CPSW_ALE_UNICAST_AGEABLE_NOT; tbl.word2 = ((block & 0x1) << 1) | (secure & 0x1) | ((port & 0x3) << 2); cpswAleWrite(pSwCtrl, &tbl, entry); return 0; } /******************************************************************************* * * cpswAleMatchVlan - find the ALE entry with the given vid * * This routine finds the ALE entry index with the given vid * * RETURNS: ALE entry index on success or -1 if entry not found * * ERRNO: N/A */ LOCAL int cpswAleMatchVlan(CPSW_SW_CTRL *pSwCtrl, int vid) { int i; CPSW_ALE_TBL t; for (i = 0; i < CPSW_ALE_ENTRY_NR; i++) { cpswAleRead(pSwCtrl, &t, i); if (((t.word1 >> 28) & 0x3) == 0x2) { if (((t.word1 >> 16) & 0xfff) == vid) return i; } } return -1; } /******************************************************************************* * * cpswAleAddVlan - add a vlan entry into ALE table * * This routine adds a vlan entry into ALE table * * RETURNS: 0 on success, ENOMEM if can not find a emtpy entry or * EEXIST if the given vlan is alreay in ALE table * * ERRNO: N/A */ LOCAL int cpswAleAddVlan(CPSW_SW_CTRL *pSwCtrl, int vlanMemberList, int unregFloodMask, int regFloodMask, int forceUntaggedEgress, int vid) { CPSW_ALE_TBL t; int entry; if (vid <= 0 || vid >= 4095) return EINVAL; entry = cpswAleMatchVlan(pSwCtrl, vid); if (entry < 0) { entry = cpswAleFind(pSwCtrl); if (entry < 0) return ENOMEM; } t.word0 = ((vlanMemberList & 0x7)) | ((unregFloodMask & 0x7) << 8) | ((regFloodMask & 0x7) << 16) | ((forceUntaggedEgress & 0x7) << 24); t.word1 = ((vid & 0xfff) << 16) | (0x2 << 28); t.word2 = 0; cpswAleWrite(pSwCtrl, &t, entry); return 0; } /******************************************************************************* * * cpswPhyRead - miiBus miiRead method * * This function implements an miiRead() method that allows PHYs * on the miiBus to access our MII management registers. * * RETURNS: ERROR if invalid PHY addr, else OK * * ERRNO: N/A */ STATUS cpswPhyRead(VXB_DEVICE_ID pDev, UINT8 phyAddr, UINT8 regAddr, UINT16 *pDataVal) { UINT32 ret; UINT32 index = 0; CPSW_DRV_CTRL *pDrvCtrl = pDev->pDrvCtrl; if (phyAddr >= 32) return ERROR; /* we only work on the exactly phy address which is specified */ if (pDrvCtrl->cpswPhyAddr != phyAddr) { *pDataVal = 0xFFFF; return ERROR; } (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); do { ret = ECSR_READ_4(pSwCtrl, pSwCtrl->mdioOffset + CPSW_USERACCESSn(index)); } while (ret & CPSW_MDIO_GO); ret = phyAddr << CPSW_PHY_ADDR_SHIFT | regAddr << CPSW_REG_ADDR_SHIFT | CPSW_MDIO_GO; ECSR_WRITE_4(pSwCtrl, pSwCtrl->mdioOffset + CPSW_USERACCESSn(index), ret); do { ret = ECSR_READ_4(pSwCtrl, pSwCtrl->mdioOffset + CPSW_USERACCESSn(index)); } while (ret & CPSW_MDIO_GO); *pDataVal = ret & 0xffff; (void) semGive(pSwCtrl->cpswDevSem); return OK; } /******************************************************************************* * * cpswPhyWrite - miiBus miiWrite method * * This function implements an miiWrite() method that allows PHYs * on the miiBus to access our MII management registers. This routine * works in much the same way as cpswPhyRead(), using the shortcut * PHY management registers to make it look like there's a single * PHY at MII address 0. * * RETURNS: ERROR if invalid PHY addr, else OK * * ERRNO: N/A */ STATUS cpswPhyWrite(VXB_DEVICE_ID pDev, UINT8 phyAddr, UINT8 regAddr, UINT16 dataVal) { UINT32 ret; UINT32 index = 0; CPSW_DRV_CTRL *pDrvCtrl = pDev->pDrvCtrl; if (phyAddr >= 32) return ERROR; /* we only work on the exactly phy address which is specified */ if (pDrvCtrl->cpswPhyAddr != phyAddr) return ERROR; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); do { ret = ECSR_READ_4(pSwCtrl, pSwCtrl->mdioOffset + CPSW_USERACCESSn(index)); } while (ret & CPSW_MDIO_GO); ret = phyAddr << CPSW_PHY_ADDR_SHIFT | regAddr << CPSW_REG_ADDR_SHIFT | CPSW_MDIO_GO | CPSW_MDIO_WRITE | dataVal; ECSR_WRITE_4(pSwCtrl, pSwCtrl->mdioOffset + CPSW_USERACCESSn(index), ret); do { ret = ECSR_READ_4(pSwCtrl, pSwCtrl->mdioOffset + CPSW_USERACCESSn(index)); } while (ret & CPSW_MDIO_GO); (void) semGive(pSwCtrl->cpswDevSem); return OK; } /***************************************************************************** * * cpswLinkUpdate - miiBus miiLinkUpdate method * * This function implements an miiLinkUpdate() method that allows * miiBus to notify us of link state changes. This routine will be * invoked by the miiMonitor task when it detects a change in link * status. Normally, the miiMonitor task checks for link events every * two seconds. * * Once we determine the new link state, we will announce the change * to any bound protocols via muxError(). We also update the ifSpeed * fields in the MIB2 structures so that SNMP queries can detect the * correct link speed. * * RETURNS: ERROR if obtaining the new media setting fails, else OK * * ERRNO: N/A */ LOCAL STATUS cpswLinkUpdate(VXB_DEVICE_ID pDev) { CPSW_DRV_CTRL *pDrvCtrl; UINT32 oldStatus; UINT32 val; if (pDev->pDrvCtrl == NULL) return ERROR; pDrvCtrl = pDev->pDrvCtrl; if (pDrvCtrl->cpswMiiBus == NULL) return ERROR; semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); oldStatus = pDrvCtrl->cpswCurStatus; if (miiBusModeGet(pDrvCtrl->cpswMiiBus, &pDrvCtrl->cpswCurMedia, &pDrvCtrl->cpswCurStatus) == ERROR) { semGive(pSwCtrl->cpswDevSem); return ERROR; } if (!(pDrvCtrl->cpswEndObj.flags & IFF_UP)) { semGive(pSwCtrl->cpswDevSem); return OK; } val = ECSR_READ_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL); switch (IFM_SUBTYPE(pDrvCtrl->cpswCurMedia)) { case (IFM_1000_T): case (IFM_1000_SX): val &= ~CPSW_EXT_EN; ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, GAMC_CTL_GIG | val); pDrvCtrl->cpswEndObj.mib2Tbl.ifSpeed = 1000000000; if (pDrvCtrl->cpswEndObj.pMib2Tbl != NULL) { pDrvCtrl->cpswEndObj.pMib2Tbl->m2Data.mibIfTbl.ifSpeed = 1000000000; } break; case (IFM_100_TX): val &= ~CPSW_EXT_EN; val |= CPSW_RMII_EN_100; ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val & ~GAMC_CTL_GIG); pDrvCtrl->cpswEndObj.mib2Tbl.ifSpeed = 100000000; if (pDrvCtrl->cpswEndObj.pMib2Tbl != NULL) { pDrvCtrl->cpswEndObj.pMib2Tbl->m2Data.mibIfTbl.ifSpeed = 100000000; } break; case (IFM_10_T): val |= CPSW_EXT_EN; val &= ~CPSW_RMII_EN_100; ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val & ~GAMC_CTL_GIG); pDrvCtrl->cpswEndObj.mib2Tbl.ifSpeed = 10000000; if (pDrvCtrl->cpswEndObj.pMib2Tbl != NULL) { pDrvCtrl->cpswEndObj.pMib2Tbl->m2Data.mibIfTbl.ifSpeed = 10000000; } break; default: pDrvCtrl->cpswEndObj.mib2Tbl.ifSpeed = 0; if (pDrvCtrl->cpswEndObj.pMib2Tbl != NULL) { pDrvCtrl->cpswEndObj.pMib2Tbl->m2Data.mibIfTbl.ifSpeed = 0; } break; } val = ECSR_READ_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL); if ((pDrvCtrl->cpswCurMedia & IFM_GMASK) == IFM_FDX) { ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val | GMAC_CTL_FULLDUPLEX); } else { ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val & ~GMAC_CTL_FULLDUPLEX); } if (pDrvCtrl->cpswEndObj.pMib2Tbl != NULL) pDrvCtrl->cpswEndObj.pMib2Tbl->m2Data.mibIfTbl.ifSpeed = pDrvCtrl->cpswEndObj.mib2Tbl.ifSpeed; if (!(pDrvCtrl->cpswEndObj.flags & IFF_UP)) { semGive(pSwCtrl->cpswDevSem); return (OK); } /* If status went from down to up, announce link up. */ if (pDrvCtrl->cpswCurStatus & IFM_ACTIVE && !(oldStatus & IFM_ACTIVE)) { jobQueueStdPost(pDrvCtrl->cpswJobQueue, NET_TASK_QJOB_PRI, muxLinkUpNotify, &pDrvCtrl->cpswEndObj, NULL, NULL, NULL, NULL); } /* If status went from up to down, announce link down. */ if (!(pDrvCtrl->cpswCurStatus & IFM_ACTIVE) && oldStatus & IFM_ACTIVE) { jobQueueStdPost(pDrvCtrl->cpswJobQueue, NET_TASK_QJOB_PRI, muxLinkDownNotify, &pDrvCtrl->cpswEndObj, NULL, NULL, NULL, NULL); } semGive(pSwCtrl->cpswDevSem); return (OK); } /******************************************************************************* * * cpswMuxConnect - muxConnect method handler * * This function handles muxConnect() events, which may be triggered * manually or(more likely) by the bootstrap code. Most VxBus * initialization occurs before the MUX has been fully initialized, * so the usual muxDevLoad()/muxDevStart() sequence must be defered * until the networking subsystem is ready. This routine will ultimately * trigger a call to cpswEndLoad() to create the END interface instance. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswMuxConnect(VXB_DEVICE_ID pDev, void *unused) { CPSW_DRV_CTRL *pDrvCtrl = pDev->pDrvCtrl; pDrvCtrl->cookie = muxDevLoad(pDev->unitNumber, cpswEndLoad, "", TRUE, pDev); if ((pDrvCtrl->cookie != NULL) && (muxDevStart(pDrvCtrl->cookie) == OK)) { if (_func_m2PollStatsIfPoll != NULL) { (void) endPollStatsInit(pDrvCtrl->cookie, _func_m2PollStatsIfPoll); } } } /******************************************************************************* * * cpswEndLoad - END driver entry point * * This routine initializes the END interface instance associated * with this device. In traditional END drivers, this function is * the only public interface, and it's typically invoked by a BSP * driver configuration stub. With VxBus, the BSP stub code is no * longer needed, and this function is now invoked automatically * whenever this driver's muxConnect() method is called. * * For older END drivers, the load string would contain various * configuration parameters, but with VxBus this use is deprecated. * The load string should just be an empty string. The second * argument should be a pointer to the VxBus device instance * associated with this device. Like older END drivers, this routine * will still return the device name if the init string is empty, * since this behavior is still expected by the MUX. The MUX will * invoke this function twice: once to obtain the device name, * and then again to create the actual END_OBJ instance. * * When this function is called the second time, it will initialize * the END object, perform MIB2 setup, allocate a buffer pool, and * initialize the supported END capabilities. The only special * capability we support is VLAN_MTU, since we can receive slightly * larger than normal frames. * * RETURNS: An END object pointer, or NULL on error, or 0 and the name * of the device if the <loadStr> was empty. * * ERRNO: N/A */ END_OBJ *cpswEndLoad(char *loadStr, void *pArg) { CPSW_DRV_CTRL *pDrvCtrl; VXB_DEVICE_ID pDev; if (loadStr == NULL) return NULL; if (loadStr[0] == 0) { bcopy(CPSW_NAME, loadStr, sizeof(CPSW_NAME)); return NULL; } pDev = pArg; pDrvCtrl = pDev->pDrvCtrl; G_cpswIpdetect = 0; GnSemId_cpswIpDetect = semMCreate( SEM_Q_PRIORITY ); if( !GnSemId_cpswIpDetect ) { goto EndLoadErrorExit; } if (END_OBJ_INIT(&pDrvCtrl->cpswEndObj, NULL, pDev->pName, pDev->unitNumber, &cpswNetFuncs, "CPSW VxBus END Driver") != OK) { DBG_MSG("%s%d: END_OBJ_INIT failed\n", pDev->pName, pDev->unitNumber); return NULL; } endM2Init(&pDrvCtrl->cpswEndObj, M2_ifType_ethernet_csmacd, pDrvCtrl->macaddr, ETHER_ADDR_LEN, ETHERMTU, 100000000, IFF_NOTRAILERS | IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST); pDrvCtrl->cpswMtu = CPSW_MTU; if (endPoolCreate(CPSW_DESC_CNT * 8, &pDrvCtrl->cpswEndObj.pNetPool) != OK) { DBG_MSG("%s%d: endPoolCreate() failed\n", pDev->pName, pDev->unitNumber); return NULL; } /* allocated the temp buffer used by poll mode */ pDrvCtrl->cpswPollbuf = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool); /* Set up cpswPolling stats. */ pDrvCtrl->cpswStatsConf.ifPollInterval = sysClkRateGet(); pDrvCtrl->cpswStatsConf.ifEndObj = &pDrvCtrl->cpswEndObj; pDrvCtrl->cpswStatsConf.ifWatchdog = NULL; pDrvCtrl->cpswStatsConf.ifValidCounters = (END_IFINMULTICASTPKTS_VALID | END_IFINBROADCASTPKTS_VALID | END_IFINOCTETS_VALID | END_IFINERRORS_VALID | END_IFINDISCARDS_VALID | END_IFOUTMULTICASTPKTS_VALID | END_IFOUTBROADCASTPKTS_VALID | END_IFOUTOCTETS_VALID | END_IFOUTERRORS_VALID); /* Set up capabilities. */ pDrvCtrl->cpswCaps.cap_available = IFCAP_VLAN_MTU; pDrvCtrl->cpswCaps.cap_enabled = IFCAP_VLAN_MTU; return (&pDrvCtrl->cpswEndObj); EndLoadErrorExit: if( GnSemId_cpswIpDetect != NULL ) { semDelete( GnSemId_cpswIpDetect ); GnSemId_cpswIpDetect = (SEM_ID)NULL; } return (NULL); } /******************************************************************************* * * cpswEndUnload - unload END driver instance * * This routine undoes the effects of cpswEndLoad(). The END object * is destroyed, our network pool is released, the endM2 structures * are released, and the cpswPolling stats watchdog is terminated. * * Note that the END interface instance can't be unloaded if the * device is still running. The device must be stopped with muxDevStop() * first. * * RETURNS: ERROR if device is still in the IFF_UP state, otherwise OK * * ERRNO: N/A */ LOCAL STATUS cpswEndUnload(END_OBJ *pEnd) { CPSW_DRV_CTRL *pDrvCtrl; if (pEnd->flags & IFF_UP) return ERROR; pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; /* free the poll mode temp buffer */ endPoolTupleFree(pDrvCtrl->cpswPollbuf); /* release our buffer pool */ endPoolDestroy(pDrvCtrl->cpswEndObj.pNetPool); /* terminate stats polling */ wdDelete(pDrvCtrl->cpswStatsConf.ifWatchdog); endM2Free(&pDrvCtrl->cpswEndObj); semDelete (GnSemId_cpswIpDetect); GnSemId_cpswIpDetect = (SEM_ID)NULL; END_OBJECT_UNLOAD(&pDrvCtrl->cpswEndObj); /* prevent freeing of pDrvCtrl */ return (EALREADY); } /***************************************************************************** * * cpswEndHashTblPopulate - set the Multicast table entry * * This function programs the CPSW controller's multicast table entry * to receive frames sent to the multicast groups specified. * If VLAN ID is greater than zero then VLAN LLDP/Multicast is added. * If the interface is in IFF_ALLMULTI or IFF_PROMISC mode, the filter * will be programmed to receive all multicast packets by setting to the * bypass mode.The packet with a matching multicast destination address * set to be a supervisory packet. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndHashTblPopulate(CPSW_DRV_CTRL *pDrvCtrl) { int i, type, multicast, rt; ETHER_MULTI *mCastNode = NULL; CPSW_ALE_TBL tbl; UINT32 aleCtl; /* If flags & (IFF_ALLMULTI | IFF_PROMISC) is true, set to bypass mode. */ if (pDrvCtrl->cpswEndObj.flags & (IFF_ALLMULTI | IFF_PROMISC)) { aleCtl = ECSR_READ_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_CONTROL); aleCtl |= CPSW_ALE_BYPASS; ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_CONTROL, aleCtl); return; } /* First, clear out all multicast ALEs. */ for (i = 0; i < CPSW_ALE_ENTRY_NR; i++) { cpswAleRead(pSwCtrl, &tbl, i); type = (tbl.word1 >> 28) & 0x3; multicast = tbl.word1 & 0x100; if (multicast && ((type == CPSW_ALE_TYPE_ADDR) || (type == CPSW_ALE_TYPE_VLAN_ADDR))) { memset((char *) &tbl, 0, sizeof(tbl)); cpswAleWrite(pSwCtrl, &tbl, i); } } /* Otherwise, add multicast ALEs */ for (mCastNode = (ETHER_MULTI *) lstFirst(&pDrvCtrl->cpswEndObj.multiList); mCastNode != NULL; mCastNode = (ETHER_MULTI *) lstNext(&mCastNode->node)) { rt = cpswAleAddMultiCast(pSwCtrl, (unsigned char *) mCastNode->addr, pDrvCtrl->portVlan, CPSW_ALE_MCAST_FWD_2, CPSW_ALE_MCAST_SUPER, 1 << pSwCtrl->hostPortIndex); if (rt != 0) break; } } /******************************************************************************* * * cpswEndMCastAddrAdd - add a multicast address for the device * * This routine adds a multicast address to whatever the driver * is already listening for. It then resets the address filter. * * RETURNS: OK, always. * * ERRNO: N/A */ LOCAL STATUS cpswEndMCastAddrAdd(END_OBJ *pEnd, char *pAddr) { int retVal; CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); if (!(pDrvCtrl->cpswEndObj.flags & IFF_UP)) { (void) semGive(pSwCtrl->cpswDevSem); return OK; } retVal = etherMultiAdd(&pEnd->multiList, pAddr); if (retVal == ENETRESET) { pEnd->nMulti++; cpswEndHashTblPopulate(pDrvCtrl); } (void) semGive(pSwCtrl->cpswDevSem); return OK; } /******************************************************************************* * * cpswEndMCastAddrDel - delete a multicast address for the device * * This routine removes a multicast address from whatever the driver * is listening for. It then resets the address filter. * * RETURNS: OK, always. * * ERRNO: N/A */ LOCAL STATUS cpswEndMCastAddrDel(END_OBJ *pEnd, char *pAddr) { int retVal; CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); if (!(pDrvCtrl->cpswEndObj.flags & IFF_UP)) { (void) semGive(pSwCtrl->cpswDevSem); return OK; } retVal = etherMultiDel(&pEnd->multiList, pAddr); if (retVal == ENETRESET) { pEnd->nMulti--; cpswEndHashTblPopulate(pDrvCtrl); } (void) semGive(pSwCtrl->cpswDevSem); return OK; } /******************************************************************************* * * cpswEndMCastAddrGet - get the multicast address list for the device * * This routine gets the multicast list of whatever the driver * is already listening for. * * RETURNS: OK, always. * * ERRNO: N/A */ LOCAL STATUS cpswEndMCastAddrGet(END_OBJ *pEnd, MULTI_TABLE *pTable) { int retVal; CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); if (!(pDrvCtrl->cpswEndObj.flags & IFF_UP)) { (void) semGive(pSwCtrl->cpswDevSem); return OK; } retVal = etherMultiGet(&pEnd->multiList, pTable); if (retVal == ENETRESET) { pEnd->nMulti++; } (void) semGive(pSwCtrl->cpswDevSem); return OK; } /******************************************************************************* * * cpswEndStatsDump - return polled statistics counts * * This routine is automatically invoked periodically by the polled * statistics watchdog. * * RETURNS: always OK * * ERRNO: N/A */ LOCAL STATUS cpswEndStatsDump(CPSW_DRV_CTRL *pDrvCtrl) { int i; UINT32 *temp = (UINT32 *) &pSwCtrl->cpswStat; END_IFCOUNTERS *pEndStatsCounters; /* * hardware statistic counters are write-to-decrement, * after a read, we write the value read to clear * the counters */ #ifdef CPSW_END_STATUS_PRINT /* show Status all register */ printf( "\n\n===== ALL status COUNTER =====\n" ); #endif for (i = 0; i < sizeof(CPSW_STAT) / sizeof(UINT32); i++) { *temp = ECSR_READ_4(pSwCtrl, pSwCtrl->statsOffset + i * 4); #ifdef CPSW_END_STATUS_PRINT /* show Status all register */ printf( "Register%4d %10x\n", i, *temp); #endif ECSR_WRITE_4(pSwCtrl, pSwCtrl->statsOffset + i * 4, *temp); temp++; } pEndStatsCounters = &pDrvCtrl->cpswStatsCounters; pEndStatsCounters->ifInOctets = pSwCtrl->cpswStat.rxoctets; pEndStatsCounters->ifInMulticastPkts = pSwCtrl->cpswStat.rxmulticast; pEndStatsCounters->ifInBroadcastPkts = pSwCtrl->cpswStat.rxbroadcast; pEndStatsCounters->ifInErrors = pSwCtrl->cpswStat.rxpause + pSwCtrl->cpswStat.rxcrcerros + pSwCtrl->cpswStat.rxalignmenterrors + pSwCtrl->cpswStat.rxoversized + pSwCtrl->cpswStat.rxjabber + pSwCtrl->cpswStat.rxundersized; pEndStatsCounters->ifInDiscards = pDrvCtrl->cpswInDropped; pEndStatsCounters->ifOutOctets = pSwCtrl->cpswStat.txoctets; pEndStatsCounters->ifOutMulticastPkts = pSwCtrl->cpswStat.txmulticast; pEndStatsCounters->ifOutBroadcastPkts = pSwCtrl->cpswStat.txbroadcast; pEndStatsCounters->ifOutErrors = pSwCtrl->cpswStat.txpause + pSwCtrl->cpswStat.txdefered + pSwCtrl->cpswStat.txcollision + pSwCtrl->cpswStat.txexceesive + pSwCtrl->cpswStat.txsinglecol + pSwCtrl->cpswStat.txmulticol + pSwCtrl->cpswStat.txlatecol + pSwCtrl->cpswStat.txunderrun; return OK; } /******************************************************************************* * * cpswEndIoctl - the driver I/O control routine * * This function processes ioctl requests supplied via the muxIoctl() * routine. In addition to the normal boilerplate END ioctls, this * driver supports the IFMEDIA ioctls, END capabilities ioctls, and * polled stats ioctls. * * RETURNS: A command specific response, usually OK or ERROR. * * ERRNO: N/A */ LOCAL int cpswEndIoctl(END_OBJ *pEnd, int cmd, caddr_t data) { CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; END_MEDIALIST *mediaList; END_CAPABILITIES *hwCaps; END_MEDIA *pMedia; END_RCVJOBQ_INFO *qinfo; UINT32 nQs; INT32 value; int error = OK; int i; if (cmd != EIOCPOLLSTART && cmd != EIOCPOLLSTOP) (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); switch (cmd) { case EIOTESTLB: /* loopback test interface for RTP */ error = cpswTestLoopBack(pDrvCtrl->portIndex -1); break; case EIOSHOWSTAT: /* show device status interface for RTP */ cpswEndStatsDump(pDrvCtrl); case EIOGETLNKSTS: /* get link status */ error = cpswGetLinkStat(pDrvCtrl->portIndex -1); break; case EIOGETLNKSPEED: /* get link speed */ error = cpswGetLinkSpeed(pDrvCtrl->portIndex -1); break; case EIOMACCHECK: /* mac address checksum */ error = cpswCheckSum(pDrvCtrl->portIndex-1); break; case EIOMACRD_HI: /* read MAC address[HIGH] interface for RTP */ { UINT8 buf[MAC_ADRS_LEN]; if (cpswReadMac(buf,(pDrvCtrl->portIndex-1) )== OK) { UINT32 rtVal = 0; rtVal |= (buf[0] << (8 * 2)); rtVal |= (buf[1] << (8)); rtVal |= (buf[2]); error = rtVal; } else { error = ERROR; } break; } case EIOMACRD_LO: /* read MAC address[LOW] interface for RTP */ { UINT8 buf[MAC_ADRS_LEN]; if (cpswReadMac(buf,(pDrvCtrl->portIndex-1)) == OK) { UINT32 rtVal = 0; rtVal |= (buf[3] << (8 * 2)); rtVal |= (buf[4] << (8)); rtVal |= (buf[5]); error = rtVal; } else { error = ERROR; } break; } case EIOMACWR_HI: /* write MAC address[HIGH] interface for RTP */ { UINT8 buf[MAC_ADRS_LEN]; if ((UINT32) data == 0) { error = ERROR; } if (cpswReadMac(buf,(pDrvCtrl->portIndex-1)) == OK) { buf[0] = (UINT8) ((UINT32) data >> (8 * 2)); buf[1] = (UINT8) ((UINT32) data >> (8)); buf[2] = (UINT8) ((UINT32) data); if (cpswWriteMac(buf,(pDrvCtrl->portIndex-1)) == ERROR) { error = ERROR; } } else { error = ERROR; } break; } case EIOMACWR_LO: /* write MAC address[LOW] interface for RTP */ { UINT8 buf[MAC_ADRS_LEN]; if ((UINT32) data == 0) { error = ERROR; } if (cpswReadMac(buf, (pDrvCtrl->portIndex-1)) == OK) { buf[3] = (UINT8) ((UINT32) data >> (8 * 2)); buf[4] = (UINT8) ((UINT32) data >> (8)); buf[5] = (UINT8) ((UINT32) data); if (cpswWriteMac(buf,(pDrvCtrl->portIndex-1)) == ERROR) { error = ERROR; } } else { error = ERROR; } break; } /**************************************************************************/ case EIOCSADDR: if (data == NULL) error = EINVAL; else { UINT32 lo, hi; CPSW_ALE_TBL t; unsigned char oldmac[ETHER_ADDR_LEN]; bcopy((char *) pDrvCtrl->macaddr, (char *) oldmac, ETHER_ADDR_LEN); bcopy((char *) data, (char *) pDrvCtrl->macaddr, ETHER_ADDR_LEN); bcopy((char *) data, (char *) pEnd->mib2Tbl.ifPhysAddress.phyAddress, ETHER_ADDR_LEN); if (pEnd->pMib2Tbl != NULL) bcopy((char *) data, (char *) pEnd->pMib2Tbl->m2Data.mibIfTbl.ifPhysAddress.phyAddress, ETHER_ADDR_LEN); if ((i = cpswAleMatch(pSwCtrl, oldmac)) < 0) { /* * This shall not happen, but we still handle it. * Add mac addres in ALE table. */ cpswAleAddUniCast(pSwCtrl, pDrvCtrl->macaddr, pDrvCtrl->portVlan, 1, 0, pSwCtrl->hostPortIndex); } else { /* Read the old mac entry, replace it with the new one */ cpswAleRead(pSwCtrl, &t, i); lo = (pDrvCtrl->macaddr[0] << 8) | (pDrvCtrl->macaddr[1]); hi = (pDrvCtrl->macaddr[2] << 24) | (pDrvCtrl->macaddr[3] << 16) | (pDrvCtrl->macaddr[4] << 8) | (pDrvCtrl->macaddr[5]); t.word0 = hi; t.word1 &= ~0xFFFF; t.word1 |= lo; cpswAleWrite(pSwCtrl, &t, i); } } break; case EIOCGADDR: if (data == NULL) error = EINVAL; else bcopy((char *) pDrvCtrl->macaddr, (char *) data, ETHER_ADDR_LEN); break; case EIOCSFLAGS: { long oldFlags; long newFlags; oldFlags = END_FLAGS_GET(pEnd); value = (INT32) data; if (value < 0) { value = -value; value--; END_FLAGS_CLR(pEnd, value); } else { END_FLAGS_SET(pEnd, value); } newFlags = END_FLAGS_GET(pEnd); if (!(newFlags & IFF_PROMISC) && (oldFlags & IFF_PROMISC)) { UINT32 tmp = CPSW_ALE_EN_TABLE | CPSW_ALE_CLR_TABLE; if (pSwCtrl->workMode == CPSW_MODE_INDEPENDENT_PORT) tmp |= CPSW_ALE_VLAN_AWARE; ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_CONTROL, tmp); for (i = 0; i < pSwCtrl->refCount; i++) { cpswAleAddVlan(pSwCtrl, (0x1 << pSwCtrl->hostPortIndex) | (0x1 << pSwCtrl->port[i]->portIndex), 0, 0, 0, pSwCtrl->port[i]->portVlan); cpswAleAddUniCast(pSwCtrl, pSwCtrl->port[i]->macaddr, pSwCtrl->port[i]->portVlan, 1, 0, pSwCtrl->hostPortIndex); } } if ((newFlags & IFF_PROMISC) && !(oldFlags & IFF_PROMISC)) { ECSR_WRITE_4(pSwCtrl, pSwCtrl->aleOffset + CPSW_ALE_CONTROL, CPSW_ALE_EN_TABLE | CPSW_ALE_CLR_TABLE | CPSW_ALE_BYPASS); } } break; case EIOCGFLAGS: if (data == NULL) error = EINVAL; else *(long *) data = END_FLAGS_GET(pEnd); break; case EIOCMULTIADD: error = cpswEndMCastAddrAdd(pEnd, (char *) data); break; case EIOCMULTIDEL: error = cpswEndMCastAddrDel(pEnd, (char *) data); break; case EIOCMULTIGET: error = cpswEndMCastAddrGet(pEnd, (MULTI_TABLE *) data); break; case EIOCPOLLSTART: /* set poll flag */ pDrvCtrl->cpswPolling = TRUE; /* stop tx rx dma channel interrupt */ /* stop tx channel interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_CLR, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_CLR_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); /* stop rx channel interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_CLR, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_CLR_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); /* * We may have been asked to enter polled mode while there are * transmissions pending. This is a problem, because the polled * transmit routine expects that the TX ring will be empty when * it's called. In order to guarantee this, we have to drain * the TX ring here. We could also just plain reset and * reinitialize the transmitter, but this is faster. */ while (pDrvCtrl->cpswTxFree < CPSW_DESC_CNT) { M_BLK_ID pMblk; volatile CPSW_DESC *desc; desc = &pDrvCtrl->cpswTxDescMem[pDrvCtrl->cpswTxQHead]; if (desc->flags & CPSW_EOQ) { ECSR_WRITE_4(pSwCtrl, CPDMA_TX_CP(pDrvCtrl->cpswTxDmaChan), desc->phys); if (desc->link) { ECSR_WRITE_4(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan), desc->link); } } while (desc->flags & CPSW_OWNERSHIP) ; pMblk = pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead]; if (pMblk != NULL) { endPoolTupleFree(pMblk); pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead] = NULL; } pDrvCtrl->cpswTxFree++; CPSW_DESC_INC(pDrvCtrl->cpswTxQHead, CPSW_DESC_CNT); } break; case EIOCPOLLSTOP: pDrvCtrl->cpswPolling = FALSE; /* enable relative tx channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_SET, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); /* enable relative rx channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_SET, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); break; case EIOCGMIB2233: case EIOCGMIB2: error = endM2Ioctl(&pDrvCtrl->cpswEndObj, cmd, data); break; case EIOCGPOLLCONF: if (data == NULL) error = EINVAL; else *((END_IFDRVCONF **) data) = &pDrvCtrl->cpswStatsConf; break; case EIOCGPOLLSTATS: if (data == NULL) error = EINVAL; else { error = cpswEndStatsDump(pDrvCtrl); if (error == OK) *((END_IFCOUNTERS **) data) = &pDrvCtrl->cpswStatsCounters; } break; case EIOCGMEDIALIST: if (data == NULL) { error = EINVAL; break; } if (pDrvCtrl->cpswMediaList->endMediaListLen == 0) { error = ENOTSUP; break; } mediaList = (END_MEDIALIST *) data; if (mediaList->endMediaListLen < pDrvCtrl->cpswMediaList->endMediaListLen) { mediaList->endMediaListLen = pDrvCtrl->cpswMediaList->endMediaListLen; error = ENOSPC; break; } bcopy((char *) pDrvCtrl->cpswMediaList, (char *) mediaList, sizeof(END_MEDIALIST) + (sizeof(UINT32) * pDrvCtrl->cpswMediaList->endMediaListLen)); break; case EIOCGIFMEDIA: if (data == NULL) error = EINVAL; else { pMedia = (END_MEDIA *) data; pMedia->endMediaActive = pDrvCtrl->cpswCurMedia; pMedia->endMediaStatus = pDrvCtrl->cpswCurStatus; } break; case EIOCSIFMEDIA: if (data == NULL) error = EINVAL; else { pMedia = (END_MEDIA *) data; miiBusModeSet(pDrvCtrl->cpswMiiBus, pMedia->endMediaActive); cpswLinkUpdate(pDrvCtrl->pDev); error = OK; } break; case EIOCGIFCAP: hwCaps = (END_CAPABILITIES *) data; if (hwCaps == NULL) { error = EINVAL; break; } hwCaps->cap_available = pDrvCtrl->cpswCaps.cap_available; hwCaps->cap_enabled = pDrvCtrl->cpswCaps.cap_enabled; break; case EIOCSIFCAP: error = ENOTSUP; break; case EIOCGIFMTU: if (data == NULL) error = EINVAL; else *(INT32 *) data = pEnd->pMib2Tbl->m2Data.mibIfTbl.ifMtu; break; case EIOCSIFMTU: value = (INT32) data; if (value <= 0 || value > pDrvCtrl->cpswMtu) { error = EINVAL; break; } pEnd->pMib2Tbl->m2Data.mibIfTbl.ifMtu = value; break; case EIOCGRCVJOBQ: if (data == NULL) { error = EINVAL; break; } qinfo = (END_RCVJOBQ_INFO *) data; nQs = qinfo->numRcvJobQs; qinfo->numRcvJobQs = 1; if (nQs < 1) error = ENOSPC; else qinfo->qIds[0] = pDrvCtrl->cpswJobQueue; break; #if 0 case EIOCGFBUF: if( data == NULL ) { error = EINVAL; } else { *( int * )data = 4; } break; case EIOCGMWIDTH: if( data == NULL ) { error = EINVAL; } else { *( int * )data = 4; } break; case EIOCGHDRLEN: if( data == NULL ) { error = EINVAL; } else { *( int * )data = 14; } break; #endif default: error = EINVAL; break; } if (cmd != EIOCPOLLSTART && cmd != EIOCPOLLSTOP) (void) semGive(pSwCtrl->cpswDevSem); return (error); } /******************************************************************************* * * cpswEndStart - start the device * * This function resets the device to put it into a known state and * then configures it for RX and TX operation. The RX and TX configuration * registers are initialized, and the address of the RX DMA window is * loaded into the device. Interrupts are then enabled, and the initial * link state is configured. * * Note that this routine also checks to see if an alternate jobQueue * has been specified via the vxbParam subsystem. This allows the driver * to divert its work to an alternate processing task, such as may be * done with TIPC. This means that the jobQueue can be changed while * the system is running, but the device must be stopped and restarted * for the change to take effect. * * RETURNS: ERROR if device initialization failed, otherwise OK * * ERRNO: N/A */ LOCAL STATUS cpswEndStart(END_OBJ *pEnd) { int i; CPSW_DESC *desc; STATUS ret = OK; CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; VXB_DEVICE_ID pDev = pDrvCtrl->pDev; UINT32 val; M_BLK_ID pMblk; VXB_INST_PARAM_VALUE paramVal; HEND_RX_QUEUE_PARAM *pRxQueue; semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); END_TX_SEM_TAKE(pEnd, WAIT_FOREVER); if (pEnd->flags & IFF_UP) { ret = ERROR; DBG_MSG("%s%d: end already startup\n", pDev->pName, pDev->unitNumber); goto out; } pDrvCtrl->portVlan = CPSW_PORT_VLAN_PROG(pDrvCtrl->portIndex); /* initialize mac port(s) */ if (cpswGmacPortInit(pDrvCtrl) != OK) goto failed; /* we use the device unit number as the channel number */ pDrvCtrl->cpswRxDmaChan = pDev->unitNumber; pDrvCtrl->cpswTxDmaChan = pDev->unitNumber; /* request for rx dma channel, */ if (cpswDmaChanRequest(pSwCtrl, TRUE, pDrvCtrl->cpswRxDmaChan) != OK) { ret = ERROR; DBG_MSG("%s%d: alloc rx dma chan[%d] failed\n", pDev->pName, pDev->unitNumber, pDrvCtrl->cpswRxDmaChan); pDrvCtrl->cpswRxDmaChan = 0xffffffff; pDrvCtrl->cpswTxDmaChan = 0xffffffff; goto failed; } /* request for tx dma channel */ if (cpswDmaChanRequest(pSwCtrl, FALSE, pDrvCtrl->cpswTxDmaChan) != OK) { ret = ERROR; DBG_MSG("%s%d: alloc tx dma chan[%d] failed\n", pDev->pName, pDev->unitNumber, pDrvCtrl->cpswTxDmaChan); pDrvCtrl->cpswTxDmaChan = 0xffffffff; goto failed; } /* setup channel dma priority mapping for independent port mode */ val = ECSR_READ_4(pSwCtrl, pSwCtrl->portOffset + CPDMA_RX_CH_MAP); if (pDev->unitNumber == 0) { val = val & 0xffff0000; val |= (pDrvCtrl->cpswRxDmaChan | (pDrvCtrl->cpswRxDmaChan << 4) | (pDrvCtrl->cpswRxDmaChan << 8) | (pDrvCtrl->cpswRxDmaChan << 12)); } else { val = val & 0xffff; val |= (pDrvCtrl->cpswRxDmaChan | (pDrvCtrl->cpswRxDmaChan << 4) | (pDrvCtrl->cpswRxDmaChan << 8) | (pDrvCtrl->cpswRxDmaChan << 12)) << 16; } ECSR_WRITE_4(pSwCtrl, pSwCtrl->portOffset + CPDMA_RX_CH_MAP, val); vxAtomicSet(&pDrvCtrl->cpswTxIntPend, FALSE); vxAtomicSet(&pDrvCtrl->cpswRxIntPend, FALSE); vxAtomicSet(&pDrvCtrl->cpswMiscIntPend, FALSE); /* set the default job queue to netJobQueueId */ pDrvCtrl->cpswJobQueue = netJobQueueId; /* Override the job queue ID if the user supplied an alternate one */ /* * The rxQueue00 parameter specifies a pointer to a HEND_RX_QUEUE_PARAM * structure, which contains, among other things, an ID for the job queue * to be used for this instance. */ if (vxbInstParamByNameGet(pDev, "rxQueue00", VXB_PARAM_POINTER, ¶mVal) == OK) { pRxQueue = (HEND_RX_QUEUE_PARAM *) paramVal.pValue; if (pRxQueue->jobQueId != NULL) { pDrvCtrl->cpswJobQueue = pRxQueue->jobQueId; DBG_MSG("%s%d: use rxQueue00[0x%08x]\n", pDev->pName, pDev->unitNumber, pDrvCtrl->cpswJobQueue); } } /* setup the job queue fucntion */ QJOB_SET_PRI(&pDrvCtrl->cpswTxQJob, NET_TASK_QJOB_PRI); pDrvCtrl->cpswTxQJob.func = (QJOB_FUNC) cpswEndTxHandle; QJOB_SET_PRI(&pDrvCtrl->cpswRxQJob, NET_TASK_QJOB_PRI); pDrvCtrl->cpswRxQJob.func = (QJOB_FUNC) cpswEndRxHandle; QJOB_SET_PRI(&pDrvCtrl->cpswMiscQJob, NET_TASK_QJOB_PRI); pDrvCtrl->cpswMiscQJob.func = (QJOB_FUNC) cpswEndMiscHandle; /* set up rx mblks */ for (i = 0; i < CPSW_DESC_CNT; i++) { pMblk = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool); if (!pMblk) { ret = ERROR; DBG_MSG("%s%d: endPoolTupleGet() failed for rx desc[%d]\n", pDev->pName, pDev->unitNumber, i); goto failed; } (void) cacheInvalidate(DATA_CACHE, pMblk->m_data, pMblk->m_len); pDrvCtrl->cpswRxblk[i] = pMblk; desc = pDrvCtrl->cpswRxDescMem + i; desc->buf = mtod(pMblk, UINT32); desc->len = CPSW_MTU; desc->offset = 0; desc->flags = CPSW_OWNERSHIP; } /* link all the RX descriptors, here we need physical address */ desc = (CPSW_DESC *) pDrvCtrl->cpswRxDescMem[0].phys; for (i = 0; i < CPSW_DESC_CNT - 1; i++) pDrvCtrl->cpswRxDescMem[i].link = (UINT32) (desc + (i + 1)); pDrvCtrl->cpswRxDescTail = &pDrvCtrl->cpswRxDescMem[CPSW_DESC_CNT - 1]; CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_FREEBUF(pDrvCtrl->cpswRxDmaChan), CPSW_DESC_CNT); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_HDP(pDrvCtrl->cpswRxDmaChan), pDrvCtrl->cpswRxDescMem[0].phys); /* setup desc index */ pDrvCtrl->cpswTxQHead = CPSW_TXQ_INVALID; pDrvCtrl->cpswTxFree = CPSW_DESC_CNT; pDrvCtrl->cpswTxstall = FALSE; pDrvCtrl->cpswTxIdx = 0; pDrvCtrl->cpswRxIdx = 0; /* enable the GMII */ val = ECSR_READ_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL); val |= CPSW_GMII_EN; ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val); /* enable relative tx channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_SET, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); /* enable relative rx channel interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_SET, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); /* enable misc interrupts */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_INTMASK_SET, 0x2); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_MISC_EN, 0x1f); /* tell the stack that we are on-line */ pDrvCtrl->cpswCurMedia = IFM_ETHER | IFM_NONE; pDrvCtrl->cpswCurStatus = IFM_AVALID; miiBusModeSet(pDrvCtrl->cpswMiiBus, pDrvCtrl->cpswMediaList->endMediaListDefault); END_FLAGS_SET(pEnd, (IFF_UP | IFF_RUNNING)); vxbIntEnable(pDev, 0, cpswEndTxInt, pSwCtrl); vxbIntEnable(pDev, 1, cpswEndRxInt, pSwCtrl); vxbIntEnable(pDev, 2, cpswEndMiscInt, pSwCtrl); DBG_MSG("%s%d: cpswEndStart() ok\n", pDev->pName, pDev->unitNumber); goto out; failed: if (pDrvCtrl->cpswRxDmaChan != 0xffffffff) cpswDmaChanRelease(pSwCtrl, TRUE, pDrvCtrl->cpswRxDmaChan); if (pDrvCtrl->cpswTxDmaChan != 0xffffffff) cpswDmaChanRelease(pSwCtrl, FALSE, pDrvCtrl->cpswTxDmaChan); for (i = 0; i < CPSW_DESC_CNT; i++) { if (pDrvCtrl->cpswRxblk[i]) endPoolTupleFree(pDrvCtrl->cpswRxblk[i]); pDrvCtrl->cpswRxblk[i] = NULL; } DBG_MSG("%s%d: cpswEndStart() failed\n", pDev->pName, pDev->unitNumber); out: END_TX_SEM_GIVE(pEnd); semGive(pSwCtrl->cpswDevSem); return ret; } /******************************************************************************* * * cpswEndStop - stop the device * * This function undoes the effects of cpswEndStart(). The device is shut * down and all resources are released. Note that the shutdown process * pauses to wait for all pending RX, TX and link event jobs that may have * been initiated by the interrupt handler to complete. This is done * to prevent tNetTask from accessing any data that might be released by * this routine. * * RETURNS: ERROR if device shutdown failed, otherwise OK * * ERRNO: N/A */ LOCAL STATUS cpswEndStop(END_OBJ *pEnd) { int i; UINT32 val; CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; VXB_DEVICE_ID pDev = pDrvCtrl->pDev; #ifdef CPSW_DBG logMsg("%s%d: cpswEndStop is called\n", (_Vx_usr_arg_t)pDev->pName, (_Vx_usr_arg_t)pDev->unitNumber, 3, 4, 5, 6); #endif (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); if (!(pEnd->flags & IFF_UP)) { (void) semGive(pSwCtrl->cpswDevSem); return OK; } END_FLAGS_CLR(pEnd, (IFF_UP | IFF_RUNNING)); /* disable the GMII */ val = ECSR_READ_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL); val &= ~CPSW_GMII_EN; ECSR_WRITE_4(pSwCtrl, pDrvCtrl->gmacOffset + CPSW_SL_MAC_CTL, val); /* stop tx channel interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_CLR, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_CLR_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); /* stop rx channel interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_CLR, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_CLR_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); /* disable the ISR handle */ (void) vxbIntDisable(pDev, 0, cpswEndTxInt, pSwCtrl); (void) vxbIntDisable(pDev, 1, cpswEndRxInt, pSwCtrl); (void) vxbIntDisable(pDev, 2, cpswEndMiscInt, pSwCtrl); /* * Wait for all jobs to drain. * Note: this must be done before we disable the receiver and * transmitter below. If someone tries to reboot us via WDB, * this routine may be invoked while the RX handler is still * running in tNetTask. If we disable the chip while that * function is running, it'll start reading inconsistent status * from the chip. We have to wait for that job to terminate first, * then we can disable the receiver and transmitter. */ for (i = 0; i < CPSW_TIMEOUT_VAL; i++) { if ((vxAtomicGet(&pDrvCtrl->cpswTxIntPend) == FALSE) && (vxAtomicGet(&pDrvCtrl->cpswRxIntPend) == FALSE)) break; taskDelay(1); } if (i == CPSW_TIMEOUT_VAL) { DBG_MSG("%s%d: timed out waiting for job to complete\n", pDev->pName, pDev->unitNumber); (void) semGive(pSwCtrl->cpswDevSem); return ERROR; } /* release resources */ for (i = 0; i < CPSW_DESC_CNT; i++) { if (pDrvCtrl->cpswRxblk[i] != NULL) { endPoolTupleFree(pDrvCtrl->cpswRxblk[i]); pDrvCtrl->cpswRxblk[i] = NULL; } } /* * Flush the recycle cache to shake loose any of our mBlks that may be * stored there. */ endMcacheFlush(); END_TX_SEM_TAKE(pEnd, WAIT_FOREVER); for (i = 0; i < CPSW_DESC_CNT; i++) { if (pDrvCtrl->cpswTxblk[i] != NULL) { endPoolTupleFree(pDrvCtrl->cpswTxblk[i]); pDrvCtrl->cpswTxblk[i] = NULL; } } END_TX_SEM_GIVE(pEnd); cpswDmaChanRelease(pSwCtrl, TRUE, pDrvCtrl->cpswRxDmaChan); cpswDmaChanRelease(pSwCtrl, FALSE, pDrvCtrl->cpswTxDmaChan); semGive(pSwCtrl->cpswDevSem); #ifdef CPSW_DBG logMsg("%s%d: cpswEndStop done\n", (_Vx_usr_arg_t)pDev->pName, (_Vx_usr_arg_t)pDev->unitNumber, 3, 4, 5, 6); #endif return OK; } /******************************************************************************* * * cpswEndTxInt - interrupt handle for tx done * * This routine is the interrupt handle for tx done. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndTxInt(CPSW_SW_CTRL *pSwCtrl) { CPSW_DRV_CTRL *pDrvCtrl; UINT32 txStat; UINT32 txChannel; /* disable all tx interrupt first */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_CLR, 0xff); ECSR_WRITE_4(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, 0); /* ack the tx interrupts by write 0x2 to CPDMA_EOI_VECTOR */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_DMA_EOI, EOI_VEC_TX_DONE); /* ERRATA Advisory 1.0.9 of am335x */ if (pSwCtrl->chipVersion == TI_AM335X_DEV_ID) { *(volatile UINT32 *) (GPTIMER6_BASE + GPTIMER_IRQ_STATUS) = TCAR_IF_FLAG; } /* * read the TX_STAT bit address location to determine which * channel(s) caused the interrupt */ txStat = CPDMA_REG_READ(pSwCtrl, CPDMA_TX_INTSTAT_RAW) & 0xff; /* search looking for tx events */ for (txChannel = 0; txChannel < NR_DMA_CHANS; txChannel++) { if (txStat & BIT(txChannel)) { pDrvCtrl = pSwCtrl->port[txChannel]; if ((pDrvCtrl != NULL) && (vxAtomicCas(&pDrvCtrl->cpswTxIntPend, FALSE, TRUE) == TRUE)) { /* post the tx done job to task */ (void) jobQueuePost(pDrvCtrl->cpswJobQueue, &pDrvCtrl->cpswTxQJob); } } } /* enable the tx interrupt except the one which has just occured */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_SET, (~txStat) & 0xff); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, (~txStat) & 0xff); } /******************************************************************************* * * cpswEndRxInt - interrupt handle for rx done * * This routine is the interrupt handle for rx done. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndRxInt(CPSW_SW_CTRL *pSwCtrl) { CPSW_DRV_CTRL *pDrvCtrl; UINT32 rxStat; UINT32 rxChannel; /* disable all rx interrupt first */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_CLR, 0xff); ECSR_WRITE_4(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, 0); /* ack the rx interrupts by write 0x1 to CPDMA_EOI_VECTOR */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_DMA_EOI, EOI_VEC_RX_DONE); /* ERRATA Advisory 1.0.9 of am335x */ if (pSwCtrl->chipVersion == TI_AM335X_DEV_ID) { *(volatile UINT32 *) (GPTIMER5_BASE + GPTIMER_IRQ_STATUS) = TCAR_IF_FLAG; } /* * read the RX_STAT bit address location to determine which * channel(s) caused the interrupt */ rxStat = CPDMA_REG_READ(pSwCtrl, CPDMA_RX_INTSTAT_RAW) & 0xff; /* search looking for rx events */ for (rxChannel = 0; rxChannel < NR_DMA_CHANS; rxChannel++) { if (rxStat & BIT(rxChannel)) { pDrvCtrl = pSwCtrl->port[rxChannel]; if ((pDrvCtrl != NULL) && (vxAtomicCas(&pDrvCtrl->cpswRxIntPend, FALSE, TRUE) == TRUE)) { /* post the rx done job to task */ (void) jobQueuePost(pDrvCtrl->cpswJobQueue, &pDrvCtrl->cpswRxQJob); } } } /* enable the rx interrupt except the one which has just occured */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_SET, (~rxStat) & 0xff); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, (~rxStat) & 0xff); } /******************************************************************************* * * cpswEndMiscInt - interrupt handle for misc interrupt * * This routine is the interrupt handle for misc interrupt. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndMiscInt(CPSW_SW_CTRL *pSwCtrl) { CPSW_DRV_CTRL *pDrvCtrl; UINT32 dmaStatus, rxErr, txErr; UINT32 rxChan, txChan, errChanNum; VXB_DEVICE_ID pDev; /* ack the interrupt */ CPDMA_REG_WRITE(pSwCtrl, CPDMA_DMA_EOI, EOI_VEC_MISC); /* disable msic interrupt */ ECSR_WRITE_4(pSwCtrl, CPSW_DMA_BAR(pSwCtrl) + CPDMA_INTMASK_CLR, 0x2); /* get error code */ dmaStatus = CPDMA_REG_READ(pSwCtrl, CPDMA_DMASTATUS); rxErr = CPDMA_RX_HOST_ERR_CODE(dmaStatus); txErr = CPDMA_TX_HOST_ERR_CODE(dmaStatus); rxChan = CPDMA_RX_HOST_ERR_CHAN(dmaStatus)&0x7; txChan = CPDMA_TX_HOST_ERR_CHAN(dmaStatus)&0x7; if (rxErr || txErr) { while (1) { errChanNum = rxChan; /* Following Klocwork analysis - Benoit TANI * CPSW_DRV_CTRL port suports NR_MAC_PORTS number of ports. * but rxChan might actually be > NR_MAC_PORTS. * --> update PDrvCtrl only if port # is in range. * --> Or error will be catch in the following NULL test. * */ if(errChanNum < NR_MAC_PORTS) pDrvCtrl = pSwCtrl->port[errChanNum]; if (pDrvCtrl != NULL) { pDev = pDrvCtrl->pDev; /* log the error message */ logMsg("%s%d: dma status error\n", (_Vx_usr_arg_t) pDev->pName, (_Vx_usr_arg_t) pDev->unitNumber, 3, 4, 5, 6); #if 0 /* HF.M just for debug */ logMsg("rx chan = %d rx err code = %d (%s)\n" "tx chan = %d tx err code = %d (%s)\n", (_Vx_usr_arg_t) rxChan, (_Vx_usr_arg_t) rxErr, (_Vx_usr_arg_t) rxChanErrMsg[rxErr], (_Vx_usr_arg_t) txChan, (_Vx_usr_arg_t) txErr, (_Vx_usr_arg_t) txChanErrMsg[txErr]); #endif if (vxAtomicCas(&pDrvCtrl->cpswMiscIntPend, FALSE, TRUE) == TRUE) { /* post the job to task */ (void) jobQueuePost(pDrvCtrl->cpswJobQueue, &pDrvCtrl->cpswMiscQJob); } } /* * if rxChan is same as txChan, only one job is enough, * otherwise execute once more. */ if (rxChan == txChan) { break; } else { rxChan = txChan; } } } } /******************************************************************************* * * cpswEndRxHandle - process received frames * * This function is scheduled by the ISR to run in the context of tNetTask * whenever an RX interrupt is received. It processes packets from the * RX window and encapsulates them into mBlk tuples which are handed up * to the MUX. * * There may be several packets waiting in the window to be processed. * We take care not to process too many packets in a single run through * this function so as not to monopolize tNetTask and starve out other * jobs waiting in the jobQueue. If we detect that there's still more * packets waiting to be processed, we queue ourselves up for another * round of processing. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndRxHandle(void *pArg) { QJOB *job = pArg; CPSW_DRV_CTRL *pDrvCtrl = member_to_object(job, CPSW_DRV_CTRL, cpswRxQJob); UINT32 inPktLen; CPSW_DESC *desc; M_BLK_ID pMblk = NULL; M_BLK_ID newblk; UINT32 val; int loopCounter = CPSW_DESC_CNT; int status; int key; #ifdef CPSW_DBG VXB_DEVICE_ID pDev = pDrvCtrl->pDev; #endif /* CPSW_DBG */ (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); val = CPDMA_REG_READ(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan)); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan), val); while (loopCounter--) { desc = &pDrvCtrl->cpswRxDescMem[pDrvCtrl->cpswRxIdx]; if (desc->flags & CPSW_OWNERSHIP) { break; } inPktLen = desc->flags & CPSW_PKT_LEN_MASK; status = desc->flags & CPSW_EOQ; if ((desc->flags & CPSW_PKT_ERROR) || !inPktLen) { DBG_MSG("%s%d, desc indicate the packet is wrong\n", pDev->pName, pDev->unitNumber); desc->len = CPSW_MTU; desc->offset = 0; desc->flags = CPSW_OWNERSHIP; pDrvCtrl->cpswInDropped++; CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan), desc->phys); CPSW_DESC_INC(pDrvCtrl->cpswRxIdx, CPSW_DESC_CNT); goto addToTail; } newblk = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool); if (!newblk) { DBG_MSG("%s%d, endPoolTupleGet return NULL\n", pDev->pName, pDev->unitNumber); pDrvCtrl->cpswInDropped++; pDrvCtrl->cpswLastError.errCode = END_ERR_NO_BUF; muxError(&pDrvCtrl->cpswEndObj, &pDrvCtrl->cpswLastError); desc->len = CPSW_MTU; desc->offset = 0; desc->flags = CPSW_OWNERSHIP; CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan), desc->phys); CPSW_DESC_INC(pDrvCtrl->cpswRxIdx, CPSW_DESC_CNT); goto addToTail; } /* pre-invalidate the new buffer */ (void) cacheInvalidate(DATA_CACHE, newblk->m_data, newblk->m_len); pMblk = pDrvCtrl->cpswRxblk[pDrvCtrl->cpswRxIdx]; pDrvCtrl->cpswRxblk[pDrvCtrl->cpswRxIdx] = newblk; newblk->m_next = NULL; if (desc->flags & CPSW_PASS_CRC) pMblk->m_len = pMblk->m_pkthdr.len = inPktLen - ETHER_CRC_LEN; else pMblk->m_len = pMblk->m_pkthdr.len = inPktLen; pMblk->m_flags = M_PKTHDR | M_EXT; CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan), desc->phys); desc->buf = mtod(newblk, UINT32); desc->len = CPSW_MTU; desc->offset = 0; desc->flags = CPSW_OWNERSHIP; CPSW_DESC_INC(pDrvCtrl->cpswRxIdx, CPSW_DESC_CNT); addToTail: desc->link = 0; pDrvCtrl->cpswRxDescTail->link = desc->phys; pDrvCtrl->cpswRxDescTail = desc; desc = &pDrvCtrl->cpswRxDescMem[pDrvCtrl->cpswRxIdx]; if (status) { CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_HDP(pDrvCtrl->cpswRxDmaChan), desc->phys); } if (pMblk) { cacheInvalidate(DATA_CACHE, pMblk->m_data, pMblk->m_len); #if 0 END_RCV_RTN_CALL(&pDrvCtrl->cpswEndObj, pMblk); #else /* Processing that passes the Rx packet */ if( semTake( GnSemId_cpswIpDetect, WAIT_FOREVER ) == ERROR ) { goto EndRxErrExit; } if( G_cpswIpdetect == 0 ) { /* case NetWork stack */ END_RCV_RTN_CALL (&pDrvCtrl->cpswEndObj, pMblk); } else { /* case IPDetect */ ARPPktST *pArp = ( ARPPktST * )( ( char * )pMblk->mBlkHdr.mData ); UINT8 *P = ( UINT8 * )&pArp->Type; if( ( *G_cpswIpdetect )( pArp, ( P[0] << 8 ) | P[1] ) ) { netMblkClChainFree( pMblk ); } else { /* call normal receive routine */ END_RCV_RTN_CALL (&pDrvCtrl->cpswEndObj, pMblk); } } if( semGive( GnSemId_cpswIpDetect ) == ERROR ) { goto EndRxErrExit; } #endif } } EndRxErrExit: (void) semGive(pSwCtrl->cpswDevSem); vxAtomicSet(&pDrvCtrl->cpswRxIntPend, FALSE); /* * enable the rx channel interrupt which is relative * with current pDrvCtrl again */ if (pDrvCtrl->cpswPolling == FALSE) { key = intLock(); CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_INTMASK_SET, BIT(pDrvCtrl->cpswRxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_RX_EN, BIT(pDrvCtrl->cpswRxDmaChan)); intUnlock(key); } } /******************************************************************************* * * cpswEndTxHandle - process TX completion events * * This function is scheduled by the ISR to run in the context of tNetTask * whenever an TX interrupt is received. It runs through all of the * TX register pairs and checks the TX status to see how many have * completed. For each completed transmission, the associated TX mBlk * is released, and the outbound packet stats are updated. * * If the transmitter has stalled, this routine will also call muxTxRestart() * to drain any packets that may be waiting in the protocol send queues, * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndTxHandle(void *pArg) { QJOB *job = pArg; CPSW_DRV_CTRL *pDrvCtrl = member_to_object(job, CPSW_DRV_CTRL, cpswTxQJob); BOOL restart = FALSE; CPSW_DESC *desc; M_BLK_ID pMblk; int key; (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); (void) END_TX_SEM_TAKE(&pDrvCtrl->cpswEndObj, WAIT_FOREVER); if (pDrvCtrl->cpswTxQHead == CPSW_TXQ_INVALID) { goto out; } while (pDrvCtrl->cpswTxFree < CPSW_DESC_CNT) { desc = pDrvCtrl->cpswTxDescMem + pDrvCtrl->cpswTxQHead; if (desc->flags & CPSW_OWNERSHIP) { break; } if ((desc->flags & CPSW_EOQ) && (desc->link)) { CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan), desc->link); } CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_CP(pDrvCtrl->cpswTxDmaChan), desc->phys); pMblk = pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead]; if (pMblk) { endPoolTupleFree(pMblk); pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead] = NULL; } pDrvCtrl->cpswTxFree++; CPSW_DESC_INC(pDrvCtrl->cpswTxQHead, CPSW_DESC_CNT); } out: if (pDrvCtrl->cpswTxstall == TRUE) { pDrvCtrl->cpswTxstall = FALSE; restart = TRUE; } (void) END_TX_SEM_GIVE(&pDrvCtrl->cpswEndObj); (void) semGive(pSwCtrl->cpswDevSem); if (restart == TRUE) muxTxRestart(pDrvCtrl); vxAtomicSet(&pDrvCtrl->cpswTxIntPend, FALSE); /* * enable the tx channel interrupt which is relative * with current pDrvCtrl again */ if (pDrvCtrl->cpswPolling == FALSE) { key = intLock(); CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_INTMASK_SET, BIT(pDrvCtrl->cpswTxDmaChan)); CSR_SET_BIT(pSwCtrl, pSwCtrl->wrOffset + CPSW_WR_C0_TX_EN, BIT(pDrvCtrl->cpswTxDmaChan)); intUnlock(key); } } /******************************************************************************* * * cpswEndMiscHandle - reset hardware when need * * This routine reset hardware when need. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cpswEndMiscHandle(void *pArg) { QJOB *job = pArg; CPSW_DRV_CTRL *pDrvCtrl = member_to_object(job, CPSW_DRV_CTRL, cpswMiscQJob); VXB_DEVICE_ID pDev = pDrvCtrl->pDev; /* reset hardware */ if (muxDevStop(pDrvCtrl->cookie) != OK) { logMsg("%s%d muxDevStop return ERROR\n", (_Vx_usr_arg_t) pDev->pName, (_Vx_usr_arg_t) pDev->unitNumber, 3, 4, 5, 6); return; } if (muxDevStart(pDrvCtrl->cookie) != OK) { logMsg("%s%d muxDevStart return ERROR\n", (_Vx_usr_arg_t) pDev->pName, (_Vx_usr_arg_t) pDev->unitNumber, 3, 4, 5, 6); return; } /* enable int again */ ECSR_WRITE_4(pSwCtrl, CPSW_DMA_BAR(pSwCtrl) + CPDMA_INTMASK_SET, 0x2); } /******************************************************************************* * * cpswEndEncap - encpswCapsulate an outbound packet in the TX ring * * This function transmits the packet specified in <pMblk>. * * RETURNS: EAGAIN if ring is full, otherwise OK. * * ERRNO: N/A */ LOCAL int cpswEndEncap(CPSW_DRV_CTRL *pDrvCtrl, M_BLK_ID pMblk) { CPSW_DESC *desc = NULL; CPSW_DESC *head = NULL; CPSW_DESC *prev = NULL;/* (struct cpsw_desc*)malloc(sizeof(struct cpsw_desc));*/ UINT32 headIndex; M_BLK_ID pCurr; int nFrags = 0; #ifdef CPSW_DBG VXB_DEVICE_ID pDev = pDrvCtrl->pDev; #endif /* CPSW_DBG */ for (pCurr = pMblk; pCurr != NULL; pCurr = pCurr->m_next) { if (pCurr->m_len != 0) nFrags++; } if (nFrags > pDrvCtrl->cpswTxFree) { DBG_MSG("%s%d, no engough tx desc, nFrags[%d], cpswTxFree[%d]\n", pDev->pName, pDev->unitNumber, nFrags, pDrvCtrl->cpswTxFree); return (ENOSPC); } pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxIdx] = pMblk; headIndex = pDrvCtrl->cpswTxIdx; head = &pDrvCtrl->cpswTxDescMem[headIndex]; /* setup descriptors for all the segments */ for (pCurr = pMblk; pCurr != NULL; pCurr = pCurr->m_next) { desc = &pDrvCtrl->cpswTxDescMem[pDrvCtrl->cpswTxIdx]; desc->buf = mtod(pCurr, UINT32); desc->offset = 0; /* * According to ethernet standard, minimum frame size is 64 bytes, * including packet data and FCS(frame check sequence). * When packet size + FCS(4bytes) is smaller than minimum frame size, * some padding bytes need to be added. * Here insure packet size is equal to or larger than 60 bytes. */ desc->len = pCurr->m_len < CPSW_MIN_PKT_PADDING ? CPSW_MIN_PKT_PADDING : pCurr->m_len; desc->flags = CPSW_SOP | CPSW_EOP | CPSW_OWNERSHIP | CPSW_TO_PORT_EN | (pDrvCtrl->portIndex << CPSW_TO_PORT_SHIFT) | desc->len; if (prev) prev->link = desc->phys; /* make sure data is coherent */ (void) cacheFlush(DATA_CACHE, pCurr->m_data, pCurr->m_len); prev = desc; CPSW_DESC_INC(pDrvCtrl->cpswTxIdx, CPSW_DESC_CNT); pDrvCtrl->cpswTxFree--; } /* Following Klocwork analysis: Make sure the pointer is defined before derefnrencing it - Benoit TANI*/ if(prev != NULL) prev->link = 0; /* if current tx queue is empty, trigger a new transfer */ if (pDrvCtrl->cpswTxQHead == CPSW_TXQ_INVALID) { pDrvCtrl->cpswTxQHead = headIndex; CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan), head->phys); } /* else add the packet to current queue also detect mis queue event */ else { prev = &pDrvCtrl->cpswTxDescMem[ headIndex == 0 ? CPSW_DESC_CNT - 1 : headIndex - 1]; prev->link = head->phys; if ((prev->flags & (CPSW_OWNERSHIP | CPSW_EOQ)) == CPSW_EOQ) { prev->flags &= ~CPSW_EOQ; CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan), head->phys); } } return OK; } /******************************************************************************* * * cpswEndSend - transmit a packet * * This function transmits the packet specified in <pMblk>. * * RETURNS: OK, ERROR, or END_ERR_BLOCK. * * ERRNO: N/A */ LOCAL int cpswEndSend(END_OBJ *pEnd, M_BLK_ID pMblk) { CPSW_DRV_CTRL *pDrvCtrl; VXB_DEVICE_ID pDev; UINT32 rval; M_BLK_ID pTmp = NULL; short Ipdetect = 0; pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; pDev = pDrvCtrl->pDev; if((pDrvCtrl->portIndex)==1) { Ipdetect = G_cpsw0IpDetectPermission; } else { Ipdetect = G_cpsw1IpDetectPermission; } /* IP Detect controll send */ if (Ipdetect & 0x01) { endPoolTupleFree(pMblk); return (ERROR); } (void) semTake(pSwCtrl->cpswDevSem, WAIT_FOREVER); END_TX_SEM_TAKE(pEnd, WAIT_FOREVER); if (pDrvCtrl->cpswPolling == TRUE) { endPoolTupleFree(pMblk); END_TX_SEM_GIVE(pEnd); (void) semGive(pSwCtrl->cpswDevSem); DBG_MSG("%s%d sending while in polling mode!\n", pDev->pName, pDev->unitNumber); return ERROR; } if (pDrvCtrl->cpswTxFree == 0) { goto txBlock; } if (pMblk->m_next != NULL) { if ((pTmp = endPoolTupleGet(pDrvCtrl->cpswEndObj.pNetPool)) == NULL) goto txBlock; pTmp->m_len = pTmp->m_pkthdr.len = netMblkToBufCopy(pMblk, mtod(pTmp, char *), NULL); pTmp->m_flags = pMblk->m_flags; rval = cpswEndEncap(pDrvCtrl, pTmp); if (rval == OK) endPoolTupleFree(pMblk); else endPoolTupleFree(pTmp); } else { rval = cpswEndEncap(pDrvCtrl, pMblk); } if (rval != OK) { goto txBlock; } END_TX_SEM_GIVE(pEnd); (void) semGive(pSwCtrl->cpswDevSem); return (OK); txBlock: pDrvCtrl->cpswTxstall = TRUE; END_TX_SEM_GIVE(pEnd); (void) semGive(pSwCtrl->cpswDevSem); return (END_ERR_BLOCK); } /******************************************************************************* * * cpswEndPollSend - polled mode transmit routine * * This function is similar to the cpswEndSend() routine shown above, except * it performs transmissions synchronously with interrupts disabled. After * the transmission is initiated, the routine will poll the state of the * TX status register associated with the current slot until transmission * completed. * * RETURNS: OK, EAGAIN, or ERROR * * ERRNO: N/A */ LOCAL STATUS cpswEndPollSend(END_OBJ *pEnd, M_BLK_ID pMblk) { M_BLK_ID pTemp; UINT32 val; CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; CPSW_DESC *desc; int i; #ifdef CPSW_DBG VXB_DEVICE_ID pDev = pDrvCtrl->pDev; #endif /* CPSW_DBG */ short Ipdetect = 0; if((pDrvCtrl->portIndex)==1) { Ipdetect = G_cpsw0IpDetectPermission; } else { Ipdetect = G_cpsw1IpDetectPermission; } if (gnLBTflag2 == FALSE) { /* IP Detect controll send */ if (Ipdetect & 0x01) { DBG_MSG("cpswEndPollSend(:%d) (pMblk)blocked. \n", __LINE__); return ( ERROR); } } if (pDrvCtrl->cpswPolling == FALSE) return ERROR; pTemp = pDrvCtrl->cpswPollbuf; pTemp->m_len = pTemp->m_pkthdr.len = netMblkToBufCopy(pMblk, mtod(pTemp, char *), NULL); CPSWDRV_LOGMSG(CPSWDRV_DEBUG_POLL, "(:%d) poll send () ...\n", pTemp->m_len, 2, 3, 4, 5, 6); pTemp->m_pkthdr.csum_flags = pMblk->m_pkthdr.csum_flags; pTemp->m_pkthdr.csum_data = pMblk->m_pkthdr.csum_data; pTemp->m_pkthdr.vlan = pMblk->m_pkthdr.vlan; if (cpswEndEncap(pDrvCtrl, pTemp) != OK) { return EAGAIN; } for (i = 0; i < CPSW_TIMEOUT_VAL; i++) { val = CPDMA_REG_READ(pSwCtrl, CPDMA_TX_HDP(pDrvCtrl->cpswTxDmaChan)); if (val == 0) break; } if (i >= CPSW_TIMEOUT_VAL) { DBG_MSG("%s%d cpswEndPollSend timeout!\n", pDev->pName, pDev->unitNumber); return ERROR; } desc = &pDrvCtrl->cpswTxDescMem[pDrvCtrl->cpswTxQHead]; pTemp = pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead]; if (pTemp) { pDrvCtrl->cpswTxblk[pDrvCtrl->cpswTxQHead] = NULL; } pDrvCtrl->cpswTxFree++; CPDMA_REG_WRITE(pSwCtrl, CPDMA_TX_CP(pDrvCtrl->cpswTxDmaChan), desc->phys); CPSW_DESC_INC(pDrvCtrl->cpswTxQHead, CPSW_DESC_CNT); CPSWDRV_LOGMSG(CPSWDRV_DEBUG_POLL, "poll send ok\n", 1, 2, 3, 4, 5, 6); return OK; } /******************************************************************************* * * cpswEndPollReceive - polled mode receive routine * * This function receives a packet in polled mode, with interrupts disabled. * It's similar in operation to the cpswEndRxHandle() routine, except it * doesn't process more than one packet at a time and does not load out * buffers. Instead, the caller supplied an mBlk tuple into which this * function will place the received packet. * * RETURNS: OK, or ERROR if operation failed. * * ERRNO: N/A */ LOCAL int cpswEndPollReceive(END_OBJ *pEnd, M_BLK_ID pMblk) { CPSW_DRV_CTRL *pDrvCtrl = (CPSW_DRV_CTRL *) pEnd; CPSW_DESC *pDesc; M_BLK_ID pPkt; UINT32 inPktLen; STATUS ret = OK; UINT32 val; UINT32 status; #ifdef CPSW_DBG VXB_DEVICE_ID pDev = pDrvCtrl->pDev; #endif if (pDrvCtrl->cpswPolling == FALSE) { CPSWDRV_LOGMSG(CPSWDRV_DEBUG_POLL, "%d cpswEndPollReceive pMblk \n", __LINE__, 2, 3, 4, 5, 6); return ERROR; } if (!(pMblk->m_flags & M_EXT)) { DBG_MSG("%s%d cpswEndPollReceive pMblk has no M_EXT\n", pDev->pName, pDev->unitNumber); return ERROR; } pDesc = &pDrvCtrl->cpswRxDescMem[pDrvCtrl->cpswRxIdx]; if ((pDesc->flags & CPSW_OWNERSHIP) != 0x0) { CPSWDRV_LOGMSG(CPSWDRV_DEBUG_POLL, "%d cpswEndPollReceive pMblk \n", __LINE__, 2, 3, 4, 5, 6); return (EAGAIN); } val = CPDMA_REG_READ(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan)); inPktLen = pDesc->flags & CPSW_PKT_LEN_MASK; status = pDesc->flags & CPSW_EOQ; if (!(pDesc->flags & CPSW_PKT_ERROR) && inPktLen) { pPkt = pDrvCtrl->cpswRxblk[pDrvCtrl->cpswRxIdx]; (void) cacheInvalidate(DATA_CACHE, pPkt->m_data, pPkt->m_len); if (pDesc->flags & CPSW_PASS_CRC) pMblk->m_len = pMblk->m_pkthdr.len = inPktLen - ETHER_CRC_LEN; else pMblk->m_len = pMblk->m_pkthdr.len = inPktLen; pMblk->m_flags |= M_PKTHDR; bcopy(mtod(pPkt, char *), mtod(pMblk, char *), pMblk->m_len); } else { CPSWDRV_LOGMSG(CPSWDRV_DEBUG_POLL, "%d cpswEndPollReceive pMblk \n", __LINE__, 2, 3, 4, 5, 6); ret = ERROR; } CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_CP(pDrvCtrl->cpswRxDmaChan), val); pDesc->len = CPSW_MTU; pDesc->offset = 0; pDesc->flags = CPSW_OWNERSHIP; CPSW_DESC_INC(pDrvCtrl->cpswRxIdx, CPSW_DESC_CNT); pDesc->link = 0; pDrvCtrl->cpswRxDescTail->link = pDesc->phys; pDrvCtrl->cpswRxDescTail = pDesc; pDesc = &pDrvCtrl->cpswRxDescMem[pDrvCtrl->cpswRxIdx]; if (status) { CPDMA_REG_WRITE(pSwCtrl, CPDMA_RX_HDP(pDrvCtrl->cpswRxDmaChan), pDesc->phys); } return ret; } /******************************************************************************* * * cpswPHYPowerDown - Power down PHY * * This function is just used for PFO case * * RETURNS: N/A * * ERRNO: N/A */ void cpswPHYPowerDown ( VXB_DEVICE_ID pDev ) { #if 0 CPSW_DRV_CTRL * pDrvCtrl = (CPSW_DRV_CTRL *) endFindByName( CPSW_NAME, 0); if(pDrvCtrl==NULL){ return; } MII_DRV_CTRL * pMiiDrvCtrl; pMiiDrvCtrl = (MII_DRV_CTRL *)pDev->pDrvCtrl; miiBusWrite (pDrvCtrl->pDev, pMiiDrvCtrl->miiPhyAddr, MII_CTRL_REG, MII_CR_POWER_DOWN); #else sysGpioSetValue(18, 0); sysGpioSetValue(20, 0); #endif }