⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ve100.c

📁 Linux 下的ve100网卡驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 
	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 + -