📄 gt96100eth.c
字号:
/* * Copyright 2000 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. * */#ifndef __mips__#error This driver only works with MIPS architectures!#endif#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.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 <asm/irq.h>#include <asm/bitops.h>#include <asm/io.h>#include "gt96100eth.h"#ifdef GT96100_DEBUGstatic int gt96100_debug = GT96100_DEBUG;#elsestatic int gt96100_debug = 3;#endif// prototypesstatic void *dmaalloc(size_t size, dma_addr_t * dma_handle);static void dmafree(size_t size, void *vaddr);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(struct net_device *dev, u32 reg);static int write_MII(struct net_device *dev, u32 reg, u16 data);static void dump_MII(struct net_device *dev);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 __init gt96100_probe1(struct net_device *dev, long ioaddr, int irq, int port_num);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 void 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);static char version[] __devinitdata = "gt96100eth.c:0.1 stevel@mvista.com\n";// FIX! Need real Ethernet addressesstatic unsigned char gt96100_station_addr[2][6] __devinitdata = { {0x01, 0x02, 0x03, 0x04, 0x05, 0x06},{0x01, 0x02, 0x03, 0x04, 0x05, 0x07}};#define nibswap(x) ((((x) >> 4) & 0x0f) | (((x) << 4) & 0xf0))#define RUN_AT(x) (jiffies + (x))// For reading/writing 32-bit words from/to DMA memory#define cpu_to_dma32 cpu_to_be32#define dma32_to_cpu be32_to_cpu/* * Base address and interupt of the GT96100 ethernet controllers */static struct { unsigned int port; int irq;} gt96100_iflist[NUM_INTERFACES] = { { GT96100_ETH0_BASE, GT96100_ETHER0_IRQ}, { GT96100_ETH1_BASE, GT96100_ETHER1_IRQ}};/* 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 = KSEG1ADDR(ret); } return ret;}static void dmafree(size_t size, void *vaddr){ vaddr = KSEG0ADDR(vaddr); free_pages((unsigned long) vaddr, get_order(size));}static int read_MII(struct net_device *dev, u32 reg){ struct gt96100_private *gp = (struct gt96100_private *) dev->priv; int timedout = 20; u32 smir = smirOpCode | (gp->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#if 0 current->state = TASK_INTERRUPTIBLE; schedule_timeout(10 * HZ / 10000);#else mdelay(1);#endif if (--timedout == 0) { printk(KERN_ERR "%s: read_MII busy timeout!!\n", dev->name); return -1; } } 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#if 0 current->state = TASK_INTERRUPTIBLE; schedule_timeout(10 * HZ / 10000);#else mdelay(1);#endif if (--timedout == 0) { printk(KERN_ERR "%s: read_MII timeout!!\n", dev->name); return -1; } } return (int) (smir & smirDataMask);}static int write_MII(struct net_device *dev, u32 reg, u16 data){ struct gt96100_private *gp = (struct gt96100_private *) dev->priv; int timedout = 20; u32 smir = (gp->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#if 0 current->state = TASK_INTERRUPTIBLE; schedule_timeout(10 * HZ / 10000);#else mdelay(1);#endif if (--timedout == 0) { printk(KERN_ERR "%s: write_MII busy timeout!!\n", dev->name); return -1; } } GT96100_WRITE(GT96100_ETH_SMI_REG, smir); return 0;}static void dump_MII(struct net_device *dev){ int i, val; for (i = 0; i < 7; i++) { if ((val = read_MII(dev, i)) >= 0) printk("%s: MII Reg %d=%x\n", dev->name, i, val); } for (i = 16; i < 21; i++) { if ((val = read_MII(dev, i)) >= 0) printk("%s: MII Reg %d=%x\n", dev->name, i, val); }}static intgt96100_add_hash_entry(struct net_device *dev, unsigned char *addr){ struct gt96100_private *gp = (struct gt96100_private *) dev->priv; u16 hashResult, stmp; unsigned char ctmp, hash_ea[6]; u32 tblEntry, *tblEntryAddr; int i; 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); } 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 } tblEntryAddr = (u32 *) (&gp->hash_table[((u32) hashResult & 0x7ff) << 3]); for (i = 0; i < HASH_HOP_NUMBER; i++) { if ((*tblEntryAddr & hteValid) && !(*tblEntryAddr & hteSkip)) { // This entry is already occupied, go to next entry tblEntryAddr += 2; } else { memset(tblEntryAddr, 0, 8); tblEntry = hteValid | hteRD; tblEntry |= (u32) addr[5] << 3; tblEntry |= (u32) addr[4] << 11; tblEntry |= (u32) addr[3] << 19; tblEntry |= ((u32) addr[2] & 0x1f) << 27; *(tblEntryAddr + 1) = cpu_to_dma32(tblEntry); tblEntry = ((u32) addr[2] >> 5) & 0x07; tblEntry |= (u32) addr[1] << 3; tblEntry |= (u32) addr[0] << 11; *tblEntryAddr = cpu_to_dma32(tblEntry); break; } } if (i >= HASH_HOP_NUMBER) { printk(KERN_ERR "%s: gt96100_add_hash_entry expired!\n", dev->name); return -1; // Couldn't find an unused entry } return 0;}static void read_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 void update_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; // Tx collisions incremented by ISR, so add in MIB Rx collisions stats->collisions += mib->collision + mib->lateCollision; 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 void abort(struct net_device *dev, u32 abort_bits){ struct gt96100_private *gp = (struct gt96100_private *) dev->priv; int timedout = 100; // wait up to 100 msec for hard stop to complete if (gt96100_debug > 2) printk(KERN_INFO "%s: abort\n", dev->name); // 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); if (gt96100_debug > 2) printk(KERN_INFO "%s: abort: SDMA comm = %x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_SDMA_COMM)); // wait for abort to complete while (GT96100ETH_READ(gp, GT96100_ETH_SDMA_COMM) & abort_bits) { // snooze for 20 msec and check again#if 0 current->state = TASK_INTERRUPTIBLE; schedule_timeout(10 * HZ / 10000);#else mdelay(1);#endif if (--timedout == 0) { printk(KERN_ERR "%s: abort timeout!!\n", dev->name); break; } } if (gt96100_debug > 2) printk(KERN_INFO "%s: abort: timedout=%d\n", dev->name, timedout); spin_unlock(&gp->lock);}static void hard_stop(struct net_device *dev){ struct gt96100_private *gp = (struct gt96100_private *) dev->priv; if (gt96100_debug > 2) printk(KERN_INFO "%s: hard stop\n", dev->name); disable_ether_irq(dev); abort(dev, sdcmrAR | sdcmrAT); // disable port GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG, 0);}static void enable_ether_irq(struct net_device *dev){ struct gt96100_private *gp = (struct gt96100_private *) dev->priv; u32 intMask; // unmask interrupts GT96100ETH_WRITE(gp, GT96100_ETH_INT_MASK, icrRxBuffer | icrTxBufferLow | icrTxEndLow | icrRxError | icrTxErrorLow | icrRxOVR | icrTxUdr | icrRxBufferQ0 | icrRxErrorQ0 | icrMIIPhySTC);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -