📄 lan91c111.c
字号:
//##############################################################
//#
//# LAN91C111.H
//#
//# ADSP-21535 Embedded Web Server Project
//#
//# (c) ANALOG DEVICES 2002
//# eDSP Division
//# Stefan Hacker
//# 23-DEC-2002
//#
//# History
//# 16-APR-2003 HS release 1.0
//#
#include <stdio.h>
#include "lan91c111.h"
#include "io_sprt.h"
#include "globals.h"
#include "tcp_ip.h"
#include "string.h"
/******************************************************************************
* Initializes SMSC91C111
*****************************************************************************/
void smc_init_91c111()
{
int i;
WORD wtmp, wstat, tmp, ioaddr,rev_reg;
/* First, see if the high byte is 0x33 */
tmp = _inpw(BSR_REG);
if ( (tmp & 0xFF00) != 0x3300 )
{
return;
}
/* The above MIGHT indicate a device, but I need to write to further test this. */
_outpw(BSR_REG, 0x0);
tmp = _inpw(BSR_REG);
if ( (tmp & 0xFF00 ) != 0x3300 )
{
return;
}
_outpw(BSR_REG, 1); // select bank 1
tmp = _inpw(BASE_REG );
ioaddr = ( tmp >> 3 & 0x3E0 );
_outpw(BSR_REG, 3); // select bank 1
tmp = _inpw(REV_REG );
rev_reg = ( tmp >> 4 & 0xF );
//init SMSC
_outpw(BSR_REG, 0); // select bank 0
_outpw(RCR_REG, RCR_SOFTRST); // issue soft reset
smc_write_phy_register(PHY_CNTL_REG, 0x8000); // get phy from reset
_outpw(RCR_REG, 0); // clear reset
Timeout_LAN = 2; // wait 250ms
while(Timeout_LAN);
// 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
_outpw(ADDR0_REG, MAC_IA_2<<8|MAC_IA_1); // set MAC addr
_outpw(ADDR1_REG, MAC_IA_4<<8|MAC_IA_3); // set MAC addr
_outpw(ADDR2_REG, MAC_IA_6<<8|MAC_IA_5); // set MAC addr
// 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);
// turn off isolation mode
smc_write_phy_register(PHY_CNTL_REG, 0x3000);
// wait 1.5sec
Timeout_LAN = 16;
while(Timeout_LAN);
// read PHY_STAT once due to update latency
smc_read_phy_register(PHY_STAT_REG);
#if 0//L_DP
printf("\n");
wtmp = smc_read_phy_register(PHY_CNTL_REG);
printf("PHY_CNTL_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_STAT_REG);
printf("PHY_STAT_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_ID1_REG);
printf("PHY_ID1_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_ID2_REG);
printf("PHY_ID2_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_AD_REG);
printf("PHY_AD_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_RMT_REG);
printf("PHY_RMT_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_CFG1_REG);
printf("PHY_CFG1_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_CFG2_REG);
printf("PHY_CFG2_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_INT_REG);
printf("PHY_INT_REG: 0x%x\n", wtmp);
wtmp = smc_read_phy_register(PHY_STAT_REG);
printf("PHY_STAT_REG: 0x%x\n", wtmp);
#endif //D_LP
// check anego bits
wtmp = smc_read_phy_register(PHY_STAT_REG);
if ((wtmp & (PHY_STAT_LINK|PHY_STAT_ANEG_ACK)) == (PHY_STAT_LINK|PHY_STAT_ANEG_ACK))
{ // success
wstat = smc_read_phy_register(PHY_INT_REG);
#if 1//D_LP==2
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 = smc_read_phy_register(PHY_INT_REG);
#if D_LP==2
printf("\nANEG failed, Status = 0x%x", wstat);
#endif
}
// 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);
}
/******************************************************************************
* Writes a word through the MII interface to the PHY
*****************************************************************************/
void smc_write_phy_register(BYTE phyreg, WORD phydata)
{
int i;
WORD oldBank, wmask, mii_reg;
BYTE bits[65], bmask;
int clk_idx = 0;
// 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;
// Write command <01>
bits[clk_idx++] = MII_MDOE;
bits[clk_idx++] = MII_MDOE | MII_MDO;
// Output the PHY address, msb first, internal PHY = 0
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) <10>
bits[clk_idx++] = MII_MDOE | MII_MDO;;
bits[clk_idx++] = 0;
// Write out 16 bits of data, msb first
wmask = 0x8000;
for (i = 0; i < 16; ++i)
{
if (phydata & wmask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
// Shift to next lowest bit
wmask >>= 1;
}
// 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 );
}
/******************************************************************************
* Reads a word through the MII interface off the PHY
*****************************************************************************/
WORD smc_read_phy_register(BYTE phyreg)
{
BYTE bits[64];
BYTE bmask;
WORD oldBank, mii_reg, phydata, wmask;
int i, clk_idx = 0;
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);
}
/******************************************************************************
* Main IRQ Handler Function for SMSC91C111
*****************************************************************************/
void smc_interrupt()
{
/*
* 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.
*/
BYTE status;
WORD card_status;
BYTE mask;
WORD saved_bank;
WORD saved_pointer;
// reset IRQ occurred
IRQ_LAN = 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);
/////////////////////
// set here a timeout, typically less than 1sec
//
Timeout_LAN = 4;
do {
// read the status flag and mask it
status = _inp(INT_REG)& mask;
if (!status )
break;
if (status & IM_RCV_INT) {
// store debug
LAN_state.last_IRQ_serviced = IM_RCV_INT;
// Got a packet(s), receive them
smc_rcv();
} else if (status & IM_TX_INT ) {
// store debug
LAN_state.last_IRQ_serviced = IM_TX_INT;
smc_tx();
// Acknowledge the interrupt
_outp(INT_REG, IM_TX_INT);
} else if (status & IM_TX_EMPTY_INT ) {
// store debug
LAN_state.last_IRQ_serviced = IM_TX_EMPTY_INT;
_outpw(BSR_REG, 0);
card_status = _inpw(COUNTER_REG );
#ifdef log_net_errs
// multiple collisions
stats.collisions += card_stats & 0xF;
card_stats >>= 4;
// multiple collisions
stats.collisions += card_stats & 0xF;
#endif
_outpw(BSR_REG, 2);
// Acknowledge the interrupt
_outp(INT_REG, IM_TX_EMPTY_INT);
mask &= ~IM_TX_EMPTY_INT;
#ifdef log_net_errs
stats.tx_packets += packets_waiting;
#endif
// clear state of waiting packets
LAN_state.packets_waiting = 0;
} else if (status & IM_ALLOC_INT ) {
// store debug
LAN_state.last_IRQ_serviced = IM_ALLOC_INT;
// allocation IRQ
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 );
} else if (status & IM_RX_OVRN_INT ) {
// store debug
LAN_state.last_IRQ_serviced = IM_RX_OVRN_INT;
#ifdef log_net_errs
stats.rx_errors++;
stats.rx_fifo_errors++;
#endif
// Acknowledge the interrupt
_outp(INT_REG, IM_RX_OVRN_INT);
} else if (status & IM_EPH_INT ) {
// currently unsupported IRQ
// store debug
LAN_state.last_IRQ_serviced = IM_EPH_INT;
} else if (status & IM_MDINT ) {
// store debug
LAN_state.last_IRQ_serviced = IM_MDINT;
smc_phy_interrupt();
// Acknowledge the interrupt
_outp(INT_REG, IM_MDINT);
} else if (status & IM_ERCV_INT ) {
// store debug
// currently unsupported IRQ
LAN_state.last_IRQ_serviced = IM_ERCV_INT;
// Acknowledge the interrupt
_outp(INT_REG, IM_ERCV_INT);
}
} while (Timeout_LAN--);
//restore found register states
_outpw(BSR_REG, 2);
_outp(IM_REG, mask);
_outpw(PTR_REG, saved_pointer);
_outpw(BSR_REG, saved_bank);
}
/******************************************************************************
* 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
*
*****************************************************************************/
void smc_rcv()
{
int i, packet_number;
WORD status;
WORD packet_length;
#ifndef USE_16_BIT
DWORD *data;
#else
WORD *data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -