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

📄 921x_4_linux.c

📁 ethernet device driver for smsc9115
💻 C
📖 第 1 页 / 共 5 页
字号:
/***************************************************************************
 *
 * Copyright (C) 2004-2006 SMSC
 * Copyright (C) 2005-2006 ARM
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ***************************************************************************
 * Rewritten, heavily based on smsc911x simple driver by SMSC.
 * Partly uses io macros from smc91x.c by Nicolas Pitre
 *
 * Supported devices:
 *   LAN9115, LAN9116, LAN9117, LAN9118
 *   LAN9215, LAN9216, LAN9217, LAN9218
 *
 * History:
 *   05/05/2005 bahadir.balban <at> arm.com
 *     - Transition to linux coding style
 *     - Platform driver and module interface
 *
 *   17/07/2006 steve.glendinning <at> smsc.com
 *     - Added support for LAN921x family
 *     - Added workaround for multicast filters
 *
 *   31/07/2006 steve.glendinning <at> smsc.com
 *     - Removed tasklet, using NAPI poll instead
 *     - Multiple device support
 *     - Large tidy-up following feedback from netdev list
 *
 *   03/08/2006 steve.glendinning <at> smsc.com
 *     - Added ethtool support
 *     - Convert to use generic MII interface
 *
 *   04/08/2006 bahadir.balban <at> arm.com
 *     - Added ethtool eeprom r/w support
 *
 *   30/12/2006 steve.glendinning <at> smsc.com
 *     - Fix for external PHY initialisation
 *
 *   26/03/2007 kf701.ye <at> gmail.com
 *     - Modify it for kernel 2.4.20
 */

#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/module.h>
//#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/version.h>
//#include <asm/bug.h>
#include <asm/bitops.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "smsc911x.h"

#define SMSC_CHIPNAME        "smsc911x"
#define SMSC_DRV_VERSION    "2007-03-26"

MODULE_LICENSE("GPL");

struct net_device *gl_dev;

/* define some function self,this function appear in 2.6 */
struct smsc911x_data *netdev_priv(struct net_device *dev)
{
    return dev->priv;
}

static inline void random_ether_addr(u8 *addr)
{ 
    addr [0] &= 0xfe;       /* clear multicast bit */
    addr [0] |= 0x02;       /* set local assignment bit (IEEE802) */
    addr [1] = 0x01;
    addr [2] = 0x02;
    addr [3] = 0x03;
    addr [4] = 0x04;
    addr [5] = 0x05;
} 
/*
void msleep(unsigned int msecs)
{
    unsigned long timeout = msecs_to_jiffies(msecs) + 1;
    while (timeout)
        timeout = schedule_timeout_uninterruptible(timeout);
}
*/
static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
{
    return (struct mii_ioctl_data *) &rq->ifr_ifru;
}
/* End, copy from 2.6 kernel */

#if SMSC_CAN_USE_32BIT

static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
{
    return readl(pdata->ioaddr + reg);
}

static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
                      u32 reg)
{
    writel(val, pdata->ioaddr + reg);
}

#else                /* SMSC_CAN_USE_32BIT */

static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
{
    u32 reg_val;
    unsigned long flags;

    /* these two 16-bit reads must be performed consecutively, so must
     * not be interrupted by our own ISR (which would start another
     * read operation) */
    local_irq_save(flags);
    reg_val = ((readw(pdata->ioaddr + reg) & 0xFFFF) |
           ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
    local_irq_restore(flags);

    return reg_val;
}

static inline void smsc911x_reg_write(u32 val, struct smsc911x_data *pdata,
                      u32 reg)
{
    unsigned long flags;

    /* these two 16-bit writes must be performed consecutively, so must
     * not be interrupted by our own ISR (which would start another
     * read operation) */
    local_irq_save(flags);
    writew(val & 0xFFFF, pdata->ioaddr + reg);
    writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
    local_irq_restore(flags);
}

#endif                /* SMSC_CAN_USE_32BIT */

/* Writes a packet to the TX_DATA_FIFO */
static inline void
smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
              unsigned int wordcount)
{
    while (wordcount--)
        smsc911x_reg_write(*buf++, pdata, TX_DATA_FIFO);
}

/* Reads a packet out of the RX_DATA_FIFO */
static inline void
smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
             unsigned int wordcount)
{
    while (wordcount--)
        *buf++ = smsc911x_reg_read(pdata, RX_DATA_FIFO);
}

/* waits for MAC not busy, with timeout.  Only called by smsc911x_mac_read
 * and smsc911x_mac_write, so assumes phy_lock is held */
static int smsc911x_mac_notbusy(struct smsc911x_data *pdata)
{
    int i;
    u32 val;

    for (i = 0; i < 40; i++) {
        val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
        if (!(val & MAC_CSR_CMD_CSR_BUSY_))
            return 1;
    }
    SMSC_WARNING("Timed out waiting for MAC not BUSY. "
             "MAC_CSR_CMD: 0x%08X", val);
    return 0;
}

/* Fetches a MAC register value. Assumes phy_lock is acquired */
static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
{
    unsigned int temp;

#ifdef CONFIG_DEBUG_SPINLOCK
    if (!spin_is_locked(&pdata->phy_lock))
        SMSC_WARNING("phy_lock not held");
#endif                /* CONFIG_DEBUG_SPINLOCK */

    temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
    if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
        SMSC_WARNING("smsc911x_mac_read failed, MAC busy at entry");
        return 0xFFFFFFFF;
    }

    /* Send the MAC cmd */
    smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_
                | MAC_CSR_CMD_R_NOT_W_), pdata, MAC_CSR_CMD);

    /* Workaround for hardware read-after-write restriction */
    temp = smsc911x_reg_read(pdata, BYTE_TEST);

    /* Wait for the read to happen */
    if (likely(smsc911x_mac_notbusy(pdata)))
        return smsc911x_reg_read(pdata, MAC_CSR_DATA);

    SMSC_WARNING("smsc911x_mac_read failed, MAC busy after read");
    return 0xFFFFFFFF;
}

/* Set a mac register, phy_lock must be acquired before calling */
static void smsc911x_mac_write(struct smsc911x_data *pdata,
                   unsigned int offset, u32 val)
{
    unsigned int temp;

#ifdef CONFIG_DEBUG_SPINLOCK
    if (!spin_is_locked(&pdata->phy_lock))
        SMSC_WARNING("phy_lock not held");
#endif                /* CONFIG_DEBUG_SPINLOCK */

    temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
    if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
        SMSC_WARNING("smsc911x_mac_write failed, MAC busy at entry");
        return;
    }

    /* Send data to write */
    smsc911x_reg_write(val, pdata, MAC_CSR_DATA);

    /* Write the actual data */
    smsc911x_reg_write(((offset & 0xFF) | MAC_CSR_CMD_CSR_BUSY_), pdata,
               MAC_CSR_CMD);

    /* Workaround for hardware read-after-write restriction */
    temp = smsc911x_reg_read(pdata, BYTE_TEST);

    /* Wait for the write to complete */
    if (likely(smsc911x_mac_notbusy(pdata)))
        return;

    SMSC_WARNING("smsc911x_mac_write failed, MAC busy after write");
}

/* Gets a phy register, phy_lock must be acquired before calling */
static u16 smsc911x_phy_read(struct smsc911x_data *pdata, unsigned int index)
{
    unsigned int addr;
    int i;

#ifdef CONFIG_DEBUG_SPINLOCK
    if (!spin_is_locked(&pdata->phy_lock))
        SMSC_WARNING("phy_lock not held");
#endif                /* CONFIG_DEBUG_SPINLOCK */

    /* Confirm MII not busy */
    if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
        SMSC_WARNING("MII is busy in smsc911x_phy_read???");
        return 0;
    }

    /* Set the address, index & direction (read from PHY) */
    addr = (((pdata->mii.phy_id) & 0x1F) << 11)
        | ((index & 0x1F) << 6);
    smsc911x_mac_write(pdata, MII_ACC, addr);

    /* Wait for read to complete w/ timeout */
    for (i = 0; i < 100; i++) {
        /* See if MII is finished yet */
        if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
            return smsc911x_mac_read(pdata, MII_DATA);
        }
    }
    SMSC_WARNING("Timed out waiting for MII write to finish");
    return 0xFFFF;
}

/* Sets a phy register, phy_lock must be acquired before calling */
static void smsc911x_phy_write(struct smsc911x_data *pdata,
                   unsigned int index, u16 val)
{
    unsigned int addr;
    int i;

#ifdef CONFIG_DEBUG_SPINLOCK
    if (!spin_is_locked(&pdata->phy_lock))
        SMSC_WARNING("phy_lock not held");
#endif                /* CONFIG_DEBUG_SPINLOCK */

    /* Confirm MII not busy */
    if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
        SMSC_WARNING("MII is busy in smsc911x_write_phy???");
        return;
    }

    /* Put the data to write in the MAC */
    smsc911x_mac_write(pdata, MII_DATA, val);

    /* Set the address, index & direction (write to PHY) */
    addr = (((pdata->mii.phy_id) & 0x1F) << 11) |
        ((index & 0x1F) << 6) | MII_ACC_MII_WRITE_;
    smsc911x_mac_write(pdata, MII_ACC, addr);

    /* Wait for write to complete w/ timeout */
    for (i = 0; i < 100; i++) {
        /* See if MII is finished yet */
        if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_))
            return;
    }
    SMSC_WARNING("Timed out waiting for MII write to finish");
}

static int smsc911x_mdio_read(struct net_device *dev, int phy_id, int location)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned long flags;
    int reg;

    spin_lock_irqsave(&pdata->phy_lock, flags);
    reg = smsc911x_phy_read(pdata, location);
    spin_unlock_irqrestore(&pdata->phy_lock, flags);

    return reg;
}

static void smsc911x_mdio_write(struct net_device *dev, int phy_id,
                int location, int val)
{
    struct smsc911x_data *pdata = netdev_priv(dev);
    unsigned long flags;

    spin_lock_irqsave(&pdata->phy_lock, flags);
    smsc911x_phy_write(pdata, location, val);
    spin_unlock_irqrestore(&pdata->phy_lock, flags);
}

.......中间没有改动

/* Increments the Rx error counters */
static void
smsc911x_rx_counterrors(struct smsc911x_data *pdata, unsigned int rxstat)
{
    int crc_err = 0;

    if (unlikely(rxstat & 0x00008000)) {
        pdata->stats.rx_errors++;
        if (unlikely(rxstat & 0x00000002)) {
            pdata->stats.rx_crc_errors++;
            crc_err = 1;
        }
    }
    if (likely(!crc_err)) {
        if (unlikely((rxstat & 0x00001020) == 0x00001020)) {
            /* Frame type indicates length,
             * and length error is set */
            pdata->stats.rx_length_errors++;
        }
        if (rxstat & RX_STS_MCAST_)
            pdata->stats.multicast++;
    }
}

/* Quickly dumps bad packets */
static void
smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int count)
{
    unsigned int temp;

    if (likely(count >= 4)) {
        unsigned int timeout = 500;
        unsigned int val;
        smsc911x_reg_write(RX_DP_CTRL_RX_FFWD_, pdata, RX_DP_CTRL);
        do {
            udelay(1);
            val = smsc911x_reg_read(pdata, RX_DP_CTRL);
        } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_));

        if (unlikely(timeout == 0))
            SMSC_WARNING("Timed out waiting for RX FFWD "
                     "to finish, RX_DP_CTRL: 0x%08X", val);
    } else {
        while (count--)
            temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -