⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lan9118.c

📁 SMSC9118网卡驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/*****************************************************************************
   
	Copyright (c) 2004-2006 SMSC. All rights reserved.

	Use of this source code is subject to the terms of the SMSC Software
	License Agreement (SLA) under which you licensed this software product.	 
	If you did not accept the terms of the SLA, you are not authorized to use
	this source code. 

	This code and information is provided as is without warranty of any kind,
	either expressed or implied, including but not limited to the implied
	warranties of merchantability and/or fitness for a particular purpose.
	 
	File name   : lan9118.c 
	Description : lan9118 driver 

	History	    :
		03-16-05 WH			First Release
		08-12-05 MDG		ver 1.01 
			- add LED1 inversion, add PHY work around
		11-07-05 WH			ver 1.02
			- Fixed middle buffer handling bug
			  (Driver didn't handle middle buffers correctly if it is less than 
               4bytes size)
			- workaround for Multicast bug
			- Workaround for MAC RXEN bug
		11-17-05 WH			ver 1.03
			- 1.02 didn't have 1.01 patches
			- 1.03 is 1.02 + 1.01
		12-06-05 WH			ver 1.04
			- Fixed RX doesn't work on Silicon A1 (REV_ID = 0x011x0002)
			- Support SMSC9118x/117x/116x/115x family
		02-27-05 WH			ver 1.05
			- Fixing External Phy bug that doesn't work with 117x/115x
		03-23-05 WH			ver 1.06
			- Put the variable to avoid PHY_WORKAROUND for External PHY
			- Change product name to 9118x->9218, 9115x->9215
		07-26-06 WH, MDG, NL		ver 1.07
			- Add RXE and TXE interrupt handlers
			- Workaround Code for direct GPIO connection from 9118 family 
			  Interrupt (Level Interrupt -> Edge Interrupt)
			- Change GPT interrupt interval to 200mSec from 50mSec
			- clean up un-used SH3 code
		08-25-06  WH, MDG, NL       ver 1.08
		    - Fixed RXE and TXE interrupt handlers bug
			- support for direct and nondirect Interrupt
*****************************************************************************/
#ifndef OS_LINUX
#pragma warning(disable: 4127 4201 4214 4115 4100 4514)
#endif

#include "lan9118.h"

/* csAccessMacReg and csAccessPhyReg are for CRITICAL_SECTION in CE.NET
 * Those are for exclusive access
 * It is multi-thread safe by APIs. (Actually for multi-thread APIs)
 */
/*lint -save*/
/*lint -e956 */
CRITICAL_SECTION	csAccessMacReg, csAccessPhyReg;
/*lint -restore*/

inline static BOOL Lan_MacNotBusy(const DWORD dwLanBase);

#if 0
extern void DumpSIMRegs(LAN9118_DATA * const pLan9118Data);
extern void DumpPHYRegs(LAN9118_DATA * const pLan9118Data);
#endif
#define OLD_REGISTERS(privData) (((privData->dwIdRev&0x0000FFFFUL)==0UL)&& \
								 ((privData->dwFpgaRev)>=0x01UL)&& \
								 ((privData->dwFpgaRev)<=0x25UL))

/*
FUNCTION: Lan_WriteTxFifo
	This function is used to write a buffer to the
	Tx Fifo in PIO mode.
	This function is only intended to be called 
	  from with in other Lan_xxx functions.
*/
void Lan_WriteTxFifo(const DWORD dwLanBase, const DWORD * const pdwBuf, const DWORD dwDwordCount)
{
	WriteFifo(dwLanBase, TX_DATA_FIFO_PORT, pdwBuf, dwDwordCount);
}

/*
FUNCTION: Lan_ReadRxFifo
	This function is used to read a buffer to the 
	Rx Fifo in PIO mode.
	This function is only intended to be called
	  from with in other Lan_xxx functions.
*/
inline void Lan_ReadRxFifo(const DWORD dwLanBase, DWORD * const pdwBuf, const DWORD dwDwordCount)
{
	ReadFifo(dwLanBase, RX_DATA_FIFO_PORT, pdwBuf, dwDwordCount);
}

/*
 ****************************************************************************
 *	Indirect (MAC) Registers and Reader/Writers
 ****************************************************************************
 *
 *	Only DWORD accesses are valid on 12X
 */
inline static BOOL Lan_MacNotBusy(const DWORD dwLanBase)
{
	int i=0;
	// wait for MAC not busy, w/ timeout
	for(i=0;i<40;i++)
	{
		if((GetRegDW(dwLanBase, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)==(0UL)) {
			return TRUE;
		}
	}
	SMSC_WARNING1("timeout waiting for MAC not BUSY. MAC_CSR_CMD = 0x%08lX\n",
		(DWORD)(*(volatile DWORD *)(dwLanBase + MAC_CSR_CMD)));
	return FALSE;
}


/*
FUNCTION: Lan_GetMacRegDW
	This function is used to read a Mac Register.
	This function is only intended to be called
	  from with in other Lan_xxx functions.
*/
inline DWORD Lan_GetMacRegDW(const DWORD dwLanBase, const DWORD dwOffset)
{
	DWORD	dwRet;

	// wait until not busy, w/ timeout
	if (GetRegDW(dwLanBase, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
	{
		SMSC_WARNING0("LanGetMacRegDW() failed MAC already busy at entry\n");
		return 0xFFFFFFFFUL;
	}

	EnterCriticalSection(&csAccessMacReg);
	// send the MAC Cmd w/ offset
	SetRegDW(dwLanBase, MAC_CSR_CMD, 
		((dwOffset & 0x000000FFUL) | MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_));

	// wait for the read to happen, w/ timeout
	if (!Lan_MacNotBusy(dwLanBase))
	{
		SMSC_WARNING0("LanGetMacRegDW() failed waiting for MAC not busy after read\n");
		dwRet = 0xFFFFFFFFUL;
	}
	else
	{
		// finally, return the read data
		dwRet = GetRegDW(dwLanBase, MAC_CSR_DATA);
	}

	LeaveCriticalSection(&csAccessMacReg);
	return dwRet;
}

/*
FUNCTION: Lan_SetMacRegDW
	This function is used to write a Mac register.
	This function is only intended to be called
	  from with in other Lan_xxx functions.
*/
inline void Lan_SetMacRegDW(const DWORD dwLanBase, const DWORD dwOffset, const DWORD dwVal)
{
	if (GetRegDW(dwLanBase, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
	{
		SMSC_WARNING0("LanSetMacRegDW() failed MAC already busy at entry\n");
		return;
	}

	EnterCriticalSection(&csAccessMacReg);

	// send the data to write
	SetRegDW(dwLanBase, MAC_CSR_DATA, dwVal);

	// do the actual write
	SetRegDW(dwLanBase, MAC_CSR_CMD, 
		((dwOffset & 0x000000FFUL) | MAC_CSR_CMD_CSR_BUSY_));

	// wait for the write to complete, w/ timeout
	if (!Lan_MacNotBusy(dwLanBase))
	{
		SMSC_WARNING0("LanSetMacRegDW() failed waiting for MAC not busy after write\n");
	}

	LeaveCriticalSection(&csAccessMacReg);
}

/*
FUNCTION: Lan_GetMiiRegW, Lan_GetPhyRegW
	This function is used to read a Mii/Phy register.
	This function is only intended to be called 
	  from with in other Lan_xxx functions.
*/
inline WORD Lan_GetMiiRegW(
	const DWORD dwLanBase,
	const DWORD dwPhyAddress,
	const DWORD dwMiiIndex)
{
	DWORD dwAddr;
	WORD  wRet = (WORD)0xFFFF;
	int i=0;

	// confirm MII not busy
	if ((Lan_GetMacRegDW(dwLanBase, MII_ACC) & MII_ACC_MII_BUSY_) != 0UL)
	{
		SMSC_WARNING0("MII is busy in MiiGetReg???\r\n");
		return (WORD)0;
	}

	EnterCriticalSection(&csAccessPhyReg);

	// set the address, index & direction (read from PHY)
	dwAddr = ((dwPhyAddress & 0x1FUL)<<11) | ((dwMiiIndex & 0x1FUL)<<6);
	Lan_SetMacRegDW(dwLanBase, MII_ACC, dwAddr);

	// wait for read to complete w/ timeout
	for(i=0;i<100;i++) {
		// see if MII is finished yet
		if ((Lan_GetMacRegDW(dwLanBase, MII_ACC) & MII_ACC_MII_BUSY_) == 0UL)
		{
			// get the read data from the MAC & return i
			wRet = ((WORD)Lan_GetMacRegDW(dwLanBase, MII_DATA));
			break;
		}
	}
	if (i == 100) {
		SMSC_WARNING0("timeout waiting for MII write to finish\n");
		wRet = ((WORD)0xFFFFU);
	}

	LeaveCriticalSection(&csAccessPhyReg);
	return wRet;
	
}

/*
FUNCTION: Lan_SetMiiRegW, Lan_SetPhyRegW
	This function is used to write a Mii/Phy register.
	This function is only intended to be called
	  from with in other Lan_xxx functions.
*/
inline void Lan_SetMiiRegW(
	const DWORD dwLanBase,
	const DWORD dwPhyAddress,
	const DWORD dwMiiIndex,
	const WORD wVal)
{
	DWORD dwAddr;
	int i=0;

	// confirm MII not busy
	if ((Lan_GetMacRegDW(dwLanBase, MII_ACC) & MII_ACC_MII_BUSY_) != 0UL)
	{
		SMSC_WARNING0("MII is busy in MiiGetReg???\n");
		return;
	}

	EnterCriticalSection(&csAccessPhyReg);

	// put the data to write in the MAC
	Lan_SetMacRegDW(dwLanBase, MII_DATA, (DWORD)wVal);

	// set the address, index & direction (write to PHY)
	dwAddr = ((dwPhyAddress & 0x1FUL)<<11) | ((dwMiiIndex & 0x1FUL)<<6) | MII_ACC_MII_WRITE_;
	Lan_SetMacRegDW(dwLanBase, MII_ACC, dwAddr);

	// wait for write to complete w/ timeout
	for(i=0;i<100;i++) {
		// see if MII is finished yet
		if ((Lan_GetMacRegDW(dwLanBase, MII_ACC) & MII_ACC_MII_BUSY_) == 0UL)
		{
			LeaveCriticalSection(&csAccessPhyReg);
			return;
		}
	}

	LeaveCriticalSection(&csAccessPhyReg);
	SMSC_WARNING0("timeout waiting for MII write to finish\r\n");
	return;
}

/*
FUNCTION: Lan_Initialize
  This function should be the first Lan_xxx function called
  It begins to initialize the LAN9118_DATA structure.
  It reads some ID values from the chip.
  It resets the chip.
  It initialize flow control registers.

RETURN VALUE:
	returns TRUE on Success,
	returns FALSE on Failure,
*/
BOOL Lan_Initialize(PLAN9118_DATA const pLan9118Data, const DWORD dwLanBase)
{
	BOOL  	result = FALSE;
	DWORD 	dwTimeout, dwTemp;

	SMSC_TRACE2(DBG_INIT,"+Lan_Initialize(dwLanBase=0x%08lX, pLan9118Data=0x%08lX)\r\n", dwLanBase,(DWORD)pLan9118Data);

	if (pLan9118Data==NULL) {
		SMSC_WARNING0("Lan_Initialize(pLan9118Data==NULL)\r\n");
		goto DONE;
	}

	InitializeCriticalSection(&csAccessMacReg);
	InitializeCriticalSection(&csAccessPhyReg);

	SMSC_ZERO_MEMORY(pLan9118Data,sizeof(LAN9118_DATA));

	if (dwLanBase==0x0UL) {
		SMSC_WARNING0("Lan_Initialize(dwLanBase==0)\r\n");
		goto DONE;
	}

	SetRegDW(dwLanBase, HW_CFG, HW_CFG_SRST_);
	dwTimeout=100000UL;
	do {
		SMSC_MICRO_DELAY(10U);
		dwTemp = GetRegDW(dwLanBase,HW_CFG);
		dwTimeout--;
	} while((dwTimeout > 0UL) && (dwTemp & HW_CFG_SRST_));

	if (dwTemp & HW_CFG_SRST_) {
		SMSC_WARNING0("  Failed to complete reset.\r\n");
		goto DONE;
	}

	pLan9118Data->dwLanBase = dwLanBase;
	pLan9118Data->dwIdRev = GetRegDW(dwLanBase, ID_REV);
	pLan9118Data->dwFpgaRev = GetRegDW(dwLanBase, FPGA_REV);
	pLan9118Data->LanInitialized = (BOOLEAN)TRUE;

	result = TRUE;

DONE:
	SMSC_TRACE1(DBG_INIT,"-Lan_Initialize, result=%s\r\n",result?"TRUE":"FALSE");
	return result;
}


#ifdef USE_PHY_WORK_AROUND
BOOLEAN Phy_Reset(const LAN9118_DATA * const pLan9118Data)
{
	BOOLEAN result=(BOOLEAN)FALSE;
	WORD wTemp=(WORD)0;
	DWORD dwLoopCount=100000UL;

	SMSC_TRACE1(DBG_PHY,"PHY_BCR:0x%04x ... ", LanReadPhy(PHY_BCR));

	SMSC_TRACE0(DBG_PHY,"Performing PHY BCR ");
	LanWritePhy(PHY_BCR,PHY_BCR_RESET_);
	do {
		SMSC_MICRO_DELAY(10U);
		wTemp=LanReadPhy(PHY_BCR);
		dwLoopCount--;
	} while((dwLoopCount>0UL)&&(wTemp&(WORD)PHY_BCR_RESET_));
	if(wTemp&PHY_BCR_RESET_) {
		SMSC_TRACE0(DBG_PHY, "Phy Reset failed to complete.\r\n");
		goto DONE;
	}
	//extra delay required because the phy may not be completed with its reset
	//  when PHY_BCR_RESET_ is cleared.
	//  They say 256 uS is enough delay but I'm using 500 here to be safe
	SMSC_MICRO_DELAY(500U);
	result=(BOOLEAN)TRUE;
DONE:
	SMSC_TRACE1(DBG_PHY,"Performing PHY BCR Reset result=%s\n\r",
		result==TRUE? "TRUE":"FALSE");
	return result;
}

DWORD Phy_LBT_GetTxStatus(const LAN9118_DATA * const pLan9118Data)
{
	DWORD result=GetRegDW(pLan9118Data->dwLanBase, TX_FIFO_INF);
	if(OLD_REGISTERS(pLan9118Data)) {
		result&=TX_FIFO_INF_TSFREE_;
		if(result!=0x00800000UL) {
			result=GetRegDW(pLan9118Data->dwLanBase, TX_STATUS_FIFO_PORT);
		} else {
			result=0UL;
		}
	} else {
		result&=TX_FIFO_INF_TSUSED_;
		if(result!=0x00000000UL) {
			result=GetRegDW(pLan9118Data->dwLanBase, TX_STATUS_FIFO_PORT);
		} else {
			result=0UL;
		}
	}
	return result;
}

DWORD Phy_LBT_GetRxStatus(const LAN9118_DATA * const pLan9118Data)
{
	DWORD result=GetRegDW(pLan9118Data->dwLanBase, RX_FIFO_INF);
	if(result&0x00FF0000UL) {
		//Rx status is available, read it
		result=GetRegDW(pLan9118Data->dwLanBase, RX_STATUS_FIFO_PORT);
	} else {
		result=0UL;
	}
	return result;
}


BOOLEAN Phy_CheckLoopBackPacket(LAN9118_DATA * const pLan9118Data)
{
	BOOLEAN result=(BOOLEAN)FALSE;
	DWORD tryCount=0UL;
	DWORD dwLoopCount=0UL;
	for(tryCount=0UL;tryCount<10UL;tryCount++)
	{
		DWORD dwTxCmdA=0UL;
		DWORD dwTxCmdB=0UL;
		DWORD dwStatus=0UL;
		DWORD dwPacketLength=0UL;
		
		//zero-out Rx Packet memory
		memset(pLan9118Data->LoopBackRxPacket,0,(UINT)MIN_PACKET_SIZE);
		
		//write Tx Packet to 118
		dwTxCmdA=
			((((DWORD)(pLan9118Data->LoopBackTxPacket))&0x03UL)<<16) | //DWORD alignment adjustment
			TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
			((MIN_PACKET_SIZE));
		dwTxCmdB=
			(((DWORD)(MIN_PACKET_SIZE))<<16) |
			((DWORD)(MIN_PACKET_SIZE));
		SetRegDW(pLan9118Data->dwLanBase,TX_DATA_FIFO_PORT,dwTxCmdA);
		SetRegDW(pLan9118Data->dwLanBase,TX_DATA_FIFO_PORT,dwTxCmdB);
		Lan_WriteTxFifo(
			pLan9118Data->dwLanBase,
			(DWORD *)(((DWORD)(pLan9118Data->LoopBackTxPacket))&0xFFFFFFFCUL),
			(((DWORD)(MIN_PACKET_SIZE))+3UL+
			(((DWORD)(pLan9118Data->LoopBackTxPacket))&0x03UL))>>2);

		//wait till transmit is done
		dwLoopCount=60UL;
		while(((dwStatus=Phy_LBT_GetTxStatus(pLan9118Data))==0UL)&&(dwLoopCount>0UL)) {
			SMSC_MICRO_DELAY(5U);
			dwLoopCount--;
		}
		if(dwStatus==0UL) {
			SMSC_WARNING0("Failed to Transmit during Loop Back Test\r\n");
			continue;
		}
		if(dwStatus&0x00008000UL) {
			SMSC_WARNING0("Transmit encountered errors during Loop Back Test\r\n");
			continue;
		}

		//wait till receive is done
		dwLoopCount=60UL;
		while(((dwStatus=Phy_LBT_GetRxStatus(pLan9118Data))==0UL)&&(dwLoopCount>0UL))
		{
	         SMSC_MICRO_DELAY(5U);
	         dwLoopCount--;
		}
		if(dwStatus==0UL) {
			SMSC_WARNING0("Failed to Receive during Loop Back Test\r\n");
			continue;
		}
		if(dwStatus&RX_STS_ES)
		{
			SMSC_WARNING0("Receive encountered errors during Loop Back Test\r\n");
			continue;
		}

		dwPacketLength=((dwStatus&0x3FFF0000UL)>>16);

		Lan_ReadRxFifo(
			pLan9118Data->dwLanBase,
			((DWORD *)(pLan9118Data->LoopBackRxPacket)),
			(dwPacketLength+3UL+(((DWORD)(pLan9118Data->LoopBackRxPacket))&0x03UL))>>2);
		
		if(dwPacketLength!=(MIN_PACKET_SIZE+4UL)) {
			SMSC_TRACE1(DBG_INIT, "Unexpected packet size during loop back test, size=%ld, will retry",dwPacketLength);
		} else {
			DWORD byteIndex=0UL;
			BOOLEAN foundMissMatch=(BOOLEAN)FALSE;
			for(byteIndex=0UL;byteIndex<MIN_PACKET_SIZE;byteIndex++) {
				if(pLan9118Data->LoopBackTxPacket[byteIndex]!=pLan9118Data->LoopBackRxPacket[byteIndex])
				{
					foundMissMatch=(BOOLEAN)TRUE;
					break;
				}         
			}
			if(foundMissMatch != TRUE) {
				SMSC_TRACE0(DBG_PHY, "Successfully verified Loop Back Packet\n\r");
				result=(BOOLEAN)TRUE;
				goto DONE;
			} else {
				SMSC_WARNING0("Data miss match during loop back test, will retry.\r\n");
			}
		}
	}
DONE:
	return result;
}

BOOLEAN Phy_LoopBackTest(LAN9118_DATA * const pLan9118Data)
{
	BOOLEAN result=(BOOLEAN)FALSE;
	DWORD byteIndex=0UL;
	DWORD tryCount=0UL;
//	DWORD failed=0;
	//Initialize Tx Packet
	for(byteIndex=0UL;byteIndex<6UL;byteIndex++) {
		//use broadcast destination address
		pLan9118Data->LoopBackTxPacket[byteIndex]=(BYTE)0xFF;
	}
	for(byteIndex=6UL;byteIndex<12UL;byteIndex++) {
		//use incrementing source address
		pLan9118Data->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex;
	}
	//Set length type field
	pLan9118Data->LoopBackTxPacket[12]=(BYTE)0x00;
	pLan9118Data->LoopBackTxPacket[13]=(BYTE)0x00;
	for(byteIndex=14UL;byteIndex<MIN_PACKET_SIZE;byteIndex++)
	{
		pLan9118Data->LoopBackTxPacket[byteIndex]=(BYTE)byteIndex;
	}
//TRY_AGAIN:
	{
		DWORD dwRegVal=GetRegDW(pLan9118Data->dwLanBase, HW_CFG);
		dwRegVal&=HW_CFG_TX_FIF_SZ_;
		dwRegVal|=HW_CFG_SF_;
		SetRegDW(pLan9118Data->dwLanBase,HW_CFG,dwRegVal);
	}
	SetRegDW(pLan9118Data->dwLanBase,TX_CFG,TX_CFG_TX_ON_);

	SetRegDW(pLan9118Data->dwLanBase,RX_CFG,(((DWORD)(pLan9118Data->LoopBackRxPacket))&0x03UL)<<8);
	
	{
#if 1
	//Set Phy to 10/FD, no ANEG,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -