All of lore.kernel.org
 help / color / mirror / Atom feed
From: Graham Gower <graham.gower@gmail.com>
To: linux-mips@linux-mips.org
Subject: [PATCH 3/3] net: add driver for JZ4730 ethernet controller.
Date: Thu, 25 Feb 2010 17:01:25 +1030	[thread overview]
Message-ID: <4B86193D.2010307@gmail.com> (raw)
In-Reply-To: <4B861890.6090002@gmail.com>


Signed-off-by: Graham Gower <graham.gower@gmail.com>
---
 drivers/net/Kconfig  |   10 +
 drivers/net/Makefile |    1 +
 drivers/net/jz_eth.c | 1232 ++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/jz_eth.h |  403 +++++++++++++++++
 4 files changed, 1646 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/jz_eth.c
 create mode 100644 drivers/net/jz_eth.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7f8ad5d..7e8060a 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1951,6 +1951,16 @@ config BCM63XX_ENET
 	  This driver supports the ethernet MACs in the Broadcom 63xx
 	  MIPS chipset family (BCM63XX).
 
+config JZ_ETH
+	tristate "JZ4730/JZ5730 On-Chip Ethernet support"
+	depends on XBURST_JZ4730
+	select MII
+	help
+	  This driver supports the JZ4730/JZ5730 On-Chip Ethernet interface.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called jz_eth.
+
 source "drivers/net/fs_enet/Kconfig"
 
 source "drivers/net/octeon/Kconfig"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 657ccc3..da8ff86 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o
 obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
 obj-$(CONFIG_AX88796) += ax88796.o
 obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_JZ_ETH) += jz_eth.o
 
 obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
diff --git a/drivers/net/jz_eth.c b/drivers/net/jz_eth.c
new file mode 100644
index 0000000..624c081
--- /dev/null
+++ b/drivers/net/jz_eth.c
@@ -0,0 +1,1232 @@
+/*
+ *  Jz4730/Jz5730 On-Chip ethernet driver.
+ *
+ *  Copyright (C) 2009, 2010 Ubiq Technologies <graham.gower@gmail.com>
+ *  Copyright (C) 2005 - 2007  Ingenic Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <xburst.h>
+
+#include "jz_eth.h"
+
+#define DRV_NAME	"jz_eth"
+#define DRV_VERSION	"1.2"
+
+MODULE_AUTHOR("Peter Wei <jlwei@ingenic.cn>");
+MODULE_DESCRIPTION("JzSOC On-chip Ethernet driver");
+MODULE_LICENSE("GPL");
+
+static void __iomem *jz_eth_base;
+static char *hwaddr = NULL;
+static int debug = -1;
+static struct mii_if_info mii_info;
+
+MODULE_PARM_DESC(debug, "i");
+MODULE_PARM_DESC(hwaddr,"s");
+
+static irqreturn_t jz_eth_interrupt(int irq, void *dev_id);
+
+static int link_check_thread (void *data);
+
+static inline void
+ring_inv(jz_desc_t ring[], int ring_index)
+{
+	unsigned long addr = (unsigned long)&ring[ring_index];
+	dma_cache_inv(addr, sizeof(jz_desc_t));
+}
+
+static inline void
+ring_wback(jz_desc_t ring[], int ring_index)
+{
+	unsigned long addr = (unsigned long)&ring[ring_index];
+	dma_cache_wback(addr, sizeof(jz_desc_t));
+}
+
+static inline unsigned char str2hexnum(unsigned char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	return 0; /* foo */
+}
+
+static inline void str2eaddr(unsigned char *ea, unsigned char *str)
+{
+	int i;
+
+	for (i = 0; i < 6; i++) {
+		unsigned char num;
+
+		if((*str == '.') || (*str == ':'))
+			str++;
+		num = str2hexnum(*str++) << 4;
+		num |= (str2hexnum(*str++));
+		ea[i] = num;
+	}
+}
+
+static int ethaddr_cmd = 0;
+static unsigned char ethaddr_hex[6];
+
+static int __init ethernet_addr_setup(char *str)
+{
+	if (!str) {
+	        printk("ethaddr not set in command line\n");
+		return -1;
+	}
+	ethaddr_cmd = 1;
+	str2eaddr(ethaddr_hex, str);
+
+	return 0;
+}
+
+__setup("ethaddr=", ethernet_addr_setup);
+
+static int get_mac_address(struct net_device *dev)
+{
+	int i;
+	unsigned char flag0=0;
+	unsigned char flag1=0xff;
+	
+	dev->dev_addr[0] = 0xff;
+	if (hwaddr != NULL) {
+		/* insmod jz-ethc.o hwaddr=00:ef:a3:c1:00:10 */
+		str2eaddr(dev->dev_addr, hwaddr);
+	} else if (ethaddr_cmd) {
+		/* linux command line: ethaddr=00:ef:a3:c1:00:10 */
+		for (i=0; i<6; i++)
+			dev->dev_addr[i] = ethaddr_hex[i];
+	}
+
+	/* check whether valid MAC address */
+	for (i=0; i<6; i++) {
+		flag0 |= dev->dev_addr[i];
+		flag1 &= dev->dev_addr[i];
+	}
+	if ((dev->dev_addr[0] & 0xC0) || (flag0 == 0) || (flag1 == 0xff)) {
+		printk("WARNING: There is not MAC address, use default ..\n");
+		dev->dev_addr[0] = 0x00;
+		dev->dev_addr[1] = 0xef;
+		dev->dev_addr[2] = 0xa3;
+		dev->dev_addr[3] = 0xc1;
+		dev->dev_addr[4] = 0x00;
+		dev->dev_addr[5] = 0x10;
+		dev->dev_addr[5] = 0x03;
+	}
+	memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
+	if (!is_valid_ether_addr(dev->perm_addr))
+		printk(KERN_ERR "%s: ethaddr is b0rken!\n", dev->name);
+	return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+static u32 jz_eth_curr_mode(struct net_device *dev);
+
+/*
+ * Ethernet START/STOP routines
+ */
+#define START_ETH() \
+	do { \
+		s32 val; \
+		val = readl(jz_eth_base + DMA_OMR); \
+		val |= OMR_ST | OMR_SR; \
+		writel(val, jz_eth_base + DMA_OMR); \
+	} while(0)
+
+#define STOP_ETH() \
+	do { \
+		s32 val; \
+		val = readl(jz_eth_base + DMA_OMR); \
+		val &= ~(OMR_ST|OMR_SR); \
+		writel(val, jz_eth_base + DMA_OMR); \
+	} while(0)
+
+/*
+ * Link check routines
+ */
+static void start_check(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+
+	np->thread_die = 0;
+	init_waitqueue_head(&np->thr_wait);
+	init_completion (&np->thr_exited);
+
+	np->thread = kthread_run(link_check_thread,(void *)dev,
+			"%s-linkcheck", dev->name);
+	if (IS_ERR(np->thread))
+		printk(KERN_ERR "%s: unable to start kernel thread\n", dev->name);
+}
+
+static int close_check(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	int ret = 0;
+
+	if (np->thread != NULL) {
+		np->thread_die = 1;
+		wmb();
+		ret = send_sig(SIGTERM, np->thread, 1);
+		if (ret) {
+			printk(KERN_ERR "%s: unable to signal thread\n", dev->name);
+			return 1;
+		}
+		wait_for_completion (&np->thr_exited);
+	}
+	return 0;
+}
+
+static int link_check_thread(void *data)
+{
+	struct net_device *dev=(struct net_device *)data;
+	struct jz_eth_private *np = netdev_priv(dev);
+	unsigned char current_link;
+	unsigned long timeout;
+
+	while (1) {
+		timeout = 3*HZ;
+		do {
+			timeout = interruptible_sleep_on_timeout (&np->thr_wait, timeout);
+			/* make swsusp happy with our thread */
+//			if (current->flags & PF_FREEZE)
+//				refrigerator(PF_FREEZE);
+		} while (!signal_pending (current) && (timeout > 0));
+
+		if (signal_pending (current)) {
+			spin_lock_irq(&current->sighand->siglock);
+			flush_signals(current);
+			spin_unlock_irq(&current->sighand->siglock);
+		}
+
+		if (np->thread_die)
+			break;
+		
+		current_link=mii_link_ok(&mii_info);
+		if (np->link_state!=current_link) {
+			if (current_link) {
+				printk(KERN_INFO "%s: Ethernet Link OK!\n",dev->name);
+				jz_eth_curr_mode(dev);
+				netif_carrier_on(dev);
+			}
+			else {
+				printk(KERN_WARNING "%s: Ethernet Link offline!\n",dev->name);
+				netif_carrier_off(dev);
+			}
+		}
+		np->link_state=current_link;
+
+	}
+	complete_and_exit (&np->thr_exited, 0);	
+}
+
+
+/*
+ * Reset ethernet device
+ */
+static inline void jz_eth_reset(void)
+{				
+	u32 i;					
+	i = readl(jz_eth_base + DMA_BMR);
+	writel(i | BMR_SWR, jz_eth_base + DMA_BMR);			
+	for(i = 0; i < 1000; i++) {			
+		if(!(readl(jz_eth_base + DMA_BMR) & BMR_SWR)) break;	
+		mdelay(1);			
+	}						
+}
+
+/*
+ * MII operation routines 
+ */
+static inline void mii_wait(void)
+{
+	int i;
+	for(i = 0; i < 10000; i++) {
+		if(!(readl(jz_eth_base + MAC_MIIA) & 0x1)) 
+			break;
+		mdelay(1);
+	}
+	if (i >= 10000)
+		printk("MII wait timeout : %d.\n", i);
+}
+
+static int mdio_read(struct net_device *dev,int phy_id, int location)
+{
+	u32 mii_cmd = (phy_id << 11) | (location << 6) | 1;
+	int retval = 0;
+	
+	writel(mii_cmd, jz_eth_base + MAC_MIIA);
+	mii_wait();
+	retval = readl(jz_eth_base + MAC_MIID) & 0x0000ffff;
+
+	return retval;
+	
+}
+
+static void mdio_write(struct net_device *dev,int phy_id, int location, int data)
+{
+	u32 mii_cmd = (phy_id << 11) | (location << 6) | 0x2 /*| 1*/;
+
+	writel(mii_cmd, jz_eth_base + MAC_MIIA);
+	writel(data & 0x0000ffff, jz_eth_base + MAC_MIID);
+	mii_cmd = (phy_id << 11) | (location << 6) | 0x2 | 1;
+	writel(mii_cmd, jz_eth_base + MAC_MIIA);
+	mii_wait();
+}
+
+
+/*
+ * Search MII phy
+ */
+static int jz_search_mii_phy(struct net_device *dev)
+{
+	
+	struct jz_eth_private *np = netdev_priv(dev);
+	int phy, phy_idx = 0;
+
+	np->valid_phy = 0xff;
+	for (phy = 0; phy < 32; phy++) {
+		int mii_status = mdio_read(dev,phy, 1);
+		if (mii_status != 0xffff  &&  mii_status != 0x0000) {
+			np->phys[phy_idx] = phy;
+			np->ecmds[phy_idx].speed=SPEED_100;
+			np->ecmds[phy_idx].duplex=DUPLEX_FULL;
+			np->ecmds[phy_idx].port=PORT_MII;
+			np->ecmds[phy_idx].transceiver=XCVR_INTERNAL;
+			np->ecmds[phy_idx].phy_address=np->phys[phy_idx];
+			np->ecmds[phy_idx].autoneg=AUTONEG_ENABLE;
+			np->ecmds[phy_idx].advertising=(ADVERTISED_10baseT_Half |
+							ADVERTISED_10baseT_Full |
+							ADVERTISED_100baseT_Half |
+							ADVERTISED_100baseT_Full);
+			phy_idx++;
+			break;
+		}
+	}
+	if (phy_idx == 1) {
+		np->valid_phy = np->phys[0];
+		np->phy_type = 0;
+	}
+	if (phy_idx != 0) {
+		phy = np->valid_phy;
+		np->advertising = mdio_read(dev,phy, 4);
+	}
+	return phy_idx;
+}
+
+/*
+ * CRC calc for Destination Address for gets hashtable index
+ */
+
+#define POLYNOMIAL 0x04c11db7UL
+static u16 jz_hashtable_index(u8 *addr)
+{
+	u32 crc = 0xffffffff, msb;
+	int  i, j;
+	u32  byte;
+	for (i = 0; i < 6; i++) {
+		byte = *addr++;
+		for (j = 0; j < 8; j++) {
+			msb = crc >> 31;
+			crc <<= 1;
+			if (msb ^ (byte & 1)) crc ^= POLYNOMIAL;
+			byte >>= 1;
+		}
+	}
+	return ((int)(crc >> 26));
+}
+
+/*
+ * Multicast filter and config multicast hash table
+ */
+#define MULTICAST_FILTER_LIMIT 64
+
+static void jz_set_multicast_list(struct net_device *dev)
+{
+	int i, hash_index;
+	u32 mcr, hash_h, hash_l, hash_bit;
+	
+	mcr = readl(jz_eth_base + MAC_MCR);
+	mcr &= ~(MCR_PR | MCR_PM | MCR_HP);
+	
+	if (dev->flags & IFF_PROMISC) {
+		/* Accept any kinds of packets */
+		mcr |= MCR_PR;
+		hash_h = 0xffffffff;
+		hash_l = 0xffffffff;
+	}
+	else  if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > MULTICAST_FILTER_LIMIT)){
+		/* Accept all multicast packets */
+		mcr |= MCR_PM;
+		hash_h = 0xffffffff;
+		hash_l = 0xffffffff;
+	}
+	else if (dev->flags & IFF_MULTICAST)
+	{
+		/* Update multicast hash table */
+		struct dev_mc_list *mclist;
+		hash_h = readl(jz_eth_base + MAC_HTH);
+		hash_l = readl(jz_eth_base + MAC_HTL);
+		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+		     i++, mclist = mclist->next)
+		{
+			hash_index = jz_hashtable_index(mclist->dmi_addr);
+			hash_bit=0x00000001;
+			hash_bit <<= (hash_index & 0x1f);
+			if (hash_index > 0x1f) 
+				hash_h |= hash_bit;
+			else
+				hash_l |= hash_bit;
+		}
+		writel(hash_h, jz_eth_base + MAC_HTH);
+		writel(hash_l, jz_eth_base + MAC_HTL);
+		mcr |= MCR_HP;
+	}
+	writel(mcr, jz_eth_base + MAC_MCR);
+}
+
+static inline int jz_phy_reset(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	unsigned int mii_reg0;
+	int i;
+
+	mii_reg0 = mdio_read(dev,np->valid_phy,MII_BMCR);
+	mii_reg0 |= MII_CR_RST;
+	mdio_write(dev, np->valid_phy, MII_BMCR, mii_reg0);
+
+	for (i=0; i<1000; i++) {
+		mdelay(1);
+		mii_reg0 = mdio_read(dev, np->valid_phy, MII_BMCR);
+		if (!(mii_reg0 & MII_CR_RST))
+			break;
+	}
+
+	if (i >= 1000)
+		/* phy error */
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Start Auto-Negotiation function for PHY 
+ */
+static int jz_autonet_complete(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	int i;
+	u32 mii_reg1, timeout = 3000;
+
+	for (i=0; i<timeout; i++) {
+		mdelay(1);
+		mii_reg1 = mdio_read(dev, np->valid_phy, MII_BMSR);
+		if (mii_reg1 & MII_SR_ASSC)
+			break;
+	}
+
+	if (i >= timeout)
+		/* auto negotiation error */
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Get current mode of eth phy
+ */
+static u32 jz_eth_curr_mode(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	unsigned int mii_reg17;
+	u32 flag = 0;
+
+	mii_reg17 = mdio_read(dev,np->valid_phy,MII_DSCSR); 
+	np->media = mii_reg17>>12;
+	if (np->media==8) {
+		printk(KERN_INFO "%s: Current Mode is [100M Full Duplex]",dev->name);
+		flag = 0;
+		np->full_duplex=1;
+	}
+	if (np->media==4) {
+		printk(KERN_INFO "%s: Current Mode is [100M Half Duplex]",dev->name);
+		flag = 0;
+		np->full_duplex=0;
+	}
+	if (np->media==2) {
+		printk(KERN_INFO "%s: Current Mode is [10M Full Duplex]",dev->name);
+		flag = OMR_TTM;
+		np->full_duplex=1;
+	}
+	if (np->media==1) {
+		printk(KERN_INFO "%s: Current Mode is [10M Half Duplex]",dev->name);
+		flag = OMR_TTM;
+		np->full_duplex=0;
+	}
+	printk("\n");
+	return flag;
+}
+
+/*
+ * Ethernet device hardware init
+ * This routine initializes the ethernet device hardware and PHY
+ */
+static int jz_init_hw(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	struct ethtool_cmd ecmd;
+	u32 mcr, omr;
+	u32 sts, flag = 0;
+	int i;
+
+	jz_eth_reset();
+	STOP_ETH();
+
+	/* Set MAC address */
+	writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[0]),
+			jz_eth_base + MAC_MAL);
+	writel(le32_to_cpu(*(unsigned long *)&dev->dev_addr[4]),
+			jz_eth_base + MAC_MAH);
+	printk("%s: JZ On-Chip ethernet (MAC ", dev->name);
+	for (i = 0; i < 5; i++) {
+		printk("%2.2x:", dev->dev_addr[i]);
+	}
+	printk("%2.2x, IRQ %d)\n", dev->dev_addr[i], dev->irq);
+
+	if ((np->mii_phy_cnt = jz_search_mii_phy(dev)) == 0) {
+		printk("%s: PHY not found, bailing.\n", dev->name);
+		return -ENXIO;
+	}
+
+	printk("%s: Found %d PHY on JZ MAC\n", dev->name, np->mii_phy_cnt);
+
+	mii_info.phy_id = np->valid_phy;
+	mii_info.dev = dev;
+	mii_info.mdio_read = &mdio_read;
+	mii_info.mdio_write = &mdio_write;
+
+	ecmd.speed = SPEED_100;
+	ecmd.duplex = DUPLEX_FULL;
+	ecmd.port = PORT_MII;
+	ecmd.transceiver = XCVR_INTERNAL;
+	ecmd.phy_address = np->valid_phy;
+	ecmd.autoneg = AUTONEG_ENABLE;
+	ecmd.advertising = ADVERTISED_10baseT_Half
+				| ADVERTISED_10baseT_Full
+				| ADVERTISED_100baseT_Half
+				| ADVERTISED_100baseT_Full;
+
+	mii_ethtool_sset(&mii_info,&ecmd);
+
+	if (jz_autonet_complete(dev)) 
+		printk(KERN_WARNING "%s: Ethernet Module AutoNegotiation failed\n",dev->name);
+	mii_ethtool_gset(&mii_info,&ecmd);
+
+	printk(KERN_INFO "%s: Provide Modes: ",dev->name);
+	for (i = 0; i < 5;i++) 
+		if (ecmd.advertising & (1<<i))
+			printk("(%d)%s", i+1, media_types[i]);
+	printk("\n");  
+
+	flag = jz_eth_curr_mode(dev);
+
+	/* Config OMR register */
+	omr = readl(jz_eth_base + DMA_OMR) & ~OMR_TTM;
+	omr |= flag;
+	//omr |= OMR_OSF;
+	omr |= OMR_SF;
+	writel(omr, jz_eth_base + DMA_OMR);
+
+	readl(jz_eth_base + DMA_MFC); //through read operation to clear the register for 0x0000000
+
+	/* Set the programmable burst length (value 1 or 4 is validate)*/
+#if 0 /* __BIG_ENDIAN__ */
+	writel(PBL_4 | DSL_0 | 0x100080, jz_eth_base + DMA_BMR);  /* DSL_0: see DESC_SKIP_LEN and DESC_ALIGN */
+#else /* __LITTLE_ENDIAN__ */
+	writel(PBL_4 | DSL_0, jz_eth_base + DMA_BMR);  /* DSL_0: see DESC_SKIP_LEN and DESC_ALIGN */
+#endif
+
+	/* Config MCR register*/
+	mcr = (readl(jz_eth_base + MAC_MCR) & ~(MCR_PS | MCR_HBD | MCR_FDX));   
+	if(np->full_duplex)
+		mcr |= MCR_FDX;
+	mcr |= MCR_BFD | MCR_TE | MCR_RE | MCR_OWD|MCR_HBD;
+	writel(mcr, jz_eth_base + MAC_MCR);
+
+	/* Set base address of TX and RX descriptors */
+	writel(np->dma_rx_ring, jz_eth_base + DMA_RRBA);
+	writel(np->dma_tx_ring, jz_eth_base + DMA_TRBA);
+
+	START_ETH();
+
+	/* set interrupt mask */
+	writel(IMR_DEFAULT | IMR_ENABLE, jz_eth_base + DMA_IMR);
+
+	/* Reset any pending (stale) interrupts */
+	sts = readl(jz_eth_base + DMA_STS);
+	writel(sts, jz_eth_base + DMA_STS);
+
+	return 0;
+}
+
+static void jz_eth_ring_init(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	int i;
+
+	for (i = 0; i < NUM_RX_DESCS; i++) {
+		np->rx_ring[i].status = cpu_to_le32(R_OWN);
+		np->rx_ring[i].desc1 = cpu_to_le32(RX_BUF_SIZE | RD_RCH);
+		np->rx_ring[i].buf1_addr = cpu_to_le32(np->dma_rx_buf + i*RX_BUF_SIZE);
+		np->rx_ring[i].next_addr = cpu_to_le32(np->dma_rx_ring + (i+1) * sizeof (jz_desc_t));
+	}
+	np->rx_ring[NUM_RX_DESCS - 1].next_addr = cpu_to_le32(np->dma_rx_ring);
+
+	for (i = 0; i < NUM_TX_DESCS; i++) {
+		np->tx_ring[i].status = cpu_to_le32(0);
+		np->tx_ring[i].desc1  = cpu_to_le32(TD_TCH);
+		np->tx_ring[i].buf1_addr = 0;
+		np->tx_ring[i].next_addr = cpu_to_le32(np->dma_tx_ring + (i+1) * sizeof (jz_desc_t));
+	}
+	np->tx_ring[NUM_TX_DESCS - 1].next_addr = cpu_to_le32(np->dma_tx_ring);
+
+	np->rx_head = 0;
+	np->tx_head = np->tx_tail = 0;
+
+	dma_cache_wback((unsigned long)np->rx_ring, sizeof(jz_desc_t)*NUM_RX_DESCS);
+	dma_cache_wback((unsigned long)np->tx_ring, sizeof(jz_desc_t)*NUM_TX_DESCS);
+}
+
+static int jz_eth_open(struct net_device *dev)
+{
+	int retval;
+
+	jz_eth_ring_init(dev);
+
+	if ((retval = jz_init_hw(dev)) != 0)
+		return retval;
+
+	retval = request_irq(dev->irq, jz_eth_interrupt, 0, dev->name, dev);
+	if (retval) {
+		printk(KERN_WARNING "%s: unable to get IRQ %d .\n", dev->name, dev->irq);
+		return -EAGAIN;
+	}
+
+	dev->trans_start = jiffies;
+	netif_start_queue(dev);
+	start_check(dev);
+
+	return 0;
+}
+
+static int jz_eth_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	close_check(dev);
+	STOP_ETH();
+	free_irq(dev->irq, dev);
+	return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the device open or closed.
+ */
+static struct net_device_stats * jz_eth_get_stats(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	int tmp;
+
+	tmp = readl(jz_eth_base + DMA_MFC); // After read clear to zero
+	np->stats.rx_missed_errors += (tmp & MFC_CNT2) + ((tmp & MFC_CNT1) >> 16);
+
+	return &np->stats;
+}
+
+/*
+ * ethtool routines
+ */
+static int jz_ethtool_ioctl(struct net_device *dev, void *useraddr)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		strcpy (info.bus_info, "OCS");
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&np->lock);
+		mii_ethtool_gset(&mii_info, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = mii_ethtool_sset(&mii_info, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&mii_info);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&mii_info);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+
+}
+
+/*
+ * Config device
+ */
+static int jz_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	struct mii_ioctl_data *data, rdata;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return jz_ethtool_ioctl(dev, (void *) rq->ifr_data);
+	case SIOCGMIIPHY:
+	case SIOCDEVPRIVATE:
+		data = (struct mii_ioctl_data *)&rq->ifr_data;
+		data->phy_id = np->valid_phy;
+	case SIOCGMIIREG:
+	case SIOCDEVPRIVATE+1:
+		data = (struct mii_ioctl_data *)&rq->ifr_data;
+		data->val_out = mdio_read(dev,np->valid_phy, data->reg_num & 0x1f);
+		return 0;
+	case SIOCSMIIREG:
+	case SIOCDEVPRIVATE+2:
+		data = (struct mii_ioctl_data *)&rq->ifr_data;
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		mdio_write(dev,np->valid_phy, data->reg_num & 0x1f, data->val_in);
+		return 0;
+	case READ_COMMAND:	
+		data = (struct mii_ioctl_data *)rq->ifr_data;
+		if (copy_from_user(&rdata,data,sizeof(rdata)))
+			return -EFAULT;
+		rdata.val_out = mdio_read(dev,rdata.phy_id, rdata.reg_num & 0x1f);
+		if (copy_to_user(data,&rdata,sizeof(rdata)))
+			return -EFAULT;
+		return 0;
+	case WRITE_COMMAND:
+		if (np->phy_type==1) {
+			data = (struct mii_ioctl_data *)rq->ifr_data;
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			if (copy_from_user(&rdata,data,sizeof(rdata)))
+				return -EFAULT;
+			mdio_write(dev,rdata.phy_id, rdata.reg_num & 0x1f, rdata.val_in);
+		}
+		return 0;
+	case GETDRIVERINFO:
+		if (np->phy_type==1) {
+			data = (struct mii_ioctl_data *)rq->ifr_data;
+			if (copy_from_user(&rdata,data,sizeof(rdata)))
+				return -EFAULT;
+			rdata.val_in = 0x1;
+			rdata.val_out = 0x00d0;
+			if (copy_to_user(data,&rdata,sizeof(rdata)))
+				return -EFAULT;
+		}
+		return 0;
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+/*
+ * Received one packet
+ */
+static void eth_rxready(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	struct sk_buff *skb;
+	unsigned char *pkt_ptr;
+	u32 pkt_len;
+	u32 status;
+
+	ring_inv(np->rx_ring, np->rx_head);
+	status = le32_to_cpu(np->rx_ring[np->rx_head].status);
+	while (!(status & R_OWN)) {               /* owner bit = 0 */
+		if (status & RD_ES) {              /* error summary */
+			np->stats.rx_errors++;    /* Update the error stats. */
+			if (status & (RD_RF | RD_TL))
+				np->stats.rx_frame_errors++;
+			if (status & RD_CE)
+				np->stats.rx_crc_errors++;
+			if (status & RD_TL)
+				np->stats.rx_length_errors++;
+		} else {
+			pkt_ptr = bus_to_virt(le32_to_cpu(np->rx_ring[np->rx_head].buf1_addr));
+			pkt_len = ((status & RD_FL) >> 16) - 4;
+
+			skb = dev_alloc_skb(pkt_len + 2);
+			if (skb == NULL) {
+				printk("%s: Memory squeeze, dropping.\n",
+				       dev->name);
+				np->stats.rx_dropped++;
+				break;
+			}
+			skb->dev = dev;
+			skb_reserve(skb, 2); /* 16 byte align */
+
+			memcpy(skb->data, pkt_ptr, pkt_len);
+			skb_put(skb, pkt_len);
+
+			skb->protocol = eth_type_trans(skb,dev);
+			netif_rx(skb);	/* pass the packet to upper layers */
+			dev->last_rx = jiffies;
+			np->stats.rx_packets++;
+			np->stats.rx_bytes += pkt_len;
+		}
+		np->rx_ring[np->rx_head].status = cpu_to_le32(R_OWN);
+
+		np->rx_head ++;
+		if (np->rx_head >= NUM_RX_DESCS)
+			np->rx_head = 0;
+		ring_inv(np->rx_ring, np->rx_head);
+		status = le32_to_cpu(np->rx_ring[np->rx_head].status);
+	}
+}
+
+/*
+ * Tx timeout routine 
+ */
+static void jz_eth_tx_timeout(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+
+	jz_init_hw(dev);
+	np->stats.tx_errors ++;
+	netif_wake_queue(dev);
+}
+
+/*
+ * One packet was transmitted
+ */
+static void eth_txdone(struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	int tx_tail = np->tx_tail;
+	int entry;
+	u32 status;
+
+	while (tx_tail != np->tx_head) {
+		entry = tx_tail % NUM_TX_DESCS;
+		ring_inv(np->tx_ring, entry);
+		status = le32_to_cpu(np->tx_ring[entry].status);
+		if (status & T_OWN)
+			break;
+		if (status & TD_ES ) {       /* Error summary */
+			np->stats.tx_errors++;
+			if (status & TD_NC) np->stats.tx_carrier_errors++;
+			if (status & TD_LC) np->stats.tx_window_errors++;
+			if (status & TD_UF) np->stats.tx_fifo_errors++;
+			if (status & TD_DE) np->stats.tx_aborted_errors++;
+			if (np->tx_head != np->tx_tail)
+				writel(1, jz_eth_base + DMA_TPD);  /* Restart a stalled TX */
+		} else
+			np->stats.tx_packets++;
+		/* Update the collision counter */
+		np->stats.collisions += ((status & TD_EC) ? 16 : ((status & TD_CC) >> 3));
+		/* Free the original skb */
+		if (np->tx_skb[entry]) {
+			dev_kfree_skb_irq(np->tx_skb[entry]);
+			np->tx_skb[entry] = 0;
+		}
+		tx_tail++;
+	}
+	if (np->tx_full && (tx_tail + NUM_TX_DESCS > np->tx_head + 1)) {
+		/* The ring is no longer full */
+		np->tx_full = 0;
+		netif_wake_queue(dev);
+	}
+	np->tx_tail = tx_tail;
+}
+
+/*
+ * Update the tx descriptor
+ */
+static void load_tx_packet(struct net_device *dev, char *buf, u32 flags, struct sk_buff *skb)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	int entry = np->tx_head % NUM_TX_DESCS;
+
+	np->tx_ring[entry].buf1_addr = cpu_to_le32(virt_to_bus(buf));
+	np->tx_ring[entry].desc1 &= cpu_to_le32((TD_TER | TD_TCH));
+	np->tx_ring[entry].desc1 |= cpu_to_le32(flags);
+	np->tx_ring[entry].status = cpu_to_le32(T_OWN);
+	np->tx_skb[entry] = skb;
+
+	ring_wback(np->tx_ring, entry);
+}
+
+/*
+ * Transmit one packet
+ */
+static int jz_eth_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct jz_eth_private *np = netdev_priv(dev);
+	u32 length;
+
+	if (np->tx_full) {
+		return 0;
+	}
+
+	length = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+	dma_cache_wback((unsigned long)skb->data, length);
+	load_tx_packet(dev, (char *)skb->data,
+			TD_IC | TD_LS | TD_FS | (length & TD_TBS1), skb);
+
+	spin_lock_irq(&np->lock);
+	np->tx_head ++;
+	np->stats.tx_bytes += length;
+	writel(1, jz_eth_base + DMA_TPD);		/* Start the TX */
+
+	dev->trans_start = jiffies;	/* for timeout */
+	if (np->tx_tail + NUM_TX_DESCS > np->tx_head + 1) {
+		np->tx_full = 0;
+	}
+	else {
+		np->tx_full = 1;
+		netif_stop_queue(dev);
+	}
+	spin_unlock_irq(&np->lock);
+
+	return 0;
+}
+
+/*
+ * Interrupt service routine
+ */
+static irqreturn_t jz_eth_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct jz_eth_private *np = netdev_priv(dev);
+	u32 sts;
+	int i;
+
+	spin_lock(&np->lock);
+
+	writel((readl(jz_eth_base + DMA_IMR) & ~IMR_ENABLE),
+			jz_eth_base + DMA_IMR);
+
+	for (i = 0; i < 100; i++) {
+		/* clear status */
+		sts = readl(jz_eth_base + DMA_STS);
+		writel(sts, jz_eth_base + DMA_STS);
+
+		if (!(sts & IMR_DEFAULT))
+			break;
+
+		if (sts & (DMA_INT_RI | DMA_INT_RU))
+			/* Rx IRQ */
+			eth_rxready(dev);
+		if (sts & (DMA_INT_TI | DMA_INT_TU)) {
+			/* Tx IRQ */
+			eth_txdone(dev);
+		}
+
+		/* check error conditions */
+		if (sts & DMA_INT_FB){
+			STOP_ETH();
+			printk(KERN_ERR "%s: Fatal bus error occurred, sts=%#8x, "
+					"device stopped.\n",dev->name, sts);
+			break;
+		}
+
+		/* Transmit underrun */
+		if (sts & DMA_INT_UN) {
+			u32 omr;
+			omr = readl(jz_eth_base + DMA_OMR);
+			if (!(omr & OMR_SF)) {
+				omr &= ~(OMR_ST | OMR_SR);
+				writel(omr, jz_eth_base + DMA_OMR);
+
+				/* wait for stop */
+				while (readl(jz_eth_base + DMA_STS) & STS_TS)
+					;
+
+				/* increase transmit threshold if possible */
+				if ((omr & OMR_TR) < OMR_TR) {
+					omr += TR_24;
+				} else {
+					omr |= OMR_SF;
+				}
+				writel(omr | OMR_ST | OMR_SR,
+						jz_eth_base + DMA_OMR);
+			}
+		}
+	}
+
+	writel(readl(jz_eth_base + DMA_IMR)|IMR_ENABLE, jz_eth_base + DMA_IMR);
+
+	spin_unlock(&np->lock);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Suspend the ETH interface.
+ */
+static int jz_eth_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct jz_eth_private *jep = netdev_priv(dev);
+	unsigned long flags, tmp;
+
+	if (!netif_running(dev))
+		return 0;
+
+	netif_device_detach(dev);
+
+	spin_lock_irqsave(&jep->lock, flags);
+
+	/* Disable interrupts, stop Tx and Rx. */
+	writel(0, jz_eth_base + DMA_IMR);
+	STOP_ETH();
+
+	/* Update the error counts. */
+	tmp = readl(jz_eth_base + DMA_MFC);
+	jep->stats.rx_missed_errors += (tmp & 0x1ffff);
+	jep->stats.rx_fifo_errors += ((tmp >> 17) & 0x7ff);
+
+	spin_unlock_irqrestore(&jep->lock, flags);
+
+	return 0;
+}
+
+/*
+ * Resume the ETH interface.
+ */
+static int jz_eth_resume(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+
+	if (!netif_running(dev))
+		return 0;
+
+	jz_eth_ring_init(dev);
+	jz_init_hw(dev);
+
+	netif_device_attach(dev);
+	netif_wake_queue(dev);
+
+	return 0;
+}
+#else
+#define jz_eth_suspend NULL
+#define jz_eth_resume NULL
+#endif /* CONFIG_PM */
+
+static const struct net_device_ops jz_netdev_ops = {
+	.ndo_open = jz_eth_open,
+	.ndo_stop = jz_eth_close,
+	.ndo_start_xmit = jz_eth_send_packet,
+	.ndo_set_multicast_list = jz_set_multicast_list,
+	.ndo_tx_timeout = jz_eth_tx_timeout,
+	.ndo_get_stats = jz_eth_get_stats,
+	.ndo_do_ioctl = jz_eth_ioctl,
+};
+
+static int jz_eth_probe(struct platform_device *pdev)
+{
+	struct net_device *dev;
+	struct jz_eth_private *np;
+	int ret;
+
+	dev = alloc_etherdev(sizeof(struct jz_eth_private));
+	if (!dev) {
+		printk(KERN_ERR "%s: alloc_etherdev failed\n", DRV_NAME);
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	SET_NETDEV_DEV(dev, &pdev->dev);
+
+	np = netdev_priv(dev);
+	memset(np, 0, sizeof(struct jz_eth_private));
+
+	np->vaddr_rx_buf = (u32)dma_alloc_noncoherent(NULL,
+					NUM_RX_DESCS*RX_BUF_SIZE,
+					&np->dma_rx_buf, 0);
+
+	if (!np->vaddr_rx_buf) {
+		printk(KERN_ERR "%s: Cannot alloc dma buffers\n", DRV_NAME);
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	np->dma_rx_ring = virt_to_bus(np->rx_ring);
+	np->dma_tx_ring = virt_to_bus(np->tx_ring);
+	np->full_duplex = 1;
+	np->link_state = 1;
+
+	dev->irq = platform_get_irq(pdev, 0);
+	if (dev->irq < 0) {
+		ret = -ENXIO;
+		goto err2;
+	}
+
+	np->iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!np->iores) {
+		ret = -ENXIO;
+		goto err2;
+	}
+
+	np->iores = request_mem_region(np->iores->start,
+			resource_size(np->iores), pdev->name);
+	if (!np->iores) {
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	jz_eth_base = ioremap_nocache(np->iores->start,
+			resource_size(np->iores));
+	if (!jz_eth_base) {
+		ret = -EBUSY;
+		goto err3;
+	}
+
+	spin_lock_init(&np->lock);
+
+	dev->netdev_ops = &jz_netdev_ops;
+	dev->watchdog_timeo = ETH_TX_TIMEOUT;
+
+	/* configure MAC address */
+	get_mac_address(dev);
+
+	platform_set_drvdata(pdev, dev);
+
+	if ((ret = register_netdev(dev)) != 0) {
+		printk(KERN_ERR "%s: Cannot register net device, error %d\n",
+				DRV_NAME, ret);
+		goto err4;
+	}
+
+	return 0;
+
+err4:
+	iounmap(jz_eth_base);
+err3:
+	release_mem_region(np->iores->start, 0x10000);
+err2:
+	dma_free_noncoherent(NULL, NUM_RX_DESCS * RX_BUF_SIZE,
+			     (void *)np->vaddr_rx_buf, np->dma_rx_buf);
+err1:
+	free_netdev(dev);
+err0:
+	return ret;
+}
+
+static int jz_eth_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct jz_eth_private *np = netdev_priv(dev);
+
+	unregister_netdev(dev);
+	iounmap(jz_eth_base);
+	release_mem_region(np->iores->start, 0x10000);
+	dma_free_noncoherent(NULL, NUM_RX_DESCS * RX_BUF_SIZE,
+			     (void *)np->vaddr_rx_buf, np->dma_rx_buf);
+	free_netdev(dev);
+	return 0;
+}
+
+static struct platform_driver jz_eth_driver = {
+	.driver = {
+		.name = "jz-eth"
+	},
+	.probe = jz_eth_probe,
+	.remove = jz_eth_remove,
+	.suspend = jz_eth_suspend,
+	.resume = jz_eth_resume,
+};
+
+static int __init jz_eth_init(void)
+{
+	return platform_driver_register(&jz_eth_driver);
+}
+
+static void __exit jz_eth_exit(void)
+{
+	platform_driver_unregister(&jz_eth_driver);
+}
+
+module_init(jz_eth_init);
+module_exit(jz_eth_exit);
diff --git a/drivers/net/jz_eth.h b/drivers/net/jz_eth.h
new file mode 100644
index 0000000..62d147c
--- /dev/null
+++ b/drivers/net/jz_eth.h
@@ -0,0 +1,403 @@
+/*
+ *  Jz4730/Jz5730 On-Chip ethernet driver.
+ *
+ *  Copyright (C) 2005 - 2007  Ingenic Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __JZ_ETH_H__
+#define __JZ_ETH_H__
+
+/* DMA control and status registers */
+#define DMA_BMR                0x1000 // Bus mode
+#define DMA_TPD                0x1004 // Transmit poll demand register
+#define DMA_RPD                0x1008 // Receieve poll demand register
+#define DMA_RRBA               0x100C // Receieve descriptor base address
+#define DMA_TRBA               0x1010 // Transmit descriptor base address
+#define DMA_STS                0x1014 // Status register
+#define DMA_OMR                0x1018 // Command register
+#define DMA_IMR                0x101C
+#define DMA_MFC                0x1020
+
+/* DMA CSR8-CSR19 reserved */
+#define DMA_CTA                0x1050
+#define DMA_CRA                0x1054
+
+/* Mac control and status registers */
+#define MAC_MCR                0x0000
+#define MAC_MAH                0x0004
+#define MAC_MAL                0x0008
+#define MAC_HTH                0x000C
+#define MAC_HTL                0x0010
+#define MAC_MIIA               0x0014
+#define MAC_MIID               0x0018
+#define MAC_FCR                0x001C
+#define MAC_VTR1               0x0020
+#define MAC_VTR2               0x0024
+
+/*
+ * Bus Mode Register (DMA_BMR)
+ */
+#define BMR_PBL    0x00003f00       /* Programmable Burst Length */
+#define BMR_DSL    0x0000007c       /* Descriptor Skip Length */
+#define BMR_BAR    0x00000002       /* Bus ARbitration */
+#define BMR_SWR    0x00000001       /* Software Reset */
+
+#define PBL_0      0x00000000       /*  DMA burst length = amount in RX FIFO */
+#define PBL_1      0x00000100       /*  1 longword  DMA burst length */
+#define PBL_2      0x00000200       /*  2 longwords DMA burst length */
+#define PBL_4      0x00000400       /*  4 longwords DMA burst length */
+#define PBL_8      0x00000800       /*  8 longwords DMA burst length */
+#define PBL_16     0x00001000       /* 16 longwords DMA burst length */
+#define PBL_32     0x00002000       /* 32 longwords DMA burst length */
+
+#define DSL_0      0x00000000       /*  0 longword  / descriptor */
+#define DSL_1      0x00000004       /*  1 longword  / descriptor */
+#define DSL_2      0x00000008       /*  2 longwords / descriptor */
+#define DSL_4      0x00000010       /*  4 longwords / descriptor */
+#define DSL_8      0x00000020       /*  8 longwords / descriptor */
+#define DSL_16     0x00000040       /* 16 longwords / descriptor */
+#define DSL_32     0x00000080       /* 32 longwords / descriptor */
+
+/*
+ * Status Register (DMA_STS)
+ */
+#define STS_BE     0x03800000       /* Bus Error Bits */
+#define STS_TS     0x00700000       /* Transmit Process State */
+#define STS_RS     0x000e0000       /* Receive Process State */
+
+#define TS_STOP    0x00000000       /* Stopped */
+#define TS_FTD     0x00100000       /* Running Fetch Transmit Descriptor */
+#define TS_WEOT    0x00200000       /* Running Wait for End Of Transmission */
+#define TS_QDAT    0x00300000       /* Running Queue skb data into TX FIFO */
+#define TS_RES     0x00400000       /* Reserved */
+#define TS_SPKT    0x00500000       /* Reserved */
+#define TS_SUSP    0x00600000       /* Suspended */
+#define TS_CLTD    0x00700000       /* Running Close Transmit Descriptor */
+
+#define RS_STOP    0x00000000       /* Stopped */
+#define RS_FRD     0x00020000       /* Running Fetch Receive Descriptor */
+#define RS_CEOR    0x00040000       /* Running Check for End of Receive Packet */
+#define RS_WFRP    0x00060000       /* Running Wait for Receive Packet */
+#define RS_SUSP    0x00080000       /* Suspended */
+#define RS_CLRD    0x000a0000       /* Running Close Receive Descriptor */
+#define RS_FLUSH   0x000c0000       /* Running Flush RX FIFO */
+#define RS_QRFS    0x000e0000       /* Running Queue RX FIFO into RX Skb */
+
+/*
+ * Operation Mode Register (DMA_OMR)
+ */
+#define OMR_TTM    0x00400000       /* Transmit Threshold Mode */
+#define OMR_SF     0x00200000       /* Store and Forward */
+#define OMR_TR     0x0000c000       /* Threshold Control Bits */
+#define OMR_ST     0x00002000       /* Start/Stop Transmission Command */
+#define OMR_OSF    0x00000004       /* Operate on Second Frame */
+#define OMR_SR     0x00000002       /* Start/Stop Receive */
+
+#define TR_18      0x00000000       /* Threshold set to 18 (32) bytes */
+#define TR_24      0x00004000       /* Threshold set to 24 (64) bytes */
+#define TR_32      0x00008000       /* Threshold set to 32 (128) bytes */
+#define TR_40      0x0000c000       /* Threshold set to 40 (256) bytes */
+
+/*
+ * Missed Frames Counters (DMA_MFC)
+ */
+//#define MFC_CNT1   0xffff0000       /* Missed Frames Counter Bits by application */
+#define MFC_CNT1   0x0ffe0000       /* Missed Frames Counter Bits by application */
+#define MFC_CNT2   0x0000ffff       /* Missed Frames Counter Bits by controller */
+
+/*
+ * Mac control  Register (MAC_MCR)
+ */
+#define MCR_RA     0x80000000       /* Receive All */
+#define MCR_HBD    0x10000000       /* HeartBeat Disable */
+#define MCR_PS     0x08000000       /* Port Select */
+#define MCR_OWD    0x00800000       /* Receive own Disable */
+#define MCR_OM     0x00600000       /* Operating(loopback) Mode */
+#define MCR_FDX    0x00100000       /* Full Duplex Mode */
+#define MCR_PM     0x00080000       /* Pass All Multicast */
+#define MCR_PR     0x00040000       /* Promiscuous Mode */
+#define MCR_IF     0x00020000       /* Inverse Filtering */
+#define MCR_PB     0x00010000       /* Pass Bad Frames */
+#define MCR_HO     0x00008000       /* Hash Only Filtering Mode */
+#define MCR_HP     0x00002000       /* Hash/Perfect Receive Filtering Mode */
+#define MCR_FC     0x00001000       /* Late Collision control */
+#define MCR_BFD    0x00000800       /* Boardcast frame Disable */
+#define MCR_RED    0x00000400       /* Retry Disable */
+#define MCR_APS    0x00000100       /* Automatic pad stripping */
+#define MCR_BL     0x000000c0       /* Back off Limit */
+#define MCR_DC     0x00000020       /* Deferral check */
+#define MCR_TE     0x00000008       /* Transmitter enable */
+#define MCR_RE     0x00000004       /* Receiver enable */
+
+#define MCR_MII_10  ( OMR_TTM | MCR_PS)
+#define MCR_MII_100 ( MCR_HBD | MCR_PS)
+
+/* Flow control Register (MAC_FCR) */
+#define FCR_PT     0xffff0000       /* Pause time */
+#define FCR_PCF    0x00000004       /* Pass control frames */
+#define FCR_FCE    0x00000002       /* Flow control enable */
+#define FCR_FCB    0x00000001       /* Flow control busy */
+
+
+/* Constants for the interrupt mask and
+ * interrupt status registers. (DMA_SIS and DMA_IMR)
+ */
+#define DMA_INT_NI             0x00010000       // Normal interrupt summary
+#define DMA_INT_AI             0x00008000       // Abnormal interrupt summary
+#define DMA_INT_ER             0x00004000       // Early receive interrupt
+#define DMA_INT_FB             0x00002000       // Fatal bus error
+#define DMA_INT_ET             0x00000400       // Early transmit interrupt
+#define DMA_INT_RW             0x00000200       // Receive watchdog timeout
+#define DMA_INT_RS             0x00000100       // Receive stop
+#define DMA_INT_RU             0x00000080       // Receive buffer unavailble
+#define DMA_INT_RI             0x00000040       // Receive interrupt
+#define DMA_INT_UN             0x00000020       // Underflow 
+#define DMA_INT_TJ             0x00000008       // Transmit jabber timeout
+#define DMA_INT_TU             0x00000004       // Transmit buffer unavailble 
+#define DMA_INT_TS             0x00000002       // Transmit stop
+#define DMA_INT_TI             0x00000001       // Transmit interrupt
+
+/*
+ * Receive Descriptor Bit Summary
+ */
+#define R_OWN      0x80000000       /* Own Bit */
+#define RD_FF      0x40000000       /* Filtering Fail */
+#define RD_FL      0x3fff0000       /* Frame Length */
+#define RD_ES      0x00008000       /* Error Summary */
+#define RD_DE      0x00004000       /* Descriptor Error */
+#define RD_LE      0x00001000       /* Length Error */
+#define RD_RF      0x00000800       /* Runt Frame */
+#define RD_MF      0x00000400       /* Multicast Frame */
+#define RD_FS      0x00000200       /* First Descriptor */
+#define RD_LS      0x00000100       /* Last Descriptor */
+#define RD_TL      0x00000080       /* Frame Too Long */
+#define RD_CS      0x00000040       /* Collision Seen */
+#define RD_FT      0x00000020       /* Frame Type */
+#define RD_RJ      0x00000010       /* Receive Watchdog timeout*/
+#define RD_RE      0x00000008       /* Report on MII Error */
+#define RD_DB      0x00000004       /* Dribbling Bit */
+#define RD_CE      0x00000002       /* CRC Error */
+
+#define RD_RER     0x02000000       /* Receive End Of Ring */
+#define RD_RCH     0x01000000       /* Second Address Chained */
+#define RD_RBS2    0x003ff800       /* Buffer 2 Size */
+#define RD_RBS1    0x000007ff       /* Buffer 1 Size */
+
+/*
+ * Transmit Descriptor Bit Summary
+ */
+#define T_OWN      0x80000000       /* Own Bit */
+#define TD_ES      0x00008000       /* Frame Aborted (error summary)*/
+#define TD_LO      0x00000800       /* Loss Of Carrier */
+#define TD_NC      0x00000400       /* No Carrier */
+#define TD_LC      0x00000200       /* Late Collision */
+#define TD_EC      0x00000100       /* Excessive Collisions */
+#define TD_HF      0x00000080       /* Heartbeat Fail */
+#define TD_CC      0x0000003c       /* Collision Counter */
+#define TD_UF      0x00000002       /* Underflow Error */
+#define TD_DE      0x00000001       /* Deferred */
+
+#define TD_IC      0x80000000       /* Interrupt On Completion */
+#define TD_LS      0x40000000       /* Last Segment */
+#define TD_FS      0x20000000       /* First Segment */
+#define TD_FT1     0x10000000       /* Filtering Type */
+#define TD_SET     0x08000000       /* Setup Packet */
+#define TD_AC      0x04000000       /* Add CRC Disable */
+#define TD_TER     0x02000000       /* Transmit End Of Ring */
+#define TD_TCH     0x01000000       /* Second Address Chained */
+#define TD_DPD     0x00800000       /* Disabled Padding */
+#define TD_FT0     0x00400000       /* Filtering Type */
+#define TD_TBS2    0x003ff800       /* Buffer 2 Size */
+#define TD_TBS1    0x000007ff       /* Buffer 1 Size */
+
+#define PERFECT_F  0x00000000
+#define HASH_F     TD_FT0
+#define INVERSE_F  TD_FT1
+#define HASH_O_F   (TD_FT1 | TD_F0)
+
+/*
+ * Constant setting
+ */
+
+#define IMR_DEFAULT    ( DMA_INT_TI | DMA_INT_RI |	\
+                         DMA_INT_TS | DMA_INT_RS |	\
+                         DMA_INT_TU | DMA_INT_RU |	\
+                         DMA_INT_FB )
+
+#define IMR_ENABLE     (DMA_INT_NI | DMA_INT_AI)
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
+
+#define HASH_TABLE_LEN   512       /* Bits */
+#define HASH_BITS        0x01ff    /* 9 LS bits */
+
+#define SETUP_FRAME_LEN  192       /* Bytes */
+#define IMPERF_PA_OFFSET 156       /* Bytes */
+
+/*
+ * Address Filtering Modes
+ */
+#define PERFECT              0     /* 16 perfect physical addresses */
+#define HASH_PERF            1     /* 1 perfect, 512 multicast addresses */
+#define PERFECT_REJ          2     /* Reject 16 perfect physical addresses */
+#define ALL_HASH             3     /* Hashes all physical & multicast addrs */
+
+#define ALL                  0     /* Clear out all the setup frame */
+#define PHYS_ADDR_ONLY       1     /* Update the physical address only */
+
+/* MII register */
+#define MII_BMCR       0x00          /* MII Basic Mode Control Register */
+#define MII_BMSR       0x01          /* MII Basic Mode Status Register */
+#define MII_ID1        0x02          /* PHY Identifier Register 1 */
+#define MII_ID2        0x03          /* PHY Identifier Register 2 */
+#define MII_ANAR       0x04          /* Auto Negotiation Advertisement Register */
+#define MII_ANLPAR     0x05          /* Auto Negotiation Link Partner Ability */
+#define MII_ANER       0x06          /* Auto Negotiation Expansion */
+#define MII_DSCR       0x10          /* Davicom Specified Configration Register */
+#define MII_DSCSR      0x11          /* Davicom Specified Configration/Status Register */
+#define MII_10BTCSR    0x12          /* 10base-T Specified Configration/Status Register */
+
+
+#define MII_PREAMBLE 0xffffffff    /* MII Management Preamble */
+#define MII_TEST     0xaaaaaaaa    /* MII Test Signal */
+#define MII_STRD     0x06          /* Start of Frame+Op Code: use low nibble */
+#define MII_STWR     0x0a          /* Start of Frame+Op Code: use low nibble */
+
+/*
+ * MII Management Control Register
+ */
+#define MII_CR_RST  0x8000         /* RESET the PHY chip */
+#define MII_CR_LPBK 0x4000         /* Loopback enable */
+#define MII_CR_SPD  0x2000         /* 0: 10Mb/s; 1: 100Mb/s */
+#define MII_CR_ASSE 0x1000         /* Auto Speed Select Enable */
+#define MII_CR_PD   0x0800         /* Power Down */
+#define MII_CR_ISOL 0x0400         /* Isolate Mode */
+#define MII_CR_RAN  0x0200         /* Restart Auto Negotiation */
+#define MII_CR_FDM  0x0100         /* Full Duplex Mode */
+#define MII_CR_CTE  0x0080         /* Collision Test Enable */
+
+/*
+ * MII Management Status Register
+ */
+#define MII_SR_T4C  0x8000         /* 100BASE-T4 capable */
+#define MII_SR_TXFD 0x4000         /* 100BASE-TX Full Duplex capable */
+#define MII_SR_TXHD 0x2000         /* 100BASE-TX Half Duplex capable */
+#define MII_SR_TFD  0x1000         /* 10BASE-T Full Duplex capable */
+#define MII_SR_THD  0x0800         /* 10BASE-T Half Duplex capable */
+#define MII_SR_ASSC 0x0020         /* Auto Speed Selection Complete*/
+#define MII_SR_RFD  0x0010         /* Remote Fault Detected */
+#define MII_SR_ANC  0x0008         /* Auto Negotiation capable */
+#define MII_SR_LKS  0x0004         /* Link Status */
+#define MII_SR_JABD 0x0002         /* Jabber Detect */
+#define MII_SR_XC   0x0001         /* Extended Capabilities */
+
+/*
+ * MII Management Auto Negotiation Advertisement Register
+ */
+#define MII_ANA_TAF  0x03e0        /* Technology Ability Field */
+#define MII_ANA_T4AM 0x0200        /* T4 Technology Ability Mask */
+#define MII_ANA_TXAM 0x0180        /* TX Technology Ability Mask */
+#define MII_ANA_FDAM 0x0140        /* Full Duplex Technology Ability Mask */
+#define MII_ANA_HDAM 0x02a0        /* Half Duplex Technology Ability Mask */
+#define MII_ANA_100M 0x0380        /* 100Mb Technology Ability Mask */
+#define MII_ANA_10M  0x0060        /* 10Mb Technology Ability Mask */
+#define MII_ANA_CSMA 0x0001        /* CSMA-CD Capable */
+
+/*
+ * MII Management Auto Negotiation Remote End Register
+ */
+#define MII_ANLPA_NP   0x8000      /* Next Page (Enable) */
+#define MII_ANLPA_ACK  0x4000      /* Remote Acknowledge */
+#define MII_ANLPA_RF   0x2000      /* Remote Fault */
+#define MII_ANLPA_TAF  0x03e0      /* Technology Ability Field */
+#define MII_ANLPA_T4AM 0x0200      /* T4 Technology Ability Mask */
+#define MII_ANLPA_TXAM 0x0180      /* TX Technology Ability Mask */
+#define MII_ANLPA_FDAM 0x0140      /* Full Duplex Technology Ability Mask */
+#define MII_ANLPA_HDAM 0x02a0      /* Half Duplex Technology Ability Mask */
+#define MII_ANLPA_100M 0x0380      /* 100Mb Technology Ability Mask */
+#define MII_ANLPA_10M  0x0060      /* 10Mb Technology Ability Mask */
+#define MII_ANLPA_CSMA 0x0001      /* CSMA-CD Capable */
+
+/*
+ * MII Management DAVICOM Specified Configuration And Status Register
+ */
+#define MII_DSCSR_100FDX       0x8000  /* 100M Full Duplex Operation Mode */    
+#define MII_DSCSR_100HDX       0x4000  /* 100M Half Duplex Operation Mode */
+#define MII_DSCSR_10FDX        0x2000  /* 10M  Full Duplex Operation Mode */
+#define MII_DSCSR_10HDX        0x1000  /* 10M  Half Duplex Operation Mode */
+#define MII_DSCSR_ANMB         0x000f  /* Auto-Negotiation Monitor Bits   */
+
+
+/*
+ * Used by IOCTL
+ */
+#define READ_COMMAND		(SIOCDEVPRIVATE+4)
+#define WRITE_COMMAND		(SIOCDEVPRIVATE+5)
+#define GETDRIVERINFO		(SIOCDEVPRIVATE+6)
+
+/*
+ * Device data and structure
+ */
+
+#define ETH_TX_TIMEOUT		(6*HZ)
+
+#define RX_BUF_SIZE		1536
+
+#define NUM_RX_DESCS		32
+#define NUM_TX_DESCS		16
+
+static const char *media_types[] = {
+	"10BaseT-HD ", "10BaseT-FD ","100baseTx-HD ", 
+	"100baseTx-FD", "100baseT4", 0
+};
+
+typedef struct {
+	unsigned int status;
+	unsigned int desc1;
+	unsigned int buf1_addr;
+	unsigned int next_addr;
+} jz_desc_t;	
+
+struct jz_eth_private {
+	jz_desc_t tx_ring[NUM_TX_DESCS];	/* transmit descriptors */
+	jz_desc_t rx_ring[NUM_RX_DESCS];	/* receive descriptors */
+	dma_addr_t dma_tx_ring;                 /* bus address of tx ring */
+	dma_addr_t dma_rx_ring;                 /* bus address of rx ring */
+	dma_addr_t dma_rx_buf;			/* DMA address of rx buffer  */	
+	unsigned int vaddr_rx_buf;		/* virtual address of rx buffer  */
+
+	unsigned int rx_head;			/* first rx descriptor */
+	unsigned int tx_head;			/* first tx descriptor */
+	unsigned int tx_tail;  			/* last unacked transmit packet */
+	unsigned int tx_full;			/* transmit buffers are full */
+	struct sk_buff *tx_skb[NUM_TX_DESCS];	/* skbuffs for packets to transmit */
+
+	struct net_device_stats stats;
+	spinlock_t lock;
+
+	int media;				/* Media (eg TP), mode (eg 100B)*/
+	int full_duplex;			/* Current duplex setting. */
+	int link_state;
+	char phys[32];				/* List of attached PHY devices */
+	char valid_phy;				/* Current linked phy-id with MAC */
+	int mii_phy_cnt;
+	int phy_type;				/* 1-RTL8309,0-DVCOM */
+	struct ethtool_cmd ecmds[32];
+	u16 advertising;			/* NWay media advertisement */
+
+	struct task_struct *thread;		/* Link check thread */
+	int thread_die;
+	struct completion thr_exited;
+	wait_queue_head_t thr_wait;
+
+	struct pm_dev *pmdev;
+
+	struct resource *iores;
+};
+
+#endif /* __JZ_ETH_H__ */
-- 
1.6.4

  parent reply	other threads:[~2010-02-25 14:52 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-02-25  6:28 [PATCH 0/3] XBurst JZ4730 support Graham Gower
2010-02-25  6:29 ` [PATCH 1/3] Add " Graham Gower
2010-02-25  6:30 ` [PATCH 2/3] 8250: serial driver changes for XBurst SoCs Graham Gower
2010-02-25  6:31 ` Graham Gower [this message]
2010-02-25  7:52 ` [PATCH 0/3] XBurst JZ4730 support Florian Fainelli
2010-02-25 21:12   ` Graham Gower
2010-02-26  1:51     ` Lars-Peter Clausen
2010-02-26  3:34       ` Graham Gower
2010-02-26 11:41         ` blast_dcache32 problem with PREEMPT kernel Anoop P.A.
2010-02-26 11:41           ` Anoop P.A.

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4B86193D.2010307@gmail.com \
    --to=graham.gower@gmail.com \
    --cc=linux-mips@linux-mips.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.