📄 smc91x.c
字号:
/*------------------------------------------------------------------------ . 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 + -