📄 ve100.c
字号:
/*
device driver for network card
Intel pro/100 VE 82801db
written by Team X
2008-5
*/
#include "ve100.h"
static int ve100_start(struct nic* nic);
static void ve100_end(struct nic* nic);
static void ve100_rx_clean(struct nic* nic,unsigned int* workdone,unsigned int worktodo);
static int ve100_tx_clean(struct nic* nic);
static void ve100_xmit_prepare(struct nic* nic,struct cb* cb,struct sk_buff* skb);
static void ve100_set_multicast_list(struct net_device* ndev);
/* basic function */
/* use a read method to push write */
static void ve100_write_flush(struct nic* nic)
{
ioread8(&nic->csr->scb.status);
}
static void ve100_disable_irq(struct nic* nic)
{
unsigned long flags;
spin_lock_irqsave(&nic->cmd_lock,flags);
/* Lionel 2008 The mask bits range from bit 31 to 26. Writing a 1 to a mask bit disables the 8255x
(except the 82557) from generating an interrupt */
iowrite8(irq_mask_all,&nic->csr->scb.cmd_high);
ve100_write_flush(nic);
spin_unlock_irqrestore(&nic->cmd_lock,flags);
}
static void ve100_enable_irq(struct nic* nic)
{
unsigned long flags;
spin_lock_irqsave(&nic->cmd_lock,flags);
iowrite8(irq_mask_none,&nic->csr->scb.cmd_high);
ve100_write_flush(nic);
spin_unlock_irqrestore(&nic->cmd_lock,flags);
}
/* lionel 2008 The SCB Command word is also addressable as two bytes. The upper byte is called the Interrupt
Control byte. The least significant byte is called the Command byte. */
static void ve100_hw_reset(struct nic* nic)
{
/* Lionel 2008 The Port Selective Reset is useful when only the device needs to be reset and all configuration
parameters need to be maintained */
iowrite32(selective_reset,&nic->csr->port);
ve100_write_flush(nic);
udelay(20);
/* Lionel 2008 The Port Software Reset is synonymous with the software reset and is used to issue a complete
reset to the device */
iowrite32(software_reset,&nic->csr->port);
ve100_write_flush(nic);
udelay(20);
ve100_disable_irq(nic);
}
/* write the eeprom */
static void ve100_eeprom_write(struct nic* nic,u16 addr_len,u16 addr, u16 data)
{
u8 ctrl;
int i,j;
u32 cmd_addr_data[3];
/* to write an eeprom should use three steps */
cmd_addr_data[0] = op_ewen << (addr_len - 2);
cmd_addr_data[1] = (((op_write << addr_len) | addr) << 16) | cpu_to_le16(data);
cmd_addr_data[2] = op_ewds << (addr_len - 2);
for(j = 0; j < 3; j++)
{
/*eesk Serial Clock. Setting this bit to 1 drives the serial clock line to the EEPROM high.
Setting this bit to 0 drives the serial clock line low. Toggling this bit high and then low
clocks data in or out of the EEPROM. The serial EEPROM specifies a minimum
clock period of 4 μs. The minimum period that the clock can be high or low is 1 μs. If
the clock is driven high for only 1 μs, then it must followed by a low period of 3 μs to
meet the minimum clock frequency specification. */
/* eecs Chip Select. Setting this bit to 1 enables the EEPROM. Setting the bit to 0 disables
the EEPROM. This bit must be set to 0 for a minimum of 1 μs between consecutive
instruction cycles.*/
iowrite8(eesk|eecs, &nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
for(i = 31; i >= 0; i--)
{
/* Lionel 2008 Serial Data In. The value of this bit is written to the EEPROM when performing
write operations. */
ctrl = (cmd_addr_data[j] & (1<<i)) ? eecs | eedi : eecs;
iowrite8(ctrl,&nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
iowrite8(ctrl|eesk, &nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
}
msleep(10);
iowrite8(0,&nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
}
}
/* read the eeprom */
static u16 ve100_eeprom_read(struct nic* nic,u16* addr_len,u16 addr)
{
u16 data = 0;
u8 ctrl;
int i;
u32 cmd_addr_data;
cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
/* Chip select */
/*Lionel 2008 Chip Select. Setting this bit to 1 enables the EEPROM. Setting the bit to 0 disables
17EECS the EEPROM. This bit must be set to 0 for a minimum of 1 μs between consecutive
instruction cycles.
Serial Clock. Setting this bit to 1 drives the serial clock line to the EEPROM high.
Setting this bit to 0 drives the serial clock line low. Toggling this bit high and then low
clocks data in or out of the EEPROM. The serial EEPROM specifies a minimum 16EESK
clock period of 4 us. The minimum period that the clock can be high or low is 1us. If
the clock is driven high for only 1 us, then it must followed by a low period of 3us to
meet the minimum clock frequency specification.*/
iowrite8(eecs|eesk,&nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
for(i = 31; i >= 0; i--)
{
ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs;
iowrite8(ctrl,&nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
iowrite8(ctrl|eesk, & nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
ctrl = ioread8(&nic->csr->eeprom_ctrl_low);
/* Serial Data Out. This bit contains the value read from the EEPROM when
performing a read operation on the EEPROM. */
if(!(ctrl & eedo) && i > 16)
{
*addr_len -= (i - 16);
i = 17;
}
data = (data << 1) | (ctrl & eedo ? 1:0);
}
iowrite8(0,&nic->csr->eeprom_ctrl_low);
ve100_write_flush(nic);
udelay(4);
return le16_to_cpu(data);
}
/* load from eeprom to cache and validate checksum */
static int ve100_eeprom_load(struct nic* nic)
{
u16 addr, addr_len = 8, checksum = 0;
/* try reading with 8bit address to discover actual address length */
ve100_eeprom_read(nic,&addr_len,0);
nic->eeprom_wc = 1 << addr_len;
for(addr=0; addr < nic->eeprom_wc; addr++)
{
nic->eeprom[addr] = ve100_eeprom_read(nic,&addr_len,addr);
if(addr < nic->eeprom_wc - 1)
checksum += cpu_to_le16(nic->eeprom[addr]);
}
/* the checksum,stored in the last word,is calculated such that
the sum of words should be 0xBABA */
checksum = le16_to_cpu(0xBABA - checksum);
if(checksum != nic->eeprom[nic->eeprom_wc - 1])
{
printk("eeprom corrupted\n");
if(!eeprom_bad_csum_allow)
return -EAGAIN;
}
return 0;
}
/* execute command */
static int ve100_exec_cmd(struct nic* nic, u8 cmd, dma_addr_t dma_addr)
{
unsigned int i;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&nic->cmd_lock,flags);
/* Previous command is accepted when SCB clears */
/*Lione 2008 waiting for 100ms */
/* check if scb cmd low byte be cleared */
for(i = 0; i < VE100_WAIT_SCB_TIMEOUT; i++)
{
if(likely(!ioread8(&nic->csr->scb.cmd_low)))
break;
cpu_relax();
if(unlikely(i > VE100_WAIT_SCB_FAST))
udelay(5);
}
if(unlikely(i == VE100_WAIT_SCB_TIMEOUT))
{
ret = -EAGAIN;
goto err_unlock;
}
/* The SCB General Pointer is a 32-bit entity, which points to various data structures depending on
the command in the CUC or RUC field. */
if(unlikely(cmd != cuc_resume))
iowrite32(dma_addr,&nic->csr->scb.gen_ptr);
/*Lionel 2008 write command to system control block */
iowrite8(cmd, &nic->csr->scb.cmd_low);
err_unlock:
spin_unlock_irqrestore(&nic->cmd_lock,flags);
return ret;
}
/* execute command block */
static int ve100_exec_cb(struct nic* nic, struct sk_buff* skb, void(*func)(struct nic*,struct cb*,struct sk_buff *))
{
unsigned long flags;
struct cb* cb;
int ret = 0;
spin_lock_irqsave(&nic->cb_lock,flags);
if(unlikely(!nic->cbs_avail))
{
ret = -ENOMEM;
goto err_unlock;
}
cb = nic->cbs_to_use;
nic->cbs_to_use = cb->next;
nic->cbs_avail--;
cb->skb = skb;
if(unlikely(!nic->cbs_avail))
ret = -ENOSPC;
func(nic,cb,skb);
/* Lionel 2008 If this bit is set to one, the CU will be suspended after the completion of this CB. A CNA
interrupt will be generated if the device is configured for this. The CU transitions from the
active to the suspended state after the execution of the CB. */
cb->command |= cpu_to_le16(cb_s);
wmb();
cb->prev->command &= cpu_to_le16(~cb_s);
while(nic->cbs_to_send != nic->cbs_to_use)
{
if(unlikely(ve100_exec_cmd(nic,nic->cuc_cmd,nic->cbs_to_send->dma_addr)))
{
if(ret == -ENOSPC)
schedule_work(&nic->tx_timeout_task);
break;
}
else
{
/* Lionel 2008 The CU Resume (CU_RESUME) command resumes CU operation. */
nic->cuc_cmd = cuc_resume;
nic->cbs_to_send = nic->cbs_to_send->next;
}
}
err_unlock:
spin_unlock_irqrestore(&nic->cb_lock,flags);
return ret;
}
static u16 mdio_ctrl(struct nic* nic,u32 addr,u32 dir,u32 reg,u16 data)
{
unsigned int i;
unsigned long flags;
u32 data_out = 0;
spin_lock_irqsave(&nic->mdio_lock,flags);
for(i = 100; i; --i)
{
if(ioread32(&nic->csr->mdi_ctrl) & mdi_ready)
break;
udelay(20);
}
if(unlikely(!i))
{
spin_unlock_irqrestore(&nic->mdio_lock,flags);
return 0;
}
iowrite32((reg<<16)|(addr << 21)|dir|data, &nic->csr->mdi_ctrl);
for(i = 0; i < 100; i++)
{
udelay(20);
if((data_out = ioread32(&nic->csr->mdi_ctrl)) & mdi_ready)
break;
}
spin_unlock_irqrestore(&nic->mdio_lock,flags);
return (u16)data_out;
}
static int mdio_read(struct net_device* ndev,int addr,int reg)
{
return mdio_ctrl(netdev_priv(ndev),addr,mdi_read,reg,0);
}
static void mdio_write(struct net_device* ndev,int addr,int reg, int data)
{
mdio_ctrl(netdev_priv(ndev),addr,mdi_write,reg,data);
}
/* get defaults */
static void ve100_get_defaults(struct nic* nic)
{
struct param_range cbs = {.min = 64,.max = 256, .count = 128};
struct param_range rfds = {.min= 16,.max = 256, .count = 256};
// pci_read_config_byte(nic->pdev, PCI_REVISION_ID,&nic->rev_id);
if(nic->flags & ich)
nic->mac = mac_82559_D101M;
else
nic->mac = nic->pdev->revision;
if(nic->mac == mac_unknown)
nic->mac = mac_82557_D100_A;
nic->params.rfds = rfds;
nic->params.cbs = cbs;
/*Quadwords to DMA into FIFO before starting frame transmit */
nic->tx_threshold = 0xE0;
/*no interrupt for every tx completion, delay = 256us if not 557 */
nic->tx_command = cpu_to_le16(cb_tx | cb_tx_sf | ((nic->mac >= mac_82558_D101_A4) ? cb_cid:cb_i));
nic->blank_rfd.command = cpu_to_le16(cb_el);
nic->blank_rfd.rbd = 0xffffffff;
nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN);
/*MII setup */
nic->mii.phy_id_mask = 0x1F;
nic->mii.reg_num_mask = 0x1F;
nic->mii.dev = nic->ndev;
nic->mii.mdio_read = mdio_read;
nic->mii.mdio_write = mdio_write;
}
/*configuration */
static void ve100_configure(struct nic* nic,struct cb* cb, struct sk_buff* skb)
{
struct config* config = &cb->u.config;
u8* c = (u8*)config;
/* Lionel 2008 The configure command is used to load the device with its operating
parameters */
cb->command = cpu_to_le16(cb_config);
memset(config,0,sizeof(struct config));
config->byte_count = 0x16; /* bytes of the struct */
config->rx_fifo_limit = 0x8; /*bytes in FIFO before DMA */
config->direct_rx_dma = 0x1;
config->standard_tcb = 0x1; /* 1 standard 0 extended */
config->standard_stat_counter = 0x1;
config->rx_discard_short_frames = 0x1; /* 1 discard, 0 pass*/
config->tx_underrun_retry = 0x3; /* # of underrun retries */
config->mii_mode = 0x1; /*1 MII mode 0 503mode */
config->pad10 = 0x6;
config->no_source_addr_insertion = 0x1;
config->preamble_length = 0x2; /*0 -> 1 1 -> 3 2 -> 7, 3->15 bytes */
config->ifs = 0x6; /* inter frame spacing */
config->ip_addr_hi = 0xF2; /* arp ip filter */
config->pad15_1 = 0x1;
config->pad15_2 = 0x1;
config->crs_or_cdt = 0x0; /* 0 CRS only, 1 CRS or CDT */
config->fc_delay_high = 0x40; /* time delay for fc frame */
config->tx_padding = 0x1;
config->fc_priority_threshold = 0x7; /* priority fc disabled */
config->pad18 = 0x1;
config->full_duplex_pin = 0x1; /* 1= examine FDX# pin */
config->pad20_1 = 0x1F;
config->fc_priority_location = 0x1; /*1 byte 31 0 byte19 */
config->pad21_1 = 0x5;
config->adaptive_ifs = nic->adaptive_ifs;
config->loopback = nic->loopback;
if(nic->mii.force_media && nic->mii.full_duplex)
config->full_duplex_force = 0x1;
if(nic->flags & promiscuous || nic->loopback)
{
config->rx_save_bad_frames = 0x1; /* 1 save 0 discard */
config->rx_discard_short_frames = 0x0; /* 1 discard 0 save */
config->promiscuous_mode = 0x1; /* 1 on 0 off*/
}
if(nic->flags & multicast_all)
config->multicast_all = 0x1; /* 1 accept 0 no */
/*disable wol when up */
if(netif_running(nic->ndev) || !(nic->flags & wol_magic))
config->magic_packet_disable = 0x1; /* 1 off 0 on */
if(nic->mac >= mac_82558_D101_A4)
{
config->fc_disable = 0x1; /*1 tx fc off 0 tx fc on */
config->mwi_enable = 0x1; /*1 enable 0 disable */
config->standard_tcb = 0x0; /* 1 standard 0 extended */
config->rx_long_ok = 0x1; /*1 vlans 0 standard */
if(nic->mac >= mac_82559_D101M)
{
config->tno_intr = 0x1;
if(nic->mac >= mac_82551_10)
{
config->byte_count = 0x20;
config->rx_d102_mode = 0x1;
}
}
else
config->standard_stat_counter = 0x0;
}
}
/* setup iaaddr */
static void ve100_setup_iaaddr(struct nic* nic,struct cb* cb,struct sk_buff* skb)
{
cb->command = cpu_to_le16(cb_iaaddr);
memcpy(cb->u.iaaddr,nic->ndev->dev_addr,ETH_ALEN);
}
/* physical init */
static int ve100_phy_init(struct nic* nic)
{
struct net_device* ndev = nic->ndev;
u16 bmcr,stat,id_low,id_high,cong;
u32 addr;
for(addr = 0; addr < 32; addr++)
{
nic->mii.phy_id = (addr==0) ? 1 : (addr == 1) ? 0 : addr;
bmcr = mdio_read(ndev,nic->mii.phy_id,MII_BMCR);
stat = mdio_read(ndev,nic->mii.phy_id,MII_BMSR);
stat = mdio_read(ndev,nic->mii.phy_id,MII_BMSR);
if(!((bmcr == 0xffff) || ((stat==0) && (bmcr == 0))))
break;
}
printk("phy addr is %d\n",nic->mii.phy_id);
if(addr == 32)
return -EAGAIN;
/* select the phy and isolate the rest */
for(addr = 0; addr < 32; addr ++)
{
if(addr != nic->mii.phy_id)
mdio_write(ndev,addr,MII_BMCR,BMCR_ISOLATE);
else
{
bmcr = mdio_read(ndev,addr,MII_BMCR);
mdio_write(ndev,addr,MII_BMCR,bmcr & ~BMCR_ISOLATE);
}
}
/*Get physical ID */
id_low = mdio_read(ndev,nic->mii.phy_id,MII_PHYSID1);
id_high = mdio_read(ndev,nic->mii.phy_id,MII_PHYSID2);
nic->phy = (u32)id_high << 16 | (u32)id_low;
printk("phy ID = %x\n",nic->phy);
#define NCS_PHY_MODEL_MASK 0XFFF0FFFF
if((nic->phy & NCS_PHY_MODEL_MASK) == phy_nsc_tx)
{
/* disable congestion control */
cong = mdio_read(ndev,nic->mii.phy_id, MII_NSC_CONG);
cong |= NSC_CONG_TXREADY;
cong &= ~NSC_CONG_ENABLE;
mdio_write(ndev,nic->mii.phy_id, MII_NSC_CONG,cong);
}
if((nic->mac >= mac_82550_D102) || ((nic->flags & ich) &&
(mdio_read(ndev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000) &&
!(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled)))
{
/* enable/disable MDI/MDI-X auto-switching. */
mdio_write(ndev, nic->mii.phy_id, MII_NCONFIG,
nic->mii.force_media ? 0 : NCONFIG_AUTO_SWITCH);
}
return 0;
}
/* hardware init */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -