📄 921x_4_linux.c
字号:
/***************************************************************************
*
* 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 + -