📄 lan91c11x.c
字号:
//==========================================================================
//
// lan91c11x.c
//
// lan91c11x Ethernet Code
//
// Author(s): Michael Kelly, Cogent Computer Systems, Inc.
// Contributors:
// Date: 05-26-2002
// Description: This file contains intialization and support routines
// for the SMC LAN91c111 and 113 ethernet controllers found
// on some CSB's. Adapted from Ed Sutters original smc91c9x.c
// The PHY code was modeled after the Linux lan91c11x.c code
// with modifications.
//
//--------------------------------------------------------------------------
#include "config.h"
#if INCLUDE_ETHERNET
#include "cpuio.h"
#include "stddefs.h"
#include "genlib.h"
#include "ether.h"
#include "lan91c11x.h"
static int EtherRXOVRNCnt, EtherALGNERRCnt, EtherBADCRCCnt;
static int EtherTOOSHORTCnt, EtherTOOLONGCnt, EtherWFATMTCnt;
extern void enreset(), eninit();
ushort lan91c11x_phy(uchar phyreg, ushort phydata, int phy_dir);
int lan91c11x_phy_cfg(void);
//--------------------------------------------------------------------------
// lan91c11x_rd()
//
// This function does a read of the desired register in the lan91c11x
// using the upper 4 bits as the bank where the register is located
ushort lan91c11x_rd(ushort reg){
// first set the bank register for this access
LAN_REG(LAN_BANK) = ((reg & 0x00f0) >> 4);
// now return the data from the desired register
return LAN_REG((reg & 0x000f));
}
//--------------------------------------------------------------------------
// lan91c11x_wr()
//
// This function does a write to the desired register in the lan91c11x
// using the upper 4 bits as the bank where the register is located
void lan91c11x_wr(ushort reg, ushort data){
// first set the bank register for this access
LAN_REG(LAN_BANK) = ((reg & 0x00f0) >> 4);
// now write the data to the desired register
LAN_REG((reg & 0x000f)) = data;
}
//--------------------------------------------------------------------------
// lan91c11x_init()
//
// This code performs the following steps:
//
// 1. Verify the presence of the lan91c11x
// 2. setup the operating mode
// 3. Set the ethernet address from BinEnetAdd
// 4. Disable all interrupts
// 5. Enable the transmitter and receiver
// 6. Return
//
int lan91c11x_init(void)
{
int i;
// printf("lan91c11x Init.\n");
/* Initialize device-driver counters: */
EtherRXOVRNCnt = 0;
EtherALGNERRCnt = 0;
EtherBADCRCCnt = 0;
EtherTOOLONGCnt = 0;
EtherTOOSHORTCnt = 0;
EtherWFATMTCnt = 0;
// first see if we can ID the device
if((lan91c11x_rd(LAN_REV) & LAN_REV_CHIP_MASK) != LAN_REV_LAN91C11x){
printf("Bad lan91c11x ID = %04x!\n", lan91c11x_rd(LAN_REV) & LAN_REV_CHIP_MASK);
return -1;
}
// setup the operating mode
lan91c11x_wr(LAN_TCR, 0); // note - we pad the length in sendBuffer() in etherdev.c
lan91c11x_wr(LAN_RCR, LAN_RCR_STRIP_CRC); // strip CRC from rx packets before we get them
lan91c11x_wr(LAN_CONFIG, LAN_CONFIG_NO_WAIT | LAN_CONFIG_EPH_PWREN); // fast bus mode, power on
// enable auto-release mode for transmitted packets
lan91c11x_wr(LAN_CTRL, lan91c11x_rd(LAN_CTRL) | LAN_CTRL_AUTO_REL);
// enable auto-negotiate mode. Note that the PHY Control register
// ANEG bit must also be set, which it is on reset. Since we don't
// really care what mode we end up connecting in, we set the ANEG bit
// and then move one. We also set the LED's here
lan91c11x_wr(LAN_RPCR, LAN_RPCR_LEDA_LINK // The green led = any link, 10 or 100Mbit
| LAN_RPCR_LEDB_TXRX // The yellow led = activity
| LAN_RPCR_ANEG); // get whatever the link gives us!
monDelay(2);
// Store the ethernet address.
lan91c11x_wr(LAN_IA01, *(ushort *)BinEnetAddr);
lan91c11x_wr(LAN_IA23, *(ushort *)&BinEnetAddr[2]);
lan91c11x_wr(LAN_IA45, *(ushort *)&BinEnetAddr[4]);
// Disable Interrupts
lan91c11x_wr(LAN_INT, 0x0000);
// Enable the transmitter and receiver
lan91c11x_wr(LAN_TCR, lan91c11x_rd(LAN_TCR) | LAN_TCR_TXENA);
lan91c11x_wr(LAN_RCR, lan91c11x_rd(LAN_RCR) | LAN_RCR_RXEN);
// configure the PHY and attempt auotnegotiate
if (lan91c11x_phy_cfg())
{
// dump out the phy registers if it failed
for (i = 0; i < 32; i++)
{
printf("lan91c11x PHY Register %d = 0x%04x\n", i, lan91c11x_phy(i, i, 0));
}
}
// done
return 0;
}
//--------------------------------------------------------------------------
// lan91c11x_reset()
//
// Force a soft reset of the lan91c11x, then reset the mmu and fifos
void lan91c11x_reset(void)
{
/* Reset the device: */
lan91c11x_wr(LAN_RCR, LAN_RCR_SOFT_RST);
monDelay(2);
lan91c11x_wr(LAN_RCR, 0);
monDelay(5);
/* Reset MMU and TX fifos: */
lan91c11x_wr(LAN_MMU_CMD, LAN_MMU_CMD_RESETMMU);
lan91c11x_wr(LAN_MMU_CMD, LAN_MMU_CMD_RESETTXFIFOS);
}
//--------------------------------------------------------------------------
// lan91c11x_tx()
//
// This function puts the tx buffer into the lan91c11x as follows:
//
// 1. Write the Transmit Command
// 2. Write the Transmit Frame Length
// 3. Wait for CS_BusStat_TxRDY to go true
// 4. Copy txbuf into lan91c11x
// 5. Return
//
int lan91c11x_tx(uchar *txbuf, short len)
{
ushort temp16, *sptr, i;
ulong timeout;
if (EtherVerbose == SHOW_ALL) {
printf("lan91c11x: Transmit Buffer Address = 0x%08lx, Length = 0x%04x.\n", (ulong)txbuf, len);
}
// convert the length into the number of 256 byte
// pages we need to request from the lan91c11x.
temp16 = (ushort)(len >> 8);
// don't request more the 5 pages (1280 bytes), since
// that's bigger than any legal frame should be.
if (temp16 > 5) {
printf("sendBuffer(): len too long\n");
return(-1);
}
// Allocate the space and wait for the allocation to succeed.
lan91c11x_wr(LAN_MMU_CMD, LAN_MMU_CMD_ALLOCTXMEM | temp16);
// loop until the allocate interrupt bit is set or we exhaust the timeout
for(timeout = 10000; timeout > 0; timeout--) {
if (lan91c11x_rd(LAN_INT) & LAN_INT_ALLOC)
break;
}
// we should always succeed within the timeout period
// since we are only sending one packet at a time. If
// we don't it means we have either a chip problem or
// problems on the wire that are preventing the transmission
if (timeout == 0) {
printf("sendBuffer(%d) wait-for-alloc timeout\n",len);
// keep track of how often this happens - good for diag purposes
EtherWFATMTCnt++;
// restart the chip
EtherdevStartup(0);
return(-1);
}
// give the LAN back the pointer to the memory it just allocated to us
temp16 = lan91c11x_rd(LAN_PNRARR);
lan91c11x_wr(LAN_PNRARR, LAN_PNRARR_ARRTOPNR(temp16));
if (EtherVerbose == SHOW_ALL) {
printf("lan91c11x: Transmit Allocate Pointer = 0x%02x.\n", temp16);
}
// increment the pointer to point to this new packet
lan91c11x_wr(LAN_PTR, LAN_PTR_AUTOINC);
// a delay is necessary between POINTER and DATA access.
for(i = 0; i < 10; i++);
lan91c11x_wr(LAN_DATA1, 0); // control word
lan91c11x_wr(LAN_DATA2, len + 6); // total byte count including control bytes
// we do 16-bit writes, so get a 16-bit pointer to the tx buffer
sptr = (ushort *)txbuf;
// transfer the packet into the lan91c11x
while(1) {
// write the data until we have only one or no bytes left
if (len > 1) {
lan91c11x_wr(LAN_DATA1,*sptr++);
len -= 2;
}
// last byte of an odd size packet? set the odd length bit
else if (len == 1) {
lan91c11x_wr(LAN_DATA1,(*sptr & 0xff00) | LAN_CTL_BYTE_ODD_LENGTH);
break;
}
// even packet, clear the control byte
else {
lan91c11x_wr(LAN_DATA1,0);
break;
}
}
// tell the LAN to queue the packet for transmission
// remember: we use autorelease, so no cleanup of the
// MMU is required. It also means we have to rely on
// the upper layers to handle re-transmission.
lan91c11x_wr(LAN_MMU_CMD, LAN_MMU_CMD_ENQUEUETXPKT);
#if 0 /* This block of code is only applicable if we do not
* use AUTO_RELEASE...
*/
// wait for TX Success bit, then check status
// loop until the TX interrupt bit is set or we exhaust the timeout
for(timeout = 10000; timeout > 0; timeout--) {
if (lan91c11x_rd(LAN_EPH) & LAN_EPH_TX_SUC)
break;
}
// we should always succeed within the timeout period
// since we are only sending one packet at a time. If
// we don't it means we have either a chip problem or
// problems on the wire that are preventing the transmission
if (timeout == 0) {
printf("lan91c11x: Transmit Interrupt Timed Out\n");
// restart the chip
// EtherdevStartup(0);
// return(-1);
}
// clear the interrupt
// lan91c11x_wr(LAN_INT, LAN_INT_TX);
// check the status
temp16 = lan91c11x_rd(LAN_EPH);
if (!(temp16 & LAN_EPH_TX_SUC))
{
if (!(temp16 & LAN_EPH_LINK_OK)) {
printf("lan91c11x: Transmit Phy Link Not OK!\n");
}
if (temp16 & LAN_EPH_TXUNRN) {
printf("lan91c11x: Transmit Underrun Error!\n");
}
if (temp16 & LAN_EPH_EXC_DEF) {
printf("lan91c11x: Transmit Ecsessive Deferral Error!\n");
}
if (temp16 & LAN_EPH_LOST_CARR) {
printf("lan91c11x: Transmit Lost Carrier Error!\n");
}
if (temp16 & LAN_EPH_LATECOL) {
printf("lan91c11x: Transmit Late Collision Error!\n");
}
if (temp16 & LAN_EPH_SQET) {
printf("lan91c11x: Transmit Signal Quality (SQEL) Error!\n");
}
if (temp16 & LAN_EPH_16COL) {
printf("lan91c11x: Transmit 16 Collision Error!\n");
}
return(-1);
}
// got through OK!
printf("lan91c11x: Transmit Completed OK.\n");
#endif
// Enable the transmitter and receiver
lan91c11x_wr(LAN_TCR, lan91c11x_rd(LAN_TCR) | LAN_TCR_TXENA);
lan91c11x_wr(LAN_RCR, lan91c11x_rd(LAN_RCR) | LAN_RCR_RXEN);
return 0;
}
//--------------------------------------------------------------------------
// lan91c11x_rx()
//
// This function checks to see if the lan91c11x has a receive buffer
// ready. If so, it copies it into the buffer pointed to by pktbuf
//
int lan91c11x_rx(uchar *pktbuf)
{
ushort packet_num, rfsw, pktsize, count, *data, i, istat;
// check the RX interrupt to see if we have a packet
istat = lan91c11x_rd(LAN_INT);
// if it's an overrun, then we just reset the MMU to free
// up the memory, increment the error counter and leave
if (istat & LAN_INT_RXOVRN) {
lan91c11x_wr(LAN_MMU_CMD, LAN_MMU_CMD_RESETMMU);
if (EtherVerbose)
printf("lan91c11x Receive Overrun Error.\n");
// clear the over run interrupt bit
lan91c11x_wr(LAN_INT, LAN_INT_RXOVRN);
EtherRXOVRNCnt++;
return 0;
}
if (istat & LAN_INT_RCV)
{
// clear the RCV interrupt bit
lan91c11x_wr(LAN_INT, LAN_INT_RCV);
packet_num = lan91c11x_rd(LAN_FIFOPORT);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -