/**************************************************************************
 *
 *  BRIEF MODULE DESCRIPTION
 *     Driver for the IDT RC32434 on-chip ethernet controller.
 *
 *  Copyright 2004 IDT Inc. (rischelp@idt.com)
 *  Copyright 2006 Felix Fietkau <nbd@openwrt.org>
 *         
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 **************************************************************************
 * May 2004 rkt, neb
 *
 * Based on the driver developed by B. Maruthanayakam, H. Kou and others.
 *
 * Aug 2004 Sadik
 *
 * Added NAPI
 *
 **************************************************************************
 */

#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <asm/bootinfo.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/dma.h>

#include <asm/rc32434/rb.h>
#include "rc32434_eth.h"

#define DRIVER_VERSION "(mar2904)"

#define DRIVER_NAME "rc32434 Ethernet driver. " DRIVER_VERSION

#define STATION_ADDRESS_HIGH(dev) (((dev)->dev_addr[0] << 8) | \
			           ((dev)->dev_addr[1]))
#define STATION_ADDRESS_LOW(dev)  (((dev)->dev_addr[2] << 24) | \
				   ((dev)->dev_addr[3] << 16) | \
				   ((dev)->dev_addr[4] << 8)  | \
				   ((dev)->dev_addr[5]))

#define MII_CLOCK 1250000 				/* no more than 2.5MHz */
#define CONFIG_IDT_USE_NAPI 1


static inline void rc32434_abort_tx(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	rc32434_abort_dma(dev, lp->tx_dma_regs);
	
}

static inline void rc32434_abort_rx(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	rc32434_abort_dma(dev, lp->rx_dma_regs);
	
}

static inline void rc32434_start_tx(struct rc32434_local *lp,  volatile DMAD_t td)
{
	rc32434_start_dma(lp->tx_dma_regs, CPHYSADDR(td));
}

static inline void rc32434_start_rx(struct rc32434_local *lp, volatile DMAD_t rd)
{
	rc32434_start_dma(lp->rx_dma_regs, CPHYSADDR(rd));
}

static inline void rc32434_chain_tx(struct rc32434_local *lp, volatile DMAD_t td)
{
	rc32434_chain_dma(lp->tx_dma_regs, CPHYSADDR(td));
}

static inline void rc32434_chain_rx(struct rc32434_local *lp, volatile DMAD_t rd)
{
	rc32434_chain_dma(lp->rx_dma_regs, CPHYSADDR(rd));
}

#ifdef RC32434_PROC_DEBUG
static int rc32434_read_proc(char *buf, char **start, off_t fpos,
			     int length, int *eof, void *data)
{
	struct net_device *dev = (struct net_device *)data;
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	int len = 0;
	
	/* print out header */
	len += sprintf(buf + len, "\n\tKorina Ethernet Debug\n\n");
	len += sprintf (buf + len,
			"DMA halt count      = %10d, DMA run count = %10d\n",
			lp->dma_halt_cnt, lp->dma_run_cnt);
	
	if (fpos >= len) {
		*start = buf;
		*eof = 1;
		return 0;
	}
	*start = buf + fpos;
	
	if ((len -= fpos) > length) 
		return length;	
	*eof = 1;
	
	return len;
	
}
#endif


/*
 * Restart the RC32434 ethernet controller. 
 */
static int rc32434_restart(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	
	/*
	 * Disable interrupts
	 */
	disable_irq(lp->rx_irq);
	disable_irq(lp->tx_irq);
#ifdef	RC32434_REVISION
	disable_irq(lp->ovr_irq);
#endif	
	disable_irq(lp->und_irq);
	
	/* Mask F E bit in Tx DMA */
	__raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) | DMASM_f_m | DMASM_e_m, &lp->tx_dma_regs->dmasm);
	/* Mask D H E bit in Rx DMA */
	__raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) | DMASM_d_m | DMASM_h_m | DMASM_e_m, &lp->rx_dma_regs->dmasm);
	
	rc32434_init(dev);
	rc32434_multicast_list(dev);
	
	enable_irq(lp->und_irq);
#ifdef	RC32434_REVISION
	enable_irq(lp->ovr_irq);
#endif
	enable_irq(lp->tx_irq);
	enable_irq(lp->rx_irq);
	
	return 0;
}

static int rc32434_probe(struct platform_device *pdev)
{
	struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data;
	struct rc32434_local *lp = NULL;
	struct net_device *dev = NULL;
	struct resource *r;
	int i, retval,err;
	
	dev = alloc_etherdev(sizeof(struct rc32434_local));
	if(!dev) {
		ERR("Korina_eth: alloc_etherdev failed\n");
		return -1;
	}

	platform_set_drvdata(pdev, dev);
	SET_NETDEV_DEV(dev, &pdev->dev);
	bif->dev = dev;
	
	memcpy(dev->dev_addr, bif->mac, 6);

	/* Initialize the device structure. */
	if (dev->priv == NULL) {
		lp = (struct rc32434_local *)kmalloc(sizeof(*lp), GFP_KERNEL);
		memset(lp, 0, sizeof(struct rc32434_local));
	} 
	else {
		lp = (struct rc32434_local *)dev->priv;
	}
	
	lp->rx_irq = platform_get_irq_byname(pdev, "korina_rx");
	lp->tx_irq = platform_get_irq_byname(pdev, "korina_tx");
	lp->ovr_irq = platform_get_irq_byname(pdev, "korina_ovr");
	lp->und_irq = platform_get_irq_byname(pdev, "korina_und");

	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_regs");
	dev->base_addr = r->start;
	lp->eth_regs = ioremap_nocache(r->start, r->end - r->start);
	if (!lp->eth_regs) {
		ERR("Can't remap eth registers\n");
		retval = -ENXIO;
		goto probe_err_out;
	}

	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_rx");
	lp->rx_dma_regs = ioremap_nocache(r->start, r->end - r->start);
	if (!lp->rx_dma_regs) {
		ERR("Can't remap Rx DMA registers\n");
		retval = -ENXIO;
		goto probe_err_out;
	}

	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "korina_dma_tx");
	lp->tx_dma_regs = ioremap_nocache(r->start, r->end - r->start);
	if (!lp->tx_dma_regs) {
		ERR("Can't remap Tx DMA registers\n");
		retval = -ENXIO;
		goto probe_err_out;
	}
	
#ifdef RC32434_PROC_DEBUG
	lp->ps = create_proc_read_entry (bif->name, 0, proc_net,
					 rc32434_read_proc, dev);
#endif
	
	lp->td_ring =	(DMAD_t)kmalloc(TD_RING_SIZE + RD_RING_SIZE, GFP_KERNEL);
	if (!lp->td_ring) {
		ERR("Can't allocate descriptors\n");
		retval = -ENOMEM;
		goto probe_err_out;
	}
	
	dma_cache_inv((unsigned long)(lp->td_ring), TD_RING_SIZE + RD_RING_SIZE);
	
	/* now convert TD_RING pointer to KSEG1 */
	lp->td_ring = (DMAD_t )KSEG1ADDR(lp->td_ring);
	lp->rd_ring = &lp->td_ring[RC32434_NUM_TDS];
	
	
	spin_lock_init(&lp->lock);
	
	/* just use the rx dma irq */
	dev->irq = lp->rx_irq;
	
	dev->priv = lp;
	lp->dev = dev;
	
	dev->open = rc32434_open;
	dev->stop = rc32434_close;
	dev->hard_start_xmit = rc32434_send_packet;
	dev->get_stats	= rc32434_get_stats;
	dev->set_multicast_list = &rc32434_multicast_list;
	dev->tx_timeout = rc32434_tx_timeout;
	dev->watchdog_timeo = RC32434_TX_TIMEOUT;

	netif_napi_add(dev, &lp->napi, rc32434_poll, 64);
	lp->tx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
	tasklet_init(lp->tx_tasklet, rc32434_tx_tasklet, (unsigned long)dev);
	
	if ((err = register_netdev(dev))) {
		printk(KERN_ERR "rc32434 ethernet. Cannot register net device %d\n", err);
		free_netdev(dev);
		retval = -EINVAL;
		goto probe_err_out;
	}
	
	INFO("Rx IRQ %d, Tx IRQ %d, ", lp->rx_irq, lp->tx_irq);
	for (i = 0; i < 6; i++) {
		printk("%2.2x", dev->dev_addr[i]);
		if (i<5)
			printk(":");
	}
	printk("\n");
	
	return 0;
	
 probe_err_out:
	rc32434_cleanup_module();
	ERR(" failed.  Returns %d\n", retval);
	return retval;
	
}

static int rc32434_remove(struct platform_device *pdev)
{
	struct korina_device *bif = (struct korina_device *) pdev->dev.platform_data;
	
	if (bif->dev != NULL) {
		struct rc32434_local *lp = (struct rc32434_local *)bif->dev->priv;
		if (lp != NULL) {
			if (lp->eth_regs)
				iounmap((void*)lp->eth_regs);
			if (lp->rx_dma_regs)
				iounmap((void*)lp->rx_dma_regs);
			if (lp->tx_dma_regs)
				iounmap((void*)lp->tx_dma_regs);
			if (lp->td_ring)
				kfree((void*)KSEG0ADDR(lp->td_ring));
			
#ifdef RC32434_PROC_DEBUG
			if (lp->ps) {
				remove_proc_entry(bif->name, proc_net);
			}
#endif
			kfree(lp);
		}
		
		platform_set_drvdata(pdev, NULL);
		unregister_netdev(bif->dev);
		free_netdev(bif->dev);
		kfree(bif->dev);
	}
	return 0;
}


static int rc32434_open(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	
	/* Initialize */
	if (rc32434_init(dev)) {
		ERR("Error: cannot open the Ethernet device\n");
		return -EAGAIN;
	}
	
	/* Install the interrupt handler that handles the Done Finished Ovr and Und Events */	
	if (request_irq(lp->rx_irq, &rc32434_rx_dma_interrupt,
		  IRQF_SHARED | IRQF_DISABLED,
			"Korina ethernet Rx", dev)) {
		ERR(": unable to get Rx DMA IRQ %d\n",
		    lp->rx_irq);
		return -EAGAIN;
	}
	if (request_irq(lp->tx_irq, &rc32434_tx_dma_interrupt,
		  IRQF_SHARED | IRQF_DISABLED,
			"Korina ethernet Tx", dev)) {
		ERR(": unable to get Tx DMA IRQ %d\n",
		    lp->tx_irq);
		free_irq(lp->rx_irq, dev);
		return -EAGAIN;
	}
	
#ifdef	RC32434_REVISION
	/* Install handler for overrun error. */
	if (request_irq(lp->ovr_irq, &rc32434_ovr_interrupt,
			IRQF_SHARED | IRQF_DISABLED,
			"Ethernet Overflow", dev)) {
		ERR(": unable to get OVR IRQ %d\n",
		    lp->ovr_irq);
		free_irq(lp->rx_irq, dev);
		free_irq(lp->tx_irq, dev);
		return -EAGAIN;
	}
#endif
	
	/* Install handler for underflow error. */
	if (request_irq(lp->und_irq, &rc32434_und_interrupt,
			IRQF_SHARED | IRQF_DISABLED,
			"Ethernet Underflow", dev)) {
		ERR(": unable to get UND IRQ %d\n",
		    lp->und_irq);
		free_irq(lp->rx_irq, dev);
		free_irq(lp->tx_irq, dev);
#ifdef	RC32434_REVISION		
		free_irq(lp->ovr_irq, dev);		
#endif
		return -EAGAIN;
	}
	
	
	return 0;
}




static int rc32434_close(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	u32 tmp;
	
	/* Disable interrupts */
	disable_irq(lp->rx_irq);
	disable_irq(lp->tx_irq);
#ifdef	RC32434_REVISION
	disable_irq(lp->ovr_irq);
#endif
	disable_irq(lp->und_irq);
	
	tmp = __raw_readl(&lp->tx_dma_regs->dmasm);
	tmp = tmp | DMASM_f_m | DMASM_e_m;
	__raw_writel(tmp, &lp->tx_dma_regs->dmasm);
	
	tmp = __raw_readl(&lp->rx_dma_regs->dmasm);
	tmp = tmp | DMASM_d_m | DMASM_h_m | DMASM_e_m;
	__raw_writel(tmp, &lp->rx_dma_regs->dmasm);
	
	free_irq(lp->rx_irq, dev);
	free_irq(lp->tx_irq, dev);
#ifdef	RC32434_REVISION	
	free_irq(lp->ovr_irq, dev);
#endif
	free_irq(lp->und_irq, dev);
	return 0;
}


/* transmit packet */
static int rc32434_send_packet(struct sk_buff *skb, struct net_device *dev)
{
	struct rc32434_local		*lp = (struct rc32434_local *)dev->priv;
	unsigned long 			flags;
	u32					length;
	DMAD_t				td;
	
	
	spin_lock_irqsave(&lp->lock, flags);
	
	td = &lp->td_ring[lp->tx_chain_tail];
	
	/* stop queue when full, drop pkts if queue already full */
	if(lp->tx_count >= (RC32434_NUM_TDS - 2)) {
		lp->tx_full = 1;
		
		if(lp->tx_count == (RC32434_NUM_TDS - 2)) {
			netif_stop_queue(dev);
		}
		else {
			lp->stats.tx_dropped++;
			dev_kfree_skb_any(skb);
			spin_unlock_irqrestore(&lp->lock, flags);
			return 1;
		}	   
	}	 
	
	lp->tx_count ++;
	
	lp->tx_skb[lp->tx_chain_tail] = skb;
	
	length = skb->len;
	dma_cache_wback((u32)skb->data, skb->len);
	
	/* Setup the transmit descriptor. */
	dma_cache_inv((u32) td, sizeof(*td));
	td->ca = CPHYSADDR(skb->data);
	
	if(__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0) {
		if( lp->tx_chain_status == empty ) {
			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;                                /*  Update tail      */
			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /*   Move tail       */
			__raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR    */
			lp->tx_chain_head = lp->tx_chain_tail;                                                  /* Move head to tail */
		}
		else {
			td->control = DMA_COUNT(length) |DMAD_cof_m|DMAD_iof_m;                                 /* Update tail */
			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &=  ~(DMAD_cof_m);          /* Link to prev */
			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link =  CPHYSADDR(td);              /* Link to prev */
			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /* Move tail */
			__raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr)); /* Write to NDPTR */
			lp->tx_chain_head = lp->tx_chain_tail;                                                  /* Move head to tail */
			lp->tx_chain_status = empty;
		}
	}
	else {
		if( lp->tx_chain_status == empty ) {
			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;                                /* Update tail */
			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /* Move tail */
			lp->tx_chain_status = filled;
		}
		else {
			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;                                /* Update tail */
			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].control &=  ~(DMAD_cof_m);          /* Link to prev */
			lp->td_ring[(lp->tx_chain_tail-1)& RC32434_TDS_MASK].link =  CPHYSADDR(td);              /* Link to prev */
			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & RC32434_TDS_MASK;                          /* Move tail */
		}
	}
	dma_cache_wback((u32) td, sizeof(*td));
	
	dev->trans_start = jiffies;				
	
	spin_unlock_irqrestore(&lp->lock, flags);
	
	return 0;
}


/* Ethernet MII-PHY Handler */
static void rc32434_mii_handler(unsigned long data)
{
	struct net_device *dev = (struct net_device *)data;		
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	unsigned long 	flags;
	unsigned long duplex_status;
	int port_addr = (lp->rx_irq == 0x2c? 1:0) << 8;
	
	spin_lock_irqsave(&lp->lock, flags);
	
	/* Two ports are using the same MII, the difference is the PHY address */
	__raw_writel(0, &rc32434_eth0_regs->miimcfg);  
	__raw_writel(0, &rc32434_eth0_regs->miimcmd);  
	__raw_writel(port_addr |0x05, &rc32434_eth0_regs->miimaddr);  
	__raw_writel(MIIMCMD_scn_m, &rc32434_eth0_regs->miimcmd);  
	while(__raw_readl(&rc32434_eth0_regs->miimind) & MIIMIND_nv_m);
	
	ERR("irq:%x		port_addr:%x	RDD:%x\n", 
	    lp->rx_irq, port_addr, __raw_readl(&rc32434_eth0_regs->miimrdd));
	duplex_status = (__raw_readl(&rc32434_eth0_regs->miimrdd) & 0x140)? ETHMAC2_fd_m: 0;
	if(duplex_status != lp->duplex_mode) {
		ERR("The MII-PHY is Auto-negotiated to %s-Duplex mode for Eth-%x\n", duplex_status? "Full":"Half", lp->rx_irq == 0x2c? 1:0);		
		lp->duplex_mode = duplex_status;
		rc32434_restart(dev);		
	}
	
	lp->mii_phy_timer.expires = jiffies + 10 * HZ;	
	add_timer(&lp->mii_phy_timer);
	
	spin_unlock_irqrestore(&lp->lock, flags);
	
}

#ifdef	RC32434_REVISION	
/* Ethernet Rx Overflow interrupt */
static irqreturn_t
rc32434_ovr_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = (struct net_device *)dev_id;
	struct rc32434_local *lp;
	unsigned int ovr;
	irqreturn_t retval = IRQ_NONE;
	
	ASSERT(dev != NULL);
	
	lp = (struct rc32434_local *)dev->priv;
	spin_lock(&lp->lock);
	ovr = __raw_readl(&lp->eth_regs->ethintfc);
	
	if(ovr & ETHINTFC_ovr_m) {
		netif_stop_queue(dev);
		
		/* clear OVR bit */
		__raw_writel((ovr & ~ETHINTFC_ovr_m), &lp->eth_regs->ethintfc);
		
		/* Restart interface */
		rc32434_restart(dev);
		retval = IRQ_HANDLED;
	}
	spin_unlock(&lp->lock);
	
	return retval;
}

#endif


/* Ethernet Tx Underflow interrupt */
static irqreturn_t
rc32434_und_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = (struct net_device *)dev_id;
	struct rc32434_local *lp;
	unsigned int und;
	irqreturn_t retval = IRQ_NONE;
	
	ASSERT(dev != NULL);
	
	lp = (struct rc32434_local *)dev->priv;
	
	spin_lock(&lp->lock);
	
	und = __raw_readl(&lp->eth_regs->ethintfc);
	
	if(und & ETHINTFC_und_m) {
		netif_stop_queue(dev);
		
		__raw_writel((und & ~ETHINTFC_und_m), &lp->eth_regs->ethintfc);
		
		/* Restart interface */
		rc32434_restart(dev);
		retval = IRQ_HANDLED;
	}
	
	spin_unlock(&lp->lock);
	
	return retval;
}


/* Ethernet Rx DMA interrupt */
static irqreturn_t
rc32434_rx_dma_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = (struct net_device *)dev_id;
	struct rc32434_local* lp;
	volatile u32 dmas,dmasm;
	irqreturn_t retval;
	
	ASSERT(dev != NULL);
	
	lp = (struct rc32434_local *)dev->priv;
	
	dmas = __raw_readl(&lp->rx_dma_regs->dmas);
	if(dmas & (DMAS_d_m|DMAS_h_m|DMAS_e_m)) {
		/* Mask D H E bit in Rx DMA */
		dmasm = __raw_readl(&lp->rx_dma_regs->dmasm);
		__raw_writel(dmasm | (DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm);
		netif_rx_schedule(dev, &lp->napi);

		if (dmas & DMAS_e_m)
			ERR(": DMA error\n");
		
		retval = IRQ_HANDLED;
	}
	else
		retval = IRQ_NONE;
	
	return retval;
}


static int rc32434_rx(struct net_device *dev, int limit)
{
        struct rc32434_local *lp = netdev_priv(dev);
	volatile DMAD_t  rd = &lp->rd_ring[lp->rx_next_done];
        struct sk_buff *skb, *skb_new;
        u8 *pkt_buf;
        u32 devcs, pkt_len, dmas, rx_free_desc;
	u32 pktuncrc_len;
        int count;

	dma_cache_inv((u32)rd, sizeof(*rd));
	for (count = 0; count < limit; count++) {
		/* init the var. used for the later operations within the while loop */
		skb_new = NULL;
		devcs = rd->devcs;
		pkt_len = RCVPKT_LENGTH(devcs);
		skb = lp->rx_skb[lp->rx_next_done];
      
		if ((devcs & ( ETHRX_ld_m)) !=	ETHRX_ld_m) {
			/* check that this is a whole packet */
			/* WARNING: DMA_FD bit incorrectly set in Rc32434 (errata ref #077) */
			lp->stats.rx_errors++;
			lp->stats.rx_dropped++;
		}
		else if ( (devcs & ETHRX_rok_m)  ) {
			
				/* must be the (first and) last descriptor then */
				pkt_buf = (u8*)lp->rx_skb[lp->rx_next_done]->data;
				
				pktuncrc_len = pkt_len - 4;
				/* invalidate the cache */
				dma_cache_inv((unsigned long)pkt_buf, pktuncrc_len);
				
				/* Malloc up new buffer. */					  
				skb_new = netdev_alloc_skb(dev, RC32434_RBSIZE + 2);					             	
				
				if (skb_new != NULL){
					/* Make room */
					skb_put(skb, pktuncrc_len);		    
					
					skb->protocol = eth_type_trans(skb, dev);
					
					/* pass the packet to upper layers */
					netif_receive_skb(skb);
					
					dev->last_rx = jiffies;
					lp->stats.rx_packets++;
					lp->stats.rx_bytes += pktuncrc_len;
					
					if (IS_RCV_MP(devcs))
						lp->stats.multicast++;
					
					/* 16 bit align */						  
					skb_reserve(skb_new, 2);	
					
					skb_new->dev = dev;
					lp->rx_skb[lp->rx_next_done] = skb_new;
				}
				else {
					ERR("no memory, dropping rx packet.\n");
					lp->stats.rx_errors++;		
					lp->stats.rx_dropped++;					
				}
		}			
		else {
			/* This should only happen if we enable accepting broken packets */
			lp->stats.rx_errors++;
			lp->stats.rx_dropped++;
			
			/* add statistics counters */
			if (IS_RCV_CRC_ERR(devcs)) {
				DBG(2, "RX CRC error\n");
				lp->stats.rx_crc_errors++;
			} 
			else if (IS_RCV_LOR_ERR(devcs)) {
				DBG(2, "RX LOR error\n");
				lp->stats.rx_length_errors++;
			}				
			else if (IS_RCV_LE_ERR(devcs)) {
				DBG(2, "RX LE error\n");
				lp->stats.rx_length_errors++;
			}
			else if (IS_RCV_OVR_ERR(devcs)) {
				lp->stats.rx_over_errors++;
			}
			else if (IS_RCV_CV_ERR(devcs)) {
				/* code violation */
				DBG(2, "RX CV error\n");
				lp->stats.rx_frame_errors++;
			}
			else if (IS_RCV_CES_ERR(devcs)) {
				DBG(2, "RX Preamble error\n");
			}
		}
		rd->devcs = 0;
		
		/* restore descriptor's curr_addr */
		if(skb_new) {
			rd->ca = CPHYSADDR(skb_new->data);
		}
		else
			rd->ca = CPHYSADDR(skb->data);
		
		rd->control = DMA_COUNT(RC32434_RBSIZE) |DMAD_cod_m |DMAD_iod_m;
		lp->rd_ring[(lp->rx_next_done-1)& RC32434_RDS_MASK].control &=  ~(DMAD_cod_m); 	
		
		lp->rx_next_done = (lp->rx_next_done + 1) & RC32434_RDS_MASK;
		dma_cache_wback((u32)rd, sizeof(*rd));
		rd = &lp->rd_ring[lp->rx_next_done];
		__raw_writel( ~DMAS_d_m, &lp->rx_dma_regs->dmas);
	}	
	
	dmas = __raw_readl(&lp->rx_dma_regs->dmas);
	
	if(dmas & DMAS_h_m) {
		/* Mask off halt and error bits */
		__raw_writel( ~(DMAS_h_m | DMAS_e_m), &lp->rx_dma_regs->dmas);
#ifdef RC32434_PROC_DEBUG
		lp->dma_halt_cnt++;
#endif
		rd->devcs = 0;
		skb = lp->rx_skb[lp->rx_next_done];
		rd->ca = CPHYSADDR(skb->data);
		dma_cache_wback((u32)rd, sizeof(*rd));
		rc32434_chain_rx(lp,rd);
	}
	
	return count;
}

static int rc32434_poll(struct napi_struct *napi, int budget)
{
        struct rc32434_local *lp =
                container_of(napi, struct rc32434_local, napi);
        struct net_device *dev = lp->dev;
        int work_done;

        work_done = rc32434_rx(dev, budget);
        if (work_done < budget) {
                netif_rx_complete(dev, napi);

		/* Mask off interrupts */
                writel(readl(&lp->rx_dma_regs->dmasm) &
			(DMASM_d_m | DMASM_h_m |DMASM_e_m),
                        &lp->rx_dma_regs->dmasm);
        }
        return work_done;
}



/* Ethernet Tx DMA interrupt */
static irqreturn_t
rc32434_tx_dma_interrupt(int irq, void *dev_id)
{
	struct net_device *dev = (struct net_device *)dev_id;
	struct rc32434_local *lp;
	volatile u32 dmas,dmasm;
	irqreturn_t retval;
	
	ASSERT(dev != NULL);
	
	lp = (struct rc32434_local *)dev->priv;
	
	dmas = __raw_readl(&lp->tx_dma_regs->dmas);
	
	if (dmas & (DMAS_f_m | DMAS_e_m)) {
		dmasm = __raw_readl(&lp->tx_dma_regs->dmasm);
		/* Mask F E bit in Tx DMA */
		__raw_writel(dmasm | (DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm);
		
		tasklet_hi_schedule(lp->tx_tasklet);
		
		if(lp->tx_chain_status == filled && (__raw_readl(&(lp->tx_dma_regs->dmandptr)) == 0)) {
			__raw_writel(CPHYSADDR(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr));			
			lp->tx_chain_status = empty;
			lp->tx_chain_head = lp->tx_chain_tail;
			dev->trans_start = jiffies;
		}
		
		if (dmas & DMAS_e_m)
			ERR(": DMA error\n");
		
		retval = IRQ_HANDLED;
	}
	else
		retval = IRQ_NONE;
	
	return retval;
}


static void rc32434_tx_tasklet(unsigned long tx_data_dev)
{
	struct net_device *dev = (struct net_device *)tx_data_dev;	
	struct rc32434_local* lp = (struct rc32434_local *)dev->priv;
	volatile DMAD_t td = &lp->td_ring[lp->tx_next_done];
	u32 devcs;
	unsigned long 	flags;
	volatile u32 dmas;
	
	spin_lock_irqsave(&lp->lock, flags);
	
	/* process all desc that are done */
	while(IS_DMA_FINISHED(td->control)) {
		if(lp->tx_full == 1) {
			netif_wake_queue(dev);
			lp->tx_full = 0;
		}
		
		devcs = lp->td_ring[lp->tx_next_done].devcs;    
		if ((devcs & (ETHTX_fd_m | ETHTX_ld_m)) != (ETHTX_fd_m | ETHTX_ld_m)) {
			lp->stats.tx_errors++;
			lp->stats.tx_dropped++;				
			
			/* should never happen */
			DBG(1, __FUNCTION__ ": split tx ignored\n");
		}
		else if (IS_TX_TOK(devcs)) {
			lp->stats.tx_packets++;
			lp->stats.tx_bytes+=lp->tx_skb[lp->tx_next_done]->len;
		}
		else {
			lp->stats.tx_errors++;
			lp->stats.tx_dropped++;				
			
			/* underflow */
			if (IS_TX_UND_ERR(devcs)) 
				lp->stats.tx_fifo_errors++;
			
			/* oversized frame */
			if (IS_TX_OF_ERR(devcs))
				lp->stats.tx_aborted_errors++;
			
			/* excessive deferrals */
			if (IS_TX_ED_ERR(devcs))
				lp->stats.tx_carrier_errors++;
			
			/* collisions: medium busy */
			if (IS_TX_EC_ERR(devcs))
				lp->stats.collisions++;
			
			/* late collision */
			if (IS_TX_LC_ERR(devcs))
				lp->stats.tx_window_errors++;
			
		}
		
		/* We must always free the original skb */
		if (lp->tx_skb[lp->tx_next_done] != NULL) {
			dev_kfree_skb_any(lp->tx_skb[lp->tx_next_done]);
			lp->tx_skb[lp->tx_next_done] = NULL;
		}
		
		lp->td_ring[lp->tx_next_done].control = DMAD_iof_m;
		lp->td_ring[lp->tx_next_done].devcs = ETHTX_fd_m | ETHTX_ld_m;	
		lp->td_ring[lp->tx_next_done].link = 0;
		lp->td_ring[lp->tx_next_done].ca = 0;
		lp->tx_count --;
		
		/* go on to next transmission */
		lp->tx_next_done = (lp->tx_next_done + 1) & RC32434_TDS_MASK;
		td = &lp->td_ring[lp->tx_next_done];
		
	}
	
	dmas = __raw_readl(&lp->tx_dma_regs->dmas);
	__raw_writel( ~dmas, &lp->tx_dma_regs->dmas);
	
	/* Enable F E bit in Tx DMA */
	__raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); 
	spin_unlock_irqrestore(&lp->lock, flags);
	
}


static struct net_device_stats * rc32434_get_stats(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	return &lp->stats;
}


/*
 * Set or clear the multicast filter for this adaptor.
 */
static void rc32434_multicast_list(struct net_device *dev)
{   
	/* listen to broadcasts always and to treat 	*/
	/*       IFF bits independantly	*/
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	unsigned long flags;
	u32 recognise = ETHARC_ab_m; 		/* always accept broadcasts */
	
	if (dev->flags & IFF_PROMISC)         		/* set promiscuous mode */
		recognise |= ETHARC_pro_m;
	
	if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 15))
		recognise |= ETHARC_am_m;    	  	/* all multicast & bcast */
	else if (dev->mc_count > 0) {
		DBG(2, __FUNCTION__ ": mc_count %d\n", dev->mc_count);
		recognise |= ETHARC_am_m;    	  	/* for the time being */
	}
	
	spin_lock_irqsave(&lp->lock, flags);
	__raw_writel(recognise, &lp->eth_regs->etharc);
	spin_unlock_irqrestore(&lp->lock, flags);
}


static void rc32434_tx_timeout(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	unsigned long flags;
	
	spin_lock_irqsave(&lp->lock, flags);
	rc32434_restart(dev);
	spin_unlock_irqrestore(&lp->lock, flags);
	
}


/*
 * Initialize the RC32434 ethernet controller.
 */
static int rc32434_init(struct net_device *dev)
{
	struct rc32434_local *lp = (struct rc32434_local *)dev->priv;
	int i, j;
	
	/* Disable DMA */       
	rc32434_abort_tx(dev);
	rc32434_abort_rx(dev); 
	
	/* reset ethernet logic */ 
	__raw_writel(0, &lp->eth_regs->ethintfc);
	while((__raw_readl(&lp->eth_regs->ethintfc) & ETHINTFC_rip_m))
		dev->trans_start = jiffies;	
	
	/* Enable Ethernet Interface */ 
	__raw_writel(ETHINTFC_en_m, &lp->eth_regs->ethintfc); 
	
	tasklet_disable(lp->tx_tasklet);
	
	/* Initialize the transmit Descriptors */
	for (i = 0; i < RC32434_NUM_TDS; i++) {
		lp->td_ring[i].control = DMAD_iof_m;
		lp->td_ring[i].devcs = ETHTX_fd_m | ETHTX_ld_m;
		lp->td_ring[i].ca = 0;
		lp->td_ring[i].link = 0;
		if (lp->tx_skb[i] != NULL) {
			dev_kfree_skb_any(lp->tx_skb[i]);
			lp->tx_skb[i] = NULL;
		}
	}
	lp->tx_next_done = lp->tx_chain_head = lp->tx_chain_tail = 	lp->tx_full = lp->tx_count = 0;
	lp->	tx_chain_status = empty;
	
	/*
	 * Initialize the receive descriptors so that they
	 * become a circular linked list, ie. let the last
	 * descriptor point to the first again.
	 */
	for (i=0; i<RC32434_NUM_RDS; i++) {
		struct sk_buff *skb = lp->rx_skb[i];
		
		if (lp->rx_skb[i] == NULL) {
			skb = dev_alloc_skb(RC32434_RBSIZE + 2);
			if (skb == NULL) {
				ERR("No memory in the system\n");
				for (j = 0; j < RC32434_NUM_RDS; j ++)
					if (lp->rx_skb[j] != NULL) 
						dev_kfree_skb_any(lp->rx_skb[j]);
				
				return 1;
			}
			else {
				skb->dev = dev;
				skb_reserve(skb, 2);
				lp->rx_skb[i] = skb;
				lp->rd_ring[i].ca = CPHYSADDR(skb->data); 
				
			}
		}
		lp->rd_ring[i].control =	DMAD_iod_m | DMA_COUNT(RC32434_RBSIZE);
		lp->rd_ring[i].devcs = 0;
		lp->rd_ring[i].ca = CPHYSADDR(skb->data);
		lp->rd_ring[i].link = CPHYSADDR(&lp->rd_ring[i+1]);
		
	}
	/* loop back */
	lp->rd_ring[RC32434_NUM_RDS-1].link = CPHYSADDR(&lp->rd_ring[0]);
	lp->rx_next_done   = 0;
	
	lp->rd_ring[RC32434_NUM_RDS-1].control |= DMAD_cod_m;
	lp->rx_chain_head = 0;
	lp->rx_chain_tail = 0;
	lp->rx_chain_status = empty;
	
	__raw_writel(0, &lp->rx_dma_regs->dmas);
	/* Start Rx DMA */
	rc32434_start_rx(lp, &lp->rd_ring[0]);
	
	/* Enable F E bit in Tx DMA */
	__raw_writel(__raw_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); 
	/* Enable D H E bit in Rx DMA */
	__raw_writel(__raw_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); 
	
	/* Accept only packets destined for this Ethernet device address */
	__raw_writel(ETHARC_ab_m, &lp->eth_regs->etharc); 
	
	/* Set all Ether station address registers to their initial values */ 
	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal0); 
	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah0);
	
	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal1); 
	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah1);
	
	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal2); 
	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah2);
	
	__raw_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal3); 
	__raw_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah3); 
	
	
	/* Frame Length Checking, Pad Enable, CRC Enable, Full Duplex set */ 
	__raw_writel(ETHMAC2_pe_m | ETHMAC2_cen_m | ETHMAC2_fd_m, &lp->eth_regs->ethmac2);  
	//ETHMAC2_flc_m		ETHMAC2_fd_m	lp->duplex_mode
	
	/* Back to back inter-packet-gap */ 
	__raw_writel(0x15, &lp->eth_regs->ethipgt); 
	/* Non - Back to back inter-packet-gap */ 
	__raw_writel(0x12, &lp->eth_regs->ethipgr); 
	
	/* Management Clock Prescaler Divisor */
	/* Clock independent setting */
	__raw_writel(((idt_cpu_freq)/MII_CLOCK+1) & ~1,
		       &lp->eth_regs->ethmcp);
	
	/* don't transmit until fifo contains 48b */
	__raw_writel(48, &lp->eth_regs->ethfifott);
	
	__raw_writel(ETHMAC1_re_m, &lp->eth_regs->ethmac1);
	
	napi_enable(&lp->napi);
	tasklet_enable(lp->tx_tasklet);
	
	netif_start_queue(dev);
	
	return 0; 
}

static struct platform_driver korina_driver = {
	.driver.name = "korina",
	.probe = rc32434_probe,
	.remove = rc32434_remove,
};

static int __init rc32434_init_module(void)
{
	return platform_driver_register(&korina_driver);
}

static void rc32434_cleanup_module(void)
{
	return platform_driver_unregister(&korina_driver);
}

module_init(rc32434_init_module);
module_exit(rc32434_cleanup_module);
