📄 dm9000.txt
字号:
/*
* 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 <weilun_huang@davicom.com.tw>:
* Fixed phy reset.
* Added tx/rx 32 bit mode.
* Cleaned up for kernel merge.
*
* 03/03/2004 Sascha Hauer <s.hauer@pengutronix.de>
* Port to 2.6 kernel
*
* 24-Sep-2004 Ben Dooks <ben@simtec.co.uk>
* Cleanup of code to remove ifdefs
* Allowed platform device data to influence access width
* Reformatting areas of code
*
* 17-Mar-2005 Sascha Hauer <s.hauer@pengutronix.de>
* * 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 <ben@simtec.co.uk>
* * fixed spinlock call without pointer
* * ensure spinlock is initialised
*
* 09-Jan-2007 Si Baozhi <sbz@spgui.org>
* * fixed resource,interrupt and some register
* * for running on S3C2440 board;
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "dm9000.h"
/* Board/System/Debug information/definition ---------------- */
#define CARDNAME "dm9000"
#define PFX CARDNAME ": "
#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
#define res_size(_r) (((_r)->end - (_r)->start) + 1)
/*
* Transmit timeout, default 5 seconds.
*/
#if 0
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
static void dmfe_packet_receive(unsigned long);
DECLARE_TASKLET(dmfe_rx_tasklet,dmfe_packet_receive,0);
#endif
/* 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 tx_pkt_cnt;
u16 queue_pkt_len;
u8 io_mode; /* 0:word, 2:byte */
struct resource *addr_res; /* resources found */
struct resource *addr_req; /* resources requested */
struct resource *irq_res;
struct timer_list timer;
struct net_device_stats stats;
spinlock_t lock;
u32 runt_length_counter; /* counter: RX length < 64byte */
u32 long_length_counter; /* counter: RX length > 1514byte */
u32 reset_counter;
u32 reset_tx_timeout; /* RESET caused by TX Timeout */
u32 reset_rx_status; /* RESET caused by RX Statsus wrong */
u8 op_mode; /* PHY operation mode */
u8 nic_type; /* NIC type */
u8 reg0, reg5, reg8, reg9, rega;/* registers saved */
u8 link_failed; /* Ever link failed */
u8 device_wait_reset; /* device state */
} 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 int dm9000_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static struct net_device_stats *dm9000_get_stats(struct net_device *);
static void dm9000_init_dm9000(struct net_device *);
static void dm9000_timer(unsigned long);
static irqreturn_t dm9000_interrupt(int, void *, struct pt_regs *);
static u16 phy_read(board_info_t *db, int reg);
static void phy_write(board_info_t *db, int reg, u16 value);
static void dm9000_rx(struct net_device *);
static void dm9000_tx_done(struct net_device *dev, board_info_t *db);
static void dm9000_hash_table(struct net_device *);
static u8 nfloor = 0;
static uint32_t polling_times = 0;
static unsigned char eth_mac_addr[6] = {0x80, 0xe2, 0x66, 0x60, 0x00, 0x01};
/* DM9000 network board routine ---------------------------- */
/**
* Read a byte from I/O port
**/
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
**/
static void iow(board_info_t * db, int reg, int value)
{
writeb(reg, db->io_addr);
writeb(value, db->io_data);
}
/**
* Read a word from phyxcer
**/
static u16 phy_read(board_info_t *db, int reg)
{
/* Fill the phyxcer register into REG_0C */
iow(db, 0xc, DM9000_PHY | reg);
iow(db, 0xb, 0xc); /* Issue phyxcer read command */
udelay(100); /* Wait read complete */
iow(db, 0xb, 0x0); /* Clear phyxcer read command */
/* The read data keeps on REG_0D & REG_0E */
return ( ior(db, 0xe) << 8 ) | ior(db, 0xd);
}
/**
* Write a word to phyxcer
**/
static void phy_write(board_info_t *db, int reg, u16 value)
{
/* Fill the phyxcer register into REG_0C */
iow(db, 0xc, DM9000_PHY | reg);
/* Fill the written data into REG_0D & REG_0E */
iow(db, 0xd, (value & 0xff));
iow(db, 0xe, ( (value >> 8) & 0xff));
iow(db, 0xb, 0xa); /* Issue phyxcer write command */
udelay(500); /* Wait write complete */
iow(db, 0xb, 0x0); /* Clear phyxcer write command */
}
/**
* Reset the chip
**/
static void dm9000_reset(board_info_t * db)
{
PRINTK1("dm9000x: resetting db->io_addr = 0x%x db->id_data = 0x%x\n",(int)db->io_addr,(int)db->io_data);
/* RESET device */
writeb(DM9000_NCR, db->io_addr);
udelay(200);
writeb(NCR_RST, db->io_data);
udelay(200);
}
/** dm9000_release_board
*
* release a board, and any mapped resources
**/
static void dm9000_release_board(struct platform_device *pdev, struct board_info *db)
{
/* unmap our resources */
iounmap(db->io_addr);
/* release the resources */
if (db->addr_req != NULL) {
release_resource(db->addr_req);
kfree(db->addr_req);
}
}
/**
* Set PHY operationg mode
**/
static void set_PHY_mode(board_info_t *db)
{
u16 phy_reg4 = 0x01e1, phy_reg0=0x1000;
if ( !(db->op_mode & DM9000_AUTO) ) {
switch(db->op_mode) {
case DM9000_10MHD: phy_reg4 = 0x21; phy_reg0 = 0x0000; break;
case DM9000_10MFD: phy_reg4 = 0x41; phy_reg0 = 0x1100; break;
case DM9000_100MHD: phy_reg4 = 0x81; phy_reg0 = 0x2000; break;
case DM9000_100MFD: phy_reg4 = 0x101;phy_reg0 = 0x3100; break;
}
phy_write(db, 4, phy_reg4); /* Set PHY media mode */
phy_write(db, 0, phy_reg0); /* Tmp */
}
iow(db, DM9000_GPCR, 0x01);/* Let GPIO0 output */
iow(db, DM9000_GPR, 0x00); /* Enable PHY */
}
/**
*Init HomeRun DM9801
**/
static void program_dm9801(board_info_t *db, u16 HPNA_rev)
{
__u16 reg16, reg17, reg24, reg25;
if ( !nfloor )
nfloor = DM9801_NOISE_FLOOR;
reg16 = phy_read(db, 16);
reg17 = phy_read(db, 17);
reg24 = phy_read(db, 24);
reg25 = phy_read(db, 25);
switch(HPNA_rev) {
case 0xb900: /* DM9801 E3 */
reg16 |= 0x1000;
reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xf000;
break;
case 0xb901: /* DM9801 E4 */
reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xc200;
reg17 = (reg17 & 0xfff0) + nfloor + 3;
break;
case 0xb902: /* DM9801 E5 */
case 0xb903: /* DM9801 E6 */
default:
reg16 |= 0x1000;
reg25 = ( (reg24 + nfloor - 3) & 0x00ff) | 0xc200;
reg17 = (reg17 & 0xfff0) + nfloor;
}
phy_write(db, 16, reg16);
phy_write(db, 17, reg17);
phy_write(db, 25, reg25);
}
/*
Init LongRun DM9802
*/
static void program_dm9802(board_info_t *db)
{
__u16 reg25;
if ( !nfloor )
nfloor = DM9802_NOISE_FLOOR;
reg25 = phy_read(db, 25);
reg25 = (reg25 & 0xff00) + nfloor;
phy_write(db, 25, reg25);
}
/* Identify NIC type
*/
static void identify_nic(board_info_t *db)
{
u16 phy_reg3;
iow(db, 0, DM9000_EXT_MII);
phy_reg3 = phy_read(db, 3);
switch(phy_reg3 & 0xfff0) {
case 0xb900:
if (phy_read(db, 31) == 0x4404) {
db->nic_type = HOMERUN_NIC;
program_dm9801(db, phy_reg3);
} else {
db->nic_type = LONGRUN_NIC;
program_dm9802(db);
}
break;
default: db->nic_type = FASTETHER_NIC;
break;
}
iow(db, 0, DM9000_INT_MII);
}
/**
* Initilize dm9000 board
**/
static void dm9000_init_dm9000(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
PRINTK1("entering %s\n",__FUNCTION__);
polling_times=0;
/* RESET device */
iow(db,DM9000_GPR,0);
udelay(100);
/* delay 100us */
iow(db,DM9000_NCR,3);
udelay(100);
iow(db,DM9000_NCR,3);
udelay(100);
/* I/O mode */
db->io_mode = ior(db,DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
if(!db->io_mode) PRINTK1(" DM9000 work in 16bit bus width\n", 0);
else if(db->io_mode==2) PRINTK1(" DM9000 work in 8bit bus width\n", 0);
else if(db->io_mode==1) PRINTK1(" DM9000 work in 32bit bus width\n", 0);
else PRINTK1("DM9000 work in wrong bus width,error\n", 0);
/* NIC Type: FASTETHER, HOMERUN, LONGRUN */
identify_nic(db);
/* Set PHY */
db->op_mode = DM9000_AUTO;
set_PHY_mode(db);
/* Init needed register value */
db->reg0 = DM9000_REG00;
if ( (db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_1M_HPNA) )
db->reg0 |= DM9000_EXT_MII;
/* User passed argument */
db->reg5 = DM9000_REG05;
db->reg8 = DM9000_REG08;
db->reg9 = DM9000_REG09;
db->rega = DM9000_REG0A;
/* Program operating register */
iow(db, DM9000_NCR, db->reg0);
iow(db, DM9000_TCR, 0); /* TX Polling clear */
iow(db, DM9000_BPTR, 0x3f); /* Less 3Kb, 200us */
iow(db, DM9000_FCTR, db->reg9); /* Flow Control : High/Low Water */
iow(db, DM9000_FCR, db->rega); /* Flow Control */
iow(db, DM9000_SMCR, 0); /* Special Mode */
iow(db, DM9000_NSR, 0x2c); /* clear TX status */
//iow(db,DM9000_ISR, 0x0f); /* Clear interrupt status */
/* Set address filter table */
dm9000_hash_table(dev);
/* Activate DM9000 */
iow(db, DM9000_RCR, 0x31); /* RX enable */
iow(db, DM9000_IMR, DM9000_REGFF); /* Enable TX/RX interrupt mask */
/* Init Driver variable */
db->link_failed = 1;
db->tx_pkt_cnt = 0;
db->queue_pkt_len = 0;
dev->trans_start = 0;
}
static void dm9000_shutdown(struct net_device *dev)
{
board_info_t *db = (board_info_t *) dev->priv;
/* RESET device */
phy_write(db, 0x00,0x8000); /* PHY RESET */
iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */
iow(db, DM9000_IMR, 0x80); /* Disable all interrupt */
iow(db, DM9000_RCR, 0x00);
}
/**
* Search DM9000 board, allocate space and register it
**/
static int
dm9000_probe(struct platform_device *pdev)
{
struct board_info *db; /* Point a board information structure */
struct net_device *ndev;
int ret = 0;
int i;
u32 id_val;
/* Init network device */
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);
/* setup board info structure */
db = (struct board_info *) ndev->priv;
memset(db, 0, sizeof (*db));
spin_lock_init(&db->lock);
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (db->addr_res == NULL || db->irq_res == NULL) {
printk(KERN_ERR PFX "insufficient resources\n");
ret = -ENOENT; goto release;
}
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 release;
}
db->io_addr = ioremap(db->addr_res->start, 0x400);
if (db->io_addr == NULL) {
printk(KERN_ERR "failed to ioremap address reg\n");
ret = -EINVAL; goto release;
}
db->io_data =( void __iomem *) ((u32)db->io_addr + 4);
ndev->base_addr = (unsigned long)db->io_addr;
ndev->irq = db->irq_res->start;
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);
}
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 */
ether_setup(ndev);
ndev->open = &dm9000_open;
ndev->hard_start_xmit = &dm9000_start_xmit;
ndev->stop = &dm9000_stop;
ndev->get_stats = &dm9000_get_stats;
ndev->set_multicast_list = &dm9000_hash_table;
ndev->do_ioctl = &dm9000_do_ioctl;
#if 0
ndev->tx_timeout = &dm9000_timeout;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
#endif
/* set MAC */
for (i=0; i<6; i++) {
ndev->dev_addr[i] = eth_mac_addr[i];
}
/* Request IO from system */
iow(db,0x1d,0x80);
/* request_region(base, 2, ndev->name); */
platform_set_drvdata(pdev, ndev);
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]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -