/* drivers/net/ip3902.c
 *
 * Copyright 2008 NXP Semiconductors
 *	Chris Steel <chris.steel@nxp.com>
 *
 * NXP ip3902 embedded 10/100 Ethernet controller support
 *	Based on ax88796.c, by Ben Dooks.
	Based on previous ip3902.c by Nikita V. Youshchenko
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/isapnp.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/inet_lro.h>
#include <asm/system.h>
#include <asm/io.h>

#ifdef CONFIG_MOCA_PHY
#include <gpio.h>
#endif

#if defined (CONFIG_TIME_DOCTOR)
#include <linux/timedoctor.h>
#endif

#define DRVNAME "ip3902-eth"
#define DRVVERSION "1.00"

/* #define IP3902_NAPI */

/* "Strange hardware" support macros */

/* These control endianness of descriptors and statuses.
 * If none if LITTLE_ENDIAN_xxx and BIG_ENDIAN_xxx is defined, system endian
 * is used for xxx */
#define LITTLE_ENDIAN_DESCRIPTORS
#undef BIG_ENDIAN_DESCRIPTORS
#undef LITTLE_ENDIAN_STATUSES
#define BIG_ENDIAN_STATUSES

#define ETH_RX_SKB_SIZE        0x600   /* 1536 bytes, just over max mtu */
#define TX_RING_SIZE           64
#ifdef CONFIG_INET_LRO
#define RX_RING_SIZE           128
#else
#define RX_RING_SIZE           64
#endif
#define IP3902_NAPI_WEIGHT     16
#define MAX_LRO_DESCRIPTORS    8
#define LRO_THRESHOLD          3
#define LRO_TIMEOUT            2

#define BYTES_IN_ETHERNET_CRC   4
#define MAX_DESCS_PER_SKB      (MAX_SKB_FRAGS + 1)

#define NEXT_TX(i) (((i) == TX_RING_SIZE-1) ? 0 : (i)+1)
#define NEXT_RX(i) (((i) == RX_RING_SIZE-1) ? 0 : (i)+1)

/* Access to IP3902 registers */

/* Alcatel (Packet Engines) core registers */
#define MAC1_REG		0x000	/* R/W: MAC configuration register 1 */
#define MAC2_REG		0x004	/* R/W: MAC configuration register 2 */
#define IPGT_REG		0x008	/* R/W: Back-to-Back Inter-Packet-Gap register */
#define IPGR_REG		0x00c	/* R/W: Non Back-to-Back Inter-Packet-Gap register */
#define CLRT_REG		0x010	/* R/W: Collision window / Retry register */
#define MAXF_REG		0x014	/* R/W: Maximum Frame register */
#define SUPP_REG		0x018	/* R/W: PHY Support register */
#define TEST_REG		0x01C	/* R/W: Test register */
#define MCFG_REG		0x020	/* R/W: MII Mgmt Con???guration register */
#define MCMD_REG		0x024	/* R/W: MII Mgmt Command register */
#define MADR_REG		0x028	/* R/W: MII Mgmt Address register */
#define MWTD_REG		0x02C	/* WO:  MII Mgmt Write Data register */
#define MRDD_REG		0x030	/* RO:  MII Mgmt Read Data register */
#define MIND_REG		0x034	/* RO:  MII Mgmt Indicators register */
#define SA0_REG			0x040	/* R/W: Station Address 0 register */
#define SA1_REG			0x044	/* R/W: Station Address 1 register */
#define SA2_REG			0x048	/* R/W: Station Address 2 register */

/* Control registers */
#define COMMAND_REG		0x100	/* R/W: Command register */
#define STATUS_REG		0x104	/* RO:  Status register */
#define RX_DESC_REG		0x108	/* R/W: Receive descriptor base address register */
#define RX_STATUS_REG		0x10C	/* R/W: Receive status base address register */
#define RX_DESC_NUMBER_REG	0x110	/* R/W: Receive number of descriptors register */
#define RX_PRODUCE_INDEX_REG	0x114	/* RO:  Receive produce index register */
#define RX_CONSUME_INDEX_REG	0x118	/* R/W: Receive consume index register */
#define TX_DESC_REG		0x11C	/* R/W: Non real-time transmit descriptor base address register */
#define TX_STATUS_REG		0x120	/* R/W: Non real-time transmit status base address register */
#define TX_DESC_NUMBER_REG	0x124	/* R/W: Non real-time transmit number of descriptors register */
#define TX_PRODUCE_INDEX_REG	0x128	/* R/W: Non real-time transmit produce index register */
#define TX_CONSUME_INDEX_REG	0x12C	/* RO:  Non real-time transmit consume index register */
#define TX_RT_DESC_REG		0x130	/* R/W: Real-time transmit descriptor base address register */
#define TX_RT_STATUS_REG	0x134	/* R/W: Real-time transmit status base address register */
#define TX_RT_DESC_NUMBER_REG	0x138	/* R/W: Real-time transmit number of descriptors register */
#define TX_RT_PRODUCE_INDEX_REG	0x13C	/* R/W: Real-time transmit produce index register */
#define TX_RT_CONSUME_INDEX_REG	0x140	/* RO:  Real-time transmit consume index register */
#define QOS_TIMEOUT_REG		0x148	/* R/W: Transmit quality of service time-out register */
#define TSV0_REG		0x158	/* RO:  Transmit status vector 0 register */
#define TSV1_REG		0x15C	/* RO:  Transmit status vector 1 register */
#define RSV_REG			0x160	/* RO:  Receive status vector register */
#define FC_COUNTER_REG		0x170	/* R/W: Flow control counter register */
#define FC_STATUS_REG		0x174	/* RO:  Flow control status register */

/* Rx filter registers */
#define FILTER_CTRL_REG		0x200	/* R/W: Receive filter control register */
#define FILTER_WOL_STATUS_REG	0x204	/* RO:  Receive filter WoL status register */
#define FILTER_WOL_CLEAR_REG	0x208	/* WO:  Receive filter WoL clear register */
#define HASH_FILTER_L_REG	0x210	/* R/W: Hash filter table LSBs register */
#define HASH_FILTER_H_REG	0x214	/* R/W: Hash filter table MSBs register */

/* DVP Standard registers */
#define INT_STATUS_REG		0xFE0	/* RO:  Interrupt status register */
#define INT_ENABLE_REG		0xFE4	/* R/W: Interrupt enable register */
#define INT_CLEAR_REG		0xFE8	/* WO:  Interrupt clear register */
#define INT_SET_REG		0xFEC	/* WO:  Interrupt set register */
#define POWERDOWN_REG		0xFF4	/* R/W: Power-down register */
#define MODULE_ID_REG		0xFFC	/* RO:  Module ID register */

/* Bits for MAC1 register */
#define MAC1_SOFT_RESET		(1 << 15)
#define MAC1_TX_FLOW_CONTROL	(1 << 3)
#define MAC1_RX_FLOW_CONTROL	(1 << 2)
#define MAC1_RECEIVE_PASS_ALL	(1 << 1)
#define MAC1_RECEIVE_ENABLE	(1 << 0)

/* Bits for MAC2 register */
#define MAC2_AUTO_DETECT_PAD_ENABLE	(1 << 7)
#define MAC2_VLAN_PAD_ENABLE		(1 << 6)
#define MAC2_PAD_CRC_ENABLE		(1 << 5)
#define MAC2_CRC_ENABLE			(1 << 4)
#define MAC2_FULL_DUPLEX		(1 << 0)

#define INITIAL_MAC2	(MAC2_AUTO_DETECT_PAD_ENABLE | MAC2_VLAN_PAD_ENABLE | MAC2_PAD_CRC_ENABLE | MAC2_CRC_ENABLE)

/* Recommended values for IPGT register (see sec. 3.3.2.3 0f datasheet */
#define IPGT_FD_VALUE	0x15
#define IPGT_HD_VALUE	0x12

/* Bits for MCMD register */
#define MCMD_READ		(1 << 0)

/* Bits for MIND register */
#define MIND_NOT_VALID		(1 << 2)
#define MIND_BUSY		(1 << 0)

/* Bits for command register */
#define COMMAND_ENABLE_QOS	(1 << 11)
#define COMMAND_FULL_DUPLEX	(1 << 10)
#define COMMAND_RMII_MODE	(1 << 9)
#define COMMAND_TX_FLOW_CONTROL	(1 << 8)
#define COMMAND_PROMISC		(1 << 7)
#define COMMAND_ALLOW_SHORT	(1 << 6)
#define COMMAND_RX_RESET	(1 << 5)
#define COMMAND_TX_RESET	(1 << 4)
#define COMMAND_RESET		(1 << 3)
#define COMMAND_TX_RT_ENABLE	(1 << 2)
#define COMMAND_TX_ENABLE	(1 << 1)
#define COMMAND_RX_ENABLE	(1 << 0)

/* Bits for receive filter control register */
#define FILTER_ACCEPT_SELF		(1 << 5)
#define FILTER_ACCEPT_MCAST_HASH	(1 << 4)
#define FILTER_ACCEPT_UCAST_HASH	(1 << 3)
#define FILTER_ACCEPT_MCAST_ANY		(1 << 2)
#define FILTER_ACCEPT_BCAST_ANY		(1 << 1)
#define FILTER_ACCEPT_UCAST_ANY		(1 << 0)

/* Bits for interrupt registers */
#define WAKEUP_INT		(1 << 13)
#define SOFT_INT		(1 << 12)
#define TX_RT_DONE_INT		(1 << 11)
#define TX_RT_FINISHED_INT	(1 << 10)
#define TX_RT_ERROR_INT		(1 << 9)
#define TX_RT_UNDERRUN_INT	(1 << 8)
#define TX_DONE_INT		(1 << 7)
#define TX_FINISHED_INT		(1 << 6)
#define TX_ERROR_INT		(1 << 5)
#define TX_UNDERRUN_INT		(1 << 4)
#define RX_DONE_INT		(1 << 3)
#define RX_FINISHED_INT		(1 << 2)
#define RX_ERROR_INT		(1 << 1)
#define RX_OVERRUN_INT		(1 << 0)

/* Bit for POWERDOWN register */
#define POWERDOWN_VALUE		(1 << 31)

/* Bits for TX control */
#define TX_CONTROL_INT		(1 << 31)
#define TX_CONTROL_LAST		(1 << 30)
#define TX_CONTROL_CRC		(1 << 29)
#define TX_CONTROL_PAD		(1 << 28)
#define TX_CONTROL_HUGE		(1 << 27)
#define TX_CONTROL_OVERRIDE	(1 << 26)

/* these flags used for non-last fragment of a frame */
#define TX_CONTROL_ALL_NOTLAST	(TX_CONTROL_CRC | TX_CONTROL_PAD | TX_CONTROL_OVERRIDE)
/* these flags used for last fragment of a frame, and for single-fragment
 * frames */
#define TX_CONTROL_ALL_LAST	(TX_CONTROL_ALL_NOTLAST | TX_CONTROL_LAST)

/* Bits for TX status */
#define TX_STATUS_ERROR			(1 << 31)
#define TX_STATUS_UNDERRUN		(1 << 29)
#define TX_STATUS_LATE_COLLISION	(1 << 28)
#define TX_STATUS_MANY_COLLISIONS	(1 << 27)
#define TX_STATUS_MANY_DEFER		(1 << 26)
#define TX_STATUS_COLLISIONS(s)	((s >> 21) & 15)

/* Bits for RX control */
#define RX_CONTROL_INT		(1 << 31)

/* Bits for RX status */
#define RX_STATUS_ERROR			(1 << 31)
#define RX_STATUS_LAST_FRAG		(1 << 30)
#define RX_STATUS_OVERRUN		(1 << 28)
#define RX_STATUS_ALIGNMENT_ERROR	(1 << 27)
#define RX_STATUS_RANGE_ERROR		(1 << 26)
#define RX_STATUS_LENGTH_ERROR		(1 << 25)
#define RX_STATUS_SYMBOL_ERROR		(1 << 24)
#define RX_STATUS_CRC_ERROR		(1 << 23)
#define RX_STATUS_BROADCAST		(1 << 22)
#define RX_STATUS_MULTICAST		(1 << 21)
#define RX_STATUS_FAIL_FILTER		(1 << 20)
#define RX_STATUS_VLAN			(1 << 19)
#define RX_STATUS_CONTROL_FRAME		(1 << 18)
#define RX_STATUS_LENGTH(s)		((s & 0x7ff) + 1)

/* Bits for RSV register */
#define RSV_VLAN			(1 << 30)
#define RSV_CONTROL_FRAME		(1 << 27)
#define RSV_DRIBBLE_NIBBLE		(1 << 26)
#define RSV_BROADCAST			(1 << 25)
#define RSV_MULTICAST			(1 << 24)
#define RSV_LENGTH_OUT_OF_RANGE		(1 << 22)
#define RSV_LENGTH_CHECK_ERROR		(1 << 21)
#define RSV_CRC_ERROR			(1 << 20)
#define RSV_RECEIVE_CODE_VIOLATION	(1 << 19)
#define RSV_MASK			0xFFFF

#ifdef IP3902_POWERDOWN_PHY_ON_SUSPEND
static int keep_phy_control=0;
#endif

static char *mac_address;
module_param(mac_address, charp, S_IRUGO);
MODULE_PARM_DESC(mac_address, "MAC address of the device");

#ifdef CONFIG_MOCA_PHY
#define MOCA_PHY_ADDR					14
#define DRV_MOCA_CTL					55
#define MOCA_CTL_VERSION				0x01

#define SIOCGMOCAMEM   					(SIOCDEVPRIVATE+2)
#define SIOCSMOCAMEM   					(SIOCDEVPRIVATE+3)

#define MMI_CSR_BASE					(0x0)
#define MMI_REG_CTL						(MMI_CSR_BASE + 0x0)
#define MMI_REG_STATUS					(MMI_CSR_BASE + 0x1)
#define MMI_REG_EXT_STATUS				(MMI_CSR_BASE + 0x0F)
#define MMI_REG_ENH_CTL					(MMI_CSR_BASE + 0x1A)
#define MMI_REG_ENH_ADDR_MODE			(MMI_CSR_BASE + 0x1B)
#define MMI_REG_ENH_ADDR_PORT_HI		(MMI_CSR_BASE + 0x1C)
#define MMI_REG_ENH_ADDR_PORT_LO		(MMI_CSR_BASE + 0x1D)
#define MMI_REG_ENH_DATA_PORT_HI		(MMI_CSR_BASE + 0x1E)
#define MMI_REG_ENH_DATA_PORT_LO		(MMI_CSR_BASE + 0x1F)

#define GPHY_ENH_CTL__GPHY_MODE_EN		(0x80)
#define GPHY_ENH_CTL__GMAC_ISOLATE		(0x40)
#define GPHY_ENH_CTL__LINK_UP			(0x20)
#define GPHY_ENH_CTL__GPHY_ADDR_MASK	(0x1F)
#define GPHY_ENH_ADDR_MODE__START_WRITE	(0x01)
#define GPHY_ENH_ADDR_MODE__START_READ	(0x02)
#define GPHY_ENH_ADDR_MODE__AUTO_INCR	(0x04)
#define GPHY_ENH_ADDR_MODE__BUSY		(0x08)
#define GPHY_ENH_ADDR_MODE__ERROR		(0x10)

struct moca_io {
	u32	*in;
	u32	in_len;
	u32	*out;
	u32	out_len;
};

struct moca_ifr {
	u32   cmd;
	void* param1;
	void* param2;
	void* param3;
};
#endif

/* device private data */
struct ip3902_descriptor {
	unsigned long address;
	unsigned long control;
};

struct ip3902_rx_status {
	unsigned long status;
	unsigned long hash_crc;
};

struct ip3902_dma_struct {
	struct ip3902_descriptor rx_desc[RX_RING_SIZE];
	struct ip3902_rx_status rx_status[RX_RING_SIZE];
	struct ip3902_descriptor tx_desc[TX_RING_SIZE];
	unsigned long tx_status[TX_RING_SIZE];
};

struct ip3902_private {
	struct timer_list	mii_timer;
	spinlock_t		mii_lock;
	struct mii_if_info	mii;
	u32			msg_enable;

	struct net_device	*ndev;
	struct platform_device	*pdev;
	struct resource		*bus;
	void __iomem		*mem;
#ifdef IP3902_NAPI
	struct napi_struct napi;
#endif

	struct ip3902_dma_struct *ds;		/* descriptors and statuses */
	dma_addr_t ds_dma;

	struct sk_buff *rx_skb[RX_RING_SIZE];	/* where to recieve to */
	struct sk_buff *tx_skb[TX_RING_SIZE];	/* where to send from */
	bool tx_first_desc[TX_RING_SIZE];	/* true if this is the first desc of an skb */

	int rx_next_allocate;			/* index in rx ring where skb should be allocated */
	int rx_next_consume;			/* index in rx ring where data should be read when available */
	int tx_next_produce;			/* index in tx ring where new data should be put */
	int tx_next_deallocate;			/* index in tx ring of first not freed skb */

#ifdef CONFIG_INET_LRO
	bool                use_lro;
	bool                lro_lock;
	int                 lro_count;
	struct net_lro_mgr  lro_mgr;
	struct net_lro_desc lro_desc[MAX_LRO_DESCRIPTORS];
	struct timer_list   lro_timer;
#endif

	unsigned char running;
	unsigned char resume_open;

};

static inline unsigned long ip3902_read_reg(struct net_device *ndev, int reg)
{
	unsigned long value = readl((void * __iomem)(ndev->base_addr + reg));
	return value;
}

static inline void ip3902_write_reg(struct net_device *ndev, int reg,
		unsigned long val)
{
	writel(val, (void * __iomem)(ndev->base_addr + reg));
}

static inline void ip3902_write_tx_desc(struct ip3902_private *ip3902_priv, int pos, unsigned long address, unsigned long control)
{
#if defined(BIG_ENDIAN_DESCRIPTORS)
	ip3902_priv->ds->tx_desc[pos].address = cpu_to_be32(address);
	ip3902_priv->ds->tx_desc[pos].control = cpu_to_be32(control);
#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
	ip3902_priv->ds->tx_desc[pos].address = cpu_to_le32(address);
	ip3902_priv->ds->tx_desc[pos].control = cpu_to_le32(control);
#else
	ip3902_priv->ds->tx_desc[pos].address = address;
	ip3902_priv->ds->tx_desc[pos].control = control;
#endif
	wmb();
}

static inline void ip3902_read_tx_desc(struct ip3902_private *ip3902_priv, int pos, dma_addr_t *address, int *length)
{
#if defined(BIG_ENDIAN_DESCRIPTORS)
	*address = (dma_addr_t)be32_to_cpu(ip3902_priv->ds->tx_desc[pos].address);
	*length  = (int)be32_to_cpu(ip3902_priv->ds->tx_desc[pos].control) & 0xffff;
#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
	*address = (dma_addr_t)le32_to_cpu(ip3902_priv->ds->tx_desc[pos].address);
	*length  = (int)le32_to_cpu(ip3902_priv->ds->tx_desc[pos].control) & 0xffff;
#else
	*address = (dma_addr_t)ip3902_priv->ds->tx_desc[pos].address;
	*length  = (int)ip3902_priv->ds->tx_desc[pos].control & 0xffff;
#endif
}

static inline unsigned long ip3902_read_tx_status(struct ip3902_private *ip3902_priv, int pos)
{
#if defined(BIG_ENDIAN_STATUSES)
	return be32_to_cpu(ip3902_priv->ds->tx_status[pos]);
#elif defined(LITTLE_ENDIAN_STATUSES)
	return le32_to_cpu(ip3902_priv->ds->tx_status[pos]);
#else
	return ip3902_priv->ds->tx_status[pos];
#endif
}

static inline void ip3902_write_rx_desc(struct ip3902_private *ip3902_priv, int pos, unsigned long address, unsigned long control)
{
#if defined(BIG_ENDIAN_DESCRIPTORS)
	ip3902_priv->ds->rx_desc[pos].address = cpu_to_be32(address);
	ip3902_priv->ds->rx_desc[pos].control = cpu_to_be32(control);
#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
	ip3902_priv->ds->rx_desc[pos].address = cpu_to_le32(address);
	ip3902_priv->ds->rx_desc[pos].control = cpu_to_le32(control);
#else
	ip3902_priv->ds->rx_desc[pos].address = address;
	ip3902_priv->ds->rx_desc[pos].control = control;
#endif
	wmb();
}

static inline void ip3902_read_rx_desc(struct ip3902_private *ip3902_priv, int pos, dma_addr_t *address, int *length)
{
#if defined(BIG_ENDIAN_DESCRIPTORS)
	*address = (dma_addr_t)be32_to_cpu(ip3902_priv->ds->rx_desc[pos].address);
	*length  = (int)be32_to_cpu(ip3902_priv->ds->rx_desc[pos].control) & 0xffff;
#elif defined(LITTLE_ENDIAN_DESCRIPTORS)
	*address = (dma_addr_t)le32_to_cpu(ip3902_priv->ds->rx_desc[pos].address);
	*length  = (int)le32_to_cpu(ip3902_priv->ds->rx_desc[pos].control) & 0xffff;
#else
	*address = (dma_addr_t)ip3902_priv->ds->rx_desc[pos].address;
	*length  = (int)ip3902_priv->ds->rx_desc[pos].control & 0xffff;
#endif
}

static inline unsigned long ip3902_read_rx_status(struct net_device *ndev, struct ip3902_private *ip3902_priv, int pos)
{
#if defined(BIG_ENDIAN_STATUSES)
	return be32_to_cpu(ip3902_priv->ds->rx_status[pos].status);
#elif defined(LITTLE_ENDIAN_STATUSES)
	return le32_to_cpu(ip3902_priv->ds->rx_status[pos].status);
#else
	return ip3902_priv->ds->rx_status[pos].status;
#endif
}

static inline void ip3902_write_madr_reg(struct net_device *ndev, int phy_id, int location)
{
	/* assume ranges of phy_id and location are correct - we set masks in
	 * struct mii_if_info for that */
	
	unsigned long val = (phy_id << 8) | location;
	ip3902_write_reg(ndev, MADR_REG, val);
}

static inline int ip3902_wait_mdio_op_complete(struct net_device *ndev, unsigned long mask)
{
	int timeout = 10000;		/* to avoid hangup in case of unexpected badness ... */

	while (--timeout > 0)
	{
		if ((ip3902_read_reg(ndev, MIND_REG) & mask) == 0)
		{
			return 0;
		}
		udelay(1);
	}

	return -EIO;
}

static int ip3902_mdio_read(struct net_device *ndev, int phy_id, int location)
{
	ip3902_write_madr_reg(ndev, phy_id, location);
	ip3902_write_reg(ndev, MCMD_REG, 0);
	ip3902_write_reg(ndev, MCMD_REG, MCMD_READ);
	if (ip3902_wait_mdio_op_complete(ndev, MIND_NOT_VALID | MIND_BUSY) < 0)
	{
		return 0;
	}
	else
	{
		unsigned long value = ip3902_read_reg(ndev, MRDD_REG) & 0xffff;
		return value;
	}
}

static void ip3902_mdio_write(struct net_device *ndev, int phy_id, int location, int value)
{
	ip3902_write_madr_reg(ndev, phy_id, location);
	ip3902_write_reg(ndev, MWTD_REG, value & 0xffff);
	ip3902_wait_mdio_op_complete(ndev, MIND_BUSY);
}

static inline int ip3902_nr_free_descs(int head, int tail, int size)
{
	int free;

	if (head >= tail)
	{
		free = (tail + size) - head;
	}
	else
	{
		free = tail - head;
	}

	return free;
}

static void ip3902_eth_rx_refill_descs(struct net_device *ndev, struct ip3902_private *ip3902_priv)
{
	do
	{
		int rx_index = ip3902_priv->rx_next_allocate;
		struct sk_buff *skb = netdev_alloc_skb(ndev, ETH_RX_SKB_SIZE + dma_get_cache_alignment());

		if (skb)
		{
			int unaligned = (((u32)skb->data) + ETH_HLEN) & (dma_get_cache_alignment() - 1);
			unsigned long desc_address;

			if (unaligned)
			{
				skb_reserve(skb, (dma_get_cache_alignment() - unaligned));
			}

			desc_address = dma_map_single(NULL, skb->data, ETH_RX_SKB_SIZE, DMA_FROM_DEVICE);
			ip3902_write_rx_desc(ip3902_priv, rx_index, desc_address, (ETH_RX_SKB_SIZE -1) | RX_CONTROL_INT);

			ip3902_priv->rx_skb[rx_index] = skb;
			ip3902_priv->rx_next_allocate = NEXT_RX(rx_index);
		}
		else
		{
			ip3902_write_reg(ndev, RX_CONSUME_INDEX_REG, ip3902_priv->rx_next_allocate);
			return;
		}
	} while (ip3902_priv->rx_next_allocate != ip3902_priv->rx_next_consume);

	ip3902_write_reg(ndev, RX_CONSUME_INDEX_REG, ip3902_priv->rx_next_allocate);
}

static int ip3902_eth_receive_queue(struct net_device *ndev, struct ip3902_private *ip3902_priv, int budget)
{
	int rx_index    = ip3902_priv->rx_next_consume;
	int write_index = ip3902_read_reg(ndev, RX_PRODUCE_INDEX_REG);
	int received = 0;
	int limit;

	do
	{
		limit = write_index;

#ifdef CONFIG_INET_LRO
		ip3902_priv->lro_lock = true;
#endif
		while (rx_index != limit)
		{
			unsigned long status = ip3902_read_rx_status(ndev, ip3902_priv, rx_index);

			if (!(status & RX_STATUS_LAST_FRAG))
			{
				printk(DRVNAME ": broken RX status: %08lx\n", status);
				continue;
			}

			if (status & RX_STATUS_FAIL_FILTER)
			{
				continue;
			}

			/* Looks like hardware returns RANGE_ERROR for each frame */
			if (status & (RX_STATUS_OVERRUN | RX_STATUS_ALIGNMENT_ERROR | RX_STATUS_LENGTH_ERROR | RX_STATUS_CRC_ERROR))
			{
				ndev->stats.rx_errors++;

				if (status & RX_STATUS_OVERRUN)
				{
					ndev->stats.rx_fifo_errors++;
				}
				if (status & RX_STATUS_ALIGNMENT_ERROR)
				{
					ndev->stats.rx_frame_errors++;
				}
				if (status & (RX_STATUS_RANGE_ERROR | RX_STATUS_LENGTH_ERROR))
				{
					ndev->stats.rx_length_errors++;
				}
				if (status & RX_STATUS_CRC_ERROR)
				{
					ndev->stats.rx_crc_errors++;
				}
			}
			else
			{
				if (--budget < 0)
				{
					/* we got packets, but no quota */
					/* store current ring pointer state */
					ip3902_priv->rx_next_consume = rx_index;
					return received;
				}
				else
				{
					struct sk_buff *skb    = ip3902_priv->rx_skb[rx_index];
					int             length = RX_STATUS_LENGTH(status);
					dma_addr_t      data_addr;
					int             data_length;

					ndev->stats.rx_packets++;
					ndev->stats.rx_bytes += length;
					if (status & RX_STATUS_MULTICAST)
					{
						ndev->stats.multicast++;
					}
					skb_put(skb, length - BYTES_IN_ETHERNET_CRC);
					skb->protocol = eth_type_trans(skb, ndev);

#ifdef CONFIG_INET_LRO
					ip3902_priv->lro_count++;
					if (ip3902_priv->use_lro)
					{
						lro_receive_skb(&ip3902_priv->lro_mgr, skb, ip3902_priv);
					}
					else
#endif
					{
#ifdef IP3902_NAPI
						netif_receive_skb(skb);
#else
						netif_rx(skb);
#endif
					}

					ip3902_read_rx_desc(ip3902_priv, rx_index, &data_addr, &data_length);
					dma_unmap_single(NULL, data_addr, ETH_RX_SKB_SIZE, DMA_FROM_DEVICE);

					ip3902_priv->rx_skb[rx_index] = NULL;
					ndev->last_rx = jiffies;
					received++;
				}
			}
			rx_index = NEXT_RX(rx_index);
		}

		ip3902_write_reg(ndev, INT_CLEAR_REG, RX_DONE_INT);
		ip3902_write_reg(ndev, INT_CLEAR_REG, 0);

#ifdef CONFIG_INET_LRO
		ip3902_priv->lro_lock = false;
#endif

		ip3902_priv->rx_next_consume = rx_index;
		ip3902_eth_rx_refill_descs(ndev, ip3902_priv);
		write_index = ip3902_read_reg(ndev, RX_PRODUCE_INDEX_REG);
	} while (limit != write_index);

#ifdef CONFIG_INET_LRO
	if (ip3902_priv->use_lro)
	{
		int i = MAX_LRO_DESCRIPTORS;
		struct net_lro_desc *ptr = ip3902_priv->lro_desc;

		if (timer_pending (&ip3902_priv->lro_timer))
		{
			mod_timer(&ip3902_priv->lro_timer, jiffies + LRO_TIMEOUT);
		}
		else
		{
			ip3902_priv->lro_timer.expires  = jiffies + LRO_TIMEOUT;
			add_timer(&ip3902_priv->lro_timer);
		}

		do
		{
			if ((ptr->active) && ((jiffies - ptr->last_updated) >= LRO_TIMEOUT))
			{
				lro_flush_pkt(&ip3902_priv->lro_mgr, ptr->iph, ptr->tcph);
			}
			ptr++;
		} while (--i);
	}
#endif

	return received;
}

#ifdef IP3902_NAPI
static int ip3902_poll(struct napi_struct *napi, int budget)
{
	struct ip3902_private *ip3902_priv = container_of(napi, struct ip3902_private, napi);
	struct net_device *ndev = ip3902_priv->ndev;
	int work_done;

	work_done = ip3902_eth_receive_queue(ndev, ip3902_priv, budget);

	if (work_done < budget)
	{
		netif_rx_complete(ndev, napi);
		ip3902_write_reg(ndev, INT_ENABLE_REG, (TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT));
	}

	return work_done;
}
#endif

#ifdef CONFIG_INET_LRO
static void ip3902_lro_timeout(unsigned long data)
{
	struct ip3902_private *ip3902_priv = (struct ip3902_private *)data;

#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_START, TDI_EVENT), CHAR_ARRAY_TO_U32("LRO"), CHAR_ARRAY_TO_U32("3902"));
#endif
	if (!ip3902_priv->lro_lock)
	{
		ip3902_priv->use_lro = false;
		ip3902_priv->lro_count = 0;
		lro_flush_all(&ip3902_priv->lro_mgr);
	}
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_STOP, TDI_EVENT), CHAR_ARRAY_TO_U32("LRO"), CHAR_ARRAY_TO_U32("3902"));
#endif
}
#endif

#define ip3902_eth_free_completed_tx_descs(ndev, priv) ip3902_eth_free_tx_descs(ndev, priv, 0)
#define ip3902_eth_free_all_tx_descs(ndev, priv) ip3902_eth_free_tx_descs(ndev, priv, 1)

static void ip3902_eth_free_tx_descs(struct net_device *ndev, struct ip3902_private *ip3902_priv, int force)
{
	int limit;

	if (force)
	{
		limit = ip3902_priv->tx_next_produce;
	}
	else
	{
		limit = ip3902_read_reg(ndev, TX_CONSUME_INDEX_REG);
	}

	while (ip3902_priv->tx_next_deallocate != limit)
	{
		int             length;
		int             tx_index;
		unsigned long   status;
		dma_addr_t      addr;
		struct sk_buff *skb;

		tx_index = ip3902_priv->tx_next_deallocate;

		ip3902_priv->tx_next_deallocate = NEXT_TX(tx_index);
		ip3902_read_tx_desc(ip3902_priv, tx_index, &addr, &length);
		skb = ip3902_priv->tx_skb[tx_index];

		status = ip3902_read_tx_status(ip3902_priv, tx_index);
		if (status & TX_STATUS_ERROR)
		{
			ndev->stats.tx_errors++;
			if (status & TX_STATUS_LATE_COLLISION)
			{
				ndev->stats.tx_aborted_errors++;
			}
			if (status & (TX_STATUS_MANY_COLLISIONS | TX_STATUS_MANY_DEFER))
			{
				ndev->stats.tx_window_errors++;
			}
		}
		else
		{
			ndev->stats.tx_packets++;
			ndev->stats.tx_bytes   += length;
			ndev->stats.collisions += TX_STATUS_COLLISIONS(status);
		}

		if (skb)
		{
			ip3902_priv->tx_skb[tx_index] = NULL;
		}

		if (ip3902_priv->tx_first_desc[tx_index] == true)
		{
			dma_unmap_single(NULL, addr, length, DMA_TO_DEVICE);
		}
		else
		{
			dma_unmap_page(NULL, addr, length, DMA_TO_DEVICE);
		}

		if (skb)
		{
			dev_kfree_skb_irq(skb);
		}
	}
}

static void ip3902_reset_tx(struct net_device *ndev, struct ip3902_private *ip3902_priv, int initial)
{
	unsigned long val;

	/* Reset Tx hardware */
	val = ip3902_read_reg(ndev, COMMAND_REG);
	val &= ~(COMMAND_TX_RT_ENABLE | COMMAND_TX_ENABLE);
	val |= COMMAND_TX_RESET;
	ip3902_write_reg(ndev, COMMAND_REG, val);

	if (!initial)
	{
		ip3902_eth_free_all_tx_descs(ndev, ip3902_priv);
	}

	ip3902_priv->tx_next_produce    = 0;
	ip3902_priv->tx_next_deallocate = 0;

	/* Configure Tx registers */
	ip3902_write_reg(ndev, TX_DESC_REG,   ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, tx_desc));
	ip3902_write_reg(ndev, TX_STATUS_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, tx_status));
	ip3902_write_reg(ndev, TX_DESC_NUMBER_REG, TX_RING_SIZE - 1);
	ip3902_write_reg(ndev, TX_PRODUCE_INDEX_REG, ip3902_priv->tx_next_produce);
	ip3902_write_reg(ndev, TX_CONSUME_INDEX_REG, ip3902_priv->tx_next_deallocate);
}

static void ip3902_reset_rx(struct net_device *ndev, struct ip3902_private *ip3902_priv, int init)
{
	unsigned long val;

	/* Reset Rx hardware */
	val = ip3902_read_reg(ndev, COMMAND_REG);
	val &= ~COMMAND_RX_ENABLE;
	val |= COMMAND_RX_RESET;
	ip3902_write_reg(ndev, COMMAND_REG, val);

	/* Set maximum frame size register */
	ip3902_write_reg(ndev, MAXF_REG, ETH_RX_SKB_SIZE);

	if (init)
	{
		ip3902_priv->rx_next_allocate   = 0;
		ip3902_priv->rx_next_consume    = 0;
		ip3902_eth_rx_refill_descs(ndev, ip3902_priv);
	}

	/* Prepare skb's for Rx (any skb's already prepared will be reused)
	 * and configure Rx registers */
	ip3902_write_reg(ndev, RX_DESC_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, rx_desc));
	ip3902_write_reg(ndev, RX_STATUS_REG, ip3902_priv->ds_dma + offsetof(struct ip3902_dma_struct, rx_status));
	ip3902_write_reg(ndev, RX_DESC_NUMBER_REG, RX_RING_SIZE - 1);
	ip3902_write_reg(ndev, RX_PRODUCE_INDEX_REG, ip3902_priv->rx_next_consume);
}

static inline void ip3902_start_tx(struct net_device *ndev)
{
	unsigned long val;
	
	val = ip3902_read_reg(ndev, COMMAND_REG);
	val |= COMMAND_TX_ENABLE;
	ip3902_write_reg(ndev, COMMAND_REG, val);
}

static inline void ip3902_start_rx(struct net_device *ndev)
{
	unsigned long val;
	
	/* First on high-level ... */
	val = ip3902_read_reg(ndev, COMMAND_REG);
	val |= (COMMAND_RX_ENABLE | COMMAND_ALLOW_SHORT);
	ip3902_write_reg(ndev, COMMAND_REG, val);

	/* ... and then on low-level (after high level is ready to receive) */
	val = ip3902_read_reg(ndev, MAC1_REG);
	val |= MAC1_RECEIVE_ENABLE;	/* flow control frames won't be passed to driver */
	ip3902_write_reg(ndev, MAC1_REG, val);
}

/* Interrupt handler body - split out to use both in interrupt handler
 * and in net poll controller.
 *
 * Internal routine, called with lock held. */
static void ip3902_do_handle_interrupt(struct net_device *ndev,	struct ip3902_private *ip3902_priv, unsigned long status)
{
	if (status & TX_UNDERRUN_INT)
	{
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_START, TDI_EVENT), CHAR_ARRAY_TO_U32("TX_U"), CHAR_ARRAY_TO_U32("3902"));
#endif
		ip3902_write_reg(ndev, INT_CLEAR_REG, TX_UNDERRUN_INT);
		ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
		printk(KERN_ERR DRVNAME ": %s: fatal Tx underrun, resetting Tx\n", ndev->name);
		ip3902_reset_tx(ndev, ip3902_priv, 0);
		ip3902_start_tx(ndev);
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_STOP, TDI_EVENT), CHAR_ARRAY_TO_U32("TX_U"), CHAR_ARRAY_TO_U32("3902"));
#endif
	}

	if (status & RX_OVERRUN_INT)
	{
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_START, TDI_EVENT), CHAR_ARRAY_TO_U32("RX_O"), CHAR_ARRAY_TO_U32("3902"));
#endif
		ip3902_write_reg(ndev, INT_CLEAR_REG, RX_OVERRUN_INT);
		ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
		printk(KERN_ERR DRVNAME ": %s: fatal Rx overrun, resetting Rx\n", ndev->name);
		ip3902_reset_rx(ndev, ip3902_priv, 0);
		ip3902_start_rx(ndev);
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_STOP, TDI_EVENT), CHAR_ARRAY_TO_U32("RX_O"), CHAR_ARRAY_TO_U32("3902"));
#endif
	}
	else if (status & RX_DONE_INT)
	{
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_START, TDI_EVENT), CHAR_ARRAY_TO_U32("RX"), CHAR_ARRAY_TO_U32("3902"));
#endif
#ifdef IP3902_NAPI
	        /* Disable the Rx interrupt */
		ip3902_write_reg(ndev, INT_ENABLE_REG, (RX_OVERRUN_INT | TX_UNDERRUN_INT));
		netif_rx_schedule(ndev, &ip3902_priv->napi);
		ip3902_write_reg(ndev, INT_CLEAR_REG, RX_DONE_INT);
		ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
#else
		ip3902_eth_receive_queue(ndev, ip3902_priv, RX_RING_SIZE);
#endif
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_STOP, TDI_EVENT), CHAR_ARRAY_TO_U32("RX"), CHAR_ARRAY_TO_U32("3902"));
#endif
	}
}

static irqreturn_t ip3902_interrupt(int irq, void *dev_instance)
{
	struct net_device *ndev = (struct net_device *) dev_instance;
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	unsigned long status;

	status = ip3902_read_reg(ndev, INT_STATUS_REG) & (TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT);
	do
	{
		if (!status)
		{
#if defined (CONFIG_TIME_DOCTOR)
            timeDoctor_Info(TDI_COMMAND(TDI_START, TDI_EVENT), CHAR_ARRAY_TO_U32("INT"), CHAR_ARRAY_TO_U32("3902"));
            timeDoctor_Info(TDI_COMMAND(TDI_STOP, TDI_EVENT), CHAR_ARRAY_TO_U32("INT"), CHAR_ARRAY_TO_U32("3902"));
#endif
			return IRQ_NONE;
		}
		else
		{
			ip3902_do_handle_interrupt(ndev, ip3902_priv, status);
			status = ip3902_read_reg(ndev, INT_STATUS_REG) & (TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT);
		}
	} while (status);

	return IRQ_HANDLED;
}

#ifdef CONFIG_NET_POLL_CONTROLLER
static void ip3902_net_poll(struct net_device *ndev)
{
	disable_irq_lockdep(ndev->irq);
	ip3902_interrupt(ndev->irq, ndev);
	enable_irq_lockdep(ndev->irq);
}
#endif

static int eth_alloc_tx_desc_index(struct ip3902_private *ip3902_priv)
{
	int tx_desc_curr;

	tx_desc_curr = ip3902_priv->tx_next_produce;
	ip3902_priv->tx_next_produce = NEXT_TX(tx_desc_curr);

	return tx_desc_curr;
}

static void eth_tx_fill_frag_descs(struct ip3902_private *ip3902_priv, struct sk_buff *skb)
{
	int frag = 0;
	int tx_index;

	do
	{
		skb_frag_t   *this_frag = &skb_shinfo(skb)->frags[frag];
		unsigned long desc_address;
		unsigned long desc_control;

		tx_index = eth_alloc_tx_desc_index(ip3902_priv);

		if (frag == (skb_shinfo(skb)->nr_frags - 1))
		{
			desc_control = (this_frag->size - 1) | TX_CONTROL_ALL_NOTLAST;
			ip3902_priv->tx_skb[tx_index] = skb;
		}
		else
		{
			desc_control = (this_frag->size - 1) | TX_CONTROL_ALL_LAST;
			ip3902_priv->tx_skb[tx_index] = NULL;
		}

		ip3902_priv->tx_first_desc[tx_index] = false;
		desc_address = dma_map_page(NULL, this_frag->page,
						this_frag->page_offset,
						this_frag->size,
						DMA_TO_DEVICE);
		ip3902_write_tx_desc(ip3902_priv, tx_index, desc_address, desc_control);
	} while (++frag < skb_shinfo(skb)->nr_frags);
}

static int ip3902_submit_skb_for_tx (struct net_device *ndev, struct sk_buff *skb)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	int nr_frags;
	int free_desc;
	int ret = 0;

#ifdef CONFIG_INET_LRO
	if (ip3902_priv->lro_count > LRO_THRESHOLD)
	{
		ip3902_priv->use_lro = true;
	}
	ip3902_priv->lro_count = 0;
#endif

	free_desc = ip3902_nr_free_descs(ip3902_priv->tx_next_produce, ip3902_priv->tx_next_deallocate, TX_RING_SIZE);
	nr_frags  = skb_shinfo(skb)->nr_frags;

	if (free_desc <= nr_frags)
	{
		ip3902_eth_free_completed_tx_descs(ndev, ip3902_priv);
		free_desc = ip3902_nr_free_descs(ip3902_priv->tx_next_produce, ip3902_priv->tx_next_deallocate, TX_RING_SIZE);
	}
	
	if (free_desc > nr_frags)
	{
		unsigned long desc_address;
		unsigned long desc_control;
		int tx_index;
		int length;

		tx_index = eth_alloc_tx_desc_index(ip3902_priv);

		if (nr_frags)
		{
			eth_tx_fill_frag_descs(ip3902_priv, skb);
			length = skb_headlen(skb);
			desc_control = (length - 1) | TX_CONTROL_ALL_NOTLAST;
			ip3902_priv->tx_skb[tx_index] = NULL;
		}
		else
		{
			length = skb->len;
			desc_control = (length - 1) | TX_CONTROL_ALL_LAST;
			ip3902_priv->tx_skb[tx_index] = skb;
		}

		ip3902_priv->tx_first_desc[tx_index] = true;
		desc_address  = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);

		ip3902_write_tx_desc(ip3902_priv, tx_index, desc_address, desc_control);
		ip3902_write_reg(ndev, TX_PRODUCE_INDEX_REG, ip3902_priv->tx_next_produce);
		ip3902_eth_free_completed_tx_descs(ndev, ip3902_priv);
	}
	else
	{
		ret = -ENOMEM;
	}

	return ret;
}

static int ip3902_start_xmit (struct sk_buff *skb, struct net_device *ndev)
{
	int ret;

	BUG_ON(netif_queue_stopped(ndev));
	BUG_ON(skb == NULL);
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_START, TDI_EVENT), CHAR_ARRAY_TO_U32("TX"), CHAR_ARRAY_TO_U32("3902"));
#endif

	ret = ip3902_submit_skb_for_tx (ndev, skb);

	if (ret)
	{
		printk(KERN_ERR "%s: transmit with queue full\n", ndev->name);
		netif_stop_queue(ndev);
	}
	else
	{
		ndev->stats.tx_bytes += skb->len;
		ndev->stats.tx_packets++;
		ndev->trans_start = jiffies;
	}
#if defined (CONFIG_TIME_DOCTOR)
    timeDoctor_Info(TDI_COMMAND(TDI_STOP, TDI_EVENT), CHAR_ARRAY_TO_U32("TX"), CHAR_ARRAY_TO_U32("3902"));
#endif

	return ret;		/* success */
}

static void ip3902_do_set_rx_filter(struct net_device *ndev, struct ip3902_private *ip3902_priv)
{
	unsigned long creg, freg;

	creg = ip3902_read_reg(ndev, COMMAND_REG);
	if (ndev->flags & IFF_PROMISC)
	{
		/* If interface is in promiscuous mode, just disable filter */
		ip3902_write_reg(ndev, COMMAND_REG, creg | COMMAND_PROMISC);
		return;
	}
	/* Enable filter */
	ip3902_write_reg(ndev, COMMAND_REG, creg & ~COMMAND_PROMISC);

	/* Frames for self address and broadcast frames are always accepted */
	freg = FILTER_ACCEPT_SELF | FILTER_ACCEPT_BCAST_ANY;

	if (ndev->flags & IFF_ALLMULTI)
	{
		/* Accept all multicast frames */
		freg |= FILTER_ACCEPT_MCAST_ANY;
	}
	else if (ndev->mc_count > 0)
	{
		/* Accept some multicast frames */
		u64 hash_mask = 0;
		struct dev_mc_list *mc;

		freg |= FILTER_ACCEPT_MCAST_HASH;
		for (mc = ndev->mc_list; mc; mc = mc->next)
		{
			int b = (ether_crc(ETH_ALEN, mc->dmi_addr) >> 23) & 0x3f;
			hash_mask |= (1 << b);
		}
		ip3902_write_reg(ndev, HASH_FILTER_L_REG, hash_mask & 0xffffffff);
		ip3902_write_reg(ndev, HASH_FILTER_H_REG, hash_mask >> 32);
	}

	ip3902_write_reg(ndev, FILTER_CTRL_REG, freg);
}

static void ip3902_set_rx_filter(struct net_device *ndev)
{
	struct ip3902_private *ip3902_priv = (struct ip3902_private*)netdev_priv(ndev);
	ip3902_do_set_rx_filter(ndev, ip3902_priv);
}

static void set_duplex_mode(struct net_device *ndev, int duplex)
{
	unsigned long val;

	if (duplex)
	{
		/* Full Duplex mode */
		
		val = ip3902_read_reg(ndev, MAC2_REG);
		val |= MAC2_FULL_DUPLEX;
		ip3902_write_reg(ndev, MAC2_REG, val);

		ip3902_write_reg(ndev, IPGT_REG, IPGT_FD_VALUE);

		val = ip3902_read_reg(ndev, COMMAND_REG);
		val |= COMMAND_FULL_DUPLEX;
		ip3902_write_reg(ndev, COMMAND_REG, val);
	}
	else
	{
		/* Half Duplex mode */

		val = ip3902_read_reg(ndev, MAC2_REG);
		val &= ~MAC2_FULL_DUPLEX;
		ip3902_write_reg(ndev, MAC2_REG, val);

		ip3902_write_reg(ndev, IPGT_REG, IPGT_HD_VALUE);

		val = ip3902_read_reg(ndev, COMMAND_REG);
		val &= ~COMMAND_FULL_DUPLEX;
		ip3902_write_reg(ndev, COMMAND_REG, val);
	}
}

#ifdef CONFIG_MOCA_PHY
static int ip3902_moca_reset(struct net_device *ndev)
{
	struct ip3902_private *ip3902_priv = (struct ip3902_private*)netdev_priv(ndev);

	/* ensure phy is in reset */
	gpio_write(0, 14);

	/* bring phy out of reset */
	gpio_write(1, 13);
	udelay(600);
	gpio_write(1, 14);

	/* enable phy tx clock */
	ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, 0x00, 0x0040);
	ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, 0x1a, 0xc08e);

	return 0;
}

static int ip3902_moca_read(struct net_device *ndev, struct ifreq *req, int cmd)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	struct moca_ifr *data = (struct moca_ifr *) &req->ifr_ifru;
	struct moca_io *io = NULL;
	int	length;
	unsigned long addr;
	unsigned long *out_addr;

	if ( data == NULL)
	{
		return (-EINVAL);
	} 

	io = data->param2;
	if ( io == NULL )
	{
		return (-EINVAL);
	}

	if ((data->cmd != DRV_MOCA_CTL) || ((unsigned long)data->param3 != MOCA_CTL_VERSION ))
	{
		return (-EIO);
	}

	length = (int)io->out_len;
	addr = (unsigned long)io->in;
	out_addr = (unsigned long*)io->out;

	for ( ; length ; length -=4, addr += 4, out_addr++)
	{
		unsigned long data;
	
		addr &= 0x0fffffff;
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_HI, addr >> 16);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_LO, addr & 0xffff);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_MODE, GPHY_ENH_ADDR_MODE__START_READ);
		data = ip3902_mdio_read(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_DATA_PORT_HI);
		data <<= 16;
		data |=	ip3902_mdio_read(ndev, ip3902_priv->mii.phy_id, (MMI_REG_ENH_DATA_PORT_LO) & 0xffff);
		*out_addr = data;
	}

	return 0;
}

static int ip3902_moca_write(struct net_device *ndev, struct ifreq *req, int cmd)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	struct moca_ifr *ifr = (struct moca_ifr *) &req->ifr_ifru;
	struct moca_io *io = NULL;
	int length;
	unsigned long addr;

	if (ifr == NULL)
	{
		return (-EINVAL);
	}

	io = ifr->param2;
	if (io == NULL)
	{
		return (-EINVAL);
	}

	if ((ifr->cmd != DRV_MOCA_CTL) || ((unsigned long)ifr->param3 != MOCA_CTL_VERSION))
	{
		return (-EIO);
	}
	
	length = io->in_len - 4;
	addr = (*(io->in)) & 0x0fffffff;

	if ( length <= 4 )
	{
		unsigned long data = *((unsigned long*)(io->in + 1));

		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_HI, addr >> 16);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_LO, addr & 0xffff);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_DATA_PORT_HI, data >> 16);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_DATA_PORT_LO, data & 0xffff);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_MODE, GPHY_ENH_ADDR_MODE__START_WRITE);
	}
	else
	{
		unsigned long *data = (unsigned long*)(io->in + 1);
		int	i;

		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_HI, addr >> 16);
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_LO, addr & 0xffff);

		/* TODO check this issue  why do low address write one more ? */
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_PORT_LO, addr & 0xffff);
	
		for ( i = 0 ; i < length ; i+= sizeof(*data))
		{
			ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_DATA_PORT_HI, (*data) >> 16);
			ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_DATA_PORT_LO, (*data) & 0xffff);
			ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, MMI_REG_ENH_ADDR_MODE, GPHY_ENH_ADDR_MODE__START_WRITE | GPHY_ENH_ADDR_MODE__AUTO_INCR);
			data++;
		}
		ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id,  MMI_REG_ENH_ADDR_MODE, 0);
	}
	return 0;
}
#endif

static int ip3902_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	unsigned int duplex_changed = 0;
	int rc;

#ifdef CONFIG_MOCA_PHY
	switch(cmd) 
	{
		case SIOCDEVPRIVATE:
			rc = ip3902_moca_reset(ndev);
			break;

		case SIOCGMOCAMEM:
			rc = ip3902_moca_read(ndev, req, cmd);
			break;

		case SIOCSMOCAMEM:
			rc = ip3902_moca_write(ndev, req, cmd);
			break;

		default:
			rc = generic_mii_ioctl(&ip3902_priv->mii, if_mii(req), cmd, &duplex_changed);
			break;
	}
#else
	if (netif_running(ndev))
	{
		unsigned long flags;
		spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
		rc = generic_mii_ioctl(&ip3902_priv->mii, if_mii(req), cmd, &duplex_changed);
		spin_unlock_irqrestore(&ip3902_priv->mii_lock, flags);
	}
	else
	{
		return -EINVAL;
	}
#endif

	if (duplex_changed)
	{
		set_duplex_mode(ndev, ip3902_priv->mii.full_duplex);
	}
	return rc;
}

/* ethtool ops */

static void ip3902_get_drvinfo(struct net_device *ndev,
			   struct ethtool_drvinfo *info)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);

	strcpy(info->driver, DRVNAME);
	strcpy(info->version, DRVVERSION);
	strcpy(info->bus_info, ip3902_priv->ndev->name);
}

static int ip3902_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	unsigned long flags;

	spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
	mii_ethtool_gset(&ip3902_priv->mii, cmd);
	spin_unlock_irqrestore(&ip3902_priv->mii_lock, flags);

	return 0;
}

static int ip3902_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	unsigned long flags;
	int rc;

	spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
	rc = mii_ethtool_sset(&ip3902_priv->mii, cmd);
	spin_unlock_irqrestore(&ip3902_priv->mii_lock, flags);

	return rc;
}

static int ip3902_nway_reset(struct net_device *ndev)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	return mii_nway_restart(&ip3902_priv->mii);
}

static u32 ip3902_get_link(struct net_device *ndev)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	return mii_link_ok(&ip3902_priv->mii);
}

#ifdef CONFIG_MOCA_PHY
static void ip3902_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
{
	pause->autoneg  = 0x0;
	pause->rx_pause = 0x1;
	pause->tx_pause = 0x0;
}

static int ip3902_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
{
	return 0;
}
#endif

static const struct ethtool_ops ip3902_ethtool_ops = {
	.get_drvinfo	= ip3902_get_drvinfo,
	.get_settings	= ip3902_get_settings,
	.set_settings	= ip3902_set_settings,
	.nway_reset	= ip3902_nway_reset,
	.get_link 	= ip3902_get_link,
	.get_sg		= ethtool_op_get_sg,
	.set_sg		= ethtool_op_set_sg,
#ifdef CONFIG_MOCA_PHY
	.get_pauseparam = ip3902_get_pauseparam,
	.set_pauseparam = ip3902_set_pauseparam,
#endif
};

/* setup code */

static void ip3902_eth_update_mac_address(struct net_device *ndev)
{
	ip3902_write_reg(ndev, SA0_REG,	(ndev->dev_addr[5] << 8) | ndev->dev_addr[4]);
	ip3902_write_reg(ndev, SA1_REG,	(ndev->dev_addr[3] << 8) | ndev->dev_addr[2]);
	ip3902_write_reg(ndev, SA2_REG,	(ndev->dev_addr[1] << 8) | ndev->dev_addr[0]);
}

static int ip3902_eth_set_mac_address(struct net_device *ndev, void *addr)
{
	int i;

	for (i = 0; i < 6; i++)
	{
		/* +2 is for the offset of the HW addr type */
		ndev->dev_addr[i] = ((unsigned char *)addr)[i + 2];
	}
	ip3902_eth_update_mac_address(ndev);
	return 0;
}

static void ip3902_hw_deinit(struct net_device *ndev)
{
	unsigned long val;

	/* Stop Rx and Tx hardware and disable interrupts */
	val = ip3902_read_reg(ndev, COMMAND_REG);
	val &= ~(COMMAND_TX_ENABLE | COMMAND_RX_ENABLE);
	ip3902_write_reg(ndev, COMMAND_REG, val);
	ip3902_write_reg(ndev, INT_ENABLE_REG, 0);

#ifndef IP3902_POWERDOWN_PHY_ON_SUSPEND
	/* Put low-level hardware into reset, and high-level into poweroff */
	ip3902_write_reg(ndev, MAC1_REG, MAC1_SOFT_RESET);
	ip3902_write_reg(ndev, POWERDOWN_REG, POWERDOWN_VALUE);
#endif
}

#ifdef IP3902_POWERDOWN_PHY_ON_SUSPEND
static void ip3902_phy_powerup(struct net_device *ndev)
{
	/* Poweron hardware */
	ip3902_write_reg(ndev, POWERDOWN_REG, 0);

	/* Move low level out of reset (also initialize the registers)*/
	ip3902_write_reg(ndev, MAC1_REG, 0);
	ip3902_write_reg(ndev, MAC2_REG, INITIAL_MAC2);

	/* Set MII clock rate*/
	ip3902_write_reg(ndev, MCFG_REG, 0x1c);

	/* Write to reset bit in PHY control reg */
	ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, 0x00, keep_phy_control);

	/* Restart auto negotiation in PHY control reg */
	ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, 0x00, (keep_phy_control | (0x1200)));
}

static void ip3902_phy_powerdown(struct net_device *ndev)
{
	/* Set MII clock rate*/
	ip3902_write_reg(ndev, MCFG_REG, 0x1c);

	/* Write to power down bit in PHY control reg */
	keep_phy_control = ip3902_mdio_read(ndev, ip3902_priv->mii.phy_id, 0x00);
	ip3902_mdio_write(ndev, ip3902_priv->mii.phy_id, 0x00, keep_phy_control | (1 << 11));

	/* Put low-level hardware into reset, and high-level into poweroff */
	ip3902_write_reg(ndev, MAC1_REG, MAC1_SOFT_RESET);
	ip3902_write_reg(ndev, POWERDOWN_REG, POWERDOWN_VALUE);
}
#endif

static int ip3902_hw_init(struct net_device *ndev, struct ip3902_private *ip3902_priv)
{
	int ret = 0;

#ifndef CONFIG_MOCA_PHY
	/* Poweron hardware */
	ip3902_write_reg(ndev, POWERDOWN_REG, 0);

	/* Move low level out of reset (also initialize the registers)*/
	ip3902_write_reg(ndev, MAC1_REG, 0);
	ip3902_write_reg(ndev, MAC2_REG, INITIAL_MAC2);
#endif
	
	ip3902_priv->mii.phy_id	= -EIO;
	{
		int addr;

		for (addr = 1; addr < 32; addr++)
		{
			int stat;
			stat = ip3902_mdio_read(ndev, addr, MII_BMSR);
			if ((stat != 0) && (stat != 0xffff))
			{
				ip3902_priv->mii.phy_id	= addr;
			}
		}
	}

	if (ip3902_priv->mii.phy_id < 0)
	{
		ret = ip3902_priv->mii.phy_id;
	}
	else
	{
		ip3902_eth_update_mac_address(ndev);

		/* "Initialize" command register (before resets - those routines
		 * use read-modify-write operations on that register */
		ip3902_write_reg(ndev, COMMAND_REG, COMMAND_ALLOW_SHORT);

		/* Reset and configure Rx and Tx */
		ip3902_reset_tx(ndev, ip3902_priv, 1);
		ip3902_reset_rx(ndev, ip3902_priv, 1);

		/* Initialize Rx filtering */
		ip3902_do_set_rx_filter(ndev, ip3902_priv);

		/* Clear all interrupts, and enable interesting interrupts */
		ip3902_write_reg(ndev, INT_CLEAR_REG, 0xffffffff);
		ip3902_write_reg(ndev, INT_CLEAR_REG, 0);
		ip3902_write_reg(ndev, INT_ENABLE_REG, (TX_UNDERRUN_INT | RX_DONE_INT | RX_OVERRUN_INT));

		/* Start Tx and Rx hardware */
		ip3902_start_tx(ndev);
		ip3902_start_rx(ndev);
	}
	return ret;
}

static void ip3902_mii_expiry(unsigned long data)
{
	struct net_device *ndev = (struct net_device *)data;
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	unsigned long flags;

	spin_lock_irqsave(&ip3902_priv->mii_lock, flags);
	mii_check_media(&ip3902_priv->mii, netif_msg_link(ip3902_priv), 0);
	spin_unlock_irqrestore(&ip3902_priv->mii_lock, flags);

	if (ip3902_priv->running) {
		ip3902_priv->mii_timer.expires = jiffies + HZ*2;
		add_timer(&ip3902_priv->mii_timer);
	}
}

static int ip3902_open(struct net_device *ndev)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);
	int ret;

	dev_dbg(&ip3902_priv->ndev->dev, "%s: open\n", ndev->name);

	ret = request_irq(ndev->irq, ip3902_interrupt, IRQF_DISABLED, ndev->name, ndev);
	if (ret)
	{
		return ret;
	}

	ret = ip3902_hw_init(ndev, ip3902_priv);

	if (ret)
	{
		return ret;
	}

	mii_check_media(&ip3902_priv->mii, netif_msg_link(ip3902_priv), 1);
	set_duplex_mode(ndev, ip3902_priv->mii.full_duplex);

#ifdef CONFIG_INET_LRO
	init_timer(&ip3902_priv->lro_timer);
	ip3902_priv->lro_timer.data     = (unsigned long) ip3902_priv;
	ip3902_priv->lro_timer.function = ip3902_lro_timeout;
#endif

	netif_start_queue(ndev);

#ifdef IP3902_NAPI
	napi_enable(&ip3902_priv->napi);
#endif

	ip3902_priv->running = 1;

	/* start the MII timer */

	init_timer(&ip3902_priv->mii_timer);

	ip3902_priv->mii_timer.expires  = jiffies+1;
	ip3902_priv->mii_timer.data     = (unsigned long) ndev;
	ip3902_priv->mii_timer.function = ip3902_mii_expiry;

	add_timer(&ip3902_priv->mii_timer);

	return 0;
}

static int ip3902_close(struct net_device *ndev)
{
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);

	dev_dbg(&ip3902_priv->ndev->dev, "%s: close\n", ndev->name);

	ip3902_priv->running = 0;
	wmb();

#ifdef CONFIG_INET_LRO
	del_timer(&ip3902_priv->lro_timer);
#endif

	del_timer_sync(&ip3902_priv->mii_timer);

#ifdef IP3902_NAPI
	napi_disable(&ip3902_priv->napi);
#endif

	ip3902_hw_deinit(ndev);

	netif_stop_queue(ndev);

	ip3902_eth_free_all_tx_descs(ndev, ip3902_priv);

#ifdef IP3902_POWERDOWN_PHY_ON_SUSPEND
        ip3902_phy_powerdown(ndev);
#endif
	free_irq(ndev->irq, ndev);
	return 0;
}

static int parse_mac_address(struct net_device *ndev)
{
	int n = sscanf(mac_address, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
			&ndev->dev_addr[0], &ndev->dev_addr[1],
			&ndev->dev_addr[2], &ndev->dev_addr[3],
			&ndev->dev_addr[4], &ndev->dev_addr[5]);

	if (n == 6)
	{
		return 0;
	}

	printk(KERN_WARNING DRVNAME": failed to parse mac address string \"%s\"\n", mac_address);
	return -EINVAL;
}

static void ip3902_hw_shutdown(struct net_device *ndev, struct ip3902_private *ip3902_priv)
{
	dma_free_coherent(NULL, sizeof(*(ip3902_priv->ds)), ip3902_priv->ds, ip3902_priv->ds_dma);
}

static int ip3902_hw_startup(struct net_device *ndev, struct ip3902_private *ip3902_priv)
{
	ip3902_priv->ds = dma_alloc_coherent(NULL, sizeof(*(ip3902_priv->ds)), &ip3902_priv->ds_dma, GFP_KERNEL);
	if (!ip3902_priv->ds)
	{
		printk(KERN_ERR DRVNAME ": can't allocate DMA structure\n");
		ip3902_hw_shutdown(ndev, ip3902_priv);
		return -ENOMEM;
	}

	/* Poweron hardware */
	ip3902_write_reg(ndev, POWERDOWN_REG, 0);

	/* set mii clock */
	ip3902_write_reg(ndev, MCFG_REG, 0x1c);

	/* Move low level out of reset (also initialize the registers) */
	ip3902_write_reg(ndev, MAC1_REG, 0);
	ip3902_write_reg(ndev, MAC2_REG, INITIAL_MAC2);

	if (!mac_address || parse_mac_address(ndev) < 0)
	{
		unsigned long val;

		val = ip3902_read_reg(ndev, SA0_REG);
		ndev->dev_addr[5] = (val >> 8) & 255;
		ndev->dev_addr[4] = val & 255;
		val = ip3902_read_reg(ndev, SA1_REG);
		ndev->dev_addr[3] = (val >> 8) & 255;
		ndev->dev_addr[2] = val & 255;
		val = ip3902_read_reg(ndev, SA2_REG);
		ndev->dev_addr[1] = (val >> 8) & 255;
		ndev->dev_addr[0] = val & 255;
	}

#ifdef CONFIG_MOCA_PHY
    ip3902_priv->mii.phy_id	= MOCA_PHY_ADDR;
	ip3902_moca_reset(ndev);
#else
	/* Put low-level hardware into reset, and high-level into poweroff */
	ip3902_write_reg(ndev, MAC1_REG, MAC1_SOFT_RESET);
	ip3902_write_reg(ndev, POWERDOWN_REG, POWERDOWN_VALUE);
#endif

	return 0;
}

static int ip3902_init_dev(struct net_device *ndev, struct ip3902_private *ip3902_priv)
{
	int ret;

	ret = ip3902_hw_startup(ndev, ip3902_priv);

	if (!ret)
	{
		ndev->hard_start_xmit    = ip3902_start_xmit;
		ndev->set_mac_address    = ip3902_eth_set_mac_address;
		ndev->set_multicast_list = ip3902_set_rx_filter;
		ndev->open               = ip3902_open;
		ndev->stop               = ip3902_close;
		ndev->do_ioctl           = ip3902_ioctl;
		ndev->features           = 0;
#ifdef CONFIG_NET_POLL_CONTROLLER
		ndev->poll_controller    = ip3902_net_poll;
#endif
		SET_ETHTOOL_OPS(ndev, &ip3902_ethtool_ops);

		ip3902_priv->msg_enable		= NETIF_MSG_LINK;
		ip3902_priv->mii.phy_id_mask	= 0x1f;
		ip3902_priv->mii.reg_num_mask	= 0x1f;
		ip3902_priv->mii.mdio_read	= ip3902_mdio_read;
		ip3902_priv->mii.mdio_write	= ip3902_mdio_write;
		ip3902_priv->mii.dev		= ndev;

		ret = register_netdev(ndev);

		if (ret)
		{
			ip3902_hw_shutdown(ndev, ip3902_priv);
		}
	}

	return ret;
}

#ifdef CONFIG_INET_LRO
static int ip3902_get_skb_hdr(struct sk_buff *skb, void **iphdr, void **tcph, u64 *hdr_flags, void *priv)
{
	unsigned int ip_len;
	struct iphdr *iph;

	/* non tcp packet */
	skb_reset_network_header(skb);
	iph = ip_hdr(skb);
	if (iph->protocol != IPPROTO_TCP)
	{
		return -1;
	}

	ip_len = ip_hdrlen(skb);
	skb_set_transport_header(skb, ip_len);
	*tcph = tcp_hdr(skb);

	/* check if ip header and tcp header are complete */
	if (iph->tot_len < ip_len + tcp_hdrlen(skb))
	{
		return -1;
	}

	*hdr_flags = LRO_IPV4 | LRO_TCP;
	*iphdr = iph;

	return 0;
}
#endif

static int ip3902_remove(struct platform_device *pdev)
{
	struct net_device     *ndev = platform_get_drvdata(pdev);
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);

	platform_set_drvdata(pdev, NULL);

	unregister_netdev(ndev);

	iounmap(ip3902_priv->mem);
	release_resource(ip3902_priv->bus);
	kfree(ip3902_priv->bus);

	free_netdev(ndev);

	return 0;
}

/* ip3902_probe
 *
 * This is the entry point when the platform device system uses to
 * notify us of a new device to attach to. Allocate memory, find
 * the resources and information passed, and map the necessary registers.
*/

static int ip3902_probe(struct platform_device *pdev)
{
	struct net_device     *ndev;
	struct ip3902_private *ip3902_priv;
	struct resource       *res;
	size_t                 size;
	int                    ret;

	ndev = alloc_etherdev(sizeof(struct ip3902_private));

	if (ndev == NULL)
	{
		return -ENOMEM;
	}

	ip3902_priv = netdev_priv(ndev);

	memset(ip3902_priv, 0, sizeof(struct ip3902_private));

	spin_lock_init(&ip3902_priv->mii_lock);

	ip3902_priv->ndev = ndev;
	ip3902_priv->pdev = pdev;

#ifdef IP3902_NAPI
	netif_napi_add(ndev, &ip3902_priv->napi, ip3902_poll, IP3902_NAPI_WEIGHT);
#endif

#ifdef CONFIG_INET_LRO
	ip3902_priv->use_lro   = false;
	ip3902_priv->lro_count = 0;
	ip3902_priv->lro_lock = false;
	ip3902_priv->lro_mgr.max_aggr = IP3902_NAPI_WEIGHT;
	ip3902_priv->lro_mgr.max_desc = MAX_LRO_DESCRIPTORS;
	ip3902_priv->lro_mgr.lro_arr = ip3902_priv->lro_desc;
	ip3902_priv->lro_mgr.get_skb_header = ip3902_get_skb_hdr;
#ifdef IP3902_NAPI
	ip3902_priv->lro_mgr.features = LRO_F_NAPI;
#else
	ip3902_priv->lro_mgr.features = 0;
#endif
	ip3902_priv->lro_mgr.dev = ndev;
	ip3902_priv->lro_mgr.ip_summed = 0;
	ip3902_priv->lro_mgr.ip_summed_aggr = 0;
#endif

	platform_set_drvdata(pdev, ndev);

	/* find the platform resources */
	ndev->irq  = platform_get_irq(pdev, 0);
	if (ndev->irq < 0)
	{
		dev_err(&pdev->dev, "no IRQ specified\n");
		ret = -ENXIO;
		goto exit_mem;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL)
	{
		dev_err(&pdev->dev, "no MEM specified\n");
		ret = -ENXIO;
		goto exit_mem;
	}
	size = (res->end - res->start) + 1;

	ip3902_priv->bus = request_mem_region(res->start & 0x1fffffff, size, pdev->name);
	if (ip3902_priv->bus == NULL)
	{
		dev_err(&pdev->dev, "cannot reserve registers\n");
 		ret = -ENXIO;
		goto exit_mem;
	}

	ip3902_priv->mem = ioremap(res->start & 0x1fffffff, size);
	ndev->base_addr = (unsigned long)ip3902_priv->mem;

	if (ip3902_priv->mem == NULL)
	{
		dev_err(&pdev->dev, "Cannot ioremap area (%08llx,%08llx)\n",
			(unsigned long long)res->start,
			(unsigned long long)res->end);

 		ret = -ENXIO;
		goto exit_req;
	}

	SET_NETDEV_DEV(ndev, &pdev->dev);

	/* got resources, now initialise and register device */
	ret = ip3902_init_dev(ndev, ip3902_priv);
	if (!ret)
	{
		printk("NXP ip3902 10/100 Ethernet platform driver irq %d base %08lx\n", ndev->irq, ndev->base_addr);
		return 0;
	}

 exit_req:
	release_resource(ip3902_priv->bus);
	kfree(ip3902_priv->bus);

 exit_mem:
	free_netdev(ndev);

	return ret;
}

/* suspend and resume */

#ifdef CONFIG_PM
static int ip3902_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct net_device     *ndev = platform_get_drvdata(pdev);
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);

	ip3902_priv->resume_open = ip3902_priv->running;

	netif_device_detach(ndev);
	ip3902_close(ndev);
	return 0;
}

static int ip3902_resume(struct platform_device *pdev)
{
	struct net_device     *ndev = platform_get_drvdata(pdev);
	struct ip3902_private *ip3902_priv = netdev_priv(ndev);

	netif_device_attach(ndev);

	if (ip3902_priv->resume_open)
	{
#ifdef IP3902_POWERDOWN_PHY_ON_SUSPEND
		ip3902_phy_powerup(ndev);
#endif
		ip3902_open(ndev);
	}
	return 0;
}

#else
#define ip3902_suspend NULL
#define ip3902_resume  NULL
#endif

static struct platform_driver ip3902drv = {
	.driver	= {
		.name		= "ip3902-eth",
		.owner		= THIS_MODULE,
	},
	.probe		= ip3902_probe,
	.remove		= ip3902_remove,
	.suspend	= ip3902_suspend,
	.resume		= ip3902_resume,
};

static int __init ip3902drv_init(void)
{
	return platform_driver_register(&ip3902drv);
}

static void __exit ip3902drv_exit(void)
{
	platform_driver_unregister(&ip3902drv);
}

module_init(ip3902drv_init);
module_exit(ip3902drv_exit);

MODULE_DESCRIPTION("NXP ip3902 10/100 Ethernet platform driver");
MODULE_AUTHOR("Chris Steel, <chris.steel@nxp.com>");
MODULE_LICENSE("GPL v2");
