📄 smc9218.c
字号:
/**************************************************************
** Copyight (C) 2002-2007 基础技术开发部
** All right reserved
***************************************************************
**
** 项目名称:INET
** 功能简介:网络控制芯片操作
**
** 原始作者:段太钢 Email:duantaigang@xjgc.com
** 组织部门:许继集团 许继电气技术中心嵌入式软件开发部
** 备 注:
** 建立时间:2007-3-22 17:11
** 完成时间:2007-3-22 17:11
** 版 本:1.0
***************************************************************
** 单元标识:$Id: smc9218.c,v 1.3 2007/11/20 07:32:10 dtg Exp $
** 版本历史:
** 修 改 者:
** 最近修改:
**************************************************************/
#include "DSP_TCPIP.h"
#include "smc9218.h"
#include <bios.h>
#include <swi.h>
#include <hwi.h>
static void smc9218_init_prep( struct ei_device * ei_local );
static void smc911x_reset( struct ei_device * ei_local );
static int smc9218_init( struct ei_device * ei_local );
static void smc9218_send_packet( struct ei_device * ei_local, char * buffer, unsigned int length );
static void smc9218_rcv( struct ei_device * ei_local );
static int set_promisc( struct ei_device * ei_local );
static cfg_device_func lan9218_func = {
smc9218_init_prep,
smc911x_reset,
smc9218_init,
smc9218_send_packet,
smc9218_rcv,
set_promisc
};
int lan9218_driver = ( int )& lan9218_func;
struct mii_if_info {
int phy_id;
int advertising;
int phy_id_mask;
int reg_num_mask;
unsigned int full_duplex ; /* is full duplex? */
unsigned int force_media ; /* is autoneg. disabled? */
unsigned int supports_gmii; /* are GMII registers supported? */
struct ei_device * dev; /* 网络控制结构句柄 */
int (* mdio_read ) ( struct ei_device * ei_local, int phy_id, int location );
void (* mdio_write ) ( struct ei_device * ei_local, int phy_id, int location, int val );
};
/* 统计数据 */
struct net_device_stats
{
unsigned int rx_packets; /* total packets received */
unsigned int tx_packets; /* total packets transmitted */
unsigned int rx_bytes; /* total bytes received */
unsigned int tx_bytes; /* total bytes transmitted */
unsigned int rx_errors; /* bad packets received */
unsigned int tx_errors; /* packet transmit problems */
unsigned int rx_dropped; /* no space in linux buffers */
unsigned int tx_dropped; /* no space available in linux */
unsigned int multicast; /* multicast packets received */
unsigned int collisions;
/* detailed rx_errors: */
unsigned int rx_length_errors;
unsigned int rx_over_errors; /* receiver ring buff overflow */
unsigned int rx_crc_errors; /* recved pkt with crc error */
unsigned int rx_frame_errors; /* recv'd frame alignment error */
unsigned int rx_fifo_errors; /* recv'r fifo overrun */
unsigned int rx_missed_errors; /* receiver missed packet */
/* detailed tx_errors */
unsigned long tx_aborted_errors;
unsigned long tx_carrier_errors;
unsigned long tx_fifo_errors;
unsigned long tx_heartbeat_errors;
unsigned long tx_window_errors;
/* for cslip etc */
unsigned long rx_compressed;
unsigned long tx_compressed;
};
/* 定义芯片所必需的私有结构 */
struct smc911x_local {
/* version/revision of the SMC9218 chip */
unsigned int version;
unsigned int revision;
/* FIFO sizes */
int tx_fifo_kb;
int tx_fifo_size;
int rx_fifo_size;
int afc_cfg;
/* Contains the current active receive/phy mode */
int ctl_rfduplx;
int ctl_rspeed;
unsigned int msg_enable;
unsigned int phy_type;
/* used by mii */
struct mii_if_info mii;
int work_pending;
int tx_throttle;
struct ei_device * netdev; /* 网络控制结构句柄 */
struct net_device_stats stat; /* The new statistics table. */
};
static int tx_fifo_kb = 8; /* 8K */
static inline unsigned int mii_nway_result ( unsigned int negotiated );
unsigned int mii_check_media ( struct mii_if_info * mii,
int init_media );
/* 延时us */
void udelay( int us )
{
int i = 0, count;
/* 次数 */
count = 50000 * us;
for( i = 0; i < count; i ++);
}
/* this enables an interrupt in the interrupt mask register */
#define SMC_ENABLE_INT(x) do { \
unsigned int __mask; \
__mask = SMC_GET_INT_EN(); \
__mask |= (x); \
SMC_SET_INT_EN(__mask); \
} while (0)
/* this disables an interrupt from the interrupt mask register */
#define SMC_DISABLE_INT(x) do { \
unsigned int __mask; \
__mask = SMC_GET_INT_EN(); \
__mask &= ~(x); \
SMC_SET_INT_EN(__mask); \
} while (0)
/*
* Reads a register from the MII Management serial interface
*/
static int smc911x_phy_read( struct ei_device * ei_local, int phyaddr, int phyreg )
{
unsigned int phydata;
unsigned int ioaddr = ei_local->base;
SMC_GET_MII( phyreg, phyaddr, phydata );
return phydata;
}
/*
* Writes a register to the MII Management serial interface
*/
static void smc911x_phy_write( struct ei_device * ei_local, int phyaddr, int phyreg,
int phydata )
{
unsigned int ioaddr = ei_local->base;
SMC_SET_MII( phyreg, phyaddr, phydata );
}
/* 软件复位9218 */
static void smc911x_reset( struct ei_device * ei_local )
{
unsigned int ioaddr = ei_local->base;
struct smc911x_local * lp = ( struct smc911x_local *)ei_local->data;
unsigned int reg, timeout = 0, resets = 1;
/* Take out of PM setting first */
if (( SMC_GET_PMT_CTRL() & PMT_CTRL_READY_ ) == 0 ) {
SMC_SET_BYTE_TEST( 0 ); /* Write to the bytetest will take out of powerdown */
timeout = 10; /* 延时10次 */
do {
reg = SMC_GET_PMT_CTRL() & PMT_CTRL_READY_;
} while ( timeout -- && ! reg );
if ( timeout == 0 ) {
return;
}
}
SMC_SET_INT_EN( 0 ); /* 关闭中断 */
while ( resets --) {
SMC_SET_HW_CFG( HW_CFG_SRST_ );
timeout = 10; /* 延时10次 */
do {
reg = SMC_GET_HW_CFG();
/* If chip indicates reset timeout then try again */
if ( reg & HW_CFG_SRST_TO_ ) {
resets ++;
break;
}
} while ( timeout -- && ( reg & HW_CFG_SRST_ ));
}
if ( timeout == 0 ) { /* 超时为0,直接退出 */
return;
}
/* Initialize interrupts */
SMC_SET_INT_EN( 0 ); /* 中断关闭 */
SMC_ACK_INT( 0xffffffff ); /* 回应中断 */
/* Reset the FIFO level and flow control settings */
SMC_SET_HW_CFG(( lp->tx_fifo_kb & 0xF ) << 16 ); /* 设置txfifo */
//TODO: Figure out what appropriate pause time is
SMC_SET_FLOW( FLOW_FCPT_ | FLOW_FCEN_ );
SMC_SET_AFC_CFG( lp->afc_cfg );
/* Set to LED outputs */
SMC_SET_GPIO_CFG( 0x70070000 ); /* 输出LED */
/*
* Deassert IRQ for 1*10us for edge type interrupts
* and drive IRQ pin push-pull
*/
SMC_SET_IRQ_CFG( ( 1 << 24 ) | INT_CFG_IRQ_EN_ /*| INT_CFG_IRQ_POL_ | INT_CFG_IRQ_TYPE_ */); /* comment */
}
/*
* Finds and reports the PHY address (115 and 117 have external
* PHY interface 118 has internal only
*/
static void smc911x_phy_detect( struct ei_device * ei_local )
{
int phyaddr;
unsigned int cfg, id1, id2;
unsigned int ioaddr = ei_local->base;
struct smc911x_local * lp = ( struct smc911x_local *)ei_local->data;
lp->phy_type = 0; /* phy type */
/*
* Scan all 32 PHY addresses if necessary, starting at
* PHY#1 to PHY#31, and then PHY#0 last.
*/
switch( lp->version ) {
case 0x115: /* 版本 */
case 0x117: /* 版本 */
case 0x118a: /* 9218 */
cfg = SMC_GET_HW_CFG();
if ( cfg & HW_CFG_EXT_PHY_DET_ ) {
cfg &= ~ HW_CFG_PHY_CLK_SEL_;
cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
SMC_SET_HW_CFG( cfg );
udelay( 10 ); /* Wait for clocks to stop */
cfg |= HW_CFG_EXT_PHY_EN_;
SMC_SET_HW_CFG( cfg );
udelay( 10 ); /* Wait for clocks to stop */
cfg &= ~ HW_CFG_PHY_CLK_SEL_;
cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
SMC_SET_HW_CFG( cfg );
udelay( 10 ); /* Wait for clocks to stop */
cfg |= HW_CFG_SMI_SEL_;
SMC_SET_HW_CFG( cfg );
for ( phyaddr = 1; phyaddr < 32; ++ phyaddr ) { /* phyaddr */
/* Read the PHY identifiers */
SMC_GET_PHY_ID1( phyaddr & 31, id1 ); /* 读取PHY标示1 */
SMC_GET_PHY_ID2( phyaddr & 31, id2 ); /* 读取PHY标示2 */
/* Make sure it is a valid identifier */
if ( id1 != 0x0000 && id1 != 0xffff && /* 检查有效标示 */
id1 != 0x8000 && id2 != 0x0000 && /* 检查有效标示 */
id2 != 0xffff && id2 != 0x8000 ) { /* 检查有效标示 */
/* Save the PHY's address */
lp->mii.phy_id = phyaddr & 31; /* 保存phy_id */
lp->phy_type = id1 << 16 | id2; /* 保存phy_type */
break;
}
}
}
default:
/* Internal media only */
SMC_GET_PHY_ID1( 1, id1 ); /* 缺省设置 */
SMC_GET_PHY_ID2( 1, id2 ); /* 缺省设置 */
/* Save the PHY's address */
lp->mii.phy_id = 1; /* 缺省设置 */
lp->phy_type = id1 << 16 | id2; /* 缺省设置 */
}
}
/*
* Sets the PHY to a configuration as determined by the user.
* Called with spin_lock held.
*/
static int smc911x_phy_fixed( struct ei_device* ei_local )
{
struct smc911x_local * lp = (struct smc911x_local *)ei_local->data;
unsigned int ioaddr = ei_local->base;
int phyaddr = lp->mii.phy_id;
int bmcr;
/* Enter Link Disable state */
SMC_GET_PHY_BMCR( phyaddr, bmcr );
bmcr |= BMCR_PDOWN;
SMC_SET_PHY_BMCR( phyaddr, bmcr );
/*
* Set our fixed capabilities
* Disable auto-negotiation
*/
bmcr &= ~ BMCR_ANENABLE;
if ( lp->ctl_rfduplx )
bmcr |= BMCR_FULLDPLX;
if ( lp->ctl_rspeed == 100 )
bmcr |= BMCR_SPEED100;
/* Write our capabilities to the phy control register */
SMC_SET_PHY_BMCR( phyaddr, bmcr );
/* Re-Configure the Receive/Phy Control register */
bmcr &= ~ BMCR_PDOWN;
SMC_SET_PHY_BMCR( phyaddr, bmcr );
return 1;
}
/*
* smc911x_phy_reset - reset the phy
* @dev: net device
* @phy: phy address
*
* Issue a software reset for the specified PHY and
* wait up to 100ms for the reset to complete. We should
* not access the PHY for 50ms after issuing the reset.
*
* The time to wait appears to be dependent on the PHY.
*
*/
static int smc911x_phy_reset( struct ei_device* ei_local, int phy )
{
unsigned int ioaddr = ei_local->base;
unsigned int reg;
reg = SMC_GET_PMT_CTRL();
reg &= ~ 0xfffff030; /* 寄存器 */
reg |= PMT_CTRL_PHY_RST_;
SMC_SET_PMT_CTRL( reg );
do{
udelay( 50 ); /* 延时 */
reg = SMC_GET_PMT_CTRL();
if(!( reg & PMT_CTRL_PHY_RST_ )) {
/* extra delay required because the phy may
* not be completed with its reset
* when PHY_BCR_RESET_ is cleared. 256us
* should suffice, but use 500us to be safe
*/
udelay( 500 ); /* 每次延时500us */
break;
}
}while( 1 );
return reg & PMT_CTRL_PHY_RST_;
}
/*
* smc911x_phy_check_media - check the media status and adjust BMCR
* @dev: net device
* @init: set true for initialisation
*
* Select duplex mode depending on negotiation state. This
* also updates our carrier state.
*/
static void smc911x_phy_check_media( struct ei_device* ei_local, int init )
{
struct smc911x_local * lp = ( struct smc911x_local *)ei_local->data;
unsigned int ioaddr = ei_local->base;
int phyaddr = lp->mii.phy_id;
unsigned int bmcr, cr;
/* to do */
if ( mii_check_media(& lp->mii, init ))
{
/* duplex state has changed */
SMC_GET_PHY_BMCR( phyaddr, bmcr );
SMC_GET_MAC_CR( cr );
if ( lp->mii.full_duplex ) {
bmcr |= BMCR_FULLDPLX;
cr |= MAC_CR_RCVOWN_;
} else {
bmcr &= ~ BMCR_FULLDPLX;
cr &= ~ MAC_CR_RCVOWN_;
}
SMC_SET_PHY_BMCR( phyaddr, bmcr );
SMC_SET_MAC_CR( cr );
}
}
/*
* Configures the specified PHY through the MII management interface
* using Autonegotiation.
* Calls smc911x_phy_fixed() if the user has requested a certain config.
* If RPC ANEG bit is set, the media selection is dependent purely on
* the selection by the MII (either in the MII BMCR reg or the result
* of autonegotiation.) If the RPC ANEG bit is cleared, the selection
* is controlled by the RPC SPEED and RPC DPLX bits.
*/
static void smc911x_phy_configure( struct ei_device* ei_local )
{
struct smc911x_local * lp = ( struct smc911x_local *)ei_local->data;
unsigned int ioaddr = ei_local->base;
int phyaddr = lp->mii.phy_id;
static int my_phy_caps; /* My PHY capabilities */
int my_ad_caps; /* My Advertised capabilities */
unsigned int status ;
status = status;
/*
* We should not be called if phy_type is zero.
*/
if ( lp->phy_type == 0 ) /* 不允许出现类型为0的情况 */
goto smc911x_phy_configure_exit_nolock;
if ( smc911x_phy_reset( ei_local, phyaddr )) {
goto smc911x_phy_configure_exit_nolock;
}
/*
* Enable PHY Interrupts (for register 18)
* Interrupts listed here are enabled
*/
SMC_SET_PHY_INT_MASK( phyaddr, PHY_INT_MASK_ENERGY_ON_ |
PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ |
PHY_INT_MASK_LINK_DOWN_ );
/* If the user requested no auto neg, then go set his request */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -