This patch adds support for the D-Link DSM-G600 Rev A.
This is an ARM XScale IXP4xx system relatively similar to
the NSLU2 and NAS-100D already supported by mainline. An
important difference is Gigabit Ethernet support using
the Via Velocity chipset.

This patch is the combined work of Michael Westerhof and
Alessandro Zummo, with contributions from Michael-Luke
Jones. This version addresses review comments from rmk
and Deepak Saxena.

Signed-off-by: Michael-Luke Jones <mlj28@cam.ac.uk>
Signed-off-by: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Michael Westerhof <mwester@dls.net>

---
 arch/arm/mach-ixp4xx/Kconfig           |    9 +
 arch/arm/mach-ixp4xx/Makefile          |    2 
 arch/arm/mach-ixp4xx/dsmg600-pci.c     |   74 +++++++++++++
 arch/arm/mach-ixp4xx/dsmg600-power.c   |  130 ++++++++++++++++++++++++
 arch/arm/mach-ixp4xx/dsmg600-setup.c   |  175 +++++++++++++++++++++++++++++++++
 include/asm-arm/arch-ixp4xx/dsmg600.h  |   57 ++++++++++
 include/asm-arm/arch-ixp4xx/hardware.h |    1 
 include/asm-arm/arch-ixp4xx/irqs.h     |   10 +
 8 files changed, 458 insertions(+)

Index: linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/Kconfig
===================================================================
--- linux-2.6.21.1-armeb.orig/arch/arm/mach-ixp4xx/Kconfig
+++ linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/Kconfig
@@ -89,6 +89,15 @@
 	  NAS 100d device. For more information on this platform,
 	  see http://www.nslu2-linux.org/wiki/NAS100d/HomePage
 
+config MACH_DSMG600
+	bool
+	prompt "D-Link DSM-G600 RevA"
+	select PCI
+	help
+	  Say 'Y' here if you want your kernel to support D-Link's
+	  DSM-G600 RevA device. For more information on this platform,
+	  see http://www.nslu2-linux.org/wiki/DSMG600/HomePage
+
 #
 # Avila and IXDP share the same source for now. Will change in future
 #
Index: linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/Makefile
===================================================================
--- linux-2.6.21.1-armeb.orig/arch/arm/mach-ixp4xx/Makefile
+++ linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/Makefile
@@ -12,6 +12,7 @@
 obj-pci-$(CONFIG_MACH_GTWX5715)		+= gtwx5715-pci.o
 obj-pci-$(CONFIG_MACH_NSLU2)		+= nslu2-pci.o
 obj-pci-$(CONFIG_MACH_NAS100D)		+= nas100d-pci.o
+obj-pci-$(CONFIG_MACH_DSMG600)		+= dsmg600-pci.o
 
 obj-y	+= common.o
 
@@ -22,5 +23,6 @@
 obj-$(CONFIG_MACH_GTWX5715)	+= gtwx5715-setup.o
 obj-$(CONFIG_MACH_NSLU2)	+= nslu2-setup.o nslu2-power.o
 obj-$(CONFIG_MACH_NAS100D)	+= nas100d-setup.o nas100d-power.o
+obj-$(CONFIG_MACH_DSMG600)      += dsmg600-setup.o dsmg600-power.o
 
 obj-$(CONFIG_PCI)		+= $(obj-pci-$(CONFIG_PCI)) common-pci.o
Index: linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/dsmg600-pci.c
===================================================================
--- /dev/null
+++ linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/dsmg600-pci.c
@@ -0,0 +1,74 @@
+/*
+ * DSM-G600 board-level PCI initialization
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on ixdp425-pci.c:
+ *	Copyright (C) 2002 Intel Corporation.
+ *	Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * Maintainer: http://www.nslu2-linux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/mach/pci.h>
+#include <asm/mach-types.h>
+
+void __init dsmg600_pci_preinit(void)
+{
+	set_irq_type(IRQ_DSMG600_PCI_INTA, IRQT_LOW);
+	set_irq_type(IRQ_DSMG600_PCI_INTB, IRQT_LOW);
+	set_irq_type(IRQ_DSMG600_PCI_INTC, IRQT_LOW);
+	set_irq_type(IRQ_DSMG600_PCI_INTD, IRQT_LOW);
+	set_irq_type(IRQ_DSMG600_PCI_INTE, IRQT_LOW);
+	set_irq_type(IRQ_DSMG600_PCI_INTF, IRQT_LOW);
+
+	ixp4xx_pci_preinit();
+}
+
+static int __init dsmg600_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	static int pci_irq_table[DSMG600_PCI_MAX_DEV][DSMG600_PCI_IRQ_LINES] =
+	{
+		{ IRQ_DSMG600_PCI_INTE, -1, -1 },
+		{ IRQ_DSMG600_PCI_INTA, -1, -1 },
+		{ IRQ_DSMG600_PCI_INTB, IRQ_DSMG600_PCI_INTC, IRQ_DSMG600_PCI_INTD },
+		{ IRQ_DSMG600_PCI_INTF, -1, -1 },
+	};
+
+	int irq = -1;
+
+	if (slot >= 1 && slot <= DSMG600_PCI_MAX_DEV &&
+		pin >= 1 && pin <= DSMG600_PCI_IRQ_LINES)
+		irq = pci_irq_table[slot-1][pin-1];
+
+	return irq;
+}
+
+struct hw_pci __initdata dsmg600_pci = {
+	.nr_controllers = 1,
+	.preinit	= dsmg600_pci_preinit,
+	.swizzle	= pci_std_swizzle,
+	.setup		= ixp4xx_setup,
+	.scan		= ixp4xx_scan_bus,
+	.map_irq	= dsmg600_map_irq,
+};
+
+int __init dsmg600_pci_init(void)
+{
+	if (machine_is_dsmg600())
+		pci_common_init(&dsmg600_pci);
+
+	return 0;
+}
+
+subsys_initcall(dsmg600_pci_init);
Index: linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/dsmg600-power.c
===================================================================
--- /dev/null
+++ linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/dsmg600-power.c
@@ -0,0 +1,130 @@
+/*
+ * arch/arm/mach-ixp4xx/dsmg600-power.c
+ *
+ * DSM-G600 Power/Reset driver
+ * Author: Michael Westerhof <mwester@dls.net>
+ *
+ * Based on nslu2-power.c
+ *  Copyright (C) 2005 Tower Technologies
+ *  Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * which was based on nslu2-io.c
+ *  Copyright (C) 2004 Karen Spearel
+ *
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+
+#include <asm/mach-types.h>
+
+extern void ctrl_alt_del(void);
+
+/* This is used to make sure the power-button pusher is serious.  The button
+ * must be held until the value of this counter reaches zero.
+ */
+static volatile int power_button_countdown;
+
+/* Must hold the button down for at least this many counts to be processed */
+#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */
+
+static void dsmg600_power_handler(unsigned long data);
+static DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler, 0, 0);
+
+static void dsmg600_power_handler(unsigned long data)
+{
+	/* This routine is called twice per second to check the
+	 * state of the power button.
+	 */
+
+	if (*IXP4XX_GPIO_GPINR & DSMG600_PB_BM) {
+
+		/* IO Pin is 1 (button pushed) */
+		if (power_button_countdown > 0) {
+			power_button_countdown--;
+		}
+
+	} else {
+
+		/* Done on button release, to allow for auto-power-on mods. */
+		if (power_button_countdown == 0) {
+			/* Signal init to do the ctrlaltdel action, this will bypass
+			 * init if it hasn't started and do a kernel_restart.
+			 */
+			ctrl_alt_del();
+
+			/* Change the state of the power LED to "blink" */
+			gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW);
+		} else {
+			power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
+		}
+	}
+
+	mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
+}
+
+static irqreturn_t dsmg600_reset_handler(int irq, void *dev_id)
+{
+	/* This is the paper-clip reset, it shuts the machine down directly. */
+	machine_power_off();
+
+	return IRQ_HANDLED;
+}
+
+static int __init dsmg600_power_init(void)
+{
+	if (!(machine_is_dsmg600()))
+		return 0;
+
+	if (request_irq(DSMG600_RB_IRQ, &dsmg600_reset_handler,
+		IRQF_DISABLED | IRQF_TRIGGER_LOW, "DSM-G600 reset button",
+		NULL) < 0) {
+
+		printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
+			DSMG600_RB_IRQ);
+
+		return -EIO;
+	}
+
+	/* The power button on the D-Link DSM-G600 is on GPIO 15, but
+	 * it cannot handle interrupts on that GPIO line.  So we'll
+	 * have to poll it with a kernel timer.
+	 */
+
+	/* Make sure that the power button GPIO is set up as an input */
+	gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN);
+
+	/* Set the initial value for the power button IRQ handler */
+	power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
+
+	mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500));
+
+	return 0;
+}
+
+static void __exit dsmg600_power_exit(void)
+{
+	if (!(machine_is_dsmg600()))
+		return;
+
+	del_timer_sync(&dsmg600_power_timer);
+
+	free_irq(DSMG600_RB_IRQ, NULL);
+}
+
+module_init(dsmg600_power_init);
+module_exit(dsmg600_power_exit);
+
+MODULE_AUTHOR("Michael Westerhof <mwester@dls.net>");
+MODULE_DESCRIPTION("DSM-G600 Power/Reset driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/dsmg600-setup.c
===================================================================
--- /dev/null
+++ linux-2.6.21.1-armeb/arch/arm/mach-ixp4xx/dsmg600-setup.c
@@ -0,0 +1,175 @@
+/*
+ * DSM-G600 board-setup
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based ixdp425-setup.c:
+ *      Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ * Maintainers: http://www.nslu2-linux.org/
+ */
+
+#include <linux/kernel.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+
+static struct flash_platform_data dsmg600_flash_data = {
+	.map_name		= "cfi_probe",
+	.width			= 2,
+};
+
+static struct resource dsmg600_flash_resource = {
+	.flags			= IORESOURCE_MEM,
+};
+
+static struct platform_device dsmg600_flash = {
+	.name			= "IXP4XX-Flash",
+	.id			= 0,
+	.dev.platform_data	= &dsmg600_flash_data,
+	.num_resources		= 1,
+	.resource		= &dsmg600_flash_resource,
+};
+
+static struct ixp4xx_i2c_pins dsmg600_i2c_gpio_pins = {
+	.sda_pin		= DSMG600_SDA_PIN,
+	.scl_pin		= DSMG600_SCL_PIN,
+};
+
+static struct platform_device dsmg600_i2c_controller = {
+	.name			= "IXP4XX-I2C",
+	.id			= 0,
+	.dev.platform_data	= &dsmg600_i2c_gpio_pins,
+};
+
+#ifdef CONFIG_LEDS_CLASS
+static struct resource dsmg600_led_resources[] = {
+	{
+		.name           = "power",
+		.start          = DSMG600_LED_PWR_GPIO,
+		.end            = DSMG600_LED_PWR_GPIO,
+		.flags          = IXP4XX_GPIO_HIGH,
+	},
+	{
+		.name           = "wlan",
+		.start		= DSMG600_LED_WLAN_GPIO,
+		.end            = DSMG600_LED_WLAN_GPIO,
+		.flags          = IXP4XX_GPIO_LOW,
+	},
+};
+
+static struct platform_device dsmg600_leds = {
+        .name                   = "IXP4XX-GPIO-LED",
+        .id                     = -1,
+        .num_resources          = ARRAY_SIZE(dsmg600_led_resources),
+        .resource               = dsmg600_led_resources,
+};
+#endif
+
+static struct resource dsmg600_uart_resources[] = {
+	{
+		.start		= IXP4XX_UART1_BASE_PHYS,
+		.end		= IXP4XX_UART1_BASE_PHYS + 0x0fff,
+		.flags		= IORESOURCE_MEM,
+	},
+	{
+		.start		= IXP4XX_UART2_BASE_PHYS,
+		.end		= IXP4XX_UART2_BASE_PHYS + 0x0fff,
+		.flags		= IORESOURCE_MEM,
+	}
+};
+
+static struct plat_serial8250_port dsmg600_uart_data[] = {
+	{
+		.mapbase	= IXP4XX_UART1_BASE_PHYS,
+		.membase	= (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
+		.irq		= IRQ_IXP4XX_UART1,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.iotype		= UPIO_MEM,
+		.regshift	= 2,
+		.uartclk	= IXP4XX_UART_XTAL,
+	},
+	{
+		.mapbase	= IXP4XX_UART2_BASE_PHYS,
+		.membase	= (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
+		.irq		= IRQ_IXP4XX_UART2,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.iotype		= UPIO_MEM,
+		.regshift	= 2,
+		.uartclk	= IXP4XX_UART_XTAL,
+	},
+	{ }
+};
+
+static struct platform_device dsmg600_uart = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_PLATFORM,
+	.dev.platform_data	= dsmg600_uart_data,
+	.num_resources		= ARRAY_SIZE(dsmg600_uart_resources),
+	.resource		= dsmg600_uart_resources,
+};
+
+static struct platform_device *dsmg600_devices[] __initdata = {
+	&dsmg600_i2c_controller,
+	&dsmg600_flash,
+};
+
+static void dsmg600_power_off(void)
+{
+	/* enable the pwr cntl gpio */
+	gpio_line_config(DSMG600_PO_GPIO, IXP4XX_GPIO_OUT);
+
+	/* poweroff */
+	gpio_line_set(DSMG600_PO_GPIO, IXP4XX_GPIO_HIGH);
+}
+
+static void __init dsmg600_init(void)
+{
+	ixp4xx_sys_init();
+
+	/* Make sure that GPIO14 and GPIO15 are not used as clocks */
+	*IXP4XX_GPIO_GPCLKR = 0;
+
+	dsmg600_flash_resource.start = IXP4XX_EXP_BUS_BASE(0);
+	dsmg600_flash_resource.end =
+		IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;
+
+	pm_power_off = dsmg600_power_off;
+
+	/* The UART is required on the DSM-G600 (Redboot cannot use the
+	 * NIC) -- do it here so that it does *not* get removed if
+	 * platform_add_devices fails!
+         */
+        (void)platform_device_register(&dsmg600_uart);
+
+	platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices));
+
+#ifdef CONFIG_LEDS_CLASS
+        /* We don't care whether or not this works. */
+        (void)platform_device_register(&dsmg600_leds);
+#endif
+}
+
+static void __init dsmg600_fixup(struct machine_desc *desc,
+                struct tag *tags, char **cmdline, struct meminfo *mi)
+{
+       /* The xtal on this machine is non-standard. */
+        ixp4xx_timer_freq = DSMG600_FREQ;
+}
+
+MACHINE_START(DSMG600, "D-Link DSM-G600 RevA")
+	/* Maintainer: www.nslu2-linux.org */
+	.phys_io	= IXP4XX_PERIPHERAL_BASE_PHYS,
+	.io_pg_offst	= ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xFFFC,
+	.boot_params	= 0x00000100,
+	.fixup          = dsmg600_fixup,
+	.map_io		= ixp4xx_map_io,
+	.init_irq	= ixp4xx_init_irq,
+	.timer          = &ixp4xx_timer,
+	.init_machine	= dsmg600_init,
+MACHINE_END
Index: linux-2.6.21.1-armeb/include/asm-arm/arch-ixp4xx/dsmg600.h
===================================================================
--- /dev/null
+++ linux-2.6.21.1-armeb/include/asm-arm/arch-ixp4xx/dsmg600.h
@@ -0,0 +1,57 @@
+/*
+ * DSM-G600 platform specific definitions
+ *
+ * Copyright (C) 2006 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on ixdp425.h:
+ *	Copyright 2004 (C) MontaVista, Software, Inc.
+ *
+ * This file is licensed under  the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H__
+#error "Do not include this directly, instead #include <asm/hardware.h>"
+#endif
+
+#define DSMG600_SDA_PIN		5
+#define DSMG600_SCL_PIN		4
+
+/*
+ * DSMG600 PCI IRQs
+ */
+#define DSMG600_PCI_MAX_DEV	4
+#define DSMG600_PCI_IRQ_LINES	3
+
+
+/* PCI controller GPIO to IRQ pin mappings */
+#define DSMG600_PCI_INTA_PIN	11
+#define DSMG600_PCI_INTB_PIN	10
+#define DSMG600_PCI_INTC_PIN	9
+#define DSMG600_PCI_INTD_PIN	8
+#define DSMG600_PCI_INTE_PIN	7
+#define DSMG600_PCI_INTF_PIN	6
+
+/* DSM-G600 Timer Setting */
+#define DSMG600_FREQ 66000000
+
+/* Buttons */
+
+#define DSMG600_PB_GPIO		15	/* power button */
+#define DSMG600_PB_BM		(1L << DSMG600_PB_GPIO)
+
+#define DSMG600_RB_GPIO		3	/* reset button */
+
+#define DSMG600_RB_IRQ		IRQ_IXP4XX_GPIO3
+
+#define DSMG600_PO_GPIO		2	/* power off */
+
+/* LEDs */
+
+#define DSMG600_LED_PWR_GPIO	0
+#define DSMG600_LED_PWR_BM	(1L << DSMG600_LED_PWR_GPIO)
+
+#define DSMG600_LED_WLAN_GPIO	14
+#define DSMG600_LED_WLAN_BM	(1L << DSMG600_LED_WLAN_GPIO)
Index: linux-2.6.21.1-armeb/include/asm-arm/arch-ixp4xx/hardware.h
===================================================================
--- linux-2.6.21.1-armeb.orig/include/asm-arm/arch-ixp4xx/hardware.h
+++ linux-2.6.21.1-armeb/include/asm-arm/arch-ixp4xx/hardware.h
@@ -47,5 +47,6 @@
 #include "prpmc1100.h"
 #include "nslu2.h"
 #include "nas100d.h"
+#include "dsmg600.h"
 
 #endif  /* _ASM_ARCH_HARDWARE_H */
Index: linux-2.6.21.1-armeb/include/asm-arm/arch-ixp4xx/irqs.h
===================================================================
--- linux-2.6.21.1-armeb.orig/include/asm-arm/arch-ixp4xx/irqs.h
+++ linux-2.6.21.1-armeb/include/asm-arm/arch-ixp4xx/irqs.h
@@ -118,4 +118,14 @@
 #define        IRQ_NAS100D_PCI_INTD    IRQ_IXP4XX_GPIO8
 #define        IRQ_NAS100D_PCI_INTE    IRQ_IXP4XX_GPIO7
 
+/*
+ * D-Link DSM-G600 RevA board IRQs
+ */
+#define        IRQ_DSMG600_PCI_INTA    IRQ_IXP4XX_GPIO11
+#define        IRQ_DSMG600_PCI_INTB    IRQ_IXP4XX_GPIO10
+#define        IRQ_DSMG600_PCI_INTC    IRQ_IXP4XX_GPIO9
+#define        IRQ_DSMG600_PCI_INTD    IRQ_IXP4XX_GPIO8
+#define        IRQ_DSMG600_PCI_INTE    IRQ_IXP4XX_GPIO7
+#define        IRQ_DSMG600_PCI_INTF    IRQ_IXP4XX_GPIO6
+
 #endif
