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

📄 smc9194.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 4 页
字号:
	insw(port, data, len>>1);	if (len & 1)		data[len-1] = readw(port);}static inline void smc_outb(u8 val, u_int base, u_int reg){	u_int port = base + (reg & ~1) * 4;	u_int res = readw(port);	if (reg & 1) {		res &= 0xff;		res |= val << 8;	} else {		res &= 0xff00;		res |= val;	}	writew(res, port);}static inline void smc_outw(u16 val, u_int base, u_int reg){	u_int port = base + reg * 4;	writew(val, port);}static inline void smc_outl(u32 val, u_int base, u_int reg){	u_int port = base + reg * 4;	writew(val, port);	writew(val >> 16, port + 8);}static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len){	u_int port = base + reg * 4;	outsw(port, data, len>>1);}/*------------------------------------------------------------------------- .  I define some macros to make it easier to do somewhat common . or slightly complicated, repeated tasks.  --------------------------------------------------------------------------*//* select a register bank, 0 to 3  */#define SMC_SELECT_BANK(x)				\	{						\		smc_outw(x, ioaddr, BANK_SELECT);	\	} /* define a small delay for the reset */#define SMC_DELAY()					\	{						\		smc_inw(ioaddr, RCR);			\		smc_inw(ioaddr, RCR);			\		smc_inw(ioaddr, RCR);			\	}/* this enables an interrupt in the interrupt mask register */#define SMC_ENABLE_INT(x)				\	{						\		word mask;				\		mask = smc_inw(ioaddr, INTERRUPT);	\		mask |= (x) << 8;			\		smc_outw(mask, ioaddr, INT_MASK);	\	}/* this sets the absolutel interrupt mask */#define SMC_SET_INT(x)					\	{						\		smc_outw((x) << 8, ioaddr, INTERRUPT);	\	}#endif/* . A rather simple routine to print out a packet for debugging purposes.*/#if SMC_DEBUG > 2static void print_packet(byte * buf, int length){	int i;	int remainder;	int lines;	printk("Packet of length %d \n", length);	lines = length / 16;	remainder = length % 16;	for (i = 0; i < lines ; i ++) {		int cur;		for (cur = 0; cur < 8; cur ++) {			byte a, b;			a = *(buf ++);			b = *(buf ++);			printk("%02x%02x ", a, b);		}		printk("\n");	}	for (i = 0; i < remainder/2 ; i++) {		byte a, b;		a = *(buf ++);		b = *(buf ++);		printk("%02x%02x ", a, b);	}	if (remainder & 1) {		byte a;		a = *buf++;		printk("%02x", a);	}	printk("\n");}#else#define print_packet(buf,len) do { } while (0)#endif/* . Function: smc_reset(struct net_device *dev) . Purpose: .  	This sets the SMC91xx chip to its normal state, hopefully from whatever . 	mess that any other DOS driver has put it in. . . Maybe I should reset more registers to defaults in here?  SOFTRESET  should . do that for me. . . Method: .	1.  send a SOFT RESET .	2.  wait for it to finish .	3.  enable autorelease mode .	4.  reset the memory management unit .	5.  clear all interrupts .*/static void smc_reset(struct net_device *dev){	u_int ioaddr = dev->base_addr;	/* This resets the registers mostly to defaults, but doesn't	   affect EEPROM.  That seems unnecessary */	SMC_SELECT_BANK(0);	smc_outw(RCR_SOFTRESET, ioaddr, RCR);	/* this should pause enough for the chip to be happy */	SMC_DELAY();	/* Set the transmit and receive configuration registers to	   default values */	smc_outw(RCR_CLEAR, ioaddr, RCR);	smc_outw(TCR_CLEAR, ioaddr, TCR);	/* set the control register to automatically	   release successfully transmitted packets, to make the best	   use out of our limited memory */	SMC_SELECT_BANK(1);	smc_outw(smc_inw(ioaddr, CONTROL) | CTL_AUTO_RELEASE, ioaddr, CONTROL);	/* Reset the MMU */	SMC_SELECT_BANK(2);	smc_outw(MC_RESET, ioaddr, MMU_CMD);	/* Note:  It doesn't seem that waiting for the MMU busy is needed here,	   but this is a place where future chipsets _COULD_ break.  Be wary 	   of issuing another MMU command right after this */	SMC_SET_INT(0);}/* . Function: smc_enable . Purpose: let the chip talk to the outside work . Method: .	1.  Enable the transmitter .	2.  Enable the receiver .	3.  Enable interrupts*/static void smc_enable(struct net_device *dev){	u_int ioaddr = dev->base_addr;	SMC_SELECT_BANK(0);	/* see the header file for options in TCR/RCR NORMAL*/	smc_outw(TCR_NORMAL, ioaddr, TCR);	smc_outw(RCR_NORMAL, ioaddr, RCR);	/* now, enable interrupts */	SMC_SELECT_BANK(2);	SMC_SET_INT(SMC_INTERRUPT_MASK);}/* . Function: smc_shutdown(struct net_device *dev) . Purpose:  closes down the SMC91xxx chip. . Method: .	1. zero the interrupt mask .	2. clear the enable receive flag .	3. clear the enable xmit flags . . TODO: .   (1) maybe utilize power down mode. .	Why not yet?  Because while the chip will go into power down mode, .	the manual says that it will wake up in response to any I/O requests .	in the register space.   Empirical results do not show this working.*/static void smc_shutdown(struct net_device *dev){	u_int ioaddr = dev->base_addr;	/* no more interrupts for me */	SMC_SELECT_BANK(2);	SMC_SET_INT(0);	/* and tell the card to stay away from that nasty outside world */	SMC_SELECT_BANK(0);	smc_outb(RCR_CLEAR, ioaddr, RCR);	smc_outb(TCR_CLEAR, ioaddr, TCR);#if 0	/* finally, shut the chip down */	SMC_SELECT_BANK(1);	smc_outw(smc_inw(ioaddr, CONTROL), CTL_POWERDOWN, ioaddr, CONTROL);#endif}/* . Function: smc_setmulticast(int ioaddr, int count, dev_mc_list * adds) . Purpose: .    This sets the internal hardware table to filter out unwanted multicast .    packets before they take up memory. . .    The SMC chip uses a hash table where the high 6 bits of the CRC of .    address are the offset into the table.  If that bit is 1, then the .    multicast packet is accepted.  Otherwise, it's dropped silently. . .    To use the 6 bits as an offset into the table, the high 3 bits are the .    number of the 8 bit register, while the low 3 bits are the bit within .    that register. . . This routine is based very heavily on the one provided by Peter Cammaert.*/static void smc_setmulticast(struct net_device *dev, int count, struct dev_mc_list * addrs){	u_int ioaddr = dev->base_addr;	int			i;	unsigned char		multicast_table[8];	struct dev_mc_list	* cur_addr;	/* table for flipping the order of 3 bits */	unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };	/* start with a table of all zeros: reject all */	memset(multicast_table, 0, sizeof(multicast_table));	cur_addr = addrs;	for (i = 0; i < count ; i ++, cur_addr = cur_addr->next) {		int position;		/* do we have a pointer here? */		if (!cur_addr)			break;		/* make sure this is a multicast address - shouldn't this		   be a given if we have it here ? */		if (!(*cur_addr->dmi_addr & 1))			continue;		/* only use the low order bits */		position = crc32(cur_addr->dmi_addr, 6) & 0x3f;		/* do some messy swapping to put the bit in the right spot */		multicast_table[invert3[position&7]] |=					(1<<invert3[(position>>3)&7]);	}	/* now, the table can be loaded into the chipset */	SMC_SELECT_BANK(3);	for (i = 0; i < 8 ; i++) {		smc_outb(multicast_table[i], ioaddr, MULTICAST1 + i);	}}/*  Finds the CRC32 of a set of bytes.  Again, from Peter Cammaert's code.*/static int crc32(char * s, int length){	/* indices */	int perByte;	int perBit;	/* crc polynomial for Ethernet */	const unsigned long poly = 0xedb88320;	/* crc value - preinitialized to all 1's */	unsigned long crc_value = 0xffffffff;	for (perByte = 0; perByte < length; perByte ++) {		unsigned char	c;		c = *(s++);		for (perBit = 0; perBit < 8; perBit++) {			crc_value = (crc_value>>1)^				(((crc_value^c)&0x01)?poly:0);			c >>= 1;		}	}	return	crc_value;}/* . Function: smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *) . Purpose: .    Attempt to allocate memory for a packet, if chip-memory is not .    available, then tell the card to generate an interrupt when it .    is available. . . Algorithm: . . o	if the saved_skb is not currently null, then drop this packet .	on the floor.  This should never happen, because of TBUSY. . o	if the saved_skb is null, then replace it with the current packet, . o	See if I can sending it now. . o 	(NO): Enable interrupts and let the interrupt handler deal with it. . o	(YES):Send it now.*/static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device * dev){	struct smc_local *lp 	= (struct smc_local *)dev->priv;	u_int ioaddr	 	= dev->base_addr;	word 			length;	unsigned short 		numPages;	word			time_out;	netif_stop_queue(dev);	/* Well, I want to send the packet.. but I don't know	   if I can send it right now...  */	if (lp->saved_skb) {		/* THIS SHOULD NEVER HAPPEN. */		lp->stats.tx_aborted_errors++;		printk("%s: Bad Craziness - sent packet while busy.\n",			dev->name);		return 1;	}	lp->saved_skb = skb;	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;			/*	** The MMU wants the number of pages to be the number of 256 bytes	** 'pages', minus 1 (since a packet can't ever have 0 pages :))	**	** Pkt size for allocating is data length +6 (for additional status words,	** length and ctl!) If odd size last byte is included in this header.	*/	numPages = ((length & 0xfffe) + 6) / 256;	if (numPages > 7) {		printk("%s: Far too big packet error.\n", dev->name);		/* freeing the packet is a good thing here... but should		 . any packets of this size get down here?   */		dev_kfree_skb (skb);		lp->saved_skb = NULL;		/* this IS an error, but, i don't want the skb saved */		netif_wake_queue(dev);		return 0;	}	/* either way, a packet is waiting now */	lp->packets_waiting++;	/* now, try to allocate the memory */	SMC_SELECT_BANK(2);	smc_outw(MC_ALLOC | numPages, ioaddr, MMU_CMD);	/* 	. Performance Hack	. 	. wait a short amount of time.. if I can send a packet now, I send	. it now.  Otherwise, I enable an interrupt and wait for one to be	. available.	.	. I could have handled this a slightly different way, by checking to	. see if any memory was available in the FREE MEMORY register.  However,	. either way, I need to generate an allocation, and the allocation works	. no matter what, so I saw no point in checking free memory.	*/	time_out = MEMORY_WAIT_TIME;	do {		word	status;		status = smc_inb(ioaddr, INTERRUPT);		if (status & IM_ALLOC_INT) {			/* acknowledge the interrupt */			smc_outb(IM_ALLOC_INT, ioaddr, INTERRUPT);			break;		} 	} while (-- time_out); 	if (!time_out) {		/* oh well, wait until the chip finds memory later */		SMC_ENABLE_INT(IM_ALLOC_INT);		PRINTK2(("%s: memory allocation deferred.\n", dev->name));		/* it's deferred, but I'll handle it later */		return 0; 	}	/* or YES! I can send the packet now.. */	smc_hardware_send_packet(dev);	netif_wake_queue(dev);	return 0;}/* . Function:  smc_hardware_send_packet(struct net_device *) . Purpose: .	This sends the actual packet to the SMC9xxx chip. . . Algorithm: . 	First, see if a saved_skb is available. .		(this should NOT be called if there is no 'saved_skb' .	Now, find the packet number that the chip allocated .	Point the data pointers at it in memory .	Set the length word in the chip's memory .	Dump the packet to chip memory .	Check if a last byte is needed (odd length packet) .		if so, set the control flag right . 	Tell the card to send it .	Enable the transmit interrupt, so I know if it failed . 	Free the kernel data if I actually sent it.*/static void smc_hardware_send_packet(struct net_device *dev){	struct smc_local *lp = (struct smc_local *)dev->priv;	struct sk_buff *skb = lp->saved_skb;	word length, lastword;	u_int ioaddr = dev->base_addr;	byte packet_no;	byte *buf;	if (!skb) {		PRINTK(("%s: In XMIT with no packet to send\n", dev->name));		return;	}	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;	buf = skb->data;	/* If I get here, I _know_ there is a packet slot waiting for me */	packet_no = smc_inb(ioaddr, PNR_ARR + 1);	if (packet_no & 0x80) {		/* or isn't there?  BAD CHIP! */		printk(KERN_DEBUG "%s: Memory allocation failed.\n",			dev->name);		dev_kfree_skb_any(skb);		lp->saved_skb = NULL;		netif_wake_queue(dev);		return;	}	/* we have a packet address, so tell the card to use it */	smc_outb(packet_no, ioaddr, PNR_ARR);	/* point to the beginning of the packet */	smc_outw(PTR_AUTOINC, ioaddr, POINTER); 	PRINTK3(("%s: Trying to xmit packet of length %x\n",		dev->name, length));	print_packet(buf, length);	/* send the packet length (+6 for status, length and ctl byte) 	   and the status word (set to zeros) */	smc_outl((length + 6) << 16, ioaddr, DATA_1);	/* send the actual data	 . I _think_ it's faster to send the longs first, and then	 . mop up by sending the last word.  It depends heavily 	 . on alignment, at least on the 486.  Maybe it would be 	 . a good idea to check which is optimal?  But that could take	 . almost as much time as is saved?	*/	smc_outs(ioaddr, DATA_1, buf, length);	/* Send the last byte, if there is one.   */	if ((length & 1) == 0)		lastword = 0;	else		lastword = 0x2000 | buf[length - 1];	smc_outw(lastword, ioaddr, DATA_1);	/* enable the interrupts */	SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT);	/* and let the chipset deal with it */	smc_outw(MC_ENQUEUE, ioaddr, MMU_CMD);	PRINTK2(("%s: Sent packet of length %d\n", dev->name, length));	lp->saved_skb = NULL;	dev_kfree_skb_any (skb);	dev->trans_start = jiffies;	/* we can send another packet */	netif_wake_queue(dev);	return;}/*------------------------------------------------------------------------- | | smc_init(struct net_device * dev) |   Input parameters: |	dev->base_addr == 0, try to find all possible locations |	dev->base_addr == 1, return failure code |	dev->base_addr == 2, always allocate space,  and return success |	dev->base_addr == <anything else>   this is the address to check | |   Output: |	0 --> there is a device |	anything else, error | ---------------------------------------------------------------------------*/int __init smc_init(struct net_device *dev){	int ret = -ENODEV;#if defined(CONFIG_ASSABET_NEPONSET)	if (machine_is_assabet() && machine_has_neponset()) {		unsigned int *addr;		unsigned char ecor;		unsigned long flags;		NCR_0 |= NCR_ENET_OSC_EN;		dev->irq = IRQ_NEPONSET_SMC9196;		/*		 * Map the attribute space.  This is overkill, but clean.		 */		addr = ioremap(0x18000000 + (1 << 25), 64 * 1024 * 4);		if (!addr)			return -ENOMEM;		/*		 * Reset the device.  We must disable IRQs around this.		 */		local_irq_save(flags);		ecor = readl(addr + ECOR) & ~ECOR_RESET;		writel(ecor | ECOR_RESET, addr + ECOR);		udelay(100);		/*		 * The device will ignore all writes to the enable bit while		 * reset is asserted, even if the reset bit is cleared in the		 * same write.  Must clear reset first, then enable the device.		 */		writel(ecor, addr + ECOR);		writel(ecor | ECOR_ENABLE, addr + ECOR);		/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -