/*
 * 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
 *
 * Modified for later version of Linux 2.4 kernel
 * Manish Lachwani (lachwani@pmc-sierra.com) - 04/29/2004
 */
#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

static int debug = -1;

#define GT64240_MSG_ENABLE	(NETIF_MSG_DRV          | \
				NETIF_MSG_PROBE        | \
				NETIF_MSG_LINK)


/********************************************************/

// 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);
}

static void gp_get_drvinfo (struct net_device *dev, 
				struct ethtool_drvinfo *info)
{
	strcpy(info->driver, "gt64260");
	strcpy(info->version, version);
}

static int gp_get_settings(struct net_device *dev, 
				struct ethtool_cmd *cmd)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	int rc;

	spin_lock_irq(&gp->lock);
	rc = mii_ethtool_gset(&gp->mii_if, cmd);
	spin_unlock_irq(&gp->lock);
	return rc;
}

static int gp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	int rc;

	spin_lock_irq(&gp->lock);
	rc = mii_ethtool_sset(&gp->mii_if, cmd);
	spin_unlock_irq(&gp->lock);
	return rc;
}

static int gp_nway_reset(struct net_device *dev)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	return mii_nway_restart(&gp->mii_if);
}

static u32 gp_get_link(struct net_device *dev)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	return mii_link_ok(&gp->mii_if);
}

static u32 gp_get_msglevel(struct net_device *dev)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	return gp->msg_enable;
}

static void gp_set_msglevel(struct net_device *dev, u32 value)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	gp->msg_enable = value;
}

static struct ethtool_ops gp_ethtool_ops = {
	.get_drvinfo		= gp_get_drvinfo,
	.get_settings		= gp_get_settings,
	.set_settings		= gp_set_settings,
	.nway_reset		= gp_nway_reset,
	.get_link		= gp_get_link,
	.get_msglevel		= gp_get_msglevel,
	.set_msglevel		= gp_set_msglevel,
};

static int gt64240_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct gt64240_private *gp = (struct gt64240_private *) dev->priv;
	struct mii_ioctl_data *data =
	    (struct mii_ioctl_data *) &rq->ifr_data;
	int retval;

	if (!netif_running(dev))
		return -EINVAL;

	spin_lock_irq(&gp->lock);
	retval = generic_mii_ioctl(&gp->mii_if, data, cmd, NULL);
	spin_unlock_irq(&gp->lock);

	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);
}

/*
 * 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;
	u32 cpuConfig;
	unsigned char chip_rev;

	dev = alloc_etherdev(sizeof(struct gt64240_private));
	if (!dev)
		return -ENOMEM;

	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.\n",
	     dev->name, ioaddr, irq);

	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);

	gp = dev->priv;

	gp->msg_enable = (debug < 0 ? GT64240_MSG_ENABLE : debug);
	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
	dev->ethtool_ops = &gp_ethtool_ops;

	/* Fill in the fields of the device structure with ethernet values. */
	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;

	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, &gt64240_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 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);

	return (done ? 0 : 1);
}
#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;
}
MODULE_AUTHOR("MontaVista Software, Inc.");
MODULE_DESCRIPTION("Ethernet driver for the MIPS GT96100 Advanced Communication Controller. Modified for the Gallileo/Marvell GT-64240 Communication Controller.");
MODULE_LICENSE("GPL");
