www.pudn.com > DM9000.rar > dm9000.c
/****************************************Copyright (c)**************************************************
** Guangzou ZLG-MCU Development Co.,LTD.
** graduate school
** http://www.zlgmcu.com
**
**--------------File Info-------------------------------------------------------------------------------
** File name: rtl8019.c
** Last modified Date: 2005-05-12
** Last Version: 1.0
** Descriptions: This is a Kernel module for uClinux 2.4.x .
** This module let uClinux 2.4.x can use rtl8019.
**------------------------------------------------------------------------------------------------------
** Created by: Chenmingji
** Created date: 2005-05-12
** Version: 1.0
** Descriptions: The original version
**
**------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
** Version:
** Descriptions:
**
********************************************************************************************************/
#define IN_DM9000
#include "config.h"
#include "dm9000.h"
/********************************************************************************************************
function announce
********************************************************************************************************/
static int net_init(struct net_device *dev);
static int net_open(struct net_device *dev);
static int net_release(struct net_device *dev);
static int net_config(struct net_device *dev, struct ifmap *map);
static int net_tx(struct sk_buff *skb, struct net_device *dev);
static void net_irq_handle(int irq, void *dev_id, struct pt_regs *regs);
static int net_set_mac_address(struct net_device *dev, void *addr);
int net_init_module(void);
void net_cleanup(void);
static void net_tasklet(unsigned long);
/********************************************************************************************************
define announce
********************************************************************************************************/
static struct net_device *DM9000Dev;
module_init(net_init_module);
module_exit(net_cleanup);
DECLARE_TASKLET(ZLG_net_tasklet, net_tasklet, (unsigned long)(&DM9000Dev));
MODULE_LICENSE("Proprietary");
MODULE_DESCRIPTION("Guangzou ZLG-MCU Development Co.,LTD.\ngraduate school\nhttp://www.zlgmcu.com");
MODULE_SUPPORTED_DEVICE("uClinux2.4.x LPC2200 DM9000");
MODULE_AUTHOR("chenmingji");
/*********************************************************************************************************
** "全局和静态变量在这里定义"
** global variables and static variables define here
********************************************************************************************************/
static unsigned int usage;
static u32 PinSel0Save;
static board_info_t NetDriverInfo[1];
/********************************************************************************************************/
static struct net_device net_net =
{
name: "eth0",
init: net_init,
base_addr: IOaddress0,
irq: NET_IRQ,
watchdog_timeo: NET_TIMEOUT,
};
/*********************************************************************************************************
** Function name: ior
**
** Descriptions: read a reg form dm9000
**
** input parameters: board_info_t *db ;dm9000 status struct
** reg ;reg address of dm9000
** Returned value: uint8 ;reg value
**
** Used global variables: None
** Calling modules: None
**
** Created by: Yehaoben
** Created Date: 2004/02/02
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static uint8 ior(board_info_t *db, uint8 reg)
{
unsigned int i;
unsigned int rt;
outb(reg, db->ioaddr);
for (i = 0; i < BUS_DELAY; i++)
{
}
rt = inb(db->io_data);
for (i = 0; i < BUS_DELAY; i++)
{
}
return rt;
}
/*********************************************************************************************************
** Function name: iow
**
** Descriptions: write a value to a dm9000 reg
**
** input parameters: board_info_t *db ;dm9000 status struct
** reg ;reg address of dm9000
** value ;reg value
** Returned value: None
**
** Used global variables: None
** Calling modules: None
**
** Created by: Yehaoben
** Created Date: 2004/02/02
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static void iow(board_info_t *db, uint8 reg, uint8 value)
{
unsigned int i;
outb(reg, db->ioaddr);
for(i = 0; iio_data);
for(i = 0; i> 8) & 0xff));
iow(db, 0xb, 0xa); /* Issue phyxcer write command */
udelay(500); /* Wait write complete */
iow(db, 0xb, 0x0); /* Clear phyxcer write command */
}
/*********************************************************************************************************
** Function name: set_PHY_mode
**
** Descriptions: set the PHY device working
**
** input parameters: board_info_t *db ;dm9000 status struct
**
** Returned value: None
**
** Used global variables: None
** Calling modules: None
**
** Created by: Yehaoben
** Created Date: 2004/02/02
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static void set_PHY_mode(board_info_t *db)
{
unsigned int phy_reg4 = 0x01e1, phy_reg0=0x1000;
if ( !(db->op_mode & DM9000_AUTO))
{
switch(db->op_mode)
{
case DM9000_10MHD:
phy_reg4 = 0x21; /* 10M HALF DUPLEX*/
phy_reg0 = 0x0000;
break;
case DM9000_10MFD:
phy_reg4 = 0x41; /* 10M FULL DUPLEX*/
phy_reg0 = 0x1100;
break;
case DM9000_100MHD:
phy_reg4 = 0x81; /* 100M HALF DUPLEX*/
phy_reg0 = 0x2000;
break;
case DM9000_100MFD:
phy_reg4 = 0x101;/* 100M HALF DUPLEX */
phy_reg0 =0x3100;
break;
default:
break;
}
phy_write(db, 4, phy_reg4); /* Set PHY media mode */
phy_write(db, 0, phy_reg0); /* Tmp */
}
iow(db, 0x1e, 0x01); /* Let GPIO0 output */
iow(db, 0x1f, 0x00); /* Enable PHY */
}
/*********************************************************************************************************
** Function name: device_init
** Descriptions: device init
** Input: dev: information of device
**
** Output: 0: OK
** other: errno
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int device_init(struct net_device *dev)
{
unsigned long flag;
uint8 i;
board_info_t *db = dev->priv;
local_irq_save(flag);
/* 硬件复位复位 */
Hardware_Initial0();
db->ioaddr = IOaddress0;
db->io_data = IOaddress0 + 4;
/* RESET device */
/*Hardware Reset*/
Hardware_Reset_Clr0();
udelay(500);
Hardware_Reset_Set0();
udelay(500);
Hardware_Reset_Clr0();
udelay(500);
/*software reset*/
iow(db, 0, 1);
udelay(500); /* delay 100us */
/* I/O mode */
db->io_mode = ior(db, 0xfe) >> 6; /* ISR bit7:6 keeps I/O mode *///read the io mode (8/16/32bits)
/* NIC Type: FASTETHER, HOMERUN, LONGRUN */
db->nic_type = FASTETHER_NIC; //identify_nic(db); //just check for dm9801 or dm9802
/* GPIO0 on pre-activate PHY */
iow(db, 0x1f, 0x00); /*REG_1F bit0 activate internal phyxcer*/
/* Set PHY */
db->op_mode = DM9000_AUTO;//may set it to be each one of DM9000_PHY_mode type.
set_PHY_mode(db);
/* Init needed register value */
db->reg0 = DM9000_REG00;
if ((db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_1M_HPNA) )//for dm9801 or dm9802
{
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, 0x00, db->reg0);/* DISCARD LONG\CRC_ERR\mutilcast\Promiscuous\WatchDog,Pass Runt,RX not enable */
iow(db, 0x02, 0X14); /* TX Polling clear *///??????????????enable the PAD //OLD VALUE IS 0X00
iow(db, 0x08, 0x3f); /* Less 3Kb, 600us */
iow(db, 0x09, db->reg9); /* Flow Control : High/Low Water */
iow(db, 0x0a, db->rega); /* Flow Control */
iow(db, 0x2f, 0); /* Special Mode */
iow(db, 0x01, 0x2c); /* clear TX status *///clear wakeup status; clear TX2END and TX1END
iow(db, 0xfe, 0x0f); /* Clear interrupt status */
/* Set address filter table */
for(i = 0; i < 6; i++)
{
db->nic_mac[i] = MyMacID[i];
}
for(i = 0; i < 6; i++)
{
iow(db, 0x10 + i, db->nic_mac[i]); //将芯片物理地址写入到MAC寄存器
}
/* Activate DM9000 */
iow(db, 0x05, db->reg5 | 1); /* RX enable */
iow(db, 0xff, DM9000_REGFF); /* Enable TX/RX interrupt mask */
//udelay(5000);
local_irq_restore(flag);
return 0;
}
/*********************************************************************************************************
** Function name: net_init
** Descriptions: net device init
** Input: dev: information of device
**
** Output: 0: OK
** other: errno
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int net_init(struct net_device *dev)
{
ether_setup(dev); /* assign some of the fields */
dev->open = net_open;
dev->stop = net_release;
dev->set_config = net_config;
dev->hard_start_xmit = net_tx;
dev->set_mac_address = net_set_mac_address;
dev->flags &= ~(IFF_LOOPBACK | IFF_MULTICAST);
dev->priv = NetDriverInfo;
memcpy(dev->dev_addr, MyMacID, dev->addr_len);
SET_MODULE_OWNER(dev);
return 0;
}
/*********************************************************************************************************
** Function name: net_config
** Descriptions: config device
** Input: dev: information of device
** map: config data
** Output: 0: OK
** other: errno
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int net_config(struct net_device *dev, struct ifmap *map)
{
return -EBUSY;
}
/*********************************************************************************************************
** Function name: net_set_mac_address
** Descriptions: set mac address
** Input: dev: information of device
** addr: config data
** Output: 0: OK
** other: errno
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int net_set_mac_address(struct net_device *dev, void *addr)
{
struct sockaddr *mac_addr;
int i;
mac_addr = addr;
if (netif_running(dev))
{
return -EBUSY;
}
/* Set the device copy of the Ethernet address */
memcpy(dev->dev_addr, mac_addr->sa_data, dev->addr_len);
for(i = 0; i < 6; i++)
{
MyMacID[i] = mac_addr->sa_data[i];
}
device_init(dev);
return 0;
}
/*********************************************************************************************************
** Function name: net_tx
** Descriptions: send data to other machine
** Input: skb: save data for send
** dev: information of device
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int net_tx(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flag;
int len;
u16 *data;
board_info_t *db = dev->priv;
netif_stop_queue(dev);
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
data = (u16 *)skb->data;
len = (len + 1) & (~1);
local_irq_save(flag);
iow(db, 0xff, 0x80);
outb(0xf8, db->ioaddr);
outsw(db->io_data, data, len >> 1);
iow(db, 0xfc, len & 0xff);
iow(db, 0xfd, (len >> 8) & 0xff);
iow(db, 0x2, 0x1); /* Cleared after TX complete */
/* Re-enable interrupt*/
iow(db, 0xff, 0x83);
dev->trans_start = jiffies;
local_irq_restore(flag);
dev_kfree_skb(skb);
return 0; /* Our simple device can not fail */
}
/*********************************************************************************************************
** Function name: net_open
** Descriptions: open device
** Input: dev: information of device
**
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int net_open(struct net_device *dev)
{
unsigned long flag;
u32 temp;
if (usage == 0)
{
local_irq_save(flag);
temp = inl(PINSEL0);
PinSel0Save = temp & (0x0f << (8 * 2));
temp |= (3 << (9 * 2));
temp &= ~(3 << (8 * 2));
outl(temp, PINSEL0);
temp = inl(IO0DIR);
temp |= 1 << 8;
outl(temp, IO0DIR);
outl(1 << 8, IOCLR);
device_init(dev);
temp = inl(VPBDIV);
temp = inl(VPBDIV);
outl(0, VPBDIV);
outl(inl(EXTMODE) & (~(1 << 3)), EXTMODE);
outl(inl(EXTPOLAR) | (1 << 3), EXTPOLAR);
outl(temp, VPBDIV);
local_irq_restore(flag);
request_irq(dev->irq, net_irq_handle, SA_INTERRUPT | SA_SAMPLE_RANDOM, "eth0", dev);
}
usage++;
MOD_INC_USE_COUNT;
netif_start_queue(dev);
return 0; /* success */
}
/*********************************************************************************************************
** Function name: net_release
** Descriptions: release device
** Input: dev: information of device
**
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int net_release(struct net_device *dev)
{
unsigned long flag;
u32 temp;
netif_stop_queue(dev);
MOD_DEC_USE_COUNT;
MOD_DEC_USE_COUNT;
usage--;
if (usage == 0)
{
local_irq_save(flag);
temp = inl(PINSEL0);
temp &= ~(3 << (9 * 2));
temp |= PinSel0Save;
outl(temp, PINSEL0);
local_irq_restore(flag);
free_irq(dev->irq, dev);
}
return(0);
}
/*********************************************************************************************************
** Function name: net_rx
** Descriptions: transact receving data
** Input: dev: information of device
**
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static void net_rx(struct net_device *dev, unsigned int length)
{
struct sk_buff *skb;
u8 *dec;
board_info_t *db = dev->priv;
/*
* The packet has been retrieved from the transmission
* medium. Build an skb around it, so upper layers can handle it
*/
skb = dev_alloc_skb(length + 2);
if (!skb)
{
return;
}
skb_reserve(skb, 2); /* align IP on 16B boundary */
skb_put(skb, length);
dec = skb->data;
insw(db->io_data, dec, length >> 1);
if ((length & 0x01) != 0)
{
dec[length - 1] = inb(db->io_data);
}
/* Write metadata, and then pass to the receive level */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx(skb);
dev->last_rx = jiffies;
return;
}
/*********************************************************************************************************
** Function name: device_rx
** Descriptions: device receving data
** Input: dev: information of device
**
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static int device_rx(struct net_device *dev)
{
unsigned int i, RxStatus, RxLen, GoodPacket, tmplen;
unsigned int rxbyte;
board_info_t *db = dev->priv;
while (1)
{
ior(db, 0xf0); /* Dummy read *///set the address to 0xf0
rxbyte = inb(db->io_data); /* Got most updated data */
/* Status check: this byte must be 0 or 1 */
if (rxbyte > DM9000_PKT_RDY)
{
return -1;
}
if (rxbyte != DM9000_PKT_RDY)
{
break;
}
/* A packet ready now & Get status/length */
GoodPacket = TRUE;
outb(0xf2, db->ioaddr);//set the address to 0xf2
RxStatus = inw(db->io_data);
RxLen = inw(db->io_data);
/* Packet Status check */
if (RxLen < 60)
{ //Runt Packet
GoodPacket = FALSE;
}
if (RxLen > DM9000_PKT_MAX)
{ //long packet
return -1;
}
if (RxStatus & 0xbf00)
{//status err
GoodPacket = FALSE;
}
/* Move data from DM9000 */
if ( GoodPacket )
{
net_rx(dev, RxLen);
}
else
{
/* Without buffer or error packet */
tmplen = (RxLen + 1) / 2;
for (i = 0; i < tmplen; i++)
{
inw(db->io_data);
}
}
}
return 0;
}
/*********************************************************************************************************
** Function name: net_tasklet
** Descriptions: The tasklet for interrupt handler
** Input:
** Output none
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static void net_tasklet(unsigned long data)
{
struct net_device *dev;
dev = (struct net_device *)data;
device_init(dev);
}
/*********************************************************************************************************
** Function name: net_irq_handle
** Descriptions: The top-half interrupt handler
** Input:
** Output none
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
static void net_irq_handle(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned int int_status;
struct net_device *dev;
board_info_t *db;
dev = (struct net_device *)dev_id;
db = dev->priv;
int_status = ior(db, 0xfe); /* Got ISR */
iow(db, 0xfe, int_status); /* Clear ISR status */
outl(1 << (dev->irq - 14), EXTINT);
if (int_status & DM9000_TX_INTR) //表示发送成功,判断发送状态寄存器TSR,决定是否出错
{
netif_start_queue(dev);
}
if (int_status & DM9000_RX_INTR) /* 接收成功 */
{
if (device_rx(dev) == -1)
{
DM9000Dev = dev;
tasklet_schedule (&ZLG_net_tasklet);
}
}
}
/*********************************************************************************************************
** Function name: net_init_module
** Descriptions: init driver
** Input: none
**
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
int net_init_module(void)
{
int result;
result = register_netdev(&net_net);
if (result < 0)
{
printk(KERN_ERR "eth0: error %i registering device \"%s\"\n",
result, net_net.name);
return(result);
}
printk(KERN_ERR "eth0: init OK\n");
return(0);
}
/*********************************************************************************************************
** Function name: net_cleanup
** Descriptions: exit driver
** Input: none
**
** Output: 0: OK
** other: not OK
** Created by: Chenmingji
** Created Date: 2005-05-12
**-------------------------------------------------------------------------------------------------------
** Modified by:
** Modified Date:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
void net_cleanup(void)
{
unregister_netdev(&net_net);
}
/*********************************************************************************************************
** End Of File
********************************************************************************************************/