/* * dm9000.c: Version 1.2 03/18/2003 * * A Davicom DM9000 ISA NIC fast Ethernet driver for Linux. * Copyright (C) 1997 Sten Wang * * 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. * * (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. * * V0.11 06/20/2001 REG_0A bit3=1, default enable BP with DA match * 06/22/2001 Support DM9801 progrmming * E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000 * E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200 * R17 = (R17 & 0xfff0) | NF + 3 * E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200 * R17 = (R17 & 0xfff0) | NF * * v1.00 modify by simon 2001.9.5 * change for kernel 2.4.x * * v1.1 11/09/2001 fix force mode bug * * v1.2 03/18/2003 Weilun Huang : * Fixed phy reset. * Added tx/rx 32 bit mode. * Cleaned up for kernel merge. * * 03/03/2004 Sascha Hauer * Port to 2.6 kernel * * 24-Sep-2004 Ben Dooks * Cleanup of code to remove ifdefs * Allowed platform device data to influence access width * Reformatting areas of code * * 17-Mar-2005 Sascha Hauer * * removed 2.4 style module parameters * * removed removed unused stat counter and fixed * net_device_stats * * introduced tx_timeout function * * reworked locking * * 01-Jul-2005 Ben Dooks * * fixed spinlock call without pointer * * ensure spinlock is initialised */ #i nclude <linux/module.h> #i nclude <linux/ioport.h> #i nclude <linux/netdevice.h> #i nclude <linux/etherdevice.h> #i nclude <linux/init.h> #i nclude <linux/skbuff.h> #i nclude <linux/spinlock.h> #i nclude <linux/crc32.h> #i nclude <linux/mii.h> #i nclude <linux/dm9000.h> #i nclude <linux/delay.h> #i nclude <linux/platform_device.h>
#i nclude <asm/delay.h> #i nclude <asm/irq.h> #i nclude <asm/io.h>
#i nclude "dm9000.h"
/* Board/System/Debug information/definition ---------------- */
#define DM9000_PHY 0x40 /* PHY address 0x01 */
#define CARDNAME "dm9000" #define PFX CARDNAME ": "
#define DM9000_TIMER_WUT jiffies+(HZ*2) /* timer wakeup time : 2 second */
#define DM9000_DEBUG 0
#if DM9000_DEBUG > 2 #define PRINTK3(args...) printk(CARDNAME ": " args) #else #define PRINTK3(args...) do { } while(0) #endif
#if DM9000_DEBUG > 1 #define PRINTK2(args...) printk(CARDNAME ": " args) #else #define PRINTK2(args...) do { } while(0) #endif
#if DM9000_DEBUG > 0 #define PRINTK1(args...) printk(CARDNAME ": " args) #define PRINTK(args...) printk(CARDNAME ": " args) #else #define PRINTK1(args...) do { } while(0) #define PRINTK(args...) printk(KERN_DEBUG args) #endif
/* * Transmit timeout, default 5 seconds. */ static int watchdog = 5000; module_param(watchdog, int, 0400); MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
/*DM9000 含有16K SRAM作为FIFO buffer,3K用于transmitt,13k用于receive*/
/* Structure/enum declaration ------------------------------- */ typedef struct board_info { /*命令地址寄存器地址*/ void __iomem *io_addr; /* Register I/O base address */ /*数据寄存器地址*/ void __iomem *io_data; /* Data I/O address */ u16 irq; /* IRQ */
u16 tx_pkt_cnt; u16 queue_pkt_len; u16 queue_start_addr; u16 dbug_cnt; u8 io_mode; /* 0:word, 2:byte */ u8 phy_addr;
void (*inblk)(void __iomem *port, void *data, int length); void (*outblk)(void __iomem *port, void *data, int length); void (*dumpblk)(void __iomem *port, int length);
struct resource *addr_res; /* resources found */ struct resource *data_res; struct resource *addr_req; /* resources requested */ struct resource *data_req; struct resource *irq_res;
struct timer_list timer; struct net_device_stats stats; unsigned char srom[128]; /*网卡上EERPOM的内容*/ spinlock_t lock;
struct mii_if_info mii; /*调试可能会用到*/ u32 msg_enable; } board_info_t;
/* function declaration ------------------------------------- */ static int dm9000_probe(struct platform_device *); static int dm9000_open(struct net_device *); static int dm9000_start_xmit(struct sk_buff *, struct net_device *); static int dm9000_stop(struct net_device *);
static void dm9000_timer(unsigned long); static void dm9000_init_dm9000(struct net_device *);
static struct net_device_stats *dm9000_get_stats(struct net_device *);
static irqreturn_t dm9000_interrupt(int, void *);
static int dm9000_phy_read(struct net_device *dev, int phyaddr_unsused, int reg); static void dm9000_phy_write(struct net_device *dev, int phyaddr_unused, int reg, int value); static u16 read_srom_word(board_info_t *, int); static void dm9000_rx(struct net_device *); static void dm9000_hash_table(struct net_device *);
//#define DM9000_PROGRAM_EEPROM
#ifdef DM9000_PROGRAM_EEPROM static void program_eeprom(board_info_t * db); #endif /* DM9000 network board routine ---------------------------- */
static void dm9000_reset(board_info_t * db) { PRINTK1("dm9000x: resetting\n"); /* RESET device */ /*指定DM9000当前的命令寄存器是NCR*/ writeb(DM9000_NCR, db->io_addr); udelay(200); /*向NCR寄存器写入复位标志,芯片复位*/ writeb(NCR_RST, db->io_data); udelay(200); }
/* * Read a byte from I/O port 读取寄存器的值,reg表明寄存器的偏移量 这里用宏名表示 */
static u8 ior(board_info_t * db, int reg) { writeb(reg, db->io_addr); return readb(db->io_data); }
/* * Write a byte to I/O port 写寄存器的值,reg表明寄存器的偏移量 这里用宏名表示,value是写入数据 */
static void iow(board_info_t * db, int reg, int value) { writeb(reg, db->io_addr); writeb(value, db->io_data); }
/* routines for sending block to chip */ /*类似于内存拷贝 简单的理解为memcpy(reg,data,count) count是以字节为单位的! */ static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count) { writesb(reg, data, count); }
static void dm9000_outblk_16bit(void __iomem *reg, void *data, int count) { writesw(reg, data, (count+1) >> 1); }
static void dm9000_outblk_32bit(void __iomem *reg, void *data, int count) { writesl(reg, data, (count+3) >> 2); }
/* input block from chip to memory */ /*类似于内存拷贝 简单的理解为memcpy(data,reg,count) count是以字节为单位的! */ static void dm9000_inblk_8bit(void __iomem *reg, void *data, int count) { readsb(reg, data, count); }
static void dm9000_inblk_16bit(void __iomem *reg, void *data, int count) { readsw(reg, data, (count+1) >> 1); }
static void dm9000_inblk_32bit(void __iomem *reg, void *data, int count) { readsl(reg, data, (count+3) >> 2); }
/* dump block from chip to null */ /*类似于内存清理,将reg指向的count清空。 多用于设备的接收缓存区清空 count是以字节为单位的! */ static void dm9000_dumpblk_8bit(void __iomem *reg, int count) { int i; int tmp;
for (i = 0; i < count; i++) tmp = readb(reg); }
static void dm9000_dumpblk_16bit(void __iomem *reg, int count) { int i; int tmp;
count = (count + 1) >> 1;
for (i = 0; i < count; i++) tmp = readw(reg); }
static void dm9000_dumpblk_32bit(void __iomem *reg, int count) { int i; int tmp;
count = (count + 3) >> 2;
for (i = 0; i < count; i++) tmp = readl(reg); }
/* dm9000_set_io * * select the specified set of io routines to use with the * device 根据芯片的外部总线带宽,指定对应的输入输出函数 */
static void dm9000_set_io(struct board_info *db, int byte_width) { /* use the size of the data resource to work out what IO * routines we want to use */
switch (byte_width) { case 1: db->dumpblk = dm9000_dumpblk_8bit; db->outblk = dm9000_outblk_8bit; db->inblk = dm9000_inblk_8bit; break;
case 2: db->dumpblk = dm9000_dumpblk_16bit; db->outblk = dm9000_outblk_16bit; db->inblk = dm9000_inblk_16bit; break;
case 3: printk(KERN_ERR PFX ": 3 byte IO, falling back to 16bit\n"); db->dumpblk = dm9000_dumpblk_16bit; db->outblk = dm9000_outblk_16bit; db->inblk = dm9000_inblk_16bit; break;
case 4: default: db->dumpblk = dm9000_dumpblk_32bit; db->outblk = dm9000_outblk_32bit; db->inblk = dm9000_inblk_32bit; break; } }
/*超时处理函数*/ /* Our watchdog timed out. Called by the networking layer */ static void dm9000_timeout(struct net_device *dev) { board_info_t *db = (board_info_t *) dev->priv; u8 reg_save; unsigned long flags;
/* Save previous register address */ reg_save = readb(db->io_addr); spin_lock_irqsave(&db->lock,flags); /*停止发送报文 */ netif_stop_queue(dev); /*复位DM9000芯片*/ dm9000_reset(db); /*初始化DM9000芯片*/ dm9000_init_dm9000(dev); /* We can accept TX packets again */ /*重启发送*/ dev->trans_start = jiffies; netif_wake_queue(dev);
/* Restore previous register address */ /*io_addr的恢复,保证了芯片的配置和初始值一致*/ writeb(reg_save, db->io_addr); spin_unlock_irqrestore(&db->lock,flags); }
#ifdef CONFIG_NET_POLL_CONTROLLER /* *Used by netconsole */ static void dm9000_poll_controller(struct net_device *dev) { disable_irq(dev->irq); dm9000_interrupt(dev->irq,dev); enable_irq(dev->irq); } #endif
/* dm9000_release_board * * release a board, and any mapped resources 释放资源 */
static void dm9000_release_board(struct platform_device *pdev, struct board_info *db) { if (db->data_res == NULL) { if (db->addr_res != NULL) release_mem_region((unsigned long)db->io_addr, 4); return; }
/* unmap our resources */
iounmap(db->io_addr); iounmap(db->io_data);
/* release the resources */
if (db->data_req != NULL) { release_resource(db->data_req); kfree(db->data_req); }
if (db->addr_req != NULL) { release_resource(db->addr_req); kfree(db->addr_req); } }
#define res_size(_r) (((_r)->end - (_r)->start) + 1)
/* * Search DM9000 board, allocate space and register it */ static int dm9000_probe(struct platform_device *pdev) { struct dm9000_plat_data *pdata = pdev->dev.platform_data; struct board_info *db; /* Point a board information structure */ struct net_device *ndev; unsigned long base; int ret = 0; int iosize; int i; u32 id_val;
/* 分配eth网卡资源,私有数据区保存board_info*/ ndev = alloc_etherdev(sizeof (struct board_info)); if (!ndev) { printk("%s: could not allocate device.\n", CARDNAME); return -ENOMEM; } /*忽略*/ SET_MODULE_OWNER(ndev); SET_NETDEV_DEV(ndev, &pdev->dev);
PRINTK2("dm9000_probe()");
/* setup board info structure bord info初始化为0 */ db = (struct board_info *) ndev->priv; memset(db, 0, sizeof (*db)); /*初始化spinlock*/ spin_lock_init(&db->lock);
/*忽略*/ if (pdev->num_resources < 2) { ret = -ENODEV; goto out; } else if (pdev->num_resources == 2) { base = pdev->resource[0].start;
if (!request_mem_region(base, 4, ndev->name)) { ret = -EBUSY; goto out; }
ndev->base_addr = base; ndev->irq = pdev->resource[1].start; db->io_addr = (void __iomem *)base; db->io_data = (void __iomem *)(base + 4);
} else { db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (db->addr_res == NULL || db->data_res == NULL || db->irq_res == NULL) { printk(KERN_ERR PFX "insufficient resources\n"); ret = -ENOENT; goto out; }
i = res_size(db->addr_res); db->addr_req = request_mem_region(db->addr_res->start, i, pdev->name);
if (db->addr_req == NULL) { printk(KERN_ERR PFX "cannot claim address reg area\n"); ret = -EIO; goto out; }
db->io_addr = ioremap(db->addr_res->start, i);
if (db->io_addr == NULL) { printk(KERN_ERR "failed to ioremap address reg\n"); ret = -EINVAL; goto out; }
iosize = res_size(db->data_res); db->data_req = request_mem_region(db->data_res->start, iosize, pdev->name);
if (db->data_req == NULL) { printk(KERN_ERR PFX "cannot claim data reg area\n"); ret = -EIO; goto out; }
db->io_data = ioremap(db->data_res->start, iosize);
if (db->io_data == NULL) { printk(KERN_ERR "failed to ioremap data reg\n"); ret = -EINVAL; goto out; }
/* fill in parameters for net-dev structure */
ndev->base_addr = (unsigned long)db->io_addr; ndev->irq = db->irq_res->start;
/* ensure at least we have a default set of IO routines */ dm9000_set_io(db, iosize); }
/* check to see if anything is being over-ridden */ if (pdata != NULL) { /* check to see if the driver wants to over-ride the * default IO width */
if (pdata->flags & DM9000_PLATF_8BITONLY) dm9000_set_io(db, 1);
if (pdata->flags & DM9000_PLATF_16BITONLY) dm9000_set_io(db, 2);
if (pdata->flags & DM9000_PLATF_32BITONLY) dm9000_set_io(db, 4);
/* check to see if there are any IO routine * over-rides */
if (pdata->inblk != NULL) db->inblk = pdata->inblk;
if (pdata->outblk != NULL) db->outblk = pdata->outblk;
if (pdata->dumpblk != NULL) db->dumpblk = pdata->dumpblk; } /*根据board info信息,复位DM9000芯片*/ dm9000_reset(db);
/* try two times, DM9000 sometimes gets the first read wrong */ for (i = 0; i < 2; i++) { id_val = ior(db, DM9000_VIDL); id_val |= (u32)ior(db, DM9000_VIDH) << 8; id_val |= (u32)ior(db, DM9000_PIDL) << 16; id_val |= (u32)ior(db, DM9000_PIDH) << 24;
if (id_val == DM9000_ID) break; printk("%s: read wrong id 0x%08x\n", CARDNAME, id_val); } /*芯片的ID获取失败,驱动不匹配*/ if (id_val != DM9000_ID) { printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val); goto release; }
/* from this point we assume that we have found a DM9000 */
/* driver system function 将DM9000作为以太网卡初始化内部数据结构*/ ether_setup(ndev);
ndev->open = &dm9000_open; ndev->hard_start_xmit = &dm9000_start_xmit; ndev->tx_timeout = &dm9000_timeout; ndev->watchdog_timeo = msecs_to_jiffies(watchdog); /*超时值*/ ndev->stop = &dm9000_stop; ndev->get_stats = &dm9000_get_stats; ndev->set_multicast_list = &dm9000_hash_table; #ifdef CONFIG_NET_POLL_CONTROLLER ndev->poll_controller = &dm9000_poll_controller; #endif
#ifdef DM9000_PROGRAM_EEPROM /*开发时,需要更新EERPOM的值*/ program_eeprom(db); #endif /*忽略*/ db->msg_enable = NETIF_MSG_LINK; db->mii.phy_id_mask = 0x1f; db->mii.reg_num_mask = 0x1f; db->mii.force_media = 0; db->mii.full_duplex = 0; db->mii.dev = ndev; db->mii.mdio_read = dm9000_phy_read; /*DM9000可以提供MII,外接PHY*/ db->mii.mdio_write = dm9000_phy_write;
/* 获取网卡上EEPROM9346的内容,走EERPOM接口 */ for (i = 0; i < 64; i++) /*采用16bit读取方式*/ ((u16 *) db->srom)[i] = read_srom_word(db, i);
/* Set Node Address EEPROM的前6个字节为MAC地址 */ for (i = 0; i < 6; i++) ndev->dev_addr[i] = db->srom[i]; /*验证获取的MAC地址是否合法*/ if (!is_valid_ether_addr(ndev->dev_addr)) { /* try reading from mac */ /*不合法,重新从DM9000的内部寄存器获取MAC,不走EEPROM接口*/ for (i = 0; i < 6; i++) ndev->dev_addr[i] = ior(db, i+DM9000_PAR); } /*两种方式都失败,提示用ifconfig,驱动没有对应接口!*/ if (!is_valid_ether_addr(ndev->dev_addr)) printk("%s: Invalid ethernet MAC address. Please " "set using ifconfig\n", ndev->name);
platform_set_drvdata(pdev, ndev); /*注册接口到系统中,状态默认down,不可用*/ ret = register_netdev(ndev);
if (ret == 0) { printk("%s: dm9000 at %p,%p IRQ %d MAC: ", ndev->name, db->io_addr, db->io_data, ndev->irq); for (i = 0; i < 5; i++) printk("%02x:", ndev->dev_addr[i]); printk("%02x\n", ndev->dev_addr[5]); } return 0;
release: out: printk("%s: not found (%d).\n", CARDNAME, ret); /失败时,释放资源*/ dm9000_release_board(pdev, db); free_netdev(ndev);
return ret; }
/* * Open the interface. * The interface is opened whenever "ifconfig" actives it. 当使用ifconfig激活该网络接口时调用 */ static int dm9000_open(struct net_device *dev) { board_info_t *db = (board_info_t *) dev->priv;
PRINTK2("entering dm9000_open\n"); /*申请中断资源,注册中断函数*/ if (request_irq(dev->irq, &dm9000_interrupt, IRQF_SHARED, dev->name, dev)) return -EAGAIN;
/* Initialize DM9000 board */ /*复位DM9000芯片*/ dm9000_reset(db); /*配置DM9000芯片内寄存器,使其能工作*/ dm9000_init_dm9000(dev);
/* Init driver variable */ db->dbug_cnt = 0;
/* set and active a timer process */ init_timer(&db->timer); /*初始化内核定时器*/ db->timer.expires = DM9000_TIMER_WUT; /*2s唤醒一次*/ db->timer.data = (unsigned long) dev; db->timer.function = &dm9000_timer; /*内核定时器溢出回调函数*/ add_timer(&db->timer); /*启动内核定时器*/ /*检查mii接口状态*/ mii_check_media(&db->mii, netif_msg_link(db), 1); /*启动发送队列*/ netif_start_queue(dev);
return 0; }
|