gt96100eth.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,572 行 · 第 1/3 页
C
1,572 行
/* * Copyright 2000, 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * stevel@mvista.com or source@mvista.com * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as * published by the Free Software Foundation. * * This program is distributed in the hope 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. * * Ethernet driver for the MIPS GT96100 Advanced Communication Controller. * * Revision history * * 11.11.2001 Moved to 2.4.14, ppopov@mvista.com. Modified driver to add * proper gt96100A support. * 12.05.2001 Moved eth port 0 to irq 3 (mapped to GT_SERINT0 on EV96100A) * in order for both ports to work. Also cleaned up boot * option support (mac address string parsing), fleshed out * gt96100_cleanup_module(), and other general code cleanups * <stevel@mvista.com>. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/in.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/ctype.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/io.h>#define DESC_BE 1#define DESC_DATA_BE 1#define GT96100_DEBUG 2#include "gt96100eth.h"// prototypesstatic void* dmaalloc(size_t size, dma_addr_t *dma_handle);static void dmafree(size_t size, void *vaddr);static void gt96100_delay(int msec);static int gt96100_add_hash_entry(struct net_device *dev, unsigned char* addr);static void read_mib_counters(struct gt96100_private *gp);static int read_MII(int phy_addr, u32 reg);static int write_MII(int phy_addr, u32 reg, u16 data);static int gt96100_init_module(void);static void gt96100_cleanup_module(void);static void dump_MII(int dbg_lvl, struct net_device *dev);static void dump_tx_desc(int dbg_lvl, struct net_device *dev, int i);static void dump_rx_desc(int dbg_lvl, struct net_device *dev, int i);static void dump_skb(int dbg_lvl, struct net_device *dev, struct sk_buff *skb);static void dump_hw_addr(int dbg_lvl, struct net_device *dev, const char* pfx, unsigned char* addr_str);static void update_stats(struct gt96100_private *gp);static void abort(struct net_device *dev, u32 abort_bits);static void hard_stop(struct net_device *dev);static void enable_ether_irq(struct net_device *dev);static void disable_ether_irq(struct net_device *dev);static int gt96100_probe1(struct pci_dev *pci, int port_num);static void reset_tx(struct net_device *dev);static void reset_rx(struct net_device *dev);static int gt96100_check_tx_consistent(struct gt96100_private *gp);static int gt96100_init(struct net_device *dev);static int gt96100_open(struct net_device *dev);static int gt96100_close(struct net_device *dev);static int gt96100_tx(struct sk_buff *skb, struct net_device *dev);static int gt96100_rx(struct net_device *dev, u32 status);static irqreturn_t gt96100_interrupt(int irq, void *dev_id, struct pt_regs *regs);static void gt96100_tx_timeout(struct net_device *dev);static void gt96100_set_rx_mode(struct net_device *dev);static struct net_device_stats* gt96100_get_stats(struct net_device *dev);extern char * __init prom_getcmdline(void);static int max_interrupt_work = 32;#define nibswap(x) ((((x) >> 4) & 0x0f) | (((x) << 4) & 0xf0))#define RUN_AT(x) (jiffies + (x))// For reading/writing 32-bit words and half-words from/to DMA memory#ifdef DESC_BE#define cpu_to_dma32 cpu_to_be32#define dma32_to_cpu be32_to_cpu#define cpu_to_dma16 cpu_to_be16#define dma16_to_cpu be16_to_cpu#else#define cpu_to_dma32 cpu_to_le32#define dma32_to_cpu le32_to_cpu#define cpu_to_dma16 cpu_to_le16#define dma16_to_cpu le16_to_cpu#endifstatic char mac0[18] = "00.02.03.04.05.06";static char mac1[18] = "00.01.02.03.04.05";MODULE_PARM(mac0, "c18");MODULE_PARM(mac1, "c18");MODULE_PARM_DESC(mac0, "MAC address for GT96100 ethernet port 0");MODULE_PARM_DESC(mac1, "MAC address for GT96100 ethernet port 1");/* * Info for the GT96100 ethernet controller's ports. */static struct gt96100_if_t { struct net_device *dev; unsigned int iobase; // IO Base address of this port int irq; // IRQ number of this port char *mac_str;} gt96100_iflist[NUM_INTERFACES] = { { NULL, GT96100_ETH0_BASE, GT96100_ETHER0_IRQ, mac0 }, { NULL, GT96100_ETH1_BASE, GT96100_ETHER1_IRQ, mac1 }};static inline const char*chip_name(int chip_rev){ switch (chip_rev) { case REV_GT96100: return "GT96100"; case REV_GT96100A_1: case REV_GT96100A: return "GT96100A"; default: return "Unknown GT96100"; }}/* DMA memory allocation, derived from pci_alloc_consistent.*/static void * dmaalloc(size_t size, dma_addr_t *dma_handle){ void *ret; ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, get_order(size)); if (ret != NULL) { dma_cache_inv((unsigned long)ret, size); if (dma_handle != NULL) *dma_handle = virt_to_phys(ret); /* bump virtual address up to non-cached area */ ret = (void*)KSEG1ADDR(ret); } return ret;}static void dmafree(size_t size, void *vaddr){ vaddr = (void*)KSEG0ADDR(vaddr); free_pages((unsigned long)vaddr, get_order(size));}static void gt96100_delay(int ms){ if (in_interrupt()) return; else { current->state = TASK_INTERRUPTIBLE; schedule_timeout(ms*HZ/1000); }}static intparse_mac_addr(struct net_device *dev, char* macstr){ int i, j; unsigned char result, value; for (i=0; i<6; i++) { result = 0; if (i != 5 && *(macstr+2) != '.') { err(__FILE__ "invalid mac address format: %d %c\n", i, *(macstr+2)); return -EINVAL; } for (j=0; j<2; j++) { if (isxdigit(*macstr) && (value = isdigit(*macstr) ? *macstr-'0' : toupper(*macstr)-'A'+10) < 16) { result = result*16 + value; macstr++; } else { err(__FILE__ "invalid mac address " "character: %c\n", *macstr); return -EINVAL; } } macstr++; // step over '.' dev->dev_addr[i] = result; } return 0;}static intread_MII(int phy_addr, u32 reg){ int timedout = 20; u32 smir = smirOpCode | (phy_addr << smirPhyAdBit) | (reg << smirRegAdBit); // wait for last operation to complete while (GT96100_READ(GT96100_ETH_SMI_REG) & smirBusy) { // snooze for 1 msec and check again gt96100_delay(1); if (--timedout == 0) { printk(KERN_ERR "%s: busy timeout!!\n", __FUNCTION__); return -ENODEV; } } GT96100_WRITE(GT96100_ETH_SMI_REG, smir); timedout = 20; // wait for read to complete while (!((smir = GT96100_READ(GT96100_ETH_SMI_REG)) & smirReadValid)) { // snooze for 1 msec and check again gt96100_delay(1); if (--timedout == 0) { printk(KERN_ERR "%s: timeout!!\n", __FUNCTION__); return -ENODEV; } } return (int)(smir & smirDataMask);}static voiddump_tx_desc(int dbg_lvl, struct net_device *dev, int i){ struct gt96100_private *gp = netdev_priv(dev); gt96100_td_t *td = &gp->tx_ring[i]; dbg(dbg_lvl, "Tx descriptor at 0x%08lx:\n", virt_to_phys(td)); dbg(dbg_lvl, " cmdstat=%04x, byte_cnt=%04x, buff_ptr=%04x, next=%04x\n", dma32_to_cpu(td->cmdstat), dma16_to_cpu(td->byte_cnt), dma32_to_cpu(td->buff_ptr), dma32_to_cpu(td->next));}static voiddump_rx_desc(int dbg_lvl, struct net_device *dev, int i){ struct gt96100_private *gp = netdev_priv(dev); gt96100_rd_t *rd = &gp->rx_ring[i]; dbg(dbg_lvl, "Rx descriptor at 0x%08lx:\n", virt_to_phys(rd)); dbg(dbg_lvl, " cmdstat=%04x, buff_sz=%04x, byte_cnt=%04x, " "buff_ptr=%04x, next=%04x\n", dma32_to_cpu(rd->cmdstat), dma16_to_cpu(rd->buff_sz), dma16_to_cpu(rd->byte_cnt), dma32_to_cpu(rd->buff_ptr), dma32_to_cpu(rd->next));}static intwrite_MII(int phy_addr, u32 reg, u16 data){ int timedout = 20; u32 smir = (phy_addr << smirPhyAdBit) | (reg << smirRegAdBit) | data; // wait for last operation to complete while (GT96100_READ(GT96100_ETH_SMI_REG) & smirBusy) { // snooze for 1 msec and check again gt96100_delay(1); if (--timedout == 0) { printk(KERN_ERR "%s: busy timeout!!\n", __FUNCTION__); return -1; } } GT96100_WRITE(GT96100_ETH_SMI_REG, smir); return 0;}static voiddump_MII(int dbg_lvl, struct net_device *dev){ int i, val; struct gt96100_private *gp = netdev_priv(dev); if (dbg_lvl <= GT96100_DEBUG) { for (i=0; i<7; i++) { if ((val = read_MII(gp->phy_addr, i)) >= 0) printk("MII Reg %d=%x\n", i, val); } for (i=16; i<21; i++) { if ((val = read_MII(gp->phy_addr, i)) >= 0) printk("MII Reg %d=%x\n", i, val); } }}static voiddump_hw_addr(int dbg_lvl, struct net_device *dev, const char* pfx, unsigned char* addr_str){ int i; char buf[100], octet[5]; if (dbg_lvl <= GT96100_DEBUG) { strcpy(buf, pfx); for (i = 0; i < 6; i++) { sprintf(octet, "%2.2x%s", addr_str[i], i<5 ? ":" : "\n"); strcat(buf, octet); } info("%s", buf); }}static voiddump_skb(int dbg_lvl, struct net_device *dev, struct sk_buff *skb){ int i; unsigned char* skbdata; if (dbg_lvl <= GT96100_DEBUG) { dbg(dbg_lvl, "%s: skb=%p, skb->data=%p, skb->len=%d\n", __FUNCTION__, skb, skb->data, skb->len); skbdata = (unsigned char*)KSEG1ADDR(skb->data); for (i=0; i<skb->len; i++) { if (!(i % 16)) printk(KERN_DEBUG "\n %3.3x: %2.2x,", i, skbdata[i]); else printk(KERN_DEBUG "%2.2x,", skbdata[i]); } printk(KERN_DEBUG "\n"); }}static intgt96100_add_hash_entry(struct net_device *dev, unsigned char* addr){ struct gt96100_private *gp = netdev_priv(dev); //u16 hashResult, stmp; //unsigned char ctmp, hash_ea[6]; u32 tblEntry1, tblEntry0, *tblEntryAddr; int i; tblEntry1 = hteValid | hteRD; tblEntry1 |= (u32)addr[5] << 3; tblEntry1 |= (u32)addr[4] << 11; tblEntry1 |= (u32)addr[3] << 19; tblEntry1 |= ((u32)addr[2] & 0x1f) << 27; dbg(3, "%s: tblEntry1=%x\n", __FUNCTION__, tblEntry1); tblEntry0 = ((u32)addr[2] >> 5) & 0x07; tblEntry0 |= (u32)addr[1] << 3; tblEntry0 |= (u32)addr[0] << 11; dbg(3, "%s: tblEntry0=%x\n", __FUNCTION__, tblEntry0);#if 0 for (i=0; i<6; i++) { // nibble swap ctmp = nibswap(addr[i]); // invert every nibble hash_ea[i] = ((ctmp&1)<<3) | ((ctmp&8)>>3) | ((ctmp&2)<<1) | ((ctmp&4)>>1); hash_ea[i] |= ((ctmp&0x10)<<3) | ((ctmp&0x80)>>3) | ((ctmp&0x20)<<1) | ((ctmp&0x40)>>1); } dump_hw_addr(3, dev, "%s: nib swap/invt addr=", __FUNCTION__, hash_ea); if (gp->hash_mode == 0) { hashResult = ((u16)hash_ea[0] & 0xfc) << 7; stmp = ((u16)hash_ea[0] & 0x03) | (((u16)hash_ea[1] & 0x7f) << 2); stmp ^= (((u16)hash_ea[1] >> 7) & 0x01) | ((u16)hash_ea[2] << 1); stmp ^= (u16)hash_ea[3] | (((u16)hash_ea[4] & 1) << 8); hashResult |= stmp; } else { return -1; // don't support hash mode 1 } dbg(3, "%s: hashResult=%x\n", __FUNCTION__, hashResult); tblEntryAddr = (u32 *)(&gp->hash_table[((u32)hashResult & 0x7ff) << 3]); dbg(3, "%s: tblEntryAddr=%p\n", tblEntryAddr, __FUNCTION__); for (i=0; i<HASH_HOP_NUMBER; i++) { if ((*tblEntryAddr & hteValid) && !(*tblEntryAddr & hteSkip)) { // This entry is already occupied, go to next entry tblEntryAddr += 2; dbg(3, "%s: skipping to %p\n", __FUNCTION__, tblEntryAddr); } else { memset(tblEntryAddr, 0, 8); tblEntryAddr[1] = cpu_to_dma32(tblEntry1); tblEntryAddr[0] = cpu_to_dma32(tblEntry0); break; } } if (i >= HASH_HOP_NUMBER) { err("%s: expired!\n", __FUNCTION__); return -1; // Couldn't find an unused entry }#else tblEntryAddr = (u32 *)gp->hash_table; for (i=0; i<RX_HASH_TABLE_SIZE/4; i+=2) { tblEntryAddr[i+1] = cpu_to_dma32(tblEntry1); tblEntryAddr[i] = cpu_to_dma32(tblEntry0); }#endif return 0;}static voidread_mib_counters(struct gt96100_private *gp){ u32* mib_regs = (u32*)&gp->mib; int i; for (i=0; i<sizeof(mib_counters_t)/sizeof(u32); i++) mib_regs[i] = GT96100ETH_READ(gp, GT96100_ETH_MIB_COUNT_BASE + i*sizeof(u32));}static voidupdate_stats(struct gt96100_private *gp){ mib_counters_t *mib = &gp->mib; struct net_device_stats *stats = &gp->stats; read_mib_counters(gp); stats->rx_packets = mib->totalFramesReceived; stats->tx_packets = mib->framesSent; stats->rx_bytes = mib->totalByteReceived; stats->tx_bytes = mib->byteSent; stats->rx_errors = mib->totalFramesReceived - mib->framesReceived; //the tx error counters are incremented by the ISR //rx_dropped incremented by gt96100_rx //tx_dropped incremented by gt96100_tx stats->multicast = mib->multicastFramesReceived; // collisions incremented by gt96100_tx_complete stats->rx_length_errors = mib->oversizeFrames + mib->fragments; // The RxError condition means the Rx DMA encountered a // CPU owned descriptor, which, if things are working as // they should, means the Rx ring has overflowed. stats->rx_over_errors = mib->macRxError; stats->rx_crc_errors = mib->cRCError;}static voidabort(struct net_device *dev, u32 abort_bits){ struct gt96100_private *gp = netdev_priv(dev); int timedout = 100; // wait up to 100 msec for hard stop to complete dbg(3, "%s\n", __FUNCTION__); // Return if neither Rx or Tx abort bits are set if (!(abort_bits & (sdcmrAR | sdcmrAT))) return; // make sure only the Rx/Tx abort bits are set abort_bits &= (sdcmrAR | sdcmrAT); spin_lock(&gp->lock); // abort any Rx/Tx DMA immediately GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, abort_bits);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?