/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gt96100eth.h" #ifdef GT96100_DEBUG static int gt96100_debug = GT96100_DEBUG; #else static int gt96100_debug = 3; #endif // prototypes static 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 addresses static 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 int gt96100_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); // now route ethernet interrupts to GT Int0 (eth0 and eth1 will be // sharing it). // FIX! The kernel's irq code should do this intMask = GT96100_READ(GT96100_INT0_HIGH_MASK); intMask |= 1 << gp->port_num; GT96100_WRITE(GT96100_INT0_HIGH_MASK, intMask); } static void disable_ether_irq(struct net_device *dev) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; u32 intMask; // FIX! The kernel's irq code should do this intMask = GT96100_READ(GT96100_INT0_HIGH_MASK); intMask &= ~(1 << gp->port_num); GT96100_WRITE(GT96100_INT0_HIGH_MASK, intMask); GT96100ETH_WRITE(gp, GT96100_ETH_INT_MASK, 0); } /* * Probe for a GT96100 ethernet controller. */ int __init gt96100_probe(struct net_device *dev) { unsigned int base_addr = dev ? dev->base_addr : 0; int i; #ifndef CONFIG_MIPS_GT96100ETH return -ENODEV; #endif if (gt96100_debug > 2) printk(KERN_INFO "%s: gt96100_probe\n", dev->name); if (base_addr >= KSEG0) /* Check a single specified location. */ return gt96100_probe1(dev, base_addr, dev->irq, 0); else if (base_addr != 0) /* Don't probe at all. */ return -ENXIO; // for (i = 0; i= 0; i--) { int base_addr = gt96100_iflist[i].port; #if 0 if (check_region(base_addr, GT96100_ETH_IO_SIZE)) { printk(KERN_ERR "%s: gt96100_probe: ioaddr 0x%lx taken?\n", dev->name, base_addr); continue; } #endif if (gt96100_probe1 (dev, base_addr, gt96100_iflist[i].irq, i) == 0) return 0; } return -ENODEV; } static int __init gt96100_probe1(struct net_device *dev, long ioaddr, int irq, int port_num) { static unsigned version_printed = 0; struct gt96100_private *gp = NULL; int i, retval; u32 cpuConfig; // FIX! probe for GT96100 by reading a suitable register if (gt96100_debug > 2) printk(KERN_INFO "gt96100_probe1: ioaddr 0x%lx, irq %d\n", ioaddr, irq); request_region(ioaddr, GT96100_ETH_IO_SIZE, "GT96100ETH"); cpuConfig = GT96100_READ(GT96100_CPU_INTERF_CONFIG); if (cpuConfig & (1 << 12)) { printk(KERN_ERR "gt96100_probe1: must be in Big Endian mode!\n"); retval = -ENODEV; goto free_region; } if (gt96100_debug > 2) printk(KERN_INFO "gt96100_probe1: chip in Big Endian mode - cool\n"); /* Allocate a new 'dev' if needed. */ if (dev == NULL) dev = init_etherdev(0, sizeof(struct gt96100_private)); if (gt96100_debug && version_printed++ == 0) printk(version); if (irq < 0) { printk(KERN_ERR "gt96100_probe1: irq unknown - probing not supported\n"); retval = -ENODEV; goto free_region; } printk(KERN_INFO "%s: GT-96100 ethernet found at 0x%lx, irq %d\n", dev->name, ioaddr, irq); /* private struct aligned and zeroed by init_etherdev */ /* Fill in the 'dev' fields. */ dev->base_addr = ioaddr; dev->irq = irq; memcpy(dev->dev_addr, gt96100_station_addr[port_num], sizeof(dev->dev_addr)); printk(KERN_INFO "%s: HW Address ", dev->name); for (i = 0; i < sizeof(dev->dev_addr); i++) { printk("%2.2x", dev->dev_addr[i]); printk(i < 5 ? ":" : "\n"); } /* Initialize our private structure. */ if (dev->priv == NULL) { gp = (struct gt96100_private *) kmalloc(sizeof(*gp), GFP_KERNEL); if (gp == NULL) { retval = -ENOMEM; goto free_region; } dev->priv = gp; } gp = dev->priv; memset(gp, 0, sizeof(*gp)); // clear it gp->port_num = port_num; gp->io_size = GT96100_ETH_IO_SIZE; gp->port_offset = port_num * GT96100_ETH_IO_SIZE; gp->phy_addr = port_num + 1; if (gt96100_debug > 2) printk(KERN_INFO "%s: gt96100_probe1, port %d\n", dev->name, gp->port_num); // Allocate Rx and Tx descriptor rings if (gp->rx_ring == NULL) { // All descriptors in ring must be 16-byte aligned gp->rx_ring = dmaalloc(sizeof(gt96100_rd_t) * RX_RING_SIZE + sizeof(gt96100_td_t) * TX_RING_SIZE, &gp->rx_ring_dma); if (gp->rx_ring == NULL) { retval = -ENOMEM; goto free_region; } gp->tx_ring = (gt96100_td_t *) (gp->rx_ring + RX_RING_SIZE); gp->tx_ring_dma = gp->rx_ring_dma + sizeof(gt96100_rd_t) * RX_RING_SIZE; } if (gt96100_debug > 2) printk(KERN_INFO "%s: gt96100_probe1, rx_ring=%p, tx_ring=%p\n", dev->name, gp->rx_ring, gp->tx_ring); // Allocate Rx Hash Table if (gp->hash_table == NULL) { gp->hash_table = (char *) dmaalloc(RX_HASH_TABLE_SIZE, &gp->hash_table_dma); if (gp->hash_table == NULL) { dmafree(sizeof(gt96100_rd_t) * RX_RING_SIZE + sizeof(gt96100_td_t) * TX_RING_SIZE, gp->rx_ring); retval = -ENOMEM; goto free_region; } } if (gt96100_debug > 2) printk(KERN_INFO "%s: gt96100_probe1, hash=%p\n", dev->name, gp->hash_table); spin_lock_init(&gp->lock); dev->open = gt96100_open; dev->hard_start_xmit = gt96100_tx; dev->stop = gt96100_close; dev->get_stats = gt96100_get_stats; //dev->do_ioctl = gt96100_ioctl; dev->set_multicast_list = gt96100_set_rx_mode; dev->tx_timeout = gt96100_tx_timeout; dev->watchdog_timeo = GT96100ETH_TX_TIMEOUT; /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); return 0; free_region: release_region(ioaddr, gp->io_size); unregister_netdev(dev); if (dev->priv != NULL) kfree(dev->priv); kfree(dev); printk(KERN_ERR "%s: gt96100_probe1 failed. Returns %d\n", dev->name, retval); return retval; } static int gt96100_init(struct net_device *dev) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; unsigned long flags; u32 phyAD, ciu; int i; if (gt96100_debug > 2) printk("%s: gt96100_init: dev=%p\n", dev->name, dev); // Stop and disable Port hard_stop(dev); spin_lock_irqsave(&gp->lock, flags); // First things first, set-up hash table memset(gp->hash_table, 0, RX_HASH_TABLE_SIZE); // clear it gp->hash_mode = 0; // Add a single entry to hash table - our ethernet address gt96100_add_hash_entry(dev, dev->dev_addr); // Set-up DMA ptr to hash table GT96100ETH_WRITE(gp, GT96100_ETH_HASH_TBL_PTR, gp->hash_table_dma); if (gt96100_debug > 2) printk("%s: gt96100_init: Hash Tbl Ptr=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_HASH_TBL_PTR)); // Setup Tx descriptor ring for (i = 0; i < TX_RING_SIZE; i++) { gp->tx_ring[i].cmdstat = 0; // CPU owns gp->tx_ring[i].byte_cnt = 0; gp->tx_ring[i].buff_ptr = 0; gp->tx_ring[i].next = cpu_to_dma32(gp->tx_ring_dma + sizeof(gt96100_td_t) * (i + 1)); } /* Wrap the ring. */ gp->tx_ring[i - 1].next = cpu_to_dma32(gp->tx_ring_dma); // setup only the lowest priority TxCDP reg GT96100ETH_WRITE(gp, GT96100_ETH_CURR_TX_DESC_PTR0, gp->tx_ring_dma); GT96100ETH_WRITE(gp, GT96100_ETH_CURR_TX_DESC_PTR1, 0); if (gt96100_debug > 2) printk("%s: gt96100_init: Curr Tx Desc Ptr0=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_CURR_TX_DESC_PTR0)); // Setup Rx descriptor ring for (i = 0; i < RX_RING_SIZE; i++) { dma_addr_t rx_buff_dma; gp->rx_ring[i].next = cpu_to_dma32(gp->rx_ring_dma + sizeof(gt96100_rd_t) * (i + 1)); if (gp->rx_buff[i] == NULL) gp->rx_buff[i] = dmaalloc(PKT_BUF_SZ, &rx_buff_dma); else rx_buff_dma = virt_to_phys(gp->rx_buff[i]); if (gp->rx_buff[i] == NULL) break; gp->rx_ring[i].buff_ptr = cpu_to_dma32(rx_buff_dma); gp->rx_ring[i].buff_cnt_sz = cpu_to_dma32(PKT_BUF_SZ << rdBuffSzBit); // Give ownership to device, enable interrupt gp->rx_ring[i].cmdstat = cpu_to_dma32((u32) (rxOwn | rxEI)); } if (i != RX_RING_SIZE) { int j; for (j = 0; j < RX_RING_SIZE; j++) { if (gp->rx_buff[j]) { dmafree(PKT_BUF_SZ, gp->rx_buff[j]); gp->rx_buff[j] = NULL; } } printk(KERN_ERR "%s: Rx ring allocation failed.\n", dev->name); spin_unlock_irqrestore(&gp->lock, flags); return -ENOMEM; } /* Wrap the ring. */ gp->rx_ring[i - 1].next = cpu_to_dma32(gp->rx_ring_dma); // Set our MII PHY device address phyAD = GT96100_READ(GT96100_ETH_PHY_ADDR_REG); phyAD &= ~(0x1f << (gp->port_num * 5)); phyAD |= gp->phy_addr << (gp->port_num * 5); GT96100_WRITE(GT96100_ETH_PHY_ADDR_REG, phyAD); if (gt96100_debug > 2) printk("%s: gt96100_init: PhyAD=%x\n", dev->name, GT96100_READ(GT96100_ETH_PHY_ADDR_REG)); // Clear all the RxFDP and RXCDP regs... for (i = 0; i < 4; i++) { GT96100ETH_WRITE(gp, GT96100_ETH_1ST_RX_DESC_PTR0 + i * 4, 0); GT96100ETH_WRITE(gp, GT96100_ETH_CURR_RX_DESC_PTR0 + i * 4, 0); } // and setup only the lowest priority RxFDP and RxCDP regs GT96100ETH_WRITE(gp, GT96100_ETH_1ST_RX_DESC_PTR0, gp->rx_ring_dma); GT96100ETH_WRITE(gp, GT96100_ETH_CURR_RX_DESC_PTR0, gp->rx_ring_dma); if (gt96100_debug > 2) printk("%s: gt96100_init: 1st/Curr Rx Desc Ptr0=%x/%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_1ST_RX_DESC_PTR0), GT96100ETH_READ(gp, GT96100_ETH_CURR_RX_DESC_PTR0)); // init Rx/Tx indeces and pkt counters gp->rx_next_out = gp->tx_next_in = gp->tx_next_out = 0; gp->tx_count = 0; // setup DMA // FIX! this should be done by Kernel setup code ciu = GT96100_READ(GT96100_CIU_ARBITER_CONFIG); ciu |= (0x0c << (gp->port_num * 2)); // set Ether DMA req priority to high // FIX! setting the following bit causes the EV96100 board to hang!!! //ciu |= (1 << (24+gp->port_num)); // pull Ethernet port out of Reset??? // FIX! endian mode??? ciu &= ~(1 << 31); // set desc endianess to Big GT96100_WRITE(GT96100_CIU_ARBITER_CONFIG, ciu); if (gt96100_debug > 2) printk("%s: gt96100_init: CIU Config=%x/%x\n", dev->name, ciu, GT96100_READ(GT96100_CIU_ARBITER_CONFIG)); // We want the Rx/Tx DMA to write/read data to/from memory in // Big Endian mode. Also set DMA Burst Size to 8 64Bit words. // FIX! endian mode??? GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_CONFIG, //sdcrBLMR | sdcrBLMT | (0xf << sdcrRCBit) | sdcrRIFB | (3 << sdcrBSZBit)); if (gt96100_debug > 2) printk("%s: gt96100_init: SDMA Config=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_SDMA_CONFIG)); // start Rx DMA GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, sdcmrERD); if (gt96100_debug > 2) printk("%s: gt96100_init: SDMA Comm=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_SDMA_COMM)); // enable interrupts enable_ether_irq(dev); /* * Disable all Type-of-Service queueing. All Rx packets will be * treated normally and will be sent to the lowest priority * queue. * * Disable flow-control for now. FIX! support flow control? */ // clear all the MIB ctr regs // Enable reg clear on read. FIX! desc of this bit is inconsistent // in the GT-96100A datasheet. GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG_EXT, pcxrFCTL | pcxrFCTLen | pcxrFLP); read_mib_counters(gp); GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG_EXT, pcxrFCTL | pcxrFCTLen | pcxrFLP | pcxrMIBclrMode); if (gt96100_debug > 2) printk("%s: gt96100_init: Port Config Ext=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_PORT_CONFIG_EXT)); // enable this port (set hash size to 1/2K) GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG, pcrEN | pcrHS); if (gt96100_debug > 2) printk("%s: gt96100_init: Port Config=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_PORT_CONFIG)); // we should now be receiving frames if (gt96100_debug > 2) dump_MII(dev); spin_unlock_irqrestore(&gp->lock, flags); return 0; } static int gt96100_open(struct net_device *dev) { int retval; MOD_INC_USE_COUNT; if (gt96100_debug > 2) printk("%s: gt96100_open: dev=%p\n", dev->name, dev); if ((retval = request_irq(dev->irq, >96100_interrupt, SA_SHIRQ, dev->name, dev))) { printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq); MOD_DEC_USE_COUNT; return retval; } // Initialize and startup the GT-96100 ethernet port if ((retval = gt96100_init(dev))) { printk(KERN_ERR "%s: error in gt96100_init\n", dev->name); free_irq(dev->irq, dev); MOD_DEC_USE_COUNT; return retval; } netif_start_queue(dev); if (gt96100_debug > 2) printk("%s: gt96100_open: Initialization done.\n", dev->name); return 0; } static int gt96100_close(struct net_device *dev) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; int i; if (gt96100_debug > 2) printk("%s: gt96100_close: dev=%p\n", dev->name, dev); // stop the device if (netif_device_present(dev)) { netif_stop_queue(dev); hard_stop(dev); } // free the Rx DMA buffers for (i = 0; i < RX_RING_SIZE; i++) { if (gp->rx_buff[i]) { dmafree(PKT_BUF_SZ, gp->rx_buff[i]); gp->rx_buff[i] = NULL; } } free_irq(dev->irq, dev); MOD_DEC_USE_COUNT; return 0; } static int gt96100_tx(struct sk_buff *skb, struct net_device *dev) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; unsigned long flags; int nextIn; if (gt96100_debug > 2) printk("%s: gt96100_tx: skb->len=%d, skb->data=%p\n", dev->name, skb->len, skb->data); spin_lock_irqsave(&gp->lock, flags); if (gp->tx_count >= TX_RING_SIZE) { printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n", dev->name); gp->stats.tx_dropped++; spin_unlock_irqrestore(&gp->lock, flags); return 1; } // Prepare the Descriptor at tx_next_in nextIn = gp->tx_next_in; if (dma32_to_cpu(gp->tx_ring[nextIn].cmdstat) & txOwn) { printk(KERN_ERR "%s: gt96100_tx: TxOwn bit wrong!!\n", dev->name); } gp->tx_skbuff[nextIn] = skb; gp->tx_ring[nextIn].byte_cnt = cpu_to_dma32(skb->len << tdByteCntBit); gp->tx_ring[nextIn].buff_ptr = cpu_to_dma32(virt_to_phys(skb->data)); // Give ownership to device, set first and last desc, enable interrupt // Setting of ownership bit must be *last*! gp->tx_ring[nextIn].cmdstat = cpu_to_dma32((u32) (txOwn | txEI | txFirst | txLast)); // increment tx_next_in with wrap gp->tx_next_in = (nextIn + 1) % TX_RING_SIZE; // If count is zero, DMA should be stopped, so restart if (gp->tx_count == 0) { if (GT96100ETH_READ(gp, GT96100_ETH_PORT_STATUS) & psrTxLow) printk(KERN_WARNING "%s: Tx count zero but Tx queue running!\n", dev->name); GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, sdcmrERD | sdcmrTXDL); } // increment count and stop queue if full if (++gp->tx_count == TX_RING_SIZE) netif_stop_queue(dev); dev->trans_start = jiffies; spin_unlock_irqrestore(&gp->lock, flags); return 0; } static int gt96100_rx(struct net_device *dev, u32 status) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; struct sk_buff *skb; int pkt_len, nextOut; gt96100_rd_t *rd; u32 cmdstat; if (gt96100_debug > 2) printk("%s: gt96100_rx: dev=%p, status = %x\n", dev->name, dev, status); // Continue until we reach the current descriptor pointer for (nextOut = gp->rx_next_out; nextOut != (GT96100ETH_READ(gp, GT96100_ETH_CURR_RX_DESC_PTR0) - gp->rx_ring_dma) / sizeof(gt96100_rd_t); nextOut = (nextOut + 1) % RX_RING_SIZE) { rd = &gp->rx_ring[nextOut]; cmdstat = dma32_to_cpu(rd->cmdstat); if (cmdstat & (u32) rxOwn) { cmdstat &= ~((u32) rxOwn); rd->cmdstat = cpu_to_dma32(cmdstat); printk(KERN_ERR "%s: gt96100_rx: ownership bit wrong!\n", dev->name); } // must be first and last (ie only) buffer of packet if (!(cmdstat & (u32) rxFirst) || !(cmdstat & (u32) rxLast)) { printk(KERN_ERR "%s: gt96100_rx: desc not first and last!\n", dev->name); continue; } // drop this received pkt if there were any errors if ((cmdstat & (u32) rxErrorSummary) || (status & icrRxErrorQ0)) { // update the detailed rx error counters that are not covered // by the MIB counters. if (cmdstat & (u32) rxOverrun) gp->stats.rx_fifo_errors++; continue; } pkt_len = dma32_to_cpu(rd->buff_cnt_sz) & rdByteCntMask; /* Create new skb. */ skb = dev_alloc_skb(pkt_len + 2); if (skb == NULL) { printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n", dev->name); gp->stats.rx_dropped++; continue; } skb->dev = dev; skb_reserve(skb, 2); /* 16 byte IP header align */ skb_put(skb, pkt_len); /* Make room */ eth_copy_and_sum(skb, gp->rx_buff[nextOut], pkt_len, 0); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); /* pass the packet to upper layers */ // now we can release ownership of this desc back to device cmdstat |= (u32) rxOwn; rd->cmdstat = cpu_to_dma32(cmdstat); dev->last_rx = jiffies; } gp->rx_next_out = nextOut; return 0; } static void gt96100_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; struct gt96100_private *gp = (struct gt96100_private *) dev->priv; u32 status; if (dev == NULL) { printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name); return; } status = GT96100ETH_READ(gp, GT96100_ETH_INT_CAUSE); // ACK interrupts #if 0 GT96100ETH_CLRBIT(gp, GT96100_ETH_INT_CAUSE, icrEtherIntSum | icrRxBufferQ1 | icrRxBufferQ2 | icrRxBufferQ3 | icrRxBufferQ0 | icrTxBufferHigh | icrTxEndHigh | icrTxBufferLow | icrTxEndLow | icrTxErrorHigh | icrTxErrorLow | icrTxUdr); #else GT96100ETH_WRITE(gp, GT96100_ETH_INT_CAUSE, 0); #endif if ((status & icrEtherIntSum) == 0) { // not our interrupt //printk("%s: isr: no ints? icr=%x,cp0_cause=%x\n", // dev->name, status, read_32bit_cp0_register(CP0_CAUSE)); return; } if (gt96100_debug > 3) printk("%s: isr: entry, icr=%x\n", dev->name, status); if (status & (icrRxBufferQ1 | icrRxBufferQ2 | icrRxBufferQ3)) { printk(KERN_ERR "%s: isr: Rx intr in unused queues!?\n", dev->name); } if (status & icrRxBufferQ0) { gt96100_rx(dev, status); } if (status & (icrTxBufferHigh | icrTxEndHigh)) { printk(KERN_ERR "%s: isr: Tx intr in unused queue!?\n", dev->name); } if (status & icrMIIPhySTC) { u32 psr = GT96100ETH_READ(gp, GT96100_ETH_PORT_STATUS); printk("%s: port status:\n", dev->name); printk ("%s: %s MBit/s, %s-duplex, flow-control %s, link is %s,\n", dev->name, psr & psrSpeed ? "100" : "10", psr & psrDuplex ? "full" : "half", psr & psrFctl ? "disabled" : "enabled", psr & psrLink ? "up" : "down"); printk ("%s: TxLowQ is %s, TxHighQ is %s, Transmitter is %s\n", dev->name, psr & psrTxLow ? "running" : "stopped", psr & psrTxHigh ? "running" : "stopped", psr & psrTxInProg ? "on" : "off"); gp->last_psr = psr; } if (status & (icrTxBufferLow | icrTxEndLow)) { int nextOut; gt96100_td_t *td; u32 cmdstat; // Continue until we reach the current descriptor pointer for (nextOut = gp->tx_next_out; nextOut != (GT96100ETH_READ(gp, GT96100_ETH_CURR_TX_DESC_PTR0) - gp->tx_ring_dma) / sizeof(gt96100_td_t); nextOut = (nextOut + 1) % TX_RING_SIZE) { td = &gp->tx_ring[nextOut]; cmdstat = dma32_to_cpu(td->cmdstat); if (gt96100_debug > 2) printk("%s: isr: Tx desc cmdstat=%x\n", dev->name, cmdstat); if (cmdstat & (u32) txOwn) { cmdstat &= ~((u32) txOwn); td->cmdstat = cpu_to_dma32(cmdstat); printk(KERN_ERR "%s: isr: Tx ownership bit wrong!\n", dev->name); } // increment Tx error stats if (cmdstat & (u32) txErrorSummary) { if (gt96100_debug > 2) printk ("%s: gt96100_interrupt: Tx error, cmdstat = %x\n", dev->name, cmdstat); gp->stats.tx_errors++; if (cmdstat & (u32) txReTxLimit) gp->stats.collisions++; if (cmdstat & (u32) txUnderrun) gp->stats.tx_fifo_errors++; if (cmdstat & (u32) txLateCollision) gp->stats.tx_window_errors++; } // Wake the queue if the ring was full if (gp->tx_count == TX_RING_SIZE) netif_wake_queue(dev); // decrement tx ring buffer count if (gp->tx_count) gp->tx_count--; // free the skb if (gp->tx_skbuff[nextOut]) { if (gt96100_debug > 2) printk ("%s: isr: good Tx, skb=%p\n", dev->name, gp->tx_skbuff[nextOut]); dev_kfree_skb_irq(gp->tx_skbuff[nextOut]); gp->tx_skbuff[nextOut] = NULL; } else { printk(KERN_ERR "%s: isr: no skb!\n", dev->name); } } if (gp->tx_count == 0 && nextOut != gp->tx_next_in) { // FIX! this should probably be a panic printk(KERN_ERR "%s: isr: warning! Tx queue inconsistent\n", dev->name); } gp->tx_next_out = nextOut; if ((status & icrTxEndLow) && gp->tx_count != 0) { // we must restart the DMA if (gt96100_debug > 2) printk("%s: isr: Restarting Tx DMA\n", dev->name); GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, sdcmrERD | sdcmrTXDL); } } // Now check TX errors (RX errors were handled in gt96100_rx) if (status & icrTxErrorHigh) { printk(KERN_ERR "%s: isr: Tx resource error in unused queue!?\n", dev->name); } if (status & icrTxErrorLow) { printk(KERN_ERR "%s: isr: Tx resource error\n", dev->name); } if (status & icrTxUdr) { printk(KERN_ERR "%s: isr: Tx underrun error\n", dev->name); } if (gt96100_debug > 3) printk("%s: isr: exit, icr=%x\n", dev->name, GT96100ETH_READ(gp, GT96100_ETH_INT_CAUSE)); } /* * The Tx ring has been full longer than the watchdog timeout * value, meaning that the interrupt routine has not been freeing * up space in the Tx ring buffer. */ static void gt96100_tx_timeout(struct net_device *dev) { // struct gt96100_private *gp = (struct gt96100_private *)dev->priv; printk(KERN_ERR "%s: gt96100_tx_timeout: dev=%p\n", dev->name, dev); // FIX! do something, like reset the device } static void gt96100_set_rx_mode(struct net_device *dev) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; unsigned long flags; struct dev_mc_list *mcptr; if (gt96100_debug > 2) printk("%s: gt96100_set_rx_mode: dev=%p, flags=%x\n", dev->name, dev, dev->flags); // stop the Receiver DMA abort(dev, sdcmrAR); spin_lock_irqsave(&gp->lock, flags); if (dev->flags & IFF_PROMISC) GT96100ETH_WRITE(gp, GT96100_ETH_PORT_CONFIG, pcrEN | pcrHS | pcrPM); memset(gp->hash_table, 0, RX_HASH_TABLE_SIZE); // clear hash table // Add our ethernet address gt96100_add_hash_entry(dev, dev->dev_addr); if (dev->mc_count) { for (mcptr = dev->mc_list; mcptr; mcptr = mcptr->next) { gt96100_add_hash_entry(dev, mcptr->dmi_addr); } } // restart Rx DMA GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, sdcmrERD); spin_unlock_irqrestore(&gp->lock, flags); } static struct net_device_stats *gt96100_get_stats(struct net_device *dev) { struct gt96100_private *gp = (struct gt96100_private *) dev->priv; unsigned long flags; if (gt96100_debug > 2) printk("%s: gt96100_get_stats: dev=%p\n", dev->name, dev); if (netif_device_present(dev)) { spin_lock_irqsave(&gp->lock, flags); update_stats(gp); spin_unlock_irqrestore(&gp->lock, flags); } return &gp->stats; } module_init(gt96100_probe); MODULE_LICENSE("GPL");