📄 lan91c111.c
字号:
_outp(INT_REG, IM_ALLOC_INT);
break;
}
} while ( --time_out );
// too bad, no immediate allocation...
if ( !time_out )
{
/* Check the status bit one more time just in case */
/* it snuk in between the time we last checked it */
/* and when we set the interrupt bit */
status = _inp(INT_REG );
if ( !(status & IM_ALLOC_INT) )
{
/* The allocation used to fail when ever the RXOVRN was set and
* the packets were not transmitted. This takes cares of the
* above, I have not observed any packet loss, but I am not
* that sure also, MC_RELEASE release the recieve packet,
* At the max we may loose one packet.
*/
// Relase and reset the FIFO, Now the SMSC memory is free
while ( _inpw(MMU_CMD_REG ) & 1 ) // 1 is MC_BUSY
{
//when the last release is completed
ssync();
}
_outpw(MMU_CMD_REG, MC_RELEASE);
_outpw(MMU_CMD_REG, MC_RSTTXFIFO);
goto TX_RETRY;
}
}
// or YES - I got my memory now...
dev->LAN_state.alloc_success = 1;
return (LAN91C111_hardware_send_packet(dev));
}
/******************************************************************************
* LAN Interrupt Handler
*****************************************************************************/
int LAN91C111_InterruptHandler(ADI_ETHER_LAN91C111_DATA *dev)
{
/*
* This is the main routine of the driver, to handle the
* net_device when it needs some attention.
* So:
* first, save state of the chipset
* branch off into routines to handle each case,
* and acknowledge each to the interrupt register
* and finally restore state.
*/
volatile unsigned char stat;
volatile unsigned short card_status;
volatile unsigned char mask;
volatile unsigned short saved_bank;
volatile unsigned short saved_pointer;
dev->int_state.saved_mask =0;
// save current bank
saved_bank = _inpw(BSR_REG);
// switch to bank 2 and save pointer
_outpw(BSR_REG, 2);
saved_pointer = _inpw(PTR_REG);
// read IRQ status register
mask = _inp(IM_REG);
// disable all LAN IRQs
_outp(IM_REG, 0x0);
// read the status flag and mask it
stat = _inp(INT_REG)& mask;
if(!stat)
goto no_interrupt;
if (stat & IM_RCV_INT)
{
// disable
if(dev->m_dma_protect == 0)
{
LAN91C111_disable_int(SMC_INTERRUPT_MASK);
dma_protect(dev,DMA_DIR_RX);
// store debug
dev->LAN_state.last_IRQ_serviced = IM_RCV_INT;
// Got a packet(s), receive them
dev->int_state.saved_bank = saved_bank;
dev->int_state.saved_pointer = saved_pointer;
dev->int_state.saved_mask = mask;
LAN91C111_rcv(dev);
}
else
{
while ( _inpw(MMU_CMD_REG ) & MC_BUSY );
// good or bad, delete this packet */
_outpw(MMU_CMD_REG, MC_RELEASE);
}
}
if (stat & IM_TX_INT )
{
// store debug
dev->LAN_state.last_IRQ_serviced = IM_TX_INT;
LAN91C111_tx(dev);
// Acknowledge the interrupt
_outp(INT_REG, IM_TX_INT);
// release the dma tx error
dma_relinquish();
// enable interrupts
LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
}
if (stat & IM_TX_EMPTY_INT )
{
// store debug
dev->LAN_state.last_IRQ_serviced = IM_TX_EMPTY_INT;
_outpw(BSR_REG, 0);
card_status = _inpw(COUNTER_REG );
#if LAN91C111_LOG_NET_ERRORS
// multiple collisions
dev->Stats->cEMAC_TX_CNT_ABORTC += card_status & 0xF;
card_status >>= 4;
// multiple collisions
dev->Stats->cEMAC_TX_CNT_ABORTC += card_status & 0xF;
#endif
_outpw(BSR_REG, 2);
// Acknowledge the interrupt
_outp(INT_REG, IM_TX_EMPTY_INT);
mask &= ~IM_TX_EMPTY_INT;
#if LAN91C111_LOG_NET_ERRORS
// Delayed tx packets
dev->Stats->cEMAC_TX_CNT_DEFER += dev->LAN_state.packets_waiting;
#endif
// clear state of waiting packets
dev->LAN_state.packets_waiting = 0;
}
if (stat & IM_ALLOC_INT )
{
// store debug
dev->LAN_state.last_IRQ_serviced = IM_ALLOC_INT;
// allocation IRQ
dev->LAN_state.alloc_success = 1;
// clear this interrupt so it doesn't happen again
mask &= ~IM_ALLOC_INT;
/* enable xmit interrupts based on this */
mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
}
if (stat & IM_RX_OVRN_INT )
{
// store debug
dev->LAN_state.last_IRQ_serviced = IM_RX_OVRN_INT;
#if LAN91C111_LOG_NET_ERRORS
//dev->stats.rx_errors++;
//dev->stats.rx_fifo_errors++;
dev->Stats->cEMAC_RX_CNT_IRL++;
#endif
// Acknowledge the interrupt
_outp(INT_REG, IM_RX_OVRN_INT);
// release the dma
dma_relinquish();
// enable interrupts
LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
}
if (stat & IM_EPH_INT )
{
// currently unsupported IRQ
// store debug
dev->LAN_state.last_IRQ_serviced = IM_EPH_INT;
}
if (stat & IM_MDINT )
{
// store debug
dev->LAN_state.last_IRQ_serviced = IM_MDINT;
LAN91C111_phy_interrupt(dev);
// Acknowledge the interrupt
_outp(INT_REG, IM_MDINT);
}
if (stat & IM_ERCV_INT )
{
// store debug
// currently unsupported IRQ
dev->LAN_state.last_IRQ_serviced = IM_ERCV_INT;
// Acknowledge the interrupt
_outp(INT_REG, IM_ERCV_INT);
}
// read the status flag and mask it
stat = _inp(INT_REG)& mask;
//restore found register states
// Its from receive.
if(dev->int_state.saved_mask)
{
ACK_LAN_INT(PF9);
return 1;
}
_outpw(PTR_REG, saved_pointer);
_outpw(BSR_REG, saved_bank);
no_interrupt:
ACK_LAN_INT(PF9);
_outpw(BSR_REG, 2);
_outp(IM_REG, mask);
return 1;
}
/******************************************************************************
* Configures PHY
*****************************************************************************/
static SetPhy(ADI_ETHER_LAN91C111_DATA *dev)
{
u16 phydat;
phydat = LAN91C111_read_phy_register(PHY_CNTL_REG);
LAN91C111_write_phy_register(PHY_CNTL_REG, 0x8000); // get phy from reset
phydat = 0x8000;
while (phydat&0x8000) {
phydat = LAN91C111_read_phy_register(PHY_CNTL_REG);
}
// Program PHY registers
phydat = 0;
int pha;
phydat = 0;
if (dev->Auto) {
phydat |= 0x1000; // enable auto negotiation
} else {
if (dev->FullDuplex) {
phydat |= (1 << 8); // full duplex
} else {
phydat &= (~(1 << 8)); // half duplex
}
if (dev->Port100) {
phydat |= (1 << 13); // 100 Mbps
} else {
phydat &= (~(1 << 13)); // 10 Mbps
}
}
LAN91C111_write_phy_register(PHY_CNTL_REG, phydat);
phydat = LAN91C111_read_phy_register(PHY_CNTL_REG);
if (dev->Loopback) {
phydat |= (1 << 14); // enable TX->RX loopback
}
LAN91C111_write_phy_register(PHY_CNTL_REG, phydat);
}
/******************************************************************************
* LAN Initialization routine
*****************************************************************************/
static int StartMac(ADI_ETHER_LAN91C111_DATA *dev)
{
int i;
short wtmp, wstat;
ENTER_CRITICAL_REGION();
pHandle = dev;// used in transmit_complete/receive_complete
//init SMSC
_outpw(BSR_REG, 0); // select bank 0
_outpw(RCR_REG, RCR_SOFTRST); // issue soft reset
LAN91C111_write_phy_register(PHY_CNTL_REG, 0x8000); // get phy from reset
_outpw(RCR_REG, 0); // clear reset
// Hook Interrupts
adi_int_CECHook(dev->RXIVG,(ADI_INT_HANDLER_FN)LAN91C111_InterruptHandler,&EtherDev,FALSE);
adi_int_CECHook(ik_ivg13,(ADI_INT_HANDLER_FN)dma_interrupt_handler,&EtherDev,FALSE);
// Enable interrupts in SIC PFB flag pin which is mapped to lan interrupt
//
adi_int_SICSetIVG(ADI_INT_PFB,dev->RXIVG);
adi_int_SICEnable(ADI_INT_PFB);
smsc_sleep(250);
// disable TX and RX functionality
_outpw(RCR_REG, 0); // clear RX
_outpw(TCR_REG, 0); // clear TX
// init MAC address
_outpw(BSR_REG, 1); // select bank 1
_outpw(CFG_REG, CFG_EPH_POWER_EN); // get out of low power mode
_outpw(CFG_REG, _inpw(CFG_REG)|CFG_NOWAIT); // set NO_WAIT
if(memcmp(dev->phyAddr,"\x00\x00\x00\x00\x00\x00",6))
{
//set MAC Addr
wtmp = (((dev->phyAddr[1])<<8)|(dev->phyAddr[0]));
_outpw(ADDR0_REG, wtmp);
wtmp = (((dev->phyAddr[3])<<8)|(dev->phyAddr[2]));
_outpw(ADDR1_REG, wtmp);
wtmp = (((dev->phyAddr[5])<<8)|(dev->phyAddr[4]));
_outpw(ADDR2_REG, wtmp);
}
else // get mac from the EEPROM
{
wtmp = _inpw(ADDR0_REG);
dev->phyAddr[0] = wtmp & 0x00FF;
dev->phyAddr[1] = wtmp >>8 & 0x00FF;
wtmp = _inpw(ADDR1_REG);
dev->phyAddr[2] = wtmp & 0x00FF;
dev->phyAddr[3] = wtmp >>8 & 0x00FF;
wtmp = _inpw(ADDR2_REG);
dev->phyAddr[4] = wtmp & 0x00FF;
dev->phyAddr[5] = wtmp >>8 & 0x00FF;
}
// release all pending packets
_outpw(CTL_REG, _inpw(CTL_REG)|CTL_AUTO_RELEASE);
// reset MMU
_outpw(BSR_REG, 2);
_outpw(MMU_CMD_REG, MC_RESET);
// disable all IRQs
_outp(IM_REG, 0);
// autonego + LEDs
_outpw(BSR_REG, 0);
_outpw(RPC_REG, RPC_ANEG | 0x80);
//_outpw(RPC_REG, RPC_DEFAULT);
// turn off isolation mode
LAN91C111_write_phy_register(PHY_CNTL_REG, 0x3000 | (dev->Loopback?PHY_CNTL_LPBK:0));
// read PHY_STAT once due to update latency
LAN91C111_read_phy_register(PHY_STAT_REG);
i= 25;
while (i>0) {
wtmp = LAN91C111_read_phy_register(PHY_STAT_REG);
if (wtmp & PHY_STAT_LINK) break; // the link is up
smsc_sleep(100);
i--;
}
#if LAN91C111_DBG
printf("\n");
wtmp = LAN91C111_read_phy_register(PHY_CNTL_REG);
printf("PHY_CNTL_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_STAT_REG);
printf("PHY_STAT_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_ID1_REG);
printf("PHY_ID1_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_ID2_REG);
printf("PHY_ID2_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_AD_REG);
printf("PHY_AD_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_RMT_REG);
printf("PHY_RMT_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_CFG1_REG);
printf("PHY_CFG1_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_CFG2_REG);
printf("PHY_CFG2_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_INT_REG);
printf("PHY_INT_REG: 0x%x\n", wtmp);
wtmp = LAN91C111_read_phy_register(PHY_STAT_REG);
printf("PHY_STAT_REG: 0x%x\n", wtmp);
#endif //LAN91C111_DBG
// check anego bits
wtmp = LAN91C111_read_phy_register(PHY_STAT_REG);
if ((wtmp & (PHY_STAT_LINK|PHY_STAT_ANEG_ACK)) == (PHY_STAT_LINK|PHY_STAT_ANEG_ACK))
{ // success
wstat = LAN91C111_read_phy_register(PHY_INT_REG);
#if LAN91C111_DBG
printf("\nANEG success, Status = 0x%x", wstat);
#endif
// read transmit register
wtmp = _inpw(TCR_REG);
// check for full duplex FDX
if ((wstat & PHY_INT_DPLXDET) == PHY_INT_DPLXDET)
wtmp |= TCR_SWFDUP;
else
wtmp &= ~TCR_SWFDUP;
// write FDX result
_outpw(TCR_REG, wtmp);
}
else
{ // fail
wstat = LAN91C111_read_phy_register(PHY_INT_REG);
#if LAN91C111_DBG
printf("\nANEG failed, Status = 0x%x", wstat);
#endif
}
// device is started and active now.
dev->Started = true;
// enable RX and TX
_outpw(BSR_REG, 0);
_outpw(TCR_REG, _inpw(TCR_REG)|TCR_DEFAULT); // enable TX
_outpw(RCR_REG, _inpw(RCR_REG)|RCR_DEFAULT); // enable RX
// enable IRQs
_outpw(BSR_REG, 2);
_outp(IM_REG, SMC_INTERRUPT_MASK); // standard IRQ mask
_outpw(BSR_REG, 0);
EXIT_CRTICIAL_REGION();
return 1;
}
#pragma optimize_for_speed
static int QueueNewRcvFrames (ADI_ETHER_LAN91C111_DATA *dev,ADI_ETHER_BUFFER *bufs)
{
ADI_ETHER_BUFFER *tmp_q_ele;
if (bufs == NULL)
return (0);
ENTER_CRITICAL_REGION();
tmp_q_ele = bufs;
if (dev->m_RxEnqueuedCount)
dev->m_RxEnqueuedTail->pNext = (ADI_ETHER_BUFFER*) tmp_q_ele;
else
dev->m_RxEnqueuedHead = tmp_q_ele;
//We have attached one
dev->m_RxEnqueuedCount++;
//Now look for rest
while (tmp_q_ele->pNext != NULL)
{
// increment the count
dev->m_RxEnqueuedCount++;
tmp_q_ele = (ADI_ETHER_BUFFER*)tmp_q_ele->pNext;
}
//Amd update tail
dev->m_RxEnqueuedTail = tmp_q_ele;
dev->RxStarted = 1;
EXIT_CRTICIAL_REGION();
//return (VCSE_MRESULT)MR_OK;
return 1;
}
static int QueueNewXmtFrames (ADI_ETHER_LAN91C111_DATA *dev,ADI_ETHER_BUFFER *bufs)
{
ADI_ETHER_BUFFER *tmp_q_ele;
// if the incoming element is NULL throw an error
if (bufs == NULL)
return (0);
// Come out of critical region so that DMA interrupt
// can be executed.
dma_protect(dev,DMA_DIR_TX);
ENTER_CRITICAL_REGION();
tmp_q_ele = bufs;
tmp_q_ele->ProcessedFlag = 0;
if (dev->m_TxEnqueuedCount)
dev->m_TxEnqueuedTail->pNext = (ADI_ETHER_BUFFER*)tmp_q_ele;
else
dev->m_TxEnqueuedHead = tmp_q_ele;
//We have attached one
dev->m_TxEnqueuedCount++;
// Now look for rest
// Note: Currently discriptor based DMA is not supported.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -