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

📄 smc911x.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * smc911x.c * This is a driver for SMSC's LAN911{5,6,7,8} single-chip Ethernet devices. * * Copyright (C) 2005 Sensoria Corp *	   Derived from the unified SMC91x driver by Nicolas Pitre *	   and the smsc911x.c reference driver by SMSC * * 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: *	 watchdog  = TX watchdog timeout *	 tx_fifo_kb = Size of TX FIFO in KB * * History: *	  04/16/05	Dustin McIntire		 Initial version */static const char version[] =	 "smc911x.c: v1.0 04-16-2005 by Dustin McIntire <dustin@sensoria.com>\n";/* Debugging options */#define ENABLE_SMC_DEBUG_RX		0#define ENABLE_SMC_DEBUG_TX		0#define ENABLE_SMC_DEBUG_DMA		0#define ENABLE_SMC_DEBUG_PKTS		0#define ENABLE_SMC_DEBUG_MISC		0#define ENABLE_SMC_DEBUG_FUNC		0#define SMC_DEBUG_RX		((ENABLE_SMC_DEBUG_RX	? 1 : 0) << 0)#define SMC_DEBUG_TX		((ENABLE_SMC_DEBUG_TX	? 1 : 0) << 1)#define SMC_DEBUG_DMA		((ENABLE_SMC_DEBUG_DMA	? 1 : 0) << 2)#define SMC_DEBUG_PKTS		((ENABLE_SMC_DEBUG_PKTS ? 1 : 0) << 3)#define SMC_DEBUG_MISC		((ENABLE_SMC_DEBUG_MISC ? 1 : 0) << 4)#define SMC_DEBUG_FUNC		((ENABLE_SMC_DEBUG_FUNC ? 1 : 0) << 5)#ifndef SMC_DEBUG#define SMC_DEBUG	 ( SMC_DEBUG_RX	  | \			   SMC_DEBUG_TX	  | \			   SMC_DEBUG_DMA  | \			   SMC_DEBUG_PKTS | \			   SMC_DEBUG_MISC | \			   SMC_DEBUG_FUNC   \			 )#endif#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/interrupt.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/crc32.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/spinlock.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/workqueue.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <asm/io.h>#include "smc911x.h"/* * Transmit timeout, default 5 seconds. */static int watchdog = 5000;module_param(watchdog, int, 0400);MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");static int tx_fifo_kb=8;module_param(tx_fifo_kb, int, 0400);MODULE_PARM_DESC(tx_fifo_kb,"transmit FIFO size in KB (1<x<15)(default=8)");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 "smc911x"/* * Use power-down feature of the chip */#define POWER_DOWN		 1/* store this information for the driver.. */struct smc911x_local {	/*	 * If I have to wait until the DMA is finished and ready to reload a	 * packet, I will store the skbuff here. Then, the DMA will send it	 * out and free it.	 */	struct sk_buff *pending_tx_skb;	/* version/revision of the SMC911x chip */	u16 version;	u16 revision;	/* FIFO sizes */	int tx_fifo_kb;	int tx_fifo_size;	int rx_fifo_size;	int afc_cfg;	/* Contains the current active receive/phy mode */	int ctl_rfduplx;	int ctl_rspeed;	u32 msg_enable;	u32 phy_type;	struct mii_if_info mii;	/* work queue */	struct work_struct phy_configure;	int work_pending;	int tx_throttle;	spinlock_t lock;	struct net_device *netdev;#ifdef SMC_USE_DMA	/* DMA needs the physical address of the chip */	u_long physaddr;	int rxdma;	int txdma;	int rxdma_active;	int txdma_active;	struct sk_buff *current_rx_skb;	struct sk_buff *current_tx_skb;	struct device *dev;#endif};#if SMC_DEBUG > 0#define DBG(n, args...)				 \	do {					 \		if (SMC_DEBUG & (n))		 \			printk(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_PKTS > 0static 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 int  __mask;				\	unsigned long __flags;				\	spin_lock_irqsave(&lp->lock, __flags);		\	__mask = SMC_GET_INT_EN();			\	__mask |= (x);					\	SMC_SET_INT_EN(__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 int  __mask;				\	unsigned long __flags;				\	spin_lock_irqsave(&lp->lock, __flags);		\	__mask = SMC_GET_INT_EN();			\	__mask &= ~(x);					\	SMC_SET_INT_EN(__mask);				\	spin_unlock_irqrestore(&lp->lock, __flags);	\} while (0)/* * this does a soft reset on the device */static void smc911x_reset(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc911x_local *lp = netdev_priv(dev);	unsigned int reg, timeout=0, resets=1;	unsigned long flags;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	/*	 Take out of PM setting first */	if ((SMC_GET_PMT_CTRL() & PMT_CTRL_READY_) == 0) {		/* Write to the bytetest will take out of powerdown */		SMC_SET_BYTE_TEST(0);		timeout=10;		do {			udelay(10);			reg = SMC_GET_PMT_CTRL() & PMT_CTRL_READY_;		} while ( timeout-- && !reg);		if (timeout == 0) {			PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name);			return;		}	}	/* Disable all interrupts */	spin_lock_irqsave(&lp->lock, flags);	SMC_SET_INT_EN(0);	spin_unlock_irqrestore(&lp->lock, flags);	while (resets--) {		SMC_SET_HW_CFG(HW_CFG_SRST_);		timeout=10;		do {			udelay(10);			reg = SMC_GET_HW_CFG();			/* If chip indicates reset timeout then try again */			if (reg & HW_CFG_SRST_TO_) {				PRINTK("%s: chip reset timeout, retrying...\n", dev->name);				resets++;				break;			}		} while ( timeout-- && (reg & HW_CFG_SRST_));	}	if (timeout == 0) {		PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name);		return;	}	/* make sure EEPROM has finished loading before setting GPIO_CFG */	timeout=1000;	while ( timeout-- && (SMC_GET_E2P_CMD() & E2P_CMD_EPC_BUSY_)) {		udelay(10);	}	if (timeout == 0){		PRINTK("%s: smc911x_reset timeout waiting for EEPROM busy\n", dev->name);		return;	}	/* Initialize interrupts */	SMC_SET_INT_EN(0);	SMC_ACK_INT(-1);	/* Reset the FIFO level and flow control settings */	SMC_SET_HW_CFG((lp->tx_fifo_kb & 0xF) << 16);//TODO: Figure out what appropriate pause time is	SMC_SET_FLOW(FLOW_FCPT_ | FLOW_FCEN_);	SMC_SET_AFC_CFG(lp->afc_cfg);	/* Set to LED outputs */	SMC_SET_GPIO_CFG(0x70070000);	/*	 * Deassert IRQ for 1*10us for edge type interrupts	 * and drive IRQ pin push-pull	 */	SMC_SET_IRQ_CFG( (1 << 24) | INT_CFG_IRQ_EN_ | INT_CFG_IRQ_TYPE_ );	/* clear anything saved */	if (lp->pending_tx_skb != NULL) {		dev_kfree_skb (lp->pending_tx_skb);		lp->pending_tx_skb = NULL;		dev->stats.tx_errors++;		dev->stats.tx_aborted_errors++;	}}/* * Enable Interrupts, Receive, and Transmit */static void smc911x_enable(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc911x_local *lp = netdev_priv(dev);	unsigned mask, cfg, cr;	unsigned long flags;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__);	SMC_SET_MAC_ADDR(dev->dev_addr);	/* Enable TX */	cfg = SMC_GET_HW_CFG();	cfg &= HW_CFG_TX_FIF_SZ_ | 0xFFF;	cfg |= HW_CFG_SF_;	SMC_SET_HW_CFG(cfg);	SMC_SET_FIFO_TDA(0xFF);	/* Update TX stats on every 64 packets received or every 1 sec */	SMC_SET_FIFO_TSL(64);	SMC_SET_GPT_CFG(GPT_CFG_TIMER_EN_ | 10000);	spin_lock_irqsave(&lp->lock, flags);	SMC_GET_MAC_CR(cr);	cr |= MAC_CR_TXEN_ | MAC_CR_HBDIS_;	SMC_SET_MAC_CR(cr);	SMC_SET_TX_CFG(TX_CFG_TX_ON_);	spin_unlock_irqrestore(&lp->lock, flags);	/* Add 2 byte padding to start of packets */	SMC_SET_RX_CFG((2<<8) & RX_CFG_RXDOFF_);	/* Turn on receiver and enable RX */	if (cr & MAC_CR_RXEN_)		DBG(SMC_DEBUG_RX, "%s: Receiver already enabled\n", dev->name);	spin_lock_irqsave(&lp->lock, flags);	SMC_SET_MAC_CR( cr | MAC_CR_RXEN_ );	spin_unlock_irqrestore(&lp->lock, flags);	/* Interrupt on every received packet */	SMC_SET_FIFO_RSA(0x01);	SMC_SET_FIFO_RSL(0x00);	/* now, enable interrupts */	mask = INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_ | INT_EN_RSFL_EN_ |		INT_EN_GPT_INT_EN_ | INT_EN_RXDFH_INT_EN_ | INT_EN_RXE_EN_ |		INT_EN_PHY_INT_EN_;	if (IS_REV_A(lp->revision))		mask|=INT_EN_RDFL_EN_;	else {		mask|=INT_EN_RDFO_EN_;	}	SMC_ENABLE_INT(mask);}/* * this puts the device in an inactive state */static void smc911x_shutdown(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	struct smc911x_local *lp = netdev_priv(dev);	unsigned cr;	unsigned long flags;	DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", CARDNAME, __FUNCTION__);	/* Disable IRQ's */	SMC_SET_INT_EN(0);	/* Turn of Rx and TX */	spin_lock_irqsave(&lp->lock, flags);	SMC_GET_MAC_CR(cr);	cr &= ~(MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);	SMC_SET_MAC_CR(cr);	SMC_SET_TX_CFG(TX_CFG_STOP_TX_);	spin_unlock_irqrestore(&lp->lock, flags);}static inline void smc911x_drop_pkt(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	unsigned int fifo_count, timeout, reg;	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", CARDNAME, __FUNCTION__);	fifo_count = SMC_GET_RX_FIFO_INF() & 0xFFFF;	if (fifo_count <= 4) {		/* Manually dump the packet data */		while (fifo_count--)			SMC_GET_RX_FIFO();	} else	 {		/* Fast forward through the bad packet */		SMC_SET_RX_DP_CTRL(RX_DP_CTRL_FFWD_BUSY_);		timeout=50;		do {			udelay(10);			reg = SMC_GET_RX_DP_CTRL() & RX_DP_CTRL_FFWD_BUSY_;		} while ( timeout-- && reg);		if (timeout == 0) {			PRINTK("%s: timeout waiting for RX fast forward\n", dev->name);		}	}}/* * This is the procedure to handle the receipt of a packet. * It should be called after checking for packet presence in * the RX status FIFO.	 It must be called with the spin lock * already held. */static inline void	 smc911x_rcv(struct net_device *dev){	unsigned long ioaddr = dev->base_addr;	unsigned int pkt_len, status;	struct sk_buff *skb;	unsigned char *data;	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n",		dev->name, __FUNCTION__);	status = SMC_GET_RX_STS_FIFO();	DBG(SMC_DEBUG_RX, "%s: Rx pkt len %d status 0x%08x \n",		dev->name, (status & 0x3fff0000) >> 16, status & 0xc000ffff);	pkt_len = (status & RX_STS_PKT_LEN_) >> 16;	if (status & RX_STS_ES_) {		/* Deal with a bad packet */		dev->stats.rx_errors++;		if (status & RX_STS_CRC_ERR_)			dev->stats.rx_crc_errors++;		else {			if (status & RX_STS_LEN_ERR_)				dev->stats.rx_length_errors++;			if (status & RX_STS_MCAST_)				dev->stats.multicast++;		}		/* Remove the bad packet data from the RX FIFO */		smc911x_drop_pkt(dev);	} else {		/* Receive a valid packet */		/* Alloc a buffer with extra room for DMA alignment */		skb=dev_alloc_skb(pkt_len+32);		if (unlikely(skb == NULL)) {			PRINTK( "%s: Low memory, rcvd packet dropped.\n",				dev->name);			dev->stats.rx_dropped++;			smc911x_drop_pkt(dev);			return;		}		/* Align IP header to 32 bits		 * Note that the device is configured to add a 2		 * byte padding to the packet start, so we really		 * want to write to the orignal data pointer */		data = skb->data;		skb_reserve(skb, 2);		skb_put(skb,pkt_len-4);#ifdef SMC_USE_DMA		{		struct smc911x_local *lp = netdev_priv(dev);		unsigned int fifo;		/* Lower the FIFO threshold if possible */		fifo = SMC_GET_FIFO_INT();		if (fifo & 0xFF) fifo--;		DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n",			dev->name, fifo & 0xff);		SMC_SET_FIFO_INT(fifo);		/* Setup RX DMA */		SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_));		lp->rxdma_active = 1;		lp->current_rx_skb = skb;		SMC_PULL_DATA(data, (pkt_len+2+15) & ~15);		/* Packet processing deferred to DMA RX interrupt */		}#else		SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_));		SMC_PULL_DATA(data, pkt_len+2+3);		DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name);		PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);		dev->last_rx = jiffies;		skb->protocol = eth_type_trans(skb, dev);		netif_rx(skb);		dev->stats.rx_packets++;		dev->stats.rx_bytes += pkt_len-4;#endif	}}/* * This is called to actually send a packet to the chip. */static void smc911x_hardware_send_pkt(struct net_device *dev){	struct smc911x_local *lp = netdev_priv(dev);	unsigned long ioaddr = dev->base_addr;	struct sk_buff *skb;	unsigned int cmdA, cmdB, len;	unsigned char *buf;	unsigned long flags;	DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __FUNCTION__);	BUG_ON(lp->pending_tx_skb == NULL);	skb = lp->pending_tx_skb;	lp->pending_tx_skb = NULL;	/* cmdA {25:24] data alignment [20:16] start offset [10:0] buffer length */	/* cmdB {31:16] pkt tag [10:0] length */#ifdef SMC_USE_DMA	/* 16 byte buffer alignment mode */	buf = (char*)((u32)(skb->data) & ~0xF);	len = (skb->len + 0xF + ((u32)skb->data & 0xF)) & ~0xF;	cmdA = (1<<24) | (((u32)skb->data & 0xF)<<16) |			TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |			skb->len;#else	buf = (char*)((u32)skb->data & ~0x3);	len = (skb->len + 3 + ((u32)skb->data & 3)) & ~0x3;	cmdA = (((u32)skb->data & 0x3) << 16) |			TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |			skb->len;#endif	/* tag is packet length so we can use this in stats update later */	cmdB = (skb->len  << 16) | (skb->len & 0x7FF);	DBG(SMC_DEBUG_TX, "%s: TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n",		 dev->name, len, len, buf, cmdA, cmdB);	SMC_SET_TX_FIFO(cmdA);	SMC_SET_TX_FIFO(cmdB);	DBG(SMC_DEBUG_PKTS, "%s: Transmitted packet\n", dev->name);	PRINT_PKT(buf, len <= 64 ? len : 64);	/* Send pkt via PIO or DMA */#ifdef SMC_USE_DMA	lp->current_tx_skb = skb;	SMC_PUSH_DATA(buf, len);	/* DMA complete IRQ will free buffer and set jiffies */#else	SMC_PUSH_DATA(buf, len);	dev->trans_start = jiffies;	dev_kfree_skb(skb);#endif	spin_lock_irqsave(&lp->lock, flags);	if (!lp->tx_throttle) {		netif_wake_queue(dev);	}	spin_unlock_irqrestore(&lp->lock, flags);	SMC_ENABLE_INT(INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_);}/* * Since I am not sure if I will have enough room in the chip's ram * to store the packet, I call this routine which either sends it

⌨️ 快捷键说明

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