📄 lan91c111.c
字号:
// Final clock bit (tristate)
bits[clk_idx++] = 0;
// Save the current bank
oldBank = _inpw(BSR_REG);
// Select bank 3
_outpw(BSR_REG, 3);
// Get the current MII register value
mii_reg = _inpw(MII_REG);
// Turn off all MII Interface bits
mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
// Clock all cycles
for (i = 0; i < sizeof bits; ++i)
{
// Clock Low - output data
_outpw(MII_REG, mii_reg | bits[i] );
// Clock Hi - input data
_outpw(MII_REG, mii_reg | bits[i] | MII_MCLK);
}
// Return to idle state
// Set clock to low, data to low, and output tristated
_outpw(MII_REG, mii_reg);
// Restore original bank select
_outpw(BSR_REG, oldBank );
return;
}
/******************************************************************************
* Reads a word through the MII interface off the PHY
*****************************************************************************/
static unsigned short LAN91C111_read_phy_register(unsigned char phyreg)
{
volatile unsigned char bits[64];
volatile unsigned char bmask;
volatile unsigned short oldBank, mii_reg, phydata, wmask;
volatile int i, clk_idx = 0;
volatile int input_idx;
// Prepare bit stream for PHY
// 32 consecutive ones on MDO to establish sync
for (i = 0; i < 32; ++i)
bits[clk_idx++] = MII_MDOE | MII_MDO;
// Start code <01>
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE | MII_MDO;
// Read command <10>
bits[clk_idx++] = MII_MDOE | MII_MDO;
bits[clk_idx++] = MII_MDOE;
// internal PHY address = <00000>
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE;
// Output the phy register number, msb first
bmask = 0x10;
for (i = 0; i < 5; ++i)
{
if (phyreg & bmask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
// Shift to next lowest bit
bmask >>= 1;
}
// Tristate and turnaround (2 bit times)
bits[clk_idx++] = 0;
// Input starts at this bit time
input_idx = clk_idx;
// Will input 16 bits
for (i = 0; i < 16; ++i)
bits[clk_idx++] = 0;
// Final clock bit
bits[clk_idx++] = 0;
// Save the current bank
oldBank = _inpw(BSR_REG);
// Select bank 3
_outpw(BSR_REG, 3);
// Get the current MII register value
mii_reg = _inpw(MII_REG);
// Turn off all MII Interface bits
mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
// Clock all 64 cycles
for (i = 0; i < clk_idx /*sizeof bits*/; ++i)
{
// Clock Low - output data
_outpw(MII_REG, mii_reg | bits[i]);
// Clock Hi - input data
_outpw(MII_REG, mii_reg | bits[i] | MII_MCLK);
bits[i] |= _inpw(MII_REG) & MII_MDI;
}
// Return to idle state
// Set clock to low, data to low, and output tristated
_outpw(MII_REG, mii_reg);
// Restore original bank select
_outpw(BSR_REG, oldBank);
// Recover input data
phydata = 0;
for (i = 0; i < 16; ++i)
{
phydata <<= 1;
if (bits[input_idx++] & MII_MDI)
phydata |= 0x0001;
}
return (phydata);
}
/******************************************************************************
* IRQ Sub Function: SMC_RCV for the LAN91C111
*
* Purpose:
* There is a packet waiting to be read from 91C111
*
* Actions:
* - Read the status
* - If an error, record it
* - otherwise, read in the packet
*
*****************************************************************************/
static void LAN91C111_rcv(ADI_ETHER_LAN91C111_DATA *dev)
{
volatile int i, packet_number,chip_id;
volatile unsigned short status;
volatile unsigned short packet_length;
#ifndef USE_16_BIT
unsigned int volatile *data;
#else
unsigned short volatile *data;
#endif
unsigned short *elnth;
// bank 2 already opened by main IRQ entry
packet_number = _inpw(FIFO_REG);
if ( packet_number & RXFIFO_REMPTY )
{
// we got called , but nothing was on the FIFO
// so, just leave
dma_relinquish();
LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
return;
}
// start reading from the start of the packet
_outpw(PTR_REG, PTR_READ | PTR_RCV | PTR_AUTOINC);
// First two words are status and packet_length
status = _inpw(DATA_REG);
packet_length = _inpw(DATA_REG);
// store for debug
dev->LAN_state.rpsize = packet_length;
// max 2k packets over Ethernet
packet_length &= 0x07ff; /* mask off top bits */
if ( !(status & RS_ERRORS ) )
{
ADI_ETHER_BUFFER *tmp_q_ele;
#if LAN91C111_LOG_NET_ERRORS
if ( status & RS_MULTICAST ) dev->Stats->cEMAC_RX_CNT_MULTI++;
#endif
// collect the packet
// first two words already read: status and packet size
if((status & 0x1000))
packet_length -= 5; //## used to be 4
else
{
// Get the chip id and fix the hardware anomaly rev-A
_outpw(BSR_REG,3);
chip_id = _inpw(REV_REG);
if( ((chip_id & 0x0F00) == 9) && ((chip_id & 0xF000) == 0) )
packet_length -= 4;
else
packet_length -= 6;
_outpw(BSR_REG, 2);
}
tmp_q_ele = dev->m_RxEnqueuedHead;
if (tmp_q_ele != NULL) {
elnth = (unsigned short *)tmp_q_ele->Data;
*elnth = (packet_length + 4); // add on 4 bytes for the CRC (even if we dont store it)
tmp_q_ele->ProcessedElementCount = (*elnth+2+tmp_q_ele->ElementWidth-1)/tmp_q_ele->ElementWidth;
}
if ((tmp_q_ele)&&(tmp_q_ele->ProcessedElementCount <= tmp_q_ele->ElementCount))
{
//store packet length
// assign pointer
data = (void *)( ((char *)tmp_q_ele->Data) +2);
}
else
{
#if LAN91C111_LOG_NET_ERRORS
if (tmp_q_ele) {
dev->Stats->cEMAC_RX_CNT_LONG++;
} else {
dev->Stats->cEMAC_RX_CNT_LOST++;
}
#endif
// drop packet
// good or bad, delete this packet */
while ( _inpw(MMU_CMD_REG ) & MC_BUSY );
_outpw(MMU_CMD_REG, MC_RELEASE);
dma_relinquish();
LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
return;
}
// dma is already grabbed in the interrupt handler.
dma_initiate_transfer((unsigned long)DATA_REG,(unsigned long)data,packet_length >> 1,DMA_DIR_RX);
#if LAN91C111_LOG_NET_ERRORS
dev->Stats->cEMAC_RX_CNT_OK++;
#endif
}
else
{
dma_relinquish();
LAN91C111_enable_int( (SMC_INTERRUPT_MASK) );
#if LAN91C111_LOG_NET_ERRORS
//cptr->stats.rx_errors++;
if ( status & RS_ALGNERR ) dev->Stats->cEMAC_RX_CNT_ALIGN++;
if ( status & (RS_TOOSHORT|RS_TOOLONG)) dev->Stats->cEMAC_RX_CNT_LONG++;
if ( status & RS_BADCRC) dev->Stats->cEMAC_RX_CNT_FCS++;
#endif
while ( _inpw(MMU_CMD_REG ) & MC_BUSY );
// good or bad, delete this packet */
_outpw(MMU_CMD_REG, MC_RELEASE);
}
return;
}
/******************************************************************************
* TX IRQ Error Handler Function for SMSC91C111
*
* Purpose: Handle the transmit error message.
* This is only called when an TX error occured because of
* the AUTO_RELEASE mode.
*
* Actions:
* - Save pointer and packet no
* - Get the packet no from the top of the queue
* - check if it's valid ( if not, is this an error??? )
* - read the status word
* - record the error
* - ( resend? Not really, since we don't want old packets around )
* - Restore saved values
*
*****************************************************************************/
static void LAN91C111_tx(ADI_ETHER_LAN91C111_DATA *dev)
{
volatile unsigned char saved_packet;
volatile unsigned char packet_no;
volatile unsigned short tx_status;
// bank 2 already opened by main IRQ entry
saved_packet = _inp(PN_REG);
packet_no = _inpw(FIFO_REG);
packet_no &= 0x7F;
// If the TX FIFO is empty then nothing to do
if ( packet_no & TXFIFO_TEMPTY )
return;
// select this as the packet to read from
_outp(PN_REG, packet_no);
// read the first word (status word) from this packet
_outpw(PTR_REG, PTR_AUTOINC|PTR_READ);
// read tx status
tx_status = _inpw(DATA_REG );
#if LAN91C111_LOG_NET_ERRORS
dev->Stats->cEMAC_TX_CNT_ABORT++;
if ( tx_status & TS_LOSTCAR ) dev->Stats->cEMAC_TX_CNT_CRS++;
if ( tx_status & TS_LATCOL ) dev->Stats->cEMAC_TX_CNT_LATE++;
#endif
// re-enable transmit
_outpw(BSR_REG, 0);
_outpw(TCR_REG, _inpw(TCR_REG) | TCR_TXENA);
// kill the packet
_outpw(BSR_REG, 2);
_outpw(MMU_CMD_REG, MC_FREEPKT);
// one less packet waiting for me
dev->LAN_state.packets_waiting--;
// Don't change Packet Number Reg until busy bit is cleared
while ( _inpw(MMU_CMD_REG) & MC_BUSY );
_outp(PN_REG, saved_packet);
return;
}
/******************************************************************************
* IRQ Sub Function: smc_phy_interrupt for the LAN91C111
*
* Purpose:
* Handle interrupts relating to PHY register 18.
*
* Actions:
* Log last Phy18 Interrupt Source
*
*****************************************************************************/
static void LAN91C111_phy_interrupt(ADI_ETHER_LAN91C111_DATA *dev )
{
volatile unsigned short phy18;
while (1)
{
// Read PHY Register 18, Status Output
phy18 = LAN91C111_read_phy_register(PHY_INT_REG);
// Exit if no more changes
if (phy18 == dev->LAN_state.lastPhy18)
break;
// Update the last phy 18 variable
dev->LAN_state.lastPhy18 = phy18;
} // end while
}
/******************************************************************************
* LANC91C111: LAN91C111_hardware_send_packet
*
* Purpose:
* This sends the actual packet to the SMC9xxx chip.
*
* - First, see if a pending packet is available.
* this should NOT be called if there is none
* - Find the packet number that the chip allocated
* - Point the data pointers at it in memory
* - Set the length word in the chip's memory
* - Dump the packet to chip memory
* - Check if a last byte is needed ( odd length packet )
* if so, set the control flag right
* - Tell the LAn 91C111 to send it
* - Enable the transmit interrupt, so I know if it failed
* - Free the packet data if I actually sent it.
*
*****************************************************************************/
static unsigned char LAN91C111_hardware_send_packet(ADI_ETHER_LAN91C111_DATA* dev)
{
volatile int i;
volatile unsigned char packet_no;
volatile unsigned short length;
#ifndef USE_16_BIT
unsigned int volatile *buf;
#else
unsigned short volatile *buf;
#endif
unsigned short *elnth;
unsigned short lnth_first;
ADI_ETHER_BUFFER *bf = dev->m_TxEnqueuedHead;
// get pointer
#ifndef USE_16_BIT
buf = (unsigned int *)(((char *)bf->Data)+2); // first 2 bytes has length
#else
buf = (unsigned short *)(((char *)bf->Data)+2);
#endif
// the length is held in the first two bytes of the 'frame', the ElementCount says the number of elements in the first buffer
// get length
elnth = (unsigned short *)bf->Data;
length = *elnth;
lnth_first = bf->ElementCount*bf->ElementWidth - 2;
length = ETH_ZLEN < length ? length : ETH_ZLEN;
lnth_first = ETH_ZLEN < lnth_first ? lnth_first : ETH_ZLEN;
// If I get here, I _know_ there is a packet slot waiting for me
packet_no = _inp(AR_REG);
// or isn't there? BAD CHIP
if ( packet_no & AR_FAILED ) return LAN91C111_TX_ERROR;
// we have a packet address, so tell the card to use it
_outp(PN_REG, packet_no);
// point to the beginning of the packet
_outpw(PTR_REG, PTR_AUTOINC);
// send the packet length ( +6 for status, length and ctl byte )
// and the status word ( set to zeros ) */
#ifndef USE_16_BIT
_outpd(DATA_REG, (length+6) << 16);
#else
_outpw(DATA_REG, 0);
_outpw(DATA_REG, (length+6));
#endif
if(dev->Cache)
{
FlushArea((char*)buf,((char*)buf + lnth_first));
}
dma_initiate_transfer((unsigned long)buf,DATA_REG,lnth_first >> 1,DMA_DIR_TX);
// transfer second buffer
if(lnth_first < length)
{
buf = bf->PayLoad;
length = length - lnth_first;
dma_initiate_transfer((unsigned long)buf,DATA_REG,length >> 1,DMA_DIR_TX);
}
return LAN91C111_TX_SUCCESS;
}
/******************************************************************************
* LANC91C111: LAN91C111_dma_send_packet()
*
* Purpose:
* Attempt to allocate memory for a packet, if chip-memory is not
* available, then tell the card to generate an interrupt when it
* is available.
*****************************************************************************/
static unsigned char LAN91C111_dma_send_packet(ADI_ETHER_LAN91C111_DATA *dev)
{
volatile unsigned short length;
volatile unsigned short numPages;
volatile unsigned short time_out;
volatile unsigned short status;
volatile unsigned short retry_count=0;
unsigned short *elnth;
// the length is held in the first two bytes of the 'frame', the ElementCount says the number of elements in the first buffer
// get length
elnth = (unsigned short *)dev->m_TxEnqueuedHead->Data;
length = *elnth;
// set length
length = ETH_ZLEN < length ? length : ETH_ZLEN;
// clear allocation flag
dev->LAN_state.alloc_success = 0;
/*
* The MMU wants the number of pages to be the number of 256 bytes
* 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
*
* Pkt size for allocating is data length +6 (for additional status
* words, length and ctl!)
*
* If odd size then last byte is included in this header.
*/
numPages = ((length & 0xfffe) + 6);
numPages >>= 8; // Divide by 256
if (numPages > 7 )
{
// way too big packet, this is an error
// free packet
return LAN91C111_TX_ERROR;
}
// either way, a packet is waiting now
dev->LAN_state.packets_waiting++;
// now, try to allocate the memory */
_outpw(BSR_REG, 2);
_outpw(MMU_CMD_REG, MC_ALLOC | numPages);
/*
* Performance Hack
*
* wait a short amount of time.. if I can allocate a packet now, I set
* the flag now. Otherwise, I enable an interrupt and wait for one to be
* available.
*
*/
TX_RETRY:
time_out = 200; // number of iterations
do
{
status = _inp(INT_REG);
if ( status & IM_ALLOC_INT )
{
/* acknowledge the interrupt */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -