patch-2.4.25 linux-2.4.25/drivers/net/gt64240eth.c
Next file: linux-2.4.25/drivers/net/gt64240eth.h
Previous file: linux-2.4.25/drivers/net/e100/e100_ucode.h
Back to the patch index
Back to the overall index
- Lines: 1855
- Date:
2004-02-18 05:36:31.000000000 -0800
- Orig file:
linux-2.4.24/drivers/net/gt64240eth.c
- Orig date:
1969-12-31 16:00:00.000000000 -0800
diff -urN linux-2.4.24/drivers/net/gt64240eth.c linux-2.4.25/drivers/net/gt64240eth.c
@@ -0,0 +1,1854 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 Patton Electronics Company
+ * Copyright (C) 2002 Momentum Computer
+ *
+ * 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.
+ *
+ * Modified for the Gallileo/Marvell GT-64240 Communication Controller.
+ *
+ * Support for Rx NAPI, Rx checksum offload, IOCTL and ETHTOOL added
+ * Manish Lachwani (lachwani@pmc-sierra.com) - 09/16/2003
+ */
+#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/ethtool.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/mii.h>
+
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define DESC_DATA_BE 1
+
+#include "gt64240eth.h"
+
+// enable this port (set hash size to 1/2K)
+//- #define PORT_CONFIG pcrHS
+#define PORT_CONFIG (pcrHS | pcrHD)
+//- #define PORT_CONFIG pcrHS |pcrPM |pcrPBF|pcrHDM
+//- GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, pcrEN | pcrHS);
+//- GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, pcrEN | pcrHS | pcrPM);
+//- GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, pcrEN | pcrHS | pcrPM | 1<<pcrLPBKBit);
+
+// clear all the MIB ctr regs
+#define EXT_CONFIG_CLEAR (pcxrFCTL | pcxrFCTLen | pcxrFLP | pcxrDPLXen | pcxrPRIOrxOverride | pcxrRMIIen)
+
+/*
+ * _debug level:
+ * <= 2 none.
+ * > 2 some warnings such as queue full, .....
+ * > 3 lots of change-of-state messages.
+ * > 4 EXTENSIVE data/descriptor dumps.
+ */
+#ifdef GT64240_DEBUG
+static int gt64240_debug = GT64240_DEBUG;
+#else
+static int gt64240_debug = 0;
+#endif
+
+/********************************************************/
+
+// prototypes
+static void *dmaalloc(size_t size, dma_addr_t * dma_handle);
+static void dmafree(size_t size, void *vaddr);
+static void gt64240_delay(int msec);
+static int gt64240_add_hash_entry(struct net_device *dev,
+ unsigned char *addr);
+static void read_mib_counters(struct gt64240_private *gp);
+static int read_MII(struct net_device *dev, u32 reg);
+static int write_MII(struct net_device *dev, u32 reg, u16 data);
+#if 1
+static void dump_tx_ring(struct net_device *dev);
+static void dump_rx_ring(struct net_device *dev);
+#endif
+static void dump_MII(struct net_device *dev);
+static void dump_tx_desc(struct net_device *dev, int i);
+static void dump_rx_desc(struct net_device *dev, int i);
+static void dump_skb(struct net_device *dev, struct sk_buff *skb);
+static void dump_hw_addr(unsigned char *addr_str);
+static void update_stats(struct gt64240_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 gt64240_probe1(uint32_t ioaddr, int irq, int port_num);
+static void reset_tx(struct net_device *dev);
+static void reset_rx(struct net_device *dev);
+static int gt64240_init(struct net_device *dev);
+static int gt64240_open(struct net_device *dev);
+static int gt64240_close(struct net_device *dev);
+static int gt64240_tx(struct sk_buff *skb, struct net_device *dev);
+#ifdef GT64240_NAPI
+static int gt64240_poll(struct net_device *dev, int *budget);
+static int gt64240_rx(struct net_device *dev, u32 status, int budget);
+#else
+static int gt64240_rx(struct net_device *dev, u32 status);
+#endif
+static void gt64240_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void gt64240_tx_timeout(struct net_device *dev);
+static void gt64240_set_rx_mode(struct net_device *dev);
+static struct net_device_stats *gt64240_get_stats(struct net_device *dev);
+
+extern char *__init prom_getcmdline(void);
+extern int prom_get_mac_addrs(unsigned char
+ station_addr[NUM_INTERFACES][6]);
+
+static char version[] __devinitdata =
+ "gt64240eth.o: version 0.1, <www.patton.com>\n";
+
+// PHY device addresses
+static u32 gt64240_phy_addr[NUM_INTERFACES] __devinitdata = { 0x8, 0x1, 0xa };
+
+// Need real Ethernet addresses -- in parse_mac_addr_options(),
+// these will be replaced by prom_get_mac_addrs() and/or prom_getcmdline().
+static unsigned char gt64240_station_addr[NUM_INTERFACES][6] = {
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05},
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
+ {0x02, 0x03, 0x04, 0x05, 0x06, 0x07}
+};
+
+static int max_interrupt_work = 32;
+
+/*
+ * Base address and interupt of the GT64240 ethernet controllers
+ */
+static struct {
+ unsigned int port;
+ int irq;
+} gt64240_iflist[NUM_INTERFACES] = {
+ {
+ GT64240_ETH0_BASE, 8}, {
+ GT64240_ETH1_BASE, 8}, {
+ GT64240_ETH2_BASE, 8}
+};
+
+/*
+ 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 gt64240_delay(int ms)
+{
+ if (in_interrupt())
+ return;
+ else {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(ms * HZ / 1000);
+ }
+}
+
+unsigned char prom_mac_addr_base[6];
+
+int prom_get_mac_addrs(unsigned char station_addr[NUM_INTERFACES][6])
+{
+ memcpy(station_addr[0], prom_mac_addr_base, 6);
+ memcpy(station_addr[1], prom_mac_addr_base, 6);
+ memcpy(station_addr[2], prom_mac_addr_base, 6);
+
+ station_addr[1][5] += 1;
+ station_addr[2][5] += 2;
+
+ return 0;
+}
+
+void parse_mac_addr_options(void)
+{
+ prom_get_mac_addrs(gt64240_station_addr);
+}
+
+static int read_MII(struct net_device *dev, u32 reg)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int timedout = 20;
+ u32 smir = smirOpCode | (gp->phy_addr << smirPhyAdBit) |
+ (reg << smirRegAdBit);
+
+ // wait for last operation to complete
+ while ((GT64240_READ(GT64240_ETH_SMI_REG)) & smirBusy) {
+ // snooze for 1 msec and check again
+ gt64240_delay(1);
+
+ if (--timedout == 0) {
+ printk("%s: read_MII busy timeout!!\n", dev->name);
+ return -1;
+ }
+ }
+
+ GT64240_WRITE(GT64240_ETH_SMI_REG, smir);
+
+ timedout = 20;
+ // wait for read to complete
+ while (!
+ ((smir =
+ GT64240_READ(GT64240_ETH_SMI_REG)) & smirReadValid)) {
+ // snooze for 1 msec and check again
+ gt64240_delay(1);
+
+ if (--timedout == 0) {
+ printk("%s: read_MII timeout!!\n", dev->name);
+ return -1;
+ }
+ }
+
+ return (int) (smir & smirDataMask);
+}
+
+/* Ethtool support */
+static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ u32 ethcmd;
+
+ if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
+ return -EFAULT;
+
+ switch (ethcmd) {
+
+ /* Get driver info */
+ case ETHTOOL_GDRVINFO:{
+ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+ strncpy(info.driver, "gt64260",
+ sizeof(info.driver) - 1);
+ strncpy(info.version, version,
+ sizeof(info.version) - 1);
+ if (copy_to_user(useraddr, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ /* get settings */
+ case ETHTOOL_GSET:{
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+ spin_lock_irq(&gp->lock);
+ mii_ethtool_gset(&gp->mii_if, &ecmd);
+ spin_unlock_irq(&gp->lock);
+ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+ return -EFAULT;
+ return 0;
+ }
+ /* set settings */
+ case ETHTOOL_SSET:{
+ int r;
+ struct ethtool_cmd ecmd;
+ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+ return -EFAULT;
+ spin_lock_irq(&gp->lock);
+ r = mii_ethtool_sset(&gp->mii_if, &ecmd);
+ spin_unlock_irq(&gp->lock);
+ return r;
+ }
+ /* restart autonegotiation */
+ case ETHTOOL_NWAY_RST:{
+ return mii_nway_restart(&gp->mii_if);
+ }
+ /* get link status */
+ case ETHTOOL_GLINK:{
+ struct ethtool_value edata = { ETHTOOL_GLINK };
+ edata.data = mii_link_ok(&gp->mii_if);
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+ /* get message-level */
+ case ETHTOOL_GMSGLVL:{
+ struct ethtool_value edata = { ETHTOOL_GMSGLVL };
+ edata.data = 0; /* XXX */
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+ }
+ /* set message-level */
+ case ETHTOOL_SMSGLVL:{
+ struct ethtool_value edata;
+ if (copy_from_user
+ (&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+ /* debug = edata.data; *//* XXX */
+ return 0;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+static int gt64240_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct mii_ioctl_data *data =
+ (struct mii_ioctl_data *) &rq->ifr_data;
+ int phy = dev->base_addr & 0x1f;
+ int retval;
+
+ switch (cmd) {
+ case SIOCETHTOOL:
+ retval = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+ break;
+
+ case SIOCGMIIPHY: /* Get address of MII PHY in use. */
+ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */
+ data->phy_id = phy;
+ /* Fall through */
+
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ case SIOCDEVPRIVATE + 1: /* for binary compat, remove in 2.5 */
+ data->val_out = read_MII(dev, data->reg_num & 0x1f);
+ retval = 0;
+ break;
+
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ case SIOCDEVPRIVATE + 2: /* for binary compat, remove in 2.5 */
+ if (!capable(CAP_NET_ADMIN)) {
+ retval = -EPERM;
+ } else {
+ write_MII(dev, data->reg_num & 0x1f, data->val_in);
+ retval = 0;
+ }
+ break;
+
+ default:
+ retval = -EOPNOTSUPP;
+ break;
+ }
+ return retval;
+}
+
+static void dump_tx_desc(struct net_device *dev, int i)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ gt64240_td_t *td = &gp->tx_ring[i];
+
+ printk
+ ("%s:tx[%d]: self=%08x cmd=%08x, cnt=%4d. bufp=%08x, next=%08x\n",
+ dev->name, i, td, td->cmdstat, td->byte_cnt, td->buff_ptr,
+ td->next);
+}
+
+static void dump_rx_desc(struct net_device *dev, int i)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ gt64240_rd_t *rd = &gp->rx_ring[i];
+
+ printk
+ ("%s:rx_dsc[%d]: self=%08x cst=%08x,size=%4d. cnt=%4d. bufp=%08x, next=%08x\n",
+ dev->name, i, rd, rd->cmdstat, rd->buff_sz, rd->byte_cnt,
+ rd->buff_ptr, rd->next);
+}
+
+// These routines work, just disabled to avoid compile warnings
+static int write_MII(struct net_device *dev, u32 reg, u16 data)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int timedout = 20;
+ u32 smir =
+ (gp->phy_addr << smirPhyAdBit) | (reg << smirRegAdBit) | data;
+
+ // wait for last operation to complete
+ while (GT64240_READ(GT64240_ETH_SMI_REG) & smirBusy) {
+ // snooze for 1 msec and check again
+ gt64240_delay(1);
+
+ if (--timedout == 0) {
+ printk("%s: write_MII busy timeout!!\n",
+ dev->name);
+ return -1;
+ }
+ }
+
+ GT64240_WRITE(GT64240_ETH_SMI_REG, smir);
+ return 0;
+}
+
+static void dump_tx_ring(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int i;
+
+ printk("%s: dump_tx_ring: txno/txni/cnt=%d/%d/%d\n",
+ dev->name, gp->tx_next_out, gp->tx_next_in, gp->tx_count);
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ dump_tx_desc(dev, i);
+}
+
+static void dump_rx_ring(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int i;
+
+ printk("%s: dump_rx_ring: rxno=%d\n", dev->name, gp->rx_next_out);
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ dump_rx_desc(dev, i);
+}
+
+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 void dump_hw_addr(unsigned char *addr_str)
+{
+ int i;
+ for (i = 0; i < 6; i++) {
+ printk("%2.2x", addr_str[i]);
+ printk(i < 5 ? ":" : "\n");
+ }
+}
+
+
+static void dump_skb(struct net_device *dev, struct sk_buff *skb)
+{
+ int i;
+ unsigned char *skbdata;
+
+ printk("%s: dump_skb: skb=%p, skb->data=%p, skb->len=%d.",
+ dev->name, skb, skb->data, skb->len);
+
+ skbdata = (unsigned char *) KSEG1ADDR(skb->data);
+
+ for (i = 0; i < skb->len; i++) {
+ if (!(i % 16))
+ printk("\r\n %3.3x: %2.2x,", i, skbdata[i]);
+ else
+ printk("%2.2x,", skbdata[i]);
+ }
+ printk("\r\n");
+}
+
+
+static void dump_data(struct net_device *dev, char *ptr, int len)
+{
+ int i;
+ unsigned char *data;
+
+ printk("%s: dump_data: ptr=%p, len=%d.", dev->name, ptr, len);
+
+ data = (unsigned char *) KSEG1ADDR(ptr);
+
+ for (i = 0; i < len; i++) {
+ if (!(i % 16))
+ printk("\n %3.3x: %2.2x,", i, data[i]);
+ else
+ printk("%2.2x,", data[i]);
+ }
+ printk("\n");
+}
+
+
+/*--------------------------------------------------------------*/
+/* A D D H A S H E N T R Y */
+/*--------------------------------------------------------------*/
+static int gt64240_add_hash_entry(struct net_device *dev,
+ unsigned char *addr)
+{
+ struct gt64240_private *gp;
+ int i;
+ u32 value1, value0, *entry;
+ u16 hashResult;
+ unsigned char hash_ea[6];
+ static int flag = 0;
+ static unsigned char swapped[256];
+
+ if (flag == 0) { /* Create table to swap bits in a byte */
+ flag = 1;
+ for (i = 0; i < 256; i++) {
+ swapped[i] = (i & 0x01) << 7;
+ swapped[i] |= (i & 0x02) << 5;
+ swapped[i] |= (i & 0x04) << 3;
+ swapped[i] |= (i & 0x08) << 1;
+ swapped[i] |= (i & 0x10) >> 1;
+ swapped[i] |= (i & 0x20) >> 3;
+ swapped[i] |= (i & 0x40) >> 5;
+ swapped[i] |= (i & 0x80) >> 7;
+ }
+ }
+
+ for (i = 0; i < 6; i++) { /* swap bits from mac to create hash mac */
+ hash_ea[i] = swapped[addr[i]];
+ }
+
+ gp = (struct gt64240_private *) dev->priv;
+
+ /* create hash entry address */
+ hashResult = (((hash_ea[5] >> 2) & 0x3F) << 9) & 0x7E00;
+ hashResult |= ((hash_ea[4] & 0x7F) << 2) | (hash_ea[5] & 0x03);
+ hashResult ^=
+ ((hash_ea[3] & 0xFF) << 1) | ((hash_ea[4] >> 7) & 0x01);
+ hashResult ^= ((hash_ea[1] & 0x01) << 8) | (hash_ea[2] & 0xFF);
+
+ value0 = hteValid | hteRD; /* Create hash table entry value */
+ value0 |= (u32) addr[0] << 3;
+ value0 |= (u32) addr[1] << 11;
+ value0 |= (u32) addr[2] << 19;
+ value0 |= ((u32) addr[3] & 0x1f) << 27;
+
+ value1 = ((u32) addr[3] >> 5) & 0x07;
+ value1 |= (u32) addr[4] << 3;
+ value1 |= (u32) addr[5] << 11;
+
+ /* Inset entry value into hash table */
+ for (i = 0; i < HASH_HOP_NUMBER; i++) {
+ entry = (u32 *) ((u32) gp->hash_table +
+ (((u32) hashResult & 0x07ff) << 3));
+ if ((*entry & hteValid) && !(*entry & hteSkip)) {
+ hashResult += 2; /* oops, occupied, go to next entry */
+ } else {
+#ifdef __LITTLE_ENDIAN
+ entry[1] = value1;
+ entry[0] = value0;
+#else
+ entry[0] = value1;
+ entry[1] = value0;
+#endif
+ break;
+ }
+ }
+ if (i >= HASH_HOP_NUMBER) {
+ printk("%s: gt64240_add_hash_entry expired!\n", dev->name);
+ return (-1);
+ }
+ return (0);
+}
+
+
+static void read_mib_counters(struct gt64240_private *gp)
+{
+ u32 *mib_regs = (u32 *) & gp->mib;
+ int i;
+
+ for (i = 0; i < sizeof(mib_counters_t) / sizeof(u32); i++)
+ mib_regs[i] =
+ GT64240ETH_READ(gp,
+ GT64240_ETH_MIB_COUNT_BASE +
+ i * sizeof(u32));
+}
+
+
+static void update_stats(struct gt64240_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 gt64240_rx
+ //tx_dropped incremented by gt64240_tx
+ stats->multicast = mib->multicastFramesReceived;
+ // collisions incremented by gt64240_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 void abort(struct net_device *dev, u32 abort_bits)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int timedout = 100; // wait up to 100 msec for hard stop to complete
+
+ if (gt64240_debug > 3)
+ printk("%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
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, abort_bits);
+
+ if (gt64240_debug > 3)
+ printk("%s: abort: SDMA cmd = %x/%x\n",
+ dev->name, abort_bits, GT64240ETH_READ(gp,
+ GT64240_ETH_SDMA_COMM));
+
+ // wait for abort to complete
+ while ((GT64240ETH_READ(gp, GT64240_ETH_SDMA_COMM)) & abort_bits) {
+ // snooze for 20 msec and check again
+ gt64240_delay(1);
+
+ if (--timedout == 0) {
+ printk("%s: abort timeout!!\n", dev->name);
+ break;
+ }
+ }
+
+ spin_unlock(&gp->lock);
+}
+
+
+static void hard_stop(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+
+ if (gt64240_debug > 3)
+ printk("%s: hard stop\n", dev->name);
+
+ disable_ether_irq(dev);
+
+ abort(dev, sdcmrAR | sdcmrAT);
+
+ // disable port
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, 0);
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_hard_stop: Port Config=%x\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_PORT_CONFIG));
+
+}
+
+
+static void enable_ether_irq(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ u32 intMask;
+
+ intMask =
+ icrTxBufferLow | icrTxEndLow | icrTxErrorLow |
+ icrTxBufferHigh | icrTxEndHigh | icrTxErrorHigh | icrTxUdr |
+ icrRxBuffer | icrRxOVR | icrRxError | icrMIIPhySTC |
+ icrEtherIntSum;
+
+
+//- GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0); /* CLEAR existing ints */
+ // unmask device interrupts:
+ GT64240ETH_WRITE(gp, GT64240_ETH_INT_MASK, intMask);
+
+ // now route ethernet interrupts to GT PCI1 (eth0 and eth1 will be
+ // sharing it).
+ GT_READ(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, &intMask);
+ intMask |= 1 << gp->port_num;
+ GT_WRITE(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, intMask);
+}
+
+static void disable_ether_irq(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ u32 intMask;
+
+ GT_READ(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, &intMask);
+ intMask &= ~(1 << gp->port_num);
+ GT_WRITE(PCI_1INTERRUPT_CAUSE_MASK_REGISTER_HIGH, intMask);
+
+ // mask all device interrupts:
+ GT64240ETH_WRITE(gp, GT64240_ETH_INT_MASK, 0);
+}
+
+#ifdef GT64240_NAPI
+static inline void __netif_rx_complete(struct net_device *dev)
+{
+ if (!test_bit(__LINK_STATE_RX_SCHED, &dev->state))
+ BUG();
+ list_del(&dev->poll_list);
+ clear_bit(__LINK_STATE_RX_SCHED, &dev->state);
+}
+#endif
+
+
+/*
+ * Probe for a GT64240 ethernet controller.
+ */
+static int __init gt64240_probe(void)
+{
+ unsigned int base_addr = 0;
+ int i;
+ int found = 0;
+
+ if (gt64240_debug > 2)
+ printk("gt64240_probe at 0x%08x\n", base_addr);
+
+ parse_mac_addr_options();
+
+ for (i = 0; i < NUM_INTERFACES; i++) {
+ int base_addr = gt64240_iflist[i].port;
+
+ if (check_region(base_addr, GT64240_ETH_IO_SIZE)) {
+ printk("gt64240_probe: ioaddr 0x%lx taken?\n",
+ base_addr);
+ continue;
+ }
+
+ if (gt64240_probe1(base_addr, gt64240_iflist[i].irq, i) ==
+ 0) {
+ /* Does not seem to be the "traditional" way folks do this, */
+ /* but I want to init both eth ports if at all possible! */
+ /* So, until I find out the "correct" way to do this: */
+ if (++found == NUM_INTERFACES) /* That's all of them! */
+ return 0;
+ }
+ }
+ if (found)
+ return 0; /* as long as we found at least one! */
+ return -ENODEV;
+}
+
+module_init(gt64240_probe);
+
+static int __init gt64240_probe1(uint32_t ioaddr, int irq, int port_num)
+{
+ struct net_device *dev = NULL;
+ static unsigned version_printed = 0;
+ struct gt64240_private *gp = NULL;
+ int retval;
+ u16 vendor_id, device_id;
+ u32 cpuConfig;
+ unsigned char chip_rev;
+
+ // probe for GT64240 by reading PCI0 vendor/device ID register
+ pcibios_read_config_word(0, 0, PCI_VENDOR_ID, &vendor_id);
+ pcibios_read_config_word(0, 0, PCI_DEVICE_ID, &device_id);
+
+ dev = init_etherdev(NULL, sizeof(struct gt64240_private));
+
+ if (gt64240_debug > 2)
+ printk
+ ("%s: gt64240_probe1 vendId=0x%08x, devId=0x%08x, addr=0x%08lx, irq=%d.,port=%d.\n",
+ dev->name, vendor_id, device_id, ioaddr, irq,
+ port_num);
+
+ if (irq < 0) {
+ printk
+ ("gt64240_probe1: irq unknown - probing not supported\n");
+ return -ENODEV;
+ }
+#if 1 /* KLUDGE Alert: no check on return value: */
+ if (!request_region(ioaddr, GT64240_ETH_IO_SIZE, "gt64240eth"))
+ printk("*** request_region() failed!\n");
+#endif
+
+ cpuConfig = GT64240_READ(CPU_CONFIGURATION);
+ printk("gt64240_probe1: cpu in %s-endian mode\n",
+ (cpuConfig & (1 << 12)) ? "little" : "big");
+
+ printk
+ ("%s: GT64240 found at ioaddr 0x%lx, irq %d., PCI devID=%x\n",
+ dev->name, ioaddr, irq, device_id);
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct gt64240_private));
+
+ if (gt64240_debug && version_printed++ == 0)
+ printk("%s: %s", dev->name, version);
+
+ /* private struct aligned and zeroed by init_etherdev */
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ memcpy(dev->dev_addr, gt64240_station_addr[port_num],
+ sizeof(dev->dev_addr));
+
+ printk("%s: HW Address ", dev->name);
+ dump_hw_addr(dev->dev_addr);
+
+ /* Initialize our private structure. */
+ if (dev->priv == NULL) {
+
+ gp = (struct gt64240_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 = GT64240_ETH_IO_SIZE;
+ gp->port_offset = port_num * GT64240_ETH_IO_SIZE;
+ gp->phy_addr = gt64240_phy_addr[port_num];
+
+ pcibios_read_config_byte(0, 0, PCI_REVISION_ID, &chip_rev);
+ gp->chip_rev = chip_rev;
+ printk("%s: GT64240 chip revision=%d\n", dev->name, gp->chip_rev);
+
+ printk("%s: GT64240 ethernet port %d\n", dev->name, gp->port_num);
+
+#ifdef GT64240_NAPI
+ printk("Rx NAPI supported \n");
+#endif
+
+/* MII Initialization */
+ gp->mii_if.dev = dev;
+ gp->mii_if.phy_id = dev->base_addr;
+ gp->mii_if.mdio_read = read_MII;
+ gp->mii_if.mdio_write = write_MII;
+ gp->mii_if.advertising = read_MII(dev, MII_ADVERTISE);
+
+ // 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(gt64240_rd_t) * RX_RING_SIZE
+ +
+ sizeof(gt64240_td_t) * TX_RING_SIZE,
+ &gp->rx_ring_dma);
+ if (gp->rx_ring == NULL) {
+ retval = -ENOMEM;
+ goto free_region;
+ }
+
+ gp->tx_ring =
+ (gt64240_td_t *) (gp->rx_ring + RX_RING_SIZE);
+ gp->tx_ring_dma =
+ gp->rx_ring_dma + sizeof(gt64240_rd_t) * RX_RING_SIZE;
+ }
+ // Allocate the Rx Data Buffers
+ if (gp->rx_buff == NULL) {
+ gp->rx_buff =
+ dmaalloc(PKT_BUF_SZ * RX_RING_SIZE, &gp->rx_buff_dma);
+ if (gp->rx_buff == NULL) {
+ dmafree(sizeof(gt64240_rd_t) * RX_RING_SIZE
+ + sizeof(gt64240_td_t) * TX_RING_SIZE,
+ gp->rx_ring);
+ retval = -ENOMEM;
+ goto free_region;
+ }
+ }
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_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(gt64240_rd_t) * RX_RING_SIZE
+ + sizeof(gt64240_td_t) * TX_RING_SIZE,
+ gp->rx_ring);
+ dmafree(PKT_BUF_SZ * RX_RING_SIZE, gp->rx_buff);
+ retval = -ENOMEM;
+ goto free_region;
+ }
+ }
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_probe1, hash=%p\n",
+ dev->name, gp->hash_table);
+
+ spin_lock_init(&gp->lock);
+
+ dev->open = gt64240_open;
+ dev->hard_start_xmit = gt64240_tx;
+ dev->stop = gt64240_close;
+ dev->get_stats = gt64240_get_stats;
+ dev->do_ioctl = gt64240_ioctl;
+ dev->set_multicast_list = gt64240_set_rx_mode;
+ dev->tx_timeout = gt64240_tx_timeout;
+ dev->watchdog_timeo = GT64240ETH_TX_TIMEOUT;
+
+#ifdef GT64240_NAPI
+ dev->poll = gt64240_poll;
+ dev->weight = 64;
+#endif
+
+ /* 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("%s: gt64240_probe1 failed. Returns %d\n",
+ dev->name, retval);
+ return retval;
+}
+
+
+static void reset_tx(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int i;
+
+ abort(dev, sdcmrAT);
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (gp->tx_skbuff[i]) {
+ if (in_interrupt())
+ dev_kfree_skb_irq(gp->tx_skbuff[i]);
+ else
+ dev_kfree_skb(gp->tx_skbuff[i]);
+ gp->tx_skbuff[i] = NULL;
+ }
+//- gp->tx_ring[i].cmdstat = 0; // CPU owns
+ gp->tx_ring[i].cmdstat =
+ (u32) (txGenCRC | txEI | txPad | txFirst | txLast);
+ gp->tx_ring[i].byte_cnt = 0;
+ gp->tx_ring[i].buff_ptr = 0;
+ gp->tx_ring[i].next =
+ gp->tx_ring_dma + sizeof(gt64240_td_t) * (i + 1);
+ if (gt64240_debug > 4)
+ dump_tx_desc(dev, i);
+ }
+ /* Wrap the ring. */
+ gp->tx_ring[i - 1].next = gp->tx_ring_dma;
+ if (gt64240_debug > 4)
+ dump_tx_desc(dev, i - 1);
+
+ // setup only the lowest priority TxCDP reg
+ GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0,
+ gp->tx_ring_dma);
+//- GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0, 0); /* ROLLINS */
+//- GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0,virt_to_phys(&gp->tx_ring[0])); /* ROLLINS */
+
+ GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR1, 0);
+
+ // init Tx indeces and pkt counter
+ gp->tx_next_in = gp->tx_next_out = 0;
+ gp->tx_count = 0;
+}
+
+static void reset_rx(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int i;
+
+ abort(dev, sdcmrAR);
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ gp->rx_ring[i].next =
+ gp->rx_ring_dma + sizeof(gt64240_rd_t) * (i + 1);
+ gp->rx_ring[i].buff_ptr = gp->rx_buff_dma + i * PKT_BUF_SZ;
+ gp->rx_ring[i].buff_sz = PKT_BUF_SZ;
+ gp->rx_ring[i].byte_cnt = 0; /* just for debug printk's */
+ // Give ownership to device, set first and last, enable interrupt
+ gp->rx_ring[i].cmdstat =
+ (uint32_t) (rxFirst | rxLast | rxOwn | rxEI);
+ if (gt64240_debug > 4)
+ dump_rx_desc(dev, i);
+ }
+ /* Wrap the ring. */
+ gp->rx_ring[i - 1].next = gp->rx_ring_dma;
+ if (gt64240_debug > 4)
+ dump_rx_desc(dev, i - 1);
+
+ // Setup only the lowest priority RxFDP and RxCDP regs
+ for (i = 0; i < 4; i++) {
+ if (i == 0) {
+ GT64240ETH_WRITE(gp, GT64240_ETH_1ST_RX_DESC_PTR0,
+ gp->rx_ring_dma);
+ GT64240ETH_WRITE(gp, GT64240_ETH_CURR_RX_DESC_PTR0,
+ gp->rx_ring_dma);
+ } else {
+ GT64240ETH_WRITE(gp,
+ GT64240_ETH_1ST_RX_DESC_PTR0 +
+ i * 4, 0);
+ GT64240ETH_WRITE(gp,
+ GT64240_ETH_CURR_RX_DESC_PTR0 +
+ i * 4, 0);
+ }
+ }
+
+ // init Rx NextOut index
+ gp->rx_next_out = 0;
+}
+
+
+static int gt64240_init(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ u32 ciu;
+
+ if (gt64240_debug > 3) {
+ printk("%s: gt64240_init: dev=%p\n", dev->name, dev);
+ printk("%s: gt64240_init: scs0_lo=%04x, scs0_hi=%04x\n",
+ dev->name, GT64240_READ(0x008),
+ GT64240_READ(0x010));
+ printk("%s: gt64240_init: scs1_lo=%04x, scs1_hi=%04x\n",
+ dev->name, GT64240_READ(0x208),
+ GT64240_READ(0x210));
+ printk("%s: gt64240_init: scs2_lo=%04x, scs2_hi=%04x\n",
+ dev->name, GT64240_READ(0x018),
+ GT64240_READ(0x020));
+ printk("%s: gt64240_init: scs3_lo=%04x, scs3_hi=%04x\n",
+ dev->name, GT64240_READ(0x218),
+ GT64240_READ(0x220));
+ }
+ // Stop and disable Port
+ hard_stop(dev);
+
+ GT64240_WRITE(COMM_UNIT_INTERRUPT_MASK, 0x07070777); /*+prk21aug01 */
+ if (gt64240_debug > 2)
+ printk
+ ("%s: gt64240_init: CIU Cause=%08x, Mask=%08x, EAddr=%08x\n",
+ dev->name, GT64240_READ(COMM_UNIT_INTERRUPT_CAUSE),
+ GT64240_READ(COMM_UNIT_INTERRUPT_MASK),
+ GT64240_READ(COMM_UNIT_ERROR_ADDRESS));
+
+ // 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
+ gt64240_add_hash_entry(dev, dev->dev_addr);
+ // Set-up DMA ptr to hash table
+ GT64240ETH_WRITE(gp, GT64240_ETH_HASH_TBL_PTR, gp->hash_table_dma);
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: Hash Tbl Ptr=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_HASH_TBL_PTR));
+
+ // Setup Tx
+ reset_tx(dev);
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: Curr Tx Desc Ptr0=%x\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_CURR_TX_DESC_PTR0));
+
+ // Setup Rx
+ reset_rx(dev);
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: 1st/Curr Rx Desc Ptr0=%x/%x\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_1ST_RX_DESC_PTR0),
+ GT64240ETH_READ(gp, GT64240_ETH_CURR_RX_DESC_PTR0));
+
+ if (gt64240_debug > 3)
+ dump_MII(dev);
+ write_MII(dev, 0, 0x8000); /* force a PHY reset -- self-clearing! */
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: PhyAD=%x\n", dev->name,
+ GT64240_READ(GT64240_ETH_PHY_ADDR_REG));
+
+ // setup DMA
+ // 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.
+#ifdef DESC_DATA_BE
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_CONFIG,
+ (0xf << sdcrRCBit) | sdcrRIFB | (3 <<
+ sdcrBSZBit));
+#else
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_CONFIG, sdcrBLMR | sdcrBLMT |
+//- (0xf<<sdcrRCBit) | sdcrRIFB | (3<<sdcrBSZBit));
+ (0xf << sdcrRCBit) | sdcrRIFB | (2 <<
+ sdcrBSZBit));
+#endif
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: SDMA Config=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_SDMA_CONFIG));
+
+#if 0
+ // start Rx DMA
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, sdcmrERD);
+#endif
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: SDMA Cmd =%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_SDMA_COMM));
+
+#if 1
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG, PORT_CONFIG);
+#endif
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: Port Config=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG));
+
+ /*
+ * 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?
+ */
+
+#if 1
+ // clear all the MIB ctr regs
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG_EXT,
+ EXT_CONFIG_CLEAR);
+ read_mib_counters(gp);
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG_EXT,
+ EXT_CONFIG_CLEAR | pcxrMIBclrMode);
+
+#endif
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: Port Config Ext=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG_EXT));
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: Port Command=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_COMMAND));
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_COMMAND, 0x0);
+
+ netif_start_queue(dev);
+
+ /* enable the port */
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG,
+ (PORT_CONFIG | pcrEN));
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_init: Port Config=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG));
+#if 1
+ // start Rx DMA
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, sdcmrERD);
+#endif
+
+
+ // enable interrupts
+ enable_ether_irq(dev);
+
+//--- gp->last_psr |= psrLink; /* KLUDGE ALERT */
+
+ // we should now be receiving frames
+ return 0;
+}
+
+
+static int gt64240_open(struct net_device *dev)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_open: dev=%p\n", dev->name, dev);
+
+ if ((retval = request_irq(dev->irq, >64240_interrupt,
+ SA_SHIRQ, dev->name, dev))) {
+ printk("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+ // Initialize and startup the GT-64240 ethernet port
+ if ((retval = gt64240_init(dev))) {
+ printk("%s: error in gt64240_open\n", dev->name);
+ free_irq(dev->irq, dev);
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_open: Initialization done.\n",
+ dev->name);
+
+ return 0;
+}
+
+static int gt64240_close(struct net_device *dev)
+{
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_close: dev=%p\n", dev->name, dev);
+
+ // stop the device
+ if (netif_device_present(dev)) {
+ netif_stop_queue(dev);
+ hard_stop(dev);
+ }
+
+ free_irq(dev->irq, dev);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef GT64240_NAPI
+/*
+ * Function will release Tx skbs which are now complete
+ */
+static void gt64240_tx_fill(struct net_device *netdev, u32 status)
+{
+ struct gt64240_private *gp =
+ (struct gt64240_private *) netdev->priv;
+ int nextOut, cdp;
+ gt64240_td_t *td;
+ u32 cmdstat;
+
+ cdp = (GT64240ETH_READ(gp, GT64240_ETH_CURR_TX_DESC_PTR0)
+ - gp->tx_ring_dma) / sizeof(gt64240_td_t);
+
+ for (nextOut = gp->tx_next_out; nextOut != cdp;
+ nextOut = (nextOut + 1) % TX_RING_SIZE) {
+ if (--gp->intr_work_done == 0)
+ break;
+
+ td = &gp->tx_ring[nextOut];
+ cmdstat = td->cmdstat;
+
+ if (cmdstat & (u32) txOwn)
+ break;
+
+ if (gp->tx_full) {
+ gp->tx_full = 0;
+ if (gp->last_psr & psrLink) {
+ netif_wake_queue(netdev);
+ }
+ }
+ // decrement tx ring buffer count
+ if (gp->tx_count)
+ gp->tx_count--;
+
+ // free the skb
+ if (gp->tx_skbuff[nextOut]) {
+ dev_kfree_skb_irq(gp->tx_skbuff[nextOut]);
+ gp->tx_skbuff[nextOut] = NULL;
+ }
+ }
+
+ gp->tx_next_out = nextOut;
+
+ if ((status & icrTxEndLow) && gp->tx_count != 0)
+ // we must restart the DMA
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM,
+ sdcmrERD | sdcmrTXDL);
+}
+
+/*
+ * Main function for NAPI
+ */
+static int gt64240_poll(struct net_device *netdev, int *budget)
+{
+ struct gt64240_private *gp =
+ (struct gt64240_private *) netdev->priv;
+ unsigned long flags;
+ int done = 1, orig_budget, work_done;
+ u32 intMask, status = GT64240ETH_READ(gp, GT64240_ETH_INT_CAUSE);
+
+ spin_lock_irqsave(&gp->lock, flags);
+ gt64240_tx_fill(netdev, status);
+
+ if (GT64240ETH_READ(gp, GT64240_ETH_CURR_RX_DESC_PTR0) !=
+ gp->rx_next_out) {
+ orig_budget = *budget;
+ if (orig_budget > netdev->quota)
+ orig_budget = netdev->quota;
+
+ work_done = gt64240_rx(netdev, status, orig_budget);
+ *budget -= work_done;
+ netdev->quota -= work_done;
+ if (work_done >= orig_budget)
+ done = 0;
+ if (done) {
+ __netif_rx_complete(netdev);
+ enable_ether_irq(netdev);
+ }
+ }
+
+ spin_unlock_irqrestore(&gp->lock, flags);
+}
+#endif
+
+static int gt64240_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ unsigned long flags;
+ int nextIn;
+
+ spin_lock_irqsave(&gp->lock, flags);
+
+ nextIn = gp->tx_next_in;
+
+ if (gt64240_debug > 3) {
+ printk("%s: gt64240_tx: nextIn=%d.\n", dev->name, nextIn);
+ }
+
+ if (gp->tx_count >= TX_RING_SIZE) {
+ printk("%s: Tx Ring full, pkt dropped.\n", dev->name);
+ gp->stats.tx_dropped++;
+ spin_unlock_irqrestore(&gp->lock, flags);
+ return 1;
+ }
+
+ if (!(gp->last_psr & psrLink)) {
+ printk("%s: gt64240_tx: Link down, pkt dropped.\n",
+ dev->name);
+ gp->stats.tx_dropped++;
+ spin_unlock_irqrestore(&gp->lock, flags);
+//--- dump_MII(dev); /* KLUDGE ALERT !!! */
+ return 1;
+ }
+
+ if (gp->tx_ring[nextIn].cmdstat & txOwn) {
+ printk
+ ("%s: gt64240_tx: device owns descriptor, pkt dropped.\n",
+ dev->name);
+ gp->stats.tx_dropped++;
+ // stop the queue, so Tx timeout can fix it
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&gp->lock, flags);
+ return 1;
+ }
+ // Prepare the Descriptor at tx_next_in
+ gp->tx_skbuff[nextIn] = skb;
+ gp->tx_ring[nextIn].byte_cnt = skb->len;
+ gp->tx_ring[nextIn].buff_ptr = virt_to_phys(skb->data);
+
+ // make sure packet gets written back to memory
+ dma_cache_wback_inv((unsigned long) (skb->data), skb->len);
+ mb();
+
+ // Give ownership to device, set first and last desc, enable interrupt
+ // Setting of ownership bit must be *last*!
+ gp->tx_ring[nextIn].cmdstat =
+ txOwn | txGenCRC | txEI | txPad | txFirst | txLast;
+
+ if (gt64240_debug > 5) {
+ dump_tx_desc(dev, nextIn);
+ dump_skb(dev, skb);
+ }
+ // increment tx_next_in with wrap
+ gp->tx_next_in = (nextIn + 1) % TX_RING_SIZE;
+
+//+prk20aug01:
+ if (0) { /* ROLLINS */
+ GT64240ETH_WRITE(gp, GT64240_ETH_CURR_TX_DESC_PTR0,
+ virt_to_phys(&gp->tx_ring[nextIn]));
+ }
+
+ if (gt64240_debug > 3) { /*+prk17aug01 */
+ printk
+ ("%s: gt64240_tx: TX_PTR0=0x%08x, EthPortStatus=0x%08x\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_CURR_TX_DESC_PTR0),
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS));
+ }
+ // If DMA is stopped, restart
+ if (!((GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS)) & psrTxLow)) {
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM,
+ sdcmrERD | sdcmrTXDL);
+ }
+
+ if (gt64240_debug > 3) { /*+prk17aug01 */
+ printk
+ ("%s: gt64240_tx: TX_PTR0=0x%08x, EthPortStatus=0x%08x\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_CURR_TX_DESC_PTR0),
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS));
+ }
+ // increment count and stop queue if full
+ if (++gp->tx_count >= TX_RING_SIZE) {
+ gp->tx_full = 1;
+ netif_stop_queue(dev);
+ }
+
+ dev->trans_start = jiffies;
+ spin_unlock_irqrestore(&gp->lock, flags);
+
+ return 0;
+}
+
+
+static int
+#ifdef GT64240_NAPI
+gt64240_rx(struct net_device *dev, u32 status, int budget)
+#else
+gt64240_rx(struct net_device *dev, u32 status)
+#endif
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ struct sk_buff *skb;
+ int pkt_len, nextOut, cdp;
+ gt64240_rd_t *rd;
+ u32 cmdstat;
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_rx: dev=%p, status=%x\n",
+ dev->name, dev, status);
+
+ cdp = (GT64240ETH_READ(gp, GT64240_ETH_CURR_RX_DESC_PTR0)
+ - gp->rx_ring_dma) / sizeof(gt64240_rd_t);
+
+ // Continue until we reach the current descriptor pointer
+ for (nextOut = gp->rx_next_out; nextOut != cdp;
+ nextOut = (nextOut + 1) % RX_RING_SIZE) {
+
+#ifdef GT64240_NAPI
+ if (budget <= 0)
+ break;
+
+ budget--;
+#endif
+
+ if (--gp->intr_work_done == 0)
+ break;
+
+ if (gt64240_debug > 4)
+ dump_rx_desc(dev, nextOut);
+
+ rd = &gp->rx_ring[nextOut];
+ cmdstat = rd->cmdstat;
+
+ if (gt64240_debug > 3)
+ printk("%s: isr: Rx desc cmdstat=%x, nextOut=%d\n",
+ dev->name, cmdstat, nextOut);
+
+ if (cmdstat & (u32) rxOwn) {
+ if (gt64240_debug > 2)
+ printk
+ ("%s: gt64240_rx: device owns descriptor!\n",
+ dev->name);
+ // DMA is not finished updating descriptor???
+ // Leave and come back later to pick-up where we left off.
+ break;
+ }
+ // must be first and last (ie only) buffer of packet
+ if (!(cmdstat & (u32) rxFirst)
+ || !(cmdstat & (u32) rxLast)) {
+ printk
+ ("%s: gt64240_rx: desc not first and last!\n",
+ dev->name);
+ cmdstat |= (u32) rxOwn;
+ rd->cmdstat = cmdstat;
+ continue;
+ }
+ // Drop this received pkt if there were any errors
+ if ((cmdstat & (u32) rxErrorSummary)
+ || (status & icrRxError)) {
+ // update the detailed rx error counters that are not covered
+ // by the MIB counters.
+ if (cmdstat & (u32) rxOverrun)
+ gp->stats.rx_fifo_errors++;
+ cmdstat |= (u32) rxOwn;
+ rd->cmdstat = cmdstat;
+ continue;
+ }
+
+ pkt_len = rd->byte_cnt;
+
+ /* Create new skb. */
+// skb = dev_alloc_skb(pkt_len+2);
+ skb = dev_alloc_skb(1538);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ gp->stats.rx_dropped++;
+ cmdstat |= (u32) rxOwn;
+ rd->cmdstat = cmdstat;
+ continue;
+ }
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16 byte IP header align */
+ memcpy(skb_put(skb, pkt_len),
+ &gp->rx_buff[nextOut * PKT_BUF_SZ], pkt_len);
+ skb->protocol = eth_type_trans(skb, dev);
+ if (gt64240_debug > 4) /* will probably Oops! */
+ dump_data(dev, &gp->rx_buff[nextOut * PKT_BUF_SZ],
+ pkt_len);
+ if (gt64240_debug > 4)
+ dump_skb(dev, skb);
+
+ /* NIC performed some checksum computation */
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+#ifdef GT64240_NAPI
+ netif_receive_skb(skb);
+#else
+ netif_rx(skb); /* pass the packet to upper layers */
+#endif
+
+ // now we can release ownership of this desc back to device
+ cmdstat |= (u32) rxOwn;
+ rd->cmdstat = cmdstat;
+
+ dev->last_rx = jiffies;
+ }
+
+ if (gt64240_debug > 3 && nextOut == gp->rx_next_out)
+ printk("%s: gt64240_rx: RxCDP did not increment?\n",
+ dev->name);
+
+ gp->rx_next_out = nextOut;
+ return 0;
+}
+
+
+static void gt64240_tx_complete(struct net_device *dev, u32 status)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ int nextOut, cdp;
+ gt64240_td_t *td;
+ u32 cmdstat;
+
+ cdp = (GT64240ETH_READ(gp, GT64240_ETH_CURR_TX_DESC_PTR0)
+ - gp->tx_ring_dma) / sizeof(gt64240_td_t);
+
+ if (gt64240_debug > 3) { /*+prk17aug01 */
+ nextOut = gp->tx_next_out;
+ printk
+ ("%s: tx_complete: TX_PTR0=0x%08x, cdp=%d. nextOut=%d.\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_CURR_TX_DESC_PTR0),
+ cdp, nextOut);
+ td = &gp->tx_ring[nextOut];
+ }
+
+/*** NEED to check and CLEAR these errors every time thru here: ***/
+ if (gt64240_debug > 2) {
+ if (GT64240_READ(COMM_UNIT_INTERRUPT_CAUSE))
+ printk
+ ("%s: gt64240_tx_complete: CIU Cause=%08x, Mask=%08x, EAddr=%08x\n",
+ dev->name,
+ GT64240_READ(COMM_UNIT_INTERRUPT_CAUSE),
+ GT64240_READ(COMM_UNIT_INTERRUPT_MASK),
+ GT64240_READ(COMM_UNIT_ERROR_ADDRESS));
+ GT64240_WRITE(COMM_UNIT_INTERRUPT_CAUSE, 0);
+ }
+ // Continue until we reach the current descriptor pointer
+ for (nextOut = gp->tx_next_out; nextOut != cdp;
+ nextOut = (nextOut + 1) % TX_RING_SIZE) {
+
+ if (--gp->intr_work_done == 0)
+ break;
+
+ td = &gp->tx_ring[nextOut];
+ cmdstat = td->cmdstat;
+
+ if (cmdstat & (u32) txOwn) {
+ // DMA is not finished writing descriptor???
+ // Leave and come back later to pick-up where we left off.
+ break;
+ }
+ // increment Tx error stats
+ if (cmdstat & (u32) txErrorSummary) {
+ if (gt64240_debug > 2)
+ printk
+ ("%s: tx_complete: Tx error, cmdstat = %x\n",
+ dev->name, cmdstat);
+ gp->stats.tx_errors++;
+ if (cmdstat & (u32) txReTxLimit)
+ gp->stats.tx_aborted_errors++;
+ if (cmdstat & (u32) txUnderrun)
+ gp->stats.tx_fifo_errors++;
+ if (cmdstat & (u32) txLateCollision)
+ gp->stats.tx_window_errors++;
+ }
+
+ if (cmdstat & (u32) txCollision)
+ gp->stats.collisions +=
+ (unsigned long) ((cmdstat & txReTxCntMask) >>
+ txReTxCntBit);
+
+ // Wake the queue if the ring was full
+ if (gp->tx_full) {
+ gp->tx_full = 0;
+ if (gp->last_psr & psrLink) {
+ 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 (gt64240_debug > 3)
+ printk
+ ("%s: tx_complete: 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("%s: tx_complete: no skb!\n", dev->name);
+ }
+ }
+
+ gp->tx_next_out = nextOut;
+
+ if ((status & icrTxEndLow) && gp->tx_count != 0) {
+ // we must restart the DMA
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM,
+ sdcmrERD | sdcmrTXDL);
+ }
+}
+
+
+static void gt64240_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ u32 status;
+
+ if (dev == NULL) {
+ printk("%s: isr: null dev ptr\n", dev->name);
+ return;
+ }
+
+ spin_lock(&gp->lock);
+
+ if (gt64240_debug > 3)
+ printk("%s: isr: entry\n", dev->name);
+
+ gp->intr_work_done = max_interrupt_work;
+
+ while (gp->intr_work_done > 0) {
+
+ status = GT64240ETH_READ(gp, GT64240_ETH_INT_CAUSE);
+#ifdef GT64240_NAPI
+ /* dont ack Rx interrupts */
+ if (!(status & icrRxBuffer))
+ GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0);
+#else
+ // ACK interrupts
+ GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0);
+#endif
+
+ if (gt64240_debug > 3)
+ printk("%s: isr: work=%d., icr=%x\n", dev->name,
+ gp->intr_work_done, status);
+
+ if ((status & icrEtherIntSum) == 0) {
+ if (!(status &
+ (icrTxBufferLow | icrTxBufferHigh |
+ icrRxBuffer))) {
+ /* exit from the while() loop */
+ break;
+ }
+ }
+
+ if (status & icrMIIPhySTC) {
+ u32 psr =
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_STATUS);
+ if (gp->last_psr != psr) {
+ printk("%s: port status: 0x%08x\n",
+ dev->name, psr);
+ 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");
+
+ if ((psr & psrLink) && !gp->tx_full &&
+ netif_queue_stopped(dev)) {
+ printk
+ ("%s: isr: Link up, waking queue.\n",
+ dev->name);
+ netif_wake_queue(dev);
+ } else if (!(psr & psrLink)
+ && !netif_queue_stopped(dev)) {
+ printk
+ ("%s: isr: Link down, stopping queue.\n",
+ dev->name);
+ netif_stop_queue(dev);
+ }
+
+ gp->last_psr = psr;
+ }
+ }
+
+ if (status & (icrTxBufferLow | icrTxEndLow))
+ gt64240_tx_complete(dev, status);
+
+ if (status & icrRxBuffer) {
+#ifdef GT64240_NAPI
+ if (netif_rx_schedule_prep(dev)) {
+ disable_ether_irq(dev);
+ __netif_rx_schedule(dev);
+ }
+#else
+ gt64240_rx(dev, status);
+#endif
+ }
+ // Now check TX errors (RX errors were handled in gt64240_rx)
+ if (status & icrTxErrorLow) {
+ printk("%s: isr: Tx resource error\n", dev->name);
+ }
+
+ if (status & icrTxUdr) {
+ printk("%s: isr: Tx underrun error\n", dev->name);
+ }
+ }
+
+ if (gp->intr_work_done == 0) {
+ // ACK any remaining pending interrupts
+ GT64240ETH_WRITE(gp, GT64240_ETH_INT_CAUSE, 0);
+ if (gt64240_debug > 3)
+ printk("%s: isr: hit max work\n", dev->name);
+ }
+
+ if (gt64240_debug > 3)
+ printk("%s: isr: exit, icr=%x\n",
+ dev->name, GT64240ETH_READ(gp,
+ GT64240_ETH_INT_CAUSE));
+
+ spin_unlock(&gp->lock);
+}
+
+
+static void gt64240_tx_timeout(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gp->lock, flags);
+
+
+ if (!(gp->last_psr & psrLink)) {
+ spin_unlock_irqrestore(&gp->lock, flags);
+ } else {
+ printk("======------> gt64240_tx_timeout: %d jiffies \n",
+ GT64240ETH_TX_TIMEOUT);
+
+ disable_ether_irq(dev);
+ spin_unlock_irqrestore(&gp->lock, flags);
+ reset_tx(dev);
+ enable_ether_irq(dev);
+
+ netif_wake_queue(dev);
+ }
+}
+
+
+static void gt64240_set_rx_mode(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ unsigned long flags;
+ struct dev_mc_list *mcptr;
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_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)
+ GT64240ETH_SETBIT(gp, GT64240_ETH_PORT_CONFIG, pcrPM);
+ else
+ GT64240ETH_CLRBIT(gp, GT64240_ETH_PORT_CONFIG, pcrPM);
+/*
+ GT64240ETH_WRITE(gp, GT64240_ETH_PORT_CONFIG,
+ (PORT_CONFIG | pcrPM | pcrEN));
+*/
+
+ memset(gp->hash_table, 0, RX_HASH_TABLE_SIZE); // clear hash table
+ // Add our ethernet address
+ gt64240_add_hash_entry(dev, dev->dev_addr);
+ if (dev->mc_count) {
+ for (mcptr = dev->mc_list; mcptr; mcptr = mcptr->next) {
+ if (gt64240_debug > 2) {
+ printk("%s: gt64240_set_rx_mode: addr=\n",
+ dev->name);
+ dump_hw_addr(mcptr->dmi_addr);
+ }
+ gt64240_add_hash_entry(dev, mcptr->dmi_addr);
+ }
+ }
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_set_rx: Port Config=%x\n", dev->name,
+ GT64240ETH_READ(gp, GT64240_ETH_PORT_CONFIG));
+
+ // restart Rx DMA
+ GT64240ETH_WRITE(gp, GT64240_ETH_SDMA_COMM, sdcmrERD);
+
+ spin_unlock_irqrestore(&gp->lock, flags);
+}
+
+static struct net_device_stats *gt64240_get_stats(struct net_device *dev)
+{
+ struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
+ unsigned long flags;
+
+ if (gt64240_debug > 3)
+ printk("%s: gt64240_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;
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)