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

📄 smc91x.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/*------------------------------------------------------------------------ . smc91x.c . This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices. . . Copyright (C) 1996 by Erik Stahlman . Copyright (C) 2001 Standard Microsystems Corporation .	Developed by Simple Network Magic Corporation . Copyright (C) 2003 Monta Vista Software, Inc. .	Unified SMC91x driver by Nicolas Pitre . . 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. . . You should have received a copy of the GNU General Public License . along with this program; if not, write to the Free Software . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA . . Arguments: . 	io	= for the base address .	irq	= for the IRQ .	nowait	= 0 for normal wait states, 1 eliminates additional wait states . . original author: . 	Erik Stahlman <erik@vt.edu> . . hardware multicast code: .    Peter Cammaert <pc@denkart.be> . . contributors: . 	Daris A Nevil <dnevil@snmc.com> .      Nicolas Pitre <nico@cam.org> . . History: .   08/20/00  Arnaldo Melo       fix kfree(skb) in smc_hardware_send_packet .   12/15/00  Christian Jullien  fix "Warning: kfree_skb on hard IRQ" .   03/16/01  Daris A Nevil      modified smc9194.c for use with LAN91C111 .   08/22/01  Scott Anderson     merge changes from smc9194 to smc91111 .   08/21/01  Pramod B Bhardwaj  added support for RevB of LAN91C111 .   12/20/01  Jeff Sutherland    initial port to Xscale PXA with DMA support .   04/07/03  Nicolas Pitre      unified SMC91x driver, killed irq races, .                                more bus abstraction, big cleanup, etc. .   29/09/03  Russell King       - add driver model support .                                - ethtool support .                                - convert to use generic MII interface .                                - add link up/down notification .                                - don't try to handle full negotiation in .                                  smc_phy_configure .                                - clean up (and fix stack overrun) in PHY .                                  MII read/write functions ----------------------------------------------------------------------------*/static const char version[] =	"smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre <nico@cam.org>\n";/* Debugging level */#ifndef SMC_DEBUG#define SMC_DEBUG		0#endif#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/crc32.h>#include <linux/device.h>#include <linux/spinlock.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#ifdef CONFIG_PM#include <linux/pm.h>#endif#include <asm/io.h>#include <asm/hardware.h>#include <asm/irq.h>#ifdef CONFIG_SA1100_ASSABET#include <asm/arch/assabet.h>#include <asm/arch/neponset.h>#endif#include "smc91x.h"#if 0#define GPIO_SET	*(volatile unsigned long *)0xfc900000#define GPIO_CLR	*(volatile unsigned long *)0xfc900004#define GPIO_DIR	*(volatile unsigned long *)0xfc900008#define DEBUG_DIR(x)	do { GPIO_DIR = (x); } while (0)#define DEBUG_CLR(x)	do { GPIO_CLR = (x); } while (0)#define DEBUG_SET(x)	do { GPIO_SET = (x); } while (0)#else#define DEBUG_DIR(x)	do { } while (0)#define DEBUG_CLR(x)	do { } while (0)#define DEBUG_SET(x)	do { } while (0)#endif#define LUBBOCK_ETH_PHYS       PXA_CS3_PHYS#define LUBBOCK_ETH_VIRT       (0xf1000000)#ifdef CONFIG_ISA/* . the LAN91C111 can be at any of the following port addresses.  To change, . for a slightly different card, you can add it to the array.  Keep in . mind that the array must end in zero.*/static unsigned int smc_portlist[] __initdata = {	0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,	0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0};#endif  /* CONFIG_ISA */#ifndef SMC_IOADDR# define SMC_IOADDR		-1#endifstatic int io = SMC_IOADDR;#ifndef SMC_IRQ# define SMC_IRQ		-1#endifstatic int irq = SMC_IRQ;#ifndef SMC_NOWAIT# define SMC_NOWAIT		0#endifstatic int nowait = SMC_NOWAIT;MODULE_PARM(io, "i");MODULE_PARM(irq, "i");MODULE_PARM(nowait, "i");MODULE_PARM_DESC(io, "I/O base address");MODULE_PARM_DESC(irq, "IRQ number");MODULE_PARM_DESC(nowait, "set to 1 for no wait state");/*------------------------------------------------------------------------ . . The internal workings of the driver.  If you are changing anything . here with the SMC stuff, you should have the datasheet and know . what you are doing. . -------------------------------------------------------------------------*/#define CARDNAME "smc91x"// Use power-down feature of the chip#define POWER_DOWN		1/* . Wait time for memory to be free.  This probably shouldn't be . tuned that much, as waiting for this means nothing else happens . in the system*/#define MEMORY_WAIT_TIME	16/* . This selects whether TX packets are sent one by one to the SMC91x internal . memory and throttled until transmission completes.  This may prevent . RX overruns a litle by keeping much of the memory free for RX packets . but to the expense of reduced TX throughput and increased IRQ overhead. . Note this is not a cure for a too slow data bus or too high IRQ latency. */#ifdef CONFIG_ARM_SMC91X_THROTTLE_TX#define THROTTLE_TX_PKTS	1#else#define THROTTLE_TX_PKTS	0#endif/* * The MII clock high/low times.  2x this number gives the MII clock period * in microseconds. (was 50, but this gives 6.4ms for each MII transaction!) */#define MII_DELAY		1/* store this information for the driver.. */struct smc_local {	// If I have to wait until memory is available to send	// a packet, I will store the skbuff here, until I get the	// desired memory.  Then, I'll send it out and free it.	struct sk_buff *saved_skb; 	// these are things that the kernel wants me to keep, so users	// can find out semi-useless statistics of how well the card is	// performing	struct net_device_stats stats;	// version/revision of the SMC91x chip	int	version;	// Contains the current active transmission mode	int	tcr_cur_mode;	// Contains the current active receive mode	int	rcr_cur_mode;	// Contains the current active receive/phy mode	int	rpc_cur_mode;	int	ctl_rfduplx;	int	ctl_rspeed;	u32	msg_enable;	u32	phy_type;	struct mii_if_info mii;	spinlock_t lock;};#if SMC_DEBUG > 2#define PRINTK3(args...)  printk(args)#else#define PRINTK3(args...)  do { } while(0)#endif#if SMC_DEBUG > 1#define PRINTK2(args...)  printk(args)#else#define PRINTK2(args...)  do { } while(0)#endif#if SMC_DEBUG > 0#define PRINTK1(args...)  printk(args)#define PRINTK(args...)   printk(args)#else#define PRINTK1(args...)  do { } while(0)#define PRINTK(args...)   printk(KERN_DEBUG args)#endif#if SMC_DEBUG > 3static void PRINT_PKT(u_char *buf, int length){	int i;	int remainder;	int lines;	lines = length / 16;	remainder = length % 16;	for (i = 0; i < lines ; i ++) {		int cur;		for (cur = 0; cur < 8; cur++) {			u_char a, b;			a = *buf++;			b = *buf++;			printk("%02x%02x ", a, b);		}		printk("\n");	}	for (i = 0; i < remainder/2 ; i++) {		u_char a, b;		a = *buf++;		b = *buf++;		printk("%02x%02x ", a, b );	}	printk("\n");}#else#define PRINT_PKT(x...)  do { } while(0)#endif/* this enables an interrupt in the interrupt mask register */#define SMC_ENABLE_INT(x) do {						\	unsigned long flags;						\	unsigned char mask;						\	spin_lock_irqsave(&lp->lock, flags);				\	mask = SMC_GET_INT_MASK();					\	mask |= (x);							\	SMC_SET_INT_MASK(mask);						\	spin_unlock_irqrestore(&lp->lock, flags);			\} while (0)/* this disables an interrupt from the interrupt mask register */#define SMC_DISABLE_INT(x) do {						\	unsigned long flags;						\	unsigned char mask;						\	spin_lock_irqsave(&lp->lock, flags);				\	mask = SMC_GET_INT_MASK();					\	mask &= ~(x);							\	SMC_SET_INT_MASK(mask);						\	spin_unlock_irqrestore(&lp->lock, flags);			\} while (0)/* wait while MMU is busy */#define SMC_WAIT_MMU_BUSY() do {					\	if (unlikely(SMC_GET_MMU_CMD() & MC_BUSY)) {				\		unsigned long timeout = jiffies + 2;			\		while (SMC_GET_MMU_CMD() & MC_BUSY) {			\			if (time_after(jiffies, timeout)) {		\				printk("%s: timeout %s line %d\n",	\					dev->name, __FILE__, __LINE__);	\				break;					\			}						\		}							\	}								\} while (0)/* this does a soft reset on the device */static voidsmc_reset(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	unsigned int ctl;	PRINTK2("%s: %s\n", dev->name, __FUNCTION__);	/* This resets the registers mostly to defaults, but doesn't	   affect EEPROM.  That seems unnecessary */	SMC_SELECT_BANK( 0 );	SMC_SET_RCR( RCR_SOFTRST );	/* Setup the Configuration Register */	/* This is necessary because the CONFIG_REG is not affected */	/* by a soft reset */	SMC_SELECT_BANK( 1 );	SMC_SET_CONFIG( CONFIG_DEFAULT );	/* Setup for fast accesses if requested */	/* If the card/system can't handle it then there will */	/* be no recovery except for a hard reset or power cycle */	if (nowait)		SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_NO_WAIT );#ifdef POWER_DOWN	/* Release from possible power-down state */	/* Configuration register is not affected by Soft Reset */	SMC_SELECT_BANK( 1 );	SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_EPH_POWER_EN );#endif	/* this should pause enough for the chip to be happy */	udelay(1);	/* Disable transmit and receive functionality */	SMC_SELECT_BANK( 0 );	SMC_SET_RCR( RCR_CLEAR );	SMC_SET_TCR( TCR_CLEAR );	SMC_SELECT_BANK( 1 );	ctl = SMC_GET_CTL() | CTL_LE_ENABLE;	/* set the control register to automatically	   release successfully transmitted packets, to make the best	   use out of our limited memory */#if ! THROTTLE_TX_PKTS	ctl |= CTL_AUTO_RELEASE;#else	ctl &= ~CTL_AUTO_RELEASE;#endif	SMC_SET_CTL(ctl);	/* Disable all interrupts */	SMC_SELECT_BANK( 2 );	SMC_SET_INT_MASK( 0 );	/* Reset the MMU */	SMC_SET_MMU_CMD( MC_RESET );	SMC_WAIT_MMU_BUSY();}/* Enable Interrupts, Receive, and Transmit */static voidsmc_enable(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc_local *lp = (struct smc_local *)dev->priv;	int mask;	PRINTK2("%s: %s\n", dev->name, __FUNCTION__);	/* see the header file for options in TCR/RCR DEFAULT*/	SMC_SELECT_BANK( 0 );	SMC_SET_TCR( lp->tcr_cur_mode );	SMC_SET_RCR( lp->rcr_cur_mode );	/* now, enable interrupts */	mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT;	if (lp->version >= (CHIP_91100 << 4))		mask |= IM_MDINT;	SMC_SELECT_BANK( 2 );	SMC_SET_INT_MASK( mask );}/* this puts the device in an inactive state */static voidsmc_shutdown(unsigned long ioaddr){	PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__);	/* no more interrupts for me */	SMC_SELECT_BANK( 2 );	SMC_SET_INT_MASK( 0 );	/* and tell the card to stay away from that nasty outside world */	SMC_SELECT_BANK( 0 );	SMC_SET_RCR( RCR_CLEAR );	SMC_SET_TCR( TCR_CLEAR );#ifdef POWER_DOWN	/* finally, shut the chip down */	SMC_SELECT_BANK( 1 );	SMC_SET_CONFIG( SMC_GET_CONFIG() & ~CONFIG_EPH_POWER_EN );#endif}/* This is the procedure to handle the receipt of a packet. */static inline void smc_rcv(struct net_device *dev){	struct smc_local *lp = (struct smc_local *)dev->priv;	unsigned long ioaddr = dev->base_addr;	unsigned int packet_number, status, packet_len;	PRINTK3("%s: %s\n", dev->name, __FUNCTION__);	DEBUG_SET(1);	packet_number = SMC_GET_RXFIFO();	if (unlikely(packet_number & RXFIFO_REMPTY)) {		PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name);		return;	}	/* read from start of packet */	SMC_SET_PTR( PTR_READ | PTR_RCV | PTR_AUTOINC );	/* First two words are status and packet length */	SMC_GET_PKT_HDR(status, packet_len);	packet_len &= 0x07ff;  /* mask off top bits */	PRINTK2("%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",		dev->name, packet_number, status,		packet_len, packet_len);	if (unlikely(status & RS_ERRORS)) {		lp->stats.rx_errors++;		if (status & RS_ALGNERR)			lp->stats.rx_frame_errors++;		if (status & (RS_TOOSHORT | RS_TOOLONG))			lp->stats.rx_length_errors++;		if (status & RS_BADCRC)			lp->stats.rx_crc_errors++;	} else {		struct sk_buff *skb;		unsigned char *data;		unsigned int data_len;		/* set multicast stats */		if (status & RS_MULTICAST)			lp->stats.multicast++;		/*		 * Actual payload is packet_len - 4 (or 3 if odd byte).		 * We want skb_reserve(2) and the final ctrl word		 * (2 bytes, possibly containing the payload odd byte).		 * Ence packet_len - 4 + 2 + 2.		 */		DEBUG_SET(2);		skb = dev_alloc_skb(packet_len);		if (unlikely(skb == NULL)) {			printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",				dev->name);			lp->stats.rx_dropped++;			goto done;		}		DEBUG_CLR(2);		/* Align IP header to 32 bits */		skb_reserve(skb, 2);		/* BUG: the LAN91C111 rev A never sets this bit. Force it. */		if (lp->version == 0x90)			status |= RS_ODDFRAME;		/*		 * If odd length: packet_len - 3,		 * otherwise packet_len - 4.		 */		data_len = packet_len - ((status & RS_ODDFRAME) ? 3 : 4);		data = skb_put(skb, data_len);		DEBUG_SET(4);		SMC_PULL_DATA(data, packet_len - 2);		DEBUG_CLR(4);		PRINT_PKT(data, packet_len - 2);		dev->last_rx = jiffies;		skb->dev = dev;		skb->protocol = eth_type_trans(skb, dev);		DEBUG_SET(8);		netif_rx(skb);		DEBUG_CLR(8);		lp->stats.rx_packets++;		lp->stats.rx_bytes += data_len;	}done:	SMC_WAIT_MMU_BUSY();	SMC_SET_MMU_CMD( MC_RELEASE );	DEBUG_CLR(1);}/* * This is called to actually send a packet to the chip. * Returns non-zero when successful. */static voidsmc_hardware_send_packet(struct net_device *dev){	struct smc_local *lp = (struct smc_local *)dev->priv;	unsigned long ioaddr = dev->base_addr;	struct sk_buff *skb = lp->saved_skb;	unsigned int packet_no, len;	unsigned char *buf;	PRINTK3("%s: %s\n", dev->name, __FUNCTION__);	if (unlikely(!skb)) {		printk ("%s: In XMIT with no packet to send\n", dev->name);		return;	}	packet_no = SMC_GET_AR();	if (unlikely(packet_no & AR_FAILED)) {		printk("%s: Memory allocation failed.\n", dev->name);		lp->saved_skb = NULL;		lp->stats.tx_errors++;		lp->stats.tx_fifo_errors++;		dev_kfree_skb_any(skb);		return;	}	/* point to the beginning of the packet */	SMC_SET_PN( packet_no );

⌨️ 快捷键说明

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