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

📄 smc91x.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 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> *	Russell King <rmk@arm.linux.org.uk> * * 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 *   09/15/04  Hayato Fujiwara    - Add m32r support. *                                - Modify for SMP kernel; Change spin-locked *                                  regions. */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>#include <asm/io.h>#include <asm/mach-types.h>#include <asm/irq.h>#if defined(CONFIG_ARCH_OMAP)#include <asm/arch/gpio.h> #include <asm/arch/fpga.h>#include <asm/arch/mux.h>#endif#if defined(CONFIG_ARCH_OMAP24XX)#include <asm/arch/gpio.h>#endif#include "smc91x.h"#if defined(CONFIG_MACH_OMAP_H4)void h4reset (void){	#define LAN_RESET_REGISTER (H4_DEBUG_FPGA_VA_BASE+0x1c)	int cnt = 20;	__raw_writew(0x0, LAN_RESET_REGISTER);	do {		__raw_writew(0x1, LAN_RESET_REGISTER);		udelay (100);		if(cnt == 0)			goto h4reset_err_out;		--cnt;	} while (__raw_readw(LAN_RESET_REGISTER) != 0x1);	cnt = 20;	do {		__raw_writew(0x0, LAN_RESET_REGISTER);		udelay (100);		if(cnt == 0)			goto h4reset_err_out;		--cnt;	} while (__raw_readw(LAN_RESET_REGISTER) != 0x0000);	udelay (400);h4reset_err_out:	return;}#endif#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};#ifndef SMC_IOADDR# define SMC_IOADDR		-1#endifstatic unsigned long io = SMC_IOADDR;module_param(io, ulong, 0400);MODULE_PARM_DESC(io, "I/O base address");#ifndef SMC_IRQ# define SMC_IRQ		-1#endifstatic int irq = SMC_IRQ;module_param(irq, int, 0400);MODULE_PARM_DESC(irq, "IRQ number");#endif  /* CONFIG_ISA */#ifndef SMC_NOWAIT# define SMC_NOWAIT		0#endifstatic int nowait = SMC_NOWAIT;module_param(nowait, int, 0400);MODULE_PARM_DESC(nowait, "set to 1 for no wait state");/* * Transmit timeout, default 5 seconds. */static int watchdog = 5000;module_param(watchdog, int, 0400);MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");MODULE_LICENSE("GPL");/* * 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. */#define THROTTLE_TX_PKTS	0/* * 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;#ifdef SMC_USE_PXA_DMA	/* DMA needs the physical address of the chip */	u_long physaddr;#endif};#if SMC_DEBUG > 0#define DBG(n, args...)					\	do {						\		if (SMC_DEBUG >= (n))			\			printk(KERN_DEBUG args);	\	} while (0)#define PRINTK(args...)   printk(args)#else#define DBG(n, 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 char mask;						\	mask = SMC_GET_INT_MASK();					\	mask |= (x);							\	SMC_SET_INT_MASK(mask);						\} while (0)/* this disables an interrupt from the interrupt mask register */#define SMC_DISABLE_INT(x) do {						\	unsigned char mask;						\	mask = SMC_GET_INT_MASK();					\	mask &= ~(x);							\	SMC_SET_INT_MASK(mask);						\} while (0)/* * Wait while MMU is busy.  This is usually in the order of a few nanosecs * if at all, but let's avoid deadlocking the system if the hardware * decides to go south. */#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;					\			}						\			cpu_relax();					\		}							\	}								\} while (0)/* * this does a soft reset on the device */static void smc_reset(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	unsigned int ctl, cfg;	DBG(2, "%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);	cfg = 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)		cfg |= CONFIG_NO_WAIT;	/*	 * Release from possible power-down state	 * Configuration register is not affected by Soft Reset	 */	cfg |= CONFIG_EPH_POWER_EN;	SMC_SET_CONFIG(cfg);	/* this should pause enough for the chip to be happy */	/*	 * elaborate?  What does the chip _need_? --jgarzik	 *	 * This seems to be undocumented, but something the original	 * driver(s) have always done.  Suspect undocumented timing	 * info/determined empirically. --rmk	 */	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 void smc_enable(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc_local *lp = netdev_priv(dev);	int mask;	DBG(2, "%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 void smc_shutdown(unsigned long ioaddr){	DBG(2, "%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 = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	unsigned int packet_number, status, packet_len;	DBG(3, "%s: %s\n", dev->name, __FUNCTION__);	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 */	DBG(2, "%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.		 */		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;		}		/* 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);		SMC_PULL_DATA(data, packet_len - 2);		PRINT_PKT(data, packet_len - 2);		dev->last_rx = jiffies;		skb->dev = dev;		skb->protocol = eth_type_trans(skb, dev);		netif_rx(skb);		lp->stats.rx_packets++;		lp->stats.rx_bytes += data_len;	}done:	SMC_WAIT_MMU_BUSY();	SMC_SET_MMU_CMD(MC_RELEASE);}/* * This is called to actually send a packet to the chip. * Returns non-zero when successful. */static void smc_hardware_send_packet(struct net_device *dev){	struct smc_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	struct sk_buff *skb = lp->saved_skb;	unsigned int packet_no, len;	unsigned char *buf;	DBG(3, "%s: %s\n", dev->name, __FUNCTION__);	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++;		if (!skb){			dev_kfree_skb_any(skb);		} else {			printk (KERN_ERR "Trying to free NULL skb\n");		}		return;	}	/* point to the beginning of the packet */	SMC_SET_PN(packet_no);	SMC_SET_PTR(PTR_AUTOINC);

⌨️ 快捷键说明

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