📄 lan9118.c
字号:
/*****************************************************************************
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 + -