Merge git://git.kernel.org/pub/scm/linux/kernel/git/czankel/xtensa-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/czankel/xtensa-2.6:
  xtensa: enable m41t80 driver in s6105_defconfig
  xtensa: add m41t62 rtc to s6105 platform
  xtensa: enable s6gmac in s6105_defconfig
  xtensa: s6105 specific configuration for s6gmac
  s6gmac: xtensa s6000 on-chip ethernet driver
  xtensa: support s6000 gpio irqs and alternate function selection
  xtensa: s6000 dma engine support
  xtensa: allow variant to initialize own irq chips
  xtensa: cache inquiry and unaligned cache handling functions
diff --git a/arch/xtensa/configs/s6105_defconfig b/arch/xtensa/configs/s6105_defconfig
index 768bee0..bb84fbc 100644
--- a/arch/xtensa/configs/s6105_defconfig
+++ b/arch/xtensa/configs/s6105_defconfig
@@ -263,7 +263,54 @@
 # CONFIG_SCSI_NETLINK is not set
 # CONFIG_ATA is not set
 # CONFIG_MD is not set
-# CONFIG_NETDEVICES is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_NET_ETHERNET is not set
+CONFIG_NETDEV_1000=y
+CONFIG_S6GMAC=y
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
 # CONFIG_ISDN is not set
 # CONFIG_PHONE is not set
 
@@ -304,8 +351,6 @@
 # CONFIG_LEGACY_PTYS is not set
 # CONFIG_IPMI_HANDLER is not set
 # CONFIG_HW_RANDOM is not set
-# CONFIG_RTC is not set
-# CONFIG_GEN_RTC is not set
 # CONFIG_R3964 is not set
 # CONFIG_RAW_DRIVER is not set
 # CONFIG_TCG_TPM is not set
@@ -387,7 +432,59 @@
 # CONFIG_MEMSTICK is not set
 # CONFIG_NEW_LEDS is not set
 # CONFIG_ACCESSIBILITY is not set
-# CONFIG_RTC_CLASS is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+# CONFIG_RTC_INTF_SYSFS is not set
+# CONFIG_RTC_INTF_PROC is not set
+# CONFIG_RTC_INTF_DEV is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+CONFIG_RTC_DRV_M41T80=y
+# CONFIG_RTC_DRV_M41T80_WDT is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
 # CONFIG_DMADEVICES is not set
 # CONFIG_UIO is not set
 # CONFIG_STAGING is not set
diff --git a/arch/xtensa/include/asm/cacheflush.h b/arch/xtensa/include/asm/cacheflush.h
index 8fc1c0c..b7b8fbe 100644
--- a/arch/xtensa/include/asm/cacheflush.h
+++ b/arch/xtensa/include/asm/cacheflush.h
@@ -155,5 +155,100 @@
 
 #endif
 
+#define XTENSA_CACHEBLK_LOG2	29
+#define XTENSA_CACHEBLK_SIZE	(1 << XTENSA_CACHEBLK_LOG2)
+#define XTENSA_CACHEBLK_MASK	(7 << XTENSA_CACHEBLK_LOG2)
+
+#if XCHAL_HAVE_CACHEATTR
+static inline u32 xtensa_get_cacheattr(void)
+{
+	u32 r;
+	asm volatile("	rsr %0, CACHEATTR" : "=a"(r));
+	return r;
+}
+
+static inline u32 xtensa_get_dtlb1(u32 addr)
+{
+	u32 r = addr & XTENSA_CACHEBLK_MASK;
+	return r | ((xtensa_get_cacheattr() >> (r >> (XTENSA_CACHEBLK_LOG2-2)))
+			& 0xF);
+}
+#else
+static inline u32 xtensa_get_dtlb1(u32 addr)
+{
+	u32 r;
+	asm volatile("	rdtlb1 %0, %1" : "=a"(r) : "a"(addr));
+	asm volatile("	dsync");
+	return r;
+}
+
+static inline u32 xtensa_get_cacheattr(void)
+{
+	u32 r = 0;
+	u32 a = 0;
+	do {
+		a -= XTENSA_CACHEBLK_SIZE;
+		r = (r << 4) | (xtensa_get_dtlb1(a) & 0xF);
+	} while (a);
+	return r;
+}
+#endif
+
+static inline int xtensa_need_flush_dma_source(u32 addr)
+{
+	return (xtensa_get_dtlb1(addr) & ((1 << XCHAL_CA_BITS) - 1)) >= 4;
+}
+
+static inline int xtensa_need_invalidate_dma_destination(u32 addr)
+{
+	return (xtensa_get_dtlb1(addr) & ((1 << XCHAL_CA_BITS) - 1)) != 2;
+}
+
+static inline void flush_dcache_unaligned(u32 addr, u32 size)
+{
+	u32 cnt;
+	if (size) {
+		cnt = (size + ((XCHAL_DCACHE_LINESIZE - 1) & addr)
+			+ XCHAL_DCACHE_LINESIZE - 1) / XCHAL_DCACHE_LINESIZE;
+		while (cnt--) {
+			asm volatile("	dhwb %0, 0" : : "a"(addr));
+			addr += XCHAL_DCACHE_LINESIZE;
+		}
+		asm volatile("	dsync");
+	}
+}
+
+static inline void invalidate_dcache_unaligned(u32 addr, u32 size)
+{
+	int cnt;
+	if (size) {
+		asm volatile("	dhwbi %0, 0 ;" : : "a"(addr));
+		cnt = (size + ((XCHAL_DCACHE_LINESIZE - 1) & addr)
+			- XCHAL_DCACHE_LINESIZE - 1) / XCHAL_DCACHE_LINESIZE;
+		while (cnt-- > 0) {
+			asm volatile("	dhi %0, %1" : : "a"(addr),
+						"n"(XCHAL_DCACHE_LINESIZE));
+			addr += XCHAL_DCACHE_LINESIZE;
+		}
+		asm volatile("	dhwbi %0, %1" : : "a"(addr),
+						"n"(XCHAL_DCACHE_LINESIZE));
+		asm volatile("	dsync");
+	}
+}
+
+static inline void flush_invalidate_dcache_unaligned(u32 addr, u32 size)
+{
+	u32 cnt;
+	if (size) {
+		cnt = (size + ((XCHAL_DCACHE_LINESIZE - 1) & addr)
+			+ XCHAL_DCACHE_LINESIZE - 1) / XCHAL_DCACHE_LINESIZE;
+		while (cnt--) {
+			asm volatile("	dhwbi %0, 0" : : "a"(addr));
+			addr += XCHAL_DCACHE_LINESIZE;
+		}
+		asm volatile("	dsync");
+	}
+}
+
 #endif /* __KERNEL__ */
 #endif /* _XTENSA_CACHEFLUSH_H */
diff --git a/arch/xtensa/include/asm/gpio.h b/arch/xtensa/include/asm/gpio.h
index 0763b07..a8c9fc4 100644
--- a/arch/xtensa/include/asm/gpio.h
+++ b/arch/xtensa/include/asm/gpio.h
@@ -38,14 +38,14 @@
 	return __gpio_cansleep(gpio);
 }
 
+static inline int gpio_to_irq(unsigned int gpio)
+{
+	return __gpio_to_irq(gpio);
+}
+
 /*
  * Not implemented, yet.
  */
-static inline int gpio_to_irq(unsigned int gpio)
-{
-	return -ENOSYS;
-}
-
 static inline int irq_to_gpio(unsigned int irq)
 {
 	return -EINVAL;
diff --git a/arch/xtensa/include/asm/irq.h b/arch/xtensa/include/asm/irq.h
index dfac82d..4c0ccc9 100644
--- a/arch/xtensa/include/asm/irq.h
+++ b/arch/xtensa/include/asm/irq.h
@@ -11,6 +11,7 @@
 #ifndef _XTENSA_IRQ_H
 #define _XTENSA_IRQ_H
 
+#include <linux/init.h>
 #include <platform/hardware.h>
 #include <variant/core.h>
 
@@ -21,11 +22,20 @@
 static inline void variant_irq_disable(unsigned int irq) { }
 #endif
 
+#ifndef VARIANT_NR_IRQS
+# define VARIANT_NR_IRQS 0
+#endif
 #ifndef PLATFORM_NR_IRQS
 # define PLATFORM_NR_IRQS 0
 #endif
 #define XTENSA_NR_IRQS XCHAL_NUM_INTERRUPTS
-#define NR_IRQS (XTENSA_NR_IRQS + PLATFORM_NR_IRQS)
+#define NR_IRQS (XTENSA_NR_IRQS + VARIANT_NR_IRQS + PLATFORM_NR_IRQS)
+
+#if VARIANT_NR_IRQS == 0
+static inline void variant_init_irq(void) { }
+#else
+void variant_init_irq(void) __init;
+#endif
 
 static __inline__ int irq_canonicalize(int irq)
 {
diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c
index a36c85e..a1badb3 100644
--- a/arch/xtensa/kernel/irq.c
+++ b/arch/xtensa/kernel/irq.c
@@ -197,4 +197,6 @@
 	}
 
 	cached_irq_mask = 0;
+
+	variant_init_irq();
 }
diff --git a/arch/xtensa/platforms/s6105/device.c b/arch/xtensa/platforms/s6105/device.c
index 78b08be..65333ff 100644
--- a/arch/xtensa/platforms/s6105/device.c
+++ b/arch/xtensa/platforms/s6105/device.c
@@ -5,14 +5,27 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/gpio.h>
 #include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/phy.h>
 #include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_8250.h>
 
 #include <variant/hardware.h>
+#include <variant/dmac.h>
 
+#include <platform/gpio.h>
+
+#define GPIO3_INTNUM		3
 #define UART_INTNUM		4
+#define GMAC_INTNUM		5
+
+static const signed char gpio3_irq_mappings[] = {
+	S6_INTC_GPIO(3),
+	-1
+};
 
 static const signed char uart_irq_mappings[] = {
 	S6_INTC_UART(0),
@@ -20,8 +33,18 @@
 	-1,
 };
 
+static const signed char gmac_irq_mappings[] = {
+	S6_INTC_GMAC_STAT,
+	S6_INTC_GMAC_ERR,
+	S6_INTC_DMA_HOSTTERMCNT(0),
+	S6_INTC_DMA_HOSTTERMCNT(1),
+	-1
+};
+
 const signed char *platform_irq_mappings[NR_IRQS] = {
+	[GPIO3_INTNUM] = gpio3_irq_mappings,
 	[UART_INTNUM] = uart_irq_mappings,
+	[GMAC_INTNUM] = gmac_irq_mappings,
 };
 
 static struct plat_serial8250_port serial_platform_data[] = {
@@ -46,6 +69,66 @@
 	{ },
 };
 
+static struct resource s6_gmac_resource[] = {
+	{
+		.name   = "mem",
+		.start  = (resource_size_t)S6_REG_GMAC,
+		.end    = (resource_size_t)S6_REG_GMAC + 0x10000 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	{
+		.name   = "dma",
+		.start  = (resource_size_t)
+			DMA_CHNL(S6_REG_HIFDMA, S6_HIFDMA_GMACTX),
+		.end    = (resource_size_t)
+			DMA_CHNL(S6_REG_HIFDMA, S6_HIFDMA_GMACTX) + 0x100 - 1,
+		.flags  = IORESOURCE_DMA,
+	},
+	{
+		.name   = "dma",
+		.start  = (resource_size_t)
+			DMA_CHNL(S6_REG_HIFDMA, S6_HIFDMA_GMACRX),
+		.end    = (resource_size_t)
+			DMA_CHNL(S6_REG_HIFDMA, S6_HIFDMA_GMACRX) + 0x100 - 1,
+		.flags  = IORESOURCE_DMA,
+	},
+	{
+		.name   = "io",
+		.start  = (resource_size_t)S6_MEM_GMAC,
+		.end    = (resource_size_t)S6_MEM_GMAC + 0x2000000 - 1,
+		.flags  = IORESOURCE_IO,
+	},
+	{
+		.name   = "irq",
+		.start  = (resource_size_t)GMAC_INTNUM,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.name   = "irq",
+		.start  = (resource_size_t)PHY_POLL,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static int __init prepare_phy_irq(int pin)
+{
+	int irq;
+	if (gpio_request(pin, "s6gmac_phy") < 0)
+		goto fail;
+	if (gpio_direction_input(pin) < 0)
+		goto free;
+	irq = gpio_to_irq(pin);
+	if (irq < 0)
+		goto free;
+	if (set_irq_type(irq, IRQ_TYPE_LEVEL_LOW) < 0)
+		goto free;
+	return irq;
+free:
+	gpio_free(pin);
+fail:
+	return PHY_POLL;
+}
+
 static struct platform_device platform_devices[] = {
 	{
 		.name = "serial8250",
@@ -54,12 +137,23 @@
 			.platform_data = serial_platform_data,
 		},
 	},
+	{
+		.name = "s6gmac",
+		.id = 0,
+		.resource = s6_gmac_resource,
+		.num_resources = ARRAY_SIZE(s6_gmac_resource),
+	},
+	{
+		I2C_BOARD_INFO("m41t62", S6I2C_ADDR_M41T62),
+	},
 };
 
 static int __init device_init(void)
 {
 	int i;
 
+	s6_gmac_resource[5].start = prepare_phy_irq(GPIO_PHY_IRQ);
+
 	for (i = 0; i < ARRAY_SIZE(platform_devices); i++)
 		platform_device_register(&platform_devices[i]);
 	return 0;
diff --git a/arch/xtensa/platforms/s6105/setup.c b/arch/xtensa/platforms/s6105/setup.c
index 855ddea..86ce730 100644
--- a/arch/xtensa/platforms/s6105/setup.c
+++ b/arch/xtensa/platforms/s6105/setup.c
@@ -35,12 +35,21 @@
 {
 	unsigned long reg;
 
+	reg = readl(S6_REG_GREG1 + S6_GREG1_PLLSEL);
+	reg &= ~(S6_GREG1_PLLSEL_GMAC_MASK << S6_GREG1_PLLSEL_GMAC |
+		S6_GREG1_PLLSEL_GMII_MASK << S6_GREG1_PLLSEL_GMII);
+	reg |= S6_GREG1_PLLSEL_GMAC_125MHZ << S6_GREG1_PLLSEL_GMAC |
+		S6_GREG1_PLLSEL_GMII_125MHZ << S6_GREG1_PLLSEL_GMII;
+	writel(reg, S6_REG_GREG1 + S6_GREG1_PLLSEL);
+
 	reg = readl(S6_REG_GREG1 + S6_GREG1_CLKGATE);
 	reg &= ~(1 << S6_GREG1_BLOCK_SB);
+	reg &= ~(1 << S6_GREG1_BLOCK_GMAC);
 	writel(reg, S6_REG_GREG1 + S6_GREG1_CLKGATE);
 
 	reg = readl(S6_REG_GREG1 + S6_GREG1_BLOCKENA);
 	reg |= 1 << S6_GREG1_BLOCK_SB;
+	reg |= 1 << S6_GREG1_BLOCK_GMAC;
 	writel(reg, S6_REG_GREG1 + S6_GREG1_BLOCKENA);
 
 	printk(KERN_NOTICE "S6105 on Stretch S6000 - "
@@ -49,7 +58,7 @@
 
 void __init platform_init(bp_tag_t *first)
 {
-	s6_gpio_init();
+	s6_gpio_init(0);
 	gpio_request(GPIO_LED1_NGREEN, "led1_green");
 	gpio_request(GPIO_LED1_RED, "led1_red");
 	gpio_direction_output(GPIO_LED1_NGREEN, 1);
diff --git a/arch/xtensa/variants/s6000/Makefile b/arch/xtensa/variants/s6000/Makefile
index d83f380..3e7ef0a 100644
--- a/arch/xtensa/variants/s6000/Makefile
+++ b/arch/xtensa/variants/s6000/Makefile
@@ -1,4 +1,4 @@
 # s6000 Makefile
 
-obj-y		+= irq.o gpio.o
+obj-y		+= irq.o gpio.o dmac.o
 obj-$(CONFIG_XTENSA_CALIBRATE_CCOUNT)	+= delay.o
diff --git a/arch/xtensa/variants/s6000/dmac.c b/arch/xtensa/variants/s6000/dmac.c
new file mode 100644
index 0000000..dc7f7c5
--- /dev/null
+++ b/arch/xtensa/variants/s6000/dmac.c
@@ -0,0 +1,173 @@
+/*
+ * Authors:	Oskar Schirmer <os@emlix.com>
+ *		Daniel Gloeckner <dg@emlix.com>
+ * (c) 2008 emlix GmbH http://www.emlix.com
+ *
+ * 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/kernel.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <asm/cacheflush.h>
+#include <variant/dmac.h>
+
+/* DMA engine lookup */
+
+struct s6dmac_ctrl s6dmac_ctrl[S6_DMAC_NB];
+
+
+/* DMA control, per engine */
+
+void s6dmac_put_fifo_cache(u32 dmac, int chan, u32 src, u32 dst, u32 size)
+{
+	if (xtensa_need_flush_dma_source(src)) {
+		u32 base = src;
+		u32 span = size;
+		u32 chunk = readl(DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK);
+		if (chunk && (size > chunk)) {
+			s32 skip =
+				readl(DMA_CHNL(dmac, chan) + S6_DMA_SRCSKIP);
+			u32 gaps = (size+chunk-1)/chunk - 1;
+			if (skip >= 0) {
+				span += gaps * skip;
+			} else if (-skip > chunk) {
+				s32 decr = gaps * (chunk + skip);
+				base += decr;
+				span = chunk - decr;
+			} else {
+				span = max(span + gaps * skip,
+					(chunk + skip) * gaps - skip);
+			}
+		}
+		flush_dcache_unaligned(base, span);
+	}
+	if (xtensa_need_invalidate_dma_destination(dst)) {
+		u32 base = dst;
+		u32 span = size;
+		u32 chunk = readl(DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK);
+		if (chunk && (size > chunk)) {
+			s32 skip =
+				readl(DMA_CHNL(dmac, chan) + S6_DMA_DSTSKIP);
+			u32 gaps = (size+chunk-1)/chunk - 1;
+			if (skip >= 0) {
+				span += gaps * skip;
+			} else if (-skip > chunk) {
+				s32 decr = gaps * (chunk + skip);
+				base += decr;
+				span = chunk - decr;
+			} else {
+				span = max(span + gaps * skip,
+					(chunk + skip) * gaps - skip);
+			}
+		}
+		invalidate_dcache_unaligned(base, span);
+	}
+	s6dmac_put_fifo(dmac, chan, src, dst, size);
+}
+
+void s6dmac_disable_error_irqs(u32 dmac, u32 mask)
+{
+	unsigned long flags;
+	spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock;
+	spin_lock_irqsave(spinl, flags);
+	_s6dmac_disable_error_irqs(dmac, mask);
+	spin_unlock_irqrestore(spinl, flags);
+}
+
+u32 s6dmac_int_sources(u32 dmac, u32 channel)
+{
+	u32 mask, ret, tmp;
+	mask = 1 << channel;
+
+	tmp = readl(dmac + S6_DMA_TERMCNTIRQSTAT);
+	tmp &= mask;
+	writel(tmp, dmac + S6_DMA_TERMCNTIRQCLR);
+	ret = tmp >> channel;
+
+	tmp = readl(dmac + S6_DMA_PENDCNTIRQSTAT);
+	tmp &= mask;
+	writel(tmp, dmac + S6_DMA_PENDCNTIRQCLR);
+	ret |= (tmp >> channel) << 1;
+
+	tmp = readl(dmac + S6_DMA_LOWWMRKIRQSTAT);
+	tmp &= mask;
+	writel(tmp, dmac + S6_DMA_LOWWMRKIRQCLR);
+	ret |= (tmp >> channel) << 2;
+
+	tmp = readl(dmac + S6_DMA_INTRAW0);
+	tmp &= (mask << S6_DMA_INT0_OVER) | (mask << S6_DMA_INT0_UNDER);
+	writel(tmp, dmac + S6_DMA_INTCLEAR0);
+
+	if (tmp & (mask << S6_DMA_INT0_UNDER))
+		ret |= 1 << 3;
+	if (tmp & (mask << S6_DMA_INT0_OVER))
+		ret |= 1 << 4;
+
+	tmp = readl(dmac + S6_DMA_MASTERERRINFO);
+	mask <<= S6_DMA_INT1_CHANNEL;
+	if (((tmp >> S6_DMA_MASTERERR_CHAN(0)) & S6_DMA_MASTERERR_CHAN_MASK)
+			== channel)
+		mask |= 1 << S6_DMA_INT1_MASTER;
+	if (((tmp >> S6_DMA_MASTERERR_CHAN(1)) & S6_DMA_MASTERERR_CHAN_MASK)
+			== channel)
+		mask |= 1 << (S6_DMA_INT1_MASTER + 1);
+	if (((tmp >> S6_DMA_MASTERERR_CHAN(2)) & S6_DMA_MASTERERR_CHAN_MASK)
+			== channel)
+		mask |= 1 << (S6_DMA_INT1_MASTER + 2);
+
+	tmp = readl(dmac + S6_DMA_INTRAW1) & mask;
+	writel(tmp, dmac + S6_DMA_INTCLEAR1);
+	ret |= ((tmp >> channel) & 1) << 5;
+	ret |= ((tmp >> S6_DMA_INT1_MASTER) & S6_DMA_INT1_MASTER_MASK) << 6;
+
+	return ret;
+}
+
+void s6dmac_release_chan(u32 dmac, int chan)
+{
+	if (chan >= 0)
+		s6dmac_disable_chan(dmac, chan);
+}
+
+
+/* global init */
+
+static inline void __init dmac_init(u32 dmac, u8 chan_nb)
+{
+	s6dmac_ctrl[S6_DMAC_INDEX(dmac)].dmac = dmac;
+	spin_lock_init(&s6dmac_ctrl[S6_DMAC_INDEX(dmac)].lock);
+	s6dmac_ctrl[S6_DMAC_INDEX(dmac)].chan_nb = chan_nb;
+	writel(S6_DMA_INT1_MASTER_MASK << S6_DMA_INT1_MASTER,
+		dmac + S6_DMA_INTCLEAR1);
+}
+
+static inline void __init dmac_master(u32 dmac,
+	u32 m0start, u32 m0end, u32 m1start, u32 m1end)
+{
+	writel(m0start, dmac + S6_DMA_MASTER0START);
+	writel(m0end - 1, dmac + S6_DMA_MASTER0END);
+	writel(m1start, dmac + S6_DMA_MASTER1START);
+	writel(m1end - 1, dmac + S6_DMA_MASTER1END);
+}
+
+static void __init s6_dmac_init(void)
+{
+	dmac_init(S6_REG_LMSDMA, S6_LMSDMA_NB);
+	dmac_master(S6_REG_LMSDMA,
+		S6_MEM_DDR, S6_MEM_PCIE_APER, S6_MEM_EFI, S6_MEM_GMAC);
+	dmac_init(S6_REG_NIDMA, S6_NIDMA_NB);
+	dmac_init(S6_REG_DPDMA, S6_DPDMA_NB);
+	dmac_master(S6_REG_DPDMA,
+		S6_MEM_DDR, S6_MEM_PCIE_APER, S6_REG_DP, S6_REG_DPDMA);
+	dmac_init(S6_REG_HIFDMA, S6_HIFDMA_NB);
+	dmac_master(S6_REG_HIFDMA,
+		S6_MEM_GMAC, S6_MEM_PCIE_CFG, S6_MEM_PCIE_APER, S6_MEM_AUX);
+}
+
+arch_initcall(s6_dmac_init);
diff --git a/arch/xtensa/variants/s6000/gpio.c b/arch/xtensa/variants/s6000/gpio.c
index 79317fd..380a70f 100644
--- a/arch/xtensa/variants/s6000/gpio.c
+++ b/arch/xtensa/variants/s6000/gpio.c
@@ -4,15 +4,20 @@
  * Copyright (c) 2009 emlix GmbH
  * Authors:	Oskar Schirmer <os@emlix.com>
  *		Johannes Weiner <jw@emlix.com>
+ *		Daniel Gloeckner <dg@emlix.com>
  */
+#include <linux/bitops.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/gpio.h>
 
 #include <variant/hardware.h>
 
+#define IRQ_BASE XTENSA_NR_IRQS
+
 #define S6_GPIO_DATA		0x000
 #define S6_GPIO_IS		0x404
 #define S6_GPIO_IBE		0x408
@@ -52,19 +57,175 @@
 	writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
 }
 
+static int to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset < 8)
+		return offset + IRQ_BASE;
+	return -EINVAL;
+}
+
 static struct gpio_chip gpiochip = {
 	.owner = THIS_MODULE,
 	.direction_input = direction_input,
 	.get = get,
 	.direction_output = direction_output,
 	.set = set,
+	.to_irq = to_irq,
 	.base = 0,
 	.ngpio = 24,
 	.can_sleep = 0, /* no blocking io needed */
 	.exported = 0, /* no exporting to userspace */
 };
 
-int s6_gpio_init(void)
+int s6_gpio_init(u32 afsel)
 {
+	writeb(afsel, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL);
+	writeb(afsel >> 8, S6_REG_GPIO + S6_GPIO_BANK(1) + S6_GPIO_AFSEL);
+	writeb(afsel >> 16, S6_REG_GPIO + S6_GPIO_BANK(2) + S6_GPIO_AFSEL);
 	return gpiochip_add(&gpiochip);
 }
+
+static void ack(unsigned int irq)
+{
+	writeb(1 << (irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
+}
+
+static void mask(unsigned int irq)
+{
+	u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
+	r &= ~(1 << (irq - IRQ_BASE));
+	writeb(r, S6_REG_GPIO + S6_GPIO_IE);
+}
+
+static void unmask(unsigned int irq)
+{
+	u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
+	m |= 1 << (irq - IRQ_BASE);
+	writeb(m, S6_REG_GPIO + S6_GPIO_IE);
+}
+
+static int set_type(unsigned int irq, unsigned int type)
+{
+	const u8 m = 1 << (irq - IRQ_BASE);
+	irq_flow_handler_t handler;
+	struct irq_desc *desc;
+	u8 reg;
+
+	if (type == IRQ_TYPE_PROBE) {
+		if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)
+		    || (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)
+		    || readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR
+			      + S6_GPIO_MASK(irq - IRQ_BASE)))
+			return 0;
+		type = IRQ_TYPE_EDGE_BOTH;
+	}
+
+	reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
+	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
+		reg |= m;
+		handler = handle_level_irq;
+	} else {
+		reg &= ~m;
+		handler = handle_edge_irq;
+	}
+	writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
+	desc = irq_to_desc(irq);
+	desc->handle_irq = handler;
+
+	reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
+	if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))
+		reg |= m;
+	else
+		reg &= ~m;
+	writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
+
+	reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
+	if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
+		reg |= m;
+	else
+		reg &= ~m;
+	writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
+	return 0;
+}
+
+static struct irq_chip gpioirqs = {
+	.name = "GPIO",
+	.ack = ack,
+	.mask = mask,
+	.unmask = unmask,
+	.set_type = set_type,
+};
+
+static u8 demux_masks[4];
+
+static void demux_irqs(unsigned int irq, struct irq_desc *desc)
+{
+	u8 *mask = get_irq_desc_data(desc);
+	u8 pending;
+	int cirq;
+
+	desc->chip->mask(irq);
+	desc->chip->ack(irq);
+	pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;
+	cirq = IRQ_BASE - 1;
+	while (pending) {
+		int n = ffs(pending);
+		cirq += n;
+		pending >>= n;
+		generic_handle_irq(cirq);
+	}
+	desc->chip->unmask(irq);
+}
+
+extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];
+
+void __init variant_init_irq(void)
+{
+	int irq, n;
+	writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);
+	for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {
+		const signed char *mapping = platform_irq_mappings[irq];
+		int alone = 1;
+		u8 mask;
+		if (!mapping)
+			continue;
+		for(mask = 0; *mapping != -1; mapping++)
+			switch (*mapping) {
+			case S6_INTC_GPIO(0):
+				mask |= 1 << 0;
+				break;
+			case S6_INTC_GPIO(1):
+				mask |= 1 << 1;
+				break;
+			case S6_INTC_GPIO(2):
+				mask |= 1 << 2;
+				break;
+			case S6_INTC_GPIO(3):
+				mask |= 0x1f << 3;
+				break;
+			default:
+				alone = 0;
+			}
+		if (mask) {
+			int cirq, i;
+			if (!alone) {
+				printk(KERN_ERR "chained irq chips can't share"
+					" parent irq %i\n", irq);
+				continue;
+			}
+			demux_masks[n] = mask;
+			cirq = IRQ_BASE - 1;
+			do {
+				i = ffs(mask);
+				cirq += i;
+				mask >>= i;
+				set_irq_chip(cirq, &gpioirqs);
+				set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+			} while (mask);
+			set_irq_data(irq, demux_masks + n);
+			set_irq_chained_handler(irq, demux_irqs);
+			if (++n == ARRAY_SIZE(demux_masks))
+				break;
+		}
+	}
+}
diff --git a/arch/xtensa/variants/s6000/include/variant/dmac.h b/arch/xtensa/variants/s6000/include/variant/dmac.h
new file mode 100644
index 0000000..89ab948
--- /dev/null
+++ b/arch/xtensa/variants/s6000/include/variant/dmac.h
@@ -0,0 +1,387 @@
+/*
+ * include/asm-xtensa/variant-s6000/dmac.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2006 Tensilica Inc.
+ * Copyright (C) 2008 Emlix GmbH <info@emlix.com>
+ * Authors:	Fabian Godehardt <fg@emlix.com>
+ *		Oskar Schirmer <os@emlix.com>
+ *		Daniel Gloeckner <dg@emlix.com>
+ */
+
+#ifndef __ASM_XTENSA_S6000_DMAC_H
+#define __ASM_XTENSA_S6000_DMAC_H
+#include <linux/io.h>
+#include <variant/hardware.h>
+
+/* DMA global */
+
+#define S6_DMA_INTSTAT0		0x000
+#define S6_DMA_INTSTAT1		0x004
+#define S6_DMA_INTENABLE0	0x008
+#define S6_DMA_INTENABLE1	0x00C
+#define S6_DMA_INTRAW0		0x010
+#define S6_DMA_INTRAW1		0x014
+#define S6_DMA_INTCLEAR0	0x018
+#define S6_DMA_INTCLEAR1	0x01C
+#define S6_DMA_INTSET0		0x020
+#define S6_DMA_INTSET1		0x024
+#define S6_DMA_INT0_UNDER		0
+#define S6_DMA_INT0_OVER		16
+#define S6_DMA_INT1_CHANNEL		0
+#define S6_DMA_INT1_MASTER		16
+#define S6_DMA_INT1_MASTER_MASK			7
+#define S6_DMA_TERMCNTIRQSTAT	0x028
+#define S6_DMA_TERMCNTIRQCLR	0x02C
+#define S6_DMA_TERMCNTIRQSET	0x030
+#define S6_DMA_PENDCNTIRQSTAT	0x034
+#define S6_DMA_PENDCNTIRQCLR	0x038
+#define S6_DMA_PENDCNTIRQSET	0x03C
+#define S6_DMA_LOWWMRKIRQSTAT	0x040
+#define S6_DMA_LOWWMRKIRQCLR	0x044
+#define S6_DMA_LOWWMRKIRQSET	0x048
+#define S6_DMA_MASTERERRINFO	0x04C
+#define S6_DMA_MASTERERR_CHAN(n)	(4*(n))
+#define S6_DMA_MASTERERR_CHAN_MASK		0xF
+#define S6_DMA_DESCRFIFO0	0x050
+#define S6_DMA_DESCRFIFO1	0x054
+#define S6_DMA_DESCRFIFO2	0x058
+#define S6_DMA_DESCRFIFO2_AUTODISABLE	24
+#define S6_DMA_DESCRFIFO3	0x05C
+#define S6_DMA_MASTER0START	0x060
+#define S6_DMA_MASTER0END	0x064
+#define S6_DMA_MASTER1START	0x068
+#define S6_DMA_MASTER1END	0x06C
+#define S6_DMA_NEXTFREE		0x070
+#define S6_DMA_NEXTFREE_CHAN		0
+#define S6_DMA_NEXTFREE_CHAN_MASK	0x1F
+#define S6_DMA_NEXTFREE_ENA		16
+#define S6_DMA_NEXTFREE_ENA_MASK	((1 << 16) - 1)
+#define S6_DMA_DPORTCTRLGRP(p)	((p) * 4 + 0x074)
+#define S6_DMA_DPORTCTRLGRP_FRAMEREP	0
+#define S6_DMA_DPORTCTRLGRP_NRCHANS	1
+#define S6_DMA_DPORTCTRLGRP_NRCHANS_1		0
+#define S6_DMA_DPORTCTRLGRP_NRCHANS_3		1
+#define S6_DMA_DPORTCTRLGRP_NRCHANS_4		2
+#define S6_DMA_DPORTCTRLGRP_NRCHANS_2		3
+#define S6_DMA_DPORTCTRLGRP_ENA		31
+
+
+/* DMA per channel */
+
+#define DMA_CHNL(dmac, n)	((dmac) + 0x1000 + (n) * 0x100)
+#define DMA_INDEX_CHNL(addr)	(((addr) >> 8) & 0xF)
+#define DMA_MASK_DMAC(addr)	((addr) & 0xFFFF0000)
+#define S6_DMA_CHNCTRL		0x000
+#define S6_DMA_CHNCTRL_ENABLE		0
+#define S6_DMA_CHNCTRL_PAUSE		1
+#define S6_DMA_CHNCTRL_PRIO		2
+#define S6_DMA_CHNCTRL_PRIO_MASK		3
+#define S6_DMA_CHNCTRL_PERIPHXFER	4
+#define S6_DMA_CHNCTRL_PERIPHENA	5
+#define S6_DMA_CHNCTRL_SRCINC		6
+#define S6_DMA_CHNCTRL_DSTINC		7
+#define S6_DMA_CHNCTRL_BURSTLOG		8
+#define S6_DMA_CHNCTRL_BURSTLOG_MASK		7
+#define S6_DMA_CHNCTRL_DESCFIFODEPTH	12
+#define S6_DMA_CHNCTRL_DESCFIFODEPTH_MASK	0x1F
+#define S6_DMA_CHNCTRL_DESCFIFOFULL	17
+#define S6_DMA_CHNCTRL_BWCONSEL		18
+#define S6_DMA_CHNCTRL_BWCONENA		19
+#define S6_DMA_CHNCTRL_PENDGCNTSTAT	20
+#define S6_DMA_CHNCTRL_PENDGCNTSTAT_MASK	0x3F
+#define S6_DMA_CHNCTRL_LOWWMARK		26
+#define S6_DMA_CHNCTRL_LOWWMARK_MASK		0xF
+#define S6_DMA_CHNCTRL_TSTAMP		30
+#define S6_DMA_TERMCNTNB	0x004
+#define S6_DMA_TERMCNTNB_MASK			0xFFFF
+#define S6_DMA_TERMCNTTMO	0x008
+#define S6_DMA_TERMCNTSTAT	0x00C
+#define S6_DMA_TERMCNTSTAT_MASK		0xFF
+#define S6_DMA_CMONCHUNK	0x010
+#define S6_DMA_SRCSKIP		0x014
+#define S6_DMA_DSTSKIP		0x018
+#define S6_DMA_CUR_SRC		0x024
+#define S6_DMA_CUR_DST		0x028
+#define S6_DMA_TIMESTAMP	0x030
+
+/* DMA channel lists */
+
+#define S6_DPDMA_CHAN(stream, channel)	(4 * (stream) + (channel))
+#define S6_DPDMA_NB	16
+
+#define S6_HIFDMA_GMACTX	0
+#define S6_HIFDMA_GMACRX	1
+#define S6_HIFDMA_I2S0		2
+#define S6_HIFDMA_I2S1		3
+#define S6_HIFDMA_EGIB		4
+#define S6_HIFDMA_PCITX		5
+#define S6_HIFDMA_PCIRX		6
+#define S6_HIFDMA_NB	7
+
+#define S6_NIDMA_NB	4
+
+#define S6_LMSDMA_NB	12
+
+/* controller access */
+
+#define S6_DMAC_NB	4
+#define S6_DMAC_INDEX(dmac)	(((unsigned)(dmac) >> 18) % S6_DMAC_NB)
+
+struct s6dmac_ctrl {
+	u32 dmac;
+	spinlock_t lock;
+	u8 chan_nb;
+};
+
+extern struct s6dmac_ctrl s6dmac_ctrl[S6_DMAC_NB];
+
+
+/* DMA control, per channel */
+
+static inline int s6dmac_fifo_full(u32 dmac, int chan)
+{
+	return (readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL)
+		& (1 << S6_DMA_CHNCTRL_DESCFIFOFULL)) && 1;
+}
+
+static inline int s6dmac_termcnt_irq(u32 dmac, int chan)
+{
+	u32 m = 1 << chan;
+	int r = (readl(dmac + S6_DMA_TERMCNTIRQSTAT) & m) && 1;
+	if (r)
+		writel(m, dmac + S6_DMA_TERMCNTIRQCLR);
+	return r;
+}
+
+static inline int s6dmac_pendcnt_irq(u32 dmac, int chan)
+{
+	u32 m = 1 << chan;
+	int r = (readl(dmac + S6_DMA_PENDCNTIRQSTAT) & m) && 1;
+	if (r)
+		writel(m, dmac + S6_DMA_PENDCNTIRQCLR);
+	return r;
+}
+
+static inline int s6dmac_lowwmark_irq(u32 dmac, int chan)
+{
+	int r = (readl(dmac + S6_DMA_LOWWMRKIRQSTAT) & (1 << chan)) ? 1 : 0;
+	if (r)
+		writel(1 << chan, dmac + S6_DMA_LOWWMRKIRQCLR);
+	return r;
+}
+
+static inline u32 s6dmac_pending_count(u32 dmac, int chan)
+{
+	return (readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL)
+			>> S6_DMA_CHNCTRL_PENDGCNTSTAT)
+		& S6_DMA_CHNCTRL_PENDGCNTSTAT_MASK;
+}
+
+static inline void s6dmac_set_terminal_count(u32 dmac, int chan, u32 n)
+{
+	n &= S6_DMA_TERMCNTNB_MASK;
+	n |= readl(DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB)
+	      & ~S6_DMA_TERMCNTNB_MASK;
+	writel(n, DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB);
+}
+
+static inline u32 s6dmac_get_terminal_count(u32 dmac, int chan)
+{
+	return (readl(DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB))
+		& S6_DMA_TERMCNTNB_MASK;
+}
+
+static inline u32 s6dmac_timestamp(u32 dmac, int chan)
+{
+	return readl(DMA_CHNL(dmac, chan) + S6_DMA_TIMESTAMP);
+}
+
+static inline u32 s6dmac_cur_src(u32 dmac, int chan)
+{
+	return readl(DMA_CHNL(dmac, chan) + S6_DMA_CUR_SRC);
+}
+
+static inline u32 s6dmac_cur_dst(u32 dmac, int chan)
+{
+	return readl(DMA_CHNL(dmac, chan) + S6_DMA_CUR_DST);
+}
+
+static inline void s6dmac_disable_chan(u32 dmac, int chan)
+{
+	u32 ctrl;
+	writel(readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL)
+		& ~(1 << S6_DMA_CHNCTRL_ENABLE),
+		DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
+	do
+		ctrl = readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
+	while (ctrl & (1 << S6_DMA_CHNCTRL_ENABLE));
+}
+
+static inline void s6dmac_set_stride_skip(u32 dmac, int chan,
+	int comchunk,		/* 0: disable scatter/gather */
+	int srcskip, int dstskip)
+{
+	writel(comchunk, DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK);
+	writel(srcskip, DMA_CHNL(dmac, chan) + S6_DMA_SRCSKIP);
+	writel(dstskip, DMA_CHNL(dmac, chan) + S6_DMA_DSTSKIP);
+}
+
+static inline void s6dmac_enable_chan(u32 dmac, int chan,
+	int prio,               /* 0 (highest) .. 3 (lowest) */
+	int periphxfer,         /* <0: disable p.req.line, 0..1: mode */
+	int srcinc, int dstinc, /* 0: dont increment src/dst address */
+	int comchunk,		/* 0: disable scatter/gather */
+	int srcskip, int dstskip,
+	int burstsize,		/* 4 for I2S, 7 for everything else */
+	int bandwidthconserve,  /* <0: disable, 0..1: select */
+	int lowwmark,           /* 0..15 */
+	int timestamp,		/* 0: disable timestamp */
+	int enable)		/* 0: disable for now */
+{
+	writel(1, DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB);
+	writel(0, DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTTMO);
+	writel(lowwmark << S6_DMA_CHNCTRL_LOWWMARK,
+		DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
+	s6dmac_set_stride_skip(dmac, chan, comchunk, srcskip, dstskip);
+	writel(((enable ? 1 : 0) << S6_DMA_CHNCTRL_ENABLE) |
+		(prio << S6_DMA_CHNCTRL_PRIO) |
+		(((periphxfer > 0) ? 1 : 0) << S6_DMA_CHNCTRL_PERIPHXFER) |
+		(((periphxfer < 0) ? 0 : 1) << S6_DMA_CHNCTRL_PERIPHENA) |
+		((srcinc ? 1 : 0) << S6_DMA_CHNCTRL_SRCINC) |
+		((dstinc ? 1 : 0) << S6_DMA_CHNCTRL_DSTINC) |
+		(burstsize << S6_DMA_CHNCTRL_BURSTLOG) |
+		(((bandwidthconserve > 0) ? 1 : 0) << S6_DMA_CHNCTRL_BWCONSEL) |
+		(((bandwidthconserve < 0) ? 0 : 1) << S6_DMA_CHNCTRL_BWCONENA) |
+		(lowwmark << S6_DMA_CHNCTRL_LOWWMARK) |
+		((timestamp ? 1 : 0) << S6_DMA_CHNCTRL_TSTAMP),
+		DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
+}
+
+
+/* DMA control, per engine */
+
+static inline unsigned _dmac_addr_index(u32 dmac)
+{
+	unsigned i = S6_DMAC_INDEX(dmac);
+	if (s6dmac_ctrl[i].dmac != dmac)
+		BUG();
+	return i;
+}
+
+static inline void _s6dmac_disable_error_irqs(u32 dmac, u32 mask)
+{
+	writel(mask, dmac + S6_DMA_TERMCNTIRQCLR);
+	writel(mask, dmac + S6_DMA_PENDCNTIRQCLR);
+	writel(mask, dmac + S6_DMA_LOWWMRKIRQCLR);
+	writel(readl(dmac + S6_DMA_INTENABLE0)
+		& ~((mask << S6_DMA_INT0_UNDER) | (mask << S6_DMA_INT0_OVER)),
+		dmac + S6_DMA_INTENABLE0);
+	writel(readl(dmac + S6_DMA_INTENABLE1) & ~(mask << S6_DMA_INT1_CHANNEL),
+		dmac + S6_DMA_INTENABLE1);
+	writel((mask << S6_DMA_INT0_UNDER) | (mask << S6_DMA_INT0_OVER),
+		dmac + S6_DMA_INTCLEAR0);
+	writel(mask << S6_DMA_INT1_CHANNEL, dmac + S6_DMA_INTCLEAR1);
+}
+
+/*
+ * request channel from specified engine
+ * with chan<0, accept any channel
+ * further parameters see s6dmac_enable_chan
+ * returns < 0 upon error, channel nb otherwise
+ */
+static inline int s6dmac_request_chan(u32 dmac, int chan,
+	int prio,
+	int periphxfer,
+	int srcinc, int dstinc,
+	int comchunk,
+	int srcskip, int dstskip,
+	int burstsize,
+	int bandwidthconserve,
+	int lowwmark,
+	int timestamp,
+	int enable)
+{
+	int r = chan;
+	unsigned long flags;
+	spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock;
+	spin_lock_irqsave(spinl, flags);
+	if (r < 0) {
+		r = (readl(dmac + S6_DMA_NEXTFREE) >> S6_DMA_NEXTFREE_CHAN)
+			& S6_DMA_NEXTFREE_CHAN_MASK;
+	}
+	if (r >= s6dmac_ctrl[_dmac_addr_index(dmac)].chan_nb) {
+		if (chan < 0)
+			r = -EBUSY;
+		else
+			r = -ENXIO;
+	} else if (((readl(dmac + S6_DMA_NEXTFREE) >> S6_DMA_NEXTFREE_ENA)
+			>> r) & 1) {
+		r = -EBUSY;
+	} else {
+		s6dmac_enable_chan(dmac, r, prio, periphxfer,
+			srcinc, dstinc, comchunk, srcskip, dstskip, burstsize,
+			bandwidthconserve, lowwmark, timestamp, enable);
+	}
+	spin_unlock_irqrestore(spinl, flags);
+	return r;
+}
+
+static inline void s6dmac_put_fifo(u32 dmac, int chan,
+	u32 src, u32 dst, u32 size)
+{
+	unsigned long flags;
+	spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock;
+	spin_lock_irqsave(spinl, flags);
+	writel(src, dmac + S6_DMA_DESCRFIFO0);
+	writel(dst, dmac + S6_DMA_DESCRFIFO1);
+	writel(size, dmac + S6_DMA_DESCRFIFO2);
+	writel(chan, dmac + S6_DMA_DESCRFIFO3);
+	spin_unlock_irqrestore(spinl, flags);
+}
+
+static inline u32 s6dmac_channel_enabled(u32 dmac, int chan)
+{
+	return readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL) &
+			(1 << S6_DMA_CHNCTRL_ENABLE);
+}
+
+/*
+ * group 1-4 data port channels
+ * with port=0..3, nrch=1-4 channels,
+ * frrep=0/1 (dis- or enable frame repeat)
+ */
+static inline void s6dmac_dp_setup_group(u32 dmac, int port,
+			int nrch, int frrep)
+{
+	const static u8 mask[4] = {0, 3, 1, 2};
+	BUG_ON(dmac != S6_REG_DPDMA);
+	if ((port < 0) || (port > 3) || (nrch < 1) || (nrch > 4))
+		return;
+	writel((mask[nrch - 1] << S6_DMA_DPORTCTRLGRP_NRCHANS)
+		| ((frrep ? 1 : 0) << S6_DMA_DPORTCTRLGRP_FRAMEREP),
+		dmac + S6_DMA_DPORTCTRLGRP(port));
+}
+
+static inline void s6dmac_dp_switch_group(u32 dmac, int port, int enable)
+{
+	u32 tmp;
+	BUG_ON(dmac != S6_REG_DPDMA);
+	tmp = readl(dmac + S6_DMA_DPORTCTRLGRP(port));
+	if (enable)
+		tmp |= (1 << S6_DMA_DPORTCTRLGRP_ENA);
+	else
+		tmp &= ~(1 << S6_DMA_DPORTCTRLGRP_ENA);
+	writel(tmp, dmac + S6_DMA_DPORTCTRLGRP(port));
+}
+
+extern void s6dmac_put_fifo_cache(u32 dmac, int chan,
+	u32 src, u32 dst, u32 size);
+extern void s6dmac_disable_error_irqs(u32 dmac, u32 mask);
+extern u32 s6dmac_int_sources(u32 dmac, u32 channel);
+extern void s6dmac_release_chan(u32 dmac, int chan);
+
+#endif /* __ASM_XTENSA_S6000_DMAC_H */
diff --git a/arch/xtensa/variants/s6000/include/variant/gpio.h b/arch/xtensa/variants/s6000/include/variant/gpio.h
index 8327f62..8484ab0 100644
--- a/arch/xtensa/variants/s6000/include/variant/gpio.h
+++ b/arch/xtensa/variants/s6000/include/variant/gpio.h
@@ -1,6 +1,6 @@
 #ifndef _XTENSA_VARIANT_S6000_GPIO_H
 #define _XTENSA_VARIANT_S6000_GPIO_H
 
-extern int s6_gpio_init(void);
+extern int s6_gpio_init(u32 afsel);
 
 #endif /* _XTENSA_VARIANT_S6000_GPIO_H */
diff --git a/arch/xtensa/variants/s6000/include/variant/irq.h b/arch/xtensa/variants/s6000/include/variant/irq.h
index fa031cb..97d6fc4 100644
--- a/arch/xtensa/variants/s6000/include/variant/irq.h
+++ b/arch/xtensa/variants/s6000/include/variant/irq.h
@@ -1,9 +1,9 @@
-#ifndef __XTENSA_S6000_IRQ_H
-#define __XTENSA_S6000_IRQ_H
+#ifndef _XTENSA_S6000_IRQ_H
+#define _XTENSA_S6000_IRQ_H
 
 #define NO_IRQ		(-1)
+#define VARIANT_NR_IRQS 8 /* GPIO interrupts */
 
 extern void variant_irq_enable(unsigned int irq);
-extern void variant_irq_disable(unsigned int irq);
 
 #endif /* __XTENSA_S6000_IRQ_H */
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 892a9e4..1dc7215 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2443,6 +2443,17 @@
 	  To compile this driver as a module, choose M here. The module
 	  will be called jme.
 
+config S6GMAC
+	tristate "S6105 GMAC ethernet support"
+	depends on XTENSA_VARIANT_S6000
+	select PHYLIB
+	help
+	  This driver supports the on chip ethernet device on the
+	  S6105 xtensa processor.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called s6gmac.
+
 endif # NETDEV_1000
 
 #
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index d366fb2..4b58a59 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -245,6 +245,7 @@
 
 obj-$(CONFIG_DNET) += dnet.o
 obj-$(CONFIG_MACB) += macb.o
+obj-$(CONFIG_S6GMAC) += s6gmac.o
 
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_DEV_APPLETALK) += appletalk/
diff --git a/drivers/net/s6gmac.c b/drivers/net/s6gmac.c
new file mode 100644
index 0000000..5345e47
--- /dev/null
+++ b/drivers/net/s6gmac.c
@@ -0,0 +1,1073 @@
+/*
+ * Ethernet driver for S6105 on chip network device
+ * (c)2008 emlix GmbH http://www.emlix.com
+ * Authors:	Oskar Schirmer <os@emlix.com>
+ *		Daniel Gloeckner <dg@emlix.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if.h>
+#include <linux/stddef.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <variant/hardware.h>
+#include <variant/dmac.h>
+
+#define DRV_NAME "s6gmac"
+#define DRV_PRMT DRV_NAME ": "
+
+
+/* register declarations */
+
+#define S6_GMAC_MACCONF1	0x000
+#define S6_GMAC_MACCONF1_TXENA		0
+#define S6_GMAC_MACCONF1_SYNCTX		1
+#define S6_GMAC_MACCONF1_RXENA		2
+#define S6_GMAC_MACCONF1_SYNCRX		3
+#define S6_GMAC_MACCONF1_TXFLOWCTRL	4
+#define S6_GMAC_MACCONF1_RXFLOWCTRL	5
+#define S6_GMAC_MACCONF1_LOOPBACK	8
+#define S6_GMAC_MACCONF1_RESTXFUNC	16
+#define S6_GMAC_MACCONF1_RESRXFUNC	17
+#define S6_GMAC_MACCONF1_RESTXMACCTRL	18
+#define S6_GMAC_MACCONF1_RESRXMACCTRL	19
+#define S6_GMAC_MACCONF1_SIMULRES	30
+#define S6_GMAC_MACCONF1_SOFTRES	31
+#define S6_GMAC_MACCONF2	0x004
+#define S6_GMAC_MACCONF2_FULL		0
+#define S6_GMAC_MACCONF2_CRCENA		1
+#define S6_GMAC_MACCONF2_PADCRCENA	2
+#define S6_GMAC_MACCONF2_LENGTHFCHK	4
+#define S6_GMAC_MACCONF2_HUGEFRAMENA	5
+#define S6_GMAC_MACCONF2_IFMODE		8
+#define S6_GMAC_MACCONF2_IFMODE_NIBBLE		1
+#define S6_GMAC_MACCONF2_IFMODE_BYTE		2
+#define S6_GMAC_MACCONF2_IFMODE_MASK		3
+#define S6_GMAC_MACCONF2_PREAMBLELEN	12
+#define S6_GMAC_MACCONF2_PREAMBLELEN_MASK	0x0F
+#define S6_GMAC_MACIPGIFG	0x008
+#define S6_GMAC_MACIPGIFG_B2BINTERPGAP	0
+#define S6_GMAC_MACIPGIFG_B2BINTERPGAP_MASK	0x7F
+#define S6_GMAC_MACIPGIFG_MINIFGENFORCE	8
+#define S6_GMAC_MACIPGIFG_B2BINTERPGAP2	16
+#define S6_GMAC_MACIPGIFG_B2BINTERPGAP1	24
+#define S6_GMAC_MACHALFDUPLEX	0x00C
+#define S6_GMAC_MACHALFDUPLEX_COLLISWIN	0
+#define S6_GMAC_MACHALFDUPLEX_COLLISWIN_MASK	0x3F
+#define S6_GMAC_MACHALFDUPLEX_RETXMAX	12
+#define S6_GMAC_MACHALFDUPLEX_RETXMAX_MASK	0x0F
+#define S6_GMAC_MACHALFDUPLEX_EXCESSDEF	16
+#define S6_GMAC_MACHALFDUPLEX_NOBACKOFF	17
+#define S6_GMAC_MACHALFDUPLEX_BPNOBCKOF	18
+#define S6_GMAC_MACHALFDUPLEX_ALTBEBENA	19
+#define S6_GMAC_MACHALFDUPLEX_ALTBEBTRN	20
+#define S6_GMAC_MACHALFDUPLEX_ALTBEBTR_MASK	0x0F
+#define S6_GMAC_MACMAXFRAMELEN	0x010
+#define S6_GMAC_MACMIICONF	0x020
+#define S6_GMAC_MACMIICONF_CSEL		0
+#define S6_GMAC_MACMIICONF_CSEL_DIV10		0
+#define S6_GMAC_MACMIICONF_CSEL_DIV12		1
+#define S6_GMAC_MACMIICONF_CSEL_DIV14		2
+#define S6_GMAC_MACMIICONF_CSEL_DIV18		3
+#define S6_GMAC_MACMIICONF_CSEL_DIV24		4
+#define S6_GMAC_MACMIICONF_CSEL_DIV34		5
+#define S6_GMAC_MACMIICONF_CSEL_DIV68		6
+#define S6_GMAC_MACMIICONF_CSEL_DIV168		7
+#define S6_GMAC_MACMIICONF_CSEL_MASK		7
+#define S6_GMAC_MACMIICONF_PREAMBLESUPR	4
+#define S6_GMAC_MACMIICONF_SCANAUTOINCR	5
+#define S6_GMAC_MACMIICMD	0x024
+#define S6_GMAC_MACMIICMD_READ		0
+#define S6_GMAC_MACMIICMD_SCAN		1
+#define S6_GMAC_MACMIIADDR	0x028
+#define S6_GMAC_MACMIIADDR_REG		0
+#define S6_GMAC_MACMIIADDR_REG_MASK		0x1F
+#define S6_GMAC_MACMIIADDR_PHY		8
+#define S6_GMAC_MACMIIADDR_PHY_MASK		0x1F
+#define S6_GMAC_MACMIICTRL	0x02C
+#define S6_GMAC_MACMIISTAT	0x030
+#define S6_GMAC_MACMIIINDI	0x034
+#define S6_GMAC_MACMIIINDI_BUSY		0
+#define S6_GMAC_MACMIIINDI_SCAN		1
+#define S6_GMAC_MACMIIINDI_INVAL	2
+#define S6_GMAC_MACINTERFSTAT	0x03C
+#define S6_GMAC_MACINTERFSTAT_LINKFAIL	3
+#define S6_GMAC_MACINTERFSTAT_EXCESSDEF	9
+#define S6_GMAC_MACSTATADDR1	0x040
+#define S6_GMAC_MACSTATADDR2	0x044
+
+#define S6_GMAC_FIFOCONF0	0x048
+#define S6_GMAC_FIFOCONF0_HSTRSTWT	0
+#define S6_GMAC_FIFOCONF0_HSTRSTSR	1
+#define S6_GMAC_FIFOCONF0_HSTRSTFR	2
+#define S6_GMAC_FIFOCONF0_HSTRSTST	3
+#define S6_GMAC_FIFOCONF0_HSTRSTFT	4
+#define S6_GMAC_FIFOCONF0_WTMENREQ	8
+#define S6_GMAC_FIFOCONF0_SRFENREQ	9
+#define S6_GMAC_FIFOCONF0_FRFENREQ	10
+#define S6_GMAC_FIFOCONF0_STFENREQ	11
+#define S6_GMAC_FIFOCONF0_FTFENREQ	12
+#define S6_GMAC_FIFOCONF0_WTMENRPLY	16
+#define S6_GMAC_FIFOCONF0_SRFENRPLY	17
+#define S6_GMAC_FIFOCONF0_FRFENRPLY	18
+#define S6_GMAC_FIFOCONF0_STFENRPLY	19
+#define S6_GMAC_FIFOCONF0_FTFENRPLY	20
+#define S6_GMAC_FIFOCONF1	0x04C
+#define S6_GMAC_FIFOCONF2	0x050
+#define S6_GMAC_FIFOCONF2_CFGLWM	0
+#define S6_GMAC_FIFOCONF2_CFGHWM	16
+#define S6_GMAC_FIFOCONF3	0x054
+#define S6_GMAC_FIFOCONF3_CFGFTTH	0
+#define S6_GMAC_FIFOCONF3_CFGHWMFT	16
+#define S6_GMAC_FIFOCONF4	0x058
+#define S6_GMAC_FIFOCONF_RSV_PREVDROP	0
+#define S6_GMAC_FIFOCONF_RSV_RUNT	1
+#define S6_GMAC_FIFOCONF_RSV_FALSECAR	2
+#define S6_GMAC_FIFOCONF_RSV_CODEERR	3
+#define S6_GMAC_FIFOCONF_RSV_CRCERR	4
+#define S6_GMAC_FIFOCONF_RSV_LENGTHERR	5
+#define S6_GMAC_FIFOCONF_RSV_LENRANGE	6
+#define S6_GMAC_FIFOCONF_RSV_OK		7
+#define S6_GMAC_FIFOCONF_RSV_MULTICAST	8
+#define S6_GMAC_FIFOCONF_RSV_BROADCAST	9
+#define S6_GMAC_FIFOCONF_RSV_DRIBBLE	10
+#define S6_GMAC_FIFOCONF_RSV_CTRLFRAME	11
+#define S6_GMAC_FIFOCONF_RSV_PAUSECTRL	12
+#define S6_GMAC_FIFOCONF_RSV_UNOPCODE	13
+#define S6_GMAC_FIFOCONF_RSV_VLANTAG	14
+#define S6_GMAC_FIFOCONF_RSV_LONGEVENT	15
+#define S6_GMAC_FIFOCONF_RSV_TRUNCATED	16
+#define S6_GMAC_FIFOCONF_RSV_MASK		0x3FFFF
+#define S6_GMAC_FIFOCONF5	0x05C
+#define S6_GMAC_FIFOCONF5_DROPLT64	18
+#define S6_GMAC_FIFOCONF5_CFGBYTM	19
+#define S6_GMAC_FIFOCONF5_RXDROPSIZE	20
+#define S6_GMAC_FIFOCONF5_RXDROPSIZE_MASK	0xF
+
+#define S6_GMAC_STAT_REGS	0x080
+#define S6_GMAC_STAT_SIZE_MIN		12
+#define S6_GMAC_STATTR64	0x080
+#define S6_GMAC_STATTR64_SIZE		18
+#define S6_GMAC_STATTR127	0x084
+#define S6_GMAC_STATTR127_SIZE		18
+#define S6_GMAC_STATTR255	0x088
+#define S6_GMAC_STATTR255_SIZE		18
+#define S6_GMAC_STATTR511	0x08C
+#define S6_GMAC_STATTR511_SIZE		18
+#define S6_GMAC_STATTR1K	0x090
+#define S6_GMAC_STATTR1K_SIZE		18
+#define S6_GMAC_STATTRMAX	0x094
+#define S6_GMAC_STATTRMAX_SIZE		18
+#define S6_GMAC_STATTRMGV	0x098
+#define S6_GMAC_STATTRMGV_SIZE		18
+#define S6_GMAC_STATRBYT	0x09C
+#define S6_GMAC_STATRBYT_SIZE		24
+#define S6_GMAC_STATRPKT	0x0A0
+#define S6_GMAC_STATRPKT_SIZE		18
+#define S6_GMAC_STATRFCS	0x0A4
+#define S6_GMAC_STATRFCS_SIZE		12
+#define S6_GMAC_STATRMCA	0x0A8
+#define S6_GMAC_STATRMCA_SIZE		18
+#define S6_GMAC_STATRBCA	0x0AC
+#define S6_GMAC_STATRBCA_SIZE		22
+#define S6_GMAC_STATRXCF	0x0B0
+#define S6_GMAC_STATRXCF_SIZE		18
+#define S6_GMAC_STATRXPF	0x0B4
+#define S6_GMAC_STATRXPF_SIZE		12
+#define S6_GMAC_STATRXUO	0x0B8
+#define S6_GMAC_STATRXUO_SIZE		12
+#define S6_GMAC_STATRALN	0x0BC
+#define S6_GMAC_STATRALN_SIZE		12
+#define S6_GMAC_STATRFLR	0x0C0
+#define S6_GMAC_STATRFLR_SIZE		16
+#define S6_GMAC_STATRCDE	0x0C4
+#define S6_GMAC_STATRCDE_SIZE		12
+#define S6_GMAC_STATRCSE	0x0C8
+#define S6_GMAC_STATRCSE_SIZE		12
+#define S6_GMAC_STATRUND	0x0CC
+#define S6_GMAC_STATRUND_SIZE		12
+#define S6_GMAC_STATROVR	0x0D0
+#define S6_GMAC_STATROVR_SIZE		12
+#define S6_GMAC_STATRFRG	0x0D4
+#define S6_GMAC_STATRFRG_SIZE		12
+#define S6_GMAC_STATRJBR	0x0D8
+#define S6_GMAC_STATRJBR_SIZE		12
+#define S6_GMAC_STATRDRP	0x0DC
+#define S6_GMAC_STATRDRP_SIZE		12
+#define S6_GMAC_STATTBYT	0x0E0
+#define S6_GMAC_STATTBYT_SIZE		24
+#define S6_GMAC_STATTPKT	0x0E4
+#define S6_GMAC_STATTPKT_SIZE		18
+#define S6_GMAC_STATTMCA	0x0E8
+#define S6_GMAC_STATTMCA_SIZE		18
+#define S6_GMAC_STATTBCA	0x0EC
+#define S6_GMAC_STATTBCA_SIZE		18
+#define S6_GMAC_STATTXPF	0x0F0
+#define S6_GMAC_STATTXPF_SIZE		12
+#define S6_GMAC_STATTDFR	0x0F4
+#define S6_GMAC_STATTDFR_SIZE		12
+#define S6_GMAC_STATTEDF	0x0F8
+#define S6_GMAC_STATTEDF_SIZE		12
+#define S6_GMAC_STATTSCL	0x0FC
+#define S6_GMAC_STATTSCL_SIZE		12
+#define S6_GMAC_STATTMCL	0x100
+#define S6_GMAC_STATTMCL_SIZE		12
+#define S6_GMAC_STATTLCL	0x104
+#define S6_GMAC_STATTLCL_SIZE		12
+#define S6_GMAC_STATTXCL	0x108
+#define S6_GMAC_STATTXCL_SIZE		12
+#define S6_GMAC_STATTNCL	0x10C
+#define S6_GMAC_STATTNCL_SIZE		13
+#define S6_GMAC_STATTPFH	0x110
+#define S6_GMAC_STATTPFH_SIZE		12
+#define S6_GMAC_STATTDRP	0x114
+#define S6_GMAC_STATTDRP_SIZE		12
+#define S6_GMAC_STATTJBR	0x118
+#define S6_GMAC_STATTJBR_SIZE		12
+#define S6_GMAC_STATTFCS	0x11C
+#define S6_GMAC_STATTFCS_SIZE		12
+#define S6_GMAC_STATTXCF	0x120
+#define S6_GMAC_STATTXCF_SIZE		12
+#define S6_GMAC_STATTOVR	0x124
+#define S6_GMAC_STATTOVR_SIZE		12
+#define S6_GMAC_STATTUND	0x128
+#define S6_GMAC_STATTUND_SIZE		12
+#define S6_GMAC_STATTFRG	0x12C
+#define S6_GMAC_STATTFRG_SIZE		12
+#define S6_GMAC_STATCARRY(n)	(0x130 + 4*(n))
+#define S6_GMAC_STATCARRYMSK(n)	(0x138 + 4*(n))
+#define S6_GMAC_STATCARRY1_RDRP		0
+#define S6_GMAC_STATCARRY1_RJBR		1
+#define S6_GMAC_STATCARRY1_RFRG		2
+#define S6_GMAC_STATCARRY1_ROVR		3
+#define S6_GMAC_STATCARRY1_RUND		4
+#define S6_GMAC_STATCARRY1_RCSE		5
+#define S6_GMAC_STATCARRY1_RCDE		6
+#define S6_GMAC_STATCARRY1_RFLR		7
+#define S6_GMAC_STATCARRY1_RALN		8
+#define S6_GMAC_STATCARRY1_RXUO		9
+#define S6_GMAC_STATCARRY1_RXPF		10
+#define S6_GMAC_STATCARRY1_RXCF		11
+#define S6_GMAC_STATCARRY1_RBCA		12
+#define S6_GMAC_STATCARRY1_RMCA		13
+#define S6_GMAC_STATCARRY1_RFCS		14
+#define S6_GMAC_STATCARRY1_RPKT		15
+#define S6_GMAC_STATCARRY1_RBYT		16
+#define S6_GMAC_STATCARRY1_TRMGV	25
+#define S6_GMAC_STATCARRY1_TRMAX	26
+#define S6_GMAC_STATCARRY1_TR1K		27
+#define S6_GMAC_STATCARRY1_TR511	28
+#define S6_GMAC_STATCARRY1_TR255	29
+#define S6_GMAC_STATCARRY1_TR127	30
+#define S6_GMAC_STATCARRY1_TR64		31
+#define S6_GMAC_STATCARRY2_TDRP		0
+#define S6_GMAC_STATCARRY2_TPFH		1
+#define S6_GMAC_STATCARRY2_TNCL		2
+#define S6_GMAC_STATCARRY2_TXCL		3
+#define S6_GMAC_STATCARRY2_TLCL		4
+#define S6_GMAC_STATCARRY2_TMCL		5
+#define S6_GMAC_STATCARRY2_TSCL		6
+#define S6_GMAC_STATCARRY2_TEDF		7
+#define S6_GMAC_STATCARRY2_TDFR		8
+#define S6_GMAC_STATCARRY2_TXPF		9
+#define S6_GMAC_STATCARRY2_TBCA		10
+#define S6_GMAC_STATCARRY2_TMCA		11
+#define S6_GMAC_STATCARRY2_TPKT		12
+#define S6_GMAC_STATCARRY2_TBYT		13
+#define S6_GMAC_STATCARRY2_TFRG		14
+#define S6_GMAC_STATCARRY2_TUND		15
+#define S6_GMAC_STATCARRY2_TOVR		16
+#define S6_GMAC_STATCARRY2_TXCF		17
+#define S6_GMAC_STATCARRY2_TFCS		18
+#define S6_GMAC_STATCARRY2_TJBR		19
+
+#define S6_GMAC_HOST_PBLKCTRL	0x140
+#define S6_GMAC_HOST_PBLKCTRL_TXENA	0
+#define S6_GMAC_HOST_PBLKCTRL_RXENA	1
+#define S6_GMAC_HOST_PBLKCTRL_TXSRES	2
+#define S6_GMAC_HOST_PBLKCTRL_RXSRES	3
+#define S6_GMAC_HOST_PBLKCTRL_TXBSIZ	8
+#define S6_GMAC_HOST_PBLKCTRL_RXBSIZ	12
+#define S6_GMAC_HOST_PBLKCTRL_SIZ_16		4
+#define S6_GMAC_HOST_PBLKCTRL_SIZ_32		5
+#define S6_GMAC_HOST_PBLKCTRL_SIZ_64		6
+#define S6_GMAC_HOST_PBLKCTRL_SIZ_128		7
+#define S6_GMAC_HOST_PBLKCTRL_SIZ_MASK		0xF
+#define S6_GMAC_HOST_PBLKCTRL_STATENA	16
+#define S6_GMAC_HOST_PBLKCTRL_STATAUTOZ	17
+#define S6_GMAC_HOST_PBLKCTRL_STATCLEAR	18
+#define S6_GMAC_HOST_PBLKCTRL_RGMII	19
+#define S6_GMAC_HOST_INTMASK	0x144
+#define S6_GMAC_HOST_INTSTAT	0x148
+#define S6_GMAC_HOST_INT_TXBURSTOVER	3
+#define S6_GMAC_HOST_INT_TXPREWOVER	4
+#define S6_GMAC_HOST_INT_RXBURSTUNDER	5
+#define S6_GMAC_HOST_INT_RXPOSTRFULL	6
+#define S6_GMAC_HOST_INT_RXPOSTRUNDER	7
+#define S6_GMAC_HOST_RXFIFOHWM	0x14C
+#define S6_GMAC_HOST_CTRLFRAMXP	0x150
+#define S6_GMAC_HOST_DSTADDRLO(n) (0x160 + 8*(n))
+#define S6_GMAC_HOST_DSTADDRHI(n) (0x164 + 8*(n))
+#define S6_GMAC_HOST_DSTMASKLO(n) (0x180 + 8*(n))
+#define S6_GMAC_HOST_DSTMASKHI(n) (0x184 + 8*(n))
+
+#define S6_GMAC_BURST_PREWR	0x1B0
+#define S6_GMAC_BURST_PREWR_LEN		0
+#define S6_GMAC_BURST_PREWR_LEN_MASK		((1 << 20) - 1)
+#define S6_GMAC_BURST_PREWR_CFE		20
+#define S6_GMAC_BURST_PREWR_PPE		21
+#define S6_GMAC_BURST_PREWR_FCS		22
+#define S6_GMAC_BURST_PREWR_PAD		23
+#define S6_GMAC_BURST_POSTRD	0x1D0
+#define S6_GMAC_BURST_POSTRD_LEN	0
+#define S6_GMAC_BURST_POSTRD_LEN_MASK		((1 << 20) - 1)
+#define S6_GMAC_BURST_POSTRD_DROP	20
+
+
+/* data handling */
+
+#define S6_NUM_TX_SKB	8	/* must be larger than TX fifo size */
+#define S6_NUM_RX_SKB	16
+#define S6_MAX_FRLEN	1536
+
+struct s6gmac {
+	u32 reg;
+	u32 tx_dma;
+	u32 rx_dma;
+	u32 io;
+	u8 tx_chan;
+	u8 rx_chan;
+	spinlock_t lock;
+	u8 tx_skb_i, tx_skb_o;
+	u8 rx_skb_i, rx_skb_o;
+	struct sk_buff *tx_skb[S6_NUM_TX_SKB];
+	struct sk_buff *rx_skb[S6_NUM_RX_SKB];
+	unsigned long carry[sizeof(struct net_device_stats) / sizeof(long)];
+	unsigned long stats[sizeof(struct net_device_stats) / sizeof(long)];
+	struct phy_device *phydev;
+	struct {
+		struct mii_bus *bus;
+		int irq[PHY_MAX_ADDR];
+	} mii;
+	struct {
+		unsigned int mbit;
+		u8 giga;
+		u8 isup;
+		u8 full;
+	} link;
+};
+
+static void s6gmac_rx_fillfifo(struct s6gmac *pd)
+{
+	struct sk_buff *skb;
+	while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB)
+			&& (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan))
+			&& (skb = dev_alloc_skb(S6_MAX_FRLEN + 2))) {
+		pd->rx_skb[(pd->rx_skb_i++) % S6_NUM_RX_SKB] = skb;
+		s6dmac_put_fifo_cache(pd->rx_dma, pd->rx_chan,
+			pd->io, (u32)skb->data, S6_MAX_FRLEN);
+	}
+}
+
+static void s6gmac_rx_interrupt(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	u32 pfx;
+	struct sk_buff *skb;
+	while (((u8)(pd->rx_skb_i - pd->rx_skb_o)) >
+			s6dmac_pending_count(pd->rx_dma, pd->rx_chan)) {
+		skb = pd->rx_skb[(pd->rx_skb_o++) % S6_NUM_RX_SKB];
+		pfx = readl(pd->reg + S6_GMAC_BURST_POSTRD);
+		if (pfx & (1 << S6_GMAC_BURST_POSTRD_DROP)) {
+			dev_kfree_skb_irq(skb);
+		} else {
+			skb_put(skb, (pfx >> S6_GMAC_BURST_POSTRD_LEN)
+				& S6_GMAC_BURST_POSTRD_LEN_MASK);
+			skb->dev = dev;
+			skb->protocol = eth_type_trans(skb, dev);
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+			netif_rx(skb);
+		}
+	}
+}
+
+static void s6gmac_tx_interrupt(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	while (((u8)(pd->tx_skb_i - pd->tx_skb_o)) >
+			s6dmac_pending_count(pd->tx_dma, pd->tx_chan)) {
+		dev_kfree_skb_irq(pd->tx_skb[(pd->tx_skb_o++) % S6_NUM_TX_SKB]);
+	}
+	if (!s6dmac_fifo_full(pd->tx_dma, pd->tx_chan))
+		netif_wake_queue(dev);
+}
+
+struct s6gmac_statinf {
+	unsigned reg_size : 4; /* 0: unused */
+	unsigned reg_off : 6;
+	unsigned net_index : 6;
+};
+
+#define S6_STATS_B (8 * sizeof(u32))
+#define S6_STATS_C(b, r, f) [b] = { \
+	BUILD_BUG_ON_ZERO(r##_SIZE < S6_GMAC_STAT_SIZE_MIN) + \
+	BUILD_BUG_ON_ZERO((r##_SIZE - (S6_GMAC_STAT_SIZE_MIN - 1)) \
+			>= (1<<4)) + \
+	r##_SIZE - (S6_GMAC_STAT_SIZE_MIN - 1), \
+	BUILD_BUG_ON_ZERO(((unsigned)((r - S6_GMAC_STAT_REGS) / sizeof(u32))) \
+			>= ((1<<6)-1)) + \
+	(r - S6_GMAC_STAT_REGS) / sizeof(u32), \
+	BUILD_BUG_ON_ZERO((offsetof(struct net_device_stats, f)) \
+			% sizeof(unsigned long)) + \
+	BUILD_BUG_ON_ZERO((((unsigned)(offsetof(struct net_device_stats, f)) \
+			/ sizeof(unsigned long)) >= (1<<6))) + \
+	BUILD_BUG_ON_ZERO((sizeof(((struct net_device_stats *)0)->f) \
+			!= sizeof(unsigned long))) + \
+	(offsetof(struct net_device_stats, f)) / sizeof(unsigned long)},
+
+static const struct s6gmac_statinf statinf[2][S6_STATS_B] = { {
+	S6_STATS_C(S6_GMAC_STATCARRY1_RBYT, S6_GMAC_STATRBYT, rx_bytes)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RPKT, S6_GMAC_STATRPKT, rx_packets)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RFCS, S6_GMAC_STATRFCS, rx_crc_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RMCA, S6_GMAC_STATRMCA, multicast)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RALN, S6_GMAC_STATRALN, rx_frame_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RFLR, S6_GMAC_STATRFLR, rx_length_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RCDE, S6_GMAC_STATRCDE, rx_missed_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RUND, S6_GMAC_STATRUND, rx_length_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_ROVR, S6_GMAC_STATROVR, rx_length_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RFRG, S6_GMAC_STATRFRG, rx_crc_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RJBR, S6_GMAC_STATRJBR, rx_crc_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY1_RDRP, S6_GMAC_STATRDRP, rx_dropped)
+}, {
+	S6_STATS_C(S6_GMAC_STATCARRY2_TBYT, S6_GMAC_STATTBYT, tx_bytes)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TPKT, S6_GMAC_STATTPKT, tx_packets)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TEDF, S6_GMAC_STATTEDF, tx_aborted_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TXCL, S6_GMAC_STATTXCL, tx_aborted_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TNCL, S6_GMAC_STATTNCL, collisions)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TDRP, S6_GMAC_STATTDRP, tx_dropped)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TJBR, S6_GMAC_STATTJBR, tx_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TFCS, S6_GMAC_STATTFCS, tx_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TOVR, S6_GMAC_STATTOVR, tx_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TUND, S6_GMAC_STATTUND, tx_errors)
+	S6_STATS_C(S6_GMAC_STATCARRY2_TFRG, S6_GMAC_STATTFRG, tx_errors)
+} };
+
+static void s6gmac_stats_collect(struct s6gmac *pd,
+		const struct s6gmac_statinf *inf)
+{
+	int b;
+	for (b = 0; b < S6_STATS_B; b++) {
+		if (inf[b].reg_size) {
+			pd->stats[inf[b].net_index] +=
+				readl(pd->reg + S6_GMAC_STAT_REGS
+					+ sizeof(u32) * inf[b].reg_off);
+		}
+	}
+}
+
+static void s6gmac_stats_carry(struct s6gmac *pd,
+		const struct s6gmac_statinf *inf, u32 mask)
+{
+	int b;
+	while (mask) {
+		b = fls(mask) - 1;
+		mask &= ~(1 << b);
+		pd->carry[inf[b].net_index] += (1 << inf[b].reg_size);
+	}
+}
+
+static inline u32 s6gmac_stats_pending(struct s6gmac *pd, int carry)
+{
+	int r = readl(pd->reg + S6_GMAC_STATCARRY(carry)) &
+		~readl(pd->reg + S6_GMAC_STATCARRYMSK(carry));
+	return r;
+}
+
+static inline void s6gmac_stats_interrupt(struct s6gmac *pd, int carry)
+{
+	u32 mask;
+	mask = s6gmac_stats_pending(pd, carry);
+	if (mask) {
+		writel(mask, pd->reg + S6_GMAC_STATCARRY(carry));
+		s6gmac_stats_carry(pd, &statinf[carry][0], mask);
+	}
+}
+
+static irqreturn_t s6gmac_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct s6gmac *pd = netdev_priv(dev);
+	if (!dev)
+		return IRQ_NONE;
+	spin_lock(&pd->lock);
+	if (s6dmac_termcnt_irq(pd->rx_dma, pd->rx_chan))
+		s6gmac_rx_interrupt(dev);
+	s6gmac_rx_fillfifo(pd);
+	if (s6dmac_termcnt_irq(pd->tx_dma, pd->tx_chan))
+		s6gmac_tx_interrupt(dev);
+	s6gmac_stats_interrupt(pd, 0);
+	s6gmac_stats_interrupt(pd, 1);
+	spin_unlock(&pd->lock);
+	return IRQ_HANDLED;
+}
+
+static inline void s6gmac_set_dstaddr(struct s6gmac *pd, int n,
+	u32 addrlo, u32 addrhi, u32 masklo, u32 maskhi)
+{
+	writel(addrlo, pd->reg + S6_GMAC_HOST_DSTADDRLO(n));
+	writel(addrhi, pd->reg + S6_GMAC_HOST_DSTADDRHI(n));
+	writel(masklo, pd->reg + S6_GMAC_HOST_DSTMASKLO(n));
+	writel(maskhi, pd->reg + S6_GMAC_HOST_DSTMASKHI(n));
+}
+
+static inline void s6gmac_stop_device(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	writel(0, pd->reg + S6_GMAC_MACCONF1);
+}
+
+static inline void s6gmac_init_device(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	int is_rgmii = !!(pd->phydev->supported
+		& (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half));
+#if 0
+	writel(1 << S6_GMAC_MACCONF1_SYNCTX |
+		1 << S6_GMAC_MACCONF1_SYNCRX |
+		1 << S6_GMAC_MACCONF1_TXFLOWCTRL |
+		1 << S6_GMAC_MACCONF1_RXFLOWCTRL |
+		1 << S6_GMAC_MACCONF1_RESTXFUNC |
+		1 << S6_GMAC_MACCONF1_RESRXFUNC |
+		1 << S6_GMAC_MACCONF1_RESTXMACCTRL |
+		1 << S6_GMAC_MACCONF1_RESRXMACCTRL,
+		pd->reg + S6_GMAC_MACCONF1);
+#endif
+	writel(1 << S6_GMAC_MACCONF1_SOFTRES, pd->reg + S6_GMAC_MACCONF1);
+	udelay(1000);
+	writel(1 << S6_GMAC_MACCONF1_TXENA | 1 << S6_GMAC_MACCONF1_RXENA,
+		pd->reg + S6_GMAC_MACCONF1);
+	writel(1 << S6_GMAC_HOST_PBLKCTRL_TXSRES |
+		1 << S6_GMAC_HOST_PBLKCTRL_RXSRES,
+		pd->reg + S6_GMAC_HOST_PBLKCTRL);
+	writel(S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_TXBSIZ |
+		S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_RXBSIZ |
+		1 << S6_GMAC_HOST_PBLKCTRL_STATENA |
+		1 << S6_GMAC_HOST_PBLKCTRL_STATCLEAR |
+		is_rgmii << S6_GMAC_HOST_PBLKCTRL_RGMII,
+		pd->reg + S6_GMAC_HOST_PBLKCTRL);
+	writel(1 << S6_GMAC_MACCONF1_TXENA |
+		1 << S6_GMAC_MACCONF1_RXENA |
+		(dev->flags & IFF_LOOPBACK ? 1 : 0)
+			<< S6_GMAC_MACCONF1_LOOPBACK,
+		pd->reg + S6_GMAC_MACCONF1);
+	writel(dev->mtu && (dev->mtu < (S6_MAX_FRLEN - ETH_HLEN-ETH_FCS_LEN)) ?
+			dev->mtu+ETH_HLEN+ETH_FCS_LEN : S6_MAX_FRLEN,
+		pd->reg + S6_GMAC_MACMAXFRAMELEN);
+	writel((pd->link.full ? 1 : 0) << S6_GMAC_MACCONF2_FULL |
+		1 << S6_GMAC_MACCONF2_PADCRCENA |
+		1 << S6_GMAC_MACCONF2_LENGTHFCHK |
+		(pd->link.giga ?
+			S6_GMAC_MACCONF2_IFMODE_BYTE :
+			S6_GMAC_MACCONF2_IFMODE_NIBBLE)
+			<< S6_GMAC_MACCONF2_IFMODE |
+		7 << S6_GMAC_MACCONF2_PREAMBLELEN,
+		pd->reg + S6_GMAC_MACCONF2);
+	writel(0, pd->reg + S6_GMAC_MACSTATADDR1);
+	writel(0, pd->reg + S6_GMAC_MACSTATADDR2);
+	writel(1 << S6_GMAC_FIFOCONF0_WTMENREQ |
+		1 << S6_GMAC_FIFOCONF0_SRFENREQ |
+		1 << S6_GMAC_FIFOCONF0_FRFENREQ |
+		1 << S6_GMAC_FIFOCONF0_STFENREQ |
+		1 << S6_GMAC_FIFOCONF0_FTFENREQ,
+		pd->reg + S6_GMAC_FIFOCONF0);
+	writel(128 << S6_GMAC_FIFOCONF3_CFGFTTH |
+		128 << S6_GMAC_FIFOCONF3_CFGHWMFT,
+		pd->reg + S6_GMAC_FIFOCONF3);
+	writel((S6_GMAC_FIFOCONF_RSV_MASK & ~(
+			1 << S6_GMAC_FIFOCONF_RSV_RUNT |
+			1 << S6_GMAC_FIFOCONF_RSV_CRCERR |
+			1 << S6_GMAC_FIFOCONF_RSV_OK |
+			1 << S6_GMAC_FIFOCONF_RSV_DRIBBLE |
+			1 << S6_GMAC_FIFOCONF_RSV_CTRLFRAME |
+			1 << S6_GMAC_FIFOCONF_RSV_PAUSECTRL |
+			1 << S6_GMAC_FIFOCONF_RSV_UNOPCODE |
+			1 << S6_GMAC_FIFOCONF_RSV_TRUNCATED)) |
+		1 << S6_GMAC_FIFOCONF5_DROPLT64 |
+		pd->link.giga << S6_GMAC_FIFOCONF5_CFGBYTM |
+		1 << S6_GMAC_FIFOCONF5_RXDROPSIZE,
+		pd->reg + S6_GMAC_FIFOCONF5);
+	writel(1 << S6_GMAC_FIFOCONF_RSV_RUNT |
+		1 << S6_GMAC_FIFOCONF_RSV_CRCERR |
+		1 << S6_GMAC_FIFOCONF_RSV_DRIBBLE |
+		1 << S6_GMAC_FIFOCONF_RSV_CTRLFRAME |
+		1 << S6_GMAC_FIFOCONF_RSV_PAUSECTRL |
+		1 << S6_GMAC_FIFOCONF_RSV_UNOPCODE |
+		1 << S6_GMAC_FIFOCONF_RSV_TRUNCATED,
+		pd->reg + S6_GMAC_FIFOCONF4);
+	s6gmac_set_dstaddr(pd, 0,
+		0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0x0000FFFF);
+	s6gmac_set_dstaddr(pd, 1,
+		dev->dev_addr[5] |
+		dev->dev_addr[4] << 8 |
+		dev->dev_addr[3] << 16 |
+		dev->dev_addr[2] << 24,
+		dev->dev_addr[1] |
+		dev->dev_addr[0] << 8,
+		0xFFFFFFFF, 0x0000FFFF);
+	s6gmac_set_dstaddr(pd, 2,
+		0x00000000, 0x00000100, 0x00000000, 0x00000100);
+	s6gmac_set_dstaddr(pd, 3,
+		0x00000000, 0x00000000, 0x00000000, 0x00000000);
+	writel(1 << S6_GMAC_HOST_PBLKCTRL_TXENA |
+		1 << S6_GMAC_HOST_PBLKCTRL_RXENA |
+		S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_TXBSIZ |
+		S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_RXBSIZ |
+		1 << S6_GMAC_HOST_PBLKCTRL_STATENA |
+		1 << S6_GMAC_HOST_PBLKCTRL_STATCLEAR |
+		is_rgmii << S6_GMAC_HOST_PBLKCTRL_RGMII,
+		pd->reg + S6_GMAC_HOST_PBLKCTRL);
+}
+
+static void s6mii_enable(struct s6gmac *pd)
+{
+	writel(readl(pd->reg + S6_GMAC_MACCONF1) &
+		~(1 << S6_GMAC_MACCONF1_SOFTRES),
+		pd->reg + S6_GMAC_MACCONF1);
+	writel((readl(pd->reg + S6_GMAC_MACMIICONF)
+		& ~(S6_GMAC_MACMIICONF_CSEL_MASK << S6_GMAC_MACMIICONF_CSEL))
+		| (S6_GMAC_MACMIICONF_CSEL_DIV168 << S6_GMAC_MACMIICONF_CSEL),
+		pd->reg + S6_GMAC_MACMIICONF);
+}
+
+static int s6mii_busy(struct s6gmac *pd, int tmo)
+{
+	while (readl(pd->reg + S6_GMAC_MACMIIINDI)) {
+		if (--tmo == 0)
+			return -ETIME;
+		udelay(64);
+	}
+	return 0;
+}
+
+static int s6mii_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+	struct s6gmac *pd = bus->priv;
+	s6mii_enable(pd);
+	if (s6mii_busy(pd, 256))
+		return -ETIME;
+	writel(phy_addr << S6_GMAC_MACMIIADDR_PHY |
+		regnum << S6_GMAC_MACMIIADDR_REG,
+		pd->reg + S6_GMAC_MACMIIADDR);
+	writel(1 << S6_GMAC_MACMIICMD_READ, pd->reg + S6_GMAC_MACMIICMD);
+	writel(0, pd->reg + S6_GMAC_MACMIICMD);
+	if (s6mii_busy(pd, 256))
+		return -ETIME;
+	return (u16)readl(pd->reg + S6_GMAC_MACMIISTAT);
+}
+
+static int s6mii_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value)
+{
+	struct s6gmac *pd = bus->priv;
+	s6mii_enable(pd);
+	if (s6mii_busy(pd, 256))
+		return -ETIME;
+	writel(phy_addr << S6_GMAC_MACMIIADDR_PHY |
+		regnum << S6_GMAC_MACMIIADDR_REG,
+		pd->reg + S6_GMAC_MACMIIADDR);
+	writel(value, pd->reg + S6_GMAC_MACMIICTRL);
+	if (s6mii_busy(pd, 256))
+		return -ETIME;
+	return 0;
+}
+
+static int s6mii_reset(struct mii_bus *bus)
+{
+	struct s6gmac *pd = bus->priv;
+	s6mii_enable(pd);
+	if (s6mii_busy(pd, PHY_INIT_TIMEOUT))
+		return -ETIME;
+	return 0;
+}
+
+static void s6gmac_set_rgmii_txclock(struct s6gmac *pd)
+{
+	u32 pllsel = readl(S6_REG_GREG1 + S6_GREG1_PLLSEL);
+	pllsel &= ~(S6_GREG1_PLLSEL_GMAC_MASK << S6_GREG1_PLLSEL_GMAC);
+	switch (pd->link.mbit) {
+	case 10:
+		pllsel |= S6_GREG1_PLLSEL_GMAC_2500KHZ << S6_GREG1_PLLSEL_GMAC;
+		break;
+	case 100:
+		pllsel |= S6_GREG1_PLLSEL_GMAC_25MHZ << S6_GREG1_PLLSEL_GMAC;
+		break;
+	case 1000:
+		pllsel |= S6_GREG1_PLLSEL_GMAC_125MHZ << S6_GREG1_PLLSEL_GMAC;
+		break;
+	default:
+		return;
+	}
+	writel(pllsel, S6_REG_GREG1 + S6_GREG1_PLLSEL);
+}
+
+static inline void s6gmac_linkisup(struct net_device *dev, int isup)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	struct phy_device *phydev = pd->phydev;
+
+	pd->link.full = phydev->duplex;
+	pd->link.giga = (phydev->speed == 1000);
+	if (pd->link.mbit != phydev->speed) {
+		pd->link.mbit = phydev->speed;
+		s6gmac_set_rgmii_txclock(pd);
+	}
+	pd->link.isup = isup;
+	if (isup)
+		netif_carrier_on(dev);
+	phy_print_status(phydev);
+}
+
+static void s6gmac_adjust_link(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	struct phy_device *phydev = pd->phydev;
+	if (pd->link.isup &&
+			(!phydev->link ||
+			(pd->link.mbit != phydev->speed) ||
+			(pd->link.full != phydev->duplex))) {
+		pd->link.isup = 0;
+		netif_tx_disable(dev);
+		if (!phydev->link) {
+			netif_carrier_off(dev);
+			phy_print_status(phydev);
+		}
+	}
+	if (!pd->link.isup && phydev->link) {
+		if (pd->link.full != phydev->duplex) {
+			u32 maccfg = readl(pd->reg + S6_GMAC_MACCONF2);
+			if (phydev->duplex)
+				maccfg |= 1 << S6_GMAC_MACCONF2_FULL;
+			else
+				maccfg &= ~(1 << S6_GMAC_MACCONF2_FULL);
+			writel(maccfg, pd->reg + S6_GMAC_MACCONF2);
+		}
+
+		if (pd->link.giga != (phydev->speed == 1000)) {
+			u32 fifocfg = readl(pd->reg + S6_GMAC_FIFOCONF5);
+			u32 maccfg = readl(pd->reg + S6_GMAC_MACCONF2);
+			maccfg &= ~(S6_GMAC_MACCONF2_IFMODE_MASK
+				     << S6_GMAC_MACCONF2_IFMODE);
+			if (phydev->speed == 1000) {
+				fifocfg |= 1 << S6_GMAC_FIFOCONF5_CFGBYTM;
+				maccfg |= S6_GMAC_MACCONF2_IFMODE_BYTE
+					   << S6_GMAC_MACCONF2_IFMODE;
+			} else {
+				fifocfg &= ~(1 << S6_GMAC_FIFOCONF5_CFGBYTM);
+				maccfg |= S6_GMAC_MACCONF2_IFMODE_NIBBLE
+					   << S6_GMAC_MACCONF2_IFMODE;
+			}
+			writel(fifocfg, pd->reg + S6_GMAC_FIFOCONF5);
+			writel(maccfg, pd->reg + S6_GMAC_MACCONF2);
+		}
+
+		if (!s6dmac_fifo_full(pd->tx_dma, pd->tx_chan))
+			netif_wake_queue(dev);
+		s6gmac_linkisup(dev, 1);
+	}
+}
+
+static inline int s6gmac_phy_start(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	int i = 0;
+	struct phy_device *p = NULL;
+	while ((!(p = pd->mii.bus->phy_map[i])) && (i < PHY_MAX_ADDR))
+		i++;
+	p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, 0,
+			PHY_INTERFACE_MODE_RGMII);
+	if (IS_ERR(p)) {
+		printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+		return PTR_ERR(p);
+	}
+	p->supported &= PHY_GBIT_FEATURES;
+	p->advertising = p->supported;
+	pd->phydev = p;
+	return 0;
+}
+
+static inline void s6gmac_init_stats(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	u32 mask;
+	mask =	1 << S6_GMAC_STATCARRY1_RDRP |
+		1 << S6_GMAC_STATCARRY1_RJBR |
+		1 << S6_GMAC_STATCARRY1_RFRG |
+		1 << S6_GMAC_STATCARRY1_ROVR |
+		1 << S6_GMAC_STATCARRY1_RUND |
+		1 << S6_GMAC_STATCARRY1_RCDE |
+		1 << S6_GMAC_STATCARRY1_RFLR |
+		1 << S6_GMAC_STATCARRY1_RALN |
+		1 << S6_GMAC_STATCARRY1_RMCA |
+		1 << S6_GMAC_STATCARRY1_RFCS |
+		1 << S6_GMAC_STATCARRY1_RPKT |
+		1 << S6_GMAC_STATCARRY1_RBYT;
+	writel(mask, pd->reg + S6_GMAC_STATCARRY(0));
+	writel(~mask, pd->reg + S6_GMAC_STATCARRYMSK(0));
+	mask =	1 << S6_GMAC_STATCARRY2_TDRP |
+		1 << S6_GMAC_STATCARRY2_TNCL |
+		1 << S6_GMAC_STATCARRY2_TXCL |
+		1 << S6_GMAC_STATCARRY2_TEDF |
+		1 << S6_GMAC_STATCARRY2_TPKT |
+		1 << S6_GMAC_STATCARRY2_TBYT |
+		1 << S6_GMAC_STATCARRY2_TFRG |
+		1 << S6_GMAC_STATCARRY2_TUND |
+		1 << S6_GMAC_STATCARRY2_TOVR |
+		1 << S6_GMAC_STATCARRY2_TFCS |
+		1 << S6_GMAC_STATCARRY2_TJBR;
+	writel(mask, pd->reg + S6_GMAC_STATCARRY(1));
+	writel(~mask, pd->reg + S6_GMAC_STATCARRYMSK(1));
+}
+
+static inline void s6gmac_init_dmac(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	s6dmac_disable_chan(pd->tx_dma, pd->tx_chan);
+	s6dmac_disable_chan(pd->rx_dma, pd->rx_chan);
+	s6dmac_disable_error_irqs(pd->tx_dma, 1 << S6_HIFDMA_GMACTX);
+	s6dmac_disable_error_irqs(pd->rx_dma, 1 << S6_HIFDMA_GMACRX);
+}
+
+static int s6gmac_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	unsigned long flags;
+	spin_lock_irqsave(&pd->lock, flags);
+	dev->trans_start = jiffies;
+	writel(skb->len << S6_GMAC_BURST_PREWR_LEN |
+		0 << S6_GMAC_BURST_PREWR_CFE |
+		1 << S6_GMAC_BURST_PREWR_PPE |
+		1 << S6_GMAC_BURST_PREWR_FCS |
+		((skb->len < ETH_ZLEN) ? 1 : 0) << S6_GMAC_BURST_PREWR_PAD,
+		pd->reg + S6_GMAC_BURST_PREWR);
+	s6dmac_put_fifo_cache(pd->tx_dma, pd->tx_chan,
+		(u32)skb->data, pd->io, skb->len);
+	if (s6dmac_fifo_full(pd->tx_dma, pd->tx_chan))
+		netif_stop_queue(dev);
+	if (((u8)(pd->tx_skb_i - pd->tx_skb_o)) >= S6_NUM_TX_SKB) {
+		printk(KERN_ERR "GMAC BUG: skb tx ring overflow [%x, %x]\n",
+			pd->tx_skb_o, pd->tx_skb_i);
+		BUG();
+	}
+	pd->tx_skb[(pd->tx_skb_i++) % S6_NUM_TX_SKB] = skb;
+	spin_unlock_irqrestore(&pd->lock, flags);
+	return 0;
+}
+
+static void s6gmac_tx_timeout(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	unsigned long flags;
+	spin_lock_irqsave(&pd->lock, flags);
+	s6gmac_tx_interrupt(dev);
+	spin_unlock_irqrestore(&pd->lock, flags);
+}
+
+static int s6gmac_open(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	unsigned long flags;
+	phy_read_status(pd->phydev);
+	spin_lock_irqsave(&pd->lock, flags);
+	pd->link.mbit = 0;
+	s6gmac_linkisup(dev, pd->phydev->link);
+	s6gmac_init_device(dev);
+	s6gmac_init_stats(dev);
+	s6gmac_init_dmac(dev);
+	s6gmac_rx_fillfifo(pd);
+	s6dmac_enable_chan(pd->rx_dma, pd->rx_chan,
+		2, 1, 0, 1, 0, 0, 0, 7, -1, 2, 0, 1);
+	s6dmac_enable_chan(pd->tx_dma, pd->tx_chan,
+		2, 0, 1, 0, 0, 0, 0, 7, -1, 2, 0, 1);
+	writel(0 << S6_GMAC_HOST_INT_TXBURSTOVER |
+		0 << S6_GMAC_HOST_INT_TXPREWOVER |
+		0 << S6_GMAC_HOST_INT_RXBURSTUNDER |
+		0 << S6_GMAC_HOST_INT_RXPOSTRFULL |
+		0 << S6_GMAC_HOST_INT_RXPOSTRUNDER,
+		pd->reg + S6_GMAC_HOST_INTMASK);
+	spin_unlock_irqrestore(&pd->lock, flags);
+	phy_start(pd->phydev);
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int s6gmac_stop(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	unsigned long flags;
+	netif_stop_queue(dev);
+	phy_stop(pd->phydev);
+	spin_lock_irqsave(&pd->lock, flags);
+	s6gmac_init_dmac(dev);
+	s6gmac_stop_device(dev);
+	while (pd->tx_skb_i != pd->tx_skb_o)
+		dev_kfree_skb(pd->tx_skb[(pd->tx_skb_o++) % S6_NUM_TX_SKB]);
+	while (pd->rx_skb_i != pd->rx_skb_o)
+		dev_kfree_skb(pd->rx_skb[(pd->rx_skb_o++) % S6_NUM_RX_SKB]);
+	spin_unlock_irqrestore(&pd->lock, flags);
+	return 0;
+}
+
+static struct net_device_stats *s6gmac_stats(struct net_device *dev)
+{
+	struct s6gmac *pd = netdev_priv(dev);
+	struct net_device_stats *st = (struct net_device_stats *)&pd->stats;
+	int i;
+	do {
+		unsigned long flags;
+		spin_lock_irqsave(&pd->lock, flags);
+		for (i = 0; i < sizeof(pd->stats) / sizeof(unsigned long); i++)
+			pd->stats[i] =
+				pd->carry[i] << (S6_GMAC_STAT_SIZE_MIN - 1);
+		s6gmac_stats_collect(pd, &statinf[0][0]);
+		s6gmac_stats_collect(pd, &statinf[1][0]);
+		i = s6gmac_stats_pending(pd, 0) |
+			s6gmac_stats_pending(pd, 1);
+		spin_unlock_irqrestore(&pd->lock, flags);
+	} while (i);
+	st->rx_errors = st->rx_crc_errors +
+			st->rx_frame_errors +
+			st->rx_length_errors +
+			st->rx_missed_errors;
+	st->tx_errors += st->tx_aborted_errors;
+	return st;
+}
+
+static int __devinit s6gmac_probe(struct platform_device *pdev)
+{
+	struct net_device *dev;
+	struct s6gmac *pd;
+	int res;
+	unsigned long i;
+	struct mii_bus *mb;
+	dev = alloc_etherdev(sizeof(*pd));
+	if (!dev) {
+		printk(KERN_ERR DRV_PRMT "etherdev alloc failed, aborting.\n");
+		return -ENOMEM;
+	}
+	dev->open = s6gmac_open;
+	dev->stop = s6gmac_stop;
+	dev->hard_start_xmit = s6gmac_tx;
+	dev->tx_timeout = s6gmac_tx_timeout;
+	dev->watchdog_timeo = HZ;
+	dev->get_stats = s6gmac_stats;
+	dev->irq = platform_get_irq(pdev, 0);
+	pd = netdev_priv(dev);
+	memset(pd, 0, sizeof(*pd));
+	spin_lock_init(&pd->lock);
+	pd->reg = platform_get_resource(pdev, IORESOURCE_MEM, 0)->start;
+	i = platform_get_resource(pdev, IORESOURCE_DMA, 0)->start;
+	pd->tx_dma = DMA_MASK_DMAC(i);
+	pd->tx_chan = DMA_INDEX_CHNL(i);
+	i = platform_get_resource(pdev, IORESOURCE_DMA, 1)->start;
+	pd->rx_dma = DMA_MASK_DMAC(i);
+	pd->rx_chan = DMA_INDEX_CHNL(i);
+	pd->io = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
+	res = request_irq(dev->irq, &s6gmac_interrupt, 0, dev->name, dev);
+	if (res) {
+		printk(KERN_ERR DRV_PRMT "irq request failed: %d\n", dev->irq);
+		goto errirq;
+	}
+	res = register_netdev(dev);
+	if (res) {
+		printk(KERN_ERR DRV_PRMT "error registering device %s\n",
+			dev->name);
+		goto errdev;
+	}
+	mb = mdiobus_alloc();
+	if (!mb) {
+		printk(KERN_ERR DRV_PRMT "error allocating mii bus\n");
+		goto errmii;
+	}
+	mb->name = "s6gmac_mii";
+	mb->read = s6mii_read;
+	mb->write = s6mii_write;
+	mb->reset = s6mii_reset;
+	mb->priv = pd;
+	snprintf(mb->id, MII_BUS_ID_SIZE, "0");
+	mb->phy_mask = ~(1 << 0);
+	mb->irq = &pd->mii.irq[0];
+	for (i = 0; i < PHY_MAX_ADDR; i++) {
+		int n = platform_get_irq(pdev, i + 1);
+		if (n < 0)
+			n = PHY_POLL;
+		pd->mii.irq[i] = n;
+	}
+	mdiobus_register(mb);
+	pd->mii.bus = mb;
+	res = s6gmac_phy_start(dev);
+	if (res)
+		return res;
+	platform_set_drvdata(pdev, dev);
+	return 0;
+errmii:
+	unregister_netdev(dev);
+errdev:
+	free_irq(dev->irq, dev);
+errirq:
+	free_netdev(dev);
+	return res;
+}
+
+static int __devexit s6gmac_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	if (dev) {
+		struct s6gmac *pd = netdev_priv(dev);
+		mdiobus_unregister(pd->mii.bus);
+		unregister_netdev(dev);
+		free_irq(dev->irq, dev);
+		free_netdev(dev);
+		platform_set_drvdata(pdev, NULL);
+	}
+	return 0;
+}
+
+static struct platform_driver s6gmac_driver = {
+	.probe = s6gmac_probe,
+	.remove = __devexit_p(s6gmac_remove),
+	.driver = {
+		.name = "s6gmac",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init s6gmac_init(void)
+{
+	printk(KERN_INFO DRV_PRMT "S6 GMAC ethernet driver\n");
+	return platform_driver_register(&s6gmac_driver);
+}
+
+
+static void __exit s6gmac_exit(void)
+{
+	platform_driver_unregister(&s6gmac_driver);
+}
+
+module_init(s6gmac_init);
+module_exit(s6gmac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("S6105 on chip Ethernet driver");
+MODULE_AUTHOR("Oskar Schirmer <os@emlix.com>");