]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/czankel/xtensa-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 22 Jun 2009 19:38:31 +0000 (12:38 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 22 Jun 2009 19:38:31 +0000 (12:38 -0700)
* 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

16 files changed:
arch/xtensa/configs/s6105_defconfig
arch/xtensa/include/asm/cacheflush.h
arch/xtensa/include/asm/gpio.h
arch/xtensa/include/asm/irq.h
arch/xtensa/kernel/irq.c
arch/xtensa/platforms/s6105/device.c
arch/xtensa/platforms/s6105/setup.c
arch/xtensa/variants/s6000/Makefile
arch/xtensa/variants/s6000/dmac.c [new file with mode: 0644]
arch/xtensa/variants/s6000/gpio.c
arch/xtensa/variants/s6000/include/variant/dmac.h [new file with mode: 0644]
arch/xtensa/variants/s6000/include/variant/gpio.h
arch/xtensa/variants/s6000/include/variant/irq.h
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/s6gmac.c [new file with mode: 0644]

index 768bee006037fe9404f5657d76a16629a50f3854..bb84fbc9921f903028bc475a73c3ade4df128581 100644 (file)
@@ -263,7 +263,54 @@ CONFIG_HAVE_IDE=y
 # 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_UNIX98_PTYS=y
 # 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_SSB_POSSIBLE=y
 # 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
index 8fc1c0c8de073742aaf94428215bad8725f9959f..b7b8fbe47c77330f046bf59b27e126702da37f55 100644 (file)
@@ -155,5 +155,100 @@ extern void copy_from_user_page(struct vm_area_struct*, struct page*,
 
 #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 */
index 0763b0763960c3a4cc1a2784775c8f7324db5266..a8c9fc46c790922e6a8e6116e8a66b7a645d7e20 100644 (file)
@@ -38,14 +38,14 @@ static inline int gpio_cansleep(unsigned int gpio)
        return __gpio_cansleep(gpio);
 }
 
-/*
- * Not implemented, yet.
- */
 static inline int gpio_to_irq(unsigned int gpio)
 {
-       return -ENOSYS;
+       return __gpio_to_irq(gpio);
 }
 
+/*
+ * Not implemented, yet.
+ */
 static inline int irq_to_gpio(unsigned int irq)
 {
        return -EINVAL;
index dfac82dc52ad2eaea2945f267379bad17deab2ca..4c0ccc9c4f4c0adf0a8ac6212818567749147fed 100644 (file)
@@ -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_enable(unsigned int irq) { }
 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)
 {
index a36c85edd04588b59129f25eb17dd646a5c5b982..a1badb32fcdaea8b43350f3853f8efc66be928d8 100644 (file)
@@ -197,4 +197,6 @@ void __init init_IRQ(void)
        }
 
        cached_irq_mask = 0;
+
+       variant_init_irq();
 }
index 78b08be5a92d5473edf17dc6b7daf79aa3e06988..65333ffefb079431451c4fc5aa9f3dc011ce3d69 100644 (file)
@@ -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 @@ static const signed char uart_irq_mappings[] = {
        -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 plat_serial8250_port serial_platform_data[] = {
        { },
 };
 
+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 @@ static struct platform_device platform_devices[] = {
                        .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;
index 855ddeadc43d037e5c8c078364037a2f399bfc1c..86ce730f791321a11b7b8a52bb553a374ddfa5d7 100644 (file)
@@ -35,12 +35,21 @@ void __init platform_setup(char **cmdline)
 {
        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_setup(char **cmdline)
 
 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);
index d83f3805130cf74d1b3c02f19ab54db33ac46e8c..3e7ef0a0c498a572be129dcc9c0dc0560f28b2b4 100644 (file)
@@ -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 (file)
index 0000000..dc7f7c5
--- /dev/null
@@ -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);
index 79317fdcf14c36d36563157d079f346002f18573..380a70fff756b3e561200126bb9b5a87b671c866 100644 (file)
@@ -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 @@ static void set(struct gpio_chip *chip, unsigned int off, int val)
        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 (file)
index 0000000..89ab948
--- /dev/null
@@ -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 */
index 8327f62167eb39c462a1f7d59f4637b66f66e343..8484ab0df461135385087f370f474b6ab9ab50d7 100644 (file)
@@ -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 */
index fa031cb0acc45ab65720820fe3db88902305812a..97d6fc48deff036b1ea5a1bd940b3d998411deec 100644 (file)
@@ -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 */
index 892a9e4e275fe34eecd136adbef2ce4c46c36f57..1dc721517e4cdf30db36ed726230d74b83e55501 100644 (file)
@@ -2443,6 +2443,17 @@ config JME
          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
 
 #
index d366fb2b40e99e2065408160243c12da9cc95c33..4b58a59f211b4e329e791ae3bf881a7f9edf7a81 100644 (file)
@@ -245,6 +245,7 @@ obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o
 
 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 (file)
index 0000000..5345e47
--- /dev/null
@@ -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>");