/* vxbFtGmacEnd.c - GMAC driver */ /* * * This program is OPEN SOURCE software: you can redistribute it and/or modify it; * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #define END_MACROS #include #include #include #include #include #include #include #include <../src/hwif/h/mii/miiBus.h> #include <../src/hwif/h/vxbus/vxbAccess.h> #include <../src/hwif/h/hEnd/hEnd.h> #include "vxbFtGmacEnd.h" IMPORT int uartf(const char * fmt, ...); #define GMAC_MTU 1500 #define MAC_MAX_FRAME_SZ (2048) #define RX_FRAME_ERROR_TYPE1 \ (RDES0_DESCRIPTOR_ERROR | RDES0_SA_FILTER_FAIL | \ RDES0_OVERFLOW_ERROR | RDES0_IPC_CSUM_ERROR | RDES0_COLLISION | \ RDES0_CRC_ERROR | RDES0_DRIBBLING | RDES0_LENGTH_ERROR | RDES0_MII_ERROR) #define RX_FRAME_ERROR_TYPE2 \ (/*RDES0_DRIBBLING |*/ RDES0_LENGTH_ERROR | RDES0_MII_ERROR) #define GMAC_END_DEBUG #ifdef GMAC_END_DEBUG # define GMAC_DEBUG(fmt,a,b,c,d,e,f) \ if (_func_logMsg) \ (* _func_logMsg) (fmt,a,b,c,d,e,f) #else /* GMAC_END_DEBUG */ # define GMAC_DEBUG(fmt,a,b,c,d,e,f) #endif /* GMAC_END_DEBUG */ /* forward declarations */ IMPORT FUNCPTR _func_m2PollStatsIfPoll; IMPORT STATUS sysNetMacNVRamAddrGet (char *, int, UINT8 *, int); /* VxBus methods */ LOCAL void ftGmacInstInit (VXB_DEVICE_ID); LOCAL void ftGmacInstInit2 (VXB_DEVICE_ID); LOCAL void ftGmacInstConnect (VXB_DEVICE_ID); LOCAL STATUS ftGmacInstUnlink (VXB_DEVICE_ID, void *); /* miiBus methods */ LOCAL STATUS ftGmacPhyRead (VXB_DEVICE_ID, UINT8, UINT8, UINT16 *); LOCAL STATUS ftGmacPhyWrite (VXB_DEVICE_ID, UINT8, UINT8, UINT16); LOCAL STATUS ftGmacLinkUpdate (VXB_DEVICE_ID); LOCAL STATUS ftGmacReset (VXB_DEVICE_ID); /* mux methods */ LOCAL void ftGmacMuxConnect (VXB_DEVICE_ID, void *); LOCAL int ftGmacPhyAddrGet (VXB_DEVICE_ID pDev); LOCAL struct drvBusFuncs ftGmacFuncs = { ftGmacInstInit, /* devInstanceInit */ ftGmacInstInit2, /* devInstanceInit2 */ ftGmacInstConnect /* devConnect */ }; LOCAL struct vxbDeviceMethod ftGmacMethods[] = { DEVMETHOD(miiRead, ftGmacPhyRead), DEVMETHOD(miiWrite, ftGmacPhyWrite), DEVMETHOD(miiMediaUpdate, ftGmacLinkUpdate), DEVMETHOD(muxDevConnect, ftGmacMuxConnect), DEVMETHOD(vxbDrvUnlink, ftGmacInstUnlink), { 0, 0 } }; /* default queue parameters */ LOCAL HEND_RX_QUEUE_PARAM ftGmacRxQueueDefault = { 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 ftGmacTxQueueDefault = { NULL, /* jobQueId */ 0, /* priority */ 0, /* tbdNum */ 0, /* allowedFrags */ NULL, /* pTxBdBase */ 0, /* txBdMemSize */ 0, /* txBdMemAttributes */ NULL /* txBdMemFreeMethod */ }; LOCAL VXB_PARAMETERS ftGmacParamDefaults[] = { {"rxQueue00", VXB_PARAM_POINTER, {(void *)&ftGmacRxQueueDefault}}, {"txQueue00", VXB_PARAM_POINTER, {(void *)&ftGmacTxQueueDefault}}, {NULL, VXB_PARAM_END_OF_LIST, {NULL}} }; LOCAL struct vxbPlbRegister ftGmacDevPlbRegistration = { { NULL, /* pNext */ VXB_DEVID_DEVICE, /* devID */ VXB_BUSID_PLB, /* busID = PLB */ VXB_VER_4_0_0, /* vxbVersion */ GMAC_NAME, /* drvName */ &ftGmacFuncs, /* pDrvBusFuncs */ NULL, /* pMethods */ NULL, /* devProbe */ ftGmacParamDefaults /* pParamDefaults */ }, }; /* END functions */ /*LOCAL STATUS ftGmacReset (VXB_DEVICE_ID pDev);*/ LOCAL END_OBJ * ftGmacEndLoad (char *, void *); LOCAL STATUS ftGmacEndUnload (END_OBJ *); LOCAL int ftGmacEndIoctl (END_OBJ *, int, caddr_t); LOCAL STATUS ftGmacEndMCastAddrAdd (END_OBJ *, char *); LOCAL STATUS ftGmacEndMCastAddrDel (END_OBJ *, char *); LOCAL STATUS ftGmacEndMCastAddrGet (END_OBJ *, MULTI_TABLE *); LOCAL void ftGmacEndHashTblPopulate (GMAC_DRV_CTRL *); LOCAL STATUS ftGmacEndStatsDump (GMAC_DRV_CTRL *); LOCAL void ftGmacEndRxConfig (GMAC_DRV_CTRL *); LOCAL STATUS ftGmacEndStart (END_OBJ *); LOCAL STATUS ftGmacEndStop (END_OBJ *); LOCAL int ftGmacEndSend (END_OBJ *, M_BLK_ID); LOCAL STATUS ftGmacEndPollSend (END_OBJ *, M_BLK_ID); LOCAL int ftGmacEndPollReceive (END_OBJ *, M_BLK_ID); LOCAL void ftGmacEndInt (GMAC_DRV_CTRL *); LOCAL void ftGmacEndRxHandle ( GMAC_DRV_CTRL * pDrvCtrl); LOCAL void ftGmacEndTxHandle ( GMAC_DRV_CTRL * pDrvCtrl); LOCAL void ftGmacEndIntHandle (void *); LOCAL void ftGmacRxDescInit(GMAC_DESC * desc, int index, UINT32 phyAddr); LOCAL NET_FUNCS ftGmacNetFuncs = { ftGmacEndStart, /* start func. */ ftGmacEndStop, /* stop func. */ ftGmacEndUnload, /* unload func. */ ftGmacEndIoctl, /* ioctl func. */ ftGmacEndSend, /* send func. */ ftGmacEndMCastAddrAdd, /* multicast add func. */ ftGmacEndMCastAddrDel, /* multicast delete func. */ ftGmacEndMCastAddrGet, /* multicast get fun. */ ftGmacEndPollSend, /* polling send func. */ ftGmacEndPollReceive, /* polling receive func. */ endEtherAddressForm, /* put address info into a NET_BUFFER */ endEtherPacketDataGet, /* get pointer to data in NET_BUFFER */ endEtherPacketAddrGet /* Get packet addresses */ }; /******************************************************************************* * * ftGmacRegister - register with the VxBus subsystem * * This routine registers the ftGmac end driver with VxBus * * RETURNS: N/A * * ERRNO: N/A */ void ftGmacRegister(void) { vxbDevRegister((struct vxbDevRegInfo *)&ftGmacDevPlbRegistration); return; } /******************************************************************************* * * ftGmacInstInit - VxBus instInit handler * * This function implements the VxBus instInit handler for an ftGmac * device instance. The only thing done here is to select a unit * number for the device. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacInstInit ( VXB_DEVICE_ID pDev ) { { const struct hcfDevice * pHcf; /* always use the unit number allocated in the hwconf.c file */ pHcf = hcfDeviceGet (pDev); if (pHcf != NULL) vxbInstUnitSet (pDev, pHcf->devUnit); /* publish methods */ pDev->pMethods = ftGmacMethods; } return; } /******************************************************************************* * * ftGmacInstInit2 - VxBus instInit2 handler * * This function implements the VxBus instInit2 handler for an ftGmac * 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, connect * our ISR to our assigned interrupt vector, read the station * address from NVRAM. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacInstInit2 ( VXB_DEVICE_ID pDev ) { GMAC_DRV_CTRL * pDrvCtrl; const struct hcfDevice * pHcf; FUNCPTR ifCachedFunc = NULL; BOOL exFlag = FALSE; pDrvCtrl = malloc (sizeof (GMAC_DRV_CTRL)); if (pDrvCtrl == NULL) { return; } pHcf = hcfDeviceGet(pDev); if (pHcf == NULL) { free (pDrvCtrl); return; } bzero ((char *)pDrvCtrl, sizeof(GMAC_DRV_CTRL)); pDev->pDrvCtrl = pDrvCtrl; pDrvCtrl->gmacDev = pDev; pDrvCtrl->unit = pDev->unitNumber; pDrvCtrl->gmacBar = pDev->pRegBase[0]; vxbRegMap (pDev, 0, &pDrvCtrl->gmacHandle); pDrvCtrl->gmacDevSem = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); SPIN_LOCK_ISR_INIT (&pDrvCtrl->gmacLock, 0); if (pHcf != NULL) { (void) devResourceGet (pHcf, "ifCached", HCF_RES_ADDR, (void *) &ifCachedFunc); if (ifCachedFunc != NULL) pDrvCtrl->ifCached = ifCachedFunc; else pDrvCtrl->ifCached = NULL; (void) devResourceGet (pHcf, "isExPhy", HCF_RES_INT, (void *) &exFlag); pDrvCtrl->gmacExPhy = exFlag; } (void) vxbIntConnect (pDev, 0, ftGmacEndInt, pDrvCtrl); pDrvCtrl->gmacMiiSem = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); if ((pDrvCtrl->gmacRxDescMem = cacheDmaMalloc (sizeof(GMAC_DESC) * GMAC_RX_DESC_CNT + sizeof(GMAC_DESC) * GMAC_TX_DESC_CNT + 128)) == NULL) printf ("gmac%d: could not allocate descriptor memory\n", pDev->unitNumber, 0, 0, 0, 0, 0); pDrvCtrl->gmacRxDescMem = (GMAC_DESC *)ROUND_UP (pDrvCtrl->gmacRxDescMem, 128); pDrvCtrl->gmacTxDescMem = (GMAC_DESC *)(pDrvCtrl->gmacRxDescMem + GMAC_RX_DESC_CNT); /*pDrvCtrl->gmacIntrs = GEM_INTRS;*/ /* reset the device */ /*ftGmacReset(pDev);*/ return; } /******************************************************************************* * * ftGmacInstConnect - VxBus instConnect handler * * This function implements the VxBus instConnect handler for an ftGmac * device instance. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacInstConnect ( VXB_DEVICE_ID pDev ) { return; } /******************************************************************************* * * ftGmacInstUnlink - VxBus unlink handler * * This function shuts down an ftGmac device instance in response to an * unlink event from VxBus. This may occur if our VxBus instance has * been terminated, or if the ftGmac driver has been unloaded. When an * unlink event occurs, we must shut down and unload the END interface * associated with this device instance and then release all the * resources allocated during instance creation. We also must destroy * our child miiBus and PHY devices. * * RETURNS: OK if device was successfully destroyed, otherwise ERROR * * ERRNO: N/A */ LOCAL STATUS ftGmacInstUnlink ( VXB_DEVICE_ID pDev, void * unused ) { GMAC_DRV_CTRL * pDrvCtrl; pDrvCtrl = pDev->pDrvCtrl; /* * Stop the device and detach from the MUX. * Note: it's possible someone might try to delete * us after our vxBus instantiation has completed, * but before anyone has called our muxConnect method. * In this case, there'll be no MUX connection to * tear down, so we can skip this step. */ if (pDrvCtrl->gmacMuxDevCookie != NULL) { if (muxDevStop (pDrvCtrl->gmacMuxDevCookie) != OK) { return (ERROR); } /* Detach from the MUX. */ if (muxDevUnload (GMAC_NAME, pDev->unitNumber) != OK) { return (ERROR); } } /* free descBuf */ (void) cacheDmaFree (pDrvCtrl->gmacRxDescMem); /* Disconnect the ISR. */ vxbIntDisconnect (pDev, 0, ftGmacEndInt, pDrvCtrl); /* Destroy our MII bus and child PHYs. */ if (pDrvCtrl->gmacMiiBus != NULL) { (void) miiBusDelete (pDrvCtrl->gmacMiiBus); (void) semDelete (pDrvCtrl->gmacMiiSem); } /* Destroy the adapter context. */ free (pDrvCtrl); pDev->pDrvCtrl = NULL; return (OK); } LOCAL STATUS ftGmacMiiTimeoutPoll (VXB_DEVICE_ID pDev, UINT32 offset, UINT32 value, UINT32 timeout) { volatile UINT32 counter; volatile UINT32 status; counter = 0; do { status = CSR_READ_4(pDev, offset) & value; }while((counter++ < timeout) && (status == value)); if(counter >= timeout) { printf(" ftGmacMiiTimeoutPoll !!!!!. offset: 0x%0x value: 0x%x \n", offset, value); return ERROR; } else return OK; } /******************************************************************************* * * ftGmacPhyRead - miiBus miiRead method * * This function implements an miiRead() method that allows PHYs on the miiBus * to access our MII managmacent registers. * * RETURNS: ERROR if invalid PHY addr or register is specified, else OK * * ERRNO: N/A */ LOCAL STATUS ftGmacPhyRead ( VXB_DEVICE_ID pDev, UINT8 phyAddr, UINT8 regAddr, UINT16 * dataVal ) { GMAC_DRV_CTRL * pDrvCtrl; UINT32 mii_val; pDrvCtrl = pDev->pDrvCtrl; if (pDrvCtrl->gmacMiiDev == NULL) { *dataVal = 0xFFFF; return (ERROR); } if ((pDrvCtrl->gmacMiiPhyAddr != -1) && (phyAddr != pDrvCtrl->gmacMiiPhyAddr) && (phyAddr < 32)) { *dataVal = 0xFFFF; return (ERROR); } semTake (pDrvCtrl->gmacMiiSem, WAIT_FOREVER); mii_val = 0; mii_val |= (pDrvCtrl->clkMDC<<2) & 0x0000003C; mii_val |= (phyAddr << 11) & 0x0000F800; mii_val |= (regAddr << 6) & 0x000007C0; mii_val |= MII_BUSY; if(ftGmacMiiTimeoutPoll(pDev, GMAC_MII_ADDR, MII_BUSY, 10000000) == -1) { *dataVal = 0xFFFF; semGive (pDrvCtrl->gmacMiiSem); return ERROR; } CSR_WRITE_4(pDev, GMAC_MII_ADDR, mii_val); if(ftGmacMiiTimeoutPoll(pDev, GMAC_MII_ADDR, MII_BUSY, 10000000) == -1) { *dataVal = 0xFFFF; semGive (pDrvCtrl->gmacMiiSem); return ERROR; } *dataVal = CSR_READ_4(pDev, GMAC_MII_DATA); semGive (pDrvCtrl->gmacMiiSem); return OK; } /******************************************************************************* * * ftGmacPhyWrite - miiBus miiWrite method * * This function implements an miiWrite() method that allows PHYs on the miiBus * to access our MII managmacent registers. * * RETURNS: ERROR if invalid PHY addr or register is specified, else OK * * ERRNO: N/A */ LOCAL STATUS ftGmacPhyWrite ( VXB_DEVICE_ID pDev, UINT8 phyAddr, UINT8 regAddr, UINT16 dataVal ) { GMAC_DRV_CTRL * pDrvCtrl; UINT32 mii_val; pDrvCtrl = pDev->pDrvCtrl; if (pDrvCtrl->gmacMiiDev == NULL) { return (ERROR); } if (phyAddr != pDrvCtrl->gmacMiiPhyAddr && phyAddr < 32) { return (ERROR); } semTake (pDrvCtrl->gmacMiiSem, WAIT_FOREVER); mii_val = 0; mii_val |= (pDrvCtrl->clkMDC<<2) & 0x0000003C; mii_val |= (phyAddr << 11) & 0x0000F800; mii_val |= (regAddr << 6) & 0x000007C0; mii_val |= MII_BUSY|MII_WRITE; CSR_WRITE_4 (pDev, GMAC_MII_DATA, dataVal); CSR_WRITE_4(pDev, GMAC_MII_ADDR, mii_val); if(ftGmacMiiTimeoutPoll(pDev, GMAC_MII_ADDR, MII_BUSY, 1000000) == -1) { uartf("phy write error\r\n"); semGive (pDrvCtrl->gmacMiiSem); return ERROR; } semGive (pDrvCtrl->gmacMiiSem); return OK; } /******************************************************************************* * * ftGmacLinkUpdate - 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 ftGmacLinkUpdate ( VXB_DEVICE_ID pDev ) { GMAC_DRV_CTRL * pDrvCtrl; UINT32 oldStatus; if (pDev->pDrvCtrl == NULL) { return (ERROR); } pDrvCtrl = (GMAC_DRV_CTRL *)pDev->pDrvCtrl; if (pDrvCtrl->gmacMiiBus == NULL) { return (ERROR); } semTake (pDrvCtrl->gmacDevSem, WAIT_FOREVER); oldStatus = pDrvCtrl->gmacCurStatus; if (miiBusModeGet(pDrvCtrl->gmacMiiBus, &pDrvCtrl->gmacCurMedia, &pDrvCtrl->gmacCurStatus) == ERROR) { semGive (pDrvCtrl->gmacDevSem); return (ERROR); } /* configure full duplex mode accordingly */ if (pDrvCtrl->gmacCurMedia & IFM_FDX) { CSR_SETBIT_4 (pDev, GMAC_CONTROL, CFG_DPLX_MODE); } else { CSR_CLRBIT_4 (pDev, GMAC_CONTROL, CFG_DPLX_MODE); } if (!(pDrvCtrl->gmacEndObj.flags & IFF_UP)) { semGive (pDrvCtrl->gmacDevSem); return (OK); } /* if status went from down to up, announce link up */ if (pDrvCtrl->gmacCurStatus & IFM_ACTIVE && !(oldStatus & IFM_ACTIVE)) { switch (IFM_SUBTYPE (pDrvCtrl->gmacCurMedia)) { case IFM_1000_T: /* clear MII port, select gmac mode */ CSR_CLRBIT_4 (pDev, GMAC_CONTROL, CFG_MII_PORT_SEL); pDrvCtrl->gmacEndObj.mib2Tbl.ifSpeed = 1000000000; break; case IFM_100_TX: /* select MII port and 100Mbps mode */ CSR_SETBIT_4 (pDev, GMAC_CONTROL, CFG_MII_PORT_SEL | CFG_FES_100); pDrvCtrl->gmacEndObj.mib2Tbl.ifSpeed = 100000000; break; case IFM_10_T: /* select MII port and 10Mbps mode */ CSR_SETBIT_4 (pDev, GMAC_CONTROL, CFG_MII_PORT_SEL); CSR_CLRBIT_4 (pDev, GMAC_CONTROL, CFG_FES_100); pDrvCtrl->gmacEndObj.mib2Tbl.ifSpeed = 10000000; break; default: break; } if (pDrvCtrl->gmacEndObj.pMib2Tbl != NULL) pDrvCtrl->gmacEndObj.pMib2Tbl->m2Data.mibIfTbl.ifSpeed = pDrvCtrl->gmacEndObj.mib2Tbl.ifSpeed; jobQueueStdPost (pDrvCtrl->gmacJobQueue, NET_TASK_QJOB_PRI, muxLinkUpNotify, &pDrvCtrl->gmacEndObj, NULL, NULL, NULL, NULL); } /* if status went from up to down, announce link down */ else if (!(pDrvCtrl->gmacCurStatus & IFM_ACTIVE) && oldStatus & IFM_ACTIVE) { jobQueueStdPost(pDrvCtrl->gmacJobQueue, NET_TASK_QJOB_PRI, muxLinkDownNotify, &pDrvCtrl->gmacEndObj, NULL, NULL, NULL, NULL); } semGive (pDrvCtrl->gmacDevSem); return (OK); } /******************************************************************************* * * ftGmacMuxConnect - 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 ftGmacEndLoad() to create the END interface instance. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacMuxConnect ( VXB_DEVICE_ID pDev, void * unused ) { GMAC_DRV_CTRL * pDrvCtrl; const struct hcfDevice * pHcf; VXB_DEVICE_ID miiDev; char * miiIfName; int miiIfUnit; pDrvCtrl = pDev->pDrvCtrl; /* * Initialize MII bus. * Note that we defer this until the muxConnect stage in this driver, * unlike the others. This is because all of the PHYs are managed through * the GMAC interface, and we have to allow primary initialization of GMAC * to complete before we can start searching for PHYs. */ pHcf = hcfDeviceGet(pDev); if (pHcf == NULL) return; /* * resourceDesc { * The miiIfName resource specifies the name of the * driver that provides the MII interface for this * GMAC unit. On boards that have multiple GMAC * devices, the managmacent pins for all of the PHYs * will all be wired to the MDIO pins on just one * controller. The resource (and the * resource below) are used to tell * each GMAC instance which one is the managmacent * controller. If a device is not the managmacent * controller, it will just forward its PHY register * read and write requests to the one that is. } */ (void) devResourceGet(pHcf, "miiIfName", HCF_RES_STRING, (void *)&miiIfName); /* * resourceDesc { * The miiIfUnit resource specifies the unit number * of the device that provides the MII managmacent * methods for this GMAC instance. } */ (void) devResourceGet(pHcf, "miiIfUnit", HCF_RES_INT, (void *)&miiIfUnit); miiDev = vxbInstByNameFind(miiIfName, miiIfUnit); pDrvCtrl->gmacMiiDev = miiDev; pDrvCtrl->gmacMiiPhyRead = vxbDevMethodGet(miiDev, (UINT32)&miiRead_desc); pDrvCtrl->gmacMiiPhyWrite = vxbDevMethodGet(miiDev, (UINT32)&miiWrite_desc); /* * resourceDesc { * The clkMDC resource specifies the CSR clock range. Use clk_csr_i frequency * to compute MDC clock frequency. To be sure MDC clock is in [1.0MHz ~2.5MHz] } */ (void) devResourceGet(pHcf, "clkMDC", HCF_RES_INT, (void *)&pDrvCtrl->clkMDC); /* * resourceDesc { * The phyAddr resource specifies the MII managmacent * address (0-31) of the PHY for this particular GMAC * device. Each GMAC typically has at least one PHY * allocated to it. } */ if((ERROR == devResourceGet(pHcf, "phyAddr", HCF_RES_INT,(void *)&pDrvCtrl->gmacMiiPhyAddr)) ||(-1 == pDrvCtrl->gmacMiiPhyAddr)) { /* (after ->gmacMiiDev assigned) get phy addr dynamicly... */ pDrvCtrl->gmacMiiPhyAddr = ftGmacPhyAddrGet(pDev); } /* * resourceDesc { * The macAddrFun resource used to get mac address } */ (void) devResourceGet (pHcf, "macAddrSet", HCF_RES_ADDR,(void *)&pDrvCtrl->gmacAddrSet); /* create our MII bus */ if(miiBusCreate (pDev, &pDrvCtrl->gmacMiiBus) == ERROR) { printf(" ftgmac. miiBusCreate creat fail!!! \n"); } miiBusMediaListGet (pDrvCtrl->gmacMiiBus, &pDrvCtrl->gmacMediaList); /* force to 100M FDX miiBusMediaDefaultSet (pDrvCtrl->gmacMiiBus, IFM_ETHER|IFM_100_TX|IFM_FDX); */ miiBusModeSet (pDrvCtrl->gmacMiiBus, pDrvCtrl->gmacMediaList->endMediaListDefault); /* save the cookie */ pDrvCtrl->gmacMuxDevCookie = muxDevLoad(pDev->unitNumber, ftGmacEndLoad, "", TRUE, pDev); if (pDrvCtrl->gmacMuxDevCookie != NULL) { muxDevStart(pDrvCtrl->gmacMuxDevCookie); } if (_func_m2PollStatsIfPoll != NULL) { endPollStatsInit(pDrvCtrl->gmacMuxDevCookie, _func_m2PollStatsIfPoll); } return; } /******************************************************************************* * * ftGmacIsValidMac - Check if the given MAC address is zero * * This routine check if the given MAC address is zero * * RETURNS: TRUE/FALSE * * ERRNO: N/A */ LOCAL BOOL ftGmacIsZeroMac(UINT8 *gmacAddr) { UINT8 i = 0; /* Check that the MAC is 00:00:00:00:00:00*/ for(i = 0; i < ETHER_ADDR_LEN; i++) { if(gmacAddr[i] != 0x0) return FALSE; } return TRUE; } /******************************************************************************* * * ftGmacIsValidMac - Check if the given MAC address is multicast address * * This routine check if the given MAC address is multicast address * * RETURNS: TRUE/FALSE * * ERRNO: N/A */ LOCAL BOOL ftGmacIsmulticastMac(UINT8 *gmacAddr) { UINT8 i = 0; /* Check that the MAC is FF:FF:FF:FF:FF:FF.*/ for(i = 0; i < ETHER_ADDR_LEN; i++) { if(gmacAddr[i] != 0xff) return FALSE; } return TRUE; } /******************************************************************************* * * ftGmacMacSet - get the GMAC mac address * * This routine get the GMAC mac address. * * RETURNS: OK * * ERRNO: N/A */ void ftGmacMacSet(GMAC_DRV_CTRL * pDrvCtrl) { UINT32 macLo; UINT32 macHi; macLo = CSR_READ_4(pDrvCtrl->gmacDev, GMAC_ADDR0_LOW); macHi = CSR_READ_4(pDrvCtrl->gmacDev, GMAC_ADDR0_HIGH); pDrvCtrl->gmacAddr[0] = macLo & 0xFF; pDrvCtrl->gmacAddr[1] = (macLo >> 8) & 0xFF; pDrvCtrl->gmacAddr[2] = (macLo >> 16) & 0xFF; pDrvCtrl->gmacAddr[3] = (macLo >> 24) & 0xFF; pDrvCtrl->gmacAddr[4] = macHi & 0xFF; pDrvCtrl->gmacAddr[5] = (macHi >> 8) & 0xFF; if(((ftGmacIsZeroMac(pDrvCtrl->gmacAddr) == TRUE) || (ftGmacIsmulticastMac(pDrvCtrl->gmacAddr) == TRUE)) && (pDrvCtrl->gmacAddrSet != NULL)) { (*pDrvCtrl->gmacAddrSet)(pDrvCtrl->gmacAddr); } } /******************************************************************************* * * ftGmacReset - reset the GMAC controller * * This routine resets the GMAC controller. * * RETURNS: OK * * ERRNO: N/A */ LOCAL STATUS ftGmacReset ( VXB_DEVICE_ID pDev ) { GMAC_DRV_CTRL * pDrvCtrl; int i; pDrvCtrl = pDev->pDrvCtrl; /* Init DMA module */ CSR_SETBIT_4 (pDev, DMA_BUS_MODE, DMA_BUS_SWR); i = 0; while (i < GMAC_TIMEOUT) { if (!(CSR_READ_4 (pDev, DMA_BUS_MODE) & DMA_BUS_SWR)) break; i++; } if (i >= GMAC_TIMEOUT) { logMsg ("stmGmac DMA module reset error.\n", 1, 2, 3, 4, 5, 6); return (ERROR); } CSR_WRITE_4 (pDev, DMA_BUS_MODE, DMA_BUS_INIT); CSR_SETBIT_4 (pDev, DMA_OP_MODE, DMA_OP_FTF); i = 0; while (i < GMAC_TIMEOUT) { if (!(CSR_READ_4 (pDev, DMA_OP_MODE) & DMA_OP_FTF)) break; i++; } if (i >= GMAC_TIMEOUT) { logMsg ("stmGmac DMA module flush FIFO error.\n", 1, 2, 3, 4, 5, 6); return (ERROR); } CSR_WRITE_4 (pDev, DMA_OP_MODE, DMA_OP_INIT); /* * GMAC Init: * TX/RX checksum offload enable, * 1000Mbps and duplex mode, * Frame burst enable, * disable Rx own, * jabber disable; */ CSR_WRITE_4 (pDev, GMAC_CONTROL, GMAC_INIT); /* disable flow control */ CSR_WRITE_4 (pDev, GMAC_FLOW_CTRL, 0); /* Disable all the MMC Int */ CSR_WRITE_4 (pDev, GMAC_MMC_TXINT_MASK, 0xFFFFFFFF); CSR_WRITE_4 (pDev, GMAC_MMC_RXINT_MASK, 0xFFFFFFFF); CSR_WRITE_4 (pDev, GMAC_INT_MASK, 0xFFFFFFFF); /* DMA status register clear */ CSR_WRITE_4 (pDev, DMA_STATUS, 0xFFFFFFFF); /* DMA INT disable */ CSR_WRITE_4 (pDev, DMA_INT_EN, 0); return (OK); } /******************************************************************************* * * ftGmacEndLoad - 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 was empty. * * ERRNO: N/A */ LOCAL END_OBJ *ftGmacEndLoad ( char * loadStr, void * pArg ) { GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; /* make the MUX happy */ if (loadStr == NULL) { return NULL; } if (loadStr[0] == 0) { bcopy (GMAC_NAME, loadStr, sizeof(GMAC_NAME)); return NULL; } pDev = pArg; pDrvCtrl = pDev->pDrvCtrl; if (END_OBJ_INIT (&pDrvCtrl->gmacEndObj, NULL, pDev->pName, pDev->unitNumber, &ftGmacNetFuncs, "GMAC VxBus END Driver") == ERROR) { GMAC_DEBUG ("%s%d: END_OBJ_INIT failed\n", (int)GMAC_NAME, pDev->unitNumber, 0, 0, 0, 0); return (NULL); } /* get station address */ /* sysNetMacNVRamAddrGet (pDev->pName, pDev->unitNumber, pDrvCtrl->gmacAddr, ETHER_ADDR_LEN); */ endM2Init (&pDrvCtrl->gmacEndObj, M2_ifType_ethernet_csmacd, pDrvCtrl->gmacAddr, ETHER_ADDR_LEN, ETHERMTU, 1000000000, IFF_NOTRAILERS | IFF_SIMPLEX | IFF_MULTICAST | IFF_BROADCAST); /* allocate a buffer pool */ pDrvCtrl->gmacMaxMtu = GMAC_MTU; if (endPoolCreate(3 * GMAC_RX_DESC_CNT, &pDrvCtrl->gmacEndObj.pNetPool) == ERROR) { GMAC_DEBUG ("%s%d: pool creation failed\n", (int)GMAC_NAME, pDev->unitNumber, 0, 0, 0, 0); return (NULL); } pDrvCtrl->gmacPollBuf = endPoolTupleGet(pDrvCtrl->gmacEndObj.pNetPool); /* set up polling stats */ pDrvCtrl->gmacEndStatsConf.ifPollInterval = sysClkRateGet(); pDrvCtrl->gmacEndStatsConf.ifEndObj = &pDrvCtrl->gmacEndObj; pDrvCtrl->gmacEndStatsConf.ifWatchdog = NULL; pDrvCtrl->gmacEndStatsConf.ifValidCounters = (END_IFINUCASTPKTS_VALID | END_IFINMULTICASTPKTS_VALID | END_IFINBROADCASTPKTS_VALID | END_IFINOCTETS_VALID | END_IFINERRORS_VALID | END_IFINDISCARDS_VALID | END_IFOUTUCASTPKTS_VALID | END_IFOUTMULTICASTPKTS_VALID | END_IFOUTBROADCASTPKTS_VALID | END_IFOUTOCTETS_VALID | END_IFOUTERRORS_VALID); /* set up capabilities */ pDrvCtrl->gmacCaps.cap_available = IFCAP_VLAN_MTU | IFCAP_TXCSUM |IFCAP_RXCSUM; pDrvCtrl->gmacCaps.cap_enabled = IFCAP_VLAN_MTU | IFCAP_TXCSUM |IFCAP_RXCSUM; pDrvCtrl->gmacCaps.csum_flags_tx = CSUM_IP|CSUM_TCP|CSUM_UDP; pDrvCtrl->gmacCaps.csum_flags_rx = CSUM_IP|CSUM_UDP|CSUM_TCP; return (&pDrvCtrl->gmacEndObj); } /******************************************************************************* * * ftGmacEndUnload - unload END driver instance * * This routine undoes the effects of ftGmacEndLoad(). The END object * is destroyed, our network pool is released, the endM2 structures * are released, and the polling 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 * * RETURN: ERROR or EALREADY * ERRNO: N/A */ LOCAL STATUS ftGmacEndUnload ( END_OBJ * pEnd ) { GMAC_DRV_CTRL * pDrvCtrl; /* We must be stopped before we can be unloaded. */ if (pEnd->flags & IFF_UP) { return (ERROR); } pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; netMblkClChainFree (pDrvCtrl->gmacPollBuf); /* Relase our buffer pool */ endPoolDestroy (pDrvCtrl->gmacEndObj.pNetPool); /* terminate stats polling */ wdDelete (pDrvCtrl->gmacEndStatsConf.ifWatchdog); endM2Free (&pDrvCtrl->gmacEndObj); END_OBJECT_UNLOAD (&pDrvCtrl->gmacEndObj); return (EALREADY); /* prevent freeing of pDrvCtrl */ } /******************************************************************************* * * ftGmacMacHashClac - calculate a hash checksum * * This routine performs the GEM hash calculation over MAC addresses. * The GEM implements multicast filtering using a hash table where a hash * checksum of the multicast group address is used as the table index. * * RETURNS: the 32-bit checksum of the supplied buffer * * ERRNO: N/A */ UINT32 ftGmacMacHashCalc ( const UINT8 * pBuf ) { UINT32 hash = 0; UINT8 bit[6]; int i; bit[5] = ((pBuf[0] >> 5) ^ (pBuf[1] >> 3) ^ (pBuf[2] >> 1) ^ (pBuf[2] >> 7) ^ (pBuf[3] >> 5) ^ (pBuf[4] >> 3) ^ (pBuf[5] >> 1) ^ (pBuf[5] >> 7)) & 1; bit[4] = ((pBuf[0] >> 4) ^ (pBuf[1] >> 2) ^ (pBuf[2]) ^ (pBuf[2] >> 6) ^ (pBuf[3] >> 4) ^ (pBuf[4] >> 2) ^ (pBuf[5]) ^ (pBuf[5] >> 6)) & 1; bit[3] = ((pBuf[0] >> 3) ^ (pBuf[1] >> 1) ^ (pBuf[1] >> 7) ^ (pBuf[2] >> 5) ^ (pBuf[3] >> 3) ^ (pBuf[4] >> 1) ^ (pBuf[4] >> 7) ^ (pBuf[5] >> 5)) & 1; bit[2] = ((pBuf[0] >> 2) ^ (pBuf[1]) ^ (pBuf[1] >> 6) ^ (pBuf[2] >> 4) ^ (pBuf[3] >> 2) ^ (pBuf[4]) ^ (pBuf[4] >> 6) ^ (pBuf[5] >> 4)) & 1; bit[1] = ((pBuf[0] >> 1) ^ (pBuf[0] >> 7) ^ (pBuf[1] >> 5) ^ (pBuf[2] >> 3) ^ (pBuf[3] >> 1) ^ (pBuf[3] >> 7) ^ (pBuf[4] >> 5) ^ (pBuf[5] >> 3)) & 1; bit[0] = ((pBuf[0]) ^ (pBuf[0] >> 6) ^ (pBuf[1] >> 4) ^ (pBuf[2] >> 2) ^ (pBuf[3]) ^ (pBuf[3] >> 6) ^ (pBuf[4] >> 4) ^ (pBuf[5] >> 2)) & 1; for (i = 0; i < 6; i++) if (bit[i]) hash += (1 << i); return hash; } /******************************************************************************* * * ftBitReverse32 - get 32bit bit reverse value. * * This function compute 32bit bit reverse value. * * RETURNS: bit reversed value. * * ERRNO: N/A */ LOCAL UINT32 ftBitReverse32 ( UINT32 input ) { UINT32 ix = 0; UINT32 output = 0; for (; ix < 32; ix++) { output |= ((input & (1 << ix)) != 0) << (31 - ix); } return output; } /******************************************************************************* * * ftGmacEndHashTblPopulate - populate the multicast hash filter * * This function programs the zynq7k controller's multicast hash * filter to receive frames sent to the multicast groups specified * in the multicast address list attached to the END object. If * the interface is in IFF_ALLMULTI mode, the filter will be * programmed to receive all multicast packets by setting all the * bits in the hash table to one. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacEndHashTblPopulate ( GMAC_DRV_CTRL * pDrvCtrl ) { VXB_DEVICE_ID pDev; UINT32 h; UINT32 hashes[2] = { 0, 0 }; ETHER_MULTI * mCastNode = NULL; pDev = pDrvCtrl->gmacDev; if (pDrvCtrl->gmacEndObj.flags & IFF_ALLMULTI) { /* set all multicast mode */ CSR_WRITE_4 (pDev, GMAC_HASH_LOW, 0xFFFFFFFF); CSR_WRITE_4 (pDev, GMAC_HASH_HIGH, 0xFFFFFFFF); CSR_WRITE_4 (pDev, GMAC_FRAME_FILTER, FFILTER_PASSALL_MCAST); return; } /* first, clear out the original filter */ CSR_WRITE_4 (pDev, GMAC_HASH_LOW, 0); CSR_WRITE_4 (pDev, GMAC_HASH_HIGH, 0); /* now repopulate it */ for (mCastNode = (ETHER_MULTI *)lstFirst (&pDrvCtrl->gmacEndObj.multiList); mCastNode != NULL; mCastNode = (ETHER_MULTI *)lstNext (&mCastNode->node)) { /* * The upper 6 bits of the calculated CRC are used to * index the contens of the hash table. */ h = endEtherCrc32LeGet((const UINT8 *) mCastNode->addr, ETHER_ADDR_LEN); h = ftBitReverse32 (~h) >> 26; hashes[h >> 5] |= 1 << (h & 31); } /* reload filter */ CSR_WRITE_4 (pDev, GMAC_HASH_LOW, hashes[0]); CSR_WRITE_4 (pDev, GMAC_HASH_HIGH, hashes[1]); CSR_WRITE_4 (pDev, GMAC_FRAME_FILTER, FFILTER_HASH_MCAST); } /******************************************************************************* * * ftGmacEndMCastAddrAdd - 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. * * ERRNO: N/A */ LOCAL STATUS ftGmacEndMCastAddrAdd ( END_OBJ * pEnd, char * pAddr ) { int retVal; retVal = etherMultiAdd (&pEnd->multiList, pAddr); if (retVal == ENETRESET) { pEnd->nMulti++; ftGmacEndHashTblPopulate ((GMAC_DRV_CTRL *)pEnd); } return (OK); } /******************************************************************************* * * ftGmacEndMCastAddrDel - 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. * * ERRNO: N/A */ LOCAL STATUS ftGmacEndMCastAddrDel ( END_OBJ * pEnd, char * pAddr ) { int retVal; retVal = etherMultiDel (&pEnd->multiList, pAddr); if (retVal == ENETRESET) { pEnd->nMulti--; ftGmacEndHashTblPopulate ((GMAC_DRV_CTRL *)pEnd); } return (OK); } /******************************************************************************* * * ftGmacEndMCastAddrGet - get the multicast address list for the device * * This routine gets the multicast list of whatever the driver * is already listening for. * * RETURNS: OK or ERROR. * * ERRNO: N/A */ LOCAL STATUS ftGmacEndMCastAddrGet ( END_OBJ * pEnd, MULTI_TABLE * pTable ) { return (etherMultiGet (&pEnd->multiList, pTable)); } /******************************************************************************* * * ftGmacEndStatsDump - return polled statistics counts * * This routine is automatically invoked periodically by the polled statistics * watchdog. All stats are available from the MIB registers. * * RETURNS: always OK * * ERRNO: N/A */ LOCAL STATUS ftGmacEndStatsDump ( GMAC_DRV_CTRL * pDrvCtrl ) { END_IFCOUNTERS * pEndStatsCounters; pEndStatsCounters = &pDrvCtrl->gmacEndStatsCounters; pEndStatsCounters->ifInOctets = pDrvCtrl->gmacInOctets; pDrvCtrl->gmacInOctets = 0; pEndStatsCounters->ifInUcastPkts = pDrvCtrl->gmacInUcasts; pDrvCtrl->gmacInUcasts = 0; pEndStatsCounters->ifInMulticastPkts = pDrvCtrl->gmacInMcasts; pDrvCtrl->gmacInMcasts = 0; pEndStatsCounters->ifInBroadcastPkts = pDrvCtrl->gmacInBcasts; pDrvCtrl->gmacInBcasts = 0; pEndStatsCounters->ifInErrors = pDrvCtrl->gmacInErrors; pDrvCtrl->gmacInErrors = 0; pEndStatsCounters->ifInDiscards = pDrvCtrl->gmacInDiscards; pDrvCtrl->gmacInDiscards = 0; pEndStatsCounters->ifOutOctets = pDrvCtrl->gmacOutOctets; pDrvCtrl->gmacOutOctets = 0; pEndStatsCounters->ifOutUcastPkts = pDrvCtrl->gmacOutUcasts; pDrvCtrl->gmacOutUcasts = 0; pEndStatsCounters->ifOutMulticastPkts = pDrvCtrl->gmacOutMcasts; pDrvCtrl->gmacOutMcasts = 0; pEndStatsCounters->ifOutBroadcastPkts = pDrvCtrl->gmacOutBcasts; pDrvCtrl->gmacOutBcasts = 0; pEndStatsCounters->ifOutErrors = pDrvCtrl->gmacOutErrors; pDrvCtrl->gmacOutErrors = 0; return (OK); } /******************************************************************************* * * ftGmacEndIoctl - 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 ftGmacEndIoctl ( END_OBJ * pEnd, int cmd, caddr_t data ) { GMAC_DRV_CTRL * pDrvCtrl; END_MEDIALIST * mediaList; END_CAPABILITIES * hwCaps; END_MEDIA * pMedia; END_RCVJOBQ_INFO * qinfo; UINT32 nQs; VXB_DEVICE_ID pDev; INT32 value; int error = OK; pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; pDev = pDrvCtrl->gmacDev; switch (cmd) { case EIOCSADDR: if (data == NULL) error = EINVAL; else bcopy ((char *)data, (char *)pDrvCtrl->gmacAddr, ETHER_ADDR_LEN); ftGmacEndRxConfig (pDrvCtrl); break; case EIOCGADDR: if (data == NULL) error = EINVAL; else bcopy ((char *)pDrvCtrl->gmacAddr, (char *)data, ETHER_ADDR_LEN); break; case EIOCSFLAGS: value = (INT32) data; if (value < 0) { value = -value; value--; END_FLAGS_CLR (pEnd, value); } else END_FLAGS_SET (pEnd, value); ftGmacEndRxConfig (pDrvCtrl); break; case EIOCGFLAGS: if (data == NULL) error = EINVAL; else *(long *)data = END_FLAGS_GET(pEnd); break; case EIOCMULTIADD: error = ftGmacEndMCastAddrAdd (pEnd, (char *) data); break; case EIOCMULTIDEL: error = ftGmacEndMCastAddrDel (pEnd, (char *) data); break; case EIOCMULTIGET: error = ftGmacEndMCastAddrGet (pEnd, (MULTI_TABLE *) data); break; case EIOCPOLLSTART: pDrvCtrl->gmacPolling = TRUE; /* diable interrupts */ CSR_WRITE_4(pDev, DMA_INTR_ENA, 0); /* * 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->gmacTxFree < GMAC_TX_DESC_CNT) { volatile GMAC_DESC * pDesc; M_BLK_ID pMblk; volatile UINT32 desc_status; pDesc = &pDrvCtrl->gmacTxDescMem[pDrvCtrl->gmacTxCons]; /* Wait for ownership bit to clear */ do { desc_status = pDesc->status; if(!(desc_status & TDES0_OWN)) break; }while(1); pDesc->status = pDesc->ctrl = 0; if(pDrvCtrl->gmacTxCons == (GMAC_TX_DESC_CNT - 1)) /* the last one */ { pDesc->ctrl |= TDES1_END_RING; } pMblk = pDrvCtrl->gmacTxMblk[pDrvCtrl->gmacTxCons]; if (pMblk != NULL) { endPoolTupleFree (pMblk); pDrvCtrl->gmacTxMblk[pDrvCtrl->gmacTxCons] = NULL; } pDrvCtrl->gmacTxFree++; GMAC_INC_DESC (pDrvCtrl->gmacTxCons, GMAC_TX_DESC_CNT); } break; case EIOCPOLLSTOP: /* enable interrupts */ CSR_WRITE_4(pDev, DMA_INTR_ENA, DMA_INT_ENABLE); pDrvCtrl->gmacPolling = FALSE; break; case EIOCGMIB2233: case EIOCGMIB2: error = endM2Ioctl (&pDrvCtrl->gmacEndObj, cmd, data); break; case EIOCGPOLLCONF: if (data == NULL) error = EINVAL; else *((END_IFDRVCONF **)data) = &pDrvCtrl->gmacEndStatsConf; break; case EIOCGPOLLSTATS: if (data == NULL) error = EINVAL; else { error = ftGmacEndStatsDump(pDrvCtrl); if (error == OK) *((END_IFCOUNTERS **)data) = &pDrvCtrl->gmacEndStatsCounters; } break; case EIOCGMEDIALIST: if (data == NULL) { error = EINVAL; break; } if (pDrvCtrl->gmacMediaList->endMediaListLen == 0) { error = ENOTSUP; break; } mediaList = (END_MEDIALIST *)data; if (mediaList->endMediaListLen < pDrvCtrl->gmacMediaList->endMediaListLen) { mediaList->endMediaListLen = pDrvCtrl->gmacMediaList->endMediaListLen; error = ENOSPC; break; } bcopy((char *)pDrvCtrl->gmacMediaList, (char *)mediaList, sizeof(END_MEDIALIST) + (sizeof(UINT32) * pDrvCtrl->gmacMediaList->endMediaListLen)); break; case EIOCGIFMEDIA: if (data == NULL) error = EINVAL; else { pMedia = (END_MEDIA *)data; pMedia->endMediaActive = pDrvCtrl->gmacCurMedia; pMedia->endMediaStatus = pDrvCtrl->gmacCurStatus; } break; case EIOCSIFMEDIA: if (data == NULL) error = EINVAL; else { pMedia = (END_MEDIA *)data; miiBusModeSet (pDrvCtrl->gmacMiiBus, pMedia->endMediaActive); ftGmacLinkUpdate (pDrvCtrl->gmacDev); error = OK; } break; case EIOCGIFCAP: hwCaps = (END_CAPABILITIES *)data; if (hwCaps == NULL) { error = EINVAL; break; } hwCaps->csum_flags_tx = pDrvCtrl->gmacCaps.csum_flags_tx; hwCaps->csum_flags_rx = pDrvCtrl->gmacCaps.csum_flags_rx; hwCaps->cap_available = pDrvCtrl->gmacCaps.cap_available; hwCaps->cap_enabled = pDrvCtrl->gmacCaps.cap_enabled; break; /* * The only special capability we support is VLAN_MTU, and * it can never be turned off. */ case EIOCSIFCAP: error = ENOTSUP; break; case EIOCGIFMTU: if (data == NULL) error = EINVAL; else *(INT32 *)data = pEnd->mib2Tbl.ifMtu; break; case EIOCSIFMTU: value = (INT32)data; if (value <= 0 || value > pDrvCtrl->gmacMaxMtu) { error = EINVAL; break; } pEnd->mib2Tbl.ifMtu = value; if (pEnd->pMib2Tbl != NULL) 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->gmacJobQueue; break; default: error = EINVAL; break; } return (error); } /******************************************************************************* * * ftGmacEndRxConfig - configure the FCC's RX filter * * This is a helper routine used by ftGmacEndIoctl() and ftGmacEndStart() to * configure the controller's RX filter. The unicast address filter is * programmed with the currently configured MAC address, and the RX * configuration is set to allow unicast and broadcast frames to be * received. If the interface is in IFF_PROMISC mode, the RX_PROMISC * bit is set, which allows all packets to be received. * * The ftGmacEndHashTblPopulate() routine is also called to update the * multicast filter. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacEndRxConfig ( GMAC_DRV_CTRL * pDrvCtrl ) { VXB_DEVICE_ID pDev; UINT32 rxCtrl; pDev = pDrvCtrl->gmacDev; rxCtrl = CSR_READ_4 (pDev, GMAC_CONTROL); /* set the MAC address - must write the low first */ CSR_WRITE_4(pDev, GMAC_ADDR0_LOW, (pDrvCtrl->gmacAddr[3] << 24) | (pDrvCtrl->gmacAddr[2] << 16) | (pDrvCtrl->gmacAddr[1] << 8) | (pDrvCtrl->gmacAddr[0] << 0)); CSR_WRITE_4(pDev, GMAC_ADDR0_HIGH, (pDrvCtrl->gmacAddr[5] << 8) | (pDrvCtrl->gmacAddr[4] << 0) | GMAC_HI_REG_AE); /* Enable promisc mode, if specified. */ /* enable promisc mode, if specified */ if (pDrvCtrl->gmacEndObj.flags & IFF_PROMISC) { CSR_WRITE_4 (pDev, GMAC_FRAME_FILTER, FFILTER_PROMIS_MODE); } else { CSR_WRITE_4 (pDev, GMAC_FRAME_FILTER, FFILTER_HASH_MCAST); } /* jumbo frame support */ if (pDrvCtrl->gmacMaxMtu == GMAC_JUMBO_MTU) rxCtrl |= CFG_JUMBO_EN; else rxCtrl &= ~CFG_JUMBO_EN; /* program the hardware register */ CSR_WRITE_4 (pDev, GMAC_CONTROL, rxCtrl); /* Program the multicast filter. */ ftGmacEndHashTblPopulate (pDrvCtrl); return; } LOCAL STATUS ftGmacRxStatusCheck(UINT32 desc_status) { if ((desc_status & RDES0_ERROR_SUMMARY) && (desc_status & RX_FRAME_ERROR_TYPE1)) { #ifdef GMAC_RX_DEBUG printf("\r\n gmac rx error type1: 0x%x ", desc_status); #endif return ERROR; } if (desc_status & RX_FRAME_ERROR_TYPE2) { #ifdef GMAC_RX_DEBUG printf("\r\n gmac rx error type2: 0x%x ", desc_status); #endif return ERROR; } return OK; } LOCAL void ftGmacRxDescInit(GMAC_DESC * desc, int index, UINT32 phyAddr) { desc->ctrl = 0; desc->next = 0; desc->status = 0; desc->ctrl |= ((MAC_MAX_FRAME_SZ - 1) & RDES1_BUFFER1_SIZE_MASK); /*desc->ctrl |= (((MAC_MAX_FRAME_SZ - 1) << RDES1_BUFFER2_SIZE_SHIFT) & RDES1_BUFFER2_SIZE_MASK);*/ desc->status = RDES0_OWN; if(phyAddr != 0) desc->addr = phyAddr; if(index == (GMAC_RX_DESC_CNT - 1)) { desc->ctrl |= RDES1_END_RING; } } /******************************************************************************* * * ftGmacEndStart - 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 and TX DMA rings * are 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 ftGmacEndStart ( END_OBJ * pEnd ) { GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; VXB_INST_PARAM_VALUE val; HEND_RX_QUEUE_PARAM * pRxQueue; GMAC_DESC * pDesc; int i; M_BLK_ID pMblk = NULL; UINT32 physAddr; if (pEnd->flags & IFF_UP) return (OK); pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; pDev = pDrvCtrl->gmacDev; semTake (pDrvCtrl->gmacDevSem, WAIT_FOREVER); /* Initialize job queues */ pDrvCtrl->gmacJobQueue = netJobQueueId; /* Override the job queue ID if the user supplied an alternate one. */ /* * paramDesc { * 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, &val) == OK) { pRxQueue = (HEND_RX_QUEUE_PARAM *) val.pValue; if (pRxQueue->jobQueId != NULL) pDrvCtrl->gmacJobQueue = pRxQueue->jobQueId; } QJOB_SET_PRI(&pDrvCtrl->gmacIntJob, NET_TASK_QJOB_PRI); pDrvCtrl->gmacIntJob.func = ftGmacEndIntHandle; (void)vxAtomicSet ((atomicVal_t*) &pDrvCtrl->gmacRxPending, FALSE); (void)vxAtomicSet ((atomicVal_t*) &pDrvCtrl->gmacTxPending, FALSE); (void)vxAtomicSet ((atomicVal_t*) &pDrvCtrl->gmacIntPending, FALSE); ftGmacMacSet(pDrvCtrl); /* Reset controller to known state */ ftGmacReset (pDev); /* Set up the RX ring. */ for (i = 0; i < GMAC_RX_DESC_CNT; i++) { pMblk = endPoolTupleGet(pDrvCtrl->gmacEndObj.pNetPool); if (pMblk == NULL) { semGive (pDrvCtrl->gmacDevSem); GMAC_DEBUG ("get rx mblk failed\r\n", 0, 1, 2, 3, 4, 5); return (ERROR); } pMblk->m_next = NULL; pDrvCtrl->gmacRxMblk[i] = pMblk; pDesc = &pDrvCtrl->gmacRxDescMem[i]; /* pre-invalidate the buffer */ CACHE_USER_INVALIDATE (pMblk->m_data, pMblk->m_len); physAddr = (UINT32)GMAC_CACHE_DRV_VIRT_TO_PHYS(pMblk->m_data); ftGmacRxDescInit(pDesc, i, physAddr); } /* Set up TX ring */ bzero ((char *)pDrvCtrl->gmacTxDescMem, sizeof(GMAC_DESC) * GMAC_TX_DESC_CNT); pDesc = &pDrvCtrl->gmacTxDescMem[GMAC_TX_DESC_CNT - 1]; pDesc->ctrl |= TDES1_END_RING; /* last descriptor is end of ring */ /* Load the maps for the RX and TX DMA ring. */ /* Initialize state */ pDrvCtrl->gmacRxIdx = 0; pDrvCtrl->gmacTxLast = 0; pDrvCtrl->gmacTxStall = FALSE; pDrvCtrl->gmacTxProd = 0; pDrvCtrl->gmacTxCons = 0; pDrvCtrl->gmacTxFree = GMAC_TX_DESC_CNT; /* mask out MMC interrupts because we don't handle them yet */ CSR_WRITE_4 (pDev, MMC_RX_INTR_MASK, 0xFFFFFFFF); CSR_WRITE_4 (pDev, MMC_TX_INTR_MASK, 0xFFFFFFFF); CSR_WRITE_4 (pDev, MMC_RX_IPC_INTR_MASK, 0xFFFFFFFF); CSR_WRITE_4(pDev, GMAC_CONTROL, (0x00610c8c & ~(MAC_ENABLE_RX | MAC_ENABLE_TX)) ); CSR_WRITE_4(pDev, GMAC_FRAME_FILTER, 0x04); CSR_WRITE_4(pDev, GMAC_HASH_HIGH, 0x01); CSR_WRITE_4(pDev, GMAC_HASH_LOW, 0x22); CSR_WRITE_4(pDev, GMAC_FLOW_CTRL, 0xffff0008); CSR_WRITE_4(pDev, DMA_BUS_MODE, 0x01211000); CSR_WRITE_4(pDev, DMA_CONTROL, 0x02202906 & ~(DMA_CONTROL_ST | DMA_CONTROL_SR)); CSR_WRITE_4(pDev, 0x1028, 0x0000000e); CSR_WRITE_4(pDev, GMAC_INT_STATUS, 0x0); CSR_WRITE_4(pDev, GMAC_INT_MASK, GMAC_INT_DISABLE_RGMII); CSR_WRITE_4(pDev, DMA_MISSED_FRAME_CTR, 0x0); CSR_WRITE_4(pDev, 0x1024, 0xff); /* Set Tx/Rx pointer. */ CSR_WRITE_4(pDev, DMA_RCV_BASE_ADDR, (UINT32)GMAC_CACHE_DRV_VIRT_TO_PHYS(pDrvCtrl->gmacRxDescMem)); CSR_WRITE_4(pDev, DMA_TX_BASE_ADDR, (UINT32)GMAC_CACHE_DRV_VIRT_TO_PHYS(pDrvCtrl->gmacTxDescMem)); /* Zero the stats counters. */ pDrvCtrl->gmacInUcasts = pDrvCtrl->gmacInBcasts = pDrvCtrl->gmacInMcasts = 0; pDrvCtrl->gmacOutUcasts = pDrvCtrl->gmacOutBcasts = pDrvCtrl->gmacOutMcasts = 0; pDrvCtrl->gmacInOctets = pDrvCtrl->gmacOutOctets = 0; ftGmacEndRxConfig (pDrvCtrl); (void) vxbIntEnable (pDev, 0, ftGmacEndInt, pDrvCtrl); /* Enable interrupts */ CSR_WRITE_4(pDev, DMA_INTR_ENA, DMA_INT_ENABLE); /* start DMA RX/TX */ CSR_SETBIT_4(pDev, DMA_CONTROL, DMA_CONTROL_SR | DMA_CONTROL_ST); /* Enable GMAC TX/RX */ CSR_SETBIT_4(pDev, GMAC_CONTROL, MAC_ENABLE_RX | MAC_ENABLE_TX); /* Set initial link state */ pDrvCtrl->gmacCurMedia = IFM_ETHER | IFM_NONE; pDrvCtrl->gmacCurStatus = IFM_AVALID; END_FLAGS_SET (pEnd, (IFF_UP | IFF_RUNNING)); semGive (pDrvCtrl->gmacDevSem); return (OK); } /******************************************************************************* * * ftGmacEndStop - stop the device * * This function undoes the effects of ftGmacEndStart(). 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 ftGmacEndStop ( END_OBJ * pEnd ) { GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; int i; if (!(pEnd->flags & IFF_UP)) return (OK); pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; pDev = pDrvCtrl->gmacDev; END_FLAGS_CLR (pEnd, (IFF_UP | IFF_RUNNING)); /* Disable interrupts */ CSR_WRITE_4(pDev, DMA_INTR_ENA, 0); /* stop DMA RX/TX */ CSR_CLRBIT_4(pDev, DMA_CONTROL, DMA_CONTROL_SR | DMA_CONTROL_ST); /* Disable GMAC TX/RX */ CSR_CLRBIT_4(pDev, GMAC_CONTROL, MAC_ENABLE_RX | MAC_ENABLE_TX); (void) vxbIntDisable (pDev, 0, ftGmacEndInt, pDrvCtrl); /* * 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 < GMAC_TIMEOUT; i++) { if (vxAtomicGet ((atomic_t*) &pDrvCtrl->gmacRxPending) == FALSE && vxAtomicGet ((atomic_t*) &pDrvCtrl->gmacTxPending) == FALSE && vxAtomicGet ((atomic_t*) &pDrvCtrl->gmacIntPending) == FALSE) break; (void)taskDelay (1); } if (i == GMAC_TIMEOUT) GMAC_DEBUG ("%s%d: timed out waiting for job to complete\n", (int)GMAC_NAME, pDev->unitNumber, 0, 0, 0, 0); /* * 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 < GMAC_TX_DESC_CNT; i++) { if (pDrvCtrl->gmacTxMblk[i] != NULL) { netMblkClChainFree (pDrvCtrl->gmacTxMblk[i]); pDrvCtrl->gmacTxMblk[i] = NULL; } } for (i = 0; i < GMAC_RX_DESC_CNT; i++) { if (pDrvCtrl->gmacRxMblk[i] != NULL) { netMblkClChainFree (pDrvCtrl->gmacRxMblk[i]); pDrvCtrl->gmacRxMblk[i] = NULL; } } END_TX_SEM_GIVE (pEnd); return (OK); } /******************************************************************************* * * ftGmacEndInt - handle device interrupts * * This function is invoked whenever the FCC's interrupt line is asserted. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacEndInt ( GMAC_DRV_CTRL * pDrvCtrl ) { VXB_DEVICE_ID pDev; pDev = pDrvCtrl->gmacDev; /* not necessary to check return value here */ (void)vxAtomic32Set ((atomic_t *) &pDrvCtrl->gmacIntPending, TRUE); /* mask interrupts here */ CSR_SETBIT_4 (pDev, GMAC_INT_MASK, GMAC_INT_DISABLE_RGMII); CSR_WRITE_4 (pDev, DMA_INTR_ENA, 0); jobQueuePost (pDrvCtrl->gmacJobQueue, &pDrvCtrl->gmacIntJob); return; } /******************************************************************************* * * ftGmacEndIntHandle - process link update * * This function is scheduled by the ISR to run in the context of tNetTask * whenever an link update interrupt is received. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void ftGmacEndIntHandle ( void * pArg ) { QJOB * pJob; GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; INT32 status; pJob = pArg; pDrvCtrl = member_to_object (pJob, GMAC_DRV_CTRL, gmacIntJob); pDev = pDrvCtrl->gmacDev; /* RGMII/SGMII Interrupt mask */ status = CSR_READ_4 (pDev, GMAC_INT_STATUS); if ((status & GMAC_INT_STATUS_LINKCHANGED) == GMAC_INT_STATUS_LINKCHANGED) { /* status changed, read SGMII register to clear */ (void) CSR_READ_4 (pDev, GMAC_GMII_STATUS); (void) ftGmacLinkUpdate (pDev); } status = CSR_READ_4(pDev, DMA_STATUS); if(status & (DMA_STATUS_RI | DMA_STATUS_IRQS)) /* TX/RX NORMAL interrupts */ { /* Receive complete */ if ((status & DMA_STATUS_RI)&& vxAtomicSet ((atomic_t*)&pDrvCtrl->gmacRxPending, TRUE) == FALSE) { /* update RX watchdog timer */ CSR_WRITE_4 (pDev, DMA_RX_WATCHDOG, 0x80); ftGmacEndRxHandle(pDrvCtrl); } /* Transmit complete */ if ((status & DMA_STATUS_IRQS)&& vxAtomicSet ((atomic_t*)&pDrvCtrl->gmacTxPending, TRUE) == FALSE) { ftGmacEndTxHandle(pDrvCtrl); } } if (status & DMA_INT_ABNORMAL) { /* Receive buffer unavailable */ if ((status & DMA_INT_RX_NO_BUFFER) && vxAtomicSet ((atomic_t*)&pDrvCtrl->gmacRxPending, TRUE) == FALSE) { /* * To resume processing Receive descriptors, the host should * change the ownership of the descriptor and issue a Receive * Poll Demand command. */ CSR_WRITE_4 (pDev, DMA_RX_WATCHDOG, 0x80); ftGmacEndRxHandle(pDrvCtrl); CSR_WRITE_4 (pDev, DMA_RCV_POLL_DEMAND, status); } } vxAtomicSet ((atomic_t*)&pDrvCtrl->gmacIntPending, FALSE); /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */ CSR_WRITE_4 (pDev, DMA_STATUS, status/* & 0x1ffff*/); CSR_CLRBIT_4 (pDev, GMAC_INT_MASK, GMAC_INT_DISABLE_RGMII); CSR_WRITE_4(pDev, DMA_INTR_ENA, DMA_INT_ENABLE); } /******************************************************************************* * * ftGmacEndRxHandle - 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 DMA ring and encapsulates them into mBlk tuples which are handed up * to the MUX. * * There may be several packets waiting in the ring 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 ftGmacEndRxHandle ( GMAC_DRV_CTRL * pDrvCtrl ) { VXB_DEVICE_ID pDev; M_BLK_ID pMblk = NULL, pNewMblk = NULL; GMAC_DESC * pDesc; int rxLen; UINT32 desc_status; int loopCounter = GMAC_MAX_RX; pDev = pDrvCtrl->gmacDev; pDesc = &pDrvCtrl->gmacRxDescMem[pDrvCtrl->gmacRxIdx]; while (loopCounter ) { desc_status = pDesc->status; if (desc_status & RDES0_OWN) { break; } if(ftGmacRxStatusCheck(desc_status) != 0) { goto rx_skip; } pNewMblk = endPoolTupleGet(pDrvCtrl->gmacEndObj.pNetPool); if (pNewMblk == NULL) { GMAC_DEBUG ("%s%d: out of mBlks at %d\n", (int)GMAC_NAME, pDev->unitNumber, pDrvCtrl->gmacRxIdx,0,0,0); pDrvCtrl->gmacLastError.errCode = END_ERR_NO_BUF; muxError (&pDrvCtrl->gmacEndObj, &pDrvCtrl->gmacLastError); pDrvCtrl->gmacInDiscards++; rx_skip: pDesc->status = 0; pDesc->ctrl |= ((MAC_MAX_FRAME_SZ - 1) & RDES1_BUFFER1_SIZE_MASK); pDesc->status = RDES0_OWN; GMAC_INC_DESC(pDrvCtrl->gmacRxIdx, GMAC_RX_DESC_CNT); pDesc = &pDrvCtrl->gmacRxDescMem[pDrvCtrl->gmacRxIdx]; loopCounter--; continue; } rxLen = (desc_status & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT; pMblk = pDrvCtrl->gmacRxMblk[pDrvCtrl->gmacRxIdx]; pDrvCtrl->gmacRxMblk[pDrvCtrl->gmacRxIdx] = pNewMblk; pNewMblk->m_next = NULL; /* pre-invalidate the new buffer */ CACHE_USER_INVALIDATE (pNewMblk->m_data, pNewMblk->m_len); pMblk->m_len = pMblk->m_pkthdr.len = rxLen; pMblk->m_flags = M_PKTHDR | M_EXT; pDesc->status = 0; pDesc->ctrl |= ((MAC_MAX_FRAME_SZ - 1) & RDES1_BUFFER1_SIZE_MASK); pDesc->status = RDES0_OWN; pDesc->addr = (UINT32)GMAC_CACHE_DRV_VIRT_TO_PHYS(pNewMblk->m_data); CACHE_USER_INVALIDATE (pMblk->m_data, pMblk->m_len); /* Bump stats counters */ pDrvCtrl->gmacInOctets += pMblk->m_len; pDrvCtrl->gmacInUcasts++; GMAC_INC_DESC(pDrvCtrl->gmacRxIdx, GMAC_RX_DESC_CNT); loopCounter--; /* Give the packet to the stack. */ END_RCV_RTN_CALL (&pDrvCtrl->gmacEndObj, pMblk); pDesc = &pDrvCtrl->gmacRxDescMem[pDrvCtrl->gmacRxIdx]; } vxAtomicSet((atomic_t*)&pDrvCtrl->gmacRxPending, FALSE); #ifdef GMAC_RX_DEBUG printf("\r\n ft_gmac_rx_handle...done "); #endif } /******************************************************************************* * * ftGmacEndTxHandle - 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. * * In the event that a TX underrun error is detected, the TX FIFO * threshold is increased. This will continue until the maximum TX * FIFO threshold is reached. * * 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 ftGmacEndTxHandle ( GMAC_DRV_CTRL * pDrvCtrl ) { VXB_DEVICE_ID pDev; BOOL restart = FALSE; GMAC_DESC * pDesc; M_BLK_ID pMblk; UINT32 desc_status, desc_ctrl; pDev = pDrvCtrl->gmacDev; END_TX_SEM_TAKE (&pDrvCtrl->gmacEndObj, WAIT_FOREVER); while (pDrvCtrl->gmacTxFree < GMAC_TX_DESC_CNT) { pDesc = &pDrvCtrl->gmacTxDescMem[pDrvCtrl->gmacTxCons]; desc_status = pDesc->status; desc_ctrl = pDesc->ctrl; if(desc_status & TDES0_OWN) { goto tx_check_fail; } if(desc_ctrl & TDES0_ERROR_SUMMARY) { pDrvCtrl->gmacOutErrors++; } pDesc->status = pDesc->ctrl = 0; if(pDrvCtrl->gmacTxCons == (GMAC_TX_DESC_CNT - 1)) /* the last one */ { pDesc->ctrl |= TDES1_END_RING; } pMblk = pDrvCtrl->gmacTxMblk[pDrvCtrl->gmacTxCons]; if (pMblk != NULL) { pDrvCtrl->gmacOutOctets += pMblk->m_pkthdr.len; if ((UINT8)pMblk->m_data[0] == 0xFF) pDrvCtrl->gmacOutBcasts++; else if ((UINT8)pMblk->m_data[0] & 0x1) pDrvCtrl->gmacOutMcasts++; else pDrvCtrl->gmacOutUcasts++; endPoolTupleFree (pMblk); pDrvCtrl->gmacTxMblk[pDrvCtrl->gmacTxCons] = NULL; } pDrvCtrl->gmacTxFree++; GMAC_INC_DESC (pDrvCtrl->gmacTxCons, GMAC_TX_DESC_CNT); /* * We released at least one descriptor: if the transmit * channel is stalled, unstall it. */ if (pDrvCtrl->gmacTxStall == TRUE) { pDrvCtrl->gmacTxStall = FALSE; restart = TRUE; } } tx_check_fail: END_TX_SEM_GIVE (&pDrvCtrl->gmacEndObj); vxAtomicSet((atomic_t*)&pDrvCtrl->gmacTxPending, FALSE); if (restart == TRUE) muxTxRestart (pDrvCtrl); return; } /******************************************************************************* * * ftGmacEndEncap - encapsulate an outbound packet in the TX DMA ring * * This function sets up a descriptor for a packet transmit operation. * With the zynq7k ethrnet controller, the TX DMA ring consists of * descriptors that each describe a single packet fragment. We consume * as many descriptors as there are mBlks in the outgoing packet, unless * the chain is too long. The length is limited by the number of DMA * segments we want to allow in a given DMA map. If there are too many * segments, this routine will fail, and the caller must coalesce the * data into fewer buffers and try again. * * This routine will also fail if there aren't enough free descriptors * available in the ring, in which case the caller must defer the * transmission until more descriptors are completed by the chip. * * RETURNS: ENOSPC if there are too many fragments in the packet, EAGAIN * if the DMA ring is full, otherwise OK. * * ERRNO: N/A */ LOCAL int ftGmacEndEncap ( GMAC_DRV_CTRL * pDrvCtrl, M_BLK_ID pMblk ) { VXB_DEVICE_ID pDev; GMAC_DESC * pDesc = NULL; GMAC_DESC * pFirst = NULL; UINT32 firstIdx, lastIdx = 0; int frags = 0,nFrags; M_BLK_ID pCurr; firstIdx = pDrvCtrl->gmacTxProd; pDev = pDrvCtrl->gmacDev; pFirst = &pDrvCtrl->gmacTxDescMem [pDrvCtrl->gmacTxProd]; if (pDrvCtrl->gmacTxMblk [pDrvCtrl->gmacTxProd] != NULL) return (EAGAIN); /* * Load the DMA map to build the segment list. * This will fail if there are too many segments. */ nFrags = 0; for (pCurr = pMblk; pCurr != NULL; pCurr = pCurr->m_next) { if (pCurr->m_len != 0) ++nFrags; } if (nFrags > pDrvCtrl->gmacTxFree || nFrags >= GMAC_MAXFRAG) { return ENOSPC; } for (pCurr = pMblk; pCurr != NULL; pCurr = pCurr->m_next) { if (pCurr->m_len != 0) { pDesc = &pDrvCtrl->gmacTxDescMem [pDrvCtrl->gmacTxProd]; pDesc->addr = (UINT32)GMAC_CACHE_DRV_VIRT_TO_PHYS(pCurr->m_data); pDesc->status &= ~0x1ffff; pDesc->ctrl &= ~(DESC_TXCTRL_SIZE1MASK); pDesc->ctrl |= TDES1_INTERRUPT; pDesc->ctrl |= (pCurr->m_len & TDES1_BUFFER1_SIZE_MASK); if(frags == 0) pDesc->ctrl |= TDES1_FIRST_SEGMENT; else { pDesc->status |= TDES0_OWN; } /* sync the data buffer */ #define TX_DESC_INT (1 << 30) #define TX_DESC_CSUM_INSERT(x) ((x) << 22) pDesc->status |= TX_DESC_INT; if (pDrvCtrl->gmacCaps.cap_enabled & IFCAP_TXCSUM) { if (pCurr->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP)) pDesc->status |= TX_DESC_CSUM_INSERT(3); else if (pCurr->m_pkthdr.csum_flags & CSUM_IP) pDesc->status |= TX_DESC_CSUM_INSERT(3); } lastIdx = pDrvCtrl->gmacTxProd; CACHE_USER_FLUSH (pCurr->m_data, pCurr->m_len); GMAC_INC_DESC(pDrvCtrl->gmacTxProd, GMAC_TX_DESC_CNT); frags++; pDrvCtrl->gmacTxFree--; } } pDesc->ctrl |= TDES1_LAST_SEGMENT; pFirst->status |= TDES0_OWN; /* Save the mBlk for later. */ pDrvCtrl->gmacTxMblk[lastIdx] = pMblk; /* Transfer descriptors to the chip. */ CSR_SETBIT_4(pDev, DMA_CONTROL, DMA_CONTROL_ST); CSR_WRITE_4(pDev, DMA_XMT_POLL_DEMAND, 0xff); return (OK); } /******************************************************************************* * * ftGmacEndSend - transmit a packet * * This function transmits the packet specified in . * * RETURNS: OK, ERROR, or END_ERR_BLOCK. * * ERRNO: N/A */ LOCAL int ftGmacEndSend ( END_OBJ * pEnd, M_BLK_ID pMblk ) { GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; M_BLK_ID pTmp; int rval; pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; #ifdef GMAC_TX_DEBUG printf("\r\n\r\n ft_gmac_send... begin"); #endif if (pDrvCtrl->gmacPolling == TRUE) { endPoolTupleFree (pMblk); return (ERROR); } pDev = pDrvCtrl->gmacDev; END_TX_SEM_TAKE (pEnd, WAIT_FOREVER); if (!pDrvCtrl->gmacTxFree || !(pDrvCtrl->gmacCurStatus & IFM_ACTIVE)) goto blocked; /* * First, try to do an in-place transmission, using * gather-write DMA. */ rval = ftGmacEndEncap (pDrvCtrl, pMblk); /* * If ftGmacEndEncap returns ENOSPC, it means it ran out of TX descriptors * and couldn't encapsulate the whole packet fragment chain. In that case, * we need to coalesce everything into a single buffer and try again. If * any other error is returned, then something went wrong, and we have to * abort the transmission entirely. */ if (rval == ENOSPC) { if ((pTmp = endPoolTupleGet(pDrvCtrl->gmacEndObj.pNetPool)) == NULL) goto blocked; pTmp->m_len = pTmp->m_pkthdr.len = netMblkToBufCopy(pMblk, mtod(pTmp, char *), NULL); pTmp->m_flags = pMblk->m_flags; /* try transmission again, should succeed this time */ rval = ftGmacEndEncap (pDrvCtrl, pTmp); if (rval == OK) endPoolTupleFree (pMblk); else endPoolTupleFree (pTmp); } if (rval != OK) goto blocked; #ifdef GMAC_TX_DEBUG printf("\r\n\r\n ft_gmac_send... done"); #endif END_TX_SEM_GIVE (pEnd); return (OK); blocked: pDrvCtrl->gmacTxStall = TRUE; END_TX_SEM_GIVE (pEnd); return (END_ERR_BLOCK); } LOCAL STATUS ftGmacEndPollSend ( END_OBJ * pEnd, M_BLK_ID pMblk ) { GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; GMAC_DESC * pDesc; M_BLK_ID pTmp; int len, i; pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; if (pDrvCtrl->gmacPolling == FALSE) return (ERROR); pDev = pDrvCtrl->gmacDev; pTmp = pDrvCtrl->gmacPollBuf; len = netMblkToBufCopy (pMblk, mtod(pTmp, char *), NULL); pTmp->m_len = pTmp->m_pkthdr.len = len; pTmp->m_flags = pMblk->m_flags; pTmp->m_pkthdr.csum_flags = pMblk->m_pkthdr.csum_flags; pTmp->m_pkthdr.csum_data = pMblk->m_pkthdr.csum_data; if (ftGmacEndEncap (pDrvCtrl, pTmp) != OK) return (EAGAIN); pDesc = &pDrvCtrl->gmacTxDescMem [pDrvCtrl->gmacTxCons]; /* poll the status to see if the transmission is done */ i = 0; while(i++ < GMAC_TIMEOUT) { if (!(pDesc->status & (UINT32)TDES0_OWN)) break; } if (i == GMAC_TIMEOUT) return (ERROR); /* reset the tx descriptor */ pDesc->status = pDesc->ctrl = 0; if(pDrvCtrl->gmacTxCons == (GMAC_TX_DESC_CNT - 1)) /* the last one */ { pDesc->ctrl |= TDES1_END_RING; } pDrvCtrl->gmacTxFree++; GMAC_INC_DESC (pDrvCtrl->gmacTxCons, GMAC_TX_DESC_CNT); return (OK); } LOCAL int ftGmacEndPollReceive ( END_OBJ * pEnd, M_BLK_ID pMblk ) { GMAC_DRV_CTRL * pDrvCtrl; VXB_DEVICE_ID pDev; GMAC_DESC * pDesc; int length; M_BLK_ID pPkt; int rval = EAGAIN; pDrvCtrl = (GMAC_DRV_CTRL *)pEnd; if (pDrvCtrl->gmacPolling == FALSE) return (ERROR); if (!(pMblk->m_flags & M_EXT)) return (ERROR); pDev = pDrvCtrl->gmacDev; pDesc = &pDrvCtrl->gmacRxDescMem [pDrvCtrl->gmacRxIdx]; pPkt = pDrvCtrl->gmacRxMblk[pDrvCtrl->gmacRxIdx]; if (pDesc->status & RDES0_OWN) return (EAGAIN); length = (pDesc->status & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT; if (pMblk->m_len < length) return (ERROR); if(ftGmacRxStatusCheck(pDesc->status) != 0) { rval = ERROR; } else { pMblk->m_len = pMblk->m_pkthdr.len = length; pMblk->m_flags = M_PKTHDR | M_EXT; /* sync the DMA buffer */ CACHE_USER_INVALIDATE (pPkt->m_data, pMblk->m_len); bcopy (mtod(pPkt, char *), mtod(pMblk, char *), pMblk->m_len); CACHE_USER_INVALIDATE (pPkt->m_data, pPkt->m_len); rval = OK; } /* reset the RX descriptor */ ftGmacRxDescInit(pDesc, pDrvCtrl->gmacRxIdx, pDesc->addr); GMAC_INC_DESC (pDrvCtrl->gmacRxIdx, GMAC_RX_DESC_CNT); return (rval); } /******************************************************************************* * * ftGmacPhyAddrGet - probe phy address * * This function probe phy address from 0~31 accroding to standard. * * RETURNS: (0~31) phy address; 32 error address. * * ERRNO: N/A * * !NOTE: For some PHYs, such as RTL8211E, PHY address 0 is a broadcast from * the MAC; each PHY device should respond. (RTL8211EG Datasheet.) * * (If so, WE HOPE user set phy address in hwconf.c by "phyAddr" resource. * Because it will take lots of time to check all 32 addresses when boot.) * */ LOCAL int ftGmacPhyAddrGet (VXB_DEVICE_ID pDev) { int i = 0; UINT16 phyId1; UINT16 phyId2; BOOL flag = FALSE; for (i = 0; i < 32; i++) { ftGmacPhyRead (pDev, i, MII_PHY_ID1_REG, &phyId1); /*read ID1 from phy reg2*/ ftGmacPhyRead (pDev, i, MII_PHY_ID2_REG, &phyId2); /*read ID2 from phy reg3*/ if((phyId2 & 0xFFFF) != 0xFFFF) { if((0 == i) && (0x1C == phyId1) && ((phyId2>>10) == 0x32)) { /* skip RTL8211 broadcase address */ continue; } if((0 == i) && (0x00 == phyId1) && (phyId2 == 0x11a)) { /* skip YT8521 broadcase address */ continue; } flag = TRUE; break; /* get phy addr */ } } return i; } STATUS ftGmacPhyAddrShow (int verbose) { VXB_DEVICE_ID pDev; GMAC_DRV_CTRL * pDrvCtrl; int i, j; UINT16 phyId1; UINT16 phyId2; STATUS st; if(0 == verbose) { printf("verbose: *0, 1\r\n\n"); } else if(1 == verbose) { printf("verbose: 0, *1\r\n\n"); } for(i=0; i<2; i++) { pDev = vxbInstByNameFind("gmac", i); pDrvCtrl = pDev->pDrvCtrl; printf("gmac%d: pDrvCtrl->gmacMiiPhyAddr = %d\r\n", i, pDrvCtrl->gmacMiiPhyAddr); } if(1 == verbose) { for(i=0; i<2; i++) { pDev = vxbInstByNameFind("gmac", i); printf("---------------gmac%d probe phy addr-------------- \r\n",i); for(j=0; j<32; j++) { phyId1 = phyId2 = 0; st = ftGmacPhyRead(pDev,j,MII_PHY_ID1_REG, &phyId1); st |= ftGmacPhyRead(pDev,j,MII_PHY_ID2_REG, &phyId2); if(OK != st) { printf("failed read phy id! j=%d \r\n",j); } printf(" phyAddr[%02d] ID: 0x%04X 0x%04X \r\n", j, phyId1, phyId2); } printf(" \r\n"); } } return OK; } STATUS ftGmacRxdecShow () { VXB_DEVICE_ID pDev; GMAC_DRV_CTRL * pDrvCtrl; int i, j; for(i=0; i<1; i++) { pDev = vxbInstByNameFind("gmac", i); pDrvCtrl = pDev->pDrvCtrl; for(j=0; jgmacRxDescMem[j], pDrvCtrl->gmacRxDescMem[j].status, pDrvCtrl->gmacRxDescMem[j].ctrl,pDrvCtrl->gmacRxDescMem[j].addr, pDrvCtrl->gmacRxDescMem[j].next); } printf(" \r\n"); } return OK; }