--- /dev/null
+++ b/drivers/net/ethernet/cns21xx/cns21xx_gec_main.c
@@ -0,0 +1,2464 @@
+/*
+ *  Copyright (c) 2010-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This driver has been derived from the ethernet driver of the
+ *  Star STR81xx SoC.
+ *	Copyright (c) 2008 Cavium Networks
+ *
+ *  This file 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/bootmem.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+
+#include <mach/hardware.h>
+#include <mach/cns21xx.h>
+#include <mach/cns21xx_misc.h>
+#include <mach/cns21xx_powermgmt.h>
+#include <mach/cns21xx_gec_platform.h>
+
+#define DRIVER_NAME	"cns21xx-gec"
+
+/* VSC8601 and WavePlus Phy are the same */
+#define CNS21XX_GEC_PHY_ADDR	0
+
+#define CNS21XX_GEC_TX_HW_CHECKSUM
+#define CNS21XX_GEC_RX_HW_CHECKSUM
+
+#define CNS21XX_PEND_INT_COUNT	16
+#define CNS21XX_PEND_INT_TIME	5	/* 5 x 20 usecs */
+
+#define	MAX_PACKET_LEN		1536
+
+#define CNS21XX_GEC_NUM_TXDS	48 /* FIXME: original 64 will cause UDP fail */
+#define CNS21XX_GEC_NUM_RXDS	64
+
+struct cns21xx_gec_mib_info {
+	u32	mib_rx_ok_pkt;
+	u64	mib_rx_ok_byte;
+	u32	mib_rx_runt;
+	u32	mib_rx_over_size;
+	u32	mib_rx_no_buffer_drop;
+	u32	mib_rx_crc_err;
+	u32	mib_rx_arl_drop;
+	u32	mib_rx_myvid_drop;
+	u32	mib_rx_csum_err;
+	u32	mib_rx_pause_frame;
+	u32	mib_tx_ok_pkt;
+	u64	mib_tx_ok_byte;
+	u32	mib_tx_pause_frame;
+};
+
+/*
+ * Network Driver, Receive/Send and Initial Buffer Function
+ */
+struct cns21xx_gec_txd {
+	/* 1st 32Bits */
+	u32 sdp;
+
+	/* 2nd 32Bits */
+	u32 length:16;
+	u32 reserved0:7;
+	u32 tco:1;
+	u32 uco:1;
+	u32 ico:1;
+	u32 insv:1;
+	u32 intr:1;
+	u32 ls:1;
+	u32 fs:1;
+	u32 eor:1;
+	u32 cown:1;
+
+	/* 3rd 32Bits */
+	u32 vid:12;
+	u32 cfi:1;
+	u32 pri:3;
+	u32 epid:16;
+
+	/* 4th 32Bits */
+	u32 reserved1;
+} __packed;
+
+struct cns21xx_gec_rxd {
+	/* 1st 32Bits */
+	u32 sdp;
+
+	/* 2nd  32Bits */
+	u32 length:16;
+	u32 l4f:1;
+	u32 ipf:1;
+	u32 prot:2;
+	u32 vted:1;
+	u32 mymac:1;
+	u32 hhit:1;
+	u32 rmc:1;
+	u32 crce:1;
+	u32 osize:1;
+	u32 reserved0:2;
+	u32 ls:1;
+	u32 fs:1;
+	u32 eor:1;
+	u32 cown:1;
+
+	/* 3rd 32Bits */
+	u32 vid:12;
+	u32 cfi:1;
+	u32 pri:3;
+	u32 epid:16;
+
+	/* 4th 32Bits */
+	u32 reserved1;
+} __packed;
+
+struct cns21xx_gec_ring {
+	u32		desc_dma;
+	void		*desc_cpu;
+	u32		curr;
+	u32		dirty;
+	u32		count;
+	struct sk_buff	**skbs;
+};
+
+#define CNS21XX_GEC_NUM_VLANS	4
+struct cns21xx_gec_vlan {
+	u32 vid;	/* 0~4095 */
+	u32 control;	/* ENABLE or DISABLE */
+};
+
+/* store this information for the driver.. */
+struct cns21xx_gec {
+	struct napi_struct      napi;
+	struct net_device       *netdev;
+	struct device		*parent;
+	struct cns21xx_gec_ring	txring;
+	struct cns21xx_gec_ring	rxring;
+
+	void __iomem		*base;
+	struct resource		*mem_res;
+	struct cns21xx_gec_plat_data *pdata;
+	spinlock_t		lock;
+	spinlock_t		tx_lock;
+
+	int			status_irq;
+	int			rxrc_irq;
+	int			rxqf_irq;
+	int			txtc_irq;
+	int			txqe_irq;
+	unsigned long		rx_queue_full;
+
+	struct cns21xx_gec_vlan	vlans[CNS21XX_GEC_NUM_VLANS];
+
+	struct timer_list	internal_phy_timer;
+	struct timer_list	nic_timer;
+	u8 phy_addr;
+	u16 phy_id;
+	struct cns21xx_gec_mib_info mib_info;
+};
+
+#define GEC_REG_PHY_CTRL0	0x000
+#define GEC_REG_PHY_CTRL1	0x004
+#define GEC_REG_MAC_CFG		0x008
+#define GEC_REG_FC_CFG		0x00c
+#define GEC_REG_ARL_CFG		0x010
+#define GEC_REG_MY_MAC_H	0x014
+#define GEC_REG_MY_MAC_L	0x018
+#define GEC_REG_HASH_CTRL	0x01c
+#define GEC_REG_VLAN_CTRL	0x020
+#define GEC_REG_VLAN_ID_0_1	0x024
+#define GEC_REG_VLAN_ID_2_3	0x028
+#define GEC_REG_DMA_CFG		0x030
+#define GEC_REG_TX_DMA_CTRL	0x034
+#define GEC_REG_RX_DMA_CTRL	0x038
+#define GEC_REG_TX_DPTR		0x03c
+#define GEC_REG_RX_DPTR		0x040
+#define GEC_REG_TX_BASE_ADDR	0x044
+#define GEC_REG_RX_BASE_ADDR	0x048
+#define GEC_REG_DLY_INT_CFG	0x04c
+#define GEC_REG_INT		0x050
+#define GEC_REG_INT_MASK	0x054
+#define GEC_REG_TEST0		0x058
+#define GEC_REG_TEST1		0x05c
+#define GEC_REG_EXTEND_CFG	0x060
+
+#define	GEC_REG_RX_OK_PKT_CNTR		0x100
+#define	GEC_REG_RX_OK_BYTE_CNTR		0x104
+#define	GEC_REG_RX_RUNT_BYTE_CNTR	0x108
+#define	GEC_REG_RX_OSIZE_DROP_PKT_CNTR	0x10c
+#define	GEC_REG_RX_NO_BUF_DROP_PKT_CNTR	0x110
+#define	GEC_REG_RX_CRC_ERR_PKT_CNTR	0x114
+#define	GEC_REG_RX_ARL_DROP_PKT_CNTR	0x118
+#define	GEC_REG_MYVLANID_MISMATCH_DROP_PKT_CNTR 0x11c
+#define	GEC_REG_RX_CHKSUM_ERR_PKT_CNTR	0x120
+#define	GEC_REG_RX_PAUSE_FRAME_PKT_CNTR	0x124
+#define	GEC_REG_TX_OK_PKT_CNTR		0x128
+#define	GEC_REG_TX_OK_BYTE_CNTR		0x12c
+#define	GEC_REG_TX_COLLISION_CNTR	0x130
+#define	GEC_REG_TX_PAUSE_FRAME_CNTR	0x130
+#define	GEC_REG_TX_FIFO_UNDERRUN_RETX_CNTR 0x134
+
+#define GEC_INT_MIB_COUNTER_TH	BIT(3)
+#define GEC_INT_PORT_STATUS_CHG	BIT(2)
+
+#define FE_PHY_LED_MODE (0x1 << 12)
+
+static void internal_phy_init_timer(struct cns21xx_gec *gec);
+static void internal_phy_start_timer(struct cns21xx_gec *gec);
+static void internal_phy_stop_timer(struct cns21xx_gec *gec);
+
+static void cns21xx_gec_phy_powerdown(struct cns21xx_gec *gec);
+static void cns21xx_gec_phy_powerup(struct cns21xx_gec *gec);
+
+static inline u32 cns21xx_gec_rr(struct cns21xx_gec *gec, unsigned int reg)
+{
+	return __raw_readl(gec->base + reg);
+}
+
+static inline void cns21xx_gec_wr(struct cns21xx_gec *gec, unsigned int reg,
+			       u32 val)
+{
+	__raw_writel(val, gec->base + reg);
+}
+
+static void cns21xx_gec_timer_func(unsigned long data)
+{
+	struct cns21xx_gec *gec = (struct cns21xx_gec *) data;
+	struct cns21xx_gec_ring *txring = &gec->txring;
+	int i;
+	int txsd_index;
+	int txsd_current;
+	int skb_free_count = 0;
+	struct cns21xx_gec_txd *txd;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	txsd_current = cns21xx_gec_rr(gec, GEC_REG_TX_DPTR);
+	txsd_index = (txsd_current - (u32)txring->desc_dma) >> 4;
+	if (txsd_index > txring->dirty) {
+		skb_free_count = txsd_index - txring->dirty;
+	} else if (txsd_index <= txring->dirty) {
+		skb_free_count = txring->count + txsd_index -
+				 txring->dirty;
+	}
+	for (i = 0; i < skb_free_count; i++) {
+		txd = ((struct cns21xx_gec_txd *) txring->desc_cpu) +
+		      txring->dirty;
+
+		if (txd->cown == 0)
+			break;
+
+		if (txring->skbs[txring->dirty]) {
+			dev_kfree_skb_any(txring->skbs[txring->dirty]);
+			txring->skbs[txring->dirty] = NULL;
+
+			dma_unmap_single(gec->parent,
+					 txd->sdp,
+					 txd->length,
+					 DMA_TO_DEVICE);
+		}
+
+		txring->dirty++;
+		if (txring->dirty == txring->count)
+			txring->dirty = 0;
+	}
+	local_irq_restore(flags);
+}
+
+static void __init cns21xx_gec_timer_init(struct cns21xx_gec *gec)
+{
+	init_timer(&gec->nic_timer);
+	gec->nic_timer.function = &cns21xx_gec_timer_func;
+	gec->nic_timer.data = (unsigned long) gec;
+}
+
+static void cns21xx_gec_timer_modify(struct cns21xx_gec *gec, unsigned int t)
+{
+	mod_timer(&gec->nic_timer, jiffies + t);
+}
+
+static int cns21xx_gec_write_phy(struct cns21xx_gec *gec,
+			      u8 addr, u8 reg, u16 val)
+{
+	int i;
+
+	if (addr > 31 || reg > 31)
+		return -EINVAL;
+
+	/* clear previous rw_ok status */
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, 0x1 << 15);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0,
+		    addr | (reg  << 8) | (val << 16) | (0x1 << 13));
+
+	for (i = 0; i < 10000; i++) {
+		u32 status;
+
+		status = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL0);
+		if (status & (0x1 << 15)) {
+			/*
+			 * clear the rw_ok status,
+			 * and clear other bits value
+			 */
+			cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, (0x1 << 15));
+			return 0;
+		}
+		udelay(1000);
+	}
+
+	dev_err(&gec->netdev->dev,
+		"%s timed out, phy_addr:0x%x, phy_reg:0x%x, write_data:0x%x\n",
+	       __func__, addr, reg, val);
+
+	return -EIO;
+}
+
+static int cns21xx_gec_read_phy(struct cns21xx_gec *gec,
+			     u8 addr, u8 reg, u16 *val)
+{
+	int i;
+
+	if (addr > 31 || reg > 31)
+		return -EINVAL;
+
+	/* clear previous rw_ok status */
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, 0x1 << 15);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0,
+		    addr | (reg << 8) | (0x1 << 14));
+
+	for (i = 0; i < 10000; i++) {
+		u32 status;
+
+		status = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL0);
+		if (status & (0x1 << 15)) {
+			/*
+			 * clear the rw_ok status,
+			 * and clear other bits value
+			 */
+			cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL0, (0x1 << 15));
+			*val = (status >> 16) & 0xffff;
+			return 0;
+		}
+		udelay(1000);
+	}
+
+	dev_err(&gec->netdev->dev,
+		"%s timed out, phy_addr:0x%x, phy_reg:0x%x\n",
+	       __func__, addr, reg);
+
+	*val = 0xffff;
+	return -EIO;
+}
+
+static void cns21xx_gec_dma_config(struct cns21xx_gec *gec)
+{
+	u32 dma_config = 0;
+
+	dma_config = cns21xx_gec_rr(gec, GEC_REG_DMA_CFG);
+
+	/* Config TX DMA */
+	/* TX auto polling: 1 us */
+	dma_config &= ~(0x3 << 6);
+	/* TX auto polling :100us */
+	dma_config |= (0x2 << 6);
+	/* TX auto polling C-bit enable */
+	dma_config |= (0x1 << 5);
+	/* TX can transmit packets, No suspend */
+	dma_config &= ~(0x1 << 4);
+
+	/* Config RX DMA */
+	/* RX auto polling: 1 us */
+	dma_config &=  ~(0x3 << 2);
+	/* RX auto polling :100us */
+	dma_config |=  (0x2 << 2);
+	/* RX auto polling C-bit enable */
+	dma_config |=  (0x1 << 1);
+	/* RX can receive packets, No suspend */
+	dma_config &=  ~0x1;
+
+	/* 4N+2(for Linux) */
+	dma_config &= ~(0x1 << 16);
+
+	cns21xx_gec_wr(gec, GEC_REG_DMA_CFG, dma_config);
+}
+
+static void cns21xx_gec_mac_config(struct cns21xx_gec *gec)
+{
+	u32 mac_config;
+
+	mac_config = cns21xx_gec_rr(gec, GEC_REG_MAC_CFG);
+
+#ifdef CNS21XX_GEC_TX_HW_CHECKSUM
+	/* Tx ChkSum offload On: TCP/UDP/IP */
+	mac_config |= (0x1 << 26);
+#else
+	/* Tx ChkSum offload Off: TCP/UDP/IP */
+	mac_config &= ~(0x1 << 26);
+#endif
+
+#ifdef CNS21XX_GEC_RX_HW_CHECKSUM
+	/* Rx ChkSum offload On: TCP/UDP/IP */
+	mac_config |= (0x1 << 25);
+#else
+	/* Rx ChkSum offload Off: TCP/UDP/IP */
+	mac_config &= ~(0x1 << 25);
+#endif
+
+	/* Accept CSUM error pkt */
+	mac_config |= (0x1 << 24);
+	/* IST disable */
+	mac_config &= ~(0x1 << 23);
+	/* Strip vlan tag */
+	mac_config |= (0x1 << 22);
+	/* Accept CRC error pkt */
+	mac_config |= (0x1 << 21);
+	/* CRC strip */
+	mac_config |= (0x1 << 20);
+
+	/* Discard oversize pkt */
+	mac_config &= ~(0x1 << 18);
+
+	/* clear, set 1518 */
+	mac_config &= ~(0x3 << 16);
+
+	/* 1536 */
+	mac_config |= (0x2 << 16);
+
+	/* IPG */
+	mac_config |= (0x1f << 10);
+
+	/* Do not skip 16 consecutive collisions pkt */
+	/* allow to re-tx */
+	mac_config |= (0x1 << 9);
+	/* Fast retry */
+	mac_config |= (0x1 << 8);
+
+	cns21xx_gec_wr(gec, GEC_REG_MAC_CFG, mac_config);
+}
+
+static void cns21xx_gec_fc_config(struct cns21xx_gec *gec)
+{
+	u32 fc_config;
+
+	fc_config = cns21xx_gec_rr(gec, GEC_REG_FC_CFG);
+
+	/* Send pause on frame threshold */
+	/* Clear */
+	fc_config &= ~(0xfff << 16);
+	fc_config |= (0x360 << 16);
+	/* Disable UC_PAUSE */
+	fc_config &= ~(0x1 << 8);
+	/* Enable Half Duplex backpressure */
+	fc_config |= (0x1 << 7);
+	/* Collision-based BP */
+	fc_config &= ~(0x1 << 6);
+	/* Disable max BP collision */
+	fc_config &= ~(0x1 << 5);
+	/* Clear */
+	fc_config &= ~(0x1f);
+	/* Set */
+	fc_config |= (0xc);
+
+	cns21xx_gec_wr(gec, GEC_REG_FC_CFG, fc_config);
+}
+
+static void cns21xx_gec_internal_phy_config(struct cns21xx_gec *gec)
+{
+	u32 phy_ctrl1;
+	u32 phy_addr;
+
+	dev_info(&gec->netdev->dev, "Internal PHY\n");
+
+	phy_addr = CNS21XX_GEC_PHY_ADDR;
+	gec->phy_addr = phy_addr;
+
+	phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1);
+
+	/* set phy addr for auto-polling */
+	phy_ctrl1 |= (phy_addr & 0x1f) << 24;
+
+	/* set internal phy mode */
+	/* internel 10/100 phy */
+	phy_ctrl1 |= 0x1 << 18;
+
+	/* MII */
+	phy_ctrl1 &= ~(0x1 << 17);
+
+	/* MAC mode */
+	phy_ctrl1 &= ~(0x1 << 16);
+
+	/* config PHY LED bit[13:12] */
+	cns21xx_gec_read_phy(gec, phy_addr, 31, (u16 *)(&phy_ctrl1));
+	/* clear LED control */
+	phy_ctrl1 &= ~(0x3 << 12);
+	phy_ctrl1 |= FE_PHY_LED_MODE;
+	cns21xx_gec_write_phy(gec, phy_addr, 31, phy_ctrl1);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+}
+
+static void cns21xx_gec_vsc8601_phy_config(struct cns21xx_gec *gec)
+{
+	u32 phy_ctrl1;
+	u32 phy_addr;
+	u16 phy_data;
+
+	phy_addr = CNS21XX_GEC_PHY_ADDR;
+	gec->phy_addr = phy_addr;
+
+	phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1);
+
+	/* phy addr for auto-polling */
+	phy_ctrl1 |= phy_addr << 24;
+
+	/* set external phy mode */
+	phy_ctrl1 &= ~(0x1 << 18);
+
+	/* set RGMII */
+	phy_ctrl1 |= (0x1 << 17);
+
+	/* set MII interface */
+	phy_ctrl1 &= ~(0x1 << 16);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+
+	/* set phy addr for auto-polling */
+	phy_ctrl1 |= phy_addr << 24;
+
+	/* set external phy mode */
+	/* MII/RGMII interface */
+	phy_ctrl1 &= ~(0x1 << 18);
+
+	/* RGMII */
+	phy_ctrl1 |= (0x1 << 17);
+
+	/* MAC mode */
+	phy_ctrl1 &= ~(0x1 << 16);
+
+	cns21xx_gec_read_phy(gec, phy_addr, 3, &phy_data);
+	if ((phy_data & 0x000f) == 0x0000) {
+		/* type A chip */
+		u16 tmp16;
+
+		dev_info(&gec->netdev->dev, "VSC8601 Type A Chip\n");
+		cns21xx_gec_write_phy(gec, phy_addr, 31, 0x52B5);
+		cns21xx_gec_write_phy(gec, phy_addr, 16, 0xAF8A);
+
+		phy_data = 0x0;
+		cns21xx_gec_read_phy(gec, phy_addr, 18, &tmp16);
+		phy_data |= (tmp16 & ~0x0);
+		cns21xx_gec_write_phy(gec, phy_addr, 18, phy_data);
+
+		phy_data = 0x0008;
+		cns21xx_gec_read_phy(gec, phy_addr, 17, &tmp16);
+		phy_data |= (tmp16 & ~0x000C);
+		cns21xx_gec_write_phy(gec, phy_addr, 17, phy_data);
+
+		cns21xx_gec_write_phy(gec, phy_addr, 16, 0x8F8A);
+		cns21xx_gec_write_phy(gec, phy_addr, 16, 0xAF86);
+
+		phy_data = 0x0008;
+		cns21xx_gec_read_phy(gec, phy_addr, 18, &tmp16);
+		phy_data |= (tmp16 & ~0x000C);
+		cns21xx_gec_write_phy(gec, phy_addr, 18, phy_data);
+
+		phy_data = 0x0;
+		cns21xx_gec_read_phy(gec, phy_addr, 17, &tmp16);
+		phy_data |= (tmp16 & ~0x0);
+		cns21xx_gec_write_phy(gec, phy_addr, 17, phy_data);
+
+		cns21xx_gec_write_phy(gec, phy_addr, 16, 0x8F8A);
+
+		cns21xx_gec_write_phy(gec, phy_addr, 16, 0xAF82);
+
+		phy_data = 0x0;
+		cns21xx_gec_read_phy(gec, phy_addr, 18, &tmp16);
+		phy_data |= (tmp16 & ~0x0);
+		cns21xx_gec_write_phy(gec, phy_addr, 18, phy_data);
+
+		phy_data = 0x0100;
+		cns21xx_gec_read_phy(gec, phy_addr, 17, &tmp16);
+		phy_data |= (tmp16 & ~0x0180);
+		cns21xx_gec_write_phy(gec, phy_addr, 17, phy_data);
+
+		cns21xx_gec_write_phy(gec, phy_addr, 16, 0x8F82);
+
+		cns21xx_gec_write_phy(gec, phy_addr, 31, 0x0);
+
+		/* Set port type: single port */
+		cns21xx_gec_read_phy(gec, phy_addr, 9, &phy_data);
+		phy_data &= ~(0x1 << 10);
+		cns21xx_gec_write_phy(gec, phy_addr, 9, phy_data);
+	} else if ((phy_data & 0x000f) == 0x0001) {
+		/* type B chip */
+		dev_info(&gec->netdev->dev, "VSC8601 Type B Chip\n");
+
+		cns21xx_gec_read_phy(gec, phy_addr, 23, &phy_data);
+		phy_data |= (0x1 << 8); 	/* set RGMII timing skew */
+		cns21xx_gec_write_phy(gec, phy_addr, 23, phy_data);
+	}
+
+	/* change to extened registers */
+	cns21xx_gec_write_phy(gec, phy_addr, 31, 0x0001);
+
+	cns21xx_gec_read_phy(gec, phy_addr, 28, &phy_data);
+	phy_data &= ~(0x3 << 14); 		/* set RGMII TX timing skew */
+	phy_data |= (0x3 << 14); 		/* 2.0ns */
+	phy_data &= ~(0x3 << 12); 		/* set RGMII RX timing skew */
+	phy_data |= (0x3 << 12); 		/* 2.0ns */
+	cns21xx_gec_write_phy(gec, phy_addr, 28, phy_data);
+
+	/* change to normal registers */
+	cns21xx_gec_write_phy(gec, phy_addr, 31, 0x0000);
+
+#if 0
+	/* set TX and RX clock skew */
+	cns21xx_gec_wr(gec, GEC_REG_TEST0, (0x2 << 2) | (0x2 << 0));
+#endif
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+}
+
+static void cns21xx_gec_ip101a_phy_config(struct cns21xx_gec *gec)
+{
+	u32 phy_ctrl1;
+	u32 phy_addr;
+
+	dev_info(&gec->netdev->dev, "ICPlus IP101A\n");
+
+	phy_addr = 1;
+	gec->phy_addr = phy_addr;
+
+	phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1);
+
+	/* set phy addr for auto-polling */
+	phy_ctrl1 |= phy_addr << 24;
+
+	/* set external phy mode */
+	/* MII/RGMII interface */
+	phy_ctrl1 &= ~(0x1 << 18);
+
+	/* MII */
+	phy_ctrl1 &= ~(0x1 << 17);
+
+	/* MAC mode */
+	phy_ctrl1 &= ~(0x1 << 16);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+}
+
+static void cns21xx_gec_ip1001_phy_config(struct cns21xx_gec *gec)
+{
+	u32 phy_ctrl1;
+	u32 phy_addr;
+	u16 phy_data;
+
+	dev_info(&gec->netdev->dev, "ICPlus IP1001\n");
+
+	phy_addr = 1;
+	gec->phy_addr = phy_addr;
+
+	phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1);
+
+	/* set phy addr for auto-polling */
+	phy_ctrl1 |= phy_addr << 24;
+
+	/* set external phy mode */
+	/* MII/RGMII interface */
+	phy_ctrl1 &= ~(0x1 << 18);
+
+	/* RGMII */
+	phy_ctrl1 |= (0x1 << 17);
+
+	/* MAC mode */
+	phy_ctrl1 &= ~(0x1 << 16);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+	cns21xx_gec_read_phy(gec, phy_addr, 2, &phy_data);
+
+	/* set AN capability */
+	cns21xx_gec_read_phy(gec, phy_addr, 4, &phy_data);
+	/* clear existing values */
+	phy_data &= ~(0xf << 5);
+	/* 10Half */
+	phy_data |= (0x1 << 5);
+	/* 10Full */
+	phy_data |= (0x1 << 6);
+	/* 100Half */
+	phy_data |= (0x1 << 7);
+	/* 100Full */
+	phy_data |= (0x1 << 8);
+	/* FC on */
+	phy_data |= (0x1 << 10);
+	cns21xx_gec_write_phy(gec, phy_addr, 4, phy_data);
+
+	cns21xx_gec_read_phy(gec, phy_addr, 9, &phy_data);
+	/* 1000Full on */
+	phy_data |= (0x1 << 9);
+	phy_data &= ~(0x1 << 10);
+	phy_data |= (0x1 << 12);
+	cns21xx_gec_write_phy(gec, phy_addr, 9, phy_data);
+
+	cns21xx_gec_read_phy(gec, phy_addr, 16, &phy_data);
+	/* Smart function off */
+	phy_data &= ~(0x1 << 11);
+	/* TX delay */
+	phy_data |= (0x1 << 0);
+	/* RX delay */
+	phy_data |= (0x1 << 1);
+	cns21xx_gec_write_phy(gec, phy_addr, 16, phy_data);
+
+	cns21xx_gec_read_phy(gec, phy_addr, 16, &phy_data);
+
+#if 0
+	cns21xx_gec_read_phy(gec, phy_addr, 20, &phy_data);
+	phy_data &= ~(0x1<<2);
+
+	phy_data |=  (0x1<<9);
+	cns21xx_gec_write_phy(gec, phy_addr, 20, phy_data);
+#endif
+
+	cns21xx_gec_read_phy(gec, phy_addr, 0, &phy_data);
+	phy_data |= (0x1 << 9); 			/* re-AN */
+	cns21xx_gec_write_phy(gec, phy_addr, 0, phy_data);
+
+	cns21xx_gec_read_phy(gec, phy_addr, 9, &phy_data);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+}
+
+static int cns21xx_gec_phy_config(struct cns21xx_gec *gec)
+{
+	u32 phy_ctrl1;
+
+	switch (gec->pdata->phy_type) {
+	case CNS21XX_GEC_PHY_TYPE_INTERNAL:
+		cns21xx_gec_internal_phy_config(gec);
+		break;
+
+	case CNS21XX_GEC_PHY_TYPE_VSC8601:
+		cns21xx_gec_vsc8601_phy_config(gec);
+		break;
+
+	case CNS21XX_GEC_PHY_TYPE_IP101A:
+		cns21xx_gec_ip101a_phy_config(gec);
+		break;
+
+	case CNS21XX_GEC_PHY_TYPE_IP1001:
+		cns21xx_gec_ip1001_phy_config(gec);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1);
+
+	/* AN On */
+	phy_ctrl1 |= (0x1 << 8);
+	if (!((phy_ctrl1 >> 8) & 0x1)) { 	/* AN disable */
+		/* Force to FullDuplex mode */
+		phy_ctrl1 &= ~(0x1 << 11);	/* Half */
+
+		/* Force to 100Mbps mode */
+		phy_ctrl1 &= ~(0x3 << 9);	/* clear to 10M */
+		phy_ctrl1 |= (0x1 << 9);	/* set to 100M */
+	}
+
+	/* Force TX FlowCtrl On,in 1000M */
+	phy_ctrl1 |= (0x1 << 13);
+
+	/* Force TX FlowCtrl On, in 10/100M */
+	phy_ctrl1 |= (0x1 << 12);
+
+	/* Enable MII auto polling */
+	phy_ctrl1 &= ~(0x1 << 7);
+
+	cns21xx_gec_wr(gec, GEC_REG_PHY_CTRL1, phy_ctrl1);
+	cns21xx_gec_phy_powerdown(gec);
+
+	return 0;
+}
+
+static void cns21xx_gec_vlan_config(struct cns21xx_gec *gec)
+{
+	/* setup VLAN entries */
+	gec->vlans[0].vid = 2;
+	gec->vlans[0].control = 0;
+	gec->vlans[1].vid = 2;
+	gec->vlans[1].control = 1;
+	gec->vlans[2].vid = 1;
+	gec->vlans[2].control = 1;
+	gec->vlans[3].vid = 1;
+	gec->vlans[3].control = 0;
+
+	cns21xx_gec_wr(gec, GEC_REG_VLAN_ID_0_1,
+		       (gec->vlans[0].vid & 0x0fff) |
+		       ((gec->vlans[1].vid & 0x0fff) << 16));
+
+	cns21xx_gec_wr(gec, GEC_REG_VLAN_ID_2_3,
+		       (gec->vlans[2].vid & 0x0fff) |
+		       ((gec->vlans[3].vid & 0x0fff) << 16));
+
+	cns21xx_gec_wr(gec, GEC_REG_VLAN_CTRL,
+		       (gec->vlans[0].control << 0) |
+		       (gec->vlans[1].control << 1) |
+		       (gec->vlans[2].control << 2) |
+		       (gec->vlans[3].control << 3));
+}
+
+static int cns21xx_gec_arl_config(struct cns21xx_gec *gec)
+{
+	u32 arl_config;
+
+	arl_config = cns21xx_gec_rr(gec, GEC_REG_ARL_CFG);
+
+	/* Misc Mode ON */
+	arl_config |= (0x1 << 4);
+
+	/* My MAC only enable */
+	arl_config |= (0x1 << 3);
+
+	/* Learn SA On */
+	arl_config &= ~(0x1 << 2);
+
+	/* Forward MC to CPU */
+	arl_config &= ~(0x1 << 1);
+
+	/* Hash direct mode */
+	arl_config &= ~(0x1);
+
+	cns21xx_gec_wr(gec, GEC_REG_ARL_CFG, arl_config);
+
+	return 0;
+}
+
+static void cns21xx_gec_phy_powerdown(struct cns21xx_gec *gec)
+{
+	u16 phy_data = 0;
+
+	cns21xx_gec_read_phy(gec, gec->phy_addr, 0, &phy_data);
+	phy_data |= (0x1 << 11);
+	cns21xx_gec_write_phy(gec, gec->phy_addr, 0, phy_data);
+
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15);
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG &= ~(0x1 << 15);
+}
+
+static void cns21xx_gec_phy_powerup(struct cns21xx_gec *gec)
+{
+	u16 phy_data = 0;
+
+	cns21xx_gec_read_phy(gec, gec->phy_addr, 0, &phy_data);
+	phy_data &= ~(0x1 << 11);
+	cns21xx_gec_write_phy(gec, gec->phy_addr, 0, phy_data);
+
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15);
+}
+
+static void cns21xx_gec_enable(struct cns21xx_gec *gec)
+{
+	/* start Rx DMA */
+	cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 1);
+
+	cns21xx_gec_phy_powerup(gec);
+	internal_phy_start_timer(gec);
+}
+
+static void cns21xx_gec_shutdown(struct cns21xx_gec *gec)
+{
+	/* stop Rx and Tx DMA */
+	cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 0);
+	cns21xx_gec_wr(gec, GEC_REG_TX_DMA_CTRL, 0);
+
+	internal_phy_stop_timer(gec);
+}
+
+static irqreturn_t cns21xx_gec_receive_isr(int irq, void *dev_id)
+{
+	struct cns21xx_gec *gec = dev_id;
+
+	if (!test_bit(NAPI_STATE_SCHED, &gec->napi.state)) {
+		if (likely(napi_schedule_prep(&gec->napi)))
+			__napi_schedule(&gec->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int cns21xx_gec_install_receive_isr(struct cns21xx_gec *gec)
+{
+	int err;
+
+	err = request_irq(gec->rxrc_irq, cns21xx_gec_receive_isr,
+			  IRQF_SHARED, dev_name(&gec->netdev->dev), gec);
+	if (err)
+		dev_err(&gec->netdev->dev,
+			"unable to get IRQ %d (err=%d)\n",
+			 gec->rxrc_irq, err);
+
+	return err;
+}
+
+static void cns21xx_gec_uninstall_receive_isr(struct cns21xx_gec *gec)
+{
+	free_irq(gec->rxrc_irq, gec);
+}
+
+static irqreturn_t cns21xx_gec_rxqf_isr(int irq, void *dev_id)
+{
+	struct cns21xx_gec *gec = dev_id;
+
+	/*
+	 * because in normal state, fsql only invoke once
+	 * and set_bit is atomic function, so don't mask it
+	 */
+	set_bit(0, &gec->rx_queue_full);
+	if (!test_bit(NAPI_STATE_SCHED, &gec->napi.state)) {
+		if (likely(napi_schedule_prep(&gec->napi)))
+			__napi_schedule(&gec->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int cns21xx_gec_install_rxqf_isr(struct cns21xx_gec *gec)
+{
+	int err;
+
+	/*  QUEUE full interrupt handler */
+	err = request_irq(gec->rxqf_irq, cns21xx_gec_rxqf_isr,
+			  IRQF_SHARED, dev_name(&gec->netdev->dev), gec);
+	if (err)
+		dev_err(&gec->netdev->dev,
+			"unable to get IRQ %d (err=%d)\n",
+			 gec->rxqf_irq, err);
+
+	return err;
+}
+
+static void cns21xx_gec_uninstall_rxqf_isr(struct cns21xx_gec *gec)
+{
+	free_irq(gec->rxqf_irq, gec);
+}
+
+static void cns21xx_gec_mib_reset(struct cns21xx_gec *gec)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_OK_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_OK_BYTE_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_RUNT_BYTE_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_OSIZE_DROP_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_NO_BUF_DROP_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_CRC_ERR_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_ARL_DROP_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_MYVLANID_MISMATCH_DROP_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_CHKSUM_ERR_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_RX_PAUSE_FRAME_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_TX_OK_PKT_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_TX_OK_BYTE_CNTR);
+	(void) cns21xx_gec_rr(gec, GEC_REG_TX_PAUSE_FRAME_CNTR);
+	local_irq_restore(flags);
+}
+
+static void cns21xx_gec_mib_read(struct cns21xx_gec *gec)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	gec->mib_info.mib_rx_ok_pkt +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_OK_PKT_CNTR);
+	gec->mib_info.mib_rx_ok_byte +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_OK_BYTE_CNTR);
+	gec->mib_info.mib_rx_runt +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_RUNT_BYTE_CNTR);
+	gec->mib_info.mib_rx_over_size +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_OSIZE_DROP_PKT_CNTR);
+	gec->mib_info.mib_rx_no_buffer_drop +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_NO_BUF_DROP_PKT_CNTR);
+	gec->mib_info.mib_rx_crc_err +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_CRC_ERR_PKT_CNTR);
+	gec->mib_info.mib_rx_arl_drop +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_ARL_DROP_PKT_CNTR);
+	gec->mib_info.mib_rx_myvid_drop +=
+		cns21xx_gec_rr(gec, GEC_REG_MYVLANID_MISMATCH_DROP_PKT_CNTR);
+	gec->mib_info.mib_rx_csum_err +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_CHKSUM_ERR_PKT_CNTR);
+	gec->mib_info.mib_rx_pause_frame +=
+		cns21xx_gec_rr(gec, GEC_REG_RX_PAUSE_FRAME_PKT_CNTR);
+	gec->mib_info.mib_tx_ok_pkt +=
+		cns21xx_gec_rr(gec, GEC_REG_TX_OK_PKT_CNTR);
+	gec->mib_info.mib_tx_ok_byte +=
+		cns21xx_gec_rr(gec, GEC_REG_TX_OK_BYTE_CNTR);
+	gec->mib_info.mib_tx_pause_frame +=
+		cns21xx_gec_rr(gec, GEC_REG_TX_PAUSE_FRAME_CNTR);
+	local_irq_restore(flags);
+}
+
+static const char *cns21xx_gec_speed_str(u32 phy_ctrl1)
+{
+	switch ((phy_ctrl1 >> 2) & 3) {
+	case 0:
+		return "10";
+	case 1:
+		return "100";
+	case 2:
+		return "1000";
+	}
+
+	return "???";
+}
+
+static irqreturn_t cns21xx_gec_status_isr(int irq, void *dev_id)
+{
+	struct cns21xx_gec *gec = dev_id;
+	u32 int_status;
+
+	int_status = cns21xx_gec_rr(gec, GEC_REG_INT);
+	cns21xx_gec_wr(gec, GEC_REG_INT, int_status);
+
+	/* flush write */
+	(void) cns21xx_gec_rr(gec, GEC_REG_INT);
+
+	if (int_status & GEC_INT_MIB_COUNTER_TH)
+		cns21xx_gec_mib_read(gec);
+
+	if (int_status & GEC_INT_PORT_STATUS_CHG) {
+		u32 phy_ctrl1;
+
+		phy_ctrl1 = cns21xx_gec_rr(gec, GEC_REG_PHY_CTRL1);
+		if (phy_ctrl1 & BIT(0)) {
+			netif_carrier_on(gec->netdev);
+			dev_info(&gec->netdev->dev,
+				"link up (%sMbps/%s duplex)\n",
+				cns21xx_gec_speed_str(phy_ctrl1),
+				(phy_ctrl1 & BIT(4)) ? "Full" : "Half");
+		} else {
+			netif_carrier_off(gec->netdev);
+			dev_info(&gec->netdev->dev, "link down\n");
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static inline void cns21xx_gec_enable_interrupt(struct cns21xx_gec *gec,
+					     u32 mask)
+{
+	cns21xx_gec_wr(gec, GEC_REG_INT_MASK,
+		    cns21xx_gec_rr(gec, GEC_REG_INT_MASK) & ~mask);
+}
+
+static int cns21xx_gec_install_status_isr(struct cns21xx_gec *gec)
+{
+	int err;
+
+	err = request_irq(gec->status_irq, cns21xx_gec_status_isr,
+			  IRQF_DISABLED, dev_name(&gec->netdev->dev), gec);
+
+	if (err) {
+		dev_err(&gec->netdev->dev,
+			"unable to get IRQ %d (err=%d)\n",
+			 gec->status_irq, err);
+		return err;
+	}
+
+	cns21xx_gec_enable_interrupt(gec, GEC_INT_MIB_COUNTER_TH |
+					  GEC_INT_PORT_STATUS_CHG);
+
+	return 0;
+}
+
+static inline void cns21xx_gec_uninstall_status_isr(struct cns21xx_gec *gec)
+{
+	free_irq(gec->status_irq, gec);
+}
+
+static void cns21xx_gec_uninstall_isr(struct cns21xx_gec *gec)
+{
+	cns21xx_gec_uninstall_rxqf_isr(gec);
+	cns21xx_gec_uninstall_status_isr(gec);
+	cns21xx_gec_uninstall_receive_isr(gec);
+}
+
+static int cns21xx_gec_install_isr(struct cns21xx_gec *gec)
+{
+	int err;
+
+	/* setup delayed interrupts */
+	cns21xx_gec_wr(gec, GEC_REG_DLY_INT_CFG,
+		       (1 << 16) |
+		       ((CNS21XX_PEND_INT_COUNT & 0xFF) << 8) |
+		       (CNS21XX_PEND_INT_TIME & 0xFF));
+
+	err = cns21xx_gec_install_receive_isr(gec);
+	if (err)
+		goto err_out;
+
+	err = cns21xx_gec_install_rxqf_isr(gec);
+	if (err)
+		goto err_uninstall_receive;
+
+	err = cns21xx_gec_install_status_isr(gec);
+	if (err)
+		goto err_uninstall_rxqf;
+
+	return 0;
+
+ err_uninstall_rxqf:
+	cns21xx_gec_uninstall_rxqf_isr(gec);
+ err_uninstall_receive:
+	cns21xx_gec_uninstall_receive_isr(gec);
+ err_out:
+	return err;
+}
+
+static int cns21xx_gec_lan_open(struct net_device *dev)
+{
+	struct cns21xx_gec *gec = netdev_priv(dev);
+	int err;
+
+	dev_dbg(&gec->netdev->dev, "open\n");
+
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+
+	napi_enable(&gec->napi);
+	netif_start_queue(dev);
+	err = cns21xx_gec_install_isr(gec);
+	if (err)
+		goto err;
+
+	cns21xx_gec_enable(gec);
+
+	return 0;
+
+ err:
+	netif_stop_queue(dev);
+	napi_disable(&gec->napi);
+	return err;
+}
+
+static void cns21xx_gec_timeout(struct net_device *dev)
+{
+	dev_dbg(&dev->dev, "timeout\n");
+	netif_wake_queue(dev);
+	dev->trans_start = jiffies;
+}
+
+static int cns21xx_gec_close(struct net_device *dev)
+{
+	struct cns21xx_gec *gec = netdev_priv(dev);
+
+	cns21xx_gec_phy_powerdown(gec);
+	cns21xx_gec_uninstall_isr(gec);
+	napi_disable(&gec->napi);
+	netif_stop_queue(dev);
+	cns21xx_gec_shutdown(gec);
+
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+
+	return 0;
+}
+
+static inline struct sk_buff *cns21xx_gec_alloc_skb(void)
+{
+	struct sk_buff *skb;
+
+	skb = dev_alloc_skb(MAX_PACKET_LEN + 2);
+
+	if (unlikely(!skb))
+		return NULL;
+
+	/* Make buffer alignment 2 beyond a 16 byte boundary
+	 * this will result in a 16 byte aligned IP header after
+	 * the 14 byte MAC header is removed
+	 */
+	skb_reserve(skb, 2);	/* 16 bit alignment */
+
+	return skb;
+}
+
+static inline int cns21xx_gec_tx_dma_size(struct cns21xx_gec *gec)
+{
+	return gec->txring.count * sizeof(struct cns21xx_gec_txd);
+}
+
+static inline int cns21xx_gec_rx_dma_size(struct cns21xx_gec *gec)
+{
+	return gec->rxring.count * sizeof(struct cns21xx_gec_rxd);
+}
+
+static void __init cns21xx_gec_buffer_free(struct cns21xx_gec *gec)
+{
+	struct cns21xx_gec_ring *txring = &gec->txring;
+	struct cns21xx_gec_ring *rxring = &gec->rxring;
+	int i;
+
+	if (rxring->desc_cpu) {
+		for (i = 0; i < rxring->count; i++) {
+			if (rxring->skbs[i])
+				dev_kfree_skb(rxring->skbs[i]);
+		}
+
+		dma_free_coherent(gec->parent, cns21xx_gec_rx_dma_size(gec),
+				  rxring->desc_cpu, rxring->desc_dma);
+		memset((void *)&rxring, 0, cns21xx_gec_rx_dma_size(gec));
+	}
+
+	if (txring->desc_cpu) {
+		dma_free_coherent(gec->parent, cns21xx_gec_tx_dma_size(gec),
+				  txring->desc_cpu, txring->desc_dma);
+		memset((void *)&txring, 0, cns21xx_gec_tx_dma_size(gec));
+	}
+
+	kfree(txring->skbs);
+	kfree(rxring->skbs);
+}
+
+static int __init cns21xx_gec_buffer_alloc(struct cns21xx_gec *gec)
+{
+	struct cns21xx_gec_ring *txring = &gec->txring;
+	struct cns21xx_gec_ring *rxring = &gec->rxring;
+	struct cns21xx_gec_rxd *rxd;
+	struct cns21xx_gec_txd *txd;
+	struct sk_buff	*skb;
+	int err = -ENOMEM;
+	int i;
+
+	rxring->skbs = kzalloc(rxring->count * sizeof(struct skb *),
+			       GFP_KERNEL);
+
+	if (rxring->skbs == NULL) {
+		dev_err(&gec->netdev->dev,
+			"%s allocation failed\n", "RX buffer");
+		goto err_out;
+	}
+
+	txring->skbs = kzalloc(txring->count * sizeof(struct skb *),
+			       GFP_KERNEL);
+
+	if (txring->skbs == NULL) {
+		dev_err(&gec->netdev->dev,
+			"%s allocation failed\n", "TX buffer");
+		goto err_out;
+	}
+
+	rxring->desc_cpu = dma_alloc_coherent(gec->parent,
+					      cns21xx_gec_rx_dma_size(gec),
+					      &rxring->desc_dma,
+					      GFP_KERNEL);
+	if (!rxring->desc_cpu) {
+		dev_err(&gec->netdev->dev,
+			"%s allocation failed\n", "RX ring");
+		goto err_out;
+	}
+
+	txring->desc_cpu = dma_alloc_coherent(gec->parent,
+					      cns21xx_gec_tx_dma_size(gec),
+					      &txring->desc_dma,
+					      GFP_KERNEL);
+	if (!txring->desc_cpu) {
+		dev_err(&gec->netdev->dev,
+			"%s allocation failed\n", "TX ring");
+		goto err_out;
+	}
+
+	/* Clean RX Memory */
+	memset((void *)rxring->desc_cpu, 0, cns21xx_gec_rx_dma_size(gec));
+	dev_dbg(&gec->netdev->dev,
+		"rxring->desc_cpu=0x%08X rxring->desc_dma=0x%08X\n",
+		(u32) rxring->desc_cpu,
+		(u32) rxring->desc_dma);
+
+	/* Set cur_index Point to Zero */
+	rxring->curr = 0;
+	rxd = rxring->desc_cpu;
+	for (i = 0; i < rxring->count; i++, rxd++) {
+		if (i == (rxring->count - 1)) {
+			/* End bit == 0; */
+			rxd->eor = 1;
+		}
+		skb = cns21xx_gec_alloc_skb();
+		if (!skb) {
+			dev_err(&gec->netdev->dev,
+				"%s allocation failed\n", "skb");
+			goto err_out;
+		}
+
+		/* Trans Packet from Virtual Memory to Physical Memory */
+		rxring->skbs[i]	= skb;
+		rxd->sdp = dma_map_single(gec->parent,
+						      skb->data,
+						      MAX_PACKET_LEN,
+						      DMA_TO_DEVICE);
+		rxd->length	= MAX_PACKET_LEN;
+	}
+
+	/* Clear TX Memory */
+	memset((void *)txring->desc_cpu, 0, cns21xx_gec_tx_dma_size(gec));
+	dev_dbg(&gec->netdev->dev,
+		"txring->desc_cpu=0x%08X txring->desc_dma=0x%08X\n",
+		(u32) txring->desc_cpu,
+		(u32) txring->desc_dma);
+
+	/* Set cur_index Point to Zero */
+	txring->curr = 0;
+	txd = txring->desc_cpu;
+	for (i = 0; i < txring->count; i++, txd++) {
+		if (i == (txring->count - 1)) {
+			/* End of Ring ==1 */
+			txd->eor = 1;
+		}
+		/* TX Ring , Cown == 1 */
+		txd->cown = 1;
+
+#ifdef CNS21XX_GEC_TX_HW_CHECKSUM
+		/* Enable Checksum */
+		txd->ico		= 1;
+		txd->uco		= 1;
+		txd->tco		= 1;
+#else
+		txd->ico		= 0;
+		txd->uco		= 0;
+		txd->tco		= 0;
+#endif
+		/* clear txring->skbs */
+		txring->skbs[i] 	= NULL;
+	}
+
+	return 0;
+
+err_out:
+	cns21xx_gec_buffer_free(gec);
+	return err;
+}
+
+static int cns21xx_gec_get_rfd_buff(struct cns21xx_gec *gec, int index)
+{
+	struct cns21xx_gec_ring *rxring = &gec->rxring;
+	struct cns21xx_gec_rxd *rxd;
+	struct sk_buff *skb;
+	unsigned char *data;
+	int len;
+
+	/* TODO: get rxdesc ptr */
+	rxd = ((struct cns21xx_gec_rxd *) rxring->desc_cpu) + index;
+	skb = rxring->skbs[index];
+
+	len = rxd->length;
+
+	dma_unmap_single(gec->parent, rxd->sdp, len,
+			 DMA_FROM_DEVICE);
+
+	data = skb_put(skb, len);
+
+	skb->dev = gec->netdev;
+
+#ifdef CNS21XX_GEC_RX_HW_CHECKSUM
+	if (rxd->ipf == 1 || rxd->l4f == 1) {
+		if (rxd->prot != 0x11) {
+			skb->ip_summed = CHECKSUM_NONE;
+		} else {
+			/* CheckSum Fail */
+			skb->dev->stats.rx_errors++;
+			goto freepacket;
+		}
+	} else {
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+#else
+	skb->ip_summed = CHECKSUM_NONE;
+#endif
+
+	/* this line must, if no, packet will not send to network layer */
+	skb->protocol = eth_type_trans(skb, skb->dev);
+
+	skb->dev->stats.rx_packets++;
+	skb->dev->stats.rx_bytes += len;
+	skb->dev->last_rx = jiffies;
+
+	/* if netif_rx any package, will let this driver core dump. */
+	netif_receive_skb(skb);
+
+	return 0;
+
+freepacket:
+	dev_kfree_skb_any(skb);
+	return 0;
+}
+
+static void cns21xx_gec_receive_packet(struct cns21xx_gec *gec,
+				    int mode, int *work_done, int work_to_do)
+{
+	struct cns21xx_gec_ring *rxring = &gec->rxring;
+	int rxsd_index;
+	u32 rxsd_current;
+	struct cns21xx_gec_rxd *rxd;
+	struct sk_buff *skb;
+	int i, rxcount = 0;
+
+	rxd = ((struct cns21xx_gec_rxd *) rxring->desc_cpu) + rxring->curr;
+	rxsd_current = cns21xx_gec_rr(gec, GEC_REG_RX_DPTR);
+	rxsd_index = (rxsd_current - (u32)rxring->desc_dma) >> 4;
+
+	if (rxsd_index > rxring->curr) {
+		rxcount = rxsd_index - rxring->curr;
+	} else if (rxsd_index < rxring->curr) {
+		rxcount = (rxring->count - rxring->curr) +
+			  rxsd_index;
+	} else {
+		if (rxd->cown == 0) {
+			goto receive_packet_exit;
+		} else {
+			/* Queue Full */
+			rxcount = rxring->count;
+		}
+	}
+
+	for (i = 0; i < rxcount; i++) {
+		if (*work_done >= work_to_do)
+			break;
+
+		++(*work_done);
+
+		if (rxd->cown != 0) {
+			/* Alloc New skb_buff */
+			skb = cns21xx_gec_alloc_skb();
+
+			/* Check skb_buff */
+			if (skb != NULL) {
+				cns21xx_gec_get_rfd_buff(gec, rxring->curr);
+				rxring->skbs[rxring->curr] = skb;
+				rxd->sdp =
+					dma_map_single(gec->parent,
+						       skb->data,
+						       MAX_PACKET_LEN,
+						       DMA_TO_DEVICE);
+				rxd->length = MAX_PACKET_LEN;
+
+				/* set cbit to 0 for CPU Transfer */
+				rxd->cown = 0;
+			} else {
+				/*
+				 * TODO: I will add dev->lp.stats->rx_dropped,
+				 * it will effect the performance
+				 */
+				dev_warn(&gec->netdev->dev,
+					 "skb allocation failed, reuse the buffer\n");
+
+				/* set cbit to 0 for CPU Transfer */
+				rxd->cown = 0;
+				return;
+			}
+		} else {
+#if 0
+			dev_err(&gec->netdev->dev, "encounter COWN == 0 BUG\n");
+#endif
+		}
+
+		if (rxring->curr == (rxring->count - 1)) {
+			rxring->curr = 0;
+			rxd = rxring->desc_cpu;
+		} else {
+			rxring->curr++;
+			rxd++;
+		}
+	}
+
+ receive_packet_exit:
+	return;
+}
+
+static int cns21xx_gec_poll(struct napi_struct *napi, int budget)
+{
+	struct cns21xx_gec *gec;
+	int work_done = 0;
+	int work_to_do = budget;
+
+	gec = container_of(napi, struct cns21xx_gec, napi);
+	cns21xx_gec_receive_packet(gec, 0, &work_done, work_to_do);
+
+	budget -= work_done;
+
+	/* if no Tx and not enough Rx work done, exit the polling mode */
+	if (work_done) {
+		if (test_bit(0, &gec->rx_queue_full) == 1) {
+			/* queue full */
+			clear_bit(0, &gec->rx_queue_full);
+			/* start Rx DMA */
+			cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 1);
+			return 1;
+		}
+	} else {
+		napi_complete(&gec->napi);
+#ifdef CONFIG_STAR_NIC_NAPI_MASK_IRQ
+		enable_irq(gec->rxrc_irq);
+#endif
+		return 0;
+	}
+
+	return work_done;
+}
+
+static int cns21xx_gec_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct cns21xx_gec *gec = netdev_priv(dev);
+	struct cns21xx_gec_txd *txd;
+	struct cns21xx_gec_ring *txring = &gec->txring;
+	struct sk_buff *skb_free = NULL;
+	unsigned long flags;
+	unsigned int len;
+
+	if (skb_padto(skb, ETH_ZLEN))
+		return NETDEV_TX_OK;
+
+	len = max_t(unsigned int, skb->len, ETH_ZLEN);
+
+	spin_lock_irqsave(&gec->tx_lock, flags);
+
+	txd = ((struct cns21xx_gec_txd *) txring->desc_cpu) + txring->curr;
+	if (txd->cown == 0) {
+		/* This TFD is busy */
+		spin_unlock_irqrestore(&gec->tx_lock, flags);
+		/* re-queue the skb */
+		return NETDEV_TX_BUSY;
+	}
+
+	if (txd->sdp != 0) {
+		/* MUST TODO: Free skbuff */
+		skb_free = txring->skbs[txring->curr];
+
+		dma_unmap_single(gec->parent,
+				 txd->sdp,
+				 txd->length,
+				 DMA_TO_DEVICE);
+		txring->dirty = txring->curr + 1;
+		if (txring->dirty == txring->count)
+			txring->dirty = 0;
+	}
+
+#ifdef CNS21XX_GEC_TX_HW_CHECKSUM
+	if (skb->protocol == __constant_htons(ETH_P_IP)) {
+		if (ip_hdr(skb)->protocol == IPPROTO_UDP) {
+			txd->uco = 1;
+			txd->tco = 0;
+		} else if (ip_hdr(skb)->protocol == IPPROTO_TCP) {
+			txd->uco = 0;
+			txd->tco = 1;
+		} else {
+			txd->uco = 0;
+			txd->tco = 0;
+		}
+	} else {
+		txd->ico = 0;
+		txd->uco = 0;
+		txd->tco = 0;
+	}
+#endif /* CNS21XX_GEC_TX_HW_CHECKSUM */
+
+	txring->skbs[txring->curr] = skb;
+
+	txd->length = len;
+	txd->sdp = dma_map_single(gec->parent, skb->data, len,
+					      DMA_TO_DEVICE);
+
+	txd->fs = 1;
+	txd->ls = 1;
+
+	/* Wake interrupt */
+	txd->intr = 0;
+	txd->cown = 0;
+
+	mb();
+
+	/* Start Tx DMA */
+	cns21xx_gec_wr(gec, GEC_REG_TX_DMA_CTRL, 1);
+
+	dev->stats.tx_packets++;
+	dev->stats.tx_bytes += skb->len;
+	dev->trans_start = jiffies;
+
+	txring->curr++;
+	if (txring->curr == txring->count)
+		txring->curr = 0;
+
+	spin_unlock_irqrestore(&gec->tx_lock, flags);
+
+	if (skb_free)
+		dev_kfree_skb(skb_free);
+
+	cns21xx_gec_timer_modify(gec, 10);
+
+	return NETDEV_TX_OK;
+}
+
+static void cns21xx_gec_set_mac_addr(struct cns21xx_gec *gec,
+				  const char *mac_addr)
+{
+	cns21xx_gec_wr(gec, GEC_REG_MY_MAC_H,
+		    (mac_addr[0] << 8) | mac_addr[1]);
+
+	cns21xx_gec_wr(gec, GEC_REG_MY_MAC_L,
+		    (mac_addr[2] << 24) | (mac_addr[3] << 16) |
+		    (mac_addr[4] << 8) |  mac_addr[5]);
+
+	dev_dbg(&gec->netdev->dev, "MAC address: %pM", mac_addr);
+}
+
+static int cns21xx_gec_set_lan_mac_addr(struct net_device *dev, void *addr)
+{
+	struct sockaddr *sock_addr = addr;
+	struct cns21xx_gec *gec = netdev_priv(dev);
+
+	spin_lock_irq(&gec->lock);
+	memcpy(dev->dev_addr, sock_addr->sa_data, 6);
+	cns21xx_gec_set_mac_addr(gec, sock_addr->sa_data);
+	spin_unlock_irq(&gec->lock);
+
+	return 0;
+}
+
+static int __init cns21xx_gec_setup(struct cns21xx_gec *gec)
+{
+	int err;
+
+	/* set high */
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15);
+	/* set low */
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG &= ~(0x1 << 15);
+	/* set high */
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15);
+	/* set NIC clock to 67.5MHz */
+	PWRMGT_SYSTEM_CLOCK_CONTROL_REG |= (0x1 << 7);
+
+	/* enable NIC clock */
+	HAL_PWRMGT_ENABLE_NIC_CLOCK();
+#if 0
+	cns21xx_gec_wr(gec, GEC_REG_MAC_CFG, 0x00527C00);
+#endif
+	udelay(100);
+
+	/* Configure GPIO for NIC MDC/MDIO pins */
+	HAL_MISC_ENABLE_MDC_MDIO_PINS();
+	HAL_MISC_ENABLE_NIC_COL_PINS();
+
+#if 0
+	MISC_GPIOA_PIN_ENABLE_REG |= (0x7 << 22);
+	MISC_FAST_ETHERNET_PHY_CONFIG_REG |=  (FE_PHY_LED_MODE >> 12) & 0x3;
+
+	/* set high */
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15);
+	/* set low */
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG &= ~(0x1 << 15);
+	/* set high */
+	PWRMGT_SOFTWARE_RESET_CONTROL_REG |= (0x1 << 15);
+#endif
+
+	/* disable all interrupt status sources */
+	cns21xx_gec_wr(gec, GEC_REG_INT_MASK, ~(0));
+	/* clear pending interrupts */
+	cns21xx_gec_wr(gec, GEC_REG_INT, ~(0));
+
+	/* stop Rx and Tx DMA */
+	cns21xx_gec_wr(gec, GEC_REG_TX_DMA_CTRL, 0);
+	cns21xx_gec_wr(gec, GEC_REG_RX_DMA_CTRL, 0);
+
+	gec->txring.count = CNS21XX_GEC_NUM_TXDS;
+	gec->rxring.count = CNS21XX_GEC_NUM_RXDS;
+	err = cns21xx_gec_buffer_alloc(gec);
+	if (err)
+		return err;
+
+	cns21xx_gec_mac_config(gec);
+	cns21xx_gec_fc_config(gec);
+
+	err = cns21xx_gec_phy_config(gec);
+	if (err) {
+		cns21xx_gec_buffer_free(gec);
+		return err;
+	}
+
+	cns21xx_gec_vlan_config(gec);
+	cns21xx_gec_arl_config(gec);
+	cns21xx_gec_set_mac_addr(gec, gec->netdev->dev_addr);
+	cns21xx_gec_mib_reset(gec);
+
+	MISC_DEBUG_PROBE_SELECTION_REG = 0x00000125;    /* 0x00000105 pb0_nic */
+
+	cns21xx_gec_wr(gec, GEC_REG_TX_DPTR, gec->txring.desc_dma);
+	cns21xx_gec_wr(gec, GEC_REG_TX_BASE_ADDR, gec->txring.desc_dma);
+	cns21xx_gec_wr(gec, GEC_REG_RX_DPTR, gec->rxring.desc_dma);
+	cns21xx_gec_wr(gec, GEC_REG_RX_BASE_ADDR, gec->rxring.desc_dma);
+
+	cns21xx_gec_dma_config(gec);
+	internal_phy_init_timer(gec);
+	cns21xx_gec_timer_init(gec);
+
+	return 0;
+}
+
+static const struct net_device_ops cns21xx_gec_netdev_ops = {
+	.ndo_open		= cns21xx_gec_lan_open,
+	.ndo_stop		= cns21xx_gec_close,
+	.ndo_start_xmit		= cns21xx_gec_send_packet,
+	.ndo_set_mac_address	= cns21xx_gec_set_lan_mac_addr,
+	.ndo_tx_timeout		= cns21xx_gec_timeout,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_change_mtu		= eth_change_mtu,
+};
+
+static int __init cns21xx_gec_probe(struct platform_device *pdev)
+{
+	struct net_device *netdev;
+	struct cns21xx_gec *gec;
+	struct cns21xx_gec_plat_data *pdata;
+	int err;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_dbg(&pdev->dev, "no platform data\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	if (!pdata->mac_addr) {
+		dev_dbg(&pdev->dev, "no mac address\n");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	netdev = alloc_etherdev(sizeof(struct cns21xx_gec));
+	if (!netdev) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+
+	gec = netdev_priv(netdev);
+	gec->pdata = pdata;
+	gec->parent = &pdev->dev;
+
+	gec->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!gec->mem_res) {
+		dev_dbg(&pdev->dev, "no iomem resource\n");
+		err = -EINVAL;
+		goto err_free_netdev;
+	}
+
+	gec->status_irq = platform_get_irq_byname(pdev,
+						   CNS21XX_GEC_STATUS_IRQ_NAME);
+	if (gec->status_irq < 0) {
+		dev_dbg(&pdev->dev, "%s irq not specified\n",
+			CNS21XX_GEC_STATUS_IRQ_NAME);
+		err = -EINVAL;
+		goto err_free_netdev;
+	}
+
+	gec->rxrc_irq = platform_get_irq_byname(pdev,
+						CNS21XX_GEC_RXRC_IRQ_NAME);
+	if (gec->rxrc_irq < 0) {
+		dev_dbg(&pdev->dev, "%s irq not specified\n",
+			CNS21XX_GEC_RXRC_IRQ_NAME);
+		err = -EINVAL;
+		goto err_free_netdev;
+	}
+
+	gec->rxqf_irq = platform_get_irq_byname(pdev,
+						CNS21XX_GEC_RXQF_IRQ_NAME);
+	if (gec->rxqf_irq < 0) {
+		dev_dbg(&pdev->dev, "%s irq not specified\n",
+			CNS21XX_GEC_RXQF_IRQ_NAME);
+		err = -EINVAL;
+		goto err_free_netdev;
+	}
+
+	gec->txtc_irq = platform_get_irq_byname(pdev,
+						CNS21XX_GEC_TXTC_IRQ_NAME);
+	if (gec->txtc_irq < 0) {
+		dev_dbg(&pdev->dev, "%s irq not specified\n",
+			CNS21XX_GEC_TXTC_IRQ_NAME);
+		err = -EINVAL;
+		goto err_free_netdev;
+	}
+
+	gec->txqe_irq = platform_get_irq_byname(pdev,
+						CNS21XX_GEC_TXQE_IRQ_NAME);
+	if (gec->txqe_irq < 0) {
+		dev_dbg(&pdev->dev, "%s irq not specified\n",
+			CNS21XX_GEC_TXQE_IRQ_NAME);
+		err = -EINVAL;
+		goto err_free_netdev;
+	}
+
+	if (!request_mem_region(gec->mem_res->start,
+				resource_size(gec->mem_res), pdev->name)) {
+		dev_err(&pdev->dev, "unable to request mem region\n");
+		err = -EBUSY;
+		goto err_free_netdev;
+	}
+
+	gec->base = ioremap(gec->mem_res->start, resource_size(gec->mem_res));
+	if (!gec->base) {
+		dev_err(&pdev->dev, "ioremap failed \n");
+		err = -ENXIO;
+		goto err_release_mem;
+	}
+
+	platform_set_drvdata(pdev, netdev);
+
+	spin_lock_init(&gec->lock);
+	spin_lock_init(&gec->tx_lock);
+
+	netdev->base_addr = gec->mem_res->start;
+	netdev->netdev_ops = &cns21xx_gec_netdev_ops;
+#if defined(CNS21XX_GEC_TX_HW_CHECKSUM)
+	netdev->features = NETIF_F_IP_CSUM;
+#endif
+	memcpy(netdev->dev_addr, pdata->mac_addr, 6);
+	netif_napi_add(netdev, &gec->napi, cns21xx_gec_poll, 64);
+
+	gec->netdev = netdev;
+	err = register_netdev(netdev);
+	if (err)
+		goto err_unmap;
+
+	err = cns21xx_gec_setup(gec);
+	if (err)
+		goto err_unregister_netdev;
+
+	return 0;
+
+ err_unregister_netdev:
+	unregister_netdev(netdev);
+ err_unmap:
+	platform_set_drvdata(pdev, NULL);
+	iounmap(gec->base);
+ err_release_mem:
+	release_mem_region(gec->mem_res->start, resource_size(gec->mem_res));
+ err_free_netdev:
+	free_netdev(netdev);
+ err_out:
+	return err;
+}
+
+static int __devexit cns21xx_gec_remove(struct platform_device *pdev)
+{
+	struct net_device *netdev = platform_get_drvdata(pdev);
+	struct cns21xx_gec *gec = netdev_priv(netdev);
+
+	unregister_netdev(netdev);
+	platform_set_drvdata(pdev, NULL);
+	iounmap(gec->base);
+	release_mem_region(gec->mem_res->start, resource_size(gec->mem_res));
+	free_netdev(netdev);
+
+	return 0;
+}
+
+static struct platform_driver cns21xx_gec_driver = {
+	.remove	= __devexit_p(cns21xx_gec_remove),
+	.driver = {
+		.name	= "cns21xx-gec",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init cns21xx_gec_init(void)
+{
+	return platform_driver_probe(&cns21xx_gec_driver, cns21xx_gec_probe);
+}
+
+static void __exit cns21xx_gec_exit(void)
+{
+	platform_driver_unregister(&cns21xx_gec_driver);
+}
+
+module_init(cns21xx_gec_init);
+module_exit(cns21xx_gec_exit);
+
+#define INTERNAL_PHY_PATCH_CHECKCNT	16
+#define INTERNAL_PHY_PATCH_CHECK_PERIOD	1000 /* ms */
+
+static void (*phy_statemachine)(struct cns21xx_gec*, int, int, int);
+
+#define ETH3220_PHY_MON_PERIOD INTERNAL_PHY_PATCH_CHECK_PERIOD
+
+/*  phy monitor state  */
+#define NUM_PHY 1
+#define PHY_STATE_INIT				0
+#define LINK_DOWN_POSITIVE			1
+#define WAIT_LINK_UP_POSITIVE			2
+#define LINK_UP_POSITIVE			3
+#define WAIT_BYPASS_LINK_UP_POSITIVE		4
+#define BYPASS_AND_LINK_UP_POSITIVE		5
+#define LINK_UP_8101_POSITIVE			6
+#define WAIT_8101_LINK_UP_POSITIVE		7
+
+#define PHY_STATE_LAST				(WAIT_8101_LINK_UP_POSITIVE+1)
+
+/*  time setting  */
+#define WAIT_BYPASS_LINK_UP_POSITIVE_TIMEOUT	5000	/*  5000 ms  */
+#define WAIT_BYPASS_LINK_UP_NEGATIVE_TIMEOUT	5000	/*  5000 ms  */
+#define LINK_DOWN_ABILITY_DETECT_TIMEOUT	5000	/*  5000 ms  */
+#define DETECT_8101_PERIOD			7000	/*  7000 ms  */
+#define WAIT_8101_LINK_UP_TIMEOUT		3000	/*  3000 ms  */
+
+#define MAX_PHY_PORT				1
+#define DEFAULT_AGC_TRAIN			16
+#define MAX_AGC_TRAIN				16	/* train 16 times */
+static int agc_train_num = DEFAULT_AGC_TRAIN;
+u32 port_displaybuf[NUM_PHY][MAX_AGC_TRAIN + 1] = {
+	{0}
+};
+
+static int cuv[3][3] = {
+	{1, 1, 4},
+	{1, 1, 0},
+	{1, 1, -4}
+};
+
+static u32 link_status_old;
+
+struct eth3220_phy {
+	u16 state;
+	u16 linkdown_cnt;
+	u32 state_time;
+	u32 timer;
+};
+
+#define DEBUG_PHY_STATE_TRANSITION			1
+#if DEBUG_PHY_STATE_TRANSITION
+/*
+ * show state transition of debug phy port.
+ *  -1 for all ports
+ *  -2 for disable all ports
+ *  0 - 4 for each port
+ */
+static int debug_phy_port = -2;
+static char *phystate_name[] = {
+	"init",			/*  PHY_STATE_INIT  */
+	"ldp",			/*  LINK_DOWN_POSITIVE  */
+	"wait_lup",		/*  WAIT_LINK_UP_POSITIVE  */
+	"lup",			/*  LINK_UP_POSITIVE  */
+	"wait_bp_lup",		/*  WAIT_BYPASS_LINK_UP_POSITIVE  */
+	"bp_lup",		/*  BYPASS_AND_LINK_UP_POSITIVE  */
+	"8101_lup",		/*  LINK_UP_8101_POSITIVE  */
+	"wait_8101_lup",	/*  WAIT_8101_LINK_UP_POSITIVE  */
+	"err",
+};
+#endif  /*  DEBUG_PHY_STATE_TRANSITION  */
+
+static struct eth3220_phy phy[5] = {
+	{PHY_STATE_INIT, 0, 0, 0},
+	{PHY_STATE_INIT, 0, 0, 0},
+	{PHY_STATE_INIT, 0, 0, 0},
+	{PHY_STATE_INIT, 0, 0, 0},
+	{PHY_STATE_INIT, 0, 0, 0}
+};
+
+static u16 long_cable_global_reg[32] = {
+	0x0000, 0x19a0, 0x1d00, 0x0e80,
+	0x0f60, 0x07c0, 0x07e0, 0x03e0,
+	0x0000, 0x0000, 0x0000, 0x2000,
+	0x8250, 0x1700, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x204b, 0x01c2, 0x0000,
+	0x0000, 0x0000, 0x0fff, 0x4100,
+	0x9319, 0x0021, 0x0034, 0x270a | FE_PHY_LED_MODE
+};
+
+static u16 long_cable_local_reg[32] = {
+	0x3100, 0x786d, 0x01c1, 0xca51,
+	0x05e1, 0x45e1, 0x0003, 0x001c,
+	0x2000, 0x9828, 0xf3c4, 0x400c,
+	0xf8ff, 0x6940, 0xb906, 0x503c,
+	0x8000, 0x297a, 0x1010, 0x5010,
+	0x6ae1, 0x7c73, 0x783c, 0xfbdf,
+	0x2080, 0x3244, 0x1301, 0x1a80,
+	0x8e8f, 0x8000, 0x9c29, 0xa70a | FE_PHY_LED_MODE
+};
+
+/*=============================================================*
+ *  eth3220ac_rt8101_phy_setting
+ *=============================================================*/
+static  void eth3220ac_rt8101_phy_setting(struct cns21xx_gec *gec, int port)
+{
+	cns21xx_gec_write_phy(gec, port, 12, 0x18ff);
+	cns21xx_gec_write_phy(gec, port, 18, 0x6400);
+}
+
+static void eth3220ac_release_bpf(struct cns21xx_gec *gec, int port)
+{
+	cns21xx_gec_write_phy(gec, port, 18, 0x6210);
+}
+
+static  void eth3220ac_def_bpf(struct cns21xx_gec *gec, int port)
+{
+	cns21xx_gec_write_phy(gec, port, 18, 0x6bff);
+}
+
+static  void eth3220ac_def_linkdown_setting(struct cns21xx_gec *gec, int port)
+{
+	cns21xx_gec_write_phy(gec, port, 13, 0xe901);
+	cns21xx_gec_write_phy(gec, port, 14, 0xa3c6);
+}
+
+static  void eth3220ac_def_linkup_setting(struct cns21xx_gec *gec, int port)
+{
+	cns21xx_gec_write_phy(gec, port, 13, 0x6901);
+	cns21xx_gec_write_phy(gec, port, 14, 0xa286);
+}
+
+/*=============================================================*
+ *  eth3220ac_link_agc:
+ *=============================================================*/
+static int eth3220ac_link_agc(struct cns21xx_gec *gec, int port, int speed)
+{
+	u16 reg;
+	u32 agc_data = 0;
+	u32 short_cable;
+	int i, jj;
+
+	/* if speed = 100MHz, then continue */
+	if (speed == 0)
+		return 0;
+
+	short_cable = 0;
+	jj = 0;
+	for (i = 0; i < agc_train_num; i++) {
+		cns21xx_gec_read_phy(gec, port, 15, &reg);
+		reg &= 0x7f;
+		if (reg <= 0x12) {
+			short_cable = 1;
+			jj++;
+			agc_data += (u32)reg;
+		}
+	}
+
+	if (short_cable)
+		agc_data = (agc_data / jj) + 4;
+	else
+		agc_data = (cuv[2][0] * agc_data) / cuv[2][1] /
+			   agc_train_num - 4;
+
+	/*  Fix AGC  */
+	agc_data = 0xd0 | (agc_data << 9);
+	cns21xx_gec_write_phy(gec, port, 15, agc_data);
+	udelay(1000);
+	cns21xx_gec_read_phy(gec, port, 15, &reg);
+	reg &= ~(0x1 << 7);
+	cns21xx_gec_write_phy(gec, port, 15, reg);
+
+	return 0;
+}
+
+/*=============================================================*
+ *  eth3220ac_unlink_agc:
+ *=============================================================*/
+static void eth3220ac_unlink_agc(struct cns21xx_gec *gec, int port)
+{
+	/* start AGC adaptive */
+	cns21xx_gec_write_phy(gec, port, 15, 0xa050);
+}
+
+/*=============================================================*
+ *  eth3220ac_rt8100_check
+ *=============================================================*/
+static int eth3220ac_rt8100_check(struct cns21xx_gec *gec, int port)
+{
+	u16 reg, reg2;
+
+	/* Read reg27 (error register) */
+	cns21xx_gec_read_phy(gec, port, 27, &reg);
+	/* if error exists, set Bypass Filter enable */
+	if ((reg & 0xfffc)) {
+		cns21xx_gec_read_phy(gec, port, 15, &reg);
+		cns21xx_gec_read_phy(gec, port, 27, &reg2);
+		if ((reg2 & 0xfffc) && (((reg >> 9) & 0xff) < 0x1c)) {
+			dev_err(&gec->netdev->dev, "8100 pos err\n");
+
+			/* Bypass agcgain disable */
+			cns21xx_gec_write_phy(gec, port, 15, (reg & (~(0x1 << 7))));
+
+			/* repeat counts when reaching threshold error */
+			cns21xx_gec_write_phy(gec, port, 13, 0x4940);
+
+			/*
+			 * Speed up AN speed and compensate threshold
+			 * phase error
+			 */
+			cns21xx_gec_write_phy(gec, port, 14, 0xa306);
+
+			/* Bypass Filter enable */
+			cns21xx_gec_read_phy(gec, port, 18, &reg2);
+
+			cns21xx_gec_write_phy(gec, port, 18, (reg | 0x400));
+
+			/* restart AN */
+			cns21xx_gec_write_phy(gec, port, 0, 0x3300);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+
+/*=============================================================*
+ *  eth3220ac_rt8100_linkdown
+ *=============================================================*/
+static void eth3220ac_rt8100_linkdown(struct cns21xx_gec *gec, int port)
+{
+	u16 reg;
+
+	/* Bypass Filter disable */
+	cns21xx_gec_read_phy(gec, port, 18, &reg);
+	cns21xx_gec_write_phy(gec, port, 18, (reg & (~(0x1 << 10))));
+	eth3220ac_def_linkdown_setting(gec, port);
+}
+
+static void eth3220ac_normal_phy_setting(struct cns21xx_gec *gec, int port)
+{
+	cns21xx_gec_write_phy(gec, port, 12, 0xd8ff);
+	eth3220ac_def_bpf(gec, port);
+}
+
+/*=============================================================*
+ *  wp3220ac_phystate
+ *=============================================================*/
+static void wp3220ac_phystate(struct cns21xx_gec *gec, int port, int link, int speed)
+{
+	int next_state;
+	u16 reg, reg2;
+
+	phy[port].timer += ETH3220_PHY_MON_PERIOD;
+
+	if (link) {
+		/*  Link up state  */
+		switch (phy[port].state) {
+		case LINK_UP_POSITIVE:
+			next_state = eth3220ac_rt8100_check(gec, port) ?
+				WAIT_BYPASS_LINK_UP_POSITIVE :
+				LINK_UP_POSITIVE;
+			break;
+
+		case PHY_STATE_INIT:
+		case WAIT_LINK_UP_POSITIVE:
+		case LINK_DOWN_POSITIVE:
+			next_state = LINK_UP_POSITIVE;
+			eth3220ac_def_linkup_setting(gec, port);
+			eth3220ac_link_agc(gec, port, speed);
+			eth3220ac_release_bpf(gec, port);
+			break;
+
+		case WAIT_BYPASS_LINK_UP_POSITIVE:
+		case BYPASS_AND_LINK_UP_POSITIVE:
+			next_state = BYPASS_AND_LINK_UP_POSITIVE;
+			break;
+
+		case WAIT_8101_LINK_UP_POSITIVE:
+			next_state = LINK_UP_8101_POSITIVE;
+			eth3220ac_link_agc(gec, port, speed);
+			cns21xx_gec_write_phy(gec, port, 12, 0x98ff);
+			break;
+
+		case LINK_UP_8101_POSITIVE:
+			next_state = LINK_UP_8101_POSITIVE;
+			break;
+
+		default:
+			next_state = LINK_UP_POSITIVE;
+			eth3220ac_def_linkup_setting(gec, port);
+			eth3220ac_link_agc(gec, port, speed);
+		}
+	} else {
+		/*  Link down state  */
+		switch (phy[port].state) {
+		case LINK_DOWN_POSITIVE:
+			cns21xx_gec_read_phy(gec, port, 5, &reg);
+			cns21xx_gec_read_phy(gec, port, 28, &reg2);
+
+			/* AN Link Partner Ability Register or NLP */
+			if (reg || (reg2 & 0x100))
+				next_state = WAIT_LINK_UP_POSITIVE;
+			else
+				next_state = LINK_DOWN_POSITIVE;
+			break;
+
+		case WAIT_LINK_UP_POSITIVE:
+			if (phy[port].state_time >
+					LINK_DOWN_ABILITY_DETECT_TIMEOUT)
+				next_state = LINK_DOWN_POSITIVE;
+			else
+				next_state = WAIT_LINK_UP_POSITIVE;
+			break;
+
+		case WAIT_BYPASS_LINK_UP_POSITIVE:
+			/* set timeout = 5 sec */
+			if (phy[port].state_time >
+					WAIT_BYPASS_LINK_UP_POSITIVE_TIMEOUT) {
+				next_state = LINK_DOWN_POSITIVE;
+
+				/* Bypass Filter disable */
+				eth3220ac_rt8100_linkdown(gec, port);
+				eth3220ac_def_bpf(gec, port);
+			} else {
+				next_state = WAIT_BYPASS_LINK_UP_POSITIVE;
+			}
+			break;
+
+		case BYPASS_AND_LINK_UP_POSITIVE:
+			next_state = LINK_DOWN_POSITIVE;
+			eth3220ac_rt8100_linkdown(gec, port);
+			eth3220ac_def_bpf(gec, port);
+			break;
+
+		case WAIT_8101_LINK_UP_POSITIVE:
+			if (phy[port].state_time > WAIT_8101_LINK_UP_TIMEOUT) {
+				next_state = LINK_DOWN_POSITIVE;
+				eth3220ac_normal_phy_setting(gec, port);
+				eth3220ac_def_linkdown_setting(gec, port);
+			} else {
+				next_state = WAIT_8101_LINK_UP_POSITIVE;
+			}
+			break;
+
+		case LINK_UP_POSITIVE:
+			eth3220ac_unlink_agc(gec, port);
+			eth3220ac_def_linkdown_setting(gec, port);
+			eth3220ac_def_bpf(gec, port);
+			if (phy[port].timer > DETECT_8101_PERIOD) {
+				next_state = LINK_DOWN_POSITIVE;
+				phy[port].timer = 0;
+				phy[port].linkdown_cnt = 1;
+			} else {
+				if (++phy[port].linkdown_cnt > 2) {
+					next_state = WAIT_8101_LINK_UP_POSITIVE;
+					eth3220ac_rt8101_phy_setting(gec, port);
+				} else {
+					next_state = LINK_DOWN_POSITIVE;
+				}
+			}
+			break;
+
+		case LINK_UP_8101_POSITIVE:
+			eth3220ac_normal_phy_setting(gec, port);
+			/*  fall down to phy normal state  */
+		case PHY_STATE_INIT:
+			eth3220ac_def_linkdown_setting(gec, port);
+			eth3220ac_unlink_agc(gec, port);
+		default:
+			next_state = LINK_DOWN_POSITIVE;
+		}
+	}
+
+	if (phy[port].state != next_state) {
+		phy[port].state_time = 0;
+#if DEBUG_PHY_STATE_TRANSITION
+		if (debug_phy_port == -1 || port == debug_phy_port) {
+			if ((phy[port].state < PHY_STATE_LAST) &&
+			    (next_state < PHY_STATE_LAST))
+				dev_dbg(&gec->netdev->dev,
+					"p%d: %s->%s, %d, %d\n",
+					port, phystate_name[phy[port].state],
+					phystate_name[next_state],
+					phy[port].timer,
+					phy[port].linkdown_cnt);
+			else
+				dev_dbg(&gec->netdev->dev,
+					"p%d: %d->%d\n",
+					port, phy[port].state, next_state);
+		}
+#endif   /*  DEBUG_PHY_STATE_TRANSITION  */
+	} else {
+		phy[port].state_time += ETH3220_PHY_MON_PERIOD;
+	}
+	phy[port].state = next_state;
+}
+
+/*=============================================================*
+ *  eth3220_phyinit:
+ *=============================================================*/
+static void eth3220ac_10m_agc(struct cns21xx_gec *gec)
+{
+	/* Force 10M AGC = 2c globally */
+	cns21xx_gec_write_phy(gec, 0, 31, 0x2f1a);
+	cns21xx_gec_write_phy(gec, 0, 12, 0x112c);
+	cns21xx_gec_write_phy(gec, 0, 13, 0x2e21);
+	cns21xx_gec_write_phy(gec, 0, 31, 0xaf1a);
+}
+
+static void eth3220ac_dfe_init(struct cns21xx_gec *gec)
+{
+	int i;
+
+	cns21xx_gec_write_phy(gec, 0, 31, 0x2f1a);
+	for (i = 0; i <= 7; i++)
+		cns21xx_gec_write_phy(gec, 0, i, 0);
+	cns21xx_gec_write_phy(gec, 0, 11, 0x0b50);
+	cns21xx_gec_write_phy(gec, 0, 31, 0xaf1a);
+}
+
+static void eth3220ac_phy_cdr_training_init(struct cns21xx_gec *gec)
+{
+	int i;
+
+	/* Force all port in 10M FD mode */
+	for (i = 0; i < NUM_PHY; i++)
+		cns21xx_gec_write_phy(gec, i, 0, 0x100);
+
+	/* Global setting */
+	cns21xx_gec_write_phy(gec, 0, 31, 0x2f1a);
+	cns21xx_gec_write_phy(gec, 0, 29, 0x5021);
+	udelay(2000); 		/* 2ms, wait > 1 ms */
+	cns21xx_gec_write_phy(gec, 0, 29, 0x4021);
+	udelay(2000);		/* 2ms, wait > 1 ms */
+	cns21xx_gec_write_phy(gec, 0, 31, 0xaf1a);
+
+	/* Enable phy AN */
+	for (i = 0; i < NUM_PHY; i++)
+		cns21xx_gec_write_phy(gec, i, 0, 0x3100);
+}
+
+static void eth3220_phyinit(struct cns21xx_gec *gec)
+{
+	eth3220ac_10m_agc(gec);
+	eth3220ac_dfe_init(gec);
+	eth3220ac_phy_cdr_training_init(gec);
+}
+
+static void eth3220_phycfg(struct cns21xx_gec *gec, int phyaddr)
+{
+	eth3220ac_def_linkdown_setting(gec, phyaddr);
+	eth3220ac_normal_phy_setting(gec, phyaddr);
+	cns21xx_gec_write_phy(gec, phyaddr, 9, 0x7f);
+}
+
+static void internal_phy_patch_check(struct cns21xx_gec *gec, int init)
+{
+	u32 short_cable_agc_detect_count;
+	u32 link_status = 0, link_speed;
+	u32 phy_addr = gec->phy_addr;
+	u16 phy_data;
+	u16 phy_data2;
+	int i;
+
+	cns21xx_gec_read_phy(gec, phy_addr, 1, &phy_data);
+	udelay(100);
+	cns21xx_gec_read_phy(gec, phy_addr, 1, &phy_data2);
+	if (((phy_data & 0x0004) != 0x0004) &&
+	    ((phy_data2 & 0x0004) != 0x0004)) {
+		/* link down */
+		short_cable_agc_detect_count = 0;
+		for (i = 0; i < INTERNAL_PHY_PATCH_CHECKCNT; i++) {
+			cns21xx_gec_read_phy(gec, phy_addr, 15, &phy_data);
+			udelay(1000);
+			if ((phy_data & 0x7F) <= 0x12) {
+				/* short cable */
+				short_cable_agc_detect_count++;
+				break;
+			}
+		}
+		if (short_cable_agc_detect_count) {
+			u32 mac_cfg;
+
+			/* short cable */
+			phy_statemachine = wp3220ac_phystate;
+			eth3220_phyinit(gec);
+			cns21xx_gec_read_phy(gec, phy_addr, 1, &phy_data);
+			if (phy_data & 0x0040)
+				link_status = 1;
+
+			mac_cfg = cns21xx_gec_rr(gec, GEC_REG_MAC_CFG);
+			if ((mac_cfg & 0xC) == 0x4) /* 100Mbps */
+				link_speed = 1;
+			else
+				link_speed = 0;
+
+			link_status_old = link_status;
+			for (i = 0; i < MAX_PHY_PORT; link_status >>= 1, i++)
+				eth3220_phycfg(gec, i);
+		} else {
+			/* long cable */
+			/* set to global domain */
+			cns21xx_gec_write_phy(gec, phy_addr, 31,
+					      0x2f1a);
+			for (i = 0; i < 32; i++)
+				cns21xx_gec_write_phy(gec, phy_addr, i,
+						      long_cable_global_reg[i]);
+
+			/* set to local domain */
+			cns21xx_gec_write_phy(gec, phy_addr, 31,
+					      0xaf1a);
+			for (i = 0; i < 32; i++)
+				cns21xx_gec_write_phy(gec, phy_addr, i,
+						      long_cable_local_reg[i]);
+		}
+	}
+}
+
+static void internal_phy_timer_func(unsigned long data)
+{
+	struct cns21xx_gec *gec = (struct cns21xx_gec *) data;
+
+	internal_phy_patch_check(gec, 0);
+	mod_timer(&gec->internal_phy_timer,
+		  jiffies + INTERNAL_PHY_PATCH_CHECK_PERIOD / 10);
+}
+
+static void internal_phy_init_timer(struct cns21xx_gec *gec)
+{
+	init_timer(&gec->internal_phy_timer);
+	gec->internal_phy_timer.function = internal_phy_timer_func;
+	gec->internal_phy_timer.data = (unsigned long) gec;
+}
+
+static void internal_phy_start_timer(struct cns21xx_gec *gec)
+{
+	dev_dbg(&gec->netdev->dev, "starting patch check.\n");
+
+	internal_phy_patch_check(gec, 1);
+	mod_timer(&gec->internal_phy_timer,
+		  jiffies + INTERNAL_PHY_PATCH_CHECK_PERIOD / 10);
+}
+
+static void internal_phy_stop_timer(struct cns21xx_gec *gec)
+{
+	dev_dbg(&gec->netdev->dev, "stopping patch check.\n");
+
+	del_timer_sync(&gec->internal_phy_timer);
+}
--- /dev/null
+++ b/drivers/net/ethernet/cns21xx/Kconfig
@@ -0,0 +1,7 @@
+config CNS21XX_GEC
+	tristate  "CNS21XX Gigabit Ethernet Controller support"
+	depends on ARCH_CNS21XX
+	help
+	  If you wish to compile a kernel for Cavium Networks CNS21XX
+	  with ethernet support, then you should always answer Y to this.
+
--- /dev/null
+++ b/drivers/net/ethernet/cns21xx/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Cavium Networks CNS21XX ethernet driver
+#
+
+cns21xx_gec-y	+= cns21xx_gec_main.o
+
+obj-$(CONFIG_CNS21XX_GEC) += cns21xx_gec.o
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -32,6 +32,7 @@ source "drivers/net/ethernet/calxeda/Kco
 source "drivers/net/ethernet/chelsio/Kconfig"
 source "drivers/net/ethernet/cirrus/Kconfig"
 source "drivers/net/ethernet/cisco/Kconfig"
+source "drivers/net/ethernet/cns21xx/Kconfig"
 source "drivers/net/ethernet/davicom/Kconfig"
 
 config DNET
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_NET_CALXEDA_XGMAC) += calxe
 obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/
 obj-$(CONFIG_NET_VENDOR_CIRRUS) += cirrus/
 obj-$(CONFIG_NET_VENDOR_CISCO) += cisco/
+obj-$(CONFIG_CNS21XX_GEC) += cns21xx/
 obj-$(CONFIG_DM9000) += davicom/
 obj-$(CONFIG_DNET) += dnet.o
 obj-$(CONFIG_NET_VENDOR_DEC) += dec/