diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/Config.in | 3 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/gt96100eth.c | 1252 | ||||
-rw-r--r-- | drivers/net/gt96100eth.h | 325 |
4 files changed, 1581 insertions, 0 deletions
diff --git a/drivers/net/Config.in b/drivers/net/Config.in index df0caa6fc..4957883f9 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -49,6 +49,9 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then tristate ' MIPS JAZZ onboard SONIC Ethernet support' CONFIG_MIPS_JAZZ_SONIC fi + if [ "$CONFIG_MIPS_GT96100" = "y" ]; then + bool ' MIPS GT96100 Ethernet support' CONFIG_MIPS_GT96100ETH + fi if [ "$CONFIG_SGI_IP27" = "y" ]; then bool ' SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH fi diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 61c14198b..145747078 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -263,6 +263,7 @@ obj-$(CONFIG_HPLANCE) += hplance.o 7990.o obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o obj-$(CONFIG_EQUALIZER) += eql.o obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o +obj-$(CONFIG_MIPS_GT96100ETH) += gt96100eth.o obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o obj-$(CONFIG_BAGETLANCE) += bagetlance.o obj-$(CONFIG_DECLANCE) += declance.o diff --git a/drivers/net/gt96100eth.c b/drivers/net/gt96100eth.c new file mode 100644 index 000000000..6eecd78f1 --- /dev/null +++ b/drivers/net/gt96100eth.c @@ -0,0 +1,1252 @@ +/* + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or support@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/malloc.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_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<NUM_INTERFACES; i++) { + for (i = NUM_INTERFACES - 1; 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); diff --git a/drivers/net/gt96100eth.h b/drivers/net/gt96100eth.h new file mode 100644 index 000000000..2bca8d8ef --- /dev/null +++ b/drivers/net/gt96100eth.h @@ -0,0 +1,325 @@ +/* + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or support@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 definitions for the MIPS GT96100 Advanced + * Communication Controller. + * + */ +#ifndef _GT96100ETH_H +#define _GT96100ETH_H + +#include <asm/galileo-boards/gt96100.h> + +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + +#define RX_HASH_TABLE_SIZE 16384 +#define HASH_HOP_NUMBER 12 + +#define NUM_INTERFACES 2 + +#define GT96100ETH_TX_TIMEOUT HZ + +#define GT96100_ETH0_BASE (MIPS_GT96100_BASE + GT96100_ETH_PORT_CONFIG) +#define GT96100_ETH1_BASE (GT96100_ETH0_BASE + GT96100_ETH_IO_SIZE) + +#ifdef CONFIG_MIPS_EV96100 +#define GT96100_ETHER0_IRQ 4 +#define GT96100_ETHER1_IRQ 4 +#else +#define GT96100_ETHER0_IRQ -1 +#define GT96100_ETHER1_IRQ -1 +#endif + +#define GT96100ETH_READ(gp, offset) \ + GT96100_READ((gp->port_offset + offset)) + +#define GT96100ETH_WRITE(gp, offset, data) \ + GT96100_WRITE((gp->port_offset + offset), data) + +#define GT96100ETH_SETBIT(gp, offset, bits) {\ + u32 val = GT96100ETH_READ(gp, offset); val |= (u32)(bits); \ + GT96100ETH_WRITE(gp, offset, val); } + +#define GT96100ETH_CLRBIT(gp, offset, bits) {\ + u32 val = GT96100ETH_READ(gp, offset); val &= (u32)(~(bits)); \ + GT96100ETH_WRITE(gp, offset, val); } + + +/* Bit definitions of the SMI Reg */ +enum { + smirDataMask = 0xffff, + smirPhyAdMask = 0x1f << 16, + smirPhyAdBit = 16, + smirRegAdMask = 0x1f << 21, + smirRegAdBit = 21, + smirOpCode = 1 << 26, + smirReadValid = 1 << 27, + smirBusy = 1 << 28 +}; + +/* Bit definitions of the Port Config Reg */ +enum pcr_bits { + pcrPM = 1, + pcrRBM = 2, + pcrPBF = 4, + pcrEN = 1 << 7, + pcrLPBKMask = 0x3 << 8, + pcrLPBKBit = 8, + pcrFC = 1 << 10, + pcrHS = 1 << 12, + pcrHM = 1 << 13, + pcrHDM = 1 << 14, + pcrHD = 1 << 15, + pcrISLMask = 0x7 << 28, + pcrISLBit = 28, + pcrACCS = 1 << 31 +}; + +/* Bit definitions of the Port Config Extend Reg */ +enum pcxr_bits { + pcxrIGMP = 1, + pcxrSPAN = 2, + pcxrPAR = 4, + pcxrPRIOtxMask = 0x7 << 3, + pcxrPRIOtxBit = 3, + pcxrPRIOrxMask = 0x3 << 6, + pcxrPRIOrxBit = 6, + pcxrPRIOrxOverride = 1 << 8, + pcxrDPLXen = 1 << 9, + pcxrFCTLen = 1 << 10, + pcxrFLP = 1 << 11, + pcxrFCTL = 1 << 12, + pcxrMFLMask = 0x3 << 14, + pcxrMFLBit = 14, + pcxrMIBclrMode = 1 << 16, + pcxrSpeed = 1 << 18, + pcxrSpeeden = 1 << 19, + pcxrRMIIen = 1 << 20, + pcxrDSCPen = 1 << 21 +}; + +/* Bit definitions of the Port Command Reg */ +enum pcmr_bits { + pcmrFJ = 1 << 15 +}; + + +/* Bit definitions of the Port Status Reg */ +enum psr_bits { + psrSpeed = 1, + psrDuplex = 2, + psrFctl = 4, + psrLink = 8, + psrPause = 1 << 4, + psrTxLow = 1 << 5, + psrTxHigh = 1 << 6, + psrTxInProg = 1 << 7 +}; + +/* Bit definitions of the SDMA Config Reg */ +enum sdcr_bits { + sdcrRCMask = 0xf << 2, + sdcrRCBit = 2, + sdcrBLMR = 1 << 6, + sdcrBLMT = 1 << 7, + sdcrPOVR = 1 << 8, + sdcrRIFB = 1 << 9, + sdcrBSZMask = 0x3 << 12, + sdcrBSZBit = 12 +}; + +/* Bit definitions of the SDMA Command Reg */ +enum sdcmr_bits { + sdcmrERD = 1 << 7, + sdcmrAR = 1 << 15, + sdcmrSTDH = 1 << 16, + sdcmrSTDL = 1 << 17, + sdcmrTXDH = 1 << 23, + sdcmrTXDL = 1 << 24, + sdcmrAT = 1 << 31 +}; + +/* Bit definitions of the Interrupt Cause Reg */ +enum icr_bits { + icrRxBuffer = 1, + icrTxBufferHigh = 1 << 2, + icrTxBufferLow = 1 << 3, + icrTxEndHigh = 1 << 6, + icrTxEndLow = 1 << 7, + icrRxError = 1 << 8, + icrTxErrorHigh = 1 << 10, + icrTxErrorLow = 1 << 11, + icrRxOVR = 1 << 12, + icrTxUdr = 1 << 13, + icrRxBufferQ0 = 1 << 16, + icrRxBufferQ1 = 1 << 17, + icrRxBufferQ2 = 1 << 18, + icrRxBufferQ3 = 1 << 19, + icrRxErrorQ0 = 1 << 20, + icrRxErrorQ1 = 1 << 21, + icrRxErrorQ2 = 1 << 22, + icrRxErrorQ3 = 1 << 23, + icrMIIPhySTC = 1 << 28, + icrSMIdone = 1 << 29, + icrEtherIntSum = 1 << 31 +}; + + +/* The Rx and Tx descriptor lists. */ + +typedef struct { + u32 cmdstat; + u32 byte_cnt; + u32 buff_ptr; + u32 next; +} gt96100_td_t; + +#define tdByteCntBit 16 + +typedef struct { + u32 cmdstat; + u32 buff_cnt_sz; + u32 buff_ptr; + u32 next; +} gt96100_rd_t; + +#define rdBuffSzBit 16 +#define rdByteCntMask 0xffff + + +/* Values for the Tx command-status descriptor entry. */ +enum td_cmdstat { + txOwn = 1 << 31, + txAutoMode = 1 << 30, + txEI = 1 << 23, + txGenCRC = 1 << 22, + txPad = 1 << 18, + txFirst = 1 << 17, + txLast = 1 << 16, + txErrorSummary = 1 << 15, + txReTxCntMask = 0x0f << 10, + txReTxCntBit = 10, + txCollision = 1 << 9, + txReTxLimit = 1 << 8, + txUnderrun = 1 << 6, + txLateCollision = 1 << 5 +}; + +#define TxReTxCntBit 10 + +/* Values for the Rx command-status descriptor entry. */ +enum rd_cmdstat { + rxOwn = 1 << 31, + rxAutoMode = 1 << 30, + rxEI = 1 << 23, + rxFirst = 1 << 17, + rxLast = 1 << 16, + rxErrorSummary = 1 << 15, + rxIGMP = 1 << 14, + rxHashExpired = 1 << 13, + rxMissedFrame = 1 << 12, + rxFrameType = 1 << 11, + rxShortFrame = 1 << 8, + rxMaxFrameLen = 1 << 7, + rxOverrun = 1 << 6, + rxCollision = 1 << 4, + rxCRCError = 1 +}; + +/* Bit fields of a Hash Table Entry */ +enum hash_table_entry { + hteValid = 1, + hteSkip = 2, + hteRD = 4 +}; + +// The MIB counters +typedef struct { + u32 byteReceived; + u32 byteSent; + u32 framesReceived; + u32 framesSent; + u32 totalByteReceived; + u32 totalFramesReceived; + u32 broadcastFramesReceived; + u32 multicastFramesReceived; + u32 cRCError; + u32 oversizeFrames; + u32 fragments; + u32 jabber; + u32 collision; + u32 lateCollision; + u32 frames64; + u32 frames65_127; + u32 frames128_255; + u32 frames256_511; + u32 frames512_1023; + u32 frames1024_MaxSize; + u32 macRxError; + u32 droppedFrames; + u32 outMulticastFrames; + u32 outBroadcastFrames; + u32 undersizeFrames; +} mib_counters_t; + + +struct gt96100_private { + gt96100_rd_t *rx_ring; + gt96100_td_t *tx_ring; + // The Rx and Tx rings must be 16-byte aligned + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + char *hash_table; + // The Hash Table must be 8-byte aligned + dma_addr_t hash_table_dma; + int hash_mode; + + // The Rx buffers must be 8-byte aligned + char *rx_buff[RX_RING_SIZE]; + // Tx buffers (tx_skbuff[i]->data) with less than 8 bytes + // of payload must be 8-byte aligned + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + int rx_next_out; /* The next free ring entry to receive */ + int tx_next_in; /* The next free ring entry to send */ + int tx_next_out; /* The last ring entry the ISR processed */ + int tx_count; /* current # of pkts waiting to be sent in Tx ring */ + + mib_counters_t mib; + struct net_device_stats stats; + + int io_size; + int port_num; // 0 or 1 + u32 port_offset; + + int phy_addr; // PHY address + u32 last_psr; // last value of the port status register + + int options; /* User-settable misc. driver options. */ + int drv_flags; + unsigned char phys[2]; /* MII device addresses. */ + spinlock_t lock; /* Serialise access to device */ +}; + +#endif |