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

📄 lan9118.c

📁 Lan9118以太网芯片linux驱动程序
💻 C
📖 第 1 页 / 共 4 页
字号:
/*****************************************************************************
   
Copyright (c) 2004-2005, SMSC
 
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 :  st driver for the ethernet chip SMSC LAN9118.

Date            Modification                	Name        	
----            ------------                	-------				
18/02/05	Initial release V1.16		Phong Le
*****************************************************************************/

#ifndef LAN9118_SOURCE
#error LAN9118_SOURCE NOT DEFINED
#endif

#include "LAN9118.h"

static const NGubyte _date_code[]="021805";

/* global variables */
static const char driver_version [] = "SMSC Lan9118 Driver Version 1.16\n";

/* prototypes of local functions */
static void lan9118Isr(void *data);
static int lan9118Open(NGifnet * netp);
static void lan9118Start(NGifnet * netp);
static int lan9118Close(NGifnet* netp);
#ifdef _lint
static void lan9118SetMulti(const NGifnet * const netp);
#else
static void lan9118SetMulti(NGifnet * netp);
#endif
static BOOLEAN Lan_MacNotBusy(const DWORD dwLanBase);

/* 
 * WriteFifo (dwBase, dwOffset, pdwBuf, dwDwordCount)
 *	Writes 'dwDwordCount' 4-byte integers starting at address 'pdwBuf'
 *	into TX fifo at address (dwBase + dwOffset)
 */
void WriteFifo(
	const DWORD dwBase,
	const DWORD dwOffset,
	const DWORD *pdwBuf,
	DWORD dwDwordCount)
{
	volatile DWORD * pdwReg;
	pdwReg = (volatile DWORD *)(dwBase + dwOffset);
	
	while (dwDwordCount >= 16UL)
	{
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;

		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;
		*pdwReg = *pdwBuf++;

		dwDwordCount -= 16UL;
	}

	while (dwDwordCount)
	{
		*pdwReg = *pdwBuf++;
		dwDwordCount--;
	}
}

/*
 * ReadFifo (dwBase, dwOffset, pdwBuf, dwDwordCount)
 * 	Reads 'dwDwordCount' 4-byte integers from RX fifo at address
 * 	(dwBase + dwOffset) and stores in memory buffer at address
 * 	pdwBuf
 */
void ReadFifo(
	const DWORD dwBase,
	const DWORD dwOffset,
	DWORD *pdwBuf,
	DWORD dwDwordCount)
{
	DWORD dummy;
	const volatile DWORD * const pdwReg = 
		(const volatile DWORD * const)(dwBase + dwOffset);
	
	dwDwordCount--;		/* do not store CRC on OS20 buffer */
	while (dwDwordCount >= 16UL)
	{
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;

		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;
		*pdwBuf++ = *pdwReg;

		dwDwordCount -= 16UL;
	}

	while (dwDwordCount)
	{
		*pdwBuf++ = *pdwReg;
		dwDwordCount--;
	}
	dummy = *pdwReg;
	dummy = dummy;
}

/*
 * LanDetectChipId () returns the ID of the Concord Ethernet chip.
 * 	Possible valid values are 0x115, 0x116, 0x117, 0x118
 */
DWORD LanDetectChipId (const DWORD dwLanBase)
{
	DWORD dwIdRev;

  	dwIdRev = GetRegDW (dwLanBase, ID_REV);
	dwIdRev = (dwIdRev >> 16) & 0xffffUL;
	if ((dwIdRev != 0x0118UL) && (dwIdRev != 0x0117UL) &&
		(dwIdRev != 0x0116UL) && (dwIdRev != 0x0115UL))
	{
	  dwIdRev = 0xffffffffUL;
	}
	return dwIdRev;
}

/*
 * LanDelayUS (dwLanBase, dwUS) delays 'dwUS' microseconds in this
 * 	function.
 */
void LanDelayUS(const DWORD dwLanBase, const DWORD dwUS)
{
	DWORD dwStartTick, dwEndTick, dwCurrentTick;

	// get starting timestamp
	dwStartTick = GetRegDW(dwLanBase, FREE_RUN);

	//	Calculate ending tick.  Make sure we always wait at least 1 uS.
	//	Note that this could wrap-around (handled later).
	if (dwUS == 0UL)
	{
		dwEndTick = dwStartTick + 25UL;
	}
	else
	{
		dwEndTick = dwStartTick + (dwUS*25UL);
	}

	//	4 possible cases:
	//		1.  could start on 0's
	//		2.  could end on ff's
	//		3.  could have a wrap-around before end
	//		4.  normal case (none of the above)
	if (dwStartTick == 0UL)						// case 1 above -- started on 0's
	{
		do {
			dwCurrentTick = GetRegDW(dwLanBase, FREE_RUN);
		} while (dwCurrentTick <= dwEndTick);
	}
	else if (dwEndTick == 0xFFFFFFFFUL)			// case 2 above -- ended on ff's
	{
		do {
			dwCurrentTick = GetRegDW(dwLanBase, FREE_RUN);
		} while (dwCurrentTick >= dwStartTick);
	}
	else if (dwEndTick < dwStartTick)			// case 3 above -- wrap-around before end
	{
		do {
			dwCurrentTick = GetRegDW(dwLanBase, FREE_RUN);
		} while ((dwCurrentTick >= dwStartTick) || (dwCurrentTick <= dwEndTick));
	}
	else										// case 4 above -- the "normal" case
	{
		do {
			dwCurrentTick = GetRegDW(dwLanBase, FREE_RUN);
		} while ((dwCurrentTick >= dwStartTick) && (dwCurrentTick <= dwEndTick));
	}
}

/*
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, 
	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.
*/
void Lan_ReadRxFifo(
	const DWORD dwLanBase, 
	DWORD * pdwBuf, 
	DWORD dwDwordCount)
{
	ReadFifo(
		dwLanBase,
        RX_FIFO_PORT,
		pdwBuf,dwDwordCount);
}

/*
FUNCTION: update_tx_counters
	This function pops tx status and update statistics.
*/
void Lan_UpdateTxCounters(NGifnet *netp)
{
	DWORD dwLanBase;
	DWORD dwTxStatus=0UL;

	dwLanBase = (DWORD) NG_ETHIF_DATA(((void *) netp), eif_base);
	while((dwTxStatus=Lan_CompleteTx(dwLanBase))!=0UL)
	{
		if(dwTxStatus&0x80000000UL) {
		} else {
			if(dwTxStatus&0x00008000UL) {
				netp->if_oerrors++;
			} else {
				netp->if_opackets++;
				netp->if_obytes += (dwTxStatus>>16);
			}
		}
	}
}

/*
FUNCTION: Lan_MacLoad
	initialze MAC address.
	return TRUE on success, FALSE on failure
*/
BOOLEAN Lan_MacLoad(NGifnet * netp)
{
	int i;	
	DWORD dwHigh16, dwLow32;
	DWORD dwLanBase;
		
	dwLanBase = (DWORD) NG_ETHIF_DATA(((void *) netp), eif_base);

	for (i = 0; i < 6; i++)
  	{  
		if (NG_ETHIF_DATA(((void *) netp), eif_addr[i]) != 0)
		{
			break;
		}
  	} 
  
	if (i < 6) 
  	{ 
		/* use application-defined MAC address */
		dwHigh16 = ((DWORD)NG_ETHIF_DATA(((void *) netp), eif_addr[4])) |
		  (((DWORD)NG_ETHIF_DATA(((void *) netp), eif_addr[5])) << 8);
		dwLow32 = ((DWORD)NG_ETHIF_DATA(((void *) netp), eif_addr [0])) |
		  (((DWORD)NG_ETHIF_DATA(((void *) netp), eif_addr[1])) << 8) |
		  (((DWORD)NG_ETHIF_DATA(((void *) netp), eif_addr[2])) << 16) |
		  (((DWORD)NG_ETHIF_DATA(((void *) netp), eif_addr[3])) << 24);
  		
  		/* set MAC address into lan9118 */
		Lan_SetMacAddress (dwLanBase, dwHigh16, dwLow32);
  	} 
  	else
 	{ 
  		/* get MAC address from lan9118 */
		Lan_GetMacAddress (dwLanBase, &dwHigh16, &dwLow32);
	  	
		if (((dwHigh16 & 0xffffUL) == 0UL) && (dwLow32 == 0UL))
		{
			return FALSE;
		}	
		
		/* update MAC address into NexGen structure */
		NG_ETHIF_DATA(((void *) netp), eif_addr[4]) = (NGubyte) (dwHigh16 & 0xffUL); 
		NG_ETHIF_DATA(((void *) netp), eif_addr[5]) = (NGubyte) ((dwHigh16 >> 8) & 0xffUL); 
		NG_ETHIF_DATA(((void *) netp), eif_addr[0]) = (NGubyte) (dwLow32 & 0xffUL); 
		NG_ETHIF_DATA(((void *) netp), eif_addr[1]) = (NGubyte) ((dwLow32 >> 8) & 0xffUL); 
		NG_ETHIF_DATA(((void *) netp), eif_addr[2]) = (NGubyte) ((dwLow32 >> 16) & 0xffUL); 
		NG_ETHIF_DATA(((void *) netp), eif_addr[3]) = (NGubyte) ((dwLow32 >> 24) & 0xffUL); 
        }
	return TRUE;
}

/*
 ****************************************************************************
 *	Indirect (MAC) Registers and Reader/Writers
 ****************************************************************************
 */
static BOOLEAN Lan_MacNotBusy(const DWORD dwLanBase)
{
	NGuint i=0U;
#ifdef SMC_DEBUG
	char buf [256];
#endif

	/* wait for MAC not busy, w/ timeout */
	for(i = 0U; i < 40U; i++)
	{
		if((GetRegDW(dwLanBase, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)==(0UL)) 
		{
			return TRUE;
		}
	}
#ifdef SMC_DEBUG
	sprintf (buf, "timeout waiting for MAC not BUSY. MAC_CSR_CMD = 0x%08lX\n", (DWORD)(*(volatile DWORD *)(dwLanBase + MAC_CSR_CMD)));
	OS_TEXT_OUT (buf);
#endif
	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.
*/
DWORD Lan_GetMacRegDW(
	const DWORD dwLanBase, 
	const DWORD dwOffset)
{
	DWORD dwTemp;
	/* wait until not busy, w/ timeout */
	if (GetRegDW(dwLanBase, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
	{
#ifdef SMC_DEBUG
		OS_TEXT_OUT ("LanGetMacRegDW() failed MAC already busy at entry\n");
#endif
		return 0xFFFFFFFFUL;
	}

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

	/* force flush of previous write */
	dwTemp = GetRegDW(dwLanBase, BYTE_TEST);
	dwTemp = dwTemp;

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

/*
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.
*/
void Lan_SetMacRegDW(
	const DWORD dwLanBase, 
	const DWORD dwOffset, 
	const DWORD dwVal)
{
	DWORD dwTemp;

	if (GetRegDW(dwLanBase, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY_)
	{
#ifdef SMC_DEBUG
		OS_TEXT_OUT ("LanSetMacRegDW() failed MAC already busy at entry\n");
#endif
		return;
	}

	/* 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_));

	/* force flush of previous write */
	dwTemp = GetRegDW(dwLanBase, BYTE_TEST);
	dwTemp = dwTemp;

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

/* Phy_GetRegW () reads a 16-bit phy register values */
NGushort Phy_GetRegW(
	const NGifnet * const netp,
	const DWORD dwRegIndex)
{
	const LAN9118_DATA *pLan9118Data;
	DWORD dwLanBase;
	int ictrl;	/* save previous interrupt state */
	DWORD dwAddr=0UL;
	NGuint i=0U;
	NGushort result= (NGushort) 0xFFFF;


	dwLanBase = (DWORD) NG_ETHIF_DATA(((void *) netp), eif_base);
	pLan9118Data = (PLAN9118_DATA) netp->if_devptr1;

	ictrl = ngOSIntrCtl(NG_INTRCTL_DISABLE);

	/* confirm MII not busy
 */
	if ((Lan_GetMacRegDW(dwLanBase, MII_ACC) & MII_ACC_MII_BUSY_) != 0UL)

	{

#ifdef SMC_DEBUG
		OS_TEXT_OUT("MII is busy in Phy_GetRegW???\n");

#endif
		result = (NGushort) 0;

		goto DONE;

	}



	/* set the address, index & direction (read from PHY)
 */
	dwAddr = ((pLan9118Data->dwPhyAddress&0x1FUL)<<11) | ((dwRegIndex & 0x1FUL)<<6);

	Lan_SetMacRegDW(dwLanBase,MII_ACC,dwAddr);

⌨️ 快捷键说明

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