⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smc9218.c

📁 一个DSP_TCPIP 协议栈网络驱动的例子
💻 C
📖 第 1 页 / 共 2 页
字号:
/**************************************************************
** 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 + -