Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/arch/arm/mach-sa1100/Kconfig b/arch/arm/mach-sa1100/Kconfig
new file mode 100644
index 0000000..50cde57
--- /dev/null
+++ b/arch/arm/mach-sa1100/Kconfig
@@ -0,0 +1,162 @@
+if ARCH_SA1100
+
+menu "SA11x0 Implementations"
+
+config SA1100_ASSABET
+	bool "Assabet"
+	help
+	  Say Y here if you are using the Intel(R) StrongARM(R) SA-1110
+	  Microprocessor Development Board (also known as the Assabet).
+
+config ASSABET_NEPONSET
+	bool "Include support for Neponset"
+	depends on SA1100_ASSABET
+	select SA1111
+	help
+	  Say Y here if you are using the Intel(R) StrongARM(R) SA-1110
+	  Microprocessor Development Board (Assabet)  with the SA-1111
+	  Development Board (Nepon).
+
+config SA1100_CERF
+	bool "CerfBoard"
+	help
+	  The Intrinsyc CerfBoard is based on the StrongARM 1110 (Discontinued).
+	  More information is available at:
+	  <http://www.intrinsyc.com/products/cerfboard/>.
+
+	  Say Y if configuring for an Intrinsyc CerfBoard.
+	  Say N otherwise.
+
+choice
+	prompt "Cerf Flash available"
+	depends on SA1100_CERF
+	default SA1100_CERF_FLASH_8MB
+
+config SA1100_CERF_FLASH_8MB
+	bool "8MB"
+
+config SA1100_CERF_FLASH_16MB
+	bool "16MB"
+
+config SA1100_CERF_FLASH_32MB
+	bool "32MB"
+
+endchoice
+
+config SA1100_COLLIE
+	bool "Sharp Zaurus SL5500"
+	select SHARP_LOCOMO
+	select SHARP_SCOOP
+	select SHARP_PARAM
+	help
+	  Say Y here to support the Sharp Zaurus SL5500 PDAs.
+
+config SA1100_H3100
+	bool "Compaq iPAQ H3100"
+	help
+	  Say Y here if you intend to run this kernel on the Compaq iPAQ
+	  H3100 handheld computer.  Information about this machine and the
+	  Linux port to this machine can be found at:
+
+	  <http://www.handhelds.org/Compaq/index.html#iPAQ_H3100>
+	  <http://www.compaq.com/products/handhelds/pocketpc/>
+
+config SA1100_H3600
+	bool "Compaq iPAQ H3600/H3700"
+	help
+	  Say Y here if you intend to run this kernel on the Compaq iPAQ
+	  H3600 handheld computer.  Information about this machine and the
+	  Linux port to this machine can be found at:
+
+	  <http://www.handhelds.org/Compaq/index.html#iPAQ_H3600>
+	  <http://www.compaq.com/products/handhelds/pocketpc/>
+
+config SA1100_H3800
+	bool "Compaq iPAQ H3800"
+	help
+	  Say Y here if you intend to run this kernel on the Compaq iPAQ H3800
+	  series handheld computer.  Information about this machine and the
+	  Linux port to this machine can be found at:
+
+	  <http://www.handhelds.org/Compaq/index.html#iPAQ_H3800>
+	  <http://www.compaq.com/products/handhelds/pocketpc/>
+
+config SA1100_H3XXX
+	bool
+	depends on SA1100_H3100 || SA1100_H3600 || SA1100_H3800
+	default y
+
+config SA1100_BADGE4
+	bool "HP Labs BadgePAD 4"
+	select SA1111
+	help
+	  Say Y here if you want to build a kernel for the HP Laboratories
+	  BadgePAD 4.
+
+config SA1100_JORNADA720
+	bool "HP Jornada 720"
+	select SA1111
+	help
+	  Say Y here if you want to build a kernel for the HP Jornada 720
+	  handheld computer.  See <http://www.hp.com/jornada/products/720>
+	  for details.
+
+config SA1100_HACKKIT
+	bool "HackKit Core CPU Board"
+	help
+	  Say Y here to support the HackKit Core CPU Board
+	  <http://hackkit.eletztrick.de>;
+
+config SA1100_LART
+	bool "LART"
+	help
+	  Say Y here if you are using the Linux Advanced Radio Terminal
+	  (also known as the LART).  See <http://www.lart.tudelft.nl/> for
+	  information on the LART.
+
+config SA1100_PLEB
+	bool "PLEB"
+	help
+	  Say Y here if you are using version 1 of the Portable Linux
+	  Embedded Board (also known as PLEB).
+	  See <http://www.disy.cse.unsw.edu.au/Hardware/PLEB/>
+	  for more information.
+
+config SA1100_SHANNON
+	bool "Shannon"
+	help
+	  The Shannon (also known as a Tuxscreen, and also as a IS2630) was a
+	  limited edition webphone produced by Philips. The Shannon is a SA1100
+	  platform with a 640x480 LCD, touchscreen, CIR keyboard, PCMCIA slots,
+	  and a telco interface.
+
+config SA1100_SIMPAD
+	bool "Simpad"
+	help
+	  The SIEMENS webpad SIMpad is based on the StrongARM 1110. There
+	  are two different versions CL4 and SL4. CL4 has 32MB RAM and 16MB
+	  FLASH. The SL4 version got 64 MB RAM and 32 MB FLASH and a
+	  PCMCIA-Slot. The version for the Germany Telecom (DTAG) is the same
+	  like CL4 in additional it has a PCMCIA-Slot. For more information
+	  visit <http://www.my-siemens.com/> or <http://www.siemens.ch/>.
+
+config SA1100_SSP
+	tristate "Generic PIO SSP"
+	help
+	  Say Y here to enable support for the generic PIO SSP driver.
+	  This isn't for audio support, but for attached sensors and
+	  other devices, eg for BadgePAD 4 sensor support, or Jornada
+	  720 touchscreen support.
+
+config H3600_SLEEVE
+	tristate "Compaq iPAQ Handheld sleeve support"
+	depends on SA1100_H3600
+	help
+	  Choose this option to enable support for extension packs (sleeves)
+	  for the Compaq iPAQ H3XXX series of handheld computers.  This option
+	  is required for the CF, PCMCIA, Bluetooth and GSM/GPRS extension
+	  packs.
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile
new file mode 100644
index 0000000..e4a4a3e
--- /dev/null
+++ b/arch/arm/mach-sa1100/Makefile
@@ -0,0 +1,53 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Common support
+obj-y := generic.o irq.o dma.o time.o
+obj-m :=
+obj-n :=
+obj-  :=
+led-y := leds.o
+
+obj-$(CONFIG_CPU_FREQ_SA1100)		+= cpu-sa1100.o
+obj-$(CONFIG_CPU_FREQ_SA1110)		+= cpu-sa1110.o
+
+# Specific board support
+obj-$(CONFIG_SA1100_ASSABET)		+= assabet.o
+led-$(CONFIG_SA1100_ASSABET)		+= leds-assabet.o
+obj-$(CONFIG_ASSABET_NEPONSET)		+= neponset.o
+
+obj-$(CONFIG_SA1100_BADGE4)		+= badge4.o
+led-$(CONFIG_SA1100_BADGE4)		+= leds-badge4.o
+
+obj-$(CONFIG_SA1100_CERF)		+= cerf.o
+led-$(CONFIG_SA1100_CERF)		+= leds-cerf.o
+
+obj-$(CONFIG_SA1100_COLLIE)		+= collie.o
+
+obj-$(CONFIG_SA1100_H3600)		+= h3600.o
+
+obj-$(CONFIG_SA1100_HACKKIT)		+= hackkit.o
+led-$(CONFIG_SA1100_HACKKIT)		+= leds-hackkit.o
+
+obj-$(CONFIG_SA1100_JORNADA720)		+= jornada720.o
+
+obj-$(CONFIG_SA1100_LART)		+= lart.o
+led-$(CONFIG_SA1100_LART)		+= leds-lart.o
+
+obj-$(CONFIG_SA1100_PLEB)		+= pleb.o
+
+obj-$(CONFIG_SA1100_SHANNON)		+= shannon.o
+
+obj-$(CONFIG_SA1100_SIMPAD)		+= simpad.o
+led-$(CONFIG_SA1100_SIMPAD)		+= leds-simpad.o
+
+# LEDs support
+obj-$(CONFIG_LEDS) += $(led-y)
+
+# SA1110 USB client support
+#obj-$(CONFIG_SA1100_USB)		+= usb/
+
+# Miscelaneous functions
+obj-$(CONFIG_PM)			+= pm.o sleep.o
+obj-$(CONFIG_SA1100_SSP)		+= ssp.o
diff --git a/arch/arm/mach-sa1100/Makefile.boot b/arch/arm/mach-sa1100/Makefile.boot
new file mode 100644
index 0000000..a56ad04
--- /dev/null
+++ b/arch/arm/mach-sa1100/Makefile.boot
@@ -0,0 +1,7 @@
+   zreladdr-y	:= 0xc0008000
+ifeq ($(CONFIG_ARCH_SA1100),y)
+   zreladdr-$(CONFIG_SA1111)		:= 0xc0208000
+endif
+params_phys-y	:= 0xc0000100
+initrd_phys-y	:= 0xc0800000
+
diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c
new file mode 100644
index 0000000..bedf88f
--- /dev/null
+++ b/arch/arm/mach-sa1100/assabet.c
@@ -0,0 +1,441 @@
+/*
+ * linux/arch/arm/mach-sa1100/assabet.c
+ *
+ * Author: Nicolas Pitre
+ *
+ * This file contains all Assabet-specific tweaks.
+ *
+ * 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/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irda.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+#include <asm/arch/assabet.h>
+
+#include "generic.h"
+
+#define ASSABET_BCR_DB1110 \
+	(ASSABET_BCR_SPK_OFF    | ASSABET_BCR_QMUTE     | \
+	 ASSABET_BCR_LED_GREEN  | ASSABET_BCR_LED_RED   | \
+	 ASSABET_BCR_RS232EN    | ASSABET_BCR_LCD_12RGB | \
+	 ASSABET_BCR_IRDA_MD0)
+
+#define ASSABET_BCR_DB1111 \
+	(ASSABET_BCR_SPK_OFF    | ASSABET_BCR_QMUTE     | \
+	 ASSABET_BCR_LED_GREEN  | ASSABET_BCR_LED_RED   | \
+	 ASSABET_BCR_RS232EN    | ASSABET_BCR_LCD_12RGB | \
+	 ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_STEREO_LB | \
+	 ASSABET_BCR_IRDA_MD0   | ASSABET_BCR_CF_RST)
+
+unsigned long SCR_value = ASSABET_SCR_INIT;
+EXPORT_SYMBOL(SCR_value);
+
+static unsigned long BCR_value = ASSABET_BCR_DB1110;
+
+void ASSABET_BCR_frob(unsigned int mask, unsigned int val)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	BCR_value = (BCR_value & ~mask) | val;
+	ASSABET_BCR = BCR_value;
+	local_irq_restore(flags);
+}
+
+EXPORT_SYMBOL(ASSABET_BCR_frob);
+
+static void assabet_backlight_power(int on)
+{
+#ifndef ASSABET_PAL_VIDEO
+	if (on)
+		ASSABET_BCR_set(ASSABET_BCR_LIGHT_ON);
+	else
+#endif
+		ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON);
+}
+
+/*
+ * Turn on/off the backlight.  When turning the backlight on,
+ * we wait 500us after turning it on so we don't cause the
+ * supplies to droop when we enable the LCD controller (and
+ * cause a hard reset.)
+ */
+static void assabet_lcd_power(int on)
+{
+#ifndef ASSABET_PAL_VIDEO
+	if (on) {
+		ASSABET_BCR_set(ASSABET_BCR_LCD_ON);
+		udelay(500);
+	} else
+#endif
+		ASSABET_BCR_clear(ASSABET_BCR_LCD_ON);
+}
+
+
+/*
+ * Assabet flash support code.
+ */
+
+#ifdef ASSABET_REV_4
+/*
+ * Phase 4 Assabet has two 28F160B3 flash parts in bank 0:
+ */
+static struct mtd_partition assabet_partitions[] = {
+	{
+		.name		= "bootloader",
+		.size		= 0x00020000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "bootloader params",
+		.size		= 0x00020000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "jffs",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+	}
+};
+#else
+/*
+ * Phase 5 Assabet has two 28F128J3A flash parts in bank 0:
+ */
+static struct mtd_partition assabet_partitions[] = {
+	{
+		.name		= "bootloader",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "bootloader params",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "jffs",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+	}
+};
+#endif
+
+static struct flash_platform_data assabet_flash_data = {
+	.map_name	= "cfi_probe",
+	.parts		= assabet_partitions,
+	.nr_parts	= ARRAY_SIZE(assabet_partitions),
+};
+
+static struct resource assabet_flash_resources[] = {
+	{
+		.start	= SA1100_CS0_PHYS,
+		.end	= SA1100_CS0_PHYS + SZ_32M - 1,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= SA1100_CS1_PHYS,
+		.end	= SA1100_CS1_PHYS + SZ_32M - 1,
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+
+/*
+ * Assabet IrDA support code.
+ */
+
+static int assabet_irda_set_power(struct device *dev, unsigned int state)
+{
+	static unsigned int bcr_state[4] = {
+		ASSABET_BCR_IRDA_MD0,
+		ASSABET_BCR_IRDA_MD1|ASSABET_BCR_IRDA_MD0,
+		ASSABET_BCR_IRDA_MD1,
+		0
+	};
+
+	if (state < 4) {
+		state = bcr_state[state];
+		ASSABET_BCR_clear(state ^ (ASSABET_BCR_IRDA_MD1|
+					   ASSABET_BCR_IRDA_MD0));
+		ASSABET_BCR_set(state);
+	}
+	return 0;
+}
+
+static void assabet_irda_set_speed(struct device *dev, unsigned int speed)
+{
+	if (speed < 4000000)
+		ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL);
+	else
+		ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL);
+}
+
+static struct irda_platform_data assabet_irda_data = {
+	.set_power	= assabet_irda_set_power,
+	.set_speed	= assabet_irda_set_speed,
+};
+
+static void __init assabet_init(void)
+{
+	/*
+	 * Ensure that the power supply is in "high power" mode.
+	 */
+	GPDR |= GPIO_GPIO16;
+	GPSR = GPIO_GPIO16;
+
+	/*
+	 * Ensure that these pins are set as outputs and are driving
+	 * logic 0.  This ensures that we won't inadvertently toggle
+	 * the WS latch in the CPLD, and we don't float causing
+	 * excessive power drain.  --rmk
+	 */
+	GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM;
+	GPCR = GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM;
+
+	/*
+	 * Set up registers for sleep mode.
+	 */
+	PWER = PWER_GPIO0;
+	PGSR = 0;
+	PCFR = 0;
+	PSDR = 0;
+	PPDR |= PPC_TXD3 | PPC_TXD1;
+	PPSR |= PPC_TXD3 | PPC_TXD1;
+
+	sa1100fb_lcd_power = assabet_lcd_power;
+	sa1100fb_backlight_power = assabet_backlight_power;
+
+	if (machine_has_neponset()) {
+		/*
+		 * Angel sets this, but other bootloaders may not.
+		 *
+		 * This must precede any driver calls to BCR_set()
+		 * or BCR_clear().
+		 */
+		ASSABET_BCR = BCR_value = ASSABET_BCR_DB1111;
+
+#ifndef CONFIG_ASSABET_NEPONSET
+		printk( "Warning: Neponset detected but full support "
+			"hasn't been configured in the kernel\n" );
+#endif
+	}
+
+	sa11x0_set_flash_data(&assabet_flash_data, assabet_flash_resources,
+			      ARRAY_SIZE(assabet_flash_resources));
+	sa11x0_set_irda_data(&assabet_irda_data);
+}
+
+/*
+ * On Assabet, we must probe for the Neponset board _before_
+ * paging_init() has occurred to actually determine the amount
+ * of RAM available.  To do so, we map the appropriate IO section
+ * in the page table here in order to access GPIO registers.
+ */
+static void __init map_sa1100_gpio_regs( void )
+{
+	unsigned long phys = __PREG(GPLR) & PMD_MASK;
+	unsigned long virt = io_p2v(phys);
+	int prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO);
+	pmd_t *pmd;
+
+	pmd = pmd_offset(pgd_offset_k(virt), virt);
+	*pmd = __pmd(phys | prot);
+	flush_pmd_entry(pmd);
+}
+
+/*
+ * Read System Configuration "Register"
+ * (taken from "Intel StrongARM SA-1110 Microprocessor Development Board
+ * User's Guide", section 4.4.1)
+ *
+ * This same scan is performed in arch/arm/boot/compressed/head-sa1100.S
+ * to set up the serial port for decompression status messages. We
+ * repeat it here because the kernel may not be loaded as a zImage, and
+ * also because it's a hassle to communicate the SCR value to the kernel
+ * from the decompressor.
+ *
+ * Note that IRQs are guaranteed to be disabled.
+ */
+static void __init get_assabet_scr(void)
+{
+	unsigned long scr, i;
+
+	GPDR |= 0x3fc;			/* Configure GPIO 9:2 as outputs */
+	GPSR = 0x3fc;			/* Write 0xFF to GPIO 9:2 */
+	GPDR &= ~(0x3fc);		/* Configure GPIO 9:2 as inputs */
+	for(i = 100; i--; scr = GPLR);	/* Read GPIO 9:2 */
+	GPDR |= 0x3fc;			/*  restore correct pin direction */
+	scr &= 0x3fc;			/* save as system configuration byte. */
+	SCR_value = scr;
+}
+
+static void __init
+fixup_assabet(struct machine_desc *desc, struct tag *tags,
+	      char **cmdline, struct meminfo *mi)
+{
+	/* This must be done before any call to machine_has_neponset() */
+	map_sa1100_gpio_regs();
+	get_assabet_scr();
+
+	if (machine_has_neponset())
+		printk("Neponset expansion board detected\n");
+}
+
+
+static void assabet_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+{
+	if (port->mapbase == _Ser1UTCR0) {
+		if (state)
+			ASSABET_BCR_clear(ASSABET_BCR_RS232EN |
+					  ASSABET_BCR_COM_RTS |
+					  ASSABET_BCR_COM_DTR);
+		else
+			ASSABET_BCR_set(ASSABET_BCR_RS232EN |
+					ASSABET_BCR_COM_RTS |
+					ASSABET_BCR_COM_DTR);
+	}
+}
+
+/*
+ * Assabet uses COM_RTS and COM_DTR for both UART1 (com port)
+ * and UART3 (radio module).  We only handle them for UART1 here.
+ */
+static void assabet_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+	if (port->mapbase == _Ser1UTCR0) {
+		u_int set = 0, clear = 0;
+
+		if (mctrl & TIOCM_RTS)
+			clear |= ASSABET_BCR_COM_RTS;
+		else
+			set |= ASSABET_BCR_COM_RTS;
+
+		if (mctrl & TIOCM_DTR)
+			clear |= ASSABET_BCR_COM_DTR;
+		else
+			set |= ASSABET_BCR_COM_DTR;
+
+		ASSABET_BCR_clear(clear);
+		ASSABET_BCR_set(set);
+	}
+}
+
+static u_int assabet_get_mctrl(struct uart_port *port)
+{
+	u_int ret = 0;
+	u_int bsr = ASSABET_BSR;
+
+	/* need 2 reads to read current value */
+	bsr = ASSABET_BSR;
+
+	if (port->mapbase == _Ser1UTCR0) {
+		if (bsr & ASSABET_BSR_COM_DCD)
+			ret |= TIOCM_CD;
+		if (bsr & ASSABET_BSR_COM_CTS)
+			ret |= TIOCM_CTS;
+		if (bsr & ASSABET_BSR_COM_DSR)
+			ret |= TIOCM_DSR;
+	} else if (port->mapbase == _Ser3UTCR0) {
+		if (bsr & ASSABET_BSR_RAD_DCD)
+			ret |= TIOCM_CD;
+		if (bsr & ASSABET_BSR_RAD_CTS)
+			ret |= TIOCM_CTS;
+		if (bsr & ASSABET_BSR_RAD_DSR)
+			ret |= TIOCM_DSR;
+		if (bsr & ASSABET_BSR_RAD_RI)
+			ret |= TIOCM_RI;
+	} else {
+		ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
+	}
+
+	return ret;
+}
+
+static struct sa1100_port_fns assabet_port_fns __initdata = {
+	.set_mctrl	= assabet_set_mctrl,
+	.get_mctrl	= assabet_get_mctrl,
+	.pm		= assabet_uart_pm,
+};
+
+static struct map_desc assabet_io_desc[] __initdata = {
+ /* virtual     physical    length      type */
+  { 0xf1000000, 0x12000000, 0x00100000, MT_DEVICE }, /* Board Control Register */
+  { 0xf2800000, 0x4b800000, 0x00800000, MT_DEVICE }  /* MQ200 */
+};
+
+static void __init assabet_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(assabet_io_desc, ARRAY_SIZE(assabet_io_desc));
+
+	/*
+	 * Set SUS bit in SDCR0 so serial port 1 functions.
+	 * Its called GPCLKR0 in my SA1110 manual.
+	 */
+	Ser1SDCR0 |= SDCR0_SUS;
+
+	if (machine_has_neponset()) {
+#ifdef CONFIG_ASSABET_NEPONSET
+		extern void neponset_map_io(void);
+
+		/*
+		 * We map Neponset registers even if it isn't present since
+		 * many drivers will try to probe their stuff (and fail).
+		 * This is still more friendly than a kernel paging request
+		 * crash.
+		 */
+		neponset_map_io();
+#endif
+	} else {
+		sa1100_register_uart_fns(&assabet_port_fns);
+	}
+
+	/*
+	 * When Neponset is attached, the first UART should be
+	 * UART3.  That's what Angel is doing and many documents
+	 * are stating this.
+	 *
+	 * We do the Neponset mapping even if Neponset support
+	 * isn't compiled in so the user will still get something on
+	 * the expected physical serial port.
+	 *
+	 * We no longer do this; not all boot loaders support it,
+	 * and UART3 appears to be somewhat unreliable with blob.
+	 */
+	sa1100_register_uart(0, 1);
+	sa1100_register_uart(2, 3);
+}
+
+
+MACHINE_START(ASSABET, "Intel-Assabet")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	FIXUP(fixup_assabet)
+	MAPIO(assabet_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= assabet_init,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c
new file mode 100644
index 0000000..6a60b49
--- /dev/null
+++ b/arch/arm/mach-sa1100/badge4.c
@@ -0,0 +1,293 @@
+/*
+ * linux/arch/arm/mach-sa1100/badge4.c
+ *
+ * BadgePAD 4 specific initialization
+ *
+ *   Tim Connors <connors@hpl.hp.com>
+ *   Christopher Hoover <ch@hpl.hp.com>
+ *
+ * Copyright (C) 2002 Hewlett-Packard Company
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/errno.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+#include <asm/arch/irqs.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/sa1111.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include <asm/arch/badge4.h>
+
+#include "generic.h"
+
+static struct resource sa1111_resources[] = {
+	[0] = {
+		.start		= BADGE4_SA1111_BASE,
+		.end		= BADGE4_SA1111_BASE + 0x00001fff,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= BADGE4_IRQ_GPIO_SA1111,
+		.end		= BADGE4_IRQ_GPIO_SA1111,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static u64 sa1111_dmamask = 0xffffffffUL;
+
+static struct platform_device sa1111_device = {
+	.name		= "sa1111",
+	.id		= 0,
+	.dev		= {
+		.dma_mask = &sa1111_dmamask,
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa1111_resources),
+	.resource	= sa1111_resources,
+};
+
+static struct platform_device *devices[] __initdata = {
+	&sa1111_device,
+};
+
+static int __init badge4_sa1111_init(void)
+{
+	/*
+	 * Ensure that the memory bus request/grant signals are setup,
+	 * and the grant is held in its inactive state
+	 */
+	sa1110_mb_disable();
+
+	/*
+	 * Probe for SA1111.
+	 */
+	return platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
+
+/*
+ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit)
+ *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
+ *   Sixty-three 32 KiW Main Blocks (4032 Ki b)
+ *
+ * <or>
+ *
+ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit)
+ *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
+ *   One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b)
+ */
+static struct mtd_partition badge4_partitions[] = {
+        {
+                .name           = "BLOB boot loader",
+                .offset         = 0,
+                .size           = 0x0000A000
+        }, {
+                .name           = "params",
+                .offset         = MTDPART_OFS_APPEND,
+                .size           = 0x00006000
+        }, {
+                .name           = "root",
+                .offset         = MTDPART_OFS_APPEND,
+                .size           = MTDPART_SIZ_FULL
+        }
+};
+
+static struct flash_platform_data badge4_flash_data = {
+	.map_name	= "cfi_probe",
+	.parts		= badge4_partitions,
+	.nr_parts	= ARRAY_SIZE(badge4_partitions),
+};
+
+static struct resource badge4_flash_resource = {
+	.start		= SA1100_CS0_PHYS,
+	.end		= SA1100_CS0_PHYS + SZ_64M - 1,
+	.flags		= IORESOURCE_MEM,
+};
+
+static int five_v_on __initdata = 0;
+
+static int __init five_v_on_setup(char *ignore)
+{
+        five_v_on = 1;
+	return 1;
+}
+__setup("five_v_on", five_v_on_setup);
+
+
+static int __init badge4_init(void)
+{
+	int ret;
+
+	if (!machine_is_badge4())
+		return -ENODEV;
+
+	/* LCD */
+	GPCR  = (BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 |
+		 BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 |
+		 BADGE4_GPIO_LGP6 | BADGE4_GPIO_LGP7 |
+		 BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 |
+		 BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID |
+		 BADGE4_GPIO_GPC_VID);
+	GPDR &= ~BADGE4_GPIO_INT_VID;
+	GPDR |= (BADGE4_GPIO_LGP2 | BADGE4_GPIO_LGP3 |
+		 BADGE4_GPIO_LGP4 | BADGE4_GPIO_LGP5 |
+		 BADGE4_GPIO_LGP6 | BADGE4_GPIO_LGP7 |
+		 BADGE4_GPIO_LGP8 | BADGE4_GPIO_LGP9 |
+		 BADGE4_GPIO_GPA_VID | BADGE4_GPIO_GPB_VID |
+		 BADGE4_GPIO_GPC_VID);
+
+	/* SDRAM SPD i2c */
+	GPCR  = (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL);
+	GPDR |= (BADGE4_GPIO_SDSDA | BADGE4_GPIO_SDSCL);
+
+	/* uart */
+	GPCR  = (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2);
+	GPDR |= (BADGE4_GPIO_UART_HS1 | BADGE4_GPIO_UART_HS2);
+
+	/* CPLD muxsel0 input for mux/adc chip select */
+	GPCR  = BADGE4_GPIO_MUXSEL0;
+	GPDR |= BADGE4_GPIO_MUXSEL0;
+
+	/* test points: J5, J6 as inputs, J7 outputs */
+	GPDR &= ~(BADGE4_GPIO_TESTPT_J5 | BADGE4_GPIO_TESTPT_J6);
+	GPCR  = BADGE4_GPIO_TESTPT_J7;
+	GPDR |= BADGE4_GPIO_TESTPT_J7;
+
+ 	/* 5V supply rail. */
+ 	GPCR  = BADGE4_GPIO_PCMEN5V;		/* initially off */
+  	GPDR |= BADGE4_GPIO_PCMEN5V;
+
+	/* CPLD sdram type inputs; set up by blob */
+	//GPDR |= (BADGE4_GPIO_SDTYP1 | BADGE4_GPIO_SDTYP0);
+	printk(KERN_DEBUG __FILE__ ": SDRAM CPLD typ1=%d typ0=%d\n",
+	       !!(GPLR & BADGE4_GPIO_SDTYP1),
+	       !!(GPLR & BADGE4_GPIO_SDTYP0));
+
+	/* SA1111 reset pin; set up by blob */
+	//GPSR  = BADGE4_GPIO_SA1111_NRST;
+	//GPDR |= BADGE4_GPIO_SA1111_NRST;
+
+
+	/* power management cruft */
+	PGSR = 0;
+	PWER = 0;
+	PCFR = 0;
+	PSDR = 0;
+
+	PWER |= PWER_GPIO26;	/* wake up on an edge from TESTPT_J5 */
+	PWER |= PWER_RTC;	/* wake up if rtc fires */
+
+	/* drive sa1111_nrst during sleep */
+	PGSR |= BADGE4_GPIO_SA1111_NRST;
+	/* drive CPLD as is during sleep */
+	PGSR |= (GPLR & (BADGE4_GPIO_SDTYP0|BADGE4_GPIO_SDTYP1));
+
+
+	/* Now bring up the SA-1111. */
+	ret = badge4_sa1111_init();
+	if (ret < 0)
+		printk(KERN_ERR
+		       "%s: SA-1111 initialization failed (%d)\n",
+		       __FUNCTION__, ret);
+
+
+	/* maybe turn on 5v0 from the start */
+	badge4_set_5V(BADGE4_5V_INITIALLY, five_v_on);
+
+	sa11x0_set_flash_data(&badge4_flash_data, &badge4_flash_resource, 1);
+
+	return 0;
+}
+
+arch_initcall(badge4_init);
+
+
+static unsigned badge4_5V_bitmap = 0;
+
+void badge4_set_5V(unsigned subsystem, int on)
+{
+	unsigned long flags;
+	unsigned old_5V_bitmap;
+
+	local_irq_save(flags);
+
+	old_5V_bitmap = badge4_5V_bitmap;
+
+	if (on) {
+		badge4_5V_bitmap |= subsystem;
+	} else {
+		badge4_5V_bitmap &= ~subsystem;
+	}
+
+	/* detect on->off and off->on transitions */
+	if ((!old_5V_bitmap) && (badge4_5V_bitmap)) {
+		/* was off, now on */
+		printk(KERN_INFO "%s: enabling 5V supply rail\n", __FUNCTION__);
+		GPSR = BADGE4_GPIO_PCMEN5V;
+	} else if ((old_5V_bitmap) && (!badge4_5V_bitmap)) {
+		/* was on, now off */
+		printk(KERN_INFO "%s: disabling 5V supply rail\n", __FUNCTION__);
+		GPCR = BADGE4_GPIO_PCMEN5V;
+	}
+
+	local_irq_restore(flags);
+}
+EXPORT_SYMBOL(badge4_set_5V);
+
+
+static struct map_desc badge4_io_desc[] __initdata = {
+  /*  virtual    physical    length    type */
+  {0xf1000000, 0x08000000, 0x00100000, MT_DEVICE },/* SRAM  bank 1 */
+  {0xf2000000, 0x10000000, 0x00100000, MT_DEVICE },/* SRAM  bank 2 */
+  {0xf4000000, 0x48000000, 0x00100000, MT_DEVICE } /* SA-1111      */
+};
+
+static void
+badge4_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+{
+	if (!state) {
+		Ser1SDCR0 |= SDCR0_UART;
+	}
+}
+
+static struct sa1100_port_fns badge4_port_fns __initdata = {
+	//.get_mctrl	= badge4_get_mctrl,
+	//.set_mctrl	= badge4_set_mctrl,
+	.pm		= badge4_uart_pm,
+};
+
+static void __init badge4_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(badge4_io_desc, ARRAY_SIZE(badge4_io_desc));
+
+	sa1100_register_uart_fns(&badge4_port_fns);
+	sa1100_register_uart(0, 3);
+	sa1100_register_uart(1, 1);
+}
+
+MACHINE_START(BADGE4, "Hewlett-Packard Laboratories BadgePAD 4")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(badge4_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c
new file mode 100644
index 0000000..f8edde5
--- /dev/null
+++ b/arch/arm/mach-sa1100/cerf.c
@@ -0,0 +1,132 @@
+/*
+ * linux/arch/arm/mach-sa1100/cerf.c
+ *
+ * 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.
+ *
+ * Apr-2003 : Removed some old PDA crud [FB]
+ * Oct-2003 : Added uart2 resource [FB]
+ * Jan-2004 : Removed io map for flash [FB]
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/setup.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include <asm/arch/cerf.h>
+#include "generic.h"
+
+static struct resource cerfuart2_resources[] = {
+	[0] = {
+		.start	= 0x80030000,
+		.end	= 0x8003ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device cerfuart2_device = {
+	.name		= "sa11x0-uart",
+	.id		= 2,
+	.num_resources	= ARRAY_SIZE(cerfuart2_resources),
+	.resource	= cerfuart2_resources,
+};
+
+static struct platform_device *cerf_devices[] __initdata = {
+	&cerfuart2_device,
+};
+
+#ifdef CONFIG_SA1100_CERF_FLASH_32MB
+#  define CERF_FLASH_SIZE	0x02000000
+#elif defined CONFIG_SA1100_CERF_FLASH_16MB
+#  define CERF_FLASH_SIZE	0x01000000
+#elif defined CONFIG_SA1100_CERF_FLASH_8MB
+#  define CERF_FLASH_SIZE	0x00800000
+#else
+#  error "Undefined flash size for CERF"
+#endif
+
+static struct mtd_partition cerf_partitions[] = {
+	{
+		.name		= "Bootloader",
+		.size		= 0x00020000,
+		.offset		= 0x00000000,
+	}, {
+		.name		= "Params",
+		.size		= 0x00040000,
+		.offset		= 0x00020000,
+	}, {
+		.name		= "Kernel",
+		.size		= 0x00100000,
+		.offset		= 0x00060000,
+	}, {
+		.name		= "Filesystem",
+		.size		= CERF_FLASH_SIZE-0x00160000,
+		.offset		= 0x00160000,
+	}
+};
+
+static struct flash_platform_data cerf_flash_data = {
+	.map_name	= "cfi_probe",
+	.parts		= cerf_partitions,
+	.nr_parts	= ARRAY_SIZE(cerf_partitions),
+};
+
+static struct resource cerf_flash_resource = {
+	.start		= SA1100_CS0_PHYS,
+	.end		= SA1100_CS0_PHYS + SZ_32M - 1,
+	.flags		= IORESOURCE_MEM,
+};
+
+static void __init cerf_init_irq(void)
+{
+	sa1100_init_irq();
+	set_irq_type(CERF_ETH_IRQ, IRQT_RISING);
+}
+
+static struct map_desc cerf_io_desc[] __initdata = {
+  /* virtual	 physical    length	 type */
+  { 0xf0000000, 0x08000000, 0x00100000, MT_DEVICE }  /* Crystal Ethernet Chip */
+};
+
+static void __init cerf_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(cerf_io_desc, ARRAY_SIZE(cerf_io_desc));
+
+	sa1100_register_uart(0, 3);
+	sa1100_register_uart(1, 2); /* disable this and the uart2 device for sa1100_fir */
+	sa1100_register_uart(2, 1);
+
+	/* set some GPDR bits here while it's safe */
+	GPDR |= CERF_GPIO_CF_RESET;
+}
+
+static void __init cerf_init(void)
+{
+	platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices));
+	sa11x0_set_flash_data(&cerf_flash_data, &cerf_flash_resource, 1);
+}
+
+MACHINE_START(CERF, "Intrinsyc CerfBoard/CerfCube")
+	MAINTAINER("support@intrinsyc.com")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	MAPIO(cerf_map_io)
+	INITIRQ(cerf_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= cerf_init,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c
new file mode 100644
index 0000000..9928789
--- /dev/null
+++ b/arch/arm/mach-sa1100/collie.c
@@ -0,0 +1,192 @@
+/*
+ * linux/arch/arm/mach-sa1100/collie.c
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * This file contains all Collie-specific tweaks.
+ *
+ * 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.
+ *
+ * ChangeLog:
+ *  03-06-2004 John Lenz <jelenz@wisc.edu>
+ *  06-04-2002 Chris Larson <kergoth@digitalnemesis.net>
+ *  04-16-2001 Lineo Japan,Inc. ...
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/timer.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/arch/collie.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include <asm/hardware/scoop.h>
+#include <asm/mach/sharpsl_param.h>
+#include <asm/hardware/locomo.h>
+
+#include "generic.h"
+
+static struct resource collie_scoop_resources[] = {
+	[0] = {
+		.start		= 0x40800000,
+		.end		= 0x40800fff,
+		.flags		= IORESOURCE_MEM,
+	},
+};
+
+static struct scoop_config collie_scoop_setup = {
+	.io_dir 	= COLLIE_SCOOP_IO_DIR,
+	.io_out		= COLLIE_SCOOP_IO_OUT,
+};
+
+struct platform_device colliescoop_device = {
+	.name		= "sharp-scoop",
+	.id		= -1,
+	.dev		= {
+ 		.platform_data	= &collie_scoop_setup,
+	},
+	.num_resources	= ARRAY_SIZE(collie_scoop_resources),
+	.resource	= collie_scoop_resources,
+};
+
+
+static struct resource locomo_resources[] = {
+	[0] = {
+		.start		= 0x40000000,
+		.end		= 0x40001fff,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= IRQ_GPIO25,
+		.end		= IRQ_GPIO25,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device locomo_device = {
+	.name		= "locomo",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(locomo_resources),
+	.resource	= locomo_resources,
+};
+
+static struct platform_device *devices[] __initdata = {
+	&locomo_device,
+	&colliescoop_device,
+};
+
+static struct mtd_partition collie_partitions[] = {
+	{
+		.name		= "bootloader",
+		.offset 	= 0,
+		.size		= 0x000C0000,
+		.mask_flags	= MTD_WRITEABLE
+	}, {
+		.name		= "kernel",
+		.offset 	= MTDPART_OFS_APPEND,
+		.size		= 0x00100000,
+	}, {
+		.name		= "rootfs",
+		.offset 	= MTDPART_OFS_APPEND,
+		.size		= 0x00e20000,
+	}
+};
+
+static void collie_set_vpp(int vpp)
+{
+	write_scoop_reg(SCOOP_GPCR, read_scoop_reg(SCOOP_GPCR) | COLLIE_SCP_VPEN);
+	if (vpp) {
+		write_scoop_reg(SCOOP_GPWR, read_scoop_reg(SCOOP_GPWR) | COLLIE_SCP_VPEN);
+	} else {
+		write_scoop_reg(SCOOP_GPWR, read_scoop_reg(SCOOP_GPWR) & ~COLLIE_SCP_VPEN);
+	}
+}
+
+static struct flash_platform_data collie_flash_data = {
+	.map_name	= "cfi_probe",
+	.set_vpp	= collie_set_vpp,
+	.parts		= collie_partitions,
+	.nr_parts	= ARRAY_SIZE(collie_partitions),
+};
+
+static struct resource collie_flash_resources[] = {
+	{
+		.start	= SA1100_CS0_PHYS,
+		.end	= SA1100_CS0_PHYS + SZ_32M - 1,
+		.flags	= IORESOURCE_MEM,
+	}
+};
+
+static void __init collie_init(void)
+{
+	int ret = 0;
+
+	/* cpu initialize */
+	GAFR = ( GPIO_SSP_TXD | \
+		 GPIO_SSP_SCLK | GPIO_SSP_SFRM | GPIO_SSP_CLK | GPIO_TIC_ACK | \
+		 GPIO_32_768kHz );
+
+	GPDR = ( GPIO_LDD8 | GPIO_LDD9 | GPIO_LDD10 | GPIO_LDD11 | GPIO_LDD12 | \
+		 GPIO_LDD13 | GPIO_LDD14 | GPIO_LDD15 | GPIO_SSP_TXD | \
+		 GPIO_SSP_SCLK | GPIO_SSP_SFRM | GPIO_SDLC_SCLK | \
+		 GPIO_SDLC_AAF | GPIO_UART_SCLK1 | GPIO_32_768kHz );
+	GPLR = GPIO_GPIO18;
+
+	// PPC pin setting
+	PPDR = ( PPC_LDD0 | PPC_LDD1 | PPC_LDD2 | PPC_LDD3 | PPC_LDD4 | PPC_LDD5 | \
+		 PPC_LDD6 | PPC_LDD7 | PPC_L_PCLK | PPC_L_LCLK | PPC_L_FCLK | PPC_L_BIAS | \
+	 	 PPC_TXD1 | PPC_TXD2 | PPC_RXD2 | PPC_TXD3 | PPC_TXD4 | PPC_SCLK | PPC_SFRM );
+
+	PSDR = ( PPC_RXD1 | PPC_RXD2 | PPC_RXD3 | PPC_RXD4 );
+
+	GAFR |= GPIO_32_768kHz;
+	GPDR |= GPIO_32_768kHz;
+	TUCR  = TUCR_32_768kHz;
+
+	ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+	if (ret) {
+		printk(KERN_WARNING "collie: Unable to register LoCoMo device\n");
+	}
+
+	sa11x0_set_flash_data(&collie_flash_data, collie_flash_resources,
+			      ARRAY_SIZE(collie_flash_resources));
+
+	sharpsl_save_param();
+}
+
+static struct map_desc collie_io_desc[] __initdata = {
+	/* virtual     physical    length      type */
+	{0xe8000000, 0x00000000, 0x02000000, MT_DEVICE},	/* 32M main flash (cs0) */
+	{0xea000000, 0x08000000, 0x02000000, MT_DEVICE},	/* 32M boot flash (cs1) */
+};
+
+static void __init collie_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(collie_io_desc, ARRAY_SIZE(collie_io_desc));
+}
+
+MACHINE_START(COLLIE, "Sharp-Collie")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	MAPIO(collie_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= collie_init,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/cpu-sa1100.c b/arch/arm/mach-sa1100/cpu-sa1100.c
new file mode 100644
index 0000000..6435b2e
--- /dev/null
+++ b/arch/arm/mach-sa1100/cpu-sa1100.c
@@ -0,0 +1,249 @@
+/*
+ * cpu-sa1100.c: clock scaling for the SA1100
+ *
+ * Copyright (C) 2000 2001, The Delft University of Technology
+ *
+ * Authors: 
+ * - Johan Pouwelse (J.A.Pouwelse@its.tudelft.nl): initial version
+ * - Erik Mouw (J.A.K.Mouw@its.tudelft.nl):
+ *   - major rewrite for linux-2.3.99
+ *   - rewritten for the more generic power management scheme in 
+ *     linux-2.4.5-rmk1
+ *
+ * This software has been developed while working on the LART
+ * computing board (http://www.lart.tudelft.nl/), which is
+ * sponsored by the Mobile Multi-media Communications
+ * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications 
+ * (http://www.ubicom.tudelft.nl/) projects.
+ *
+ * The authors can be reached at:
+ *
+ *  Erik Mouw
+ *  Information and Communication Theory Group
+ *  Faculty of Information Technology and Systems
+ *  Delft University of Technology
+ *  P.O. Box 5031
+ *  2600 GA Delft
+ *  The Netherlands
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Theory of operations
+ * ====================
+ * 
+ * Clock scaling can be used to lower the power consumption of the CPU
+ * core. This will give you a somewhat longer running time.
+ *
+ * The SA-1100 has a single register to change the core clock speed:
+ *
+ *   PPCR      0x90020014    PLL config
+ *
+ * However, the DRAM timings are closely related to the core clock
+ * speed, so we need to change these, too. The used registers are:
+ *
+ *   MDCNFG    0xA0000000    DRAM config
+ *   MDCAS0    0xA0000004    Access waveform
+ *   MDCAS1    0xA0000008    Access waveform
+ *   MDCAS2    0xA000000C    Access waveform 
+ *
+ * Care must be taken to change the DRAM parameters the correct way,
+ * because otherwise the DRAM becomes unusable and the kernel will
+ * crash. 
+ *
+ * The simple solution to avoid a kernel crash is to put the actual
+ * clock change in ROM and jump to that code from the kernel. The main
+ * disadvantage is that the ROM has to be modified, which is not
+ * possible on all SA-1100 platforms. Another disadvantage is that
+ * jumping to ROM makes clock switching unecessary complicated.
+ *
+ * The idea behind this driver is that the memory configuration can be
+ * changed while running from DRAM (even with interrupts turned on!)
+ * as long as all re-configuration steps yield a valid DRAM
+ * configuration. The advantages are clear: it will run on all SA-1100
+ * platforms, and the code is very simple.
+ * 
+ * If you really want to understand what is going on in
+ * sa1100_update_dram_timings(), you'll have to read sections 8.2,
+ * 9.5.7.3, and 10.2 from the "Intel StrongARM SA-1100 Microprocessor
+ * Developers Manual" (available for free from Intel).
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+
+#include <asm/hardware.h>
+
+#include "generic.h"
+
+typedef struct {
+	int speed;
+	u32 mdcnfg;
+	u32 mdcas0; 
+	u32 mdcas1;
+	u32 mdcas2;
+} sa1100_dram_regs_t;
+
+
+static struct cpufreq_driver sa1100_driver;
+
+static sa1100_dram_regs_t sa1100_dram_settings[] =
+{
+	/* speed,     mdcnfg,     mdcas0,     mdcas1,     mdcas2  clock frequency */
+	{  59000, 0x00dc88a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /*  59.0 MHz */
+	{  73700, 0x011490a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /*  73.7 MHz */
+	{  88500, 0x014e90a3, 0xcccccccf, 0xfffffffc, 0xffffffff }, /*  88.5 MHz */
+	{ 103200, 0x01889923, 0xcccccccf, 0xfffffffc, 0xffffffff }, /* 103.2 MHz */
+	{ 118000, 0x01c29923, 0x9999998f, 0xfffffff9, 0xffffffff }, /* 118.0 MHz */
+	{ 132700, 0x01fb2123, 0x9999998f, 0xfffffff9, 0xffffffff }, /* 132.7 MHz */
+	{ 147500, 0x02352123, 0x3333330f, 0xfffffff3, 0xffffffff }, /* 147.5 MHz */
+	{ 162200, 0x026b29a3, 0x38e38e1f, 0xfff8e38e, 0xffffffff }, /* 162.2 MHz */
+	{ 176900, 0x02a329a3, 0x71c71c1f, 0xfff1c71c, 0xffffffff }, /* 176.9 MHz */
+	{ 191700, 0x02dd31a3, 0xe38e383f, 0xffe38e38, 0xffffffff }, /* 191.7 MHz */
+	{ 206400, 0x03153223, 0xc71c703f, 0xffc71c71, 0xffffffff }, /* 206.4 MHz */
+	{ 221200, 0x034fba23, 0xc71c703f, 0xffc71c71, 0xffffffff }, /* 221.2 MHz */
+	{ 235900, 0x03853a23, 0xe1e1e07f, 0xe1e1e1e1, 0xffffffe1 }, /* 235.9 MHz */
+	{ 250700, 0x03bf3aa3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3 }, /* 250.7 MHz */
+	{ 265400, 0x03f7c2a3, 0xc3c3c07f, 0xc3c3c3c3, 0xffffffc3 }, /* 265.4 MHz */
+	{ 280200, 0x0431c2a3, 0x878780ff, 0x87878787, 0xffffff87 }, /* 280.2 MHz */
+	{ 0, 0, 0, 0, 0 } /* last entry */
+};
+
+static void sa1100_update_dram_timings(int current_speed, int new_speed)
+{
+	sa1100_dram_regs_t *settings = sa1100_dram_settings;
+
+	/* find speed */
+	while (settings->speed != 0) {
+		if(new_speed == settings->speed)
+			break;
+		
+		settings++;
+	}
+
+	if (settings->speed == 0) {
+		panic("%s: couldn't find dram setting for speed %d\n",
+		      __FUNCTION__, new_speed);
+	}
+
+	/* No risk, no fun: run with interrupts on! */
+	if (new_speed > current_speed) {
+		/* We're going FASTER, so first relax the memory
+		 * timings before changing the core frequency 
+		 */
+		
+		/* Half the memory access clock */
+		MDCNFG |= MDCNFG_CDB2;
+
+		/* The order of these statements IS important, keep 8
+		 * pulses!!
+		 */
+		MDCAS2 = settings->mdcas2;
+		MDCAS1 = settings->mdcas1;
+		MDCAS0 = settings->mdcas0;
+		MDCNFG = settings->mdcnfg;
+	} else {
+		/* We're going SLOWER: first decrease the core
+		 * frequency and then tighten the memory settings.
+		 */
+
+		/* Half the memory access clock */
+		MDCNFG |= MDCNFG_CDB2;
+
+		/* The order of these statements IS important, keep 8
+		 * pulses!!
+		 */
+		MDCAS0 = settings->mdcas0;
+		MDCAS1 = settings->mdcas1;
+		MDCAS2 = settings->mdcas2;
+		MDCNFG = settings->mdcnfg;
+	}
+}
+
+static int sa1100_target(struct cpufreq_policy *policy,
+			 unsigned int target_freq,
+			 unsigned int relation)
+{
+	unsigned int cur = sa11x0_getspeed(0);
+	unsigned int new_ppcr;
+
+	struct cpufreq_freqs freqs;
+	switch(relation){
+	case CPUFREQ_RELATION_L:
+		new_ppcr = sa11x0_freq_to_ppcr(target_freq);
+		if (sa11x0_ppcr_to_freq(new_ppcr) > policy->max)
+			new_ppcr--;
+		break;
+	case CPUFREQ_RELATION_H:
+		new_ppcr = sa11x0_freq_to_ppcr(target_freq);
+		if ((sa11x0_ppcr_to_freq(new_ppcr) > target_freq) &&
+		    (sa11x0_ppcr_to_freq(new_ppcr - 1) >= policy->min))
+			new_ppcr--;
+		break;
+	}
+
+	freqs.old = cur;
+	freqs.new = sa11x0_ppcr_to_freq(new_ppcr);
+	freqs.cpu = 0;
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	if (freqs.new > cur)
+		sa1100_update_dram_timings(cur, freqs.new);
+
+	PPCR = new_ppcr;
+
+	if (freqs.new < cur)
+		sa1100_update_dram_timings(cur, freqs.new);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return 0;
+}
+
+static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
+{
+	if (policy->cpu != 0)
+		return -EINVAL;
+	policy->cur = policy->min = policy->max = sa11x0_getspeed(0);
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+	policy->cpuinfo.min_freq = 59000;
+	policy->cpuinfo.max_freq = 287000;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	return 0;
+}
+
+static struct cpufreq_driver sa1100_driver = {
+	.flags		= CPUFREQ_STICKY,
+	.verify		= sa11x0_verify_speed,
+	.target		= sa1100_target,
+	.get		= sa11x0_getspeed,
+	.init		= sa1100_cpu_init,
+	.name		= "sa1100",
+};
+
+static int __init sa1100_dram_init(void)
+{
+ 	if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID)
+		return cpufreq_register_driver(&sa1100_driver);
+	else
+		return -ENODEV;
+}
+
+arch_initcall(sa1100_dram_init);
diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c
new file mode 100644
index 0000000..8d2a89a
--- /dev/null
+++ b/arch/arm/mach-sa1100/cpu-sa1110.c
@@ -0,0 +1,367 @@
+/*
+ *  linux/arch/arm/mach-sa1100/cpu-sa1110.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ *  $Id: cpu-sa1110.c,v 1.9 2002/07/06 16:53:18 rmk Exp $
+ *
+ * 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.
+ *
+ * Note: there are two erratas that apply to the SA1110 here:
+ *  7 - SDRAM auto-power-up failure (rev A0)
+ * 13 - Corruption of internal register reads/writes following
+ *      SDRAM reads (rev A0, B0, B1)
+ *
+ * We ignore rev. A0 and B0 devices; I don't think they're worth supporting.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "generic.h"
+
+#undef DEBUG
+
+static struct cpufreq_driver sa1110_driver;
+
+struct sdram_params {
+	u_char  rows;		/* bits				 */
+	u_char  cas_latency;	/* cycles			 */
+	u_char  tck;		/* clock cycle time (ns)	 */
+	u_char  trcd;		/* activate to r/w (ns)		 */
+	u_char  trp;		/* precharge to activate (ns)	 */
+	u_char  twr;		/* write recovery time (ns)	 */
+	u_short refresh;	/* refresh time for array (us)	 */
+};
+
+struct sdram_info {
+	u_int	mdcnfg;
+	u_int	mdrefr;
+	u_int	mdcas[3];
+};
+
+static struct sdram_params tc59sm716_cl2_params __initdata = {
+	.rows			= 12,
+	.tck			= 10,
+	.trcd			= 20,
+	.trp			= 20,
+	.twr			= 10,
+	.refresh		= 64000,
+	.cas_latency		= 2,
+};
+
+static struct sdram_params tc59sm716_cl3_params __initdata = {
+	.rows			= 12,
+	.tck			= 8,
+	.trcd			= 20,
+	.trp			= 20,
+	.twr			= 8,
+	.refresh		= 64000,
+	.cas_latency		= 3,
+};
+
+static struct sdram_params samsung_k4s641632d_tc75 __initdata = {
+	.rows			= 14,
+	.tck			= 9,
+	.trcd			= 27,
+	.trp			= 20,
+	.twr			= 9,
+	.refresh		= 64000,
+	.cas_latency		= 3,
+};
+
+static struct sdram_params samsung_km416s4030ct __initdata = {
+	.rows			= 13,
+	.tck			= 8,
+	.trcd			= 24,	/* 3 CLKs */
+	.trp			= 24,	/* 3 CLKs */
+	.twr			= 16,	/* Trdl: 2 CLKs */
+	.refresh		= 64000,
+	.cas_latency		= 3,
+};
+
+static struct sdram_params wbond_w982516ah75l_cl3_params __initdata = {
+	.rows			= 16,
+	.tck			= 8,
+	.trcd			= 20,
+	.trp			= 20,
+	.twr			= 8,
+	.refresh		= 64000,
+	.cas_latency		= 3,
+};
+
+static struct sdram_params sdram_params;
+
+/*
+ * Given a period in ns and frequency in khz, calculate the number of
+ * cycles of frequency in period.  Note that we round up to the next
+ * cycle, even if we are only slightly over.
+ */
+static inline u_int ns_to_cycles(u_int ns, u_int khz)
+{
+	return (ns * khz + 999999) / 1000000;
+}
+
+/*
+ * Create the MDCAS register bit pattern.
+ */
+static inline void set_mdcas(u_int *mdcas, int delayed, u_int rcd)
+{
+	u_int shift;
+
+	rcd = 2 * rcd - 1;
+	shift = delayed + 1 + rcd;
+
+	mdcas[0]  = (1 << rcd) - 1;
+	mdcas[0] |= 0x55555555 << shift;
+	mdcas[1]  = mdcas[2] = 0x55555555 << (shift & 1);
+}
+
+static void
+sdram_calculate_timing(struct sdram_info *sd, u_int cpu_khz,
+		       struct sdram_params *sdram)
+{
+	u_int mem_khz, sd_khz, trp, twr;
+
+	mem_khz = cpu_khz / 2;
+	sd_khz = mem_khz;
+
+	/*
+	 * If SDCLK would invalidate the SDRAM timings,
+	 * run SDCLK at half speed.
+	 *
+	 * CPU steppings prior to B2 must either run the memory at
+	 * half speed or use delayed read latching (errata 13).
+	 */
+	if ((ns_to_cycles(sdram->tck, sd_khz) > 1) ||
+	    (CPU_REVISION < CPU_SA1110_B2 && sd_khz < 62000))
+		sd_khz /= 2;
+
+	sd->mdcnfg = MDCNFG & 0x007f007f;
+
+	twr = ns_to_cycles(sdram->twr, mem_khz);
+
+	/* trp should always be >1 */
+	trp = ns_to_cycles(sdram->trp, mem_khz) - 1;
+	if (trp < 1)
+		trp = 1;
+
+	sd->mdcnfg |= trp << 8;
+	sd->mdcnfg |= trp << 24;
+	sd->mdcnfg |= sdram->cas_latency << 12;
+	sd->mdcnfg |= sdram->cas_latency << 28;
+	sd->mdcnfg |= twr << 14;
+	sd->mdcnfg |= twr << 30;
+
+	sd->mdrefr = MDREFR & 0xffbffff0;
+	sd->mdrefr |= 7;
+
+	if (sd_khz != mem_khz)
+		sd->mdrefr |= MDREFR_K1DB2;
+
+	/* initial number of '1's in MDCAS + 1 */
+	set_mdcas(sd->mdcas, sd_khz >= 62000, ns_to_cycles(sdram->trcd, mem_khz));
+
+#ifdef DEBUG
+	printk("MDCNFG: %08x MDREFR: %08x MDCAS0: %08x MDCAS1: %08x MDCAS2: %08x\n",
+		sd->mdcnfg, sd->mdrefr, sd->mdcas[0], sd->mdcas[1], sd->mdcas[2]);
+#endif
+}
+
+/*
+ * Set the SDRAM refresh rate.
+ */
+static inline void sdram_set_refresh(u_int dri)
+{
+	MDREFR = (MDREFR & 0xffff000f) | (dri << 4);
+	(void) MDREFR;
+}
+
+/*
+ * Update the refresh period.  We do this such that we always refresh
+ * the SDRAMs within their permissible period.  The refresh period is
+ * always a multiple of the memory clock (fixed at cpu_clock / 2).
+ *
+ * FIXME: we don't currently take account of burst accesses here,
+ * but neither do Intels DM nor Angel.
+ */
+static void
+sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram)
+{
+	u_int ns_row = (sdram->refresh * 1000) >> sdram->rows;
+	u_int dri = ns_to_cycles(ns_row, cpu_khz / 2) / 32;
+
+#ifdef DEBUG
+	mdelay(250);
+	printk("new dri value = %d\n", dri);
+#endif
+
+	sdram_set_refresh(dri);
+}
+
+/*
+ * Ok, set the CPU frequency.  
+ */
+static int sa1110_target(struct cpufreq_policy *policy,
+			 unsigned int target_freq,
+			 unsigned int relation)
+{
+	struct sdram_params *sdram = &sdram_params;
+	struct cpufreq_freqs freqs;
+	struct sdram_info sd;
+	unsigned long flags;
+	unsigned int ppcr, unused;
+
+	switch(relation){
+	case CPUFREQ_RELATION_L:
+		ppcr = sa11x0_freq_to_ppcr(target_freq);
+		if (sa11x0_ppcr_to_freq(ppcr) > policy->max)
+			ppcr--;
+		break;
+	case CPUFREQ_RELATION_H:
+		ppcr = sa11x0_freq_to_ppcr(target_freq);
+		if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) &&
+		    (sa11x0_ppcr_to_freq(ppcr-1) >= policy->min))
+			ppcr--;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	freqs.old = sa11x0_getspeed(0);
+	freqs.new = sa11x0_ppcr_to_freq(ppcr);
+	freqs.cpu = 0;
+
+	sdram_calculate_timing(&sd, freqs.new, sdram);
+
+#if 0
+	/*
+	 * These values are wrong according to the SA1110 documentation
+	 * and errata, but they seem to work.  Need to get a storage
+	 * scope on to the SDRAM signals to work out why.
+	 */
+	if (policy->max < 147500) {
+		sd.mdrefr |= MDREFR_K1DB2;
+		sd.mdcas[0] = 0xaaaaaa7f;
+	} else {
+		sd.mdrefr &= ~MDREFR_K1DB2;
+		sd.mdcas[0] = 0xaaaaaa9f;
+	}
+	sd.mdcas[1] = 0xaaaaaaaa;
+	sd.mdcas[2] = 0xaaaaaaaa;
+#endif
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	/*
+	 * The clock could be going away for some time.  Set the SDRAMs
+	 * to refresh rapidly (every 64 memory clock cycles).  To get
+	 * through the whole array, we need to wait 262144 mclk cycles.
+	 * We wait 20ms to be safe.
+	 */
+	sdram_set_refresh(2);
+	if (!irqs_disabled()) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(20 * HZ / 1000);
+	} else {
+		mdelay(20);
+	}
+
+	/*
+	 * Reprogram the DRAM timings with interrupts disabled, and
+	 * ensure that we are doing this within a complete cache line.
+	 * This means that we won't access SDRAM for the duration of
+	 * the programming.
+	 */
+	local_irq_save(flags);
+	asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0));
+	udelay(10);
+	__asm__ __volatile__("					\n\
+		b	2f					\n\
+		.align	5					\n\
+1:		str	%3, [%1, #0]		@ MDCNFG	\n\
+		str	%4, [%1, #28]		@ MDREFR	\n\
+		str	%5, [%1, #4]		@ MDCAS0	\n\
+		str	%6, [%1, #8]		@ MDCAS1	\n\
+		str	%7, [%1, #12]		@ MDCAS2	\n\
+		str	%8, [%2, #0]		@ PPCR		\n\
+		ldr	%0, [%1, #0]				\n\
+		b	3f					\n\
+2:		b	1b					\n\
+3:		nop						\n\
+		nop"
+		: "=&r" (unused)
+		: "r" (&MDCNFG), "r" (&PPCR), "0" (sd.mdcnfg),
+		  "r" (sd.mdrefr), "r" (sd.mdcas[0]),
+		  "r" (sd.mdcas[1]), "r" (sd.mdcas[2]), "r" (ppcr));
+	local_irq_restore(flags);
+
+	/*
+	 * Now, return the SDRAM refresh back to normal.
+	 */
+	sdram_update_refresh(freqs.new, sdram);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return 0;
+}
+
+static int __init sa1110_cpu_init(struct cpufreq_policy *policy)
+{
+	if (policy->cpu != 0)
+		return -EINVAL;
+	policy->cur = policy->min = policy->max = sa11x0_getspeed(0);
+	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+	policy->cpuinfo.min_freq = 59000;
+	policy->cpuinfo.max_freq = 287000;
+	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+	return 0;
+}
+
+static struct cpufreq_driver sa1110_driver = {
+	.flags		= CPUFREQ_STICKY,
+	.verify		= sa11x0_verify_speed,
+	.target		= sa1110_target,
+	.get		= sa11x0_getspeed,
+	.init		= sa1110_cpu_init,
+	.name		= "sa1110",
+};
+
+static int __init sa1110_clk_init(void)
+{
+	struct sdram_params *sdram = NULL;
+
+	if (machine_is_assabet())
+		sdram = &tc59sm716_cl3_params;
+
+	if (machine_is_pt_system3())
+		sdram = &samsung_k4s641632d_tc75;
+
+	if (machine_is_h3100())
+		sdram = &samsung_km416s4030ct;
+
+	if (sdram) {
+		printk(KERN_DEBUG "SDRAM: tck: %d trcd: %d trp: %d"
+			" twr: %d refresh: %d cas_latency: %d\n",
+			sdram->tck, sdram->trcd, sdram->trp,
+			sdram->twr, sdram->refresh, sdram->cas_latency);
+
+		memcpy(&sdram_params, sdram, sizeof(sdram_params));
+
+		return cpufreq_register_driver(&sa1110_driver);
+	}
+
+	return 0;
+}
+
+arch_initcall(sa1110_clk_init);
diff --git a/arch/arm/mach-sa1100/dma.c b/arch/arm/mach-sa1100/dma.c
new file mode 100644
index 0000000..be0e442
--- /dev/null
+++ b/arch/arm/mach-sa1100/dma.c
@@ -0,0 +1,348 @@
+/*
+ * arch/arm/kernel/dma-sa1100.c
+ *
+ * Support functions for the SA11x0 internal DMA channels.
+ *
+ * Copyright (C) 2000, 2001 by Nicolas Pitre
+ *
+ * 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/interrupt.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/dma.h>
+
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTK( s, arg... )  printk( "dma<%p>: " s, regs , ##arg )
+#else
+#define DPRINTK( x... )
+#endif
+
+
+typedef struct {
+	const char *device_id;		/* device name */
+	u_long device;			/* this channel device, 0  if unused*/
+	dma_callback_t callback;	/* to call when DMA completes */
+	void *data;			/* ... with private data ptr */
+} sa1100_dma_t;
+
+static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS];
+
+static spinlock_t dma_list_lock;
+
+
+static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+	dma_regs_t *dma_regs = dev_id;
+	sa1100_dma_t *dma = dma_chan + (((u_int)dma_regs >> 5) & 7);
+	int status = dma_regs->RdDCSR;
+
+	if (status & (DCSR_ERROR)) {
+		printk(KERN_CRIT "DMA on \"%s\" caused an error\n", dma->device_id);
+		dma_regs->ClrDCSR = DCSR_ERROR;
+	}
+
+	dma_regs->ClrDCSR = status & (DCSR_DONEA | DCSR_DONEB);
+	if (dma->callback) {
+		if (status & DCSR_DONEA)
+			dma->callback(dma->data);
+		if (status & DCSR_DONEB)
+			dma->callback(dma->data);
+	}
+	return IRQ_HANDLED;
+}
+
+
+/**
+ *	sa1100_request_dma - allocate one of the SA11x0's DMA chanels
+ *	@device: The SA11x0 peripheral targeted by this request
+ *	@device_id: An ascii name for the claiming device
+ *	@callback: Function to be called when the DMA completes
+ *	@data: A cookie passed back to the callback function
+ *	@dma_regs: Pointer to the location of the allocated channel's identifier
+ *
+ * 	This function will search for a free DMA channel and returns the
+ * 	address of the hardware registers for that channel as the channel
+ * 	identifier. This identifier is written to the location pointed by
+ * 	@dma_regs. The list of possible values for @device are listed into
+ * 	linux/include/asm-arm/arch-sa1100/dma.h as a dma_device_t enum.
+ *
+ * 	Note that reading from a port and writing to the same port are
+ * 	actually considered as two different streams requiring separate
+ * 	DMA registrations.
+ *
+ * 	The @callback function is called from interrupt context when one
+ * 	of the two possible DMA buffers in flight has terminated. That
+ * 	function has to be small and efficient while posponing more complex
+ * 	processing to a lower priority execution context.
+ *
+ * 	If no channels are available, or if the desired @device is already in
+ * 	use by another DMA channel, then an error code is returned.  This
+ * 	function must be called before any other DMA calls.
+ **/
+
+int sa1100_request_dma (dma_device_t device, const char *device_id,
+			dma_callback_t callback, void *data,
+			dma_regs_t **dma_regs)
+{
+	sa1100_dma_t *dma = NULL;
+	dma_regs_t *regs;
+	int i, err;
+
+	*dma_regs = NULL;
+
+	err = 0;
+	spin_lock(&dma_list_lock);
+	for (i = 0; i < SA1100_DMA_CHANNELS; i++) {
+		if (dma_chan[i].device == device) {
+			err = -EBUSY;
+			break;
+		} else if (!dma_chan[i].device && !dma) {
+			dma = &dma_chan[i];
+		}
+	}
+	if (!err) {
+	       if (dma)
+		       dma->device = device;
+	       else
+		       err = -ENOSR;
+	}
+	spin_unlock(&dma_list_lock);
+	if (err)
+		return err;
+
+	i = dma - dma_chan;
+	regs = (dma_regs_t *)&DDAR(i);
+	err = request_irq(IRQ_DMA0 + i, dma_irq_handler, SA_INTERRUPT,
+			  device_id, regs);
+	if (err) {
+		printk(KERN_ERR
+		       "%s: unable to request IRQ %d for %s\n",
+		       __FUNCTION__, IRQ_DMA0 + i, device_id);
+		dma->device = 0;
+		return err;
+	}
+
+	*dma_regs = regs;
+	dma->device_id = device_id;
+	dma->callback = callback;
+	dma->data = data;
+
+	regs->ClrDCSR =
+		(DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
+		 DCSR_IE | DCSR_ERROR | DCSR_RUN);
+	regs->DDAR = device;
+
+	return 0;
+}
+
+
+/**
+ * 	sa1100_free_dma - free a SA11x0 DMA channel
+ * 	@regs: identifier for the channel to free
+ *
+ * 	This clears all activities on a given DMA channel and releases it
+ * 	for future requests.  The @regs identifier is provided by a
+ * 	successful call to sa1100_request_dma().
+ **/
+
+void sa1100_free_dma(dma_regs_t *regs)
+{
+	int i;
+
+	for (i = 0; i < SA1100_DMA_CHANNELS; i++)
+		if (regs == (dma_regs_t *)&DDAR(i))
+			break;
+	if (i >= SA1100_DMA_CHANNELS) {
+		printk(KERN_ERR "%s: bad DMA identifier\n", __FUNCTION__);
+		return;
+	}
+
+	if (!dma_chan[i].device) {
+		printk(KERN_ERR "%s: Trying to free free DMA\n", __FUNCTION__);
+		return;
+	}
+
+	regs->ClrDCSR =
+		(DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
+		 DCSR_IE | DCSR_ERROR | DCSR_RUN);
+	free_irq(IRQ_DMA0 + i, regs);
+	dma_chan[i].device = 0;
+}
+
+
+/**
+ * 	sa1100_start_dma - submit a data buffer for DMA
+ * 	@regs: identifier for the channel to use
+ * 	@dma_ptr: buffer physical (or bus) start address
+ * 	@size: buffer size
+ *
+ * 	This function hands the given data buffer to the hardware for DMA
+ * 	access. If another buffer is already in flight then this buffer
+ * 	will be queued so the DMA engine will switch to it automatically
+ * 	when the previous one is done.  The DMA engine is actually toggling
+ * 	between two buffers so at most 2 successful calls can be made before
+ * 	one of them terminates and the callback function is called.
+ *
+ * 	The @regs identifier is provided by a successful call to
+ * 	sa1100_request_dma().
+ *
+ * 	The @size must not be larger than %MAX_DMA_SIZE.  If a given buffer
+ * 	is larger than that then it's the caller's responsibility to split
+ * 	it into smaller chunks and submit them separately. If this is the
+ * 	case then a @size of %CUT_DMA_SIZE is recommended to avoid ending
+ * 	up with too small chunks. The callback function can be used to chain
+ * 	submissions of buffer chunks.
+ *
+ * 	Error return values:
+ * 	%-EOVERFLOW:	Given buffer size is too big.
+ * 	%-EBUSY:	Both DMA buffers are already in use.
+ * 	%-EAGAIN:	Both buffers were busy but one of them just completed
+ * 			but the interrupt handler has to execute first.
+ *
+ * 	This function returs 0 on success.
+ **/
+
+int sa1100_start_dma(dma_regs_t *regs, dma_addr_t dma_ptr, u_int size)
+{
+	unsigned long flags;
+	u_long status;
+	int ret;
+
+	if (dma_ptr & 3)
+		printk(KERN_WARNING "DMA: unaligned start address (0x%08lx)\n",
+		       (unsigned long)dma_ptr);
+
+	if (size > MAX_DMA_SIZE)
+		return -EOVERFLOW;
+
+	local_irq_save(flags);
+	status = regs->RdDCSR;
+
+	/* If both DMA buffers are started, there's nothing else we can do. */
+	if ((status & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) {
+		DPRINTK("start: st %#x busy\n", status);
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (((status & DCSR_BIU) && (status & DCSR_STRTB)) ||
+	    (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) {
+		if (status & DCSR_DONEA) {
+			/* give a chance for the interrupt to be processed */
+			ret = -EAGAIN;
+			goto out;
+		}
+		regs->DBSA = dma_ptr;
+		regs->DBTA = size;
+		regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN;
+		DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size);
+	} else {
+		if (status & DCSR_DONEB) {
+			/* give a chance for the interrupt to be processed */
+			ret = -EAGAIN;
+			goto out;
+		}
+		regs->DBSB = dma_ptr;
+		regs->DBTB = size;
+		regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN;
+		DPRINTK("start a=%#x s=%d on B\n", dma_ptr, size);
+	}
+	ret = 0;
+
+out:
+	local_irq_restore(flags);
+	return ret;
+}
+
+
+/**
+ * 	sa1100_get_dma_pos - return current DMA position
+ * 	@regs: identifier for the channel to use
+ *
+ * 	This function returns the current physical (or bus) address for the
+ * 	given DMA channel.  If the channel is running i.e. not in a stopped
+ * 	state then the caller must disable interrupts prior calling this
+ * 	function and process the returned value before re-enabling them to
+ * 	prevent races with the completion interrupt handler and the callback
+ * 	function. The validation of the returned value is the caller's
+ * 	responsibility as well -- the hardware seems to return out of range
+ * 	values when the DMA engine completes a buffer.
+ *
+ * 	The @regs identifier is provided by a successful call to
+ * 	sa1100_request_dma().
+ **/
+
+dma_addr_t sa1100_get_dma_pos(dma_regs_t *regs)
+{
+	int status;
+
+	/*
+	 * We must determine whether buffer A or B is active.
+	 * Two possibilities: either we are in the middle of
+	 * a buffer, or the DMA controller just switched to the
+	 * next toggle but the interrupt hasn't been serviced yet.
+	 * The former case is straight forward.  In the later case,
+	 * we'll do like if DMA is just at the end of the previous
+	 * toggle since all registers haven't been reset yet.
+	 * This goes around the edge case and since we're always
+	 * a little behind anyways it shouldn't make a big difference.
+	 * If DMA has been stopped prior calling this then the
+	 * position is exact.
+	 */
+	status = regs->RdDCSR;
+	if ((!(status & DCSR_BIU) &&  (status & DCSR_STRTA)) ||
+	    ( (status & DCSR_BIU) && !(status & DCSR_STRTB)))
+		return regs->DBSA;
+	else
+		return regs->DBSB;
+}
+
+
+/**
+ * 	sa1100_reset_dma - reset a DMA channel
+ * 	@regs: identifier for the channel to use
+ *
+ * 	This function resets and reconfigure the given DMA channel. This is
+ * 	particularly useful after a sleep/wakeup event.
+ *
+ * 	The @regs identifier is provided by a successful call to
+ * 	sa1100_request_dma().
+ **/
+
+void sa1100_reset_dma(dma_regs_t *regs)
+{
+	int i;
+
+	for (i = 0; i < SA1100_DMA_CHANNELS; i++)
+		if (regs == (dma_regs_t *)&DDAR(i))
+			break;
+	if (i >= SA1100_DMA_CHANNELS) {
+		printk(KERN_ERR "%s: bad DMA identifier\n", __FUNCTION__);
+		return;
+	}
+
+	regs->ClrDCSR =
+		(DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
+		 DCSR_IE | DCSR_ERROR | DCSR_RUN);
+	regs->DDAR = dma_chan[i].device;
+}
+
+
+EXPORT_SYMBOL(sa1100_request_dma);
+EXPORT_SYMBOL(sa1100_free_dma);
+EXPORT_SYMBOL(sa1100_start_dma);
+EXPORT_SYMBOL(sa1100_get_dma_pos);
+EXPORT_SYMBOL(sa1100_reset_dma);
+
diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c
new file mode 100644
index 0000000..95ae217
--- /dev/null
+++ b/arch/arm/mach-sa1100/generic.c
@@ -0,0 +1,419 @@
+/*
+ * linux/arch/arm/mach-sa1100/generic.c
+ *
+ * Author: Nicolas Pitre
+ *
+ * Code common to all SA11x0 machines.
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+
+#include <asm/div64.h>
+#include <asm/hardware.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+#include <asm/irq.h>
+
+#include "generic.h"
+
+#define NR_FREQS	16
+
+/*
+ * This table is setup for a 3.6864MHz Crystal.
+ */
+static const unsigned short cclk_frequency_100khz[NR_FREQS] = {
+	 590,	/*  59.0 MHz */
+	 737,	/*  73.7 MHz */
+	 885, 	/*  88.5 MHz */
+	1032,	/* 103.2 MHz */
+	1180,	/* 118.0 MHz */
+	1327,	/* 132.7 MHz */
+	1475,	/* 147.5 MHz */
+	1622,	/* 162.2 MHz */
+	1769,	/* 176.9 MHz */
+	1917,	/* 191.7 MHz */
+	2064,	/* 206.4 MHz */
+	2212,	/* 221.2 MHz */
+	2359,   /* 235.9 MHz */
+	2507,   /* 250.7 MHz */
+	2654,   /* 265.4 MHz */
+	2802    /* 280.2 MHz */
+};
+
+#if defined(CONFIG_CPU_FREQ_SA1100) || defined(CONFIG_CPU_FREQ_SA1110)
+/* rounds up(!)  */
+unsigned int sa11x0_freq_to_ppcr(unsigned int khz)
+{
+	int i;
+
+	khz /= 100;
+
+	for (i = 0; i < NR_FREQS; i++)
+		if (cclk_frequency_100khz[i] >= khz)
+			break;
+
+	return i;
+}
+
+unsigned int sa11x0_ppcr_to_freq(unsigned int idx)
+{
+	unsigned int freq = 0;
+	if (idx < NR_FREQS)
+		freq = cclk_frequency_100khz[idx] * 100;
+	return freq;
+}
+
+
+/* make sure that only the "userspace" governor is run -- anything else wouldn't make sense on
+ * this platform, anyway.
+ */
+int sa11x0_verify_speed(struct cpufreq_policy *policy)
+{
+	unsigned int tmp;
+	if (policy->cpu)
+		return -EINVAL;
+
+	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
+
+	/* make sure that at least one frequency is within the policy */
+	tmp = cclk_frequency_100khz[sa11x0_freq_to_ppcr(policy->min)] * 100;
+	if (tmp > policy->max)
+		policy->max = tmp;
+
+	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
+
+	return 0;
+}
+
+unsigned int sa11x0_getspeed(unsigned int cpu)
+{
+	if (cpu)
+		return 0;
+	return cclk_frequency_100khz[PPCR & 0xf] * 100;
+}
+
+#else
+/*
+ * We still need to provide this so building without cpufreq works.
+ */ 
+unsigned int cpufreq_get(unsigned int cpu)
+{
+	return cclk_frequency_100khz[PPCR & 0xf] * 100;
+}
+EXPORT_SYMBOL(cpufreq_get);
+#endif
+
+/*
+ * This is the SA11x0 sched_clock implementation.  This has
+ * a resolution of 271ns, and a maximum value of 1165s.
+ *  ( * 1E9 / 3686400 => * 78125 / 288)
+ */
+unsigned long long sched_clock(void)
+{
+	unsigned long long v;
+
+	v = (unsigned long long)OSCR * 78125;
+	do_div(v, 288);
+
+	return v;
+}
+
+/*
+ * Default power-off for SA1100
+ */
+static void sa1100_power_off(void)
+{
+	mdelay(100);
+	local_irq_disable();
+	/* disable internal oscillator, float CS lines */
+	PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS);
+	/* enable wake-up on GPIO0 (Assabet...) */
+	PWER = GFER = GRER = 1;
+	/*
+	 * set scratchpad to zero, just in case it is used as a
+	 * restart address by the bootloader.
+	 */
+	PSPR = 0;
+	/* enter sleep mode */
+	PMCR = PMCR_SF;
+}
+
+static struct resource sa11x0udc_resources[] = {
+	[0] = {
+		.start	= 0x80000000,
+		.end	= 0x8000ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static u64 sa11x0udc_dma_mask = 0xffffffffUL;
+
+static struct platform_device sa11x0udc_device = {
+	.name		= "sa11x0-udc",
+	.id		= -1,
+	.dev		= {
+		.dma_mask = &sa11x0udc_dma_mask,
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa11x0udc_resources),
+	.resource	= sa11x0udc_resources,
+};
+
+static struct resource sa11x0uart1_resources[] = {
+	[0] = {
+		.start	= 0x80010000,
+		.end	= 0x8001ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device sa11x0uart1_device = {
+	.name		= "sa11x0-uart",
+	.id		= 1,
+	.num_resources	= ARRAY_SIZE(sa11x0uart1_resources),
+	.resource	= sa11x0uart1_resources,
+};
+
+static struct resource sa11x0uart3_resources[] = {
+	[0] = {
+		.start	= 0x80050000,
+		.end	= 0x8005ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device sa11x0uart3_device = {
+	.name		= "sa11x0-uart",
+	.id		= 3,
+	.num_resources	= ARRAY_SIZE(sa11x0uart3_resources),
+	.resource	= sa11x0uart3_resources,
+};
+
+static struct resource sa11x0mcp_resources[] = {
+	[0] = {
+		.start	= 0x80060000,
+		.end	= 0x8006ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static u64 sa11x0mcp_dma_mask = 0xffffffffUL;
+
+static struct platform_device sa11x0mcp_device = {
+	.name		= "sa11x0-mcp",
+	.id		= -1,
+	.dev = {
+		.dma_mask = &sa11x0mcp_dma_mask,
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa11x0mcp_resources),
+	.resource	= sa11x0mcp_resources,
+};
+
+static struct resource sa11x0ssp_resources[] = {
+	[0] = {
+		.start	= 0x80070000,
+		.end	= 0x8007ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static u64 sa11x0ssp_dma_mask = 0xffffffffUL;
+
+static struct platform_device sa11x0ssp_device = {
+	.name		= "sa11x0-ssp",
+	.id		= -1,
+	.dev = {
+		.dma_mask = &sa11x0ssp_dma_mask,
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa11x0ssp_resources),
+	.resource	= sa11x0ssp_resources,
+};
+
+static struct resource sa11x0fb_resources[] = {
+	[0] = {
+		.start	= 0xb0100000,
+		.end	= 0xb010ffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_LCD,
+		.end	= IRQ_LCD,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device sa11x0fb_device = {
+	.name		= "sa11x0-fb",
+	.id		= -1,
+	.dev = {
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa11x0fb_resources),
+	.resource	= sa11x0fb_resources,
+};
+
+static struct platform_device sa11x0pcmcia_device = {
+	.name		= "sa11x0-pcmcia",
+	.id		= -1,
+};
+
+static struct platform_device sa11x0mtd_device = {
+	.name		= "flash",
+	.id		= -1,
+};
+
+void sa11x0_set_flash_data(struct flash_platform_data *flash,
+			   struct resource *res, int nr)
+{
+	sa11x0mtd_device.dev.platform_data = flash;
+	sa11x0mtd_device.resource = res;
+	sa11x0mtd_device.num_resources = nr;
+}
+
+static struct resource sa11x0ir_resources[] = {
+	{
+		.start	= __PREG(Ser2UTCR0),
+		.end	= __PREG(Ser2UTCR0) + 0x24 - 1,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= __PREG(Ser2HSCR0),
+		.end	= __PREG(Ser2HSCR0) + 0x1c - 1,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= __PREG(Ser2HSCR2),
+		.end	= __PREG(Ser2HSCR2) + 0x04 - 1,
+		.flags	= IORESOURCE_MEM,
+	}, {
+		.start	= IRQ_Ser2ICP,
+		.end	= IRQ_Ser2ICP,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device sa11x0ir_device = {
+	.name		= "sa11x0-ir",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(sa11x0ir_resources),
+	.resource	= sa11x0ir_resources,
+};
+
+void sa11x0_set_irda_data(struct irda_platform_data *irda)
+{
+	sa11x0ir_device.dev.platform_data = irda;
+}
+
+static struct platform_device *sa11x0_devices[] __initdata = {
+	&sa11x0udc_device,
+	&sa11x0uart1_device,
+	&sa11x0uart3_device,
+	&sa11x0mcp_device,
+	&sa11x0ssp_device,
+	&sa11x0pcmcia_device,
+	&sa11x0fb_device,
+	&sa11x0mtd_device,
+};
+
+static int __init sa1100_init(void)
+{
+	pm_power_off = sa1100_power_off;
+
+	if (sa11x0ir_device.dev.platform_data)
+		platform_device_register(&sa11x0ir_device);
+
+	return platform_add_devices(sa11x0_devices, ARRAY_SIZE(sa11x0_devices));
+}
+
+arch_initcall(sa1100_init);
+
+void (*sa1100fb_backlight_power)(int on);
+void (*sa1100fb_lcd_power)(int on);
+
+EXPORT_SYMBOL(sa1100fb_backlight_power);
+EXPORT_SYMBOL(sa1100fb_lcd_power);
+
+
+/*
+ * Common I/O mapping:
+ *
+ * Typically, static virtual address mappings are as follow:
+ *
+ * 0xf0000000-0xf3ffffff:	miscellaneous stuff (CPLDs, etc.)
+ * 0xf4000000-0xf4ffffff:	SA-1111
+ * 0xf5000000-0xf5ffffff:	reserved (used by cache flushing area)
+ * 0xf6000000-0xfffeffff:	reserved (internal SA1100 IO defined above)
+ * 0xffff0000-0xffff0fff:	SA1100 exception vectors
+ * 0xffff2000-0xffff2fff:	Minicache copy_user_page area
+ *
+ * Below 0xe8000000 is reserved for vm allocation.
+ *
+ * The machine specific code must provide the extra mapping beside the
+ * default mapping provided here.
+ */
+
+static struct map_desc standard_io_desc[] __initdata = {
+ /* virtual     physical    length      type */
+  { 0xf8000000, 0x80000000, 0x00100000, MT_DEVICE }, /* PCM */
+  { 0xfa000000, 0x90000000, 0x00100000, MT_DEVICE }, /* SCM */
+  { 0xfc000000, 0xa0000000, 0x00100000, MT_DEVICE }, /* MER */
+  { 0xfe000000, 0xb0000000, 0x00200000, MT_DEVICE }  /* LCD + DMA */
+};
+
+void __init sa1100_map_io(void)
+{
+	iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc));
+}
+
+/*
+ * Disable the memory bus request/grant signals on the SA1110 to
+ * ensure that we don't receive spurious memory requests.  We set
+ * the MBGNT signal false to ensure the SA1111 doesn't own the
+ * SDRAM bus.
+ */
+void __init sa1110_mb_disable(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	
+	PGSR &= ~GPIO_MBGNT;
+	GPCR = GPIO_MBGNT;
+	GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT;
+
+	GAFR &= ~(GPIO_MBGNT | GPIO_MBREQ);
+
+	local_irq_restore(flags);
+}
+
+/*
+ * If the system is going to use the SA-1111 DMA engines, set up
+ * the memory bus request/grant pins.
+ */
+void __init sa1110_mb_enable(void)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	PGSR &= ~GPIO_MBGNT;
+	GPCR = GPIO_MBGNT;
+	GPDR = (GPDR & ~GPIO_MBREQ) | GPIO_MBGNT;
+
+	GAFR |= (GPIO_MBGNT | GPIO_MBREQ);
+	TUCR |= TUCR_MR;
+
+	local_irq_restore(flags);
+}
+
diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h
new file mode 100644
index 0000000..bfe41da
--- /dev/null
+++ b/arch/arm/mach-sa1100/generic.h
@@ -0,0 +1,38 @@
+/*
+ * linux/arch/arm/mach-sa1100/generic.h
+ *
+ * Author: Nicolas Pitre
+ */
+
+struct sys_timer;
+
+extern struct sys_timer sa1100_timer;
+extern void __init sa1100_map_io(void);
+extern void __init sa1100_init_irq(void);
+
+#define SET_BANK(__nr,__start,__size) \
+	mi->bank[__nr].start = (__start), \
+	mi->bank[__nr].size = (__size), \
+	mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27)
+
+extern void (*sa1100fb_backlight_power)(int on);
+extern void (*sa1100fb_lcd_power)(int on);
+
+extern void sa1110_mb_enable(void);
+extern void sa1110_mb_disable(void);
+
+struct cpufreq_policy;
+
+extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz);
+extern int sa11x0_verify_speed(struct cpufreq_policy *policy);
+extern unsigned int sa11x0_getspeed(unsigned int cpu);
+extern unsigned int sa11x0_ppcr_to_freq(unsigned int idx);
+
+struct flash_platform_data;
+struct resource;
+
+extern void sa11x0_set_flash_data(struct flash_platform_data *flash,
+				  struct resource *res, int nr);
+
+struct irda_platform_data;
+void sa11x0_set_irda_data(struct irda_platform_data *irda);
diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c
new file mode 100644
index 0000000..9788d3a
--- /dev/null
+++ b/arch/arm/mach-sa1100/h3600.c
@@ -0,0 +1,892 @@
+/*
+ * Hardware definitions for Compaq iPAQ H3xxx Handheld Computers
+ *
+ * Copyright 2000,1 Compaq Computer Corporation.
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Author: Jamey Hicks.
+ *
+ * History:
+ *
+ * 2001-10-??	Andrew Christian   Added support for iPAQ H3800
+ *				   and abstracted EGPIO interface.
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/serial_core.h>
+
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+
+#include <asm/mach/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irda.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include <asm/arch/h3600.h>
+
+#if defined (CONFIG_SA1100_H3600) || defined (CONFIG_SA1100_H3100)
+#include <asm/arch/h3600_gpio.h>
+#endif
+
+#ifdef CONFIG_SA1100_H3800
+#include <asm/arch/h3600_asic.h>
+#endif
+
+#include "generic.h"
+
+struct ipaq_model_ops ipaq_model_ops;
+EXPORT_SYMBOL(ipaq_model_ops);
+
+static struct mtd_partition h3xxx_partitions[] = {
+	{
+		.name		= "H3XXX boot firmware",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+#ifdef CONFIG_MTD_2PARTS_IPAQ
+		.name		= "H3XXX root jffs2",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x00040000,
+#else
+		.name		= "H3XXX kernel",
+		.size		= 0x00080000,
+		.offset		= 0x00040000,
+	}, {
+		.name		= "H3XXX params",
+		.size		= 0x00040000,
+		.offset		= 0x000C0000,
+	}, {
+#ifdef CONFIG_JFFS2_FS
+		.name		= "H3XXX root jffs2",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x00100000,
+#else
+		.name		= "H3XXX initrd",
+		.size		= 0x00100000,
+		.offset		= 0x00100000,
+	}, {
+		.name		= "H3XXX root cramfs",
+		.size		= 0x00300000,
+		.offset		= 0x00200000,
+	}, {
+		.name		= "H3XXX usr cramfs",
+		.size		= 0x00800000,
+		.offset		= 0x00500000,
+	}, {
+		.name		= "H3XXX usr local",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x00d00000,
+#endif
+#endif
+	}
+};
+
+static void h3xxx_set_vpp(int vpp)
+{
+	assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp);
+}
+
+static struct flash_platform_data h3xxx_flash_data = {
+	.map_name	= "cfi_probe",
+	.set_vpp	= h3xxx_set_vpp,
+	.parts		= h3xxx_partitions,
+	.nr_parts	= ARRAY_SIZE(h3xxx_partitions),
+};
+
+static struct resource h3xxx_flash_resource = {
+	.start		= SA1100_CS0_PHYS,
+	.end		= SA1100_CS0_PHYS + SZ_32M - 1,
+	.flags		= IORESOURCE_MEM,
+};
+
+/*
+ * This turns the IRDA power on or off on the Compaq H3600
+ */
+static int h3600_irda_set_power(struct device *dev, unsigned int state)
+{
+	assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state );
+
+	return 0;
+}
+
+static void h3600_irda_set_speed(struct device *dev, int speed)
+{
+	if (speed < 4000000) {
+		clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL);
+	} else {
+		set_h3600_egpio(IPAQ_EGPIO_IR_FSEL);
+	}
+}
+
+static struct irda_platform_data h3600_irda_data = {
+	.set_power	= h3600_irda_set_power,
+	.set_speed	= h3600_irda_set_speed,
+};
+
+static void h3xxx_mach_init(void)
+{
+	sa11x0_set_flash_data(&h3xxx_flash_data, &h3xxx_flash_resource, 1);
+	sa11x0_set_irda_data(&h3600_irda_data);
+}
+
+/*
+ * low-level UART features
+ */
+
+static void h3600_uart_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+	if (port->mapbase == _Ser3UTCR0) {
+		if (mctrl & TIOCM_RTS)
+			GPCR = GPIO_H3600_COM_RTS;
+		else
+			GPSR = GPIO_H3600_COM_RTS;
+	}
+}
+
+static u_int h3600_uart_get_mctrl(struct uart_port *port)
+{
+	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
+
+	if (port->mapbase == _Ser3UTCR0) {
+		int gplr = GPLR;
+		/* DCD and CTS bits are inverted in GPLR by RS232 transceiver */
+		if (gplr & GPIO_H3600_COM_DCD)
+			ret &= ~TIOCM_CD;
+		if (gplr & GPIO_H3600_COM_CTS)
+			ret &= ~TIOCM_CTS;
+	}
+
+	return ret;
+}
+
+static void h3600_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+{
+	if (port->mapbase == _Ser2UTCR0) { /* TODO: REMOVE THIS */
+		assign_h3600_egpio(IPAQ_EGPIO_IR_ON, !state);
+	} else if (port->mapbase == _Ser3UTCR0) {
+		assign_h3600_egpio(IPAQ_EGPIO_RS232_ON, !state);
+	}
+}
+
+/*
+ * Enable/Disable wake up events for this serial port.
+ * Obviously, we only support this on the normal COM port.
+ */
+static int h3600_uart_set_wake(struct uart_port *port, u_int enable)
+{
+	int err = -EINVAL;
+
+	if (port->mapbase == _Ser3UTCR0) {
+		if (enable)
+			PWER |= PWER_GPIO23 | PWER_GPIO25; /* DCD and CTS */
+		else
+			PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */
+		err = 0;
+	}
+	return err;
+}
+
+static struct sa1100_port_fns h3600_port_fns __initdata = {
+	.set_mctrl	= h3600_uart_set_mctrl,
+	.get_mctrl	= h3600_uart_get_mctrl,
+	.pm		= h3600_uart_pm,
+	.set_wake	= h3600_uart_set_wake,
+};
+
+/*
+ * helper for sa1100fb
+ */
+static void h3xxx_lcd_power(int enable)
+{
+	assign_h3600_egpio(IPAQ_EGPIO_LCD_POWER, enable);
+}
+
+static struct map_desc h3600_io_desc[] __initdata = {
+ /* virtual	       physical 	  length      type */
+  { H3600_BANK_2_VIRT, SA1100_CS2_PHYS,   0x02800000, MT_DEVICE }, /* static memory bank 2  CS#2 */
+  { H3600_BANK_4_VIRT, SA1100_CS4_PHYS,   0x00800000, MT_DEVICE }, /* static memory bank 4  CS#4 */
+  { H3600_EGPIO_VIRT,  H3600_EGPIO_PHYS,  0x01000000, MT_DEVICE }, /* EGPIO 0		CS#5 */
+};
+
+/*
+ * Common map_io initialization
+ */
+
+static void __init h3xxx_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(h3600_io_desc, ARRAY_SIZE(h3600_io_desc));
+
+	sa1100_register_uart_fns(&h3600_port_fns);
+	sa1100_register_uart(0, 3); /* Common serial port */
+//	sa1100_register_uart(1, 1); /* Microcontroller on 3100/3600 */
+
+	/* Ensure those pins are outputs and driving low  */
+	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
+	/* Configure suspend conditions */
+	PGSR = 0;
+	PWER = PWER_GPIO0 | PWER_RTC;
+	PCFR = PCFR_OPDE;
+	PSDR = 0;
+
+	sa1100fb_lcd_power = h3xxx_lcd_power;
+}
+
+static __inline__ void do_blank(int setp)
+{
+	if (ipaq_model_ops.blank_callback)
+		ipaq_model_ops.blank_callback(1-setp);
+}
+
+/************************* H3100 *************************/
+
+#ifdef CONFIG_SA1100_H3100
+
+#define H3100_EGPIO	(*(volatile unsigned int *)H3600_EGPIO_VIRT)
+static unsigned int h3100_egpio = 0;
+
+static void h3100_control_egpio(enum ipaq_egpio_type x, int setp)
+{
+	unsigned int egpio = 0;
+	long	     gpio = 0;
+	unsigned long flags;
+
+	switch (x) {
+	case IPAQ_EGPIO_LCD_POWER:
+		egpio |= EGPIO_H3600_LCD_ON;
+		gpio  |= GPIO_H3100_LCD_3V_ON;
+		do_blank(setp);
+		break;
+	case IPAQ_EGPIO_LCD_ENABLE:
+		break;
+	case IPAQ_EGPIO_CODEC_NRESET:
+		egpio |= EGPIO_H3600_CODEC_NRESET;
+		break;
+	case IPAQ_EGPIO_AUDIO_ON:
+		gpio |= GPIO_H3100_AUD_PWR_ON
+			| GPIO_H3100_AUD_ON;
+		break;
+	case IPAQ_EGPIO_QMUTE:
+		gpio |= GPIO_H3100_QMUTE;
+		break;
+	case IPAQ_EGPIO_OPT_NVRAM_ON:
+		egpio |= EGPIO_H3600_OPT_NVRAM_ON;
+		break;
+	case IPAQ_EGPIO_OPT_ON:
+		egpio |= EGPIO_H3600_OPT_ON;
+		break;
+	case IPAQ_EGPIO_CARD_RESET:
+		egpio |= EGPIO_H3600_CARD_RESET;
+		break;
+	case IPAQ_EGPIO_OPT_RESET:
+		egpio |= EGPIO_H3600_OPT_RESET;
+		break;
+	case IPAQ_EGPIO_IR_ON:
+		gpio |= GPIO_H3100_IR_ON;
+		break;
+	case IPAQ_EGPIO_IR_FSEL:
+		gpio |= GPIO_H3100_IR_FSEL;
+		break;
+	case IPAQ_EGPIO_RS232_ON:
+		egpio |= EGPIO_H3600_RS232_ON;
+		break;
+	case IPAQ_EGPIO_VPP_ON:
+		egpio |= EGPIO_H3600_VPP_ON;
+		break;
+	}
+
+	if (egpio || gpio) {
+		local_irq_save(flags);
+		if (setp) {
+			h3100_egpio |= egpio;
+			GPSR = gpio;
+		} else {
+			h3100_egpio &= ~egpio;
+			GPCR = gpio;
+		}
+		H3100_EGPIO = h3100_egpio;
+		local_irq_restore(flags);
+	}
+}
+
+static unsigned long h3100_read_egpio(void)
+{
+	return h3100_egpio;
+}
+
+static int h3100_pm_callback(int req)
+{
+	if (ipaq_model_ops.pm_callback_aux)
+		return ipaq_model_ops.pm_callback_aux(req);
+	return 0;
+}
+
+static struct ipaq_model_ops h3100_model_ops __initdata = {
+	.generic_name	= "3100",
+	.control	= h3100_control_egpio,
+	.read		= h3100_read_egpio,
+	.pm_callback	= h3100_pm_callback
+};
+
+#define H3100_DIRECT_EGPIO (GPIO_H3100_BT_ON	  \
+			  | GPIO_H3100_GPIO3	  \
+			  | GPIO_H3100_QMUTE	  \
+			  | GPIO_H3100_LCD_3V_ON  \
+			  | GPIO_H3100_AUD_ON	  \
+			  | GPIO_H3100_AUD_PWR_ON \
+			  | GPIO_H3100_IR_ON	  \
+			  | GPIO_H3100_IR_FSEL)
+
+static void __init h3100_map_io(void)
+{
+	h3xxx_map_io();
+
+	/* Initialize h3100-specific values here */
+	GPCR = 0x0fffffff;	 /* All outputs are set low by default */
+	GPDR = GPIO_H3600_COM_RTS  | GPIO_H3600_L3_CLOCK |
+	       GPIO_H3600_L3_MODE  | GPIO_H3600_L3_DATA  |
+	       GPIO_H3600_CLK_SET1 | GPIO_H3600_CLK_SET0 |
+	       H3100_DIRECT_EGPIO;
+
+	/* Older bootldrs put GPIO2-9 in alternate mode on the
+	   assumption that they are used for video */
+	GAFR &= ~H3100_DIRECT_EGPIO;
+
+	H3100_EGPIO = h3100_egpio;
+	ipaq_model_ops = h3100_model_ops;
+}
+
+MACHINE_START(H3100, "Compaq iPAQ H3100")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(h3100_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= h3xxx_mach_init,
+MACHINE_END
+
+#endif /* CONFIG_SA1100_H3100 */
+
+/************************* H3600 *************************/
+
+#ifdef CONFIG_SA1100_H3600
+
+#define H3600_EGPIO	(*(volatile unsigned int *)H3600_EGPIO_VIRT)
+static unsigned int h3600_egpio = EGPIO_H3600_RS232_ON;
+
+static void h3600_control_egpio(enum ipaq_egpio_type x, int setp)
+{
+	unsigned int egpio = 0;
+	unsigned long flags;
+
+	switch (x) {
+	case IPAQ_EGPIO_LCD_POWER:
+		egpio |= EGPIO_H3600_LCD_ON |
+			 EGPIO_H3600_LCD_PCI |
+			 EGPIO_H3600_LCD_5V_ON |
+			 EGPIO_H3600_LVDD_ON;
+		do_blank(setp);
+		break;
+	case IPAQ_EGPIO_LCD_ENABLE:
+		break;
+	case IPAQ_EGPIO_CODEC_NRESET:
+		egpio |= EGPIO_H3600_CODEC_NRESET;
+		break;
+	case IPAQ_EGPIO_AUDIO_ON:
+		egpio |= EGPIO_H3600_AUD_AMP_ON |
+			 EGPIO_H3600_AUD_PWR_ON;
+		break;
+	case IPAQ_EGPIO_QMUTE:
+		egpio |= EGPIO_H3600_QMUTE;
+		break;
+	case IPAQ_EGPIO_OPT_NVRAM_ON:
+		egpio |= EGPIO_H3600_OPT_NVRAM_ON;
+		break;
+	case IPAQ_EGPIO_OPT_ON:
+		egpio |= EGPIO_H3600_OPT_ON;
+		break;
+	case IPAQ_EGPIO_CARD_RESET:
+		egpio |= EGPIO_H3600_CARD_RESET;
+		break;
+	case IPAQ_EGPIO_OPT_RESET:
+		egpio |= EGPIO_H3600_OPT_RESET;
+		break;
+	case IPAQ_EGPIO_IR_ON:
+		egpio |= EGPIO_H3600_IR_ON;
+		break;
+	case IPAQ_EGPIO_IR_FSEL:
+		egpio |= EGPIO_H3600_IR_FSEL;
+		break;
+	case IPAQ_EGPIO_RS232_ON:
+		egpio |= EGPIO_H3600_RS232_ON;
+		break;
+	case IPAQ_EGPIO_VPP_ON:
+		egpio |= EGPIO_H3600_VPP_ON;
+		break;
+	}
+
+	if (egpio) {
+		local_irq_save(flags);
+		if (setp)
+			h3600_egpio |= egpio;
+		else
+			h3600_egpio &= ~egpio;
+		H3600_EGPIO = h3600_egpio;
+		local_irq_restore(flags);
+	}
+}
+
+static unsigned long h3600_read_egpio(void)
+{
+	return h3600_egpio;
+}
+
+static int h3600_pm_callback(int req)
+{
+	if (ipaq_model_ops.pm_callback_aux)
+		return ipaq_model_ops.pm_callback_aux(req);
+	return 0;
+}
+
+static struct ipaq_model_ops h3600_model_ops __initdata = {
+	.generic_name	= "3600",
+	.control	= h3600_control_egpio,
+	.read		= h3600_read_egpio,
+	.pm_callback	= h3600_pm_callback
+};
+
+static void __init h3600_map_io(void)
+{
+	h3xxx_map_io();
+
+	/* Initialize h3600-specific values here */
+
+	GPCR = 0x0fffffff;	 /* All outputs are set low by default */
+	GPDR = GPIO_H3600_COM_RTS  | GPIO_H3600_L3_CLOCK |
+	       GPIO_H3600_L3_MODE  | GPIO_H3600_L3_DATA  |
+	       GPIO_H3600_CLK_SET1 | GPIO_H3600_CLK_SET0 |
+	       GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 |
+	       GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
+
+	H3600_EGPIO = h3600_egpio;	   /* Maintains across sleep? */
+	ipaq_model_ops = h3600_model_ops;
+}
+
+MACHINE_START(H3600, "Compaq iPAQ H3600")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(h3600_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= h3xxx_mach_init,
+MACHINE_END
+
+#endif /* CONFIG_SA1100_H3600 */
+
+#ifdef CONFIG_SA1100_H3800
+
+#define SET_ASIC1(x) \
+   do {if (setp) { H3800_ASIC1_GPIO_OUT |= (x); } else { H3800_ASIC1_GPIO_OUT &= ~(x); }} while(0)
+
+#define SET_ASIC2(x) \
+   do {if (setp) { H3800_ASIC2_GPIOPIOD |= (x); } else { H3800_ASIC2_GPIOPIOD &= ~(x); }} while(0)
+
+#define CLEAR_ASIC1(x) \
+   do {if (setp) { H3800_ASIC1_GPIO_OUT &= ~(x); } else { H3800_ASIC1_GPIO_OUT |= (x); }} while(0)
+
+#define CLEAR_ASIC2(x) \
+   do {if (setp) { H3800_ASIC2_GPIOPIOD &= ~(x); } else { H3800_ASIC2_GPIOPIOD |= (x); }} while(0)
+
+
+/*
+  On screen enable, we get
+
+     h3800_video_power_on(1)
+     LCD controller starts
+     h3800_video_lcd_enable(1)
+
+  On screen disable, we get
+
+     h3800_video_lcd_enable(0)
+     LCD controller stops
+     h3800_video_power_on(0)
+*/
+
+
+static void h3800_video_power_on(int setp)
+{
+	if (setp) {
+		H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_ON;
+		msleep(30);
+		H3800_ASIC1_GPIO_OUT |= GPIO1_VGL_ON;
+		msleep(5);
+		H3800_ASIC1_GPIO_OUT |= GPIO1_VGH_ON;
+		msleep(50);
+		H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_5V_ON;
+		msleep(5);
+	} else {
+		msleep(5);
+		H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_5V_ON;
+		msleep(50);
+		H3800_ASIC1_GPIO_OUT &= ~GPIO1_VGL_ON;
+		msleep(5);
+		H3800_ASIC1_GPIO_OUT &= ~GPIO1_VGH_ON;
+		msleep(100);
+		H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_ON;
+	}
+}
+
+static void h3800_video_lcd_enable(int setp)
+{
+	if (setp) {
+		msleep(17);	// Wait one from before turning on
+		H3800_ASIC1_GPIO_OUT |= GPIO1_LCD_PCI;
+	} else {
+		H3800_ASIC1_GPIO_OUT &= ~GPIO1_LCD_PCI;
+		msleep(30);	// Wait before turning off
+	}
+}
+
+
+static void h3800_control_egpio(enum ipaq_egpio_type x, int setp)
+{
+	switch (x) {
+	case IPAQ_EGPIO_LCD_POWER:
+		h3800_video_power_on(setp);
+		break;
+	case IPAQ_EGPIO_LCD_ENABLE:
+		h3800_video_lcd_enable(setp);
+		break;
+	case IPAQ_EGPIO_CODEC_NRESET:
+	case IPAQ_EGPIO_AUDIO_ON:
+	case IPAQ_EGPIO_QMUTE:
+		printk("%s: error - should not be called\n", __FUNCTION__);
+		break;
+	case IPAQ_EGPIO_OPT_NVRAM_ON:
+		SET_ASIC2(GPIO2_OPT_ON_NVRAM);
+		break;
+	case IPAQ_EGPIO_OPT_ON:
+		SET_ASIC2(GPIO2_OPT_ON);
+		break;
+	case IPAQ_EGPIO_CARD_RESET:
+		SET_ASIC2(GPIO2_OPT_PCM_RESET);
+		break;
+	case IPAQ_EGPIO_OPT_RESET:
+		SET_ASIC2(GPIO2_OPT_RESET);
+		break;
+	case IPAQ_EGPIO_IR_ON:
+		CLEAR_ASIC1(GPIO1_IR_ON_N);
+		break;
+	case IPAQ_EGPIO_IR_FSEL:
+		break;
+	case IPAQ_EGPIO_RS232_ON:
+		SET_ASIC1(GPIO1_RS232_ON);
+		break;
+	case IPAQ_EGPIO_VPP_ON:
+		H3800_ASIC2_FlashWP_VPP_ON = setp;
+		break;
+	}
+}
+
+static unsigned long h3800_read_egpio(void)
+{
+	return H3800_ASIC1_GPIO_OUT | (H3800_ASIC2_GPIOPIOD << 16);
+}
+
+/* We need to fix ASIC2 GPIO over suspend/resume.  At the moment,
+   it doesn't appear that ASIC1 GPIO has the same problem */
+
+static int h3800_pm_callback(int req)
+{
+	static u16 asic1_data;
+	static u16 asic2_data;
+	int result = 0;
+
+	printk("%s %d\n", __FUNCTION__, req);
+
+	switch (req) {
+	case PM_RESUME:
+		MSC2 = (MSC2 & 0x0000ffff) | 0xE4510000;  /* Set MSC2 correctly */
+
+		H3800_ASIC2_GPIOPIOD = asic2_data;
+		H3800_ASIC2_GPIODIR = GPIO2_PEN_IRQ
+			| GPIO2_SD_DETECT
+			| GPIO2_EAR_IN_N
+			| GPIO2_USB_DETECT_N
+			| GPIO2_SD_CON_SLT;
+
+		H3800_ASIC1_GPIO_OUT = asic1_data;
+
+		if (ipaq_model_ops.pm_callback_aux)
+			result = ipaq_model_ops.pm_callback_aux(req);
+		break;
+
+	case PM_SUSPEND:
+		if (ipaq_model_ops.pm_callback_aux &&
+		     ((result = ipaq_model_ops.pm_callback_aux(req)) != 0))
+			return result;
+
+		asic1_data = H3800_ASIC1_GPIO_OUT;
+		asic2_data = H3800_ASIC2_GPIOPIOD;
+		break;
+	default:
+		printk("%s: unrecognized PM callback\n", __FUNCTION__);
+		break;
+	}
+	return result;
+}
+
+static struct ipaq_model_ops h3800_model_ops __initdata = {
+	.generic_name	= "3800",
+	.control	= h3800_control_egpio,
+	.read		= h3800_read_egpio,
+	.pm_callback	= h3800_pm_callback
+};
+
+#define MAX_ASIC_ISR_LOOPS    20
+
+/* The order of these is important - see #include <asm/arch/irqs.h> */
+static u32 kpio_irq_mask[] = {
+	KPIO_KEY_ALL,
+	KPIO_SPI_INT,
+	KPIO_OWM_INT,
+	KPIO_ADC_INT,
+	KPIO_UART_0_INT,
+	KPIO_UART_1_INT,
+	KPIO_TIMER_0_INT,
+	KPIO_TIMER_1_INT,
+	KPIO_TIMER_2_INT
+};
+
+static u32 gpio_irq_mask[] = {
+	GPIO2_PEN_IRQ,
+	GPIO2_SD_DETECT,
+	GPIO2_EAR_IN_N,
+	GPIO2_USB_DETECT_N,
+	GPIO2_SD_CON_SLT,
+};
+
+static void h3800_IRQ_demux(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
+{
+	int i;
+
+	if (0) printk("%s: interrupt received\n", __FUNCTION__);
+
+	desc->chip->ack(irq);
+
+	for (i = 0; i < MAX_ASIC_ISR_LOOPS && (GPLR & GPIO_H3800_ASIC); i++) {
+		u32 irq;
+		int j;
+
+		/* KPIO */
+		irq = H3800_ASIC2_KPIINTFLAG;
+		if (0) printk("%s KPIO 0x%08X\n", __FUNCTION__, irq);
+		for (j = 0; j < H3800_KPIO_IRQ_COUNT; j++)
+			if (irq & kpio_irq_mask[j])
+				do_edge_IRQ(H3800_KPIO_IRQ_COUNT + j, irq_desc + H3800_KPIO_IRQ_COUNT + j, regs);
+
+		/* GPIO2 */
+		irq = H3800_ASIC2_GPIINTFLAG;
+		if (0) printk("%s GPIO 0x%08X\n", __FUNCTION__, irq);
+		for (j = 0; j < H3800_GPIO_IRQ_COUNT; j++)
+			if (irq & gpio_irq_mask[j])
+				do_edge_IRQ(H3800_GPIO_IRQ_COUNT + j, irq_desc + H3800_GPIO_IRQ_COUNT + j , regs);
+	}
+
+	if (i >= MAX_ASIC_ISR_LOOPS)
+		printk("%s: interrupt processing overrun\n", __FUNCTION__);
+
+	/* For level-based interrupts */
+	desc->chip->unmask(irq);
+
+}
+
+static struct irqaction h3800_irq = {
+	.name		= "h3800_asic",
+	.handler	= h3800_IRQ_demux,
+	.flags		= SA_INTERRUPT,
+};
+
+u32 kpio_int_shadow = 0;
+
+
+/* mask_ack <- IRQ is first serviced.
+       mask <- IRQ is disabled.
+     unmask <- IRQ is enabled
+
+     The INTCLR registers are poorly documented.  I believe that writing
+     a "1" to the register clears the specific interrupt, but the documentation
+     indicates writing a "0" clears the interrupt.  In any case, they shouldn't
+     be read (that's the INTFLAG register)
+ */
+
+static void h3800_mask_ack_kpio_irq(unsigned int irq)
+{
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow &= ~mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+	H3800_ASIC2_KPIINTCLR  = mask;
+}
+
+static void h3800_mask_kpio_irq(unsigned int irq)
+{
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow &= ~mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+}
+
+static void h3800_unmask_kpio_irq(unsigned int irq)
+{
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow |= mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+}
+
+static void h3800_mask_ack_gpio_irq(unsigned int irq)
+{
+	u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START];
+	H3800_ASIC2_GPIINTSTAT &= ~mask;
+	H3800_ASIC2_GPIINTCLR	= mask;
+}
+
+static void h3800_mask_gpio_irq(unsigned int irq)
+{
+	u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START];
+	H3800_ASIC2_GPIINTSTAT &= ~mask;
+	}
+
+static void h3800_unmask_gpio_irq(unsigned int irq)
+{
+	u32 mask = gpio_irq_mask[irq - H3800_GPIO_IRQ_START];
+	H3800_ASIC2_GPIINTSTAT |= mask;
+}
+
+static void __init h3800_init_irq(void)
+{
+	int i;
+
+	/* Initialize standard IRQs */
+	sa1100_init_irq();
+
+	/* Disable all IRQs and set up clock */
+	H3800_ASIC2_KPIINTSTAT	   =  0;     /* Disable all interrupts */
+	H3800_ASIC2_GPIINTSTAT	   =  0;
+
+	H3800_ASIC2_KPIINTCLR	   =  0;     /* Clear all KPIO interrupts */
+	H3800_ASIC2_GPIINTCLR	   =  0;     /* Clear all GPIO interrupts */
+
+//	H3800_ASIC2_KPIINTCLR	   =  0xffff;	  /* Clear all KPIO interrupts */
+//	H3800_ASIC2_GPIINTCLR	   =  0xffff;	  /* Clear all GPIO interrupts */
+
+	H3800_ASIC2_CLOCK_Enable       |= ASIC2_CLOCK_EX0;   /* 32 kHZ crystal on */
+	H3800_ASIC2_INTR_ClockPrescale |= ASIC2_INTCPS_SET;
+	H3800_ASIC2_INTR_ClockPrescale	= ASIC2_INTCPS_CPS(0x0e) | ASIC2_INTCPS_SET;
+	H3800_ASIC2_INTR_TimerSet	= 1;
+
+#if 0
+	for (i = 0; i < H3800_KPIO_IRQ_COUNT; i++) {
+		int irq = i + H3800_KPIO_IRQ_START;
+		irq_desc[irq].valid    = 1;
+		irq_desc[irq].probe_ok = 1;
+		set_irq_chip(irq, &h3800_kpio_irqchip);
+	}
+
+	for (i = 0; i < H3800_GPIO_IRQ_COUNT; i++) {
+		int irq = i + H3800_GPIO_IRQ_START;
+		irq_desc[irq].valid    = 1;
+		irq_desc[irq].probe_ok = 1;
+		set_irq_chip(irq, &h3800_gpio_irqchip);
+	}
+#endif
+	set_irq_type(IRQ_GPIO_H3800_ASIC, IRQT_RISING);
+	set_irq_chained_handler(IRQ_GPIO_H3800_ASIC, &h3800_IRQ_demux);
+}
+
+
+#define ASIC1_OUTPUTS	 0x7fff   /* First 15 bits are used */
+
+static void __init h3800_map_io(void)
+{
+	h3xxx_map_io();
+
+	/* Add wakeup on AC plug/unplug */
+	PWER  |= PWER_GPIO12;
+
+	/* Initialize h3800-specific values here */
+	GPCR = 0x0fffffff;	 /* All outputs are set low by default */
+	GAFR =	GPIO_H3800_CLK_OUT |
+		GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 |
+		GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
+	GPDR =	GPIO_H3800_CLK_OUT |
+		GPIO_H3600_COM_RTS  | GPIO_H3600_L3_CLOCK |
+		GPIO_H3600_L3_MODE  | GPIO_H3600_L3_DATA  |
+		GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 |
+		GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
+	TUCR =	TUCR_3_6864MHz;   /* Seems to be used only for the Bluetooth UART */
+
+	/* Fix the memory bus */
+	MSC2 = (MSC2 & 0x0000ffff) | 0xE4510000;
+
+	/* Set up ASIC #1 */
+	H3800_ASIC1_GPIO_DIR		= ASIC1_OUTPUTS;	    /* All outputs */
+	H3800_ASIC1_GPIO_MASK		= ASIC1_OUTPUTS;	    /* No interrupts */
+	H3800_ASIC1_GPIO_SLEEP_MASK	= ASIC1_OUTPUTS;
+	H3800_ASIC1_GPIO_SLEEP_DIR	= ASIC1_OUTPUTS;
+	H3800_ASIC1_GPIO_SLEEP_OUT	= GPIO1_EAR_ON_N;
+	H3800_ASIC1_GPIO_BATT_FAULT_DIR = ASIC1_OUTPUTS;
+	H3800_ASIC1_GPIO_BATT_FAULT_OUT = GPIO1_EAR_ON_N;
+
+	H3800_ASIC1_GPIO_OUT = GPIO1_IR_ON_N
+				      | GPIO1_RS232_ON
+				      | GPIO1_EAR_ON_N;
+
+	/* Set up ASIC #2 */
+	H3800_ASIC2_GPIOPIOD	= GPIO2_IN_Y1_N | GPIO2_IN_X1_N;
+	H3800_ASIC2_GPOBFSTAT	= GPIO2_IN_Y1_N | GPIO2_IN_X1_N;
+
+	H3800_ASIC2_GPIODIR	= GPIO2_PEN_IRQ
+				      | GPIO2_SD_DETECT
+				      | GPIO2_EAR_IN_N
+				      | GPIO2_USB_DETECT_N
+				      | GPIO2_SD_CON_SLT;
+
+	/* TODO : Set sleep states & battery fault states */
+
+	/* Clear VPP Enable */
+	H3800_ASIC2_FlashWP_VPP_ON = 0;
+	ipaq_model_ops = h3800_model_ops;
+}
+
+MACHINE_START(H3800, "Compaq iPAQ H3800")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(h3800_map_io)
+	INITIRQ(h3800_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= h3xxx_mach_init,
+MACHINE_END
+
+#endif /* CONFIG_SA1100_H3800 */
diff --git a/arch/arm/mach-sa1100/hackkit.c b/arch/arm/mach-sa1100/hackkit.c
new file mode 100644
index 0000000..5708417
--- /dev/null
+++ b/arch/arm/mach-sa1100/hackkit.c
@@ -0,0 +1,200 @@
+/*
+ * linux/arch/arm/mach-sa1100/hackkit.c
+ *
+ * Copyright (C) 2002 Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de>
+ *
+ * This file contains all HackKit tweaks. Based on original work from
+ * Nicolas Pitre's assabet fixes
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/serial_core.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include "generic.h"
+
+/**********************************************************************
+ *  prototypes
+ */
+
+/* init funcs */
+static void __init hackkit_map_io(void);
+
+static u_int hackkit_get_mctrl(struct uart_port *port);
+static void hackkit_set_mctrl(struct uart_port *port, u_int mctrl);
+static void hackkit_uart_pm(struct uart_port *port, u_int state, u_int oldstate);
+
+/**********************************************************************
+ *  global data
+ */
+
+/**********************************************************************
+ *  static data
+ */
+
+static struct map_desc hackkit_io_desc[] __initdata = {
+ /* virtual     physical    length      type */
+  { 0xe8000000, 0x00000000, 0x01000000, MT_DEVICE } /* Flash bank 0 */
+};
+
+static struct sa1100_port_fns hackkit_port_fns __initdata = {
+	.set_mctrl	= hackkit_set_mctrl,
+	.get_mctrl	= hackkit_get_mctrl,
+	.pm		= hackkit_uart_pm,
+};
+
+/**********************************************************************
+ *  Static functions
+ */
+
+static void __init hackkit_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(hackkit_io_desc, ARRAY_SIZE(hackkit_io_desc));
+
+	sa1100_register_uart_fns(&hackkit_port_fns);
+	sa1100_register_uart(0, 1);	/* com port */
+	sa1100_register_uart(1, 2);
+	sa1100_register_uart(2, 3);	/* radio module */
+
+	Ser1SDCR0 |= SDCR0_SUS;
+}
+
+/**
+ *	hackkit_uart_pm - powermgmt callback function for system 3 UART
+ *	@port: uart port structure
+ *	@state: pm state
+ *	@oldstate: old pm state
+ *
+ */
+static void hackkit_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+{
+	/* TODO: switch on/off uart in powersave mode */
+}
+
+/*
+ * Note! this can be called from IRQ context.
+ * FIXME: No modem ctrl lines yet.
+ */
+static void hackkit_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+#if 0
+	if (port->mapbase == _Ser1UTCR0) {
+		u_int set = 0, clear = 0;
+
+		if (mctrl & TIOCM_RTS)
+			set |= PT_CTRL2_RS1_RTS;
+		else
+			clear |= PT_CTRL2_RS1_RTS;
+
+		if (mctrl & TIOCM_DTR)
+			set |= PT_CTRL2_RS1_DTR;
+		else
+			clear |= PT_CTRL2_RS1_DTR;
+
+		PTCTRL2_clear(clear);
+		PTCTRL2_set(set);
+	}
+#endif
+}
+
+static u_int hackkit_get_mctrl(struct uart_port *port)
+{
+	u_int ret = 0;
+#if 0
+	u_int irqsr = PT_IRQSR;
+
+	/* need 2 reads to read current value */
+	irqsr = PT_IRQSR;
+
+	/* TODO: check IRQ source register for modem/com
+	 status lines and set them correctly. */
+#endif
+
+	ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
+
+	return ret;
+}
+
+static struct mtd_partition hackkit_partitions[] = {
+	{
+		.name		= "BLOB",
+		.size		= 0x00040000,
+		.offset		= 0x00000000,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "config",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "kernel",
+		.size		= 0x00100000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "initrd",
+		.size		= 0x00180000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "rootfs",
+		.size		= 0x700000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "data",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+	}
+};
+
+static struct flash_platform_data hackkit_flash_data = {
+	.map_name	= "cfi_probe",
+	.parts		= hackkit_partitions,
+	.nr_parts	= ARRAY_SIZE(hackkit_partitions),
+};
+
+static struct resource hackkit_flash_resource = {
+	.start		= SA1100_CS0_PHYS,
+	.end		= SA1100_CS0_PHYS + SZ_32M,
+	.flags		= IORESOURCE_MEM,
+};
+
+static void __init hackkit_init(void)
+{
+	sa11x0_set_flash_data(&hackkit_flash_data, &hackkit_flash_resource, 1);
+}
+
+/**********************************************************************
+ *  Exported Functions
+ */
+
+MACHINE_START(HACKKIT, "HackKit Cpu Board")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(hackkit_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= hackkit_init,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/irq.c b/arch/arm/mach-sa1100/irq.c
new file mode 100644
index 0000000..66a929c
--- /dev/null
+++ b/arch/arm/mach-sa1100/irq.c
@@ -0,0 +1,332 @@
+/*
+ * linux/arch/arm/mach-sa1100/irq.c
+ *
+ * Copyright (C) 1999-2001 Nicolas Pitre
+ *
+ * Generic IRQ handling for the SA11x0, GPIO 11-27 IRQ demultiplexing.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/ptrace.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+#include "generic.h"
+
+
+/*
+ * SA1100 GPIO edge detection for IRQs:
+ * IRQs are generated on Falling-Edge, Rising-Edge, or both.
+ * Use this instead of directly setting GRER/GFER.
+ */
+static int GPIO_IRQ_rising_edge;
+static int GPIO_IRQ_falling_edge;
+static int GPIO_IRQ_mask = (1 << 11) - 1;
+
+/*
+ * To get the GPIO number from an IRQ number
+ */
+#define GPIO_11_27_IRQ(i)	((i) - 21)
+#define GPIO11_27_MASK(irq)	(1 << GPIO_11_27_IRQ(irq))
+
+static int sa1100_gpio_type(unsigned int irq, unsigned int type)
+{
+	unsigned int mask;
+
+	if (irq <= 10)
+		mask = 1 << irq;
+	else
+		mask = GPIO11_27_MASK(irq);
+
+	if (type == IRQT_PROBE) {
+		if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask)
+			return 0;
+		type = __IRQT_RISEDGE | __IRQT_FALEDGE;
+	}
+
+	if (type & __IRQT_RISEDGE) {
+		GPIO_IRQ_rising_edge |= mask;
+	} else
+		GPIO_IRQ_rising_edge &= ~mask;
+	if (type & __IRQT_FALEDGE) {
+		GPIO_IRQ_falling_edge |= mask;
+	} else
+		GPIO_IRQ_falling_edge &= ~mask;
+
+	GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
+	GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
+
+	return 0;
+}
+
+/*
+ * GPIO IRQs must be acknowledged.  This is for IRQs from 0 to 10.
+ */
+static void sa1100_low_gpio_ack(unsigned int irq)
+{
+	GEDR = (1 << irq);
+}
+
+static void sa1100_low_gpio_mask(unsigned int irq)
+{
+	ICMR &= ~(1 << irq);
+}
+
+static void sa1100_low_gpio_unmask(unsigned int irq)
+{
+	ICMR |= 1 << irq;
+}
+
+static int sa1100_low_gpio_wake(unsigned int irq, unsigned int on)
+{
+	if (on)
+		PWER |= 1 << irq;
+	else
+		PWER &= ~(1 << irq);
+	return 0;
+}
+
+static struct irqchip sa1100_low_gpio_chip = {
+	.ack		= sa1100_low_gpio_ack,
+	.mask		= sa1100_low_gpio_mask,
+	.unmask		= sa1100_low_gpio_unmask,
+	.type		= sa1100_gpio_type,
+	.wake		= sa1100_low_gpio_wake,
+};
+
+/*
+ * IRQ11 (GPIO11 through 27) handler.  We enter here with the
+ * irq_controller_lock held, and IRQs disabled.  Decode the IRQ
+ * and call the handler.
+ */
+static void
+sa1100_high_gpio_handler(unsigned int irq, struct irqdesc *desc,
+			 struct pt_regs *regs)
+{
+	unsigned int mask;
+
+	mask = GEDR & 0xfffff800;
+	do {
+		/*
+		 * clear down all currently active IRQ sources.
+		 * We will be processing them all.
+		 */
+		GEDR = mask;
+
+		irq = IRQ_GPIO11;
+		desc = irq_desc + irq;
+		mask >>= 11;
+		do {
+			if (mask & 1)
+				desc->handle(irq, desc, regs);
+			mask >>= 1;
+			irq++;
+			desc++;
+		} while (mask);
+
+		mask = GEDR & 0xfffff800;
+	} while (mask);
+}
+
+/*
+ * Like GPIO0 to 10, GPIO11-27 IRQs need to be handled specially.
+ * In addition, the IRQs are all collected up into one bit in the
+ * interrupt controller registers.
+ */
+static void sa1100_high_gpio_ack(unsigned int irq)
+{
+	unsigned int mask = GPIO11_27_MASK(irq);
+
+	GEDR = mask;
+}
+
+static void sa1100_high_gpio_mask(unsigned int irq)
+{
+	unsigned int mask = GPIO11_27_MASK(irq);
+
+	GPIO_IRQ_mask &= ~mask;
+
+	GRER &= ~mask;
+	GFER &= ~mask;
+}
+
+static void sa1100_high_gpio_unmask(unsigned int irq)
+{
+	unsigned int mask = GPIO11_27_MASK(irq);
+
+	GPIO_IRQ_mask |= mask;
+
+	GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
+	GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
+}
+
+static int sa1100_high_gpio_wake(unsigned int irq, unsigned int on)
+{
+	if (on)
+		PWER |= GPIO11_27_MASK(irq);
+	else
+		PWER &= ~GPIO11_27_MASK(irq);
+	return 0;
+}
+
+static struct irqchip sa1100_high_gpio_chip = {
+	.ack		= sa1100_high_gpio_ack,
+	.mask		= sa1100_high_gpio_mask,
+	.unmask		= sa1100_high_gpio_unmask,
+	.type		= sa1100_gpio_type,
+	.wake		= sa1100_high_gpio_wake,
+};
+
+/*
+ * We don't need to ACK IRQs on the SA1100 unless they're GPIOs
+ * this is for internal IRQs i.e. from 11 to 31.
+ */
+static void sa1100_mask_irq(unsigned int irq)
+{
+	ICMR &= ~(1 << irq);
+}
+
+static void sa1100_unmask_irq(unsigned int irq)
+{
+	ICMR |= (1 << irq);
+}
+
+static struct irqchip sa1100_normal_chip = {
+	.ack		= sa1100_mask_irq,
+	.mask		= sa1100_mask_irq,
+	.unmask		= sa1100_unmask_irq,
+};
+
+static struct resource irq_resource = {
+	.name	= "irqs",
+	.start	= 0x90050000,
+	.end	= 0x9005ffff,
+};
+
+static struct sa1100irq_state {
+	unsigned int	saved;
+	unsigned int	icmr;
+	unsigned int	iclr;
+	unsigned int	iccr;
+} sa1100irq_state;
+
+static int sa1100irq_suspend(struct sys_device *dev, pm_message_t state)
+{
+	struct sa1100irq_state *st = &sa1100irq_state;
+
+	st->saved = 1;
+	st->icmr = ICMR;
+	st->iclr = ICLR;
+	st->iccr = ICCR;
+
+	/*
+	 * Disable all GPIO-based interrupts.
+	 */
+	ICMR &= ~(IC_GPIO11_27|IC_GPIO10|IC_GPIO9|IC_GPIO8|IC_GPIO7|
+		  IC_GPIO6|IC_GPIO5|IC_GPIO4|IC_GPIO3|IC_GPIO2|
+		  IC_GPIO1|IC_GPIO0);
+
+	/*
+	 * Set the appropriate edges for wakeup.
+	 */
+	GRER = PWER & GPIO_IRQ_rising_edge;
+	GFER = PWER & GPIO_IRQ_falling_edge;
+	
+	/*
+	 * Clear any pending GPIO interrupts.
+	 */
+	GEDR = GEDR;
+
+	return 0;
+}
+
+static int sa1100irq_resume(struct sys_device *dev)
+{
+	struct sa1100irq_state *st = &sa1100irq_state;
+
+	if (st->saved) {
+		ICCR = st->iccr;
+		ICLR = st->iclr;
+
+		GRER = GPIO_IRQ_rising_edge & GPIO_IRQ_mask;
+		GFER = GPIO_IRQ_falling_edge & GPIO_IRQ_mask;
+
+		ICMR = st->icmr;
+	}
+	return 0;
+}
+
+static struct sysdev_class sa1100irq_sysclass = {
+	set_kset_name("sa11x0-irq"),
+	.suspend	= sa1100irq_suspend,
+	.resume		= sa1100irq_resume,
+};
+
+static struct sys_device sa1100irq_device = {
+	.id		= 0,
+	.cls		= &sa1100irq_sysclass,
+};
+
+static int __init sa1100irq_init_devicefs(void)
+{
+	sysdev_class_register(&sa1100irq_sysclass);
+	return sysdev_register(&sa1100irq_device);
+}
+
+device_initcall(sa1100irq_init_devicefs);
+
+void __init sa1100_init_irq(void)
+{
+	unsigned int irq;
+
+	request_resource(&iomem_resource, &irq_resource);
+
+	/* disable all IRQs */
+	ICMR = 0;
+
+	/* all IRQs are IRQ, not FIQ */
+	ICLR = 0;
+
+	/* clear all GPIO edge detects */
+	GFER = 0;
+	GRER = 0;
+	GEDR = -1;
+
+	/*
+	 * Whatever the doc says, this has to be set for the wait-on-irq
+	 * instruction to work... on a SA1100 rev 9 at least.
+	 */
+	ICCR = 1;
+
+	for (irq = 0; irq <= 10; irq++) {
+		set_irq_chip(irq, &sa1100_low_gpio_chip);
+		set_irq_handler(irq, do_edge_IRQ);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	for (irq = 12; irq <= 31; irq++) {
+		set_irq_chip(irq, &sa1100_normal_chip);
+		set_irq_handler(irq, do_level_IRQ);
+		set_irq_flags(irq, IRQF_VALID);
+	}
+
+	for (irq = 32; irq <= 48; irq++) {
+		set_irq_chip(irq, &sa1100_high_gpio_chip);
+		set_irq_handler(irq, do_edge_IRQ);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	/*
+	 * Install handler for GPIO 11-27 edge detect interrupts
+	 */
+	set_irq_chip(IRQ_GPIO11_27, &sa1100_normal_chip);
+	set_irq_chained_handler(IRQ_GPIO11_27, sa1100_high_gpio_handler);
+}
diff --git a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c
new file mode 100644
index 0000000..6be7829
--- /dev/null
+++ b/arch/arm/mach-sa1100/jornada720.c
@@ -0,0 +1,105 @@
+/*
+ * linux/arch/arm/mach-sa1100/jornada720.c
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/sa1111.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include "generic.h"
+
+
+#define JORTUCR_VAL	0x20000400
+
+static struct resource sa1111_resources[] = {
+	[0] = {
+		.start		= 0x40000000,
+		.end		= 0x40001fff,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= IRQ_GPIO1,
+		.end		= IRQ_GPIO1,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static u64 sa1111_dmamask = 0xffffffffUL;
+
+static struct platform_device sa1111_device = {
+	.name		= "sa1111",
+	.id		= 0,
+	.dev		= {
+		.dma_mask = &sa1111_dmamask,
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa1111_resources),
+	.resource	= sa1111_resources,
+};
+
+static struct platform_device *devices[] __initdata = {
+	&sa1111_device,
+};
+
+static int __init jornada720_init(void)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_jornada720()) {
+		GPDR |= GPIO_GPIO20;
+		TUCR = JORTUCR_VAL;	/* set the oscillator out to the SA-1101 */
+
+		GPSR = GPIO_GPIO20;
+		udelay(1);
+		GPCR = GPIO_GPIO20;
+		udelay(1);
+		GPSR = GPIO_GPIO20;
+		udelay(20);
+
+		/* LDD4 is speaker, LDD3 is microphone */
+		PPSR &= ~(PPC_LDD3 | PPC_LDD4);
+		PPDR |= PPC_LDD3 | PPC_LDD4;
+
+		ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+	}
+	return ret;
+}
+
+arch_initcall(jornada720_init);
+
+static struct map_desc jornada720_io_desc[] __initdata = {
+ /* virtual     physical    length      type */
+  { 0xf0000000, 0x48000000, 0x00100000, MT_DEVICE }, /* Epson registers */
+  { 0xf1000000, 0x48200000, 0x00100000, MT_DEVICE }, /* Epson frame buffer */
+  { 0xf4000000, 0x40000000, 0x00100000, MT_DEVICE }  /* SA-1111 */
+};
+
+static void __init jornada720_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(jornada720_io_desc, ARRAY_SIZE(jornada720_io_desc));
+	
+	sa1100_register_uart(0, 3);
+	sa1100_register_uart(1, 1);
+}
+
+MACHINE_START(JORNADA720, "HP Jornada 720")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(jornada720_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c
new file mode 100644
index 0000000..51c08cc
--- /dev/null
+++ b/arch/arm/mach-sa1100/lart.c
@@ -0,0 +1,49 @@
+/*
+ * linux/arch/arm/mach-sa1100/lart.c
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+
+#include <asm/hardware.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+
+#include "generic.h"
+
+
+#warning "include/asm/arch-sa1100/ide.h needs fixing for lart"
+
+static struct map_desc lart_io_desc[] __initdata = {
+ /* virtual     physical    length      type */
+  { 0xe8000000, 0x00000000, 0x00400000, MT_DEVICE }, /* main flash memory */
+  { 0xec000000, 0x08000000, 0x00400000, MT_DEVICE }  /* main flash, alternative location */
+};
+
+static void __init lart_map_io(void)
+{
+	sa1100_map_io();
+	iotable_init(lart_io_desc, ARRAY_SIZE(lart_io_desc));
+
+	sa1100_register_uart(0, 3);
+	sa1100_register_uart(1, 1);
+	sa1100_register_uart(2, 2);
+
+	GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
+	GPDR |= GPIO_UART_TXD;
+	GPDR &= ~GPIO_UART_RXD;
+	PPAR |= PPAR_UPR;
+}
+
+MACHINE_START(LART, "LART")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(lart_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/leds-assabet.c b/arch/arm/mach-sa1100/leds-assabet.c
new file mode 100644
index 0000000..e9aa9df
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds-assabet.c
@@ -0,0 +1,115 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds-assabet.c
+ *
+ * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
+ *
+ * Original (leds-footbridge.c) by Russell King
+ *
+ * Assabet uses the LEDs as follows:
+ *   - Green - toggles state every 50 timer interrupts
+ *   - Red   - on if system is not idle
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+#include <asm/arch/assabet.h>
+
+#include "leds.h"
+
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+#define ASSABET_BCR_LED_MASK	(ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED)
+
+void assabet_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	switch (evt) {
+	case led_start:
+		hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
+		led_state = LED_STATE_ENABLED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
+		ASSABET_BCR_frob(ASSABET_BCR_LED_MASK, hw_led_state);
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= ASSABET_BCR_LED_GREEN;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= ASSABET_BCR_LED_RED;
+		break;
+
+	case led_idle_end:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~ASSABET_BCR_LED_RED;
+		break;
+#endif
+
+	case led_halted:
+		break;
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~ASSABET_BCR_LED_GREEN;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= ASSABET_BCR_LED_GREEN;
+		break;
+
+	case led_amber_on:
+		break;
+
+	case led_amber_off:
+		break;
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~ASSABET_BCR_LED_RED;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= ASSABET_BCR_LED_RED;
+		break;
+
+	default:
+		break;
+	}
+
+	if  (led_state & LED_STATE_ENABLED)
+		ASSABET_BCR_frob(ASSABET_BCR_LED_MASK, hw_led_state);
+
+	local_irq_restore(flags);
+}
diff --git a/arch/arm/mach-sa1100/leds-badge4.c b/arch/arm/mach-sa1100/leds-badge4.c
new file mode 100644
index 0000000..0a8f87b
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds-badge4.c
@@ -0,0 +1,112 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds-badge4.c
+ *
+ * Author: Christopher Hoover <ch@hpl.hp.com>
+ * Copyright (C) 2002 Hewlett-Packard Company
+ *
+ * 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/config.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#include "leds.h"
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+#define LED_RED		GPIO_GPIO(7)
+#define LED_GREEN       GPIO_GPIO(9)
+#define LED_MASK	(LED_RED|LED_GREEN)
+
+#define LED_IDLE	LED_GREEN
+#define LED_TIMER	LED_RED
+
+void badge4_leds_event(led_event_t evt)
+{
+        unsigned long flags;
+
+	local_irq_save(flags);
+
+        switch (evt) {
+        case led_start:
+		GPDR |= LED_MASK;
+                hw_led_state = LED_MASK;
+                led_state = LED_STATE_ENABLED;
+                break;
+
+        case led_stop:
+                led_state &= ~LED_STATE_ENABLED;
+                break;
+
+        case led_claim:
+                led_state |= LED_STATE_CLAIMED;
+                hw_led_state = LED_MASK;
+                break;
+
+        case led_release:
+                led_state &= ~LED_STATE_CLAIMED;
+                hw_led_state = LED_MASK;
+                break;
+
+#ifdef CONFIG_LEDS_TIMER
+        case led_timer:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state ^= LED_TIMER;
+                break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+        case led_idle_start:
+		/* LED off when system is idle */
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_IDLE;
+                break;
+
+        case led_idle_end:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_IDLE;
+                break;
+#endif
+
+        case led_red_on:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_RED;
+                break;
+
+        case led_red_off:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_RED;
+                break;
+
+        case led_green_on:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_GREEN;
+                break;
+
+        case led_green_off:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_GREEN;
+                break;
+
+	default:
+		break;
+        }
+
+        if  (led_state & LED_STATE_ENABLED) {
+                GPSR = hw_led_state;
+                GPCR = hw_led_state ^ LED_MASK;
+        }
+
+	local_irq_restore(flags);
+}
diff --git a/arch/arm/mach-sa1100/leds-cerf.c b/arch/arm/mach-sa1100/leds-cerf.c
new file mode 100644
index 0000000..f6635a2
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds-cerf.c
@@ -0,0 +1,111 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds-cerf.c
+ *
+ * Author: ???
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#include "leds.h"
+
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+#define LED_D0          GPIO_GPIO(0)
+#define LED_D1          GPIO_GPIO(1)
+#define LED_D2          GPIO_GPIO(2)
+#define LED_D3          GPIO_GPIO(3)
+#define LED_MASK        (LED_D0|LED_D1|LED_D2|LED_D3)
+
+void cerf_leds_event(led_event_t evt)
+{
+        unsigned long flags;
+
+	local_irq_save(flags);
+
+        switch (evt) {
+        case led_start:
+                hw_led_state = LED_MASK;
+                led_state = LED_STATE_ENABLED;
+                break;
+
+        case led_stop:
+                led_state &= ~LED_STATE_ENABLED;
+                break;
+
+        case led_claim:
+                led_state |= LED_STATE_CLAIMED;
+                hw_led_state = LED_MASK;
+                break;
+        case led_release:
+                led_state &= ~LED_STATE_CLAIMED;
+                hw_led_state = LED_MASK;
+                break;
+
+#ifdef CONFIG_LEDS_TIMER
+        case led_timer:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state ^= LED_D0;
+                break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+        case led_idle_start:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_D1;
+                break;
+
+        case led_idle_end:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_D1;
+                break;
+#endif
+        case led_green_on:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_D2;
+                break;
+
+        case led_green_off:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_D2;
+                break;
+
+        case led_amber_on:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_D3;
+                break;
+
+        case led_amber_off:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_D3;
+                break;
+
+        case led_red_on:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state &= ~LED_D1;
+                break;
+
+        case led_red_off:
+                if (!(led_state & LED_STATE_CLAIMED))
+                        hw_led_state |= LED_D1;
+                break;
+
+        default:
+                break;
+        }
+
+        if  (led_state & LED_STATE_ENABLED) {
+                GPSR = hw_led_state;
+                GPCR = hw_led_state ^ LED_MASK;
+        }
+
+	local_irq_restore(flags);
+}
diff --git a/arch/arm/mach-sa1100/leds-hackkit.c b/arch/arm/mach-sa1100/leds-hackkit.c
new file mode 100644
index 0000000..2e5fa14
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds-hackkit.c
@@ -0,0 +1,113 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds-hackkit.c
+ *
+ * based on leds-lart.c
+ *
+ * (C) Erik Mouw (J.A.K.Mouw@its.tudelft.nl), April 21, 2000
+ * (C) Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de>, 2002
+ *
+ * The HackKit has two leds (GPIO 22/23). The red led (gpio 22) is used
+ * as cpu led, the green one is used as timer led.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#include "leds.h"
+
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+#define LED_GREEN    GPIO_GPIO23
+#define LED_RED    GPIO_GPIO22
+#define LED_MASK  (LED_RED | LED_GREEN)
+
+void hackkit_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	switch(evt) {
+		case led_start:
+			/* pin 22/23 are outputs */
+			GPDR |= LED_MASK;
+			hw_led_state = LED_MASK;
+			led_state = LED_STATE_ENABLED;
+			break;
+
+		case led_stop:
+			led_state &= ~LED_STATE_ENABLED;
+			break;
+
+		case led_claim:
+			led_state |= LED_STATE_CLAIMED;
+			hw_led_state = LED_MASK;
+			break;
+
+		case led_release:
+			led_state &= ~LED_STATE_CLAIMED;
+			hw_led_state = LED_MASK;
+			break;
+
+#ifdef CONFIG_LEDS_TIMER
+		case led_timer:
+			if (!(led_state & LED_STATE_CLAIMED))
+				hw_led_state ^= LED_GREEN;
+			break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+		case led_idle_start:
+			/* The LART people like the LED to be off when the
+			   system is idle... */
+			if (!(led_state & LED_STATE_CLAIMED))
+				hw_led_state &= ~LED_RED;
+			break;
+
+		case led_idle_end:
+			/* ... and on if the system is not idle */
+			if (!(led_state & LED_STATE_CLAIMED))
+				hw_led_state |= LED_RED;
+			break;
+#endif
+
+		case led_red_on:
+			if (led_state & LED_STATE_CLAIMED)
+				hw_led_state &= ~LED_RED;
+			break;
+
+		case led_red_off:
+			if (led_state & LED_STATE_CLAIMED)
+				hw_led_state |= LED_RED;
+			break;
+
+		case led_green_on:
+			if (led_state & LED_STATE_CLAIMED)
+				hw_led_state &= ~LED_GREEN;
+			break;
+
+		case led_green_off:
+			if (led_state & LED_STATE_CLAIMED)
+				hw_led_state |= LED_GREEN;
+			break;
+
+		default:
+			break;
+	}
+
+	/* Now set the GPIO state, or nothing will happen at all */
+	if (led_state & LED_STATE_ENABLED) {
+		GPSR = hw_led_state;
+		GPCR = hw_led_state ^ LED_MASK;
+	}
+
+	local_irq_restore(flags);
+}
diff --git a/arch/arm/mach-sa1100/leds-lart.c b/arch/arm/mach-sa1100/leds-lart.c
new file mode 100644
index 0000000..1875014
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds-lart.c
@@ -0,0 +1,102 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds-lart.c
+ *
+ * (C) Erik Mouw (J.A.K.Mouw@its.tudelft.nl), April 21, 2000
+ *
+ * LART uses the LED as follows:
+ *   - GPIO23 is the LED, on if system is not idle
+ *  You can use both CONFIG_LEDS_CPU and CONFIG_LEDS_TIMER at the same
+ *  time, but in that case the timer events will still dictate the
+ *  pace of the LED.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#include "leds.h"
+
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+#define LED_23    GPIO_GPIO23
+#define LED_MASK  (LED_23)
+
+void lart_leds_event(led_event_t evt)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	switch(evt) {
+	case led_start:
+		/* pin 23 is output pin */
+		GPDR |= LED_23;
+		hw_led_state = LED_MASK;
+		led_state = LED_STATE_ENABLED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = LED_MASK;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = LED_MASK;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= LED_23;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		/* The LART people like the LED to be off when the
+                   system is idle... */
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state &= ~LED_23;
+		break;
+
+	case led_idle_end:
+		/* ... and on if the system is not idle */
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state |= LED_23;
+		break;
+#endif
+
+	case led_red_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~LED_23;
+		break;
+
+	case led_red_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= LED_23;
+		break;
+
+	default:
+		break;
+	}
+
+	/* Now set the GPIO state, or nothing will happen at all */
+	if (led_state & LED_STATE_ENABLED) {
+		GPSR = hw_led_state;
+		GPCR = hw_led_state ^ LED_MASK;
+	}
+
+	local_irq_restore(flags);
+}
diff --git a/arch/arm/mach-sa1100/leds-simpad.c b/arch/arm/mach-sa1100/leds-simpad.c
new file mode 100644
index 0000000..6a27a2d
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds-simpad.c
@@ -0,0 +1,101 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds-simpad.c
+ *
+ * Author: Juergen Messerer <juergen.messerer@siemens.ch>
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+#include <asm/arch/simpad.h>
+
+#include "leds.h"
+
+
+#define LED_STATE_ENABLED	1
+#define LED_STATE_CLAIMED	2
+
+static unsigned int led_state;
+static unsigned int hw_led_state;
+
+#define	LED_GREEN	(1)
+#define	LED_MASK	(1)
+
+extern void set_cs3_bit(int value);
+extern void clear_cs3_bit(int value);     
+
+void simpad_leds_event(led_event_t evt)
+{
+	switch (evt)
+	{
+	case led_start:
+	        hw_led_state = LED_GREEN;
+		led_state = LED_STATE_ENABLED;
+		break;
+
+	case led_stop:
+		led_state &= ~LED_STATE_ENABLED;
+		break;
+
+	case led_claim:
+		led_state |= LED_STATE_CLAIMED;
+		hw_led_state = LED_GREEN;
+		break;
+
+	case led_release:
+		led_state &= ~LED_STATE_CLAIMED;
+		hw_led_state = LED_GREEN;
+		break;
+
+#ifdef CONFIG_LEDS_TIMER
+	case led_timer:
+		if (!(led_state & LED_STATE_CLAIMED))
+			hw_led_state ^= LED_GREEN;
+		break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+	case led_idle_start:
+		break;
+
+	case led_idle_end:
+		break;
+#endif
+
+	case led_halted:
+		break;
+
+	case led_green_on:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state |= LED_GREEN;
+		break;
+
+	case led_green_off:
+		if (led_state & LED_STATE_CLAIMED)
+			hw_led_state &= ~LED_GREEN;
+		break;
+
+	case led_amber_on:
+		break;
+
+	case led_amber_off:
+		break;
+
+	case led_red_on:
+		break;
+
+	case led_red_off:
+		break;
+
+	default:
+		break;
+	}
+
+	if  (led_state & LED_STATE_ENABLED)
+		set_cs3_bit(LED2_ON);
+	else 
+	        clear_cs3_bit(LED2_ON);
+}
+
diff --git a/arch/arm/mach-sa1100/leds.c b/arch/arm/mach-sa1100/leds.c
new file mode 100644
index 0000000..4cf7c56
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds.c
@@ -0,0 +1,52 @@
+/*
+ * linux/arch/arm/mach-sa1100/leds.c
+ *
+ * SA1100 LEDs dispatcher
+ * 
+ * Copyright (C) 2001 Nicolas Pitre
+ */
+#include <linux/compiler.h>
+#include <linux/init.h>
+
+#include <asm/leds.h>
+#include <asm/mach-types.h>
+
+#include "leds.h"
+
+static int __init
+sa1100_leds_init(void)
+{
+	if (machine_is_assabet())
+		leds_event = assabet_leds_event;
+ 	if (machine_is_consus())
+ 	        leds_event = consus_leds_event;
+	if (machine_is_badge4())
+	  	leds_event = badge4_leds_event;
+	if (machine_is_brutus())
+		leds_event = brutus_leds_event;
+	if (machine_is_cerf())
+		leds_event = cerf_leds_event;
+	if (machine_is_flexanet())
+		leds_event = flexanet_leds_event;
+	if (machine_is_graphicsclient())
+		leds_event = graphicsclient_leds_event;
+	if (machine_is_hackkit())
+		leds_event = hackkit_leds_event;
+	if (machine_is_lart())
+		leds_event = lart_leds_event;
+	if (machine_is_pfs168())
+		leds_event = pfs168_leds_event;
+	if (machine_is_graphicsmaster())
+		leds_event = graphicsmaster_leds_event;
+	if (machine_is_adsbitsy())
+		leds_event = adsbitsy_leds_event;
+	if (machine_is_pt_system3())
+		leds_event = system3_leds_event;
+	if (machine_is_simpad())
+		leds_event = simpad_leds_event; /* what about machine registry? including led, apm... -zecke */
+
+	leds_event(led_start);
+	return 0;
+}
+
+core_initcall(sa1100_leds_init);
diff --git a/arch/arm/mach-sa1100/leds.h b/arch/arm/mach-sa1100/leds.h
new file mode 100644
index 0000000..68cc9f7
--- /dev/null
+++ b/arch/arm/mach-sa1100/leds.h
@@ -0,0 +1,14 @@
+extern void assabet_leds_event(led_event_t evt);
+extern void badge4_leds_event(led_event_t evt);
+extern void consus_leds_event(led_event_t evt);
+extern void brutus_leds_event(led_event_t evt);
+extern void cerf_leds_event(led_event_t evt);
+extern void flexanet_leds_event(led_event_t evt);
+extern void graphicsclient_leds_event(led_event_t evt);
+extern void hackkit_leds_event(led_event_t evt);
+extern void lart_leds_event(led_event_t evt);
+extern void pfs168_leds_event(led_event_t evt);
+extern void graphicsmaster_leds_event(led_event_t evt);
+extern void adsbitsy_leds_event(led_event_t evt);
+extern void system3_leds_event(led_event_t evt);
+extern void simpad_leds_event(led_event_t evt);
diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c
new file mode 100644
index 0000000..1405383
--- /dev/null
+++ b/arch/arm/mach-sa1100/neponset.c
@@ -0,0 +1,342 @@
+/*
+ * linux/arch/arm/mach-sa1100/neponset.c
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/serial_sa1100.h>
+#include <asm/arch/assabet.h>
+#include <asm/arch/neponset.h>
+#include <asm/hardware/sa1111.h>
+#include <asm/sizes.h>
+
+/*
+ * Install handler for Neponset IRQ.  Note that we have to loop here
+ * since the ETHERNET and USAR IRQs are level based, and we need to
+ * ensure that the IRQ signal is deasserted before returning.  This
+ * is rather unfortunate.
+ */
+static void
+neponset_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
+{
+	unsigned int irr;
+
+	while (1) {
+		struct irqdesc *d;
+
+		/*
+		 * Acknowledge the parent IRQ.
+		 */
+		desc->chip->ack(irq);
+
+		/*
+		 * Read the interrupt reason register.  Let's have all
+		 * active IRQ bits high.  Note: there is a typo in the
+		 * Neponset user's guide for the SA1111 IRR level.
+		 */
+		irr = IRR ^ (IRR_ETHERNET | IRR_USAR);
+
+		if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0)
+			break;
+
+		/*
+		 * Since there is no individual mask, we have to
+		 * mask the parent IRQ.  This is safe, since we'll
+		 * recheck the register for any pending IRQs.
+		 */
+		if (irr & (IRR_ETHERNET | IRR_USAR)) {
+			desc->chip->mask(irq);
+
+			if (irr & IRR_ETHERNET) {
+				d = irq_desc + IRQ_NEPONSET_SMC9196;
+				d->handle(IRQ_NEPONSET_SMC9196, d, regs);
+			}
+
+			if (irr & IRR_USAR) {
+				d = irq_desc + IRQ_NEPONSET_USAR;
+				d->handle(IRQ_NEPONSET_USAR, d, regs);
+			}
+
+			desc->chip->unmask(irq);
+		}
+
+		if (irr & IRR_SA1111) {
+			d = irq_desc + IRQ_NEPONSET_SA1111;
+			d->handle(IRQ_NEPONSET_SA1111, d, regs);
+		}
+	}
+}
+
+static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+	u_int mdm_ctl0 = MDM_CTL_0;
+
+	if (port->mapbase == _Ser1UTCR0) {
+		if (mctrl & TIOCM_RTS)
+			mdm_ctl0 &= ~MDM_CTL0_RTS2;
+		else
+			mdm_ctl0 |= MDM_CTL0_RTS2;
+
+		if (mctrl & TIOCM_DTR)
+			mdm_ctl0 &= ~MDM_CTL0_DTR2;
+		else
+			mdm_ctl0 |= MDM_CTL0_DTR2;
+	} else if (port->mapbase == _Ser3UTCR0) {
+		if (mctrl & TIOCM_RTS)
+			mdm_ctl0 &= ~MDM_CTL0_RTS1;
+		else
+			mdm_ctl0 |= MDM_CTL0_RTS1;
+
+		if (mctrl & TIOCM_DTR)
+			mdm_ctl0 &= ~MDM_CTL0_DTR1;
+		else
+			mdm_ctl0 |= MDM_CTL0_DTR1;
+	}
+
+	MDM_CTL_0 = mdm_ctl0;
+}
+
+static u_int neponset_get_mctrl(struct uart_port *port)
+{
+	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
+	u_int mdm_ctl1 = MDM_CTL_1;
+
+	if (port->mapbase == _Ser1UTCR0) {
+		if (mdm_ctl1 & MDM_CTL1_DCD2)
+			ret &= ~TIOCM_CD;
+		if (mdm_ctl1 & MDM_CTL1_CTS2)
+			ret &= ~TIOCM_CTS;
+		if (mdm_ctl1 & MDM_CTL1_DSR2)
+			ret &= ~TIOCM_DSR;
+	} else if (port->mapbase == _Ser3UTCR0) {
+		if (mdm_ctl1 & MDM_CTL1_DCD1)
+			ret &= ~TIOCM_CD;
+		if (mdm_ctl1 & MDM_CTL1_CTS1)
+			ret &= ~TIOCM_CTS;
+		if (mdm_ctl1 & MDM_CTL1_DSR1)
+			ret &= ~TIOCM_DSR;
+	}
+
+	return ret;
+}
+
+static struct sa1100_port_fns neponset_port_fns __initdata = {
+	.set_mctrl	= neponset_set_mctrl,
+	.get_mctrl	= neponset_get_mctrl,
+};
+
+static int neponset_probe(struct device *dev)
+{
+	sa1100_register_uart_fns(&neponset_port_fns);
+
+	/*
+	 * Install handler for GPIO25.
+	 */
+	set_irq_type(IRQ_GPIO25, IRQT_RISING);
+	set_irq_chained_handler(IRQ_GPIO25, neponset_irq_handler);
+
+	/*
+	 * We would set IRQ_GPIO25 to be a wake-up IRQ, but
+	 * unfortunately something on the Neponset activates
+	 * this IRQ on sleep (ethernet?)
+	 */
+#if 0
+	enable_irq_wake(IRQ_GPIO25);
+#endif
+
+	/*
+	 * Setup other Neponset IRQs.  SA1111 will be done by the
+	 * generic SA1111 code.
+	 */
+	set_irq_handler(IRQ_NEPONSET_SMC9196, do_simple_IRQ);
+	set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE);
+	set_irq_handler(IRQ_NEPONSET_USAR, do_simple_IRQ);
+	set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE);
+
+	/*
+	 * Disable GPIO 0/1 drivers so the buttons work on the module.
+	 */
+	NCR_0 = NCR_GP01_OFF;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+/*
+ * LDM power management.
+ */
+static int neponset_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+	/*
+	 * Save state.
+	 */
+	if (level == SUSPEND_SAVE_STATE ||
+	    level == SUSPEND_DISABLE ||
+	    level == SUSPEND_POWER_DOWN) {
+		if (!dev->power.saved_state)
+			dev->power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL);
+		if (!dev->power.saved_state)
+			return -ENOMEM;
+
+		*(unsigned int *)dev->power.saved_state = NCR_0;
+	}
+
+	return 0;
+}
+
+static int neponset_resume(struct device *dev, u32 level)
+{
+	if (level == RESUME_RESTORE_STATE || level == RESUME_ENABLE) {
+		if (dev->power.saved_state) {
+			NCR_0 = *(unsigned int *)dev->power.saved_state;
+			kfree(dev->power.saved_state);
+			dev->power.saved_state = NULL;
+		}
+	}
+
+	return 0;
+}
+
+#else
+#define neponset_suspend NULL
+#define neponset_resume  NULL
+#endif
+
+static struct device_driver neponset_device_driver = {
+	.name		= "neponset",
+	.bus		= &platform_bus_type,
+	.probe		= neponset_probe,
+	.suspend	= neponset_suspend,
+	.resume		= neponset_resume,
+};
+
+static struct resource neponset_resources[] = {
+	[0] = {
+		.start	= 0x10000000,
+		.end	= 0x17ffffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device neponset_device = {
+	.name		= "neponset",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(neponset_resources),
+	.resource	= neponset_resources,
+};
+
+static struct resource sa1111_resources[] = {
+	[0] = {
+		.start	= 0x40000000,
+		.end	= 0x40001fff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_NEPONSET_SA1111,
+		.end	= IRQ_NEPONSET_SA1111,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static u64 sa1111_dmamask = 0xffffffffUL;
+
+static struct platform_device sa1111_device = {
+	.name		= "sa1111",
+	.id		= 0,
+	.dev		= {
+		.dma_mask = &sa1111_dmamask,
+		.coherent_dma_mask = 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(sa1111_resources),
+	.resource	= sa1111_resources,
+};
+
+static struct resource smc91x_resources[] = {
+	[0] = {
+		.name	= "smc91x-regs",
+		.start	= SA1100_CS3_PHYS,
+		.end	= SA1100_CS3_PHYS + 0x01ffffff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_NEPONSET_SMC9196,
+		.end	= IRQ_NEPONSET_SMC9196,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.name	= "smc91x-attrib",
+		.start	= SA1100_CS3_PHYS + 0x02000000,
+		.end	= SA1100_CS3_PHYS + 0x03ffffff,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device smc91x_device = {
+	.name		= "smc91x",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(smc91x_resources),
+	.resource	= smc91x_resources,
+};
+
+static struct platform_device *devices[] __initdata = {
+	&neponset_device,
+	&sa1111_device,
+	&smc91x_device,
+};
+
+static int __init neponset_init(void)
+{
+	driver_register(&neponset_device_driver);
+
+	/*
+	 * The Neponset is only present on the Assabet machine type.
+	 */
+	if (!machine_is_assabet())
+		return -ENODEV;
+
+	/*
+	 * Ensure that the memory bus request/grant signals are setup,
+	 * and the grant is held in its inactive state, whether or not
+	 * we actually have a Neponset attached.
+	 */
+	sa1110_mb_disable();
+
+	if (!machine_has_neponset()) {
+		printk(KERN_DEBUG "Neponset expansion board not present\n");
+		return -ENODEV;
+	}
+
+	if (WHOAMI != 0x11) {
+		printk(KERN_WARNING "Neponset board detected, but "
+			"wrong ID: %02x\n", WHOAMI);
+		return -ENODEV;
+	}
+
+	return platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
+subsys_initcall(neponset_init);
+
+static struct map_desc neponset_io_desc[] __initdata = {
+ /* virtual     physical    length type */
+  { 0xf3000000, 0x10000000, SZ_1M, MT_DEVICE }, /* System Registers */
+  { 0xf4000000, 0x40000000, SZ_1M, MT_DEVICE }  /* SA-1111 */
+};
+
+void __init neponset_map_io(void)
+{
+	iotable_init(neponset_io_desc, ARRAY_SIZE(neponset_io_desc));
+}
diff --git a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c
new file mode 100644
index 0000000..5606bd7
--- /dev/null
+++ b/arch/arm/mach-sa1100/pleb.c
@@ -0,0 +1,154 @@
+/*
+ * linux/arch/arm/mach-sa1100/pleb.c
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/serial_sa1100.h>
+#include <asm/arch/irqs.h>
+
+#include "generic.h"
+
+
+/*
+ * Ethernet IRQ mappings
+ */
+
+#define PLEB_ETH0_P		(0x20000300)	/* Ethernet 0 in PCMCIA0 IO */
+#define PLEB_ETH0_V		(0xf6000300)
+
+#define GPIO_ETH0_IRQ		GPIO_GPIO(21)
+#define GPIO_ETH0_EN		GPIO_GPIO(26)
+
+#define IRQ_GPIO_ETH0_IRQ	IRQ_GPIO21
+
+static struct resource smc91x_resources[] = {
+	[0] = {
+		.start	=  PLEB_ETH0_P,
+		.end	=  PLEB_ETH0_P | 0x03ffffff,
+		.flags	= IORESOURCE_MEM,
+	},
+#if 0 /* Autoprobe instead, to get rising/falling edge characteristic right */
+	[1] = {
+		.start	= IRQ_GPIO_ETH0_IRQ,
+		.end	= IRQ_GPIO_ETH0_IRQ,
+		.flags	= IORESOURCE_IRQ,
+	},
+#endif
+};
+
+
+static struct platform_device smc91x_device = {
+	.name		= "smc91x",
+	.id		= 0,
+	.num_resources	= ARRAY_SIZE(smc91x_resources),
+	.resource	= smc91x_resources,
+};
+
+static struct platform_device *devices[] __initdata = {
+	&smc91x_device,
+};
+
+
+/*
+ * Pleb's memory map
+ * has flash memory (typically 4 or 8 meg) selected by
+ * the two SA1100 lowest chip select outputs.
+ */
+static struct resource pleb_flash_resources[] = {
+	[0] = {
+		.start = SA1100_CS0_PHYS,
+		.end   = SA1100_CS0_PHYS + SZ_8M - 1,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start = SA1100_CS1_PHYS,
+		.end   = SA1100_CS1_PHYS + SZ_8M - 1,
+		.flags = IORESOURCE_MEM,
+	}
+};
+
+
+static struct mtd_partition pleb_partitions[] = {
+	{
+		.name		= "blob",
+		.offset 	= 0,
+		.size		= 0x00020000,
+	}, {
+		.name		= "kernel",
+		.offset 	= MTDPART_OFS_APPEND,
+		.size		= 0x000e0000,
+	}, {
+		.name		= "rootfs",
+		.offset 	= MTDPART_OFS_APPEND,
+		.size		= 0x00300000,
+	}
+};
+
+
+static struct flash_platform_data pleb_flash_data = {
+	.map_name = "cfi_probe",
+	.parts = pleb_partitions,
+	.nr_parts = ARRAY_SIZE(pleb_partitions),
+};
+
+
+static void __init pleb_init(void)
+{
+	sa11x0_set_flash_data(&pleb_flash_data, pleb_flash_resources,
+			      ARRAY_SIZE(pleb_flash_resources));
+
+
+	platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
+
+static void __init pleb_map_io(void)
+{
+	sa1100_map_io();
+
+	sa1100_register_uart(0, 3);
+        sa1100_register_uart(1, 1);
+
+        GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
+        GPDR |= GPIO_UART_TXD;
+        GPDR &= ~GPIO_UART_RXD;
+        PPAR |= PPAR_UPR;
+
+	/*
+	 * Fix expansion memory timing for network card
+	 */
+	MECR = ((2<<10) | (2<<5) | (2<<0));
+
+	/*
+	 * Enable the SMC ethernet controller
+	 */
+	GPDR |= GPIO_ETH0_EN;	/* set to output */
+	GPCR  = GPIO_ETH0_EN;	/* clear MCLK (enable smc) */
+
+	GPDR &= ~GPIO_ETH0_IRQ;
+
+	set_irq_type(GPIO_ETH0_IRQ, IRQT_FALLING);
+}
+
+MACHINE_START(PLEB, "PLEB")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	MAPIO(pleb_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine   = pleb_init,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c
new file mode 100644
index 0000000..379ea5e
--- /dev/null
+++ b/arch/arm/mach-sa1100/pm.c
@@ -0,0 +1,167 @@
+/*
+ * SA1100 Power Management Routines
+ *
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * History:
+ *
+ * 2001-02-06:	Cliff Brake         Initial code
+ *
+ * 2001-02-25:	Sukjae Cho <sjcho@east.isi.edu> &
+ * 		Chester Kuo <chester@linux.org.tw>
+ * 			Save more value for the resume function! Support
+ * 			Bitsy/Assabet/Freebird board
+ *
+ * 2001-08-29:	Nicolas Pitre <nico@cam.org>
+ * 			Cleaned up, pushed platform dependent stuff
+ * 			in the platform specific files.
+ *
+ * 2002-05-27:	Nicolas Pitre	Killed sleep.h and the kmalloced save array.
+ * 				Storage is local on the stack now.
+ */
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+
+#include <asm/hardware.h>
+#include <asm/memory.h>
+#include <asm/system.h>
+#include <asm/mach/time.h>
+
+extern void sa1100_cpu_suspend(void);
+extern void sa1100_cpu_resume(void);
+
+#define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
+#define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x]
+
+/*
+ * List of global SA11x0 peripheral registers to preserve.
+ * More ones like CP and general purpose register values are preserved
+ * on the stack and then the stack pointer is stored last in sleep.S.
+ */
+enum {	SLEEP_SAVE_SP = 0,
+
+	SLEEP_SAVE_GPDR, SLEEP_SAVE_GAFR,
+	SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
+
+	SLEEP_SAVE_Ser1SDCR0,
+
+	SLEEP_SAVE_SIZE
+};
+
+
+static int sa11x0_pm_enter(suspend_state_t state)
+{
+	unsigned long gpio, sleep_save[SLEEP_SAVE_SIZE];
+	struct timespec delta, rtc;
+
+	if (state != PM_SUSPEND_MEM)
+		return -EINVAL;
+
+	/* preserve current time */
+	rtc.tv_sec = RCNR;
+	rtc.tv_nsec = 0;
+	save_time_delta(&delta, &rtc);
+	gpio = GPLR;
+
+	/* save vital registers */
+	SAVE(GPDR);
+	SAVE(GAFR);
+
+	SAVE(PPDR);
+	SAVE(PPSR);
+	SAVE(PPAR);
+	SAVE(PSDR);
+
+	SAVE(Ser1SDCR0);
+
+	/* Clear previous reset status */
+	RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
+
+	/* set resume return address */
+	PSPR = virt_to_phys(sa1100_cpu_resume);
+
+	/* go zzz */
+	sa1100_cpu_suspend();
+
+	/*
+	 * Ensure not to come back here if it wasn't intended
+	 */
+	PSPR = 0;
+
+	/*
+	 * Ensure interrupt sources are disabled; we will re-init
+	 * the interrupt subsystem via the device manager.
+	 */
+	ICLR = 0;
+	ICCR = 1;
+	ICMR = 0;
+
+	/* restore registers */
+	RESTORE(GPDR);
+	RESTORE(GAFR);
+
+	RESTORE(PPDR);
+	RESTORE(PPSR);
+	RESTORE(PPAR);
+	RESTORE(PSDR);
+
+	RESTORE(Ser1SDCR0);
+
+	GPSR = gpio;
+	GPCR = ~gpio;
+
+	/*
+	 * Clear the peripheral sleep-hold bit.
+	 */
+	PSSR = PSSR_PH;
+
+	/* restore current time */
+	rtc.tv_sec = RCNR;
+	restore_time_delta(&delta, &rtc);
+
+	return 0;
+}
+
+unsigned long sleep_phys_sp(void *sp)
+{
+	return virt_to_phys(sp);
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int sa11x0_pm_prepare(suspend_state_t state)
+{
+	return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static int sa11x0_pm_finish(suspend_state_t state)
+{
+	return 0;
+}
+
+/*
+ * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
+ */
+static struct pm_ops sa11x0_pm_ops = {
+	.pm_disk_mode	= PM_DISK_FIRMWARE,
+	.prepare	= sa11x0_pm_prepare,
+	.enter		= sa11x0_pm_enter,
+	.finish		= sa11x0_pm_finish,
+};
+
+static int __init sa11x0_pm_init(void)
+{
+	pm_set_ops(&sa11x0_pm_ops);
+	return 0;
+}
+
+late_initcall(sa11x0_pm_init);
diff --git a/arch/arm/mach-sa1100/shannon.c b/arch/arm/mach-sa1100/shannon.c
new file mode 100644
index 0000000..edddd55
--- /dev/null
+++ b/arch/arm/mach-sa1100/shannon.c
@@ -0,0 +1,85 @@
+/*
+ * linux/arch/arm/mach-sa1100/shannon.c
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+#include <asm/arch/shannon.h>
+
+#include "generic.h"
+
+static struct mtd_partition shannon_partitions[] = {
+	{
+		.name		= "BLOB boot loader",
+		.offset		= 0,
+		.size		= 0x20000
+	},
+	{
+		.name		= "kernel",
+		.offset		= MTDPART_OFS_APPEND,
+		.size		= 0xe0000
+	},
+	{ 
+		.name		= "initrd",
+		.offset		= MTDPART_OFS_APPEND,	
+		.size		= MTDPART_SIZ_FULL
+	}
+};
+
+static struct flash_platform_data shannon_flash_data = {
+	.map_name	= "cfi_probe",
+	.parts		= shannon_partitions,
+	.nr_parts	= ARRAY_SIZE(shannon_partitions),
+};
+
+static struct resource shannon_flash_resource = {
+	.start		= SA1100_CS0_PHYS,
+	.end		= SA1100_CS0_PHYS + SZ_4M - 1,
+	.flags		= IORESOURCE_MEM,
+};
+
+static void __init shannon_init(void)
+{
+	sa11x0_set_flash_data(&shannon_flash_data, &shannon_flash_resource, 1);
+}
+
+static void __init shannon_map_io(void)
+{
+	sa1100_map_io();
+
+	sa1100_register_uart(0, 3);
+	sa1100_register_uart(1, 1);
+
+	Ser1SDCR0 |= SDCR0_SUS;
+	GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
+	GPDR |= GPIO_UART_TXD | SHANNON_GPIO_CODEC_RESET;
+	GPDR &= ~GPIO_UART_RXD;
+	PPAR |= PPAR_UPR;
+
+	/* reset the codec */
+	GPCR = SHANNON_GPIO_CODEC_RESET;
+	GPSR = SHANNON_GPIO_CODEC_RESET;
+}
+
+MACHINE_START(SHANNON, "Shannon (AKA: Tuxscreen)")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(shannon_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+	.init_machine	= shannon_init,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c
new file mode 100644
index 0000000..8d113d6
--- /dev/null
+++ b/arch/arm/mach-sa1100/simpad.c
@@ -0,0 +1,224 @@
+/*
+ * linux/arch/arm/mach-sa1100/simpad.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h> 
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/setup.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/map.h>
+#include <asm/mach/serial_sa1100.h>
+#include <asm/arch/simpad.h>
+
+#include <linux/serial_core.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#include "generic.h"
+
+long cs3_shadow;
+
+long get_cs3_shadow(void)
+{
+	return cs3_shadow;
+}
+
+void set_cs3(long value)
+{
+	*(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow = value;
+}
+
+void set_cs3_bit(int value)
+{
+	cs3_shadow |= value;
+	*(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
+}
+
+void clear_cs3_bit(int value)
+{
+	cs3_shadow &= ~value;
+	*(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
+}
+
+EXPORT_SYMBOL(set_cs3_bit);
+EXPORT_SYMBOL(clear_cs3_bit);
+
+static struct map_desc simpad_io_desc[] __initdata = {
+        /* virtual	physical    length	type */
+	/* MQ200 */
+	{ 0xf2800000, 0x4b800000, 0x00800000, MT_DEVICE },
+	/* Paules CS3, write only */
+	{ 0xf1000000, 0x18000000, 0x00100000, MT_DEVICE },
+};
+
+
+static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+{
+	if (port->mapbase == (u_int)&Ser1UTCR0) {
+		if (state)
+		{
+			clear_cs3_bit(RS232_ON);
+			clear_cs3_bit(DECT_POWER_ON);
+		}else
+		{
+			set_cs3_bit(RS232_ON);
+			set_cs3_bit(DECT_POWER_ON);
+		}
+	}
+}
+
+static struct sa1100_port_fns simpad_port_fns __initdata = {
+	.pm	   = simpad_uart_pm,
+};
+
+
+static struct mtd_partition simpad_partitions[] = {
+	{
+		.name       = "SIMpad boot firmware",
+		.size       = 0x00080000,
+		.offset     = 0,
+		.mask_flags = MTD_WRITEABLE,
+	}, {
+		.name       = "SIMpad kernel",
+		.size       = 0x0010000,
+		.offset     = MTDPART_OFS_APPEND,
+	}, {
+		.name       = "SIMpad root jffs2",
+		.size       = MTDPART_SIZ_FULL,
+		.offset     = MTDPART_OFS_APPEND,
+	}
+};
+
+static struct flash_platform_data simpad_flash_data = {
+	.map_name    = "cfi_probe",
+	.parts       = simpad_partitions,
+	.nr_parts    = ARRAY_SIZE(simpad_partitions),
+};
+
+
+static struct resource simpad_flash_resources [] = {
+	{
+		.start     = SA1100_CS0_PHYS,
+		.end       = SA1100_CS0_PHYS + SZ_16M -1,
+		.flags     = IORESOURCE_MEM,
+	}, {
+		.start     = SA1100_CS1_PHYS,
+		.end       = SA1100_CS1_PHYS + SZ_16M -1,
+		.flags     = IORESOURCE_MEM,
+	}
+};
+
+
+
+static void __init simpad_map_io(void)
+{
+	sa1100_map_io();
+
+	iotable_init(simpad_io_desc, ARRAY_SIZE(simpad_io_desc));
+
+	set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
+		      ENABLE_5V | RESET_SIMCARD | DECT_POWER_ON);
+
+
+        sa1100_register_uart_fns(&simpad_port_fns);
+	sa1100_register_uart(0, 3);  /* serial interface */
+	sa1100_register_uart(1, 1);  /* DECT             */
+
+	// Reassign UART 1 pins
+	GAFR |= GPIO_UART_TXD | GPIO_UART_RXD;
+	GPDR |= GPIO_UART_TXD | GPIO_LDD13 | GPIO_LDD15;
+	GPDR &= ~GPIO_UART_RXD;
+	PPAR |= PPAR_UPR;
+
+	/*
+	 * Set up registers for sleep mode.
+	 */
+
+
+	PWER = PWER_GPIO0| PWER_RTC;
+	PGSR = 0x818;
+	PCFR = 0;
+	PSDR = 0;
+
+	sa11x0_set_flash_data(&simpad_flash_data, simpad_flash_resources,
+			      ARRAY_SIZE(simpad_flash_resources));
+}
+
+static void simpad_power_off(void)
+{
+	local_irq_disable(); // was cli
+	set_cs3(0x800);        /* only SD_MEDIAQ */
+
+	/* disable internal oscillator, float CS lines */
+	PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS);
+	/* enable wake-up on GPIO0 (Assabet...) */
+	PWER = GFER = GRER = 1;
+	/*
+	 * set scratchpad to zero, just in case it is used as a
+	 * restart address by the bootloader.
+	 */
+	PSPR = 0;
+	PGSR = 0;
+	/* enter sleep mode */
+	PMCR = PMCR_SF;
+	while(1);
+
+	local_irq_enable(); /* we won't ever call it */
+
+
+}
+
+
+/*
+ * MediaQ Video Device
+ */
+static struct platform_device simpad_mq200fb = {
+	.name = "simpad-mq200",
+	.id   = 0,
+};
+
+static struct platform_device *devices[] __initdata = {
+	&simpad_mq200fb
+};
+
+
+
+static int __init simpad_init(void)
+{
+	int ret;
+
+	pm_power_off = simpad_power_off;
+
+	ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+	if(ret)
+		printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device");
+
+	return 0;
+}
+
+arch_initcall(simpad_init);
+
+
+MACHINE_START(SIMPAD, "Simpad")
+	MAINTAINER("Holger Freyther")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+        BOOT_PARAMS(0xc0000100)
+	MAPIO(simpad_map_io)
+	INITIRQ(sa1100_init_irq)
+	.timer		= &sa1100_timer,
+MACHINE_END
diff --git a/arch/arm/mach-sa1100/sleep.S b/arch/arm/mach-sa1100/sleep.S
new file mode 100644
index 0000000..2fa1e28
--- /dev/null
+++ b/arch/arm/mach-sa1100/sleep.S
@@ -0,0 +1,215 @@
+/*
+ * SA11x0 Assembler Sleep/WakeUp Management Routines
+ *
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * History:
+ *
+ * 2001-02-06: Cliff Brake         Initial code
+ *
+ * 2001-08-29:	Nicolas Pitre	Simplified.
+ *
+ * 2002-05-27:	Nicolas Pitre	Revisited, more cleanup and simplification.
+ *				Storage is on the stack now.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+
+
+		.text
+
+
+
+/*
+ * sa1100_cpu_suspend()
+ *
+ * Causes sa11x0 to enter sleep state
+ *
+ */
+
+ENTRY(sa1100_cpu_suspend)
+
+	stmfd	sp!, {r4 - r12, lr}		@ save registers on stack
+
+	@ get coprocessor registers
+	mrc 	p15, 0, r4, c3, c0, 0		@ domain ID
+	mrc 	p15, 0, r5, c2, c0, 0		@ translation table base addr
+	mrc	p15, 0, r6, c13, c0, 0		@ PID
+	mrc 	p15, 0, r7, c1, c0, 0		@ control reg
+
+	@ store them plus current virtual stack ptr on stack
+	mov	r8, sp
+	stmfd	sp!, {r4 - r8}
+
+	@ preserve phys address of stack
+	mov	r0, sp
+	bl	sleep_phys_sp
+	ldr	r1, =sleep_save_sp
+	str	r0, [r1]
+
+	@ clean data cache and invalidate WB
+	bl	v4wb_flush_kern_cache_all
+
+	@ disable clock switching
+	mcr	p15, 0, r1, c15, c2, 2
+
+        @ Adjust memory timing before lowering CPU clock
+	@ Clock speed adjustment without changing memory timing makes
+	@ CPU hang in some cases
+        ldr     r0, =MDREFR
+        ldr     r1, [r0]
+        orr     r1, r1, #MDREFR_K1DB2
+        str     r1, [r0]
+
+	@ delay 90us and set CPU PLL to lowest speed
+	@ fixes resume problem on high speed SA1110
+	mov	r0, #90
+	bl	__udelay
+	ldr	r0, =PPCR
+	mov	r1, #0
+	str	r1, [r0]
+	mov	r0, #90
+	bl	__udelay
+
+	/*
+	 * SA1110 SDRAM controller workaround.  register values:
+	 *
+	 * r0  = &MSC0
+	 * r1  = &MSC1
+	 * r2  = &MSC2
+	 * r3  = MSC0 value
+	 * r4  = MSC1 value
+	 * r5  = MSC2 value
+	 * r6  = &MDREFR
+	 * r7  = first MDREFR value
+	 * r8  = second MDREFR value
+	 * r9  = &MDCNFG
+	 * r10 = MDCNFG value
+	 * r11 = third MDREFR value
+	 * r12 = &PMCR
+	 * r13 = PMCR value (1)
+	 */
+
+	ldr	r0, =MSC0
+	ldr	r1, =MSC1
+	ldr	r2, =MSC2
+
+        ldr     r3, [r0]
+        bic     r3, r3, #FMsk(MSC_RT)
+        bic     r3, r3, #FMsk(MSC_RT)<<16
+
+        ldr     r4, [r1]
+        bic     r4, r4, #FMsk(MSC_RT)
+        bic     r4, r4, #FMsk(MSC_RT)<<16
+
+        ldr     r5, [r2]
+        bic     r5, r5, #FMsk(MSC_RT)
+        bic     r5, r5, #FMsk(MSC_RT)<<16
+
+        ldr     r6, =MDREFR
+
+        ldr     r7, [r6]
+        bic     r7, r7, #0x0000FF00
+        bic     r7, r7, #0x000000F0
+        orr     r8, r7, #MDREFR_SLFRSH
+
+        ldr     r9, =MDCNFG
+        ldr     r10, [r9]
+        bic     r10, r10, #(MDCNFG_DE0+MDCNFG_DE1)
+        bic     r10, r10, #(MDCNFG_DE2+MDCNFG_DE3)
+
+        bic     r11, r8, #MDREFR_SLFRSH
+        bic     r11, r11, #MDREFR_E1PIN
+
+        ldr     r12, =PMCR
+
+        mov     r13, #PMCR_SF
+
+	b	sa1110_sdram_controller_fix
+
+	.align 5
+sa1110_sdram_controller_fix:
+
+	@ Step 1 clear RT field of all MSCx registers
+	str 	r3, [r0]
+	str	r4, [r1]
+	str	r5, [r2]
+
+	@ Step 2 clear DRI field in MDREFR
+	str	r7, [r6]
+
+	@ Step 3 set SLFRSH bit in MDREFR
+	str	r8, [r6]
+
+	@ Step 4 clear DE bis in MDCNFG
+	str	r10, [r9]
+
+	@ Step 5 clear DRAM refresh control register
+	str	r11, [r6]
+
+	@ Wow, now the hardware suspend request pins can be used, that makes them functional for
+	@ about 7 ns out of the	entire time that the CPU is running!
+
+	@ Step 6 set force sleep bit in PMCR
+
+	str	r13, [r12]
+
+20:	b	20b			@ loop waiting for sleep
+
+/*
+ * cpu_sa1100_resume()
+ *
+ * entry point from bootloader into kernel during resume
+ *
+ * Note: Yes, part of the following code is located into the .data section.
+ *       This is to allow sleep_save_sp to be accessed with a relative load
+ *       while we can't rely on any MMU translation.  We could have put
+ *       sleep_save_sp in the .text section as well, but some setups might
+ *       insist on it to be truly read-only.
+ */
+
+	.data
+	.align 5
+ENTRY(sa1100_cpu_resume)
+	mov	r0, #PSR_F_BIT | PSR_I_BIT | MODE_SVC
+	msr	cpsr_c, r0			@ set SVC, irqs off
+
+	ldr	r0, sleep_save_sp		@ stack phys addr
+	ldr	r2, =resume_after_mmu		@ its absolute virtual address
+	ldmfd	r0, {r4 - r7, sp}		@ CP regs + virt stack ptr
+
+	mov	r1, #0
+	mcr	p15, 0, r1, c8, c7, 0   	@ flush I+D TLBs
+	mcr	p15, 0, r1, c7, c7, 0		@ flush I&D cache
+	mcr	p15, 0, r1, c9, c0, 0		@ invalidate RB
+	mcr     p15, 0, r1, c9, c0, 5		@ allow user space to use RB
+
+	mcr 	p15, 0, r4, c3, c0, 0		@ domain ID
+	mcr 	p15, 0, r5, c2, c0, 0		@ translation table base addr
+	mcr	p15, 0, r6, c13, c0, 0		@ PID
+	b	resume_turn_on_mmu		@ cache align execution
+
+	.align 5
+resume_turn_on_mmu:
+	mcr 	p15, 0, r7, c1, c0, 0		@ turn on MMU, caches, etc.
+	nop
+	mov	pc, r2				@ jump to virtual addr
+	nop
+	nop
+	nop
+
+sleep_save_sp:
+	.word	0				@ preserve stack phys ptr here
+
+	.text
+resume_after_mmu:
+	mcr     p15, 0, r1, c15, c1, 2		@ enable clock switching
+	ldmfd	sp!, {r4 - r12, pc}		@ return to caller
+
+
diff --git a/arch/arm/mach-sa1100/ssp.c b/arch/arm/mach-sa1100/ssp.c
new file mode 100644
index 0000000..1604dad
--- /dev/null
+++ b/arch/arm/mach-sa1100/ssp.c
@@ -0,0 +1,214 @@
+/*
+ *  linux/arch/arm/mach-sa1100/ssp.c
+ *
+ *  Copyright (C) 2003 Russell King.
+ *
+ * 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.
+ *
+ *  Generic SSP driver.  This provides the generic core for simple
+ *  IO-based SSP applications.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/hardware/ssp.h>
+
+static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int status = Ser4SSSR;
+
+	if (status & SSSR_ROR) {
+		printk(KERN_WARNING "SSP: receiver overrun\n");
+	}
+
+	Ser4SSSR = SSSR_ROR;
+
+	return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/**
+ * ssp_write_word - write a word to the SSP port
+ * @data: 16-bit, MSB justified data to write.
+ *
+ * Wait for a free entry in the SSP transmit FIFO, and write a data
+ * word to the SSP port.  Wait for the SSP port to start sending
+ * the data.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ *   %-ETIMEDOUT	timeout occurred (for future)
+ *   0			success
+ */
+int ssp_write_word(u16 data)
+{
+	while (!(Ser4SSSR & SSSR_TNF))
+		cpu_relax();
+
+	Ser4SSDR = data;
+
+	while (!(Ser4SSSR & SSSR_BSY))
+		cpu_relax();
+
+	return 0;
+}
+
+/**
+ * ssp_read_word - read a word from the SSP port
+ *
+ * Wait for a data word in the SSP receive FIFO, and return the
+ * received data.  Data is LSB justified.
+ *
+ * Note: Currently, if data is not expected to be received, this
+ * function will wait for ever.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ *   %-ETIMEDOUT	timeout occurred (for future)
+ *   16-bit data	success
+ */
+int ssp_read_word(void)
+{
+	while (!(Ser4SSSR & SSSR_RNE))
+		cpu_relax();
+
+	return Ser4SSDR;
+}
+
+/**
+ * ssp_flush - flush the transmit and receive FIFOs
+ *
+ * Wait for the SSP to idle, and ensure that the receive FIFO
+ * is empty.
+ *
+ * The caller is expected to perform the necessary locking.
+ */
+void ssp_flush(void)
+{
+	do {
+		while (Ser4SSSR & SSSR_RNE) {
+			(void) Ser4SSDR;
+		}
+	} while (Ser4SSSR & SSSR_BSY);
+}
+
+/**
+ * ssp_enable - enable the SSP port
+ *
+ * Turn on the SSP port.
+ */
+void ssp_enable(void)
+{
+	Ser4SSCR0 |= SSCR0_SSE;
+}
+
+/**
+ * ssp_disable - shut down the SSP port
+ *
+ * Turn off the SSP port, optionally powering it down.
+ */
+void ssp_disable(void)
+{
+	Ser4SSCR0 &= ~SSCR0_SSE;
+}
+
+/**
+ * ssp_save_state - save the SSP configuration
+ * @ssp: pointer to structure to save SSP configuration
+ *
+ * Save the configured SSP state for suspend.
+ */
+void ssp_save_state(struct ssp_state *ssp)
+{
+	ssp->cr0 = Ser4SSCR0;
+	ssp->cr1 = Ser4SSCR1;
+
+	Ser4SSCR0 &= ~SSCR0_SSE;
+}
+
+/**
+ * ssp_restore_state - restore a previously saved SSP configuration
+ * @ssp: pointer to configuration saved by ssp_save_state
+ *
+ * Restore the SSP configuration saved previously by ssp_save_state.
+ */
+void ssp_restore_state(struct ssp_state *ssp)
+{
+	Ser4SSSR = SSSR_ROR;
+
+	Ser4SSCR0 = ssp->cr0 & ~SSCR0_SSE;
+	Ser4SSCR1 = ssp->cr1;
+	Ser4SSCR0 = ssp->cr0;
+}
+
+/**
+ * ssp_init - setup the SSP port
+ *
+ * initialise and claim resources for the SSP port.
+ *
+ * Returns:
+ *   %-ENODEV	if the SSP port is unavailable
+ *   %-EBUSY	if the resources are already in use
+ *   %0		on success
+ */
+int ssp_init(void)
+{
+	int ret;
+
+	if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE))
+		return -ENODEV;
+
+	if (!request_mem_region(__PREG(Ser4SSCR0), 0x18, "SSP")) {
+		return -EBUSY;
+	}
+
+	Ser4SSSR = SSSR_ROR;
+
+	ret = request_irq(IRQ_Ser4SSP, ssp_interrupt, 0, "SSP", NULL);
+	if (ret)
+		goto out_region;
+
+	return 0;
+
+ out_region:
+	release_mem_region(__PREG(Ser4SSCR0), 0x18);
+	return ret;
+}
+
+/**
+ * ssp_exit - undo the effects of ssp_init
+ *
+ * release and free resources for the SSP port.
+ */
+void ssp_exit(void)
+{
+	Ser4SSCR0 &= ~SSCR0_SSE;
+
+	free_irq(IRQ_Ser4SSP, NULL);
+	release_mem_region(__PREG(Ser4SSCR0), 0x18);
+}
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SA11x0 SSP PIO driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ssp_write_word);
+EXPORT_SYMBOL(ssp_read_word);
+EXPORT_SYMBOL(ssp_flush);
+EXPORT_SYMBOL(ssp_enable);
+EXPORT_SYMBOL(ssp_disable);
+EXPORT_SYMBOL(ssp_save_state);
+EXPORT_SYMBOL(ssp_restore_state);
+EXPORT_SYMBOL(ssp_init);
+EXPORT_SYMBOL(ssp_exit);
diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c
new file mode 100644
index 0000000..19b0c0f
--- /dev/null
+++ b/arch/arm/mach-sa1100/time.c
@@ -0,0 +1,159 @@
+/*
+ * linux/arch/arm/mach-sa1100/time.c
+ *
+ * Copyright (C) 1998 Deborah Wallach.
+ * Twiddles  (C) 1999 	Hugo Fiennes <hugo@empeg.com>
+ * 
+ * 2000/03/29 (C) Nicolas Pitre <nico@cam.org>
+ *	Rewritten: big cleanup, much simpler, better HZ accuracy.
+ *
+ */
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/signal.h>
+
+#include <asm/mach/time.h>
+#include <asm/hardware.h>
+
+#define RTC_DEF_DIVIDER		(32768 - 1)
+#define RTC_DEF_TRIM            0
+
+static unsigned long __init sa1100_get_rtc_time(void)
+{
+	/*
+	 * According to the manual we should be able to let RTTR be zero
+	 * and then a default diviser for a 32.768KHz clock is used.
+	 * Apparently this doesn't work, at least for my SA1110 rev 5.
+	 * If the clock divider is uninitialized then reset it to the
+	 * default value to get the 1Hz clock.
+	 */
+	if (RTTR == 0) {
+		RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
+		printk(KERN_WARNING "Warning: uninitialized Real Time Clock\n");
+		/* The current RTC value probably doesn't make sense either */
+		RCNR = 0;
+		return 0;
+	}
+	return RCNR;
+}
+
+static int sa1100_set_rtc(void)
+{
+	unsigned long current_time = xtime.tv_sec;
+
+	if (RTSR & RTSR_ALE) {
+		/* make sure not to forward the clock over an alarm */
+		unsigned long alarm = RTAR;
+		if (current_time >= alarm && alarm >= RCNR)
+			return -ERESTARTSYS;
+	}
+	RCNR = current_time;
+	return 0;
+}
+
+/* IRQs are disabled before entering here from do_gettimeofday() */
+static unsigned long sa1100_gettimeoffset (void)
+{
+	unsigned long ticks_to_match, elapsed, usec;
+
+	/* Get ticks before next timer match */
+	ticks_to_match = OSMR0 - OSCR;
+
+	/* We need elapsed ticks since last match */
+	elapsed = LATCH - ticks_to_match;
+
+	/* Now convert them to usec */
+	usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;
+
+	return usec;
+}
+
+/*
+ * We will be entered with IRQs enabled.
+ *
+ * Loop until we get ahead of the free running timer.
+ * This ensures an exact clock tick count and time accuracy.
+ * IRQs are disabled inside the loop to ensure coherence between
+ * lost_ticks (updated in do_timer()) and the match reg value, so we
+ * can use do_gettimeofday() from interrupt handlers.
+ */
+static irqreturn_t
+sa1100_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int next_match;
+
+	write_seqlock(&xtime_lock);
+
+	do {
+		timer_tick(regs);
+		OSSR = OSSR_M0;  /* Clear match on timer 0 */
+		next_match = (OSMR0 += LATCH);
+	} while ((signed long)(next_match - OSCR) <= 0);
+
+	write_sequnlock(&xtime_lock);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction sa1100_timer_irq = {
+	.name		= "SA11xx Timer Tick",
+	.flags		= SA_INTERRUPT,
+	.handler	= sa1100_timer_interrupt
+};
+
+static void __init sa1100_timer_init(void)
+{
+	struct timespec tv;
+
+	set_rtc = sa1100_set_rtc;
+
+	tv.tv_nsec = 0;
+	tv.tv_sec = sa1100_get_rtc_time();
+	do_settimeofday(&tv);
+
+	OSMR0 = 0;		/* set initial match at 0 */
+	OSSR = 0xf;		/* clear status on all timers */
+	setup_irq(IRQ_OST0, &sa1100_timer_irq);
+	OIER |= OIER_E0;	/* enable match on timer 0 to cause interrupts */
+	OSCR = 0;		/* initialize free-running timer, force first match */
+}
+
+#ifdef CONFIG_PM
+unsigned long osmr[4], oier;
+
+static void sa1100_timer_suspend(void)
+{
+	osmr[0] = OSMR0;
+	osmr[1] = OSMR1;
+	osmr[2] = OSMR2;
+	osmr[3] = OSMR3;
+	oier = OIER;
+}
+
+static void sa1100_timer_resume(void)
+{
+	OSSR = 0x0f;
+	OSMR0 = osmr[0];
+	OSMR1 = osmr[1];
+	OSMR2 = osmr[2];
+	OSMR3 = osmr[3];
+	OIER = oier;
+
+	/*
+	 * OSMR0 is the system timer: make sure OSCR is sufficiently behind
+	 */
+	OSCR = OSMR0 - LATCH;
+}
+#else
+#define sa1100_timer_suspend NULL
+#define sa1100_timer_resume NULL
+#endif
+
+struct sys_timer sa1100_timer = {
+	.init		= sa1100_timer_init,
+	.suspend	= sa1100_timer_suspend,
+	.resume		= sa1100_timer_resume,
+	.offset		= sa1100_gettimeoffset,
+};