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