You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

2986 lines
81 KiB

/* 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 <vxWorks.h>
#include <intLib.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>
#define END_MACROS
#include <endLib.h>
#include <cacheLib.h>
#include <vxAtomicLib.h>
#include <hwif/vxbus/vxBus.h>
#include <hwif/vxbus/hwConf.h>
#include <hwif/vxbus/vxbPlbLib.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 "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 <miiIfName> resource (and the
* <miiIfUnit> 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 <loadStr> 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 <pMblk>.
*
* 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; j<GMAC_RX_DESC_CNT; j++)
{
printf(" [%02d] des 0x%x status 0x%08X ctrl 0x%08X addr 0x%08X next 0x%08X\r\n"
, j, &pDrvCtrl->gmacRxDescMem[j], pDrvCtrl->gmacRxDescMem[j].status, pDrvCtrl->gmacRxDescMem[j].ctrl,pDrvCtrl->gmacRxDescMem[j].addr,
pDrvCtrl->gmacRxDescMem[j].next);
}
printf(" \r\n");
}
return OK;
}