📄 drv_lan91c111.c
字号:
/*---------------------------------------------------------------
* Developed By Signal Process, synergy Co.,Ltd.
* Copyright (c) 2006-2008 synergy Co.,Ltd.
* All Right Reserved.
*
* $Author Zhaoww (Zhaoww@synergy.com.cn)
* $Version: Alpha
* $Since 2006.4.11
* $Log
*
* DESCRIPTION
* This is a driver for SMSC's LAN91C111
* single-chip Ethernet device
*
*---------------------------------------------------------------*/
#define _DRV_91C111_GLOBALS_
#include "includes.h"
#include "drv_int.h"
#include "drv_lan91c111.h"
#include "tasksystem.h"
INT8U smc_mac_addr[] = { 0xc0, 0x00, 0x00, 0x1b, 0x62, 0x9d};
void SMC_outb(INT16U d,INT16U r)
{
INT16U __d = (INT8U)(d);
INT16U __w = SMC_inw((r)&~1);
__w &= ((r)&1) ? 0x00FF : 0xFF00;
__w |= ((r)&1) ? __d<<8 : __d;
SMC_outw(__w,(r)&~1);
}
void SMC_insw(INT8U r,INT8U *b,INT32U l)
{
int __i ;
INT16U *__b2;
__b2 = (INT16U *) b;
for (__i = 0; __i < l; __i++)
{
*(__b2 + __i) = SMC_inw(r);
SMC_inw(0);
};
}
void SMC_outsw(INT8U r,INT8U *b,INT32U l)
{
int __i;
INT16U *__b2;
__b2 = (INT16U *) b;
for (__i = 0; __i < l; __i++)
{
SMC_outw( *(__b2 + __i), r);
}
}
static int poll4int (INT8U mask, INT32U timeout)
{
INT16U old_bank = SMC_inw (BSR_REG);
OS_DEBUGF(DEV_91C111_DEBUG,("Polling...\r\n"));
SMC_SELECT_BANK (2);
while ((SMC_inw (SMC91111_INT_REG) & mask) == 0) {}
/* restore old bank selection */
SMC_SELECT_BANK (old_bank);
return 0;
}
/* Only one release command at a time, please */
static inline void smc_wait_mmu_release_complete (void)
{
int count = 0;
/* assume bank 2 selected */
while (SMC_inw (MMU_CMD_REG) & MC_BUSY) {
udelay (10); /* Wait until not busy */
if (++count > 200)
break;
}
}
/*
. Function: smc_reset( void )
. Purpose:
. This sets the SMC91111 chip to its normal state, hopefully from whatever
. mess that any other DOS driver has put it in.
.
. Maybe I should reset more registers to defaults in here? SOFTRST should
. do that for me.
.
. Method:
. 1. send a SOFT RESET
. 2. wait for it to finish
. 3. enable autorelease mode
. 4. reset the memory management unit
. 5. clear all interrupts
.
*/
static void smc_reset (void)
{
/* This resets the registers mostly to defaults, but doesn't
affect EEPROM. That seems unnecessary */
SMC_SELECT_BANK (0);
SMC_outw (RCR_SOFTRST, RCR_REG);
/* Setup the Configuration Register */
/* This is necessary because the CONFIG_REG is not affected */
/* by a soft reset */
SMC_SELECT_BANK (1);
SMC_outw (CONFIG_DEFAULT, CONFIG_REG);
/* Release from possible power-down state */
/* Configuration register is not affected by Soft Reset */
SMC_outw (SMC_inw (CONFIG_REG) | CONFIG_EPH_POWER_EN, CONFIG_REG);
SMC_SELECT_BANK (0);
/* this should pause enough for the chip to be happy */
//udelay( 50 );
OSTimeDly( OS_DELAY_TIME(dSYSTEM_TIME_120MS));
/* Disable transmit and receive functionality */
SMC_outw (RCR_CLEAR, RCR_REG);
SMC_outw (TCR_CLEAR, TCR_REG);
/* set the control register */
SMC_SELECT_BANK (1);
SMC_outw (CTL_DEFAULT, CTL_REG);
/* Reset the MMU */
SMC_SELECT_BANK (2);
smc_wait_mmu_release_complete ();
SMC_outw (MC_RESET, MMU_CMD_REG);
while (SMC_inw (MMU_CMD_REG) & MC_BUSY)
udelay (10); /* Wait until not busy */
/* Note: It doesn't seem that waiting for the MMU busy is needed here,
but this is a place where future chipsets _COULD_ break. Be wary
of issuing another MMU command right after this */
/* Disable all interrupts */
SMC_outb (0, IM_REG);
}
/*
. Function: smc_enable
. Purpose: let the chip talk to the outside work
. Method:
. 1. Enable the transmitter
. 2. Enable the receiver
. 3. Enable interrupts
*/
static void smc_enable()
{
SMC_SELECT_BANK( 0 );
/* see the header file for options in TCR/RCR DEFAULT*/
SMC_outw( TCR_DEFAULT, TCR_REG );
SMC_outw( RCR_DEFAULT, RCR_REG );
SMC_SELECT_BANK( 2 );
SMC_outb( IM_RCV_INT, IM_REG );
/* clear MII_DIS */
/* smc_write_phy_register(PHY_CNTL_REG, 0x0000); */
}
static INT16U smc_read_phy_register (INT8U phyreg)
{
INT32U oldBank;
INT32U i;
INT8U mask;
INT16U mii_reg;
INT8U bits[64];
INT32U clk_idx = 0;
INT32U input_idx;
INT16U phydata;
INT8U phyaddr = SMC_PHY_ADDR;
/* 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;
/* Output the PHY address, msb first */
mask = (INT8U) 0x10;
for (i = 0; i < 5; ++i) {
if (phyaddr & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Output the phy register number, msb first */
mask = (INT8U) 0x10;
for (i = 0; i < 5; ++i) {
if (phyreg & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Tristate and turnaround (2 bit times) */
bits[clk_idx++] = 0;
/*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 = SMC_inw (BANK_SELECT);
/* Select bank 3 */
SMC_SELECT_BANK (3);
/* Get the current MII register value */
mii_reg = SMC_inw (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 < sizeof bits; ++i) {
/* Clock Low - output data */
SMC_outw (mii_reg | bits[i], MII_REG);
udelay (SMC_PHY_CLOCK_DELAY);
/* Clock Hi - input data */
SMC_outw (mii_reg | bits[i] | MII_MCLK, MII_REG);
udelay (SMC_PHY_CLOCK_DELAY);
bits[i] |= SMC_inw (MII_REG) & MII_MDI;
}
/* Return to idle state */
/* Set clock to low, data to low, and output tristated */
SMC_outw (mii_reg, MII_REG);
udelay (SMC_PHY_CLOCK_DELAY);
/* Restore original bank select */
SMC_SELECT_BANK (oldBank);
/* Recover input data */
phydata = 0;
for (i = 0; i < 16; ++i) {
phydata <<= 1;
if (bits[input_idx++] & MII_MDI)
phydata |= 0x0001;
}
return (phydata);
}
/*------------------------------------------------------------
. Writes a register to the MII Management serial interface
.-------------------------------------------------------------*/
static void smc_write_phy_register (INT8U phyreg, INT16U phydata)
{
INT32U oldBank;
INT32U i;
INT16U mask;
INT16U mii_reg;
INT8U bits[65];
INT32U clk_idx = 0;
INT8U phyaddr = SMC_PHY_ADDR;
/* 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 */
mask = (INT8U) 0x10;
for (i = 0; i < 5; ++i) {
if (phyaddr & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Output the phy register number, msb first */
mask = (INT8U) 0x10;
for (i = 0; i < 5; ++i) {
if (phyreg & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Tristate and turnaround (2 bit times) */
bits[clk_idx++] = 0;
bits[clk_idx++] = 0;
/* Write out 16 bits of data, msb first */
mask = 0x8000;
for (i = 0; i < 16; ++i) {
if (phydata & mask)
bits[clk_idx++] = MII_MDOE | MII_MDO;
else
bits[clk_idx++] = MII_MDOE;
/* Shift to next lowest bit */
mask >>= 1;
}
/* Final clock bit (tristate) */
bits[clk_idx++] = 0;
/* Save the current bank */
oldBank = SMC_inw (BANK_SELECT);
/* Select bank 3 */
SMC_SELECT_BANK (3);
/* Get the current MII register value */
mii_reg = SMC_inw (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 */
SMC_outw (mii_reg | bits[i], MII_REG);
udelay (SMC_PHY_CLOCK_DELAY);
/* Clock Hi - input data */
SMC_outw (mii_reg | bits[i] | MII_MCLK, MII_REG);
udelay (SMC_PHY_CLOCK_DELAY);
bits[i] |= SMC_inw (MII_REG) & MII_MDI;
}
/* Return to idle state */
/* Set clock to low, data to low, and output tristated */
SMC_outw (mii_reg, MII_REG);
udelay (SMC_PHY_CLOCK_DELAY);
/* Restore original bank select */
SMC_SELECT_BANK (oldBank);
}
#if 0
static void smc_wait_ms(unsigned int ms)
{
udelay(ms*1000);
}
#endif
static void smc_phy_configure ()
{
INT32U timeout;
INT8U phyaddr;
INT16U my_phy_caps; /* My PHY capabilities */
INT16U my_ad_caps; /* My Advertised capabilities */
INT16U status = 0; /*;my status = 0 */
/* Get the detected phy address */
phyaddr = SMC_PHY_ADDR;
phyaddr = phyaddr ;
/* Reset the PHY, setting all other bits to zero */
smc_write_phy_register (PHY_CNTL_REG, PHY_CNTL_RST);
/* Wait for the reset to complete, or time out */
timeout = 6; /* Wait up to 3 seconds */
while (timeout--) {
if (!(smc_read_phy_register (PHY_CNTL_REG)
& PHY_CNTL_RST)) {
/* reset complete */
break;
}
//smc_wait_ms (500); /* wait 500 millisecs */
OSTimeDly( OS_DELAY_TIME(dSYSTEM_TIME_520MS));
}
if (timeout < 1) {
OS_DEBUGF(DEV_91C111_DEBUG,("%s:PHY reset timed out\r\n", SMC_DEV_NAME));
return;
}
/* Read PHY Register 18, Status Output */
/* lp->lastPhy18 = smc_read_phy_register(PHY_INT_REG); */
/* Enable PHY Interrupts (for register 18) */
/* Interrupts listed here are disabled */
smc_write_phy_register (PHY_MASK_REG, 0xffff);
/* Configure the Receive/Phy Control register */
SMC_SELECT_BANK (0);
SMC_outw (RPC_DEFAULT, RPC_REG);
/* Copy our capabilities from PHY_STAT_REG to PHY_AD_REG */
my_phy_caps = smc_read_phy_register (PHY_STAT_REG);
my_ad_caps = PHY_AD_CSMA; /* I am CSMA capable */
if (my_phy_caps & PHY_STAT_CAP_T4)
my_ad_caps |= PHY_AD_T4;
if (my_phy_caps & PHY_STAT_CAP_TXF)
my_ad_caps |= PHY_AD_TX_FDX;
if (my_phy_caps & PHY_STAT_CAP_TXH)
my_ad_caps |= PHY_AD_TX_HDX;
if (my_phy_caps & PHY_STAT_CAP_TF)
my_ad_caps |= PHY_AD_10_FDX;
if (my_phy_caps & PHY_STAT_CAP_TH)
my_ad_caps |= PHY_AD_10_HDX;
/* Update our Auto-Neg Advertisement Register */
smc_write_phy_register (PHY_AD_REG, my_ad_caps);
/* Read the register back. Without this, it appears that when */
/* auto-negotiation is restarted, sometimes it isn't ready and */
/* the link does not come up. */
smc_read_phy_register(PHY_AD_REG);
OS_DEBUGF(DEV_91C111_DEBUG, ("%s:phy caps=%x\r\n", SMC_DEV_NAME, my_phy_caps));
OS_DEBUGF(DEV_91C111_DEBUG,("%s:phy advertised caps=%x\r\n", SMC_DEV_NAME, my_ad_caps));
/* Restart auto-negotiation process in order to advertise my caps */
smc_write_phy_register (PHY_CNTL_REG,
PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST );
/* Wait for the auto-negotiation to complete. This may take from */
/* 2 to 3 seconds. */
/* Wait for the reset to complete, or time out */
timeout = 20; /* Wait up to 10 seconds */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -