diff --git a/arch/arm/config.in b/arch/arm/config.in
index 1aae024f83f0b330bab9ce9cfac7b98bec11229a..635874b18bfc2aee98caed4bf12e3fc27d3e560c 100644
--- a/arch/arm/config.in
+++ b/arch/arm/config.in
@@ -335,6 +335,16 @@ hex 'Compressed ROM boot loader BSS address' CONFIG_ZBOOT_ROM_BSS 0
 if [ "$CONFIG_ARCH_SA1100" = "y" -o \
      "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
    dep_bool 'Support CPU clock change (EXPERIMENTAL)' CONFIG_CPU_FREQ $CONFIG_EXPERIMENTAL
+else
+   define_bool CONFIG_CPU_FREQ n
+fi
+
+if [ "$CONFIG_CPU_FREQ" = "y" ]; then
+   define_bool CONFIG_CPU_FREQ_24_API y
+   define_bool CONFIG_CPU_FREQ_26_API y
+else
+   define_bool CONFIG_CPU_FREQ_24_API n
+   define_bool CONFIG_CPU_FREQ_26_API n
 fi
 
 source drivers/pci/Config.in
diff --git a/arch/arm/kernel/compat.c b/arch/arm/kernel/compat.c
index 8fd9632d0c08cf601abb295de53dd949d741df32..7195add42e74cab407e4478743993246b04a62ee 100644
--- a/arch/arm/kernel/compat.c
+++ b/arch/arm/kernel/compat.c
@@ -95,13 +95,14 @@ static void __init build_tag_list(struct param_struct *params, void *taglist)
 {
 	struct tag *tag = taglist;
 
-	printk(KERN_DEBUG "Converting old-style param struct to taglist\n");
-
 	if (params->u1.s.page_size != PAGE_SIZE) {
 		printk(KERN_WARNING "Warning: bad configuration page, "
 		       "trying to continue\n");
 		return;
 	}
+
+	printk(KERN_DEBUG "Converting old-style param struct to taglist\n");
+
 #ifdef CONFIG_ARCH_NETWINDER
 	if (params->u1.s.nr_pages != 0x02000 &&
 	    params->u1.s.nr_pages != 0x04000 &&
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 73a253c6fe6ec10a156e5c16d24857ae087ab978..399d81bf412a36fa6b2a7d743d7c09e77104cc8f 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -569,7 +569,7 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
  *	On a shared IRQ the caller must ensure the interrupt is disabled
  *	on the card it drives before calling this function.
  *
- *	This function may be called from interrupt context.
+ *	This function must not be called from interrupt context.
  */
 void free_irq(unsigned int irq, void *dev_id)
 {
@@ -591,15 +591,19 @@ void free_irq(unsigned int irq, void *dev_id)
 
 	    	/* Found it - now free it */
 		*p = action->next;
-		kfree(action);
-		goto out;
+		break;
 	}
-	printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
+
+	if (!action) {
+		printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
 #ifdef CONFIG_DEBUG_ERRORS
-	__backtrace();
+		__backtrace();
 #endif
-out:
-	spin_unlock_irqrestore(&irq_controller_lock, flags);
+	} else {
+		synchronize_irq(irq);
+		kfree(action);
+	}
 }
 
 /* Start the interrupt probing.  Unlike other architectures,
diff --git a/arch/arm/mach-sa1100/adsbitsy.c b/arch/arm/mach-sa1100/adsbitsy.c
index b0c97a9b7b8e87aab0e5c06ea336cb93f119ff08..d5eb9b67744921cafee1f25842269a2833304cb2 100644
--- a/arch/arm/mach-sa1100/adsbitsy.c
+++ b/arch/arm/mach-sa1100/adsbitsy.c
@@ -28,7 +28,6 @@
 #include <asm/mach/serial_sa1100.h>
 
 #include "generic.h"
-#include "sa1111.h"
 
 static int __init adsbitsy_init(void)
 {
@@ -53,7 +52,7 @@ static int __init adsbitsy_init(void)
 	/*
 	 * Probe for SA1111.
 	 */
-	ret = sa1111_init(NULL, 0x18000000, IRQ_GPIO0);
+	ret = sa1111_init(0x18000000, IRQ_GPIO0);
 	if (ret < 0)
 		return ret;
 
diff --git a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c
index aace82ac1870b40e8c70a36a560ff01f17c22369..6136ee54c4730ca8ecd2ed628c9d33817daa39c3 100644
--- a/arch/arm/mach-sa1100/badge4.c
+++ b/arch/arm/mach-sa1100/badge4.c
@@ -32,7 +32,6 @@
 #include <asm/mach/serial_sa1100.h>
 
 #include "generic.h"
-#include "sa1111.h"
 
 static int __init badge4_sa1111_init(void)
 {
@@ -45,7 +44,7 @@ static int __init badge4_sa1111_init(void)
 	/*
 	 * Probe for SA1111.
 	 */
-	return sa1111_init(NULL, BADGE4_SA1111_BASE, BADGE4_IRQ_GPIO_SA1111);
+	return sa1111_init(BADGE4_SA1111_BASE, BADGE4_IRQ_GPIO_SA1111);
 }
 
 static int __init badge4_init(void)
diff --git a/arch/arm/mach-sa1100/cpu-sa1100.c b/arch/arm/mach-sa1100/cpu-sa1100.c
index e587cf543608119a0fea1a7c13af3e1861d23632..64295a0fbe562bc0cb3762c454cf436416f2d70c 100644
--- a/arch/arm/mach-sa1100/cpu-sa1100.c
+++ b/arch/arm/mach-sa1100/cpu-sa1100.c
@@ -90,9 +90,7 @@
 
 #include <asm/hardware.h>
 
-extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz);
-extern unsigned int sa11x0_validatespeed(unsigned int cpu, unsigned int khz);
-extern unsigned int sa11x0_getspeed(void);
+#include "generic.h"
 
 typedef struct {
 	int speed;
@@ -107,7 +105,7 @@ typedef struct {
 
 static sa1100_dram_regs_t sa1100_dram_settings[] =
 {
-	/* { mdcnfg, mdcas0, mdcas1, mdcas2 } */ /* clock frequency */
+	/* 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 */
@@ -127,28 +125,25 @@ static sa1100_dram_regs_t sa1100_dram_settings[] =
 	{ 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) {
+	while (settings->speed != 0) {
 		if(new_speed == settings->speed)
 			break;
 		
 		settings++;
 	}
 
-	if(settings->speed == 0) {
+	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) {
+	if (new_speed > current_speed) {
 		/* We're going FASTER, so first relax the memory
 		 * timings before changing the core frequency 
 		 */
@@ -181,60 +176,39 @@ static void sa1100_update_dram_timings(int current_speed, int new_speed)
 	}
 }
 
-
-
-
-static int sa1100_dram_notifier(struct notifier_block *nb, 
-				unsigned long val, void *data)
+static void sa1100_setspeed(struct cpufreq_policy *policy)
 {
-	struct cpufreq_freqs *ci = data;
-	
-	switch(val) {
-	case CPUFREQ_MINMAX:
-		cpufreq_updateminmax(data, sa1100_dram_settings->speed, -1);
-		break;
+	unsigned int cur = sa11x0_getspeed();
+	struct cpufreq_freqs freqs;
 
-	case CPUFREQ_PRECHANGE:
-		if(ci->new > ci->cur)
-			sa1100_update_dram_timings(ci->cur, ci->new);
-		break;
+	freqs.old = cur;
+	freqs.new = policy->max;
+	freqs.cpu = CPUFREQ_ALL_CPUS;
 
-	case CPUFREQ_POSTCHANGE:
-		if(ci->new < ci->cur)
-			sa1100_update_dram_timings(ci->cur, ci->new);
-		break;
-		
-	default:
-		printk(KERN_INFO "%s: ignoring unknown notifier type (%ld)\n",
-		       __FUNCTION__, val);
-	}
-
-	return 0;
-}
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
+	if (policy->max > cur)
+		sa1100_update_dram_timings(cur, policy->max);
 
+	PPCR = sa11x0_freq_to_ppcr(policy->max);
 
+	if (policy->max < cur)
+		sa1100_update_dram_timings(cur, policy->max);
 
-static struct notifier_block sa1100_dram_block = {
-	.notifier_call	= sa1100_dram_notifier,
-};
-
-
-static void sa1100_setspeed(unsigned int cpu, unsigned int khz)
-{
-	PPCR = sa11x0_freq_to_ppcr(khz);
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 }
 
-static struct cpufreq_freqs sa1100_freqs = {
-	.min		= 59000,
-	.max		= 287000,
+static struct cpufreq_policy sa1100_policy = {
+	.cpu		= 0,
+	.policy		= CPUFREQ_POLICY_POWERSAVE,
+	.max_cpu_freq	= 287000,
 };
 
 static struct cpufreq_driver sa1100_driver = {
-	.freq		= &sa1100_freqs,
-	.validate	= sa11x0_validatespeed,
-	.setspeed	= sa1100_setspeed,
-	.sync		= 1,
+	.verify		= sa11x0_verify_speed,
+	.setpolicy	= sa1100_setspeed,
+	.policy		= &sa1100_policy,
+	.cpu_min_freq	= 59000,
 };
 
 static int __init sa1100_dram_init(void)
@@ -242,11 +216,9 @@ static int __init sa1100_dram_init(void)
 	int ret = -ENODEV;
 
 	if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID) {
-		ret = cpufreq_register_notifier(&sa1100_dram_block);
-		if (ret)
-			return ret;
-
-		sa1100_freqs.cur = sa11x0_getspeed();
+		sa1100_driver.cpu_curr_freq[0] =
+		sa1100_policy.min =
+		sa1100_policy.max = sa11x0_getspeed();
 
 		ret = cpufreq_register(&sa1100_driver);
 	}
diff --git a/arch/arm/mach-sa1100/cpu-sa1110.c b/arch/arm/mach-sa1100/cpu-sa1110.c
index 0a9777f732d0892df175382f5d1db3b01cca7421..ee0b097fc2a839674737338cc1449a62b1897008 100644
--- a/arch/arm/mach-sa1100/cpu-sa1110.c
+++ b/arch/arm/mach-sa1100/cpu-sa1110.c
@@ -28,11 +28,9 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
-#undef DEBUG
+#include "generic.h"
 
-extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz);
-extern unsigned int sa11x0_validatespeed(unsigned int cpu, unsigned int khz);
-extern unsigned int sa11x0_getspeed(void);
+#undef DEBUG
 
 struct sdram_params {
 	u_char  rows;		/* bits				 */
@@ -214,15 +212,16 @@ sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram)
  * above, we can match for an exact frequency.  If we don't find
  * an exact match, we will to set the lowest frequency to be safe.
  */
-static void sa1110_setspeed(unsigned int cpu, unsigned int khz)
+static void sa1110_setspeed(struct cpufreq_policy *policy)
 {
 	struct sdram_params *sdram = &sdram_params;
+	struct cpufreq_freqs freqs;
 	struct sdram_info sd;
 	unsigned long flags;
 	unsigned int ppcr, unused;
 
-	ppcr = sa11x0_freq_to_ppcr(khz);
-	sdram_calculate_timing(&sd, khz, sdram);
+	ppcr = sa11x0_freq_to_ppcr(policy->max);
+	sdram_calculate_timing(&sd, policy->max, sdram);
 
 #if 0
 	/*
@@ -230,7 +229,7 @@ static void sa1110_setspeed(unsigned int cpu, unsigned int khz)
 	 * and errata, but they seem to work.  Need to get a storage
 	 * scope on to the SDRAM signals to work out why.
 	 */
-	if (khz < 147500) {
+	if (policy->max < 147500) {
 		sd.mdrefr |= MDREFR_K1DB2;
 		sd.mdcas[0] = 0xaaaaaa7f;
 	} else {
@@ -240,6 +239,13 @@ static void sa1110_setspeed(unsigned int cpu, unsigned int khz)
 	sd.mdcas[1] = 0xaaaaaaaa;
 	sd.mdcas[2] = 0xaaaaaaaa;
 #endif
+
+	freqs.old = sa11x0_getspeed();
+	freqs.new = policy->max;
+	freqs.cpu = CPUFREQ_ALL_CPUS;
+
+	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
@@ -257,7 +263,7 @@ static void sa1110_setspeed(unsigned int cpu, unsigned int khz)
 	 * the programming.
 	 */
 	local_irq_save(flags);
-	asm("mcr p15, 0, %0, c10, c4" : : "r" (0));
+	asm("mcr p15, 0, %0, c7, c10, 4" : : "r" (0));
 	udelay(10);
 	__asm__ __volatile__("
 		b	2f
@@ -282,19 +288,22 @@ static void sa1110_setspeed(unsigned int cpu, unsigned int khz)
 	/*
 	 * Now, return the SDRAM refresh back to normal.
 	 */
-	sdram_update_refresh(khz, sdram);
+	sdram_update_refresh(policy->max, sdram);
+
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 }
 
-static struct cpufreq_freqs sa1110_freqs = {
-	.min		= 59000,
-	.max		= 287000,
+static struct cpufreq_policy sa1110_policy = {
+	.cpu		= 0,
+	.policy		= CPUFREQ_POLICY_POWERSAVE,
+	.max_cpu_freq	= 287000,
 };
 
 static struct cpufreq_driver sa1110_driver = {
-	.freq		= &sa1110_freqs,
-	.validate	= sa11x0_validatespeed,
-	.setspeed	= sa1110_setspeed,
-	.sync		= 1,
+	.verify		= sa11x0_verify_speed,
+	.setpolicy	= sa1110_setspeed,
+	.policy		= &sa1110_policy,
+	.cpu_min_freq	= 59000,
 };
 
 static int __init sa1110_clk_init(void)
@@ -318,8 +327,11 @@ static int __init sa1110_clk_init(void)
 
 		memcpy(&sdram_params, sdram, sizeof(sdram_params));
 
-		sa1110_freqs.cur = sa11x0_getspeed();
-		sa1110_setspeed(0, sa1110_freqs.cur);
+		sa1110_driver.cpu_cur_freq[0] =
+		sa1110_policy.min =
+		sa1110_policy.max = sa11x0_getspeed();
+
+		sa1110_setspeed(&sa1110_policy);
 
 		return cpufreq_register(&sa1110_driver);
 	}
diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c
index ad06a06c84f520b27138c42096fb3325fabb0372..45eae6434f6849f8107b0283b58dc950fc8857e2 100644
--- a/arch/arm/mach-sa1100/generic.c
+++ b/arch/arm/mach-sa1100/generic.c
@@ -55,20 +55,26 @@ unsigned int sa11x0_freq_to_ppcr(unsigned int khz)
 
 	khz /= 100;
 
-	for (i = NR_FREQS - 1; i > 0; i--)
-		if (cclk_frequency_100khz[i] <= khz)
+	for (i = 0; i < ARRAY_SIZE(cclk_frequency_100khz); i--)
+		if (cclk_frequency_100khz[i] >= khz)
 			break;
 
 	return i;
 }
 
 /*
- * Validate the speed in khz.  If we can't generate the precise
- * frequency requested, round it down (to be on the safe side).
+ * Validate the policy.  We aren't able to do any fancy in-kernel
+ * scaling, so we force min=max, and set the policy to "performance".
+ * If we can't generate the precise frequency requested, round it up.
  */
-unsigned int sa11x0_validatespeed(unsigned int cpu, unsigned int khz)
+void sa11x0_verify_speed(struct cpufreq_policy *policy)
 {
-	return cclk_frequency_100khz[sa11x0_freq_to_ppcr(khz)] * 100;
+	if (policy->max > policy->max_cpu_freq)
+		policy->max = policy->max_cpu_freq;
+
+	policy->max = cclk_frequency_100khz[sa11x0_freq_to_ppcr(policy->max)] * 100;
+	policy->min = policy->max;
+	policy->policy = CPUFREQ_POLICY_POWERSAVE;
 }
 
 unsigned int sa11x0_getspeed(void)
diff --git a/arch/arm/mach-sa1100/generic.h b/arch/arm/mach-sa1100/generic.h
index abb28817b275017f22fdd3003e139dbe35eb3d26..6a27b9b7e715ab71ce349e54e6686b9b27b0ee45 100644
--- a/arch/arm/mach-sa1100/generic.h
+++ b/arch/arm/mach-sa1100/generic.h
@@ -17,3 +17,9 @@ 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 void sa11x0_verify_speed(struct cpufreq_policy *policy);
+extern unsigned int sa11x0_getspeed(void);
diff --git a/arch/arm/mach-sa1100/graphicsmaster.c b/arch/arm/mach-sa1100/graphicsmaster.c
index 9600b3e620b5c824b8dddbbd45218bb3dc8c08c7..d3eddf63205e00db84d6b2cf95fdcfd346d4e145 100644
--- a/arch/arm/mach-sa1100/graphicsmaster.c
+++ b/arch/arm/mach-sa1100/graphicsmaster.c
@@ -25,7 +25,6 @@
 #include <asm/mach/serial_sa1100.h>
 
 #include "generic.h"
-#include "sa1111.h"
 
 static int __init graphicsmaster_init(void)
 {
@@ -43,7 +42,7 @@ static int __init graphicsmaster_init(void)
 	/*
 	 * Probe for SA1111.
 	 */
-	ret = sa1111_init(NULL, 0x18000000, ADS_EXT_IRQ(0));
+	ret = sa1111_init(0x18000000, ADS_EXT_IRQ(0));
 	if (ret < 0)
 		return ret;
 
diff --git a/arch/arm/mach-sa1100/h3600.c b/arch/arm/mach-sa1100/h3600.c
index 0881cc01ad2551ef83fe9f18459e744dd78292b2..41e19e37d0c06f6cf22493808a8fc5d45c34f500 100644
--- a/arch/arm/mach-sa1100/h3600.c
+++ b/arch/arm/mach-sa1100/h3600.c
@@ -24,65 +24,171 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/sched.h>
+#include <linux/pm.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/map.h>
 #include <asm/mach/serial_sa1100.h>
-#include <linux/serial_core.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 void msleep(unsigned int msec)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout((msec * HZ + 999) / 1000);
+}
+
 /*
- * H3600 has extended, write-only memory-mapped GPIO's
- * H3100 has 1/2 extended, write-only GPIO and 1/2 on
- *	 regular GPIO lines.
- * H3800 has memory-mapped GPIO through ASIC1 & 2
+ * low-level UART features
  */
 
-#define H3600_EGPIO	(*(volatile unsigned int *)H3600_EGPIO_VIRT)
+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 unsigned int h3600_egpio;
+static u_int h3600_uart_get_mctrl(struct uart_port *port)
+{
+	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
 
-/************************* H3100 *************************/
+	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;
+	}
 
-#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)
+	return ret;
+}
 
-static void h3100_init_egpio( void )
+static void h3600_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
 {
-	GPDR |= H3100_DIRECT_EGPIO;
-	GPCR = H3100_DIRECT_EGPIO;   /* Initially all off */
+	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);
+	}
+}
 
-	/* Older bootldrs put GPIO2-9 in alternate mode on the
-	   assumption that they are used for video */
-	GAFR &= ~H3100_DIRECT_EGPIO;
+/*
+ * 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;
+}
 
-	h3600_egpio = EGPIO_H3600_RS232_ON;
-	H3600_EGPIO = h3600_egpio;
+static __inline__ void do_blank(int setp)
+{
+	if (ipaq_model_ops.blank_callback)
+		ipaq_model_ops.blank_callback(1-setp);
 }
 
-static void h3100_control_egpio( enum ipaq_egpio_type x, int 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_ON:
+	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;
@@ -120,66 +226,104 @@ static void h3100_control_egpio( enum ipaq_egpio_type x, int setp )
 		break;
 	}
 
-	local_irq_save(flags);
-	if ( setp ) {
-		h3600_egpio |= egpio;
-		GPSR = gpio;
-	} else {
-		h3600_egpio &= ~egpio;
-		GPCR = gpio;
-	}
-	H3600_EGPIO = h3600_egpio;
-	local_irq_restore(flags);
-
-	/*
-	if ( x != IPAQ_EGPIO_VPP_ON ) {
-		printk("%s: type=%d (%s) gpio=0x%x (0x%x) egpio=0x%x (0x%x) setp=%d\n",
-		       __FUNCTION__,
-		       x, egpio_names[x], GPLR, gpio, h3600_egpio, egpio, setp );
+	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 )
+static unsigned long h3100_read_egpio(void)
 {
-	return h3600_egpio;
+	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 = {
-	model	     : IPAQ_H3100,
-	generic_name : "3100",
-	initialize   : h3100_init_egpio,
-	control      : h3100_control_egpio,
-	read	     : h3100_read_egpio
+	.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)
 
-/************************* H3600 *************************/
-
-static void h3600_init_egpio( void )
+static void __init h3100_map_io(void)
 {
-	h3600_egpio = EGPIO_H3600_RS232_ON;
-	H3600_EGPIO = h3600_egpio;
+	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;
 }
 
-static void h3600_control_egpio( enum ipaq_egpio_type x, int setp )
+MACHINE_START(H3100, "Compaq iPAQ H3100")
+	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
+	BOOT_PARAMS(0xc0000100)
+	MAPIO(h3100_map_io)
+	INITIRQ(sa1100_init_irq)
+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_ON:
+	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;
+			 EGPIO_H3600_AUD_PWR_ON;
 		break;
 	case IPAQ_EGPIO_QMUTE:
 		egpio |= EGPIO_H3600_QMUTE;
@@ -210,288 +354,441 @@ static void h3600_control_egpio( enum ipaq_egpio_type x, int setp )
 		break;
 	}
 
-	local_irq_save(flags);
-	if ( setp )
-		h3600_egpio |= egpio;
-	else
-		h3600_egpio &= ~egpio;
-	H3600_EGPIO = h3600_egpio;
-	local_irq_restore(flags);
+	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 )
+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 = {
-	model	     : IPAQ_H3600,
-	generic_name : "3600",
-	initialize   : h3600_init_egpio,
-	control      : h3600_control_egpio,
-	read	     : h3600_read_egpio
+	.generic_name	= "3600",
+	.control	= h3600_control_egpio,
+	.read		= h3600_read_egpio,
+	.pm_callback	= h3600_pm_callback
 };
 
-/************************* H3800 *************************/
+static void __init h3600_map_io(void)
+{
+	h3xxx_map_io();
 
-#define ASIC1_OUTPUTS	 0x7fff   /* First 15 bits are used */
+	/* Initialize h3600-specific values here */
 
-static unsigned int h3800_asic1_gpio;
-static unsigned int h3800_asic2_gpio;
+	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;
 
-static void h3800_init_egpio(void)
-{
-	/* Set up ASIC #1 */
-	H3800_ASIC1_GPIO_Direction    = ASIC1_OUTPUTS;		  /* All outputs */
-	H3800_ASIC1_GPIO_Mask	      = ASIC1_OUTPUTS;		  /* No interrupts */
-	H3800_ASIC1_GPIO_SleepMask    = ASIC1_OUTPUTS;
-	H3800_ASIC1_GPIO_SleepDir     = ASIC1_OUTPUTS;
-	H3800_ASIC1_GPIO_SleepOut     = GPIO_H3800_ASIC1_EAR_ON_N;
-	H3800_ASIC1_GPIO_BattFaultDir = ASIC1_OUTPUTS;
-	H3800_ASIC1_GPIO_BattFaultOut = GPIO_H3800_ASIC1_EAR_ON_N;
+	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)
+MACHINE_END
 
-	h3800_asic1_gpio = GPIO_H3800_ASIC1_IR_ON_N   /* TODO: Check IR level */
-		| GPIO_H3800_ASIC1_RS232_ON
-		| GPIO_H3800_ASIC1_EAR_ON_N;
+#endif /* CONFIG_SA1100_H3600 */
 
-	H3800_ASIC1_GPIO_Out = h3800_asic1_gpio;
+#ifdef CONFIG_SA1100_H3800
 
-	/* Set up ASIC #2 */
-	H3800_ASIC2_GPIO_Direction = GPIO_H3800_ASIC2_PEN_IRQ
-		| GPIO_H3800_ASIC2_SD_DETECT
-		| GPIO_H3800_ASIC2_EAR_IN_N
-		| GPIO_H3800_ASIC2_USB_DETECT_N
-		| GPIO_H3800_ASIC2_SD_CON_SLT;
+#define SET_ASIC1(x) \
+   do {if (setp) { H3800_ASIC1_GPIO_OUT |= (x); } else { H3800_ASIC1_GPIO_OUT &= ~(x); }} while(0)
 
-	h3800_asic2_gpio = GPIO_H3800_ASIC2_IN_Y1_N | GPIO_H3800_ASIC2_IN_X1_N;
-	H3800_ASIC2_GPIO_Data	      = h3800_asic2_gpio;
-	H3800_ASIC2_GPIO_BattFaultOut = h3800_asic2_gpio;
+#define SET_ASIC2(x) \
+   do {if (setp) { H3800_ASIC2_GPIOPIOD |= (x); } else { H3800_ASIC2_GPIOPIOD &= ~(x); }} while(0)
 
-	/* TODO : Set sleep states & battery fault states */
+#define CLEAR_ASIC1(x) \
+   do {if (setp) { H3800_ASIC1_GPIO_OUT &= ~(x); } else { H3800_ASIC1_GPIO_OUT |= (x); }} while(0)
 
-	/* Clear VPP Enable */
-	H3800_ASIC1_FlashWP_VPP_ON = 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_control_egpio( enum ipaq_egpio_type x, int setp )
+static void h3800_video_lcd_enable(int setp)
 {
-	unsigned int set_asic1_egpio = 0;
-	unsigned int clear_asic1_egpio = 0;
-	unsigned long flags;
+	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_ON:
-		set_asic1_egpio |= GPIO_H3800_ASIC1_LCD_5V_ON
-			| GPIO_H3800_ASIC1_LCD_ON
-			| GPIO_H3800_ASIC1_LCD_PCI
-			| GPIO_H3800_ASIC1_VGH_ON
-			| GPIO_H3800_ASIC1_VGL_ON;
+	case IPAQ_EGPIO_LCD_POWER:
+		h3800_video_power_on(setp);
 		break;
-	case IPAQ_EGPIO_CODEC_NRESET:
+	case IPAQ_EGPIO_LCD_ENABLE:
+		h3800_video_lcd_enable(setp);
 		break;
+	case IPAQ_EGPIO_CODEC_NRESET:
 	case IPAQ_EGPIO_AUDIO_ON:
-		break;
 	case IPAQ_EGPIO_QMUTE:
+		printk(__FUNCTION__ ": error - should not be called\n");
 		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_egpio |= GPIO_H3800_ASIC1_IR_ON_N;	 /* TODO : This is backwards? */
+		CLEAR_ASIC1(GPIO1_IR_ON_N);
 		break;
 	case IPAQ_EGPIO_IR_FSEL:
 		break;
 	case IPAQ_EGPIO_RS232_ON:
-		set_asic1_egpio |= GPIO_H3800_ASIC1_RS232_ON;
+		SET_ASIC1(GPIO1_RS232_ON);
 		break;
 	case IPAQ_EGPIO_VPP_ON:
-		H3800_ASIC1_FlashWP_VPP_ON = setp;
+		H3800_ASIC2_FlashWP_VPP_ON = setp;
 		break;
 	}
+}
 
-	local_irq_save(flags);
-	if ( setp ) {
-		h3800_asic1_gpio |= set_asic1_egpio;
-		h3800_asic1_gpio &= ~clear_asic1_egpio;
-	}
-	else {
-		h3800_asic1_gpio &= ~set_asic1_egpio;
-		h3800_asic1_gpio |= clear_asic1_egpio;
-	}
-	H3800_ASIC1_GPIO_Out = h3800_asic1_gpio;
-	local_irq_restore(flags);
+static unsigned long h3800_read_egpio(void)
+{
+	return H3800_ASIC1_GPIO_OUT | (H3800_ASIC2_GPIOPIOD << 16);
 }
 
-static unsigned long h3800_read_egpio( void )
+/* 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)
 {
-	return h3800_asic1_gpio | (h3800_asic2_gpio << 16);
+	static u16 asic1_data;
+	static u16 asic2_data;
+	int result = 0;
+
+	printk(__FUNCTION__ " %d\n", 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(__FUNCTION__ ": unrecognized PM callback\n");
+		break;
+	}
+	return result;
 }
 
 static struct ipaq_model_ops h3800_model_ops __initdata = {
-	model	     : IPAQ_H3800,
-	generic_name : "3800",
-	initialize   : h3800_init_egpio,
-	control      : h3800_control_egpio,
-	read	     : h3800_read_egpio
+	.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 h3600_lcd_power(int on)
+static void h3800_IRQ_demux(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
 {
-	if (on)
-		set_h3600_egpio(IPAQ_EGPIO_LCD_ON);
-	else
-		clr_h3600_egpio(IPAQ_EGPIO_LCD_ON);
-}
+	int i;
 
+	if (0) printk(__FUNCTION__ ": interrupt received\n");
 
-struct ipaq_model_ops ipaq_model_ops;
-EXPORT_SYMBOL(ipaq_model_ops);
+	desc->chip->ack(irq);
 
-static int __init h3600_init_model_ops(void)
-{
-	if (machine_is_h3xxx()) {
-		sa1100fb_lcd_power = h3600_lcd_power;
-
-		if (machine_is_h3100()) {
-			ipaq_model_ops = h3100_model_ops;
-		} else if (machine_is_h3600()) {
-			ipaq_model_ops = h3600_model_ops;
-		} else if (machine_is_h3800()) {
-			ipaq_model_ops = h3800_model_ops;
-		}
-		init_h3600_egpio();
+	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(__FUNCTION__" KPIO 0x%08X\n", 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(__FUNCTION__" GPIO 0x%08X\n", 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);
 	}
-	return 0;
+
+	if (i >= MAX_ASIC_ISR_LOOPS)
+		printk(__FUNCTION__ ": interrupt processing overrun\n");
+
+	/* For level-based interrupts */
+	desc->chip->unmask(irq);
+
 }
 
-__initcall(h3600_init_model_ops);
+static struct irqaction h3800_irq = {
+	.name		= "h3800_asic",
+	.handler	= h3800_IRQ_demux,
+	.flags		= SA_INTERRUPT,
+};
+
+u32 kpio_int_shadow = 0;
 
-/*
- * low-level UART features
+
+/* 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 h3600_uart_set_mctrl(struct uart_port *port, u_int mctrl)
+static void h3800_mask_ack_kpio_irq(unsigned int irq)
 {
-	if (port->mapbase == _Ser3UTCR0) {
-		if (mctrl & TIOCM_RTS)
-			GPCR = GPIO_H3600_COM_RTS;
-		else
-			GPSR = GPIO_H3600_COM_RTS;
-	}
+	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 u_int h3600_uart_get_mctrl(struct uart_port *port)
+static void h3800_mask_kpio_irq(unsigned int irq)
 {
-	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
+	u32 mask = kpio_irq_mask[irq - H3800_KPIO_IRQ_START];
+	kpio_int_shadow &= ~mask;
+	H3800_ASIC2_KPIINTSTAT = kpio_int_shadow;
+}
 
-	if (port->mapbase == _Ser3UTCR0) {
-		int gplr = GPLR;
-		if (gplr & GPIO_H3600_COM_DCD)
-			ret &= ~TIOCM_CD;
-		if (gplr & GPIO_H3600_COM_CTS)
-			ret &= ~TIOCM_CTS;
-	}
+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;
+}
 
-	return ret;
+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 h3600_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+static void h3800_mask_gpio_irq(unsigned int irq)
 {
-	if (port->mapbase == _Ser2UTCR0) {
-		assign_h3600_egpio( IPAQ_EGPIO_IR_ON, !state );
-	} else if (port->mapbase == _Ser3UTCR0) {
-		assign_h3600_egpio( IPAQ_EGPIO_RS232_ON, !state );
+	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;
 }
 
-/*
- * 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)
+static void __init h3800_init_irq(void)
 {
-	int err = -EINVAL;
+	int i;
 
-	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;
+	/* 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);
 	}
-	return err;
+
+	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);
 }
 
-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,
-};
 
-static struct map_desc h3600_io_desc[] __initdata = {
- /* virtual	       physical    length      type */
-  { H3600_EGPIO_VIRT,  0x49000000, 0x01000000, MT_DEVICE }, /* EGPIO 0		 CS#5 */
-  { H3600_BANK_2_VIRT, 0x10000000, 0x02800000, MT_DEVICE }, /* static memory bank 2  CS#2 */
-  { H3600_BANK_4_VIRT, 0x40000000, 0x00800000, MT_DEVICE }  /* static memory bank 4  CS#4 */
-};
+#define ASIC1_OUTPUTS	 0x7fff   /* First 15 bits are used */
 
-static void __init h3600_map_io(void)
+static void __init h3800_map_io(void)
 {
-	sa1100_map_io();
-	iotable_init(h3600_io_desc, ARRAY_SIZE(h3600_io_desc));
+	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;
 
-	sa1100_register_uart_fns(&h3600_port_fns);
-	sa1100_register_uart(0, 3);
-	sa1100_register_uart(1, 1); /* isn't this one driven elsewhere? */
-
-	/*
-	 * Default GPIO settings.  Should be set by machine
-	 */
-	GPCR = 0x0fffffff;
-//	GPDR = 0x0401f3fc;
-	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;
+	/* 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;
 
-	init_h3600_egpio();
+	/* 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;
 
-	/*
-	 * Ensure those pins are outputs and driving low.
-	 */
-	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
-	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+	H3800_ASIC2_GPIODIR	= GPIO2_PEN_IRQ
+				      | GPIO2_SD_DETECT
+				      | GPIO2_EAR_IN_N
+				      | GPIO2_USB_DETECT_N
+				      | GPIO2_SD_CON_SLT;
 
-	/* Configure suspend conditions */
-	PGSR = 0;
-	PWER = PWER_GPIO0 | PWER_RTC;
-	PCFR = PCFR_OPDE;
-	PSDR = 0;
+	/* TODO : Set sleep states & battery fault states */
+
+	/* Clear VPP Enable */
+	H3800_ASIC2_FlashWP_VPP_ON = 0;
+	ipaq_model_ops = h3800_model_ops;
 }
 
-MACHINE_START(H3600, "Compaq iPAQ H3600")
-	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
-	BOOT_PARAMS(0xc0000100)
-	MAPIO(h3600_map_io)
-	INITIRQ(sa1100_init_irq)
-MACHINE_END
-MACHINE_START(H3100, "Compaq iPAQ H3100")
-	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
-	BOOT_PARAMS(0xc0000100)
-	MAPIO(h3600_map_io)
-	INITIRQ(sa1100_init_irq)
-MACHINE_END
 MACHINE_START(H3800, "Compaq iPAQ H3800")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	BOOT_PARAMS(0xc0000100)
-	MAPIO(h3600_map_io)
-	INITIRQ(sa1100_init_irq)
+	MAPIO(h3800_map_io)
+	INITIRQ(h3800_init_irq)
 MACHINE_END
+
+#endif /* CONFIG_SA1100_H3800 */
diff --git a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c
index 3ff0f6999a2f621eacbcdd55e2bbe37c27a8a98b..a735626790258791f3feed102b20cc846ddb8440 100644
--- a/arch/arm/mach-sa1100/jornada720.c
+++ b/arch/arm/mach-sa1100/jornada720.c
@@ -16,7 +16,6 @@
 #include <asm/mach/serial_sa1100.h>
 
 #include "generic.h"
-#include "sa1111.h"
 
 
 #define JORTUCR_VAL	0x20000400
@@ -46,11 +45,7 @@ static int __init jornada720_init(void)
 	PPSR &= ~(PPC_LDD3 | PPC_LDD4);
 	PPDR |= PPC_LDD3 | PPC_LDD4;
 
-	/* initialize extra IRQs */
-	set_GPIO_IRQ_edge(GPIO_GPIO1, GPIO_RISING_EDGE);
-	sa1111_init_irq(IRQ_GPIO1);	/* chained on GPIO 1 */
-
-	return 0;
+	return sa1111_init(0x40000000, IRQ_GPIO1);
 }
 
 arch_initcall(jornada720_init);
diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c
index ef95e6c45f84bd529b3dcca01b878f110c40f128..9d3a939de4ae896adcf34aa3e6716060caeed674 100644
--- a/arch/arm/mach-sa1100/neponset.c
+++ b/arch/arm/mach-sa1100/neponset.c
@@ -11,6 +11,7 @@
 #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>
@@ -23,13 +24,6 @@
 #include <asm/hardware/sa1111.h>
 #include <asm/sizes.h>
 
-#include "sa1111.h"
-
-static struct device neponset_device = {
-	.name	= "Neponset",
-	.bus_id	= "nep_bus",
-};
-
 /*
  * 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
@@ -163,6 +157,52 @@ static struct sa1100_port_fns neponset_port_fns __initdata = {
 	.get_mctrl	= neponset_get_mctrl,
 };
 
+/*
+ * LDM power management.
+ */
+static int neponset_suspend(struct device *dev, u32 state, u32 level)
+{
+	/*
+	 * Save state.
+	 */
+	if (level == SUSPEND_SAVE_STATE ||
+	    level == SUSPEND_DISABLE ||
+	    level == SUSPEND_POWER_DOWN) {
+		if (!dev->saved_state)
+			dev->saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL);
+		if (!dev->saved_state)
+			return -ENOMEM;
+
+		*(unsigned int *)dev->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->saved_state) {
+			NCR_0 = *(unsigned int *)dev->saved_state;
+			kfree(dev->saved_state);
+			dev->saved_state = NULL;
+		}
+	}
+
+	return 0;
+}
+
+static struct device_driver neponset_device_driver = {
+	.suspend = neponset_suspend,
+	.resume  = neponset_resume,
+};
+
+static struct device neponset_device = {
+	.name	= "Neponset",
+	.bus_id	= "neponset",
+	.driver = &neponset_device_driver,
+};
+
 static int __init neponset_init(void)
 {
 	int ret;
@@ -191,7 +231,7 @@ static int __init neponset_init(void)
 		return -ENODEV;
 	}
 
-	ret = device_register(&neponset_device);
+	ret = register_sys_device(&neponset_device);
 	if (ret)
 		return ret;
 
@@ -213,7 +253,7 @@ static int __init neponset_init(void)
 	/*
 	 * Probe and initialise the SA1111.
 	 */
-	return sa1111_init(&neponset_device, 0x40000000, IRQ_NEPONSET_SA1111);
+	return sa1111_init(0x40000000, IRQ_NEPONSET_SA1111);
 }
 
 arch_initcall(neponset_init);
diff --git a/arch/arm/mach-sa1100/pfs168.c b/arch/arm/mach-sa1100/pfs168.c
index 2b62878b4e7c431c1cdacd8fc36a086bcd0feb85..d17c1a79e7857247c9f950dee083aafa46043ed3 100644
--- a/arch/arm/mach-sa1100/pfs168.c
+++ b/arch/arm/mach-sa1100/pfs168.c
@@ -17,7 +17,6 @@
 #include <asm/mach/serial_sa1100.h>
 
 #include "generic.h"
-#include "sa1111.h"
 
 
 static int __init pfs168_init(void)
@@ -36,7 +35,7 @@ static int __init pfs168_init(void)
 	/*
 	 * Probe for SA1111.
 	 */
-	return sa1111_init(NULL, 0x40000000, IRQ_GPIO25);
+	return sa1111_init(0x40000000, IRQ_GPIO25);
 }
 
 arch_initcall(pfs168_init);
diff --git a/arch/arm/mach-sa1100/pm.c b/arch/arm/mach-sa1100/pm.c
index 1a7cebd8d28c994705eadd8ebc3cca24845f9de9..76e0da5b973efa6c6f166efddd1ff01363dc9591 100644
--- a/arch/arm/mach-sa1100/pm.c
+++ b/arch/arm/mach-sa1100/pm.c
@@ -30,6 +30,7 @@
 #include <linux/interrupt.h>
 #include <linux/sysctl.h>
 #include <linux/errno.h>
+#include <linux/device.h>
 #include <linux/cpufreq.h>
 
 #include <asm/hardware.h>
@@ -193,11 +194,29 @@ static int sysctl_pm_do_suspend(void)
 {
 	int retval;
 
+	/*
+	 * Suspend "legacy" devices.
+	 */
 	retval = pm_send_all(PM_SUSPEND, (void *)3);
-
 	if (retval == 0) {
+		/*
+		 * Suspend LDM devices.
+		 */
+		device_suspend(4, SUSPEND_NOTIFY);
+		device_suspend(4, SUSPEND_SAVE_STATE);
+		device_suspend(4, SUSPEND_DISABLE);
+
 		retval = pm_do_suspend();
 
+		/*
+		 * Resume LDM devices.
+		 */
+		device_resume(RESUME_RESTORE_STATE);
+		device_resume(RESUME_ENABLE);
+
+		/*
+		 * Resume "legacy" devices.
+		 */
 		pm_send_all(PM_RESUME, (void *)0);
 	}
 
diff --git a/arch/arm/mach-sa1100/sa1111.c b/arch/arm/mach-sa1100/sa1111.c
index 244dfdc35c591785d94c3eb6544e652bc374944b..9ded8fe5f98e7fbbe770c2b252701107b5cefcc0 100644
--- a/arch/arm/mach-sa1100/sa1111.c
+++ b/arch/arm/mach-sa1100/sa1111.c
@@ -25,6 +25,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
@@ -34,11 +35,132 @@
 
 #include <asm/hardware/sa1111.h>
 
-#include "sa1111.h"
+/*
+ * We keep the following data for the overall SA1111.  Note that the
+ * struct device and struct resource are "fake"; they should be supplied
+ * by the bus above us.  However, in the interests of getting all SA1111
+ * drivers converted over to the device model, we provide this as an
+ * anchor point for all the other drivers.
+ */
+struct sa1111 {
+	struct device	dev;
+	struct resource	res;
+	int		irq;
+	spinlock_t	lock;
+	void		*base;
+};
+
+/*
+ * We _really_ need to eliminate this.  Its only users
+ * are the PWM and DMA checking code.
+ */
+static struct sa1111 *g_sa1111;
+
+static struct sa1111_dev usb_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [USB Controller]",
+	},
+	.skpcr_mask	= SKPCR_UCLKEN,
+	.devid		= SA1111_DEVID_USB,
+	.irq = {
+		IRQ_USBPWR,
+		IRQ_NHCIM,
+		IRQ_HCIBUFFACC,
+		IRQ_HCIRMTWKP,
+		IRQ_NHCIMFCIR,
+		IRQ_USB_PORT_RESUME
+	},
+};
 
-struct sa1111_device *sa1111;
+static struct sa1111_dev sac_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [Audio Controller]",
+	},
+	.skpcr_mask	= SKPCR_I2SCLKEN | SKPCR_L3CLKEN,
+	.devid		= SA1111_DEVID_SAC,
+	.irq = {
+		AUDXMTDMADONEA,
+		AUDXMTDMADONEB,
+		AUDRCVDMADONEA,
+		AUDRCVDMADONEB
+	},
+};
 
-EXPORT_SYMBOL(sa1111);
+static struct sa1111_dev ssp_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [SSP Controller]",
+	},
+	.skpcr_mask	= SKPCR_SCLKEN,
+	.devid		= SA1111_DEVID_SSP,
+};
+
+static struct sa1111_dev kbd_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [PS2]",
+	},
+	.skpcr_mask	= SKPCR_PTCLKEN,
+	.devid		= SA1111_DEVID_PS2,
+	.irq = {
+		IRQ_TPRXINT,
+		IRQ_TPTXINT
+	},
+};
+
+static struct sa1111_dev mse_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [PS2]",
+	},
+	.skpcr_mask	= SKPCR_PMCLKEN,
+	.devid		= SA1111_DEVID_PS2,
+	.irq = {
+		IRQ_MSRXINT,
+		IRQ_MSTXINT
+	},
+};
+
+static struct sa1111_dev int_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [Interrupt Controller]",
+	},
+	.skpcr_mask	= 0,
+	.devid		= SA1111_DEVID_INT,
+};
+
+static struct sa1111_dev pcmcia_dev = {
+	.dev = {
+		.name	= "Intel Corporation SA1111 [PCMCIA Controller]",
+	},
+	.skpcr_mask	= 0,
+	.devid		= SA1111_DEVID_PCMCIA,
+	.irq = {
+		IRQ_S0_READY_NINT,
+		IRQ_S0_CD_VALID,
+		IRQ_S0_BVD1_STSCHG,
+		IRQ_S1_READY_NINT,
+		IRQ_S1_CD_VALID,
+		IRQ_S1_BVD1_STSCHG,
+	},
+};
+
+static struct sa1111_dev *devs[] = {
+	&usb_dev,
+	&sac_dev,
+	&ssp_dev,
+	&kbd_dev,
+	&mse_dev,
+	&int_dev,
+	&pcmcia_dev,
+};
+
+static unsigned int dev_offset[] = {
+	SA1111_USB,
+	0x0600,
+	0x0800,
+	SA1111_KBD,
+	SA1111_MSE,
+	SA1111_INTC,
+	0x1800,
+};
 
 /*
  * SA1111 interrupt support.  Since clearing an IRQ while there are
@@ -50,11 +172,15 @@ sa1111_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
 {
 	unsigned int stat0, stat1, i;
 
-	desc->chip->ack(irq);
-
 	stat0 = INTSTATCLR0;
 	stat1 = INTSTATCLR1;
 
+	INTSTATCLR0 = stat0;
+
+	desc->chip->ack(irq);
+
+	INTSTATCLR1 = stat1;
+
 	if (stat0 == 0 && stat1 == 0) {
 		do_bad_IRQ(irq, desc, regs);
 		return;
@@ -75,9 +201,8 @@ sa1111_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
 #define SA1111_IRQMASK_LO(x)	(1 << (x - IRQ_SA1111_START))
 #define SA1111_IRQMASK_HI(x)	(1 << (x - IRQ_SA1111_START - 32))
 
-static void sa1111_ack_lowirq(unsigned int irq)
+static void sa1111_ack_irq(unsigned int irq)
 {
-	INTSTATCLR0 = SA1111_IRQMASK_LO(irq);
 }
 
 static void sa1111_mask_lowirq(unsigned int irq)
@@ -133,18 +258,13 @@ static int sa1111_type_lowirq(unsigned int irq, unsigned int flags)
 }
 
 static struct irqchip sa1111_low_chip = {
-	.ack		= sa1111_ack_lowirq,
+	.ack		= sa1111_ack_irq,
 	.mask		= sa1111_mask_lowirq,
 	.unmask		= sa1111_unmask_lowirq,
 	.rerun		= sa1111_rerun_lowirq,
 	.type		= sa1111_type_lowirq,
 };
 
-static void sa1111_ack_highirq(unsigned int irq)
-{
-	INTSTATCLR1 = SA1111_IRQMASK_HI(irq);
-}
-
 static void sa1111_mask_highirq(unsigned int irq)
 {
 	INTEN1 &= ~SA1111_IRQMASK_HI(irq);
@@ -198,34 +318,40 @@ static int sa1111_type_highirq(unsigned int irq, unsigned int flags)
 }
 
 static struct irqchip sa1111_high_chip = {
-	.ack		= sa1111_ack_highirq,
+	.ack		= sa1111_ack_irq,
 	.mask		= sa1111_mask_highirq,
 	.unmask		= sa1111_unmask_highirq,
 	.rerun		= sa1111_rerun_highirq,
 	.type		= sa1111_type_highirq,
 };
 
-static void __init sa1111_init_irq(int irq_nr)
+static void __init sa1111_init_irq(struct sa1111_dev *sadev)
 {
 	unsigned int irq;
 
-	request_mem_region(_INTTEST0, 512, "irqs");
+	/*
+	 * We're guaranteed that this region hasn't been taken.
+	 */
+	request_mem_region(sadev->res.start, 512, "irqs");
 
 	/* disable all IRQs */
-	INTEN0 = 0;
-	INTEN1 = 0;
+	sa1111_writel(0, sadev->mapbase + SA1111_INTEN0);
+	sa1111_writel(0, sadev->mapbase + SA1111_INTEN1);
+	sa1111_writel(0, sadev->mapbase + SA1111_WAKEEN0);
+	sa1111_writel(0, sadev->mapbase + SA1111_WAKEEN1);
 
 	/*
 	 * detect on rising edge.  Note: Feb 2001 Errata for SA1111
 	 * specifies that S0ReadyInt and S1ReadyInt should be '1'.
 	 */
-	INTPOL0 = 0;
-	INTPOL1 = SA1111_IRQMASK_HI(S0_READY_NINT) |
-		  SA1111_IRQMASK_HI(S1_READY_NINT);
+	sa1111_writel(0, sadev->mapbase + SA1111_INTPOL0);
+	sa1111_writel(SA1111_IRQMASK_HI(IRQ_S0_READY_NINT) |
+		      SA1111_IRQMASK_HI(IRQ_S1_READY_NINT),
+		      sadev->mapbase + SA1111_INTPOL1);
 
 	/* clear all IRQs */
-	INTSTATCLR0 = ~0;
-	INTSTATCLR1 = ~0;
+	sa1111_writel(~0, sadev->mapbase + SA1111_INTSTATCLR0);
+	sa1111_writel(~0, sadev->mapbase + SA1111_INTSTATCLR1);
 
 	for (irq = IRQ_GPAIN0; irq <= SSPROR; irq++) {
 		set_irq_chip(irq, &sa1111_low_chip);
@@ -233,7 +359,7 @@ static void __init sa1111_init_irq(int irq_nr)
 		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
 	}
 
-	for (irq = AUDXMTDMADONEA; irq <= S1_BVD1_STSCHG; irq++) {
+	for (irq = AUDXMTDMADONEA; irq <= IRQ_S1_BVD1_STSCHG; irq++) {
 		set_irq_chip(irq, &sa1111_high_chip);
 		set_irq_handler(irq, do_edge_IRQ);
 		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@@ -242,24 +368,11 @@ static void __init sa1111_init_irq(int irq_nr)
 	/*
 	 * Register SA1111 interrupt
 	 */
-	set_irq_type(irq_nr, IRQT_RISING);
-	set_irq_chained_handler(irq_nr, sa1111_irq_handler);
-}
-
-static int sa1111_suspend(struct device *dev, u32 state, u32 level)
-{
-	return 0;
+	set_irq_type(sadev->irq[0], IRQT_RISING);
+	set_irq_chained_handler(sadev->irq[0], sa1111_irq_handler);
 }
 
-static int sa1111_resume(struct device *dev, u32 level)
-{
-	return 0;
-}
-
-static struct device_driver sa1111_device_driver = {
-	.suspend	= sa1111_suspend,
-	.resume		= sa1111_resume,
-};
+static struct device_driver sa1111_device_driver;
 
 /**
  *	sa1111_probe - probe for a single SA1111 chip.
@@ -274,30 +387,38 @@ static struct device_driver sa1111_device_driver = {
  *	%0		successful.
  */
 static int __init
-sa1111_probe(struct device *parent, unsigned long phys_addr)
+sa1111_probe(unsigned long phys_addr, int irq)
 {
-	struct sa1111_device *sa;
+	struct sa1111 *sachip;
 	unsigned long id;
-	int ret = -ENODEV;
+	unsigned int has_devs;
+	int i, ret = -ENODEV;
 
-	sa = kmalloc(sizeof(struct sa1111_device), GFP_KERNEL);
-	if (!sa)
+	sachip = kmalloc(sizeof(struct sa1111), GFP_KERNEL);
+	if (!sachip)
 		return -ENOMEM;
 
-	memset(sa, 0, sizeof(struct sa1111_device));
+	memset(sachip, 0, sizeof(struct sa1111));
+
+	spin_lock_init(&sachip->lock);
 
-	sa->resource.name  = "SA1111";
-	sa->resource.start = phys_addr;
-	sa->resource.end   = phys_addr + 0x2000;
+	strncpy(sachip->dev.name, "Intel Corporation SA1111", sizeof(sachip->dev.name));
+	snprintf(sachip->dev.bus_id, sizeof(sachip->dev.bus_id), "%8.8lx", phys_addr);
+	sachip->dev.driver = &sa1111_device_driver;
+	sachip->dev.driver_data = sachip;
 
-	if (request_resource(&iomem_resource, &sa->resource)) {
+	sachip->res.name  = sachip->dev.name;
+	sachip->res.start = phys_addr;
+	sachip->res.end   = phys_addr + 0x2000;
+	sachip->irq = irq;
+
+	if (request_resource(&iomem_resource, &sachip->res)) {
 		ret = -EBUSY;
 		goto out;
 	}
 
-	/* eventually ioremap... */
-	sa->base = (void *)0xf4000000;
-	if (!sa->base) {
+	sachip->base = ioremap(phys_addr, PAGE_SIZE * 2);
+	if (!sachip->base) {
 		ret = -ENOMEM;
 		goto release;
 	}
@@ -305,7 +426,7 @@ sa1111_probe(struct device *parent, unsigned long phys_addr)
 	/*
 	 * Probe for the chip.  Only touch the SBI registers.
 	 */
-	id = sa1111_readl(sa->base + SA1111_SKID);
+	id = sa1111_readl(sachip->base + SA1111_SKID);
 	if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
 		printk(KERN_DEBUG "SA1111 not detected: ID = %08lx\n", id);
 		ret = -ENODEV;
@@ -315,12 +436,7 @@ sa1111_probe(struct device *parent, unsigned long phys_addr)
 	/*
 	 * We found the chip.
 	 */
-	strcpy(sa->dev.name, "SA1111");
-	sprintf(sa->dev.bus_id, "%8.8lx", phys_addr);
-	sa->dev.parent = parent;
-	sa->dev.driver = &sa1111_device_driver;
-
-	ret = device_register(&sa->dev);
+	ret = register_sys_device(&sachip->dev);
 	if (ret)
 		printk("sa1111 device_register failed: %d\n", ret);
 
@@ -328,19 +444,64 @@ sa1111_probe(struct device *parent, unsigned long phys_addr)
 		"silicon revision %lx, metal revision %lx\n",
 		(id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK));
 
-	sa1111 = sa;
+	g_sa1111 = sachip;
+
+	has_devs = ~0;
+	if (machine_is_assabet() || machine_is_jornada720() ||
+	    machine_is_badge4())
+		has_devs &= ~(1 << 4);
+	else
+		has_devs &= ~(1 << 1);
+
+	for (i = 0; i < ARRAY_SIZE(devs); i++) {
+		if (!(has_devs & (1 << i)))
+			continue;
+
+		snprintf(devs[i]->dev.bus_id, sizeof(devs[i]->dev.bus_id),
+			 "%4.4x", dev_offset[i]);
+
+		devs[i]->dev.parent = &sachip->dev;
+		devs[i]->dev.bus    = &sa1111_bus_type;
+		devs[i]->res.start  = sachip->res.start + dev_offset[i];
+		devs[i]->res.end    = devs[i]->res.start + 511;
+		devs[i]->res.name   = devs[i]->dev.name;
+		devs[i]->res.flags  = IORESOURCE_MEM;
+		devs[i]->mapbase    = sachip->base + dev_offset[i];
+
+		if (request_resource(&sachip->res, &devs[i]->res)) {
+			printk("SA1111: failed to allocate resource for %s\n",
+				devs[i]->res.name);
+			continue;
+		}
+
+		device_register(&devs[i]->dev);
+	}
 
 	return 0;
 
  unmap:
-//	iounmap(sa->base);
+	iounmap(sachip->base);
  release:
-	release_resource(&sa->resource);
+	release_resource(&sachip->res);
  out:
-	kfree(sa);
+	kfree(sachip);
 	return ret;
 }
 
+static void __sa1111_remove(struct sa1111 *sachip)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(devs); i++) {
+		put_device(&devs[i]->dev);
+		release_resource(&devs[i]->res);
+	}
+
+	iounmap(sachip->base);
+	release_resource(&sachip->res);
+	kfree(sachip);
+}
+
 /*
  * Bring the SA1111 out of reset.  This requires a set procedure:
  *  1. nRESET asserted (by hardware)
@@ -355,12 +516,11 @@ sa1111_probe(struct device *parent, unsigned long phys_addr)
  *   SBI_SMCR
  *   SBI_SKID
  */
-void sa1111_wake(void)
+static void sa1111_wake(struct sa1111 *sachip)
 {
-	struct sa1111_device *sa = sa1111;
 	unsigned long flags, r;
 
-	local_irq_save(flags);
+	spin_lock_irqsave(&sachip->lock, flags);
 
 	/*
 	 * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
@@ -373,11 +533,11 @@ void sa1111_wake(void)
 	/*
 	 * Turn VCO on, and disable PLL Bypass.
 	 */
-	r = sa1111_readl(sa->base + SA1111_SKCR);
+	r = sa1111_readl(sachip->base + SA1111_SKCR);
 	r &= ~SKCR_VCO_OFF;
-	sa1111_writel(r, sa->base + SA1111_SKCR);
+	sa1111_writel(r, sachip->base + SA1111_SKCR);
 	r |= SKCR_PLL_BYPASS | SKCR_OE_EN;
-	sa1111_writel(r, sa->base + SA1111_SKCR);
+	sa1111_writel(r, sachip->base + SA1111_SKCR);
 
 	/*
 	 * Wait lock time.  SA1111 manual _doesn't_
@@ -389,7 +549,7 @@ void sa1111_wake(void)
 	 * Enable RCLK.  We also ensure that RDYEN is set.
 	 */
 	r |= SKCR_RCLKEN | SKCR_RDYEN;
-	sa1111_writel(r, sa->base + SA1111_SKCR);
+	sa1111_writel(r, sachip->base + SA1111_SKCR);
 
 	/*
 	 * Wait 14 RCLK cycles for the chip to finish coming out
@@ -400,76 +560,31 @@ void sa1111_wake(void)
 	/*
 	 * Ensure all clocks are initially off.
 	 */
-	sa1111_writel(0, sa->base + SA1111_SKPCR);
+	sa1111_writel(0, sachip->base + SA1111_SKPCR);
 
-	local_irq_restore(flags);
-}
-
-void sa1111_doze(void)
-{
-	struct sa1111_device *sa = sa1111;
-	unsigned long flags;
-	unsigned int val;
-
-	local_irq_save(flags);
-
-	if (sa1111_readl(sa->base + SA1111_SKPCR) & SKPCR_UCLKEN) {
-		local_irq_restore(flags);
-		printk("SA1111 doze mode refused\n");
-		return;
-	}
-
-	val = sa1111_readl(sa->base + SA1111_SKCR);
-	sa1111_writel(val & ~SKCR_RCLKEN, sa->base + SA1111_SKCR);
-	local_irq_restore(flags);
+	spin_unlock_irqrestore(&sachip->lock, flags);
 }
 
 /*
  * Configure the SA1111 shared memory controller.
  */
-void sa1111_configure_smc(int sdram, unsigned int drac, unsigned int cas_latency)
+void
+sa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac,
+		     unsigned int cas_latency)
 {
-	struct sa1111_device *sa = sa1111;
 	unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC);
 
 	if (cas_latency == 3)
 		smcr |= SMCR_CLAT;
 
-	sa1111_writel(smcr, sa->base + SA1111_SMCR);
+	sa1111_writel(smcr, sachip->base + SA1111_SMCR);
 }
 
-EXPORT_SYMBOL(sa1111_wake);
-EXPORT_SYMBOL(sa1111_doze);
-
-void sa1111_enable_device(unsigned int mask)
-{
-	struct sa1111_device *sa = sa1111;
-	unsigned int val;
-
-	preempt_disable();
-	val = sa1111_readl(sa->base + SA1111_SKPCR);
-	sa1111_writel(val | mask, sa->base + SA1111_SKPCR);
-	preempt_enable();
-}
-
-void sa1111_disable_device(unsigned int mask)
-{
-	struct sa1111_device *sa = sa1111;
-	unsigned int val;
-
-	preempt_disable();
-	val = sa1111_readl(sa->base + SA1111_SKPCR);
-	sa1111_writel(val & ~mask, sa->base + SA1111_SKPCR);
-	preempt_enable();
-}
-
-EXPORT_SYMBOL(sa1111_enable_device);
-EXPORT_SYMBOL(sa1111_disable_device);
-
-/* According to the "Intel StrongARM SA-1111 Microprocessor Companion
+/*
+ * According to the "Intel StrongARM SA-1111 Microprocessor Companion
  * Chip Specification Update" (June 2000), erratum #7, there is a
- * significant bug in Serial Audio Controller DMA. If the SAC is
- * accessing a region of memory above 1MB relative to the bank base,
+ * significant bug in the SA1111 SDRAM shared memory controller.  If
+ * an access to a region of memory above 1MB relative to the bank base,
  * it is important that address bit 10 _NOT_ be asserted. Depending
  * on the configuration of the RAM, bit 10 may correspond to one
  * of several different (processor-relative) address bits.
@@ -479,7 +594,9 @@ EXPORT_SYMBOL(sa1111_disable_device);
  */
 int sa1111_check_dma_bug(dma_addr_t addr)
 {
-	unsigned int physaddr=SA1111_DMA_ADDR((unsigned int)addr);
+	struct sa1111 *sachip = g_sa1111;
+	unsigned int physaddr = SA1111_DMA_ADDR((unsigned int)addr);
+	unsigned int smcr;
 
 	/* Section 4.6 of the "Intel StrongARM SA-1111 Development Module
 	 * User's Guide" mentions that jumpers R51 and R52 control the
@@ -494,9 +611,10 @@ int sa1111_check_dma_bug(dma_addr_t addr)
 	 * above the start of the target bank:
 	 */
 	if (physaddr<(1<<20))
-	  	return 0;
+		return 0;
 
-	switch (FExtr(SBI_SMCR, SMCR_DRAC)) {
+	smcr = sa1111_readl(sachip->base + SA1111_SMCR);
+	switch (FExtr(smcr, SMCR_DRAC)) {
 	case 01: /* 10 row + bank address bits, A<20> must not be set */
 	  	if (physaddr & (1<<20))
 		  	return -1;
@@ -523,27 +641,26 @@ int sa1111_check_dma_bug(dma_addr_t addr)
 		break;
 	default:
 	  	printk(KERN_ERR "%s(): invalid SMCR DRAC value 0%lo\n",
-		       __FUNCTION__, FExtr(SBI_SMCR, SMCR_DRAC));
+		       __FUNCTION__, FExtr(smcr, SMCR_DRAC));
 		return -1;
 	}
 
 	return 0;
 }
 
-EXPORT_SYMBOL(sa1111_check_dma_bug);
-
-int sa1111_init(struct device *parent, unsigned long phys, unsigned int irq)
+int sa1111_init(unsigned long phys, unsigned int irq)
 {
+	unsigned int val;
 	int ret;
 
-	ret = sa1111_probe(parent, phys);
+	ret = sa1111_probe(phys, irq);
 	if (ret < 0)
 		return ret;
 
 	/*
 	 * We found it.  Wake the chip up.
 	 */
-	sa1111_wake();
+	sa1111_wake(g_sa1111);
 
 	/*
 	 * The SDRAM configuration of the SA1110 and the SA1111 must
@@ -552,7 +669,7 @@ int sa1111_init(struct device *parent, unsigned long phys, unsigned int irq)
 	 * MBGNT signal, so we must have called sa1110_mb_disable()
 	 * beforehand.
 	 */
-	sa1111_configure_smc(1,
+	sa1111_configure_smc(g_sa1111, 1,
 			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
 			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
 
@@ -560,7 +677,8 @@ int sa1111_init(struct device *parent, unsigned long phys, unsigned int irq)
 	 * We only need to turn on DCLK whenever we want to use the
 	 * DMA.  It can otherwise be held firmly in the off position.
 	 */
-	sa1111_enable_device(SKPCR_DCLKEN);
+	val = sa1111_readl(g_sa1111->base + SA1111_SKPCR);
+	sa1111_writel(val | SKPCR_DCLKEN, g_sa1111->base + SA1111_SKPCR);
 
 	/*
 	 * Enable the SA1110 memory bus request and grant signals.
@@ -570,7 +688,341 @@ int sa1111_init(struct device *parent, unsigned long phys, unsigned int irq)
 	/*
 	 * Initialise SA1111 IRQs
 	 */
-	sa1111_init_irq(irq);
+	int_dev.irq[0] = irq;
+	sa1111_init_irq(&int_dev);
+
+	return 0;
+}
+
+struct sa1111_save_data {
+	unsigned int	skcr;
+	unsigned int	skpcr;
+	unsigned int	skcdr;
+	unsigned char	skaud;
+	unsigned char	skpwm0;
+	unsigned char	skpwm1;
+
+	/*
+	 * Interrupt controller
+	 */
+	unsigned int	intpol0;
+	unsigned int	intpol1;
+	unsigned int	inten0;
+	unsigned int	inten1;
+	unsigned int	wakepol0;
+	unsigned int	wakepol1;
+	unsigned int	wakeen0;
+	unsigned int	wakeen1;
+};
+
+static int sa1111_suspend(struct device *dev, u32 state, u32 level)
+{
+	struct sa1111 *sachip = dev->driver_data;
+	unsigned long flags;
+	char *base;
+
+	/*
+	 * Save state.
+	 */
+	if (level == SUSPEND_SAVE_STATE ||
+	    level == SUSPEND_DISABLE ||
+	    level == SUSPEND_POWER_DOWN) {
+		struct sa1111_save_data *save;
+
+		if (!dev->saved_state)
+			dev->saved_state = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
+		if (!dev->saved_state)
+			return -ENOMEM;
+
+		save = (struct sa1111_save_data *)dev->saved_state;
+
+		spin_lock_irqsave(&sachip->lock, flags);
+		base = sachip->base;
+		save->skcr     = sa1111_readl(base + SA1111_SKCR);
+		save->skpcr    = sa1111_readl(base + SA1111_SKPCR);
+		save->skcdr    = sa1111_readl(base + SA1111_SKCDR);
+		save->skaud    = sa1111_readl(base + SA1111_SKAUD);
+		save->skpwm0   = sa1111_readl(base + SA1111_SKPWM0);
+		save->skpwm1   = sa1111_readl(base + SA1111_SKPWM1);
+
+		base = sachip->base + SA1111_INTC;
+		save->intpol0  = sa1111_readl(base + SA1111_INTPOL0);
+		save->intpol1  = sa1111_readl(base + SA1111_INTPOL1);
+		save->inten0   = sa1111_readl(base + SA1111_INTEN0);
+		save->inten1   = sa1111_readl(base + SA1111_INTEN1);
+		save->wakepol0 = sa1111_readl(base + SA1111_WAKEPOL0);
+		save->wakepol1 = sa1111_readl(base + SA1111_WAKEPOL1);
+		save->wakeen0  = sa1111_readl(base + SA1111_WAKEEN0);
+		save->wakeen1  = sa1111_readl(base + SA1111_WAKEEN1);
+		spin_unlock_irqrestore(&sachip->lock, flags);
+	}
+
+	/*
+	 * Disable.
+	 */
+	if (level == SUSPEND_DISABLE && state == 4) {
+		unsigned int val;
+
+		spin_lock_irqsave(&sachip->lock, flags);
+		base = sachip->base;
+
+		sa1111_writel(0, base + SA1111_SKPWM0);
+		sa1111_writel(0, base + SA1111_SKPWM1);
+		val = sa1111_readl(base + SA1111_SKCR);
+		sa1111_writel(val | SKCR_SLEEP, base + SA1111_SKCR);
+
+		spin_unlock_irqrestore(&sachip->lock, flags);
+	}
 
 	return 0;
 }
+
+/*
+ *	sa1111_resume - Restore the SA1111 device state.
+ *	@dev: device to restore
+ *	@level: resume level
+ *
+ *	Restore the general state of the SA1111; clock control and
+ *	interrupt controller.  Other parts of the SA1111 must be
+ *	restored by their respective drivers, and must be called
+ *	via LDM after this function.
+ */
+static int sa1111_resume(struct device *dev, u32 level)
+{
+	struct sa1111 *sachip = dev->driver_data;
+	struct sa1111_save_data *save;
+	unsigned long flags, id;
+	char *base;
+
+	if (level != RESUME_RESTORE_STATE && level != RESUME_ENABLE)
+		return 0;
+
+	save = (struct sa1111_save_data *)dev->saved_state;
+	if (!save)
+		return 0;
+
+	dev->saved_state = NULL;
+
+	/*
+	 * Ensure that the SA1111 is still here.
+	 */
+	id = sa1111_readl(sachip->base + SA1111_SKID);
+	if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
+		__sa1111_remove(sachip);
+		kfree(save);
+		return 0;
+	}
+
+	spin_lock_irqsave(&sachip->lock, flags);
+	sa1111_wake(sachip);
+
+	base = sachip->base;
+	sa1111_writel(save->skcr,     base + SA1111_SKCR);
+	sa1111_writel(save->skpcr,    base + SA1111_SKPCR);
+	sa1111_writel(save->skcdr,    base + SA1111_SKCDR);
+	sa1111_writel(save->skaud,    base + SA1111_SKAUD);
+	sa1111_writel(save->skpwm0,   base + SA1111_SKPWM0);
+	sa1111_writel(save->skpwm1,   base + SA1111_SKPWM1);
+
+	base = sachip->base + SA1111_INTC;
+	sa1111_writel(save->intpol0,  base + SA1111_INTPOL0);
+	sa1111_writel(save->intpol1,  base + SA1111_INTPOL1);
+	sa1111_writel(save->inten0,   base + SA1111_INTEN0);
+	sa1111_writel(save->inten1,   base + SA1111_INTEN1);
+	sa1111_writel(save->wakepol0, base + SA1111_WAKEPOL0);
+	sa1111_writel(save->wakepol1, base + SA1111_WAKEPOL1);
+	sa1111_writel(save->wakeen0,  base + SA1111_WAKEEN0);
+	sa1111_writel(save->wakeen1,  base + SA1111_WAKEEN1);
+	spin_unlock_irqrestore(&sachip->lock, flags);
+
+	kfree(save);
+
+	return 0;
+}
+
+static struct device_driver sa1111_device_driver = {
+	.suspend	= sa1111_suspend,
+	.resume		= sa1111_resume,
+};
+
+/*
+ *	Get the parent device driver (us) structure
+ *	from a child function device
+ */
+static inline struct sa1111 *sa1111_chip_driver(struct sa1111_dev *sadev)
+{
+	return (struct sa1111 *)sadev->dev.parent->driver_data;
+}
+
+/*
+ * The bits in the opdiv field are non-linear.
+ */
+static unsigned char opdiv_table[] = { 1, 4, 2, 8 };
+
+static unsigned int __sa1111_pll_clock(struct sa1111 *sachip)
+{
+	unsigned int skcdr, fbdiv, ipdiv, opdiv;
+
+	skcdr = sa1111_readl(sachip->base + SA1111_SKCDR);
+
+	fbdiv = (skcdr & 0x007f) + 2;
+	ipdiv = ((skcdr & 0x0f80) >> 7) + 2;
+	opdiv = opdiv_table[(skcdr & 0x3000) >> 12];
+
+	return 3686400 * fbdiv / (ipdiv * opdiv);
+}
+
+/**
+ *	sa1111_pll_clock - return the current PLL clock frequency.
+ *	@sadev: SA1111 function block
+ *
+ *	BUG: we should look at SKCR.  We also blindly believe that
+ *	the chip is being fed with the 3.6864MHz clock.
+ *
+ *	Returns the PLL clock in Hz.
+ */
+unsigned int sa1111_pll_clock(struct sa1111_dev *sadev)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+
+	return __sa1111_pll_clock(sachip);
+}
+
+/**
+ *	sa1111_select_audio_mode - select I2S or AC link mode
+ *	@sadev: SA1111 function block
+ *	@mode: One of %SA1111_AUDIO_ACLINK or %SA1111_AUDIO_I2S
+ *
+ *	Frob the SKCR to select AC Link mode or I2S mode for
+ *	the audio block.
+ */
+void sa1111_select_audio_mode(struct sa1111_dev *sadev, int mode)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+
+	val = sa1111_readl(sachip->base + SA1111_SKCR);
+	if (mode == SA1111_AUDIO_I2S) {
+		val &= ~SKCR_SELAC;
+	} else {
+		val |= SKCR_SELAC;
+	}
+	sa1111_writel(val, sachip->base + SA1111_SKCR);
+
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+/**
+ *	sa1111_set_audio_rate - set the audio sample rate
+ *	@sadev: SA1111 SAC function block
+ *	@rate: sample rate to select
+ */
+int sa1111_set_audio_rate(struct sa1111_dev *sadev, int rate)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned int div;
+
+	if (sadev->devid != SA1111_DEVID_SAC)
+		return -EINVAL;
+
+	div = (__sa1111_pll_clock(sachip) / 256 + rate / 2) / rate;
+	if (div == 0)
+		div = 1;
+	if (div > 128)
+		div = 128;
+
+	sa1111_writel(div - 1, sachip->base + SA1111_SKAUD);
+
+	return 0;
+}
+
+/**
+ *	sa1111_get_audio_rate - get the audio sample rate
+ *	@sadev: SA1111 SAC function block device
+ */
+int sa1111_get_audio_rate(struct sa1111_dev *sadev)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long div;
+
+	if (sadev->devid != SA1111_DEVID_SAC)
+		return -EINVAL;
+
+	div = sa1111_readl(sachip->base + SA1111_SKAUD) + 1;
+
+	return __sa1111_pll_clock(sachip) / (256 * div);
+}
+
+/*
+ * Individual device operations.
+ */
+
+/**
+ *	sa1111_enable_device - enable an on-chip SA1111 function block
+ *	@sadev: SA1111 function block device to enable
+ */
+void sa1111_enable_device(struct sa1111_dev *sadev)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+	val = sa1111_readl(sachip->base + SA1111_SKPCR);
+	sa1111_writel(val | sadev->skpcr_mask, sachip->base + SA1111_SKPCR);
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+/**
+ *	sa1111_disable_device - disable an on-chip SA1111 function block
+ *	@sadev: SA1111 function block device to disable
+ */
+void sa1111_disable_device(struct sa1111_dev *sadev)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+	val = sa1111_readl(sachip->base + SA1111_SKPCR);
+	sa1111_writel(val & ~sadev->skpcr_mask, sachip->base + SA1111_SKPCR);
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+/*
+ *	SA1111 "Register Access Bus."
+ *
+ *	We model this as a regular bus type, and hang devices directly
+ *	off this.
+ */
+static int sa1111_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct sa1111_dev *dev = SA1111_DEV(_dev);
+	struct sa1111_driver *drv = SA1111_DRV(_drv);
+
+	return dev->devid == drv->devid;
+}
+
+struct bus_type sa1111_bus_type = {
+	.name	= "RAB",
+	.match	= sa1111_match,
+};
+
+static int sa1111_rab_bus_init(void)
+{
+	return bus_register(&sa1111_bus_type);
+}
+
+postcore_initcall(sa1111_rab_bus_init);
+
+EXPORT_SYMBOL(sa1111_check_dma_bug);
+EXPORT_SYMBOL(sa1111_select_audio_mode);
+EXPORT_SYMBOL(sa1111_set_audio_rate);
+EXPORT_SYMBOL(sa1111_get_audio_rate);
+EXPORT_SYMBOL(sa1111_enable_device);
+EXPORT_SYMBOL(sa1111_disable_device);
+EXPORT_SYMBOL(sa1111_pll_clock);
+EXPORT_SYMBOL(sa1111_bus_type);
diff --git a/arch/arm/mach-sa1100/sa1111.h b/arch/arm/mach-sa1100/sa1111.h
deleted file mode 100644
index e3a13f66dd13ec33b66c0f542b0d65f2eba8ea07..0000000000000000000000000000000000000000
--- a/arch/arm/mach-sa1100/sa1111.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/sa1111.h
- */
-struct device;
-
-/*
- * Probe for a SA1111 chip.
- */
-extern int
-sa1111_init(struct device *parent, unsigned long phys, unsigned int irq);
-
-/*
- * Wake up a SA1111 chip.
- */
-extern void sa1111_wake(void);
-
-/*
- * Doze the SA1111 chip.
- */
-extern void sa1111_doze(void);
diff --git a/arch/arm/mach-sa1100/stork.c b/arch/arm/mach-sa1100/stork.c
index 677be2dfb5ed443234cdb5494694fd7dba3bdbe4..703a513a94d0a0129ed1339e8e7a5b36a8389b59 100644
--- a/arch/arm/mach-sa1100/stork.c
+++ b/arch/arm/mach-sa1100/stork.c
@@ -284,6 +284,17 @@ storkInitTSandDtoA(void)
     storkClockShortToDtoA(0x0A00);	/* turn on the brightness */
 }
 
+static void stork_lcd_power(int on)
+{
+	if (on) {
+		storkSetLCDCPLD(0, 1);
+		storkSetLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
+	} else {
+		storkSetLCDCPLD(0, 0);
+		storkClearLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
+	}
+}
+
 struct map_desc stork_io_desc[] __initdata = {
  /* virtual     physical    length      type */
   { STORK_VM_BASE_CS1, STORK_VM_OFF_CS1, 0x01000000, MT_DEVICE }, /* EGPIO 0 */
@@ -312,6 +323,8 @@ stork_map_io(void)
 
     storkInitTSandDtoA();
 
+    sa1100fb_lcd_power = stork_lcd_power;
+
     return 0;
 }
 
diff --git a/arch/arm/mach-sa1100/system3.c b/arch/arm/mach-sa1100/system3.c
index 0629aa5420ce30cb711f76b70c3ac573a93ec092..d0dc614b0b875438d48df8a47e4f0466e48d2584 100644
--- a/arch/arm/mach-sa1100/system3.c
+++ b/arch/arm/mach-sa1100/system3.c
@@ -56,7 +56,6 @@
 #include <linux/serial_core.h>
 
 #include "generic.h"
-#include "sa1111.h"
 #include <asm/hardware/sa1111.h>
 
 #define DEBUG 1
@@ -401,7 +400,7 @@ static int __init system3_init(void)
 	/*
 	 * Probe for a SA1111.
 	 */
-	ret = sa1111_init(NULL, PT_SA1111_BASE, IRQ_SYSTEM3_SA1111);
+	ret = sa1111_init(PT_SA1111_BASE, IRQ_SYSTEM3_SA1111);
 	if (ret < 0) {
 		printk( KERN_WARNING"PT Digital Board: no SA1111 found!\n" );
 		goto DONE;
diff --git a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c
index 2feafb6d40dc60d8038f09bf650c672398486eb4..4099f44532b94d9ae2605859e25869124963bfc1 100644
--- a/arch/arm/mm/fault-common.c
+++ b/arch/arm/mm/fault-common.c
@@ -35,7 +35,7 @@
 #else
 /*
  * "code" is actually the FSR register.  Bit 11 set means the
- * isntruction was performing a write.
+ * instruction was performing a write.
  */
 #define DO_COW(code)		((code) & (1 << 11))
 #define READ_FAULT(code)	(!DO_COW(code))
@@ -54,7 +54,7 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
 
 	printk(KERN_ALERT "pgd = %p\n", mm->pgd);
 	pgd = pgd_offset(mm, addr);
-	printk(KERN_ALERT "*pgd=%08lx", pgd_val(*pgd));
+	printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd));
 
 	do {
 		pmd_t *pmd;
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 39e28f1969189a38d1efbfe67393b22a66286498..9b8c9551fb87c923ddd6027cd3832cf7fc7b3d09 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -6,7 +6,7 @@
 # To add an entry into this database, please see Documentation/arm/README,
 # or contact rmk@arm.linux.org.uk
 #
-# Last update: Sat Jul 27 09:56:53 2002
+# Last update: Sat Sep 21 13:39:13 2002
 #
 # machine_is_xxx	CONFIG_xxxx		MACH_TYPE_xxx		number
 #
@@ -214,3 +214,31 @@ emailphone		SA1100_EMAILPHONE	EMAILPHONE		202
 h3900			ARCH_H3900		H3900			203
 pxa1			ARCH_PXA1		PXA1			204
 koan369			SA1100_KOAN369		KOAN369			205
+cogent			ARCH_COGENT		COGENT			206
+esl_simputer		ARCH_ESL_SIMPUTER	ESL_SIMPUTER		207
+esl_simputer_clr	ARCH_ESL_SIMPUTER_CLR	ESL_SIMPUTER_CLR	208
+esl_simputer_bw		ARCH_ESL_SIMPUTER_BW	ESL_SIMPUTER_BW		209
+hhp_cradle		ARCH_HHP_CRADLE		HHP_CRADLE		210
+he500			ARCH_HE500		HE500			211
+inhandelf2		SA1100_INHANDELF2	INHANDELF2		212
+inhandftip		SA1100_INHANDFTIP	INHANDFTIP		213
+dnp1110			SA1100_DNP1110		DNP1110			214
+pnp1110			SA1100_PNP1110		PNP1110			215
+csb226			ARCH_CSB226		CSB226			216
+arnold			SA1100_ARNOLD		ARNOLD			217
+psiboard		SA1100_PSIBOARD		PSIBOARD		218
+jz8028			ARCH_JZ8028		JZ8028			219
+ipaq3			ARCH_IPAQ3		IPAQ3			220
+forte			SA1100_FORTE		FORTE			221
+acam			SA1100_ACAM		ACAM			222
+abox			SA1100_ABOX		ABOX			223
+atmel			ARCH_ATMEL		ATMEL			224
+sitsang			ARCH_SITSANG		SITSANG			225
+cpu1110lcdnet		SA1100_CPU1110LCDNET	CPU1110LCDNET		226
+mpl_vcma9		ARCH_MPL_VCMA9		MPL_VCMA9		227
+opus_a1			ARCH_OPUS_A1		OPUS_A1			228
+daytona			ARCH_DAYTONA		DAYTONA			229
+killbear		SA1100_KILLBEAR		KILLBEAR		230
+yoho			ARCH_YOHO		YOHO			231
+jasper			ARCH_JASPER		JASPER			232
+dsc25			ARCH_DSC25		DSC25			233
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
index da568810512865ed2d2fade7fedc5758e2423840..502d3c90b86428d150e53b57abc0ea590c664e69 100644
--- a/drivers/input/serio/sa1111ps2.c
+++ b/drivers/input/serio/sa1111ps2.c
@@ -15,20 +15,26 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
 
-#include <asm/hardware/sa1111.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 
+#include <asm/hardware/sa1111.h>
+
 extern struct pt_regs *kbd_pt_regs;
 
 struct ps2if {
-	struct serio	io;
-	struct resource	*res;
-	unsigned long	base;
-	unsigned int	irq;
-	unsigned int	skpcr_mask;
+	struct serio		io;
+	struct sa1111_dev	*dev;
+	unsigned long		base;
+	unsigned int		open;
+	spinlock_t		lock;
+	unsigned int		head;
+	unsigned int		tail;
+	unsigned char		buf[4];
 };
 
 /*
@@ -36,104 +42,150 @@ struct ps2if {
  * at the most one, but we loop for safety.  If there was a
  * framing error, we have to manually clear the status.
  */
-static void ps2_int(int irq, void *dev_id, struct pt_regs *regs)
+static void ps2_rxint(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct ps2if *sa = dev_id;
+	struct ps2if *ps2if = dev_id;
 	unsigned int scancode, flag, status;
 
 	kbd_pt_regs = regs;
 
-	status = sa1111_readl(sa->base + SA1111_PS2STAT);
+	status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
 	while (status & PS2STAT_RXF) {
 		if (status & PS2STAT_STP)
-			sa1111_writel(PS2STAT_STP, sa->base + SA1111_PS2STAT);
+			sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT);
 
 		flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
 		       (status & PS2STAT_RXP ? 0 : SERIO_PARITY);
 
-		scancode = sa1111_readl(sa->base + SA1111_PS2DATA) & 0xff;
+		scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff;
 
 		if (hweight8(scancode) & 1)
 			flag ^= SERIO_PARITY;
 
-		serio_interrupt(&sa->io, scancode, flag);
+		serio_interrupt(&ps2if->io, scancode, flag);
 
-               	status = sa1111_readl(sa->base + SA1111_PS2STAT);
+               	status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
         }
 }
 
+/*
+ * Completion of ps2 write
+ */
+static void ps2_txint(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct ps2if *ps2if = dev_id;
+	unsigned int status;
+
+	spin_lock(&ps2if->lock);
+	status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+	if (ps2if->head == ps2if->tail) {
+		disable_irq(irq);
+		/* done */
+	} else if (status & PS2STAT_TXE) {
+		sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA);
+		ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
+	}
+	spin_unlock(&ps2if->lock);
+}
+
 /*
  * Write a byte to the PS2 port.  We have to wait for the
  * port to indicate that the transmitter is empty.
  */
 static int ps2_write(struct serio *io, unsigned char val)
 {
-	struct ps2if *sa = io->driver;
-	unsigned int timeleft = 10000; /* timeout in 100ms */
+	struct ps2if *ps2if = io->driver;
+	unsigned long flags;
+	unsigned int head;
 
-	while ((sa1111_readl(sa->base + SA1111_PS2STAT) & PS2STAT_TXE) == 0 &&
-	       timeleft--)
-		udelay(10);
+	spin_lock_irqsave(&ps2if->lock, flags);
 
-	if (timeleft)
-		sa1111_writel(val, sa->base + SA1111_PS2DATA);
+	/*
+	 * If the TX register is empty, we can go straight out.
+	 */
+	if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) {
+		sa1111_writel(val, ps2if->base + SA1111_PS2DATA);
+	} else {
+		if (ps2if->head == ps2if->tail)
+			enable_irq(ps2if->dev->irq[1]);
+		head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1);
+		if (head != ps2if->tail) {
+			ps2if->buf[ps2if->head] = val;
+			ps2if->head = head;
+		}
+	}
 
-	return timeleft ? 0 : SERIO_TIMEOUT;
+	spin_unlock_irqrestore(&ps2if->lock, flags);
+	return 0;
 }
 
 static int ps2_open(struct serio *io)
 {
-	struct ps2if *sa = io->driver;
+	struct ps2if *ps2if = io->driver;
 	int ret;
 
-	sa1111_enable_device(sa->skpcr_mask);
+	sa1111_enable_device(ps2if->dev);
 
-	ret = request_irq(sa->irq, ps2_int, 0, "ps2", sa);
+	ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0,
+			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
 	if (ret) {
 		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
-			sa->irq, ret);
+			ps2if->dev->irq[0], ret);
 		return ret;
 	}
 
-	sa1111_writel(PS2CR_ENA, sa->base + SA1111_PS2CR);
+	ret = request_irq(ps2if->dev->irq[1], ps2_txint, 0,
+			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
+	if (ret) {
+		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
+			ps2if->dev->irq[1], ret);
+		free_irq(ps2if->dev->irq[0], ps2if);
+		return ret;
+	}
 
+	ps2if->open = 1;
+
+	sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR);
 	return 0;
 }
 
 static void ps2_close(struct serio *io)
 {
-	struct ps2if *sa = io->driver;
+	struct ps2if *ps2if = io->driver;
+
+	sa1111_writel(0, ps2if->base + SA1111_PS2CR);
 
-	sa1111_writel(0, sa->base + SA1111_PS2CR);
+	ps2if->open = 0;
 
-	free_irq(sa->irq, sa);
+	free_irq(ps2if->dev->irq[1], ps2if);
+	free_irq(ps2if->dev->irq[0], ps2if);
 
-	sa1111_disable_device(sa->skpcr_mask);
+	sa1111_disable_device(ps2if->dev);
 }
 
 /*
  * Clear the input buffer.
  */
-static void __init ps2_clear_input(struct ps2if *sa)
+static void __init ps2_clear_input(struct ps2if *ps2if)
 {
 	int maxread = 100;
 
 	while (maxread--) {
-		if ((sa1111_readl(sa->base + SA1111_PS2DATA) & 0xff) == 0xff)
+		if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff)
 			break;
 	}
 }
 
 static inline unsigned int
-ps2_test_one(struct ps2if *sa, unsigned int mask)
+ps2_test_one(struct ps2if *ps2if, unsigned int mask)
 {
 	unsigned int val;
 
-	sa1111_writel(PS2CR_ENA | mask, sa->base + SA1111_PS2CR);
+	sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR);
 
 	udelay(2);
 
-	val = sa1111_readl(sa->base + SA1111_PS2STAT);
+	val = sa1111_readl(ps2if->base + SA1111_PS2STAT);
 	return val & (PS2STAT_KBC | PS2STAT_KBD);
 }
 
@@ -141,141 +193,168 @@ ps2_test_one(struct ps2if *sa, unsigned int mask)
  * Test the keyboard interface.  We basically check to make sure that
  * we can drive each line to the keyboard independently of each other.
  */
-static int __init ps2_test(struct ps2if *sa)
+static int __init ps2_test(struct ps2if *ps2if)
 {
 	unsigned int stat;
 	int ret = 0;
 
-	stat = ps2_test_one(sa, PS2CR_FKC);
+	stat = ps2_test_one(ps2if, PS2CR_FKC);
 	if (stat != PS2STAT_KBD) {
-		printk("Keyboard interface test failed[1]: %02x\n", stat);
+		printk("PS/2 interface test failed[1]: %02x\n", stat);
 		ret = -ENODEV;
 	}
 
-	stat = ps2_test_one(sa, 0);
+	stat = ps2_test_one(ps2if, 0);
 	if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
-		printk("Keyboard interface test failed[2]: %02x\n", stat);
+		printk("PS/2 interface test failed[2]: %02x\n", stat);
 		ret = -ENODEV;
 	}
 
-	stat = ps2_test_one(sa, PS2CR_FKD);
+	stat = ps2_test_one(ps2if, PS2CR_FKD);
 	if (stat != PS2STAT_KBC) {
-		printk("Keyboard interface test failed[3]: %02x\n", stat);
+		printk("PS/2 interface test failed[3]: %02x\n", stat);
 		ret = -ENODEV;
 	}
 
-	sa1111_writel(0, sa->base + SA1111_PS2CR);
+	sa1111_writel(0, ps2if->base + SA1111_PS2CR);
 
 	return ret;
 }
 
 /*
- * Initialise one PS/2 port.
+ * Add one device to this driver.
  */
-static int __init ps2_init_one(struct sa1111_device *dev, struct ps2if *sa)
+static int ps2_probe(struct device *dev)
 {
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+	struct ps2if *ps2if;
 	int ret;
 
+	ps2if = kmalloc(sizeof(struct ps2if), GFP_KERNEL);
+	if (!ps2if) {
+		return -ENOMEM;
+	}
+
+	memset(ps2if, 0, sizeof(struct ps2if));
+
+	ps2if->io.type		= SERIO_8042;
+	ps2if->io.write		= ps2_write;
+	ps2if->io.open		= ps2_open;
+	ps2if->io.close		= ps2_close;
+	ps2if->io.name		= dev->name;
+	ps2if->io.phys		= dev->bus_id;
+	ps2if->io.driver	= ps2if;
+	ps2if->dev		= sadev;
+	dev->driver_data	= ps2if;
+
+	spin_lock_init(&ps2if->lock);
+
 	/*
 	 * Request the physical region for this PS2 port.
 	 */
-	sa->res = request_mem_region(_SA1111(sa->base), 512, "ps2");
-	if (!sa->res)
-		return -EBUSY;
+	if (!request_mem_region(sadev->res.start,
+				sadev->res.end - sadev->res.start + 1,
+				SA1111_DRIVER_NAME(sadev))) {
+		ret = -EBUSY;
+		goto free;
+	}
 
 	/*
-	 * Convert the chip offset to virtual address.
+	 * Our parent device has already mapped the region.
 	 */
-	sa->base += (unsigned long)dev->base;
+	ps2if->base = (unsigned long)sadev->mapbase;
 
-	sa1111_enable_device(sa->skpcr_mask);
+	sa1111_enable_device(ps2if->dev);
 
 	/* Incoming clock is 8MHz */
-	sa1111_writel(0, sa->base + SA1111_PS2CLKDIV);
-	sa1111_writel(127, sa->base + SA1111_PS2PRECNT);
+	sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV);
+	sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT);
 
 	/*
 	 * Flush any pending input.
 	 */
-	ps2_clear_input(sa);
+	ps2_clear_input(ps2if);
 
 	/*
 	 * Test the keyboard interface.
 	 */
-	ret = ps2_test(sa);
+	ret = ps2_test(ps2if);
 	if (ret)
 		goto out;
 
 	/*
 	 * Flush any pending input.
 	 */
-	ps2_clear_input(sa);
-	sa1111_disable_device(sa->skpcr_mask);
+	ps2_clear_input(ps2if);
 
-	serio_register_port(&sa->io);
+	sa1111_disable_device(ps2if->dev);
+	serio_register_port(&ps2if->io);
 	return 0;
 
  out:
-	sa1111_disable_device(sa->skpcr_mask);
-	release_resource(sa->res);
+	sa1111_disable_device(ps2if->dev);
+	release_mem_region(sadev->res.start,
+			   sadev->res.end - sadev->res.start + 1);
+ free:
+	dev->driver_data = NULL;
+	kfree(ps2if);
 	return ret;
 }
 
 /*
- * Remove one PS/2 port.
+ * Remove one device from this driver.
  */
-static void __exit ps2_remove_one(struct ps2if *sa)
+static int ps2_remove(struct device *dev)
 {
-	serio_unregister_port(&sa->io);
- 	release_resource(sa->res);
+	struct ps2if *ps2if = dev->driver_data;
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+
+	serio_unregister_port(&ps2if->io);
+	release_mem_region(sadev->res.start,
+			   sadev->res.end - sadev->res.start + 1);
+	kfree(ps2if);
+
+	dev->driver_data = NULL;
+
+	return 0;
 }
 
-static struct ps2if ps2_kbd_port =
+/*
+ * We should probably do something here, but what?
+ */
+static int ps2_suspend(struct device *dev, u32 state, u32 level)
 {
-	io: {
-		type:		SERIO_8042,
-		write:		ps2_write,
-		open:		ps2_open,
-		close:		ps2_close,
-		name: 		"SA1111 PS/2 kbd port",
-		phys:		"sa1111/serio0",
-		driver:		&ps2_kbd_port,
-	},
-	base:		SA1111_KBD,
-	irq:		IRQ_TPRXINT,
-	skpcr_mask:	SKPCR_PTCLKEN,
-};
+	return 0;
+}
 
-static struct ps2if ps2_mse_port =
+static int ps2_resume(struct device *dev, u32 level)
 {
-	io: {
-		type:		SERIO_8042,
-		write:		ps2_write,
-		open:		ps2_open,
-		close:		ps2_close,
-		name: 		"SA1111 PS/2 mouse port",
-		phys:		"sa1111/serio1",
-		driver:		&ps2_mse_port,
+	return 0;
+}
+
+/*
+ * Our device driver structure
+ */
+static struct sa1111_driver ps2_driver = {
+	.drv = {
+		.name		= "SA1111 PS2",
+		.bus		= &sa1111_bus_type,
+		.probe		= ps2_probe,
+		.remove		= ps2_remove,
+		.suspend	= ps2_suspend,
+		.resume		= ps2_resume,
 	},
-	base:		SA1111_MSE,
-	irq:		IRQ_MSRXINT,
-	skpcr_mask:	SKPCR_PMCLKEN,
+	.devid			= SA1111_DEVID_PS2,
 };
 
 static int __init ps2_init(void)
 {
-	int ret = -ENODEV;
-
-	if (sa1111) {
-		ret = ps2_init_one(sa1111, &ps2_kbd_port);
-	}
-
-	return ret;
+	return driver_register(&ps2_driver.drv);
 }
 
 static void __exit ps2_exit(void)
 {
-	ps2_remove_one(&ps2_kbd_port);
+	remove_driver(&ps2_driver.drv);
 }
 
 module_init(ps2_init);
diff --git a/drivers/pcmcia/Config.in b/drivers/pcmcia/Config.in
index 56866d225c48eeba30dd8b6b71ee7ff3710748a0..4f8bfb54642450cf9e0c19f66bb815568616c3d5 100644
--- a/drivers/pcmcia/Config.in
+++ b/drivers/pcmcia/Config.in
@@ -20,6 +20,7 @@ if [ "$CONFIG_PCMCIA" != "n" ]; then
    fi
    if [ "$CONFIG_ARM" = "y" ]; then
       dep_tristate '  SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_PCMCIA
+      dep_tristate '  SA1111 support' CONFIG_PCMCIA_SA1111 $CONFIG_PCMCIA_SA1100 $CONFIG_SA1111 $CONFIG_PCMCIA
    fi
 fi
 
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 497523e5f7b1c6743f7d71322311435bebfcb04f..734b5a5cec438bae5d710dd16a2df275a9371fbc 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_I82092)			+= i82092.o
 obj-$(CONFIG_TCIC)			+= tcic.o
 obj-$(CONFIG_HD64465_PCMCIA)		+= hd64465_ss.o
 obj-$(CONFIG_PCMCIA_SA1100)		+= sa1100_cs.o
+obj-$(CONFIG_PCMCIA_SA1111)		+= sa1111_cs.o
 
 yenta_socket-objs				:= pci_socket.o yenta.o
 
@@ -21,26 +22,29 @@ pcmcia_core-objs-y				:= cistpl.o rsrc_mgr.o bulkmem.o cs.o
 pcmcia_core-objs-$(CONFIG_CARDBUS)		+= cardbus.o
 pcmcia_core-objs				:= $(pcmcia_core-objs-y)
 
+sa1111_cs-objs-y				:= sa1111_generic.o
+sa1111_cs-objs-$(CONFIG_SA1100_ADSBITSY)	+= sa1100_adsbitsy.o
+sa1111_cs-objs-$(CONFIG_ASSABET_NEPONSET)	+= sa1100_neponset.o
+sa1111_cs-objs-$(CONFIG_SA1100_BADGE4)		+= sa1100_badge4.o
+sa1111_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER)	+= sa1100_graphicsmaster.o
+sa1111_cs-objs-$(CONFIG_SA1100_JORNADA720)	+= sa1100_jornada720.o
+sa1111_cs-objs-$(CONFIG_SA1100_PFS168)		+= sa1100_pfs168.o
+sa1111_cs-objs-$(CONFIG_SA1100_PT_SYSTEM3)	+= sa1100_system3.o
+sa1111_cs-objs-$(CONFIG_SA1100_XP860)		+= sa1100_xp860.o
+sa1111_cs-objs					:= $(sa1111_cs-objs-y)
+
 sa1100_cs-objs-y				:= sa1100_generic.o
-sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY)	+= sa1100_adsbitsy.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_ASSABET)		+= sa1100_assabet.o
-sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET)	+= sa1100_neponset.o sa1111_generic.o
-sa1100_cs-objs-$(CONFIG_SA1100_BADGE4)		+= sa1100_badge4.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_CERF)		+= sa1100_cerf.o
 sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET)	+= sa1100_flexanet.o
 sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD)	+= sa1100_freebird.o
-sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSMASTER)	+= sa1100_graphicsmaster.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_GRAPHICSCLIENT)	+= sa1100_graphicsclient.o
 sa1100_cs-objs-$(CONFIG_SA1100_H3600)		+= sa1100_h3600.o
-sa1100_cs-objs-$(CONFIG_SA1100_JORNADA720)	+= sa1100_jornada720.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_PANGOLIN)	+= sa1100_pangolin.o
-sa1100_cs-objs-$(CONFIG_SA1100_PFS168)		+= sa1100_pfs168.o sa1111_generic.o
-sa1100_cs-objs-$(CONFIG_SA1100_PT_SYSTEM3)	+= sa1100_system3.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_SHANNON)		+= sa1100_shannon.o
 sa1100_cs-objs-$(CONFIG_SA1100_SIMPAD)		+= sa1100_simpad.o
 sa1100_cs-objs-$(CONFIG_SA1100_STORK)		+= sa1100_stork.o
 sa1100_cs-objs-$(CONFIG_SA1100_TRIZEPS) 	+= sa1100_trizeps.o
-sa1100_cs-objs-$(CONFIG_SA1100_XP860)		+= sa1100_xp860.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_YOPY)		+= sa1100_yopy.o
 sa1100_cs-objs					:= $(sa1100_cs-objs-y)
 
diff --git a/drivers/pcmcia/sa1100_adsbitsy.c b/drivers/pcmcia/sa1100_adsbitsy.c
index 17d87ab2e2a4ca7e14f582d2bf212040723715f4..202524e4c354d9328c095557cc753836b2195eba 100644
--- a/drivers/pcmcia/sa1100_adsbitsy.c
+++ b/drivers/pcmcia/sa1100_adsbitsy.c
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 
 #include "sa1100_generic.h"
 #include "sa1111_generic.h"
diff --git a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1100_badge4.c
index 546ec5d95102725062d5907780177f3f4339594d..77174645d6c1155d38d6478624d3e30f0758f0ae 100644
--- a/drivers/pcmcia/sa1100_badge4.c
+++ b/drivers/pcmcia/sa1100_badge4.c
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 #include <asm/arch/badge4.h>
 #include <asm/hardware/sa1111.h>
 
diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
index 12dc9270e402757e05189c7748cf9b19229e1ff4..47f364fbc97b0988b8e950c261ecaa12f7fba9d2 100644
--- a/drivers/pcmcia/sa1100_generic.c
+++ b/drivers/pcmcia/sa1100_generic.c
@@ -110,12 +110,11 @@ sa1100_pcmcia_default_mecr_timing(unsigned int sock, unsigned int cpu_speed,
  * Call board specific BS value calculation to allow boards
  * to tweak the BS values.
  */
-static int sa1100_pcmcia_set_mecr(int sock)
+static int sa1100_pcmcia_set_mecr(int sock, unsigned int cpu_clock)
 {
 	struct sa1100_pcmcia_socket *skt;
 	u32 mecr;
-	int clock;
-	long flags;
+	unsigned long flags;
 	unsigned int bs;
 
 	if (sock < 0 || sock > SA1100_PCMCIA_MAX_SOCK)
@@ -125,8 +124,7 @@ static int sa1100_pcmcia_set_mecr(int sock)
 
 	local_irq_save(flags);
 
-	clock = cpufreq_get(0);
-	bs = pcmcia_low_level->socket_get_timing(sock, clock, skt->speed_io);
+	bs = pcmcia_low_level->socket_get_timing(sock, cpu_clock, skt->speed_io);
 
 	mecr = MECR;
 	MECR_FAST_SET(mecr, sock, 0);
@@ -652,7 +650,7 @@ sa1100_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map)
     if ( map->speed == 0)
        map->speed = SA1100_PCMCIA_IO_ACCESS;
 
-	sa1100_pcmcia_set_mecr( sock );
+	sa1100_pcmcia_set_mecr(sock, cpufreq_get(0));
   }
 
   if (map->stop == 1)
@@ -741,7 +739,7 @@ sa1100_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map)
 			  map->speed = SA1100_PCMCIA_5V_MEM_ACCESS;
 	  }
 
-	  sa1100_pcmcia_set_mecr( sock );
+	  sa1100_pcmcia_set_mecr(sock, cpufreq_get(0));
 
   }
 
@@ -885,9 +883,8 @@ static void sa1100_pcmcia_update_mecr(unsigned int clock)
 {
 	unsigned int sock;
 
-	for (sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock) {
-		sa1100_pcmcia_set_mecr(sock);
-	}
+	for (sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock)
+		sa1100_pcmcia_set_mecr(sock, clock);
 }
 
 /* sa1100_pcmcia_notifier()
@@ -904,31 +901,31 @@ static int
 sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val,
 		       void *data)
 {
-  struct cpufreq_info *ci = data;
-
-  switch (val) {
-  case CPUFREQ_PRECHANGE:
-    if (ci->new_freq > ci->old_freq) {
-      DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n",
-	    __FUNCTION__,
-	    ci->new_freq / 1000, (ci->new_freq / 100) % 10,
-	    ci->old_freq / 1000, (ci->old_freq / 100) % 10);
-      sa1100_pcmcia_update_mecr(ci->new_freq);
-    }
-    break;
-
-  case CPUFREQ_POSTCHANGE:
-    if (ci->new_freq < ci->old_freq) {
-      DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n",
-	    __FUNCTION__,
-	    ci->new_freq / 1000, (ci->new_freq / 100) % 10,
-	    ci->old_freq / 1000, (ci->old_freq / 100) % 10);
-      sa1100_pcmcia_update_mecr(ci->new_freq);
-    }
-    break;
-  }
+	struct cpufreq_freqs *freqs = data;
+
+	switch (val) {
+	case CPUFREQ_PRECHANGE:
+		if (freqs->new > freqs->old) {
+			DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, "
+				"pre-updating\n", __FUNCTION__,
+			    freqs->new / 1000, (freqs->new / 100) % 10,
+			    freqs->old / 1000, (freqs->old / 100) % 10);
+			sa1100_pcmcia_update_mecr(freqs->new);
+		}
+		break;
+
+	case CPUFREQ_POSTCHANGE:
+		if (freqs->new < freqs->old) {
+			DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, "
+				"post-updating\n", __FUNCTION__,
+			    freqs->new / 1000, (freqs->new / 100) % 10,
+			    freqs->old / 1000, (freqs->old / 100) % 10);
+			sa1100_pcmcia_update_mecr(freqs->new);
+		}
+		break;
+	}
 
-  return 0;
+	return 0;
 }
 
 static struct notifier_block sa1100_pcmcia_notifier_block = {
@@ -946,7 +943,7 @@ int sa1100_register_pcmcia(struct pcmcia_low_level *ops)
 	struct pcmcia_init pcmcia_init;
 	struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
 	struct pcmcia_state_array state_array;
-	unsigned int i;
+	unsigned int i, cpu_clock;
 	int ret;
 
 	/*
@@ -986,6 +983,8 @@ int sa1100_register_pcmcia(struct pcmcia_low_level *ops)
 		goto shutdown;
 	}
 
+	cpu_clock = cpufreq_get(0);
+
 	/*
 	 * We initialize the MECR to default values here, because we are
 	 * not guaranteed to see a SetIOMap operation at runtime.
@@ -1019,7 +1018,7 @@ int sa1100_register_pcmcia(struct pcmcia_low_level *ops)
 			goto out_err;
 		}
 
-		sa1100_pcmcia_set_mecr( i );
+		sa1100_pcmcia_set_mecr(i, cpu_clock);
 	}
 
 
@@ -1110,7 +1109,7 @@ static int __init sa1100_pcmcia_init(void)
 	servinfo_t info;
 	int ret, i;
 
-	printk(KERN_INFO "SA-1100 PCMCIA (CS release %s)\n", CS_RELEASE);
+	printk(KERN_INFO "SA11x0 PCMCIA (CS release %s)\n", CS_RELEASE);
 
 	CardServices(GetCardServicesInfo, &info);
 	if (info.Revision != CS_RELEASE_CODE) {
@@ -1127,7 +1126,8 @@ static int __init sa1100_pcmcia_init(void)
 	}
 
 #ifdef CONFIG_CPU_FREQ
-	ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block);
+	ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block,
+					CPUFREQ_TRANSITION_NOTIFIER);
 	if (ret < 0) {
 		printk(KERN_ERR "Unable to register CPU frequency change "
 			"notifier (%d)\n", ret);
@@ -1135,15 +1135,9 @@ static int __init sa1100_pcmcia_init(void)
 	}
 #endif
 
-#ifdef CONFIG_SA1100_ADSBITSY
-	pcmcia_adsbitsy_init();
-#endif
 #ifdef CONFIG_SA1100_ASSABET
 	pcmcia_assabet_init();
 #endif
-#ifdef CONFIG_SA1100_BADGE4
-	pcmcia_badge4_init();
-#endif
 #ifdef CONFIG_SA1100_CERF
 	pcmcia_cerf_init();
 #endif
@@ -1156,24 +1150,9 @@ static int __init sa1100_pcmcia_init(void)
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
 	pcmcia_gcplus_init();
 #endif
-#ifdef CONFIG_SA1100_GRAPHICSMASTER
-	pcmcia_graphicsmaster_init();
-#endif
-#ifdef CONFIG_SA1100_JORNADA720
-	pcmcia_jornada720_init();
-#endif
-#ifdef CONFIG_ASSABET_NEPONSET
-	pcmcia_neponset_init();
-#endif
 #ifdef CONFIG_SA1100_PANGOLIN
 	pcmcia_pangolin_init();
 #endif
-#ifdef CONFIG_SA1100_PFS168
-	pcmcia_pfs_init();
-#endif
-#ifdef CONFIG_SA1100_PT_SYSTEM3
-	pcmcia_system3_init();
-#endif
 #ifdef CONFIG_SA1100_SHANNON
 	pcmcia_shannon_init();
 #endif
@@ -1186,9 +1165,6 @@ static int __init sa1100_pcmcia_init(void)
 #ifdef CONFIG_SA1100_TRIZEPS
 	pcmcia_trizeps_init();
 #endif
-#ifdef CONFIG_SA1100_XP860
-	pcmcia_xp860_init();
-#endif
 #ifdef CONFIG_SA1100_YOPY
 	pcmcia_yopy_init();
 #endif
@@ -1203,15 +1179,9 @@ static int __init sa1100_pcmcia_init(void)
  */
 static void __exit sa1100_pcmcia_exit(void)
 {
-#ifdef CONFIG_SA1100_ADSBITSY
-	pcmcia_adsbitsy_exit();
-#endif
 #ifdef CONFIG_SA1100_ASSABET
 	pcmcia_assabet_exit();
 #endif
-#ifdef CONFIG_SA1100_BADGE4
-	pcmcia_badge4_exit();
-#endif
 #ifdef CONFIG_SA1100_CERF
 	pcmcia_cerf_exit();
 #endif
@@ -1224,21 +1194,9 @@ static void __exit sa1100_pcmcia_exit(void)
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
 	pcmcia_gcplus_exit();
 #endif
-#ifdef CONFIG_SA1100_GRAPHICSMASTER
-	pcmcia_graphicsmaster_exit();
-#endif
-#ifdef CONFIG_SA1100_JORNADA720
-	pcmcia_jornada720_exit();
-#endif
-#ifdef CONFIG_ASSABET_NEPONSET
-	pcmcia_neponset_exit();
-#endif
 #ifdef CONFIG_SA1100_PANGOLIN
 	pcmcia_pangolin_exit();
 #endif
-#ifdef CONFIG_SA1100_PFS168
-	pcmcia_pfs_exit();
-#endif
 #ifdef CONFIG_SA1100_SHANNON
 	pcmcia_shannon_exit();
 #endif
@@ -1248,9 +1206,6 @@ static void __exit sa1100_pcmcia_exit(void)
 #ifdef CONFIG_SA1100_STORK
 	pcmcia_stork_exit();
 #endif
-#ifdef CONFIG_SA1100_XP860
-	pcmcia_xp860_exit();
-#endif
 #ifdef CONFIG_SA1100_YOPY
 	pcmcia_yopy_exit();
 #endif
@@ -1261,7 +1216,7 @@ static void __exit sa1100_pcmcia_exit(void)
 	}
 
 #ifdef CONFIG_CPU_FREQ
-	cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block);
+	cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
 #endif
 }
 
diff --git a/drivers/pcmcia/sa1100_graphicsmaster.c b/drivers/pcmcia/sa1100_graphicsmaster.c
index 8aeafa167cffa12ee6136612fd43024f776ef43e..9552dfa55816753ff20d1f0571df12cf1f438035 100644
--- a/drivers/pcmcia/sa1100_graphicsmaster.c
+++ b/drivers/pcmcia/sa1100_graphicsmaster.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 
 #include "sa1100_generic.h"
 #include "sa1111_generic.h"
diff --git a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1100_jornada720.c
index a5d07781929a5b27b2659b5f4bc1fee5d3d038f6..d15f51eedba68bbfda2a974b4b9f76ced133fc5c 100644
--- a/drivers/pcmcia/sa1100_jornada720.c
+++ b/drivers/pcmcia/sa1100_jornada720.c
@@ -9,6 +9,7 @@
 #include <linux/init.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 
 #include "sa1100_generic.h"
 #include "sa1111_generic.h"
diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c
index d8e09192bacc15116532dead9eee20cf64a4c068..12d6c5c662f967f3d9431970ee92a64477c1cb91 100644
--- a/drivers/pcmcia/sa1100_neponset.c
+++ b/drivers/pcmcia/sa1100_neponset.c
@@ -10,21 +10,50 @@
 #include <linux/init.h>
 
 #include <asm/hardware.h>
-#include <asm/arch/assabet.h>
+#include <asm/mach-types.h>
+#include <asm/arch/neponset.h>
 #include <asm/hardware/sa1111.h>
 
 #include "sa1100_generic.h"
 #include "sa1111_generic.h"
 
+/*
+ * Neponset uses the Maxim MAX1600, with the following connections:
+ *
+ *   MAX1600      Neponset
+ *
+ *    A0VCC        SA-1111 GPIO A<1>
+ *    A1VCC        SA-1111 GPIO A<0>
+ *    A0VPP        CPLD NCR A0VPP
+ *    A1VPP        CPLD NCR A1VPP
+ *    B0VCC        SA-1111 GPIO A<2>
+ *    B1VCC        SA-1111 GPIO A<3>
+ *    B0VPP        ground (slot B is CF)
+ *    B1VPP        ground (slot B is CF)
+ *
+ *     VX          VCC (5V)
+ *     VY          VCC3_3 (3.3V)
+ *     12INA       12V
+ *     12INB       ground (slot B is CF)
+ *
+ * The MAX1600 CODE pin is tied to ground, placing the device in 
+ * "Standard Intel code" mode. Refer to the Maxim data sheet for
+ * the corresponding truth table.
+ */
+
 static int neponset_pcmcia_init(struct pcmcia_init *init)
 {
-	/* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
-	PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
-
-	/* MAX1600 to standby mode: */
-	PA_DWR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
 	NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP);
 
+	/*
+	 * Set GPIO_A<3:0> to be outputs for the MAX1600,
+	 * and switch to standby mode.
+	 */
+	PA_DDR = 0;
+	PA_SDR = 0;
+	PA_DWR = 0;
+	PA_SSR = 0;
+
 	return sa1111_pcmcia_init(init);
 }
 
@@ -35,29 +64,6 @@ neponset_pcmcia_configure_socket(const struct pcmcia_configure *conf)
 	unsigned int ncr_set, pa_dwr_set;
 	int ret;
 
-	/* Neponset uses the Maxim MAX1600, with the following connections:
-
-	 *   MAX1600      Neponset
-	 *
-	 *    A0VCC        SA-1111 GPIO A<1>
-	 *    A1VCC        SA-1111 GPIO A<0>
-	 *    A0VPP        CPLD NCR A0VPP
-	 *    A1VPP        CPLD NCR A1VPP
-	 *    B0VCC        SA-1111 GPIO A<2>
-	 *    B1VCC        SA-1111 GPIO A<3>
-	 *    B0VPP        ground (slot B is CF)
-	 *    B1VPP        ground (slot B is CF)
-	 *
-	 *     VX          VCC (5V)
-	 *     VY          VCC3_3 (3.3V)
-	 *     12INA       12V
-	 *     12INB       ground (slot B is CF)
-	 *
-	 * The MAX1600 CODE pin is tied to ground, placing the device in 
-	 * "Standard Intel code" mode. Refer to the Maxim data sheet for
-	 * the corresponding truth table.
-	 */
-
 	switch (conf->sock) {
 	case 0:
 		pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
@@ -135,13 +141,13 @@ int __init pcmcia_neponset_init(void)
 {
 	int ret = -ENODEV;
 
-	if (machine_is_assabet() && sa1111)
+	if (machine_is_assabet())
 		ret = sa1100_register_pcmcia(&neponset_pcmcia_ops);
 
 	return ret;
 }
 
-void __exit pcmcia_neponset_exit(void)
+void __devexit pcmcia_neponset_exit(void)
 {
 	sa1100_unregister_pcmcia(&neponset_pcmcia_ops);
 }
diff --git a/drivers/pcmcia/sa1100_pfs168.c b/drivers/pcmcia/sa1100_pfs168.c
index af028d61b6822fb40494c3cac5450e00299eceb4..511afebb38a38dad07d00e47cd92829bbcbcf134 100644
--- a/drivers/pcmcia/sa1100_pfs168.c
+++ b/drivers/pcmcia/sa1100_pfs168.c
@@ -7,10 +7,11 @@
  */
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 
-#include <asm/delay.h>
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 #include <asm/irq.h>
 
 #include "sa1100_generic.h"
diff --git a/drivers/pcmcia/sa1100_system3.c b/drivers/pcmcia/sa1100_system3.c
index fc4bed07c4a32747acc3c3f3723a8b422b36f54c..2ab0d4537d0dcd8227dd6e8e73e3bd050a156cfa 100644
--- a/drivers/pcmcia/sa1100_system3.c
+++ b/drivers/pcmcia/sa1100_system3.c
@@ -31,6 +31,7 @@
 #include <linux/ioport.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 #include <asm/irq.h>
 #include <asm/hardware/sa1111.h>
 
diff --git a/drivers/pcmcia/sa1100_xp860.c b/drivers/pcmcia/sa1100_xp860.c
index 84c315a97cdd82fd45d997f4943f947dc24d6208..69d3f3ddaaccefb7f43bab5d4e257ad83d3c2b74 100644
--- a/drivers/pcmcia/sa1100_xp860.c
+++ b/drivers/pcmcia/sa1100_xp860.c
@@ -10,6 +10,7 @@
 #include <linux/init.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 #include <asm/irq.h>
 #include "sa1100_generic.h"
 
diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c
index ef5a8b0bf153a1e1d906895f91443aa4cfd8256f..9bf1a6c392d46bf3e48e9d9d8ef99a59b3af5b57 100644
--- a/drivers/pcmcia/sa1111_generic.c
+++ b/drivers/pcmcia/sa1111_generic.c
@@ -5,10 +5,12 @@
  * basically means we handle everything except controlling the
  * power.  Power is machine specific...
  */
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include <linux/init.h>
 
 #include <asm/hardware.h>
 #include <asm/hardware/sa1111.h>
@@ -21,19 +23,18 @@ static struct irqs {
 	int irq;
 	const char *str;
 } irqs[] = {
-	{ S0_CD_VALID,    "SA1111 PCMCIA card detect" },
-	{ S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1"        },
-	{ S1_CD_VALID,    "SA1111 CF card detect"     },
-	{ S1_BVD1_STSCHG, "SA1111 CF BVD1"            },
+	{ IRQ_S0_CD_VALID,    "SA1111 PCMCIA card detect" },
+	{ IRQ_S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1"        },
+	{ IRQ_S1_CD_VALID,    "SA1111 CF card detect"     },
+	{ IRQ_S1_BVD1_STSCHG, "SA1111 CF BVD1"            },
 };
 
+static struct sa1111_dev *pcmcia;
+
 int sa1111_pcmcia_init(struct pcmcia_init *init)
 {
 	int i, ret;
 
-	if (!request_mem_region(_PCCR, 512, "PCMCIA"))
-		return -1;
-
 	for (i = ret = 0; i < ARRAY_SIZE(irqs); i++) {
 		set_irq_type(irqs[i].irq, IRQT_FALLING);
 		ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
@@ -47,8 +48,6 @@ int sa1111_pcmcia_init(struct pcmcia_init *init)
 			irqs[i].irq, ret);
 		while (i--)
 			free_irq(irqs[i].irq, NULL);
-
-		release_mem_region(_PCCR, 16);
 	}
 
 	return ret ? -1 : 2;
@@ -61,8 +60,6 @@ int sa1111_pcmcia_shutdown(void)
 	for (i = 0; i < ARRAY_SIZE(irqs); i++)
 		free_irq(irqs[i].irq, NULL);
 
-	release_mem_region(_PCCR, 512);
-
 	return 0;
 }
 
@@ -73,7 +70,7 @@ int sa1111_pcmcia_socket_state(struct pcmcia_state_array *state)
 	if (state->size < 2)
 		return -1;
 
-	status = PCSR;
+	status = sa1111_readl(pcmcia->mapbase + SA1111_PCSR);
 
 	state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1;
 	state->state[0].ready  = status & PCSR_S0_READY  ? 1 : 0;
@@ -99,8 +96,8 @@ int sa1111_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
 	int ret = 0;
 
 	switch (info->sock) {
-	case 0:	info->irq = S0_READY_NINT;	break;
-	case 1: info->irq = S1_READY_NINT;	break;
+	case 0:	info->irq = IRQ_S0_READY_NINT;	break;
+	case 1: info->irq = IRQ_S1_READY_NINT;	break;
 	default: ret = 1;
 	}
 
@@ -109,7 +106,7 @@ int sa1111_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
 
 int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *conf)
 {
-	unsigned int rst, flt, wait, pse, irq, pccr_mask;
+	unsigned int rst, flt, wait, pse, irq, pccr_mask, val;
 	unsigned long flags;
 
 	switch (conf->sock) {
@@ -118,7 +115,7 @@ int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *conf)
 		flt = PCCR_S0_FLT;
 		wait = PCCR_S0_PWAITEN;
 		pse = PCCR_S0_PSE;
-		irq = S0_READY_NINT;
+		irq = IRQ_S0_READY_NINT;
 		break;
 
 	case 1:
@@ -126,7 +123,7 @@ int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *conf)
 		flt = PCCR_S1_FLT;
 		wait = PCCR_S1_PWAITEN;
 		pse = PCCR_S1_PSE;
-		irq = S1_READY_NINT;
+		irq = IRQ_S1_READY_NINT;
 		break;
 
 	default:
@@ -159,7 +156,9 @@ int sa1111_pcmcia_configure_socket(const struct pcmcia_configure *conf)
 		pccr_mask |= flt;
 
 	local_irq_save(flags);
-	PCCR = (PCCR & ~(pse | flt | wait | rst)) | pccr_mask;
+	val = sa1111_readl(pcmcia->mapbase + SA1111_PCCR);
+	val = (val & ~(pse | flt | wait | rst)) | pccr_mask;
+	sa1111_writel(val, pcmcia->mapbase + SA1111_PCCR);
 	local_irq_restore(flags);
 
 	if (conf->irq)
@@ -179,3 +178,130 @@ int sa1111_pcmcia_socket_suspend(int sock)
 {
 	return 0;
 }
+
+static int pcmcia_probe(struct device *dev)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+	unsigned long flags;
+	char *base;
+
+	local_irq_save(flags);
+	if (pcmcia) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+
+	pcmcia = sadev;
+	local_irq_restore(flags);
+
+	if (!request_mem_region(sadev->res.start, 512,
+				SA1111_DRIVER_NAME(sadev)))
+		return -EBUSY;
+
+	base = sadev->mapbase;
+
+	/*
+	 * Initialise the suspend state.
+	 */
+	sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + SA1111_PCSSR);
+	sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + SA1111_PCCR);
+
+#ifdef CONFIG_SA1100_ADSBITSY
+	pcmcia_adsbitsy_init();
+#endif
+#ifdef CONFIG_SA1100_BADGE4
+	pcmcia_badge4_init();
+#endif
+#ifdef CONFIG_SA1100_GRAPHICSMASTER
+	pcmcia_graphicsmaster_init();
+#endif
+#ifdef CONFIG_SA1100_JORNADA720
+	pcmcia_jornada720_init();
+#endif
+#ifdef CONFIG_ASSABET_NEPONSET
+	pcmcia_neponset_init();
+#endif
+#ifdef CONFIG_SA1100_PFS168
+	pcmcia_pfs_init();
+#endif
+#ifdef CONFIG_SA1100_PT_SYSTEM3
+	pcmcia_system3_init();
+#endif
+#ifdef CONFIG_SA1100_XP860
+	pcmcia_xp860_init();
+#endif
+	return 0;
+}
+
+static int __devexit pcmcia_remove(struct device *dev)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+
+#ifdef CONFIG_SA1100_ADSBITSY
+	pcmcia_adsbitsy_exit();
+#endif
+#ifdef CONFIG_SA1100_BADGE4
+	pcmcia_badge4_exit();
+#endif
+#ifdef CONFIG_SA1100_GRAPHICSMASTER
+	pcmcia_graphicsmaster_exit();
+#endif
+#ifdef CONFIG_SA1100_JORNADA720
+	pcmcia_jornada720_exit();
+#endif
+#ifdef CONFIG_ASSABET_NEPONSET
+	pcmcia_neponset_exit();
+#endif
+#ifdef CONFIG_SA1100_PFS168
+	pcmcia_pfs_exit();
+#endif
+#ifdef CONFIG_SA1100_PT_SYSTEM3
+	pcmcia_system3_exit();
+#endif
+#ifdef CONFIG_SA1100_XP860
+	pcmcia_xp860_exit();
+#endif
+
+	release_mem_region(sadev->res.start, 512);
+	pcmcia = NULL;
+
+	return 0;
+}
+
+static int pcmcia_suspend(struct device *dev, u32 state, u32 level)
+{
+	return 0;
+}
+
+static int pcmcia_resume(struct device *dev, u32 level)
+{
+	return 0;
+}
+
+static struct sa1111_driver pcmcia_driver = {
+	.drv = {
+		.name		= "SA1111 PCMCIA",
+		.bus		= &sa1111_bus_type,
+		.probe		= pcmcia_probe,
+		.remove		= __devexit_p(pcmcia_remove),
+		.suspend	= pcmcia_suspend,
+		.resume		= pcmcia_resume,
+	},
+	.devid			= SA1111_DEVID_PCMCIA,
+};
+
+static int __init sa1111_drv_pcmcia_init(void)
+{
+	return driver_register(&pcmcia_driver.drv);
+}
+
+static void __exit sa1111_drv_pcmcia_exit(void)
+{
+	remove_driver(&pcmcia_driver.drv);
+}
+
+module_init(sa1111_drv_pcmcia_init);
+module_exit(sa1111_drv_pcmcia_exit);
+
+MODULE_DESCRIPTION("SA1111 PCMCIA card socket driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index f7b44577a7a0e4c40cbe6f148bab10dae331093d..a6a34619d4fd930e63a9a58b17f0d9472528c2c3 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -25,7 +25,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void sa1111_start_hc(void)
+static void sa1111_start_hc(struct sa1111_dev *dev)
 {
 	unsigned int usb_rst = 0;
 
@@ -48,31 +48,35 @@ static void sa1111_start_hc(void)
 	 * Configure the power sense and control lines.  Place the USB
 	 * host controller in reset.
 	 */
-	USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
+	sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+		      dev->mapbase + SA1111_USB_RESET);
 
 	/*
 	 * Now, carefully enable the USB clock, and take
 	 * the USB host controller out of reset.
 	 */
-	SKPCR |= SKPCR_UCLKEN;
+	sa1111_enable_device(dev);
 	udelay(11);
-	USB_RESET = usb_rst;
+	sa1111_writel(usb_rst, dev->mapbase + SA1111_USB_RESET);
 }
 
-static void sa1111_stop_hc(void)
+static void sa1111_stop_hc(struct sa1111_dev *dev)
 {
+	unsigned int usb_rst;
 	printk(KERN_DEBUG __FILE__ 
 	       ": stopping SA-1111 OHCI USB Controller\n");
 
 	/*
 	 * Put the USB host controller into reset.
 	 */
-	USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
+	usb_rst = sa1111_readl(dev->mapbase + SA1111_USB_RESET);
+	sa1111_writel(usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET,
+		      dev->mapbase + SA1111_USB_RESET);
 
 	/*
 	 * Stop the USB clock.
 	 */
-	SKPCR &= ~SKPCR_UCLKEN;
+	sa1111_disable_device(dev);
 
 #ifdef CONFIG_SA1100_BADGE4
 	if (machine_is_badge4()) {
@@ -86,9 +90,9 @@ static void sa1111_stop_hc(void)
 /*-------------------------------------------------------------------------*/
 
 #if 0
-static void dump_hci_status(const char *label)
+static void dump_hci_status(struct usb_hcd *hcd, const char *label)
 {
-	unsigned long status = USB_STATUS; 
+	unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS);
 
 	dbg ("%s USB_STATUS = { %s%s%s%s%s}", label,
 	     ((status & USB_STATUS_IRQHCIRMTWKUP) ? "IRQHCIRMTWKUP " : ""),
@@ -101,11 +105,14 @@ static void dump_hci_status(const char *label)
 
 static void usb_hcd_sa1111_hcim_irq (int irq, void *__hcd, struct pt_regs * r)
 {
-	//dump_hci_status("irq");
+	struct usb_hcd *hcd = __hcd;
+//	unsigned long status = sa1111_readl(hcd->regs + SA1111_USB_STATUS);
+
+	//dump_hci_status(hcd, "irq");
 
 #if 0
 	/* may work better this way -- need to investigate further */
-	if (USB_STATUS & USB_STATUS_NIRQHCIM) {
+	if (status & USB_STATUS_NIRQHCIM) {
 		//dbg ("not normal HC interrupt; ignoring");
 		return;
 	}
@@ -116,7 +123,7 @@ static void usb_hcd_sa1111_hcim_irq (int irq, void *__hcd, struct pt_regs * r)
 
 /*-------------------------------------------------------------------------*/
 
-void usb_hcd_sa1111_remove (struct usb_hcd *);
+void usb_hcd_sa1111_remove (struct usb_hcd *, struct sa1111_dev *);
 
 /* configure so an HC device and id are always provided */
 /* always called with process context; sleeping is OK */
@@ -132,21 +139,20 @@ void usb_hcd_sa1111_remove (struct usb_hcd *);
  *
  * Store this function in the HCD's struct pci_driver as probe().
  */
-int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_out)
+int usb_hcd_sa1111_probe (const struct hc_driver *driver,
+			  struct usb_hcd **hcd_out,
+			  struct sa1111_dev *dev)
 {
 	int retval;
 	struct usb_hcd *hcd = 0;
 
-	if (!sa1111)
-		return -ENODEV;
-
-	if (!request_mem_region(_USB_OHCI_OP_BASE, 
-				_USB_EXTENT, hcd_name)) {
+	if (!request_mem_region(dev->res.start, 
+				dev->res.end - dev->res.start + 1, hcd_name)) {
 		dbg("request_mem_region failed");
 		return -EBUSY;
 	}
 
-	sa1111_start_hc();
+	sa1111_start_hc(dev);
 
 	hcd = driver->hcd_alloc ();
 	if (hcd == NULL){
@@ -157,9 +163,10 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o
 
 	hcd->driver = (struct hc_driver *) driver;
 	hcd->description = driver->description;
-	hcd->irq = NIRQHCIM;
-	hcd->regs = (void *) &USB_OHCI_OP_BASE;
+	hcd->irq = dev->irq[1];
+	hcd->regs = dev->mapbase;
 	hcd->pdev = SA1111_FAKE_PCIDEV;
+	hcd->parent = &dev->dev;
 
 	retval = hcd_buffer_create (hcd);
 	if (retval != 0) {
@@ -167,8 +174,8 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o
 		goto err1;
 	}
 
-	set_irq_type(NIRQHCIM, IRQT_RISING);
-	retval = request_irq (NIRQHCIM, usb_hcd_sa1111_hcim_irq, SA_INTERRUPT,
+	set_irq_type(hcd->irq, IRQT_RISING);
+	retval = request_irq (hcd->irq, usb_hcd_sa1111_hcim_irq, SA_INTERRUPT,
 			      hcd->description, hcd);
 	if (retval != 0) {
 		dbg("request_irq failed");
@@ -191,7 +198,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o
 
 	if ((retval = driver->start (hcd)) < 0) 
 	{
-		usb_hcd_sa1111_remove(hcd);
+		usb_hcd_sa1111_remove(hcd, dev);
 		return retval;
 	}
 
@@ -202,8 +209,8 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o
 	hcd_buffer_destroy (hcd);
 	if (hcd) driver->hcd_free(hcd);
  err1:
-	sa1111_stop_hc();
-	release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
+	sa1111_stop_hc(dev);
+	release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1);
 	return retval;
 }
 
@@ -221,7 +228,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, struct usb_hcd **hcd_o
  * context, normally "rmmod", "apmd", or something similar.
  *
  */
-void usb_hcd_sa1111_remove (struct usb_hcd *hcd)
+void usb_hcd_sa1111_remove (struct usb_hcd *hcd, struct sa1111_dev *dev)
 {
 	struct usb_device	*hub;
 	void *base;
@@ -244,13 +251,13 @@ void usb_hcd_sa1111_remove (struct usb_hcd *hcd)
 
 	usb_deregister_bus (&hcd->self);
 	if (atomic_read (&hcd->self.refcnt) != 1)
-		err (__FUNCTION__ ": %s, count != 1", hcd->self.bus_name);
+		err ("%s: %s, count != 1", __FUNCTION__, hcd->self.bus_name);
 
 	base = hcd->regs;
 	hcd->driver->hcd_free (hcd);
 
-	sa1111_stop_hc();
-	release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
+	sa1111_stop_hc(dev);
+	release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -275,7 +282,7 @@ ohci_sa1111_start (struct usb_hcd *hcd)
 	}
 	ohci->regs = hcd->regs;
 
-	ohci->parent_dev = &sa1111->dev;
+	ohci->parent_dev = hcd->parent;
 
 	if (hc_reset (ohci) < 0) {
 		ohci_stop (hcd);
@@ -342,26 +349,67 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
 
 /*-------------------------------------------------------------------------*/
 
-/* Only one SA-1111 ever exists. */
-static struct usb_hcd *the_sa1111_hcd;
+static int ohci_hcd_sa1111_drv_probe(struct device *_dev)
+{
+	struct sa1111_dev *dev = SA1111_DEV(_dev);
+	struct usb_hcd *hcd = NULL;
+	int ret;
+
+	ret = usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, &hcd, dev);
+
+	if (ret == 0)
+		dev->dev.driver_data = hcd;
+
+	return ret;
+}
+
+static int ohci_hcd_sa1111_drv_remove(struct device *_dev)
+{
+	struct sa1111_dev *dev = SA1111_DEV(_dev);
+	struct usb_hcd *hcd = dev->dev.driver_data;
+
+	usb_hcd_sa1111_remove(hcd, dev);
+
+	dev->dev.driver_data = NULL;
 
-static int __init ohci_hcd_sa1111_init (void) 
+	return 0;
+}
+
+static int ohci_hcd_sa1111_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+	return 0;
+}
+
+static int ohci_hcd_sa1111_drv_resume(struct device *dev, u32 level)
+{
+	return 0;
+}
+
+static struct sa1111_driver ohci_hcd_sa1111_driver = {
+	.drv = {
+		.name		= "SA1111 OHCI",
+		.bus		= &sa1111_bus_type,
+		.probe		= ohci_hcd_sa1111_drv_probe,
+		.remove		= ohci_hcd_sa1111_drv_remove,
+		.suspend	= ohci_hcd_sa1111_drv_suspend,
+		.resume		= ohci_hcd_sa1111_drv_resume,
+	},
+	.devid			= SA1111_DEVID_USB,
+};
+
+static int __init ohci_hcd_sa1111_init (void)
 {
 	dbg (DRIVER_INFO " (SA-1111)");
 	dbg ("block sizes: ed %d td %d",
 		sizeof (struct ed), sizeof (struct td));
 
-	the_sa1111_hcd = 0;
-	return usb_hcd_sa1111_probe(&ohci_sa1111_hc_driver, &the_sa1111_hcd);
+	return driver_register(&ohci_hcd_sa1111_driver.drv);
 }
-module_init (ohci_hcd_sa1111_init);
 
-
-static void __exit ohci_hcd_sa1111_cleanup (void) 
-{	
-	if (the_sa1111_hcd) {
-		usb_hcd_sa1111_remove(the_sa1111_hcd);
-		the_sa1111_hcd = 0;
-	}
+static void __exit ohci_hcd_sa1111_cleanup (void)
+{
+	remove_driver(&ohci_hcd_sa1111_driver.drv);
 }
+
+module_init (ohci_hcd_sa1111_init);
 module_exit (ohci_hcd_sa1111_cleanup);
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index 6a2683bba658b879e2786528dee9d3a340ff66fe..17b1c02cb38adee3ddb781fbe3e549952ce98136 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -209,17 +209,17 @@ extern void (*sa1100fb_lcd_power)(int on);
  * IMHO this looks wrong.  In 8BPP, length should be 8.
  */
 static struct sa1100fb_rgb rgb_8 = {
-	red:	{ offset: 0,  length: 4, },
-	green:	{ offset: 0,  length: 4, },
-	blue:	{ offset: 0,  length: 4, },
-	transp:	{ offset: 0,  length: 0, },
+	.red	= { .offset = 0,  .length = 4, },
+	.green	= { .offset = 0,  .length = 4, },
+	.blue	= { .offset = 0,  .length = 4, },
+	.transp	= { .offset = 0,  .length = 0, },
 };
 
 static struct sa1100fb_rgb def_rgb_16 = {
-	red:	{ offset: 11, length: 5, },
-	green:	{ offset: 5,  length: 6, },
-	blue:	{ offset: 0,  length: 5, },
-	transp:	{ offset: 0,  length: 0, },
+	.red	= { .offset = 11, .length = 5, },
+	.green	= { .offset = 5,  .length = 6, },
+	.blue	= { .offset = 0,  .length = 5, },
+	.transp	= { .offset = 0,  .length = 0, },
 };
 
 #ifdef CONFIG_SA1100_ASSABET
@@ -230,155 +230,151 @@ static struct sa1100fb_rgb def_rgb_16 = {
  * instead (def_rgb_16).
  */
 static struct sa1100fb_mach_info lq039q2ds54_info __initdata = {
-	pixclock:	171521,		bpp:		16,
-	xres:		320,		yres:		240,
+	.pixclock	= 171521,	.bpp		= 16,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	5,		vsync_len:	1,
-	left_margin:	61,		upper_margin:	3,
-	right_margin:	9,		lower_margin:	0,
+	.hsync_len	= 5,		.vsync_len	= 1,
+	.left_margin	= 61,		.upper_margin	= 3,
+	.right_margin	= 9,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
 };
 #else
 static struct sa1100fb_mach_info pal_info __initdata = {
-	pixclock:	67797,		bpp:		16,
-	xres:		640,		yres:		512,
+	.pixclock	= 67797,	.bpp		= 16,
+	.xres		= 640,		.yres		= 512,
 
-	hsync_len:	64,		vsync_len:	6,
-	left_margin:	125,		upper_margin:	70,
-	right_margin:	115,		lower_margin:	36,
+	.hsync_len	= 64,		.vsync_len	= 6,
+	.left_margin	= 125,		.upper_margin	= 70,
+	.right_margin	= 115,		.lower_margin	= 36,
 
-	sync:		0,
-
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg |	LCCR3_ACBsDiv(512),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
 };
 #endif
 #endif
 
 #ifdef CONFIG_SA1100_H3800
 static struct sa1100fb_mach_info h3800_info __initdata = {
-	pixclock:	174757, 	bpp:		16,
-	xres:		320,		yres:		240,
+	.pixclock	= 174757, 	.bpp		= 16,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	3,		vsync_len:	3,
-	left_margin:	12,		upper_margin:	10,
-	right_margin:	17,		lower_margin:	1,
+	.hsync_len	= 3,		.vsync_len	= 3,
+	.left_margin	= 12,		.upper_margin	= 10,
+	.right_margin	= 17,		.lower_margin	= 1,
 
-	sync:		0,		cmap_static:	1,
+	.cmap_static	= 1,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_ACBsDiv(2) | LCCR3_PixRsEdg | LCCR3_OutEnH |
-			LCCR3_ACBsCntOff,
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
 };
 #endif
 
 #ifdef CONFIG_SA1100_H3600
 static struct sa1100fb_mach_info h3600_info __initdata = {
-	pixclock:	174757, 	bpp:		16,
-	xres:		320,		yres:		240,
+	.pixclock	= 174757, 	.bpp		= 16,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	3,		vsync_len:	3,
-	left_margin:	12,		upper_margin:	10,
-	right_margin:	17,		lower_margin:	1,
+	.hsync_len	= 3,		.vsync_len	= 3,
+	.left_margin	= 12,		.upper_margin	= 10,
+	.right_margin	= 17,		.lower_margin	= 1,
 
-	sync:		0,		cmap_static:	1,
+	.cmap_static	= 1,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_ACBsDiv(2) | LCCR3_PixRsEdg | LCCR3_OutEnH |
-			LCCR3_ACBsCntOff,
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
 };
 
 static struct sa1100fb_rgb h3600_rgb_16 = {
-	red:	{ offset: 12, length: 4, },
-	green:	{ offset: 7,  length: 4, },
-	blue:	{ offset: 1,  length: 4, },
-	transp: { offset: 0,  length: 0, },
+	.red	= { .offset = 12, .length = 4, },
+	.green	= { .offset = 7,  .length = 4, },
+	.blue	= { .offset = 1,  .length = 4, },
+	.transp	= { .offset = 0,  .length = 0, },
 };
 #endif
 
 #ifdef CONFIG_SA1100_H3100
 static struct sa1100fb_mach_info h3100_info __initdata = {
-	pixclock:	406977, 	bpp:		4,
-	xres:		320,		yres:		240,
+	.pixclock	= 406977, 	.bpp		= 4,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	26,		vsync_len:	41,
-	left_margin:	4,		upper_margin:	0,
-	right_margin:	4,		lower_margin:	0,
+	.hsync_len	= 26,		.vsync_len	= 41,
+	.left_margin	= 4,		.upper_margin	= 0,
+	.right_margin	= 4,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-	cmap_greyscale: 1,
-	cmap_inverse:	1,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.cmap_greyscale	= 1,
+	.cmap_inverse	= 1,
 
-	lccr0:		LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+	.lccr0		= LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
 };
 #endif
 
 #ifdef CONFIG_SA1100_BRUTUS
 static struct sa1100fb_mach_info brutus_info __initdata = {
-	pixclock:	0,		bpp:		8,
-	xres:		320,		yres:		240,
+	.pixclock	= 0,		.bpp		= 8,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	3,		vsync_len:	1,
-	left_margin:	41,		upper_margin:	0,
-	right_margin:	101,		lower_margin:	0,
+	.hsync_len	= 3,		.vsync_len	= 1,
+	.left_margin	= 41,		.upper_margin	= 0,
+	.right_margin	= 101,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
-			LCCR3_PixClkDiv(44),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
+			  LCCR3_PixClkDiv(44),
 };
 #endif
 
 #ifdef CONFIG_SA1100_CERF
 static struct sa1100fb_mach_info cerf_info __initdata = {
 #if defined(CONFIG_CERF_LCD_72_A)
-	pixclock:       171521,         bpp:            8,
-	xres:		640,		yres:		480,
-	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
-			LCCR3_PixClkDiv(38),
+	.pixclock	= 171521, 	.bpp		= 8,
+	.xres		= 640,		.yres		= 480,
+	.lccr0		= LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
+			  LCCR3_PixClkDiv(38),
 #elif defined(CONFIG_CERF_LCD_57_A)
-	pixclock:       171521,         bpp:            8,
-	xres:		320,		yres:		240,
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
-			LCCR3_PixClkDiv(38),
+	.pixclock	= 171521, 	.bpp		= 8,
+	.xres		= 320,		.yres		= 240,
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
+			  LCCR3_PixClkDiv(38),
 #elif defined(CONFIG_CERF_LCD_38_A)
-	pixclock:       171521,         bpp:            8,
-	xres:		240,		yres:		320,
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(56) |
-			LCCR3_PixClkDiv(38),
+	.pixclock	= 171521, 	.bpp		= 8,
+	.xres		= 240,		.yres		= 320,
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(56) |
+			  LCCR3_PixClkDiv(38),
 #elif defined(CONFIG_CERF_LCD_38_B)
-	pixclock:	171521, 	bpp:		4,
-	xres:		320,		yres:		240,
-	lccr0:		LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(56) |
-			LCCR3_PixClkDiv(38),
+	.pixclock	= 171521,	.bpp		= 4,
+	.xres		= 320,		.yres		= 240,
+	.lccr0		= LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(56) |
+			  LCCR3_PixClkDiv(38),
 #else
 #error "Must have a CerfBoard LCD form factor selected"
 #endif
 
-	hsync_len:	5,		vsync_len:	1,
-	left_margin:	61,		upper_margin:	3,
-	right_margin:	9,		lower_margin:	0,
+	.hsync_len	= 5,		.vsync_len	= 1,
+	.left_margin	= 61,		.upper_margin	= 3,
+	.right_margin	= 9,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
 };
 
 #if 0
 static struct sa1100fb_rgb cerf_rgb_16 = {
-	red:	{ offset: 8,	length: 4, },
-	green:	{ offset: 4,	length: 4, },
-	blue:	{ offset: 0,	length: 4, },
-	transp:	{ offset: 0,	length: 0, },
+	.red	= { .offset = 8,	.length = 4, },
+	.green	= { .offset = 4,	.length = 4, },
+	.blue	= { .offset = 0,	.length = 4, },
+	.transp	= { .offset = 0,	.length = 0, },
 };
 #endif
 #endif
@@ -386,56 +382,54 @@ static struct sa1100fb_rgb cerf_rgb_16 = {
 #ifdef CONFIG_SA1100_FREEBIRD
 #warning Please check this carefully
 static struct sa1100fb_mach_info freebird_info __initdata = {
-	pixclock:	171521,		bpp:		16,
-	xres:		240,		yres:		320,
+	.pixclock	= 171521,	.bpp		= 16,
+	.xres		= 240,		.yres		= 320,
 
-	hsync_len:	3,		vsync_len:	2,
-	left_margin:	2,		upper_margin:	0,
-	right_margin:	2,		lower_margin:	0,
+	.hsync_len	= 3,		.vsync_len	= 2,
+	.left_margin	= 2,		.upper_margin	= 0,
+	.right_margin	= 2,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(2),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(2),
 };
 
 static struct sa1100fb_rgb freebird_rgb_16 = {
-	red:	{ offset: 8,  length: 4, },
-	green:	{ offset: 4,  length: 4, },
-	blue:	{ offset: 0,  length: 4, },
-	transp:	{ offset: 12, length: 4, },
+	.red	= { .offset = 8,  .length = 4, },
+	.green	= { .offset = 4,  .length = 4, },
+	.blue	= { .offset = 0,  .length = 4, },
+	.transp	= { .offset = 12, .length = 4, },
 };
 #endif
 
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
 static struct sa1100fb_mach_info graphicsclient_info __initdata = {
-	pixclock:	53500,		bpp:		8,
-	xres:		640,		yres:		480,
-
-	hsync_len:	9,		vsync_len:	9,
-	left_margin:	54,		upper_margin:	24,
-	right_margin:	54,		lower_margin:	32,
+	.pixclock	= 53500,	.bpp		= 8,
+	.xres		= 640,		.yres		= 480,
 
-	sync:		0,
+	.hsync_len	= 9,		.vsync_len	= 9,
+	.left_margin	= 54,		.upper_margin	= 24,
+	.right_margin	= 54,		.lower_margin	= 32,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
 };
 #endif
 
 #ifdef CONFIG_SA1100_HUW_WEBPANEL
 static struct sa1100fb_mach_info huw_webpanel_info __initdata = {
-	pixclock:	0,		bpp:		8,
-	xres:		640,		yres:		480,
+	.pixclock	= 0,		.bpp		= 8,
+	.xres		= 640,		.yres		= 480,
 
-	hsync_len:	3,		vsync_len:	1,
-	left_margin:	41,		upper_margin:	0,
-	right_margin:	101,		lower_margin:	0,
+	.hsync_len	= 3,		.vsync_len	= 1,
+	.left_margin	= 41,		.upper_margin	= 0,
+	.right_margin	= 101,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) | 8,
+	.lccr0		= LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) | 8,
 #error FIXME
 	/*
 	 * FIXME: please get rid of the '| 8' in preference to an
@@ -446,99 +440,94 @@ static struct sa1100fb_mach_info huw_webpanel_info __initdata = {
 
 #ifdef LART_GREY_LCD
 static struct sa1100fb_mach_info lart_grey_info __initdata = {
-	pixclock:	150000,		bpp:		4,
-	xres:		320,		yres:		240,
+	.pixclock	= 150000,	.bpp		= 4,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	1,		vsync_len:	1,
-	left_margin:	4,		upper_margin:	0,
-	right_margin:	2,		lower_margin:	0,
+	.hsync_len	= 1,		.vsync_len	= 1,
+	.left_margin	= 4,		.upper_margin	= 0,
+	.right_margin	= 2,		.lower_margin	= 0,
 
-	cmap_greyscale:	1,
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.cmap_greyscale	= 1,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
+	.lccr0		= LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
 };
 #endif
 #ifdef LART_COLOR_LCD
 static struct sa1100fb_mach_info lart_color_info __initdata = {
-	pixclock:	150000,		bpp:		16,
-	xres:		320,		yres:		240,
+	.pixclock	= 150000,	.bpp		= 16,
+	.xres		= 320,		.yres		= 240,
 
-	hsync_len:	2,		vsync_len:	3,
-	left_margin:	69,		upper_margin:	14,
-	right_margin:	8,		lower_margin:	4,
+	.hsync_len	= 2,		.vsync_len	= 3,
+	.left_margin	= 69,		.upper_margin	= 14,
+	.right_margin	= 8,		.lower_margin	= 4,
 
-	sync:		0,
-
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
 };
 #endif
 #ifdef LART_VIDEO_OUT
 static struct sa1100fb_mach_info lart_video_info __initdata = {
-	pixclock:	39721,		bpp:		16,
-	xres:		640,		yres:		480,
+	.pixclock	= 39721,	.bpp		= 16,
+	.xres		= 640,		.yres		= 480,
 
-	hsync_len:	95,		vsync_len:	2,
-	left_margin:	40,		upper_margin:	32,
-	right_margin:	24,		lower_margin:	11,
+	.hsync_len	= 95,		.vsync_len	= 2,
+	.left_margin	= 40,		.upper_margin	= 32,
+	.right_margin	= 24,		.lower_margin	= 11,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
 };
 #endif
 
 #ifdef LART_KIT01_LCD
-static struct sa1100fb_mach_info lart_kit01_info __initdata =
-{
-	pixclock:	63291,		bpp:		16,
-	xres:		640,		yres:		480,
+static struct sa1100fb_mach_info lart_kit01_info __initdata = {
+	.pixclock	= 63291,	.bpp		= 16,
+	.xres		= 640,		.yres		= 480,
 
-	hsync_len:	64,		vsync_len:	3,
-	left_margin:	122,		upper_margin:	45,
-	right_margin:	10,		lower_margin:	10,
+	.hsync_len	= 64,		.vsync_len	= 3,
+	.left_margin	= 122,		.upper_margin	= 45,
+	.right_margin	= 10,		.lower_margin	= 10,
 
-	sync:		0,
-
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixFlEdg
 };
 #endif
 
 #ifdef CONFIG_SA1100_SHANNON
 static struct sa1100fb_mach_info shannon_info __initdata = {
-	pixclock:	152500,		bpp:		8,
-	xres:		640,		yres:		480,
+	.pixclock	= 152500,	.bpp		= 8,
+	.xres		= 640,		.yres		= 480,
 
-	hsync_len:	4,		vsync_len:	3,
-	left_margin:	2,		upper_margin:	0,
-	right_margin:	1,		lower_margin:	0,
+	.hsync_len	= 4,		.vsync_len	= 3,
+	.left_margin	= 2,		.upper_margin	= 0,
+	.right_margin	= 1,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 
 
-	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
-	lccr3:		LCCR3_ACBsDiv(512),
+	.lccr0		= LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
+	.lccr3		= LCCR3_ACBsDiv(512),
 };
 #endif
 
 #ifdef CONFIG_SA1100_OMNIMETER
 static struct sa1100fb_mach_info omnimeter_info __initdata = {
-	pixclock:	0,		bpp:		4,
-	xres:		480,		yres:		320,
+	.pixclock	= 0,		.bpp		= 4,
+	.xres		= 480,		.yres		= 320,
 
-	hsync_len:	1,		vsync_len:	1,
-	left_margin:	10,		upper_margin:	0,
-	right_margin:	10,		lower_margin:	0,
+	.hsync_len	= 1,		.vsync_len	= 1,
+	.left_margin	= 10,		.upper_margin	= 0,
+	.right_margin	= 10,		.lower_margin	= 0,
 
-	cmap_greyscale:	1,
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.cmap_greyscale	= 1,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_8PixMono,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(255) |
-			LCCR3_PixClkDiv(44),
+	.lccr0		= LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_8PixMono,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(255) |
+			  LCCR3_PixClkDiv(44),
 #error FIXME: fix pixclock, ACBsDiv
 	/*
 	 * FIXME: I think ACBsDiv is wrong above - should it be 512 (disabled)?
@@ -549,17 +538,17 @@ static struct sa1100fb_mach_info omnimeter_info __initdata = {
 
 #ifdef CONFIG_SA1100_PANGOLIN
 static struct sa1100fb_mach_info pangolin_info __initdata = {
-	pixclock:	341521,		bpp:		16,
-	xres:		800,		yres:		600,
+	.pixclock	= 341521,	.bpp		= 16,
+	.xres		= 800,		.yres		= 600,
 
-	hsync_len:	64,		vsync_len:	7,
-	left_margin:	160,		upper_margin:	7,
-	right_margin:	24,		lower_margin:	1,
+	.hsync_len	= 64,		.vsync_len	= 7,
+	.left_margin	= 160,		.upper_margin	= 7,
+	.right_margin	= 24,		.lower_margin	= 1,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsCntOff,
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsCntOff,
 };
 #endif
 
@@ -571,49 +560,47 @@ static struct sa1100fb_mach_info pangolin_info __initdata = {
  * NB likely to be increased to ease bus timings wrt pcmcia interface
  */
 static struct sa1100fb_mach_info stork_tft_info __initdata = {
-	pixclock:	28935,		bpp:		16,
-	xres:		640,		yres:		480,
-
-	hsync_len:	64,		vsync_len:	2,
-	left_margin:	48,		upper_margin:	12,
-	right_margin:	48,		lower_margin:	31,
+	.pixclock	= 28935,	.bpp		= 16,
+	.xres		= 640,		.yres		= 480,
 
-	sync:		0,
+	.hsync_len	= 64,		.vsync_len	= 2,
+	.left_margin	= 48,		.upper_margin	= 12,
+	.right_margin	= 48,		.lower_margin	= 31,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg,
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsCntOff,
 };
 
 static struct sa1100fb_rgb stork_tft_rgb_16 = {
-	red:	{ offset: 11, length: 5, },
-	green:	{ offset: 5,  length: 6, },
-	blue:	{ offset: 0,  length: 5, },
-	transp:	{ offset: 0,  length: 0, },
+	.red	= { .offset = 11, .length = 5, },
+	.green	= { .offset = 5,  .length = 6, },
+	.blue	= { .offset = 0,  .length = 5, },
+	.transp	= { .offset = 0,  .length = 0, },
 };
 
 #else	/* Kyocera DSTN */
 
 static struct sa1100fb_mach_info stork_dstn_info __initdata = {
-	pixclock:	0,		bpp:		16,
-	xres:		640,		yres:		480,
+	.pixclock	= 0,		.bpp		= 16,
+	.xres		= 640,		.yres		= 480,
 
-	hsync_len:	2,		vsync_len:	2,
-	left_margin:	2,		upper_margin:	0,
-	right_margin:	2,		lower_margin:	0,
+	.hsync_len	= 2,		.vsync_len	= 2,
+	.left_margin	= 2,		.upper_margin	= 0,
+	.right_margin	= 2,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
 
-	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
+	.lccr0		= LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
 #error Fixme
-	lccr3:		0xff00 |
+	.lccr3		= 0xff00 |
 			0x18		/* ought to be 0x14 but DMA isn't up to that as yet */
 };
 
 static struct sa1100fb_rgb stork_dstn_rgb_16 = {
-	red:	{ offset: 8,  length: 4, },
-	green:	{ offset: 4,  length: 4, },
-	blue:	{ offset: 0,  length: 4, },
-	transp:	{ offset: 0,  length: 0, },
+	.red	= { .offset = 8,  .length = 4, },
+	.green	= { .offset = 4,  .length = 4, },
+	.blue	= { .offset = 0,  .length = 4, },
+	.transp	= { .offset = 0,  .length = 0, },
 };
 #endif
 #endif
@@ -630,34 +617,31 @@ static struct sa1100fb_rgb stork_dstn_rgb_16 = {
  *	=>4.32Mhz => 231481E-12s
  */
 static struct sa1100fb_mach_info system3_info __initdata = {
-	pixclock:	231481,		bpp:		8,
-	xres:		640,		yres:		480,
+	.pixclock	= 231481,	.bpp		= 8,
+	.xres		= 640,		.yres		= 480,
 
-	hsync_len:	2,		vsync_len:	2,
-	left_margin:	2,		upper_margin:	0,
-	right_margin:	2,		lower_margin:	0,
+	.hsync_len	= 2,		.vsync_len	= 2,
+	.left_margin	= 2,		.upper_margin	= 0,
+	.right_margin	= 2,		.lower_margin	= 0,
 
-	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 
-	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg |	LCCR3_ACBsDiv(512) |
-			LCCR3_ACBsCntOff,
+	.lccr0		= LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
 };
 #endif
 
 #ifdef CONFIG_SA1100_XP860
 static struct sa1100fb_mach_info xp860_info __initdata = {
-	pixclock:	0,		bpp:		8,
-	xres:		1024,		yres:		768,
-
-	hsync_len:	3,		vsync_len:	3,
-	left_margin:	3,		upper_margin:	2,
-	right_margin:	2,		lower_margin:	1,
+	.pixclock	= 0,		.bpp		= 8,
+	.xres		= 1024,		.yres		= 768,
 
-	sync:		0,
+	.hsync_len	= 3,		.vsync_len	= 3,
+	.left_margin	= 3,		.upper_margin	= 2,
+	.right_margin	= 2,		.lower_margin	= 1,
 
-	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
-	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_PixClkDiv(6),
+	.lccr0		= LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+	.lccr3		= LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_PixClkDiv(6),
 };
 #endif
 
@@ -808,33 +792,7 @@ static inline void sa1100fb_schedule_task(struct sa1100fb_info *fbi, u_int state
 	local_irq_restore(flags);
 }
 
-/*
- * Get the VAR structure pointer for the specified console
- */
-static inline struct fb_var_screeninfo *get_con_var(struct fb_info *info, int con)
-{
-	return (con == info->currcon || con == -1) ? &info->var : &fb_display[con].var;
-}
-
-/*
- * Get the DISPLAY structure pointer for the specified console
- */
-static inline struct display *get_con_display(struct fb_info *info, int con)
-{
-	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	return (con < 0) ? fbi->fb.disp : &fb_display[con];
-}
-
-/*
- * Get the CMAP pointer for the specified console
- */
-static inline struct fb_cmap *get_con_cmap(struct fb_info *info, int con)
-{
-	return (con == info->currcon || con == -1) ? &info->cmap : &fb_display[con].cmap;
-}
-
-static inline u_int
-chan_to_field(u_int chan, struct fb_bitfield *bf)
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
 {
 	chan &= 0xffff;
 	chan >>= 16 - bf->length;
@@ -844,8 +802,7 @@ chan_to_field(u_int chan, struct fb_bitfield *bf)
 /*
  * Convert bits-per-pixel to a hardware palette PBS value.
  */
-static inline u_int
-palette_pbs(struct fb_var_screeninfo *var)
+static inline u_int palette_pbs(struct fb_var_screeninfo *var)
 {
 	int ret = 0;
 	switch (var->bits_per_pixel) {
@@ -888,8 +845,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 		   u_int trans, struct fb_info *info)
 {
 	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	struct display *disp = get_con_display(info, info->currcon);
-	u_int val;
+	unsigned int val;
 	int ret = 1;
 
 	/*
@@ -898,7 +854,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 	 * is what you poke into the framebuffer to produce the
 	 * colour you requested.
 	 */
-	if (disp->inverse) {
+	if (fbi->cmap_inverse) {
 		red   = 0xffff - red;
 		green = 0xffff - green;
 		blue  = 0xffff - blue;
@@ -912,7 +868,7 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 		red = green = blue = (19595 * red + 38470 * green +
 					7471 * blue) >> 16;
 
-	switch (fbi->fb.disp->visual) {
+	switch (fbi->fb.fix.visual) {
 	case FB_VISUAL_TRUECOLOR:
 		/*
 		 * 12 or 16-bit True Colour.  We encode the RGB value
@@ -942,38 +898,29 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 /*
  *  sa1100fb_display_dma_period()
  *    Calculate the minimum period (in picoseconds) between two DMA
- *    requests for the LCD controller.
+ *    requests for the LCD controller.  If we hit this, it means we're
+ *    doing nothing but LCD DMA.
  */
-static unsigned int
-sa1100fb_display_dma_period(struct fb_var_screeninfo *var)
+static unsigned int sa1100fb_display_dma_period(struct fb_var_screeninfo *var)
 {
-	unsigned int mem_bits_per_pixel;
-
-	mem_bits_per_pixel = var->bits_per_pixel;
-	if (mem_bits_per_pixel == 12)
-		mem_bits_per_pixel = 16;
-
 	/*
 	 * Period = pixclock * bits_per_byte * bytes_per_transfer
 	 *		/ memory_bits_per_pixel;
 	 */
-	return var->pixclock * 8 * 16 / mem_bits_per_pixel;
+	return var->pixclock * 8 * 16 / var->bits_per_pixel;
 }
 
 /*
- *  sa1100fb_decode_var():
- *    Get the video params out of 'var'. If a value doesn't fit, round it up,
- *    if it's too big, return -EINVAL.
- *
- *    Suggestion: Round up in the following order: bits_per_pixel, xres,
+ *  sa1100fb_check_var():
+ *    Round up in the following order: bits_per_pixel, xres,
  *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
  *    bitfields, horizontal timing, vertical timing.
  */
 static int
-sa1100fb_validate_var(struct fb_var_screeninfo *var,
-		      struct sa1100fb_info *fbi)
+sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
-	int ret = -EINVAL;
+	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
+	int rgbidx;
 
 	if (var->xres < MIN_XRES)
 		var->xres = MIN_XRES;
@@ -983,42 +930,60 @@ sa1100fb_validate_var(struct fb_var_screeninfo *var,
 		var->xres = fbi->max_xres;
 	if (var->yres > fbi->max_yres)
 		var->yres = fbi->max_yres;
-	var->xres_virtual =
-	    var->xres_virtual < var->xres ? var->xres : var->xres_virtual;
-	var->yres_virtual =
-	    var->yres_virtual < var->yres ? var->yres : var->yres_virtual;
+	var->xres_virtual = max(var->xres_virtual, var->xres);
+	var->yres_virtual = max(var->yres_virtual, var->yres);
 
 	DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_CFB4
-	case 4:		ret = 0; break;
+	case 4:
+		rgbidx = RGB_8;
+		break;
 #endif
 #ifdef FBCON_HAS_CFB8
-	case 8:		ret = 0; break;
+	case 8:
+		rgbidx = RGB_8;
+		break;
 #endif
 #ifdef FBCON_HAS_CFB16
-	case 16:	ret = 0; break;
+	case 16:
+		rgbidx = RGB_16;
+		break;
 #endif
 	default:
-		break;
+		return -EINVAL;
 	}
 
+	/*
+	 * Copy the RGB parameters for this display
+	 * from the machine specific parameters.
+	 */
+	var->red    = fbi->rgb[rgbidx]->red;
+	var->green  = fbi->rgb[rgbidx]->green;
+	var->blue   = fbi->rgb[rgbidx]->blue;
+	var->transp = fbi->rgb[rgbidx]->transp;
+
+	DPRINTK("RGBT length = %d:%d:%d:%d\n",
+		var->red.length, var->green.length, var->blue.length,
+		var->transp.length);
+
+	DPRINTK("RGBT offset = %d:%d:%d:%d\n",
+		var->red.offset, var->green.offset, var->blue.offset,
+		var->transp.offset);
+
 #ifdef CONFIG_CPU_FREQ
 	printk(KERN_DEBUG "dma period = %d ps, clock = %d kHz\n",
 		sa1100fb_display_dma_period(var),
 		cpufreq_get(smp_processor_id()));
 #endif
 
-	return ret;
+	return 0;
 }
 
 static inline void sa1100fb_set_truecolor(u_int is_true_color)
 {
-	DPRINTK("true_color = %d\n", is_true_color);
-
 	if (machine_is_assabet()) {
-#if 1
-		// phase 4 or newer Assabet's
+#if 1		// phase 4 or newer Assabet's
 		if (is_true_color)
 			ASSABET_BCR_set(ASSABET_BCR_LCD_12RGB);
 		else
@@ -1033,10 +998,30 @@ static inline void sa1100fb_set_truecolor(u_int is_true_color)
 	}
 }
 
-static void
-sa1100fb_hw_set_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
+/*
+ * sa1100fb_set_par():
+ *	Set the user defined part of the display for the specified console
+ */
+static int sa1100fb_set_par(struct fb_info *info)
 {
-	u_long palette_mem_size;
+	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
+	struct fb_var_screeninfo *var = &info->var;
+	unsigned long palette_mem_size;
+
+	DPRINTK("set_par\n");
+
+	if (var->bits_per_pixel == 16)
+		fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+	else if (!fbi->cmap_static)
+		fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+	else {
+		/*
+		 * Some people have weird ideas about wanting static
+		 * pseudocolor maps.  I suspect their user space
+		 * applications are broken.
+		 */
+		fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+	}
 
 	fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
 
@@ -1047,10 +1032,10 @@ sa1100fb_hw_set_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
 	fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
 	fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
 
-	fb_set_cmap(&fbi->fb.cmap, 1, &fbi->fb);
-
-	/* Set board control register to handle new color depth */
-	sa1100fb_set_truecolor(var->bits_per_pixel >= 16);
+	/*
+	 * Set (any) board control register to handle new color depth
+	 */
+	sa1100fb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR);
 
 #ifdef CONFIG_SA1100_OMNIMETER
 #error Do we have to do this here?   We already do it at init time.
@@ -1060,9 +1045,7 @@ sa1100fb_hw_set_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
 
 	sa1100fb_activate_var(var, fbi);
 
-	fbi->palette_cpu[0] = (fbi->palette_cpu[0] &
-					 0xcfff) | palette_pbs(var);
-
+	return 0;
 }
 
 /*
@@ -1072,197 +1055,38 @@ sa1100fb_hw_set_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
 static int
 sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
-	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	struct fb_var_screeninfo *dvar = get_con_var(&fbi->fb, con);
-	struct display *display = get_con_display(&fbi->fb, con);
-	int err, chgvar = 0, rgbidx;
-
-	DPRINTK("set_var\n");
+	int ret, act;
 
-	/*
-	 * Decode var contents into a par structure, adjusting any
-	 * out of range values.
-	 */
-	err = sa1100fb_validate_var(var, fbi);
-	if (err)
-		return err;
+	act = var->activate & FB_ACTIVATE_MASK;
 
-	if (var->activate & FB_ACTIVATE_TEST)
-		return 0;
+	ret = gen_set_var(var, con, info);
 
-	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
-		return -EINVAL;
+	if (ret == 0 && act & FB_ACTIVATE_NOW) {
+		struct display *display = (con < 0) ? info->disp : fb_display + con;
 
-	if (dvar->xres != var->xres)
-		chgvar = 1;
-	if (dvar->yres != var->yres)
-		chgvar = 1;
-	if (dvar->xres_virtual != var->xres_virtual)
-		chgvar = 1;
-	if (dvar->yres_virtual != var->yres_virtual)
-		chgvar = 1;
-	if (dvar->bits_per_pixel != var->bits_per_pixel)
-		chgvar = 1;
-	if (con < 0)
-		chgvar = 0;
-
-	switch (var->bits_per_pixel) {
-#ifdef FBCON_HAS_CFB4
-	case 4:
-		if (fbi->cmap_static)
-			display->visual	= FB_VISUAL_STATIC_PSEUDOCOLOR;
-		else
-			display->visual	= FB_VISUAL_PSEUDOCOLOR;
-		display->line_length	= var->xres / 2;
-		display->dispsw		= &fbcon_cfb4;
-		rgbidx			= RGB_8;
-		break;
-#endif
-#ifdef FBCON_HAS_CFB8
-	case 8:
-		if (fbi->cmap_static)
-			display->visual	= FB_VISUAL_STATIC_PSEUDOCOLOR;
-		else
-			display->visual	= FB_VISUAL_PSEUDOCOLOR;
-		display->line_length	= var->xres;
-		display->dispsw		= &fbcon_cfb8;
-		rgbidx			= RGB_8;
-		break;
-#endif
-#ifdef FBCON_HAS_CFB16
-	case 16:
-		display->visual		= FB_VISUAL_TRUECOLOR;
-		display->line_length	= var->xres * 2;
-		display->dispsw		= &fbcon_cfb16;
-		display->dispsw_data	= fbi->fb.pseudo_palette;
-		rgbidx			= RGB_16;
-		break;
-#endif
-	default:
-		rgbidx = 0;
-		display->dispsw = &fbcon_dummy;
-		break;
+		/*
+		 * fbcon assumes too much.
+		 */
+		display->can_soft_blank = 1;
 	}
 
-	display->next_line	= display->line_length;
-	display->type		= fbi->fb.fix.type;
-	display->type_aux	= fbi->fb.fix.type_aux;
-	display->ypanstep	= fbi->fb.fix.ypanstep;
-	display->ywrapstep	= fbi->fb.fix.ywrapstep;
-	display->can_soft_blank	= 1;
-	display->inverse	= fbi->cmap_inverse;
-
-	*dvar			= *var;
-	dvar->activate		&= ~FB_ACTIVATE_ALL;
-
-	/*
-	 * Copy the RGB parameters for this display
-	 * from the machine specific parameters.
-	 */
-	dvar->red		= fbi->rgb[rgbidx]->red;
-	dvar->green		= fbi->rgb[rgbidx]->green;
-	dvar->blue		= fbi->rgb[rgbidx]->blue;
-	dvar->transp		= fbi->rgb[rgbidx]->transp;
-
-	DPRINTK("RGBT length = %d:%d:%d:%d\n",
-		dvar->red.length, dvar->green.length, dvar->blue.length,
-		dvar->transp.length);
-
-	DPRINTK("RGBT offset = %d:%d:%d:%d\n",
-		dvar->red.offset, dvar->green.offset, dvar->blue.offset,
-		dvar->transp.offset);
-
-	/*
-	 * Update the old var.  The fbcon drivers still use this.
-	 * Once they are using fbi->fb.var, this can be dropped.
-	 */
-	display->var = *dvar;
-
-	/*
-	 * If we are setting all the virtual consoles, also set the
-	 * defaults used to create new consoles.
-	 */
-	if (var->activate & FB_ACTIVATE_ALL)
-		fbi->fb.disp->var = *dvar;
-
-	/*
-	 * If the console has changed and the console has defined
-	 * a changevar function, call that function.
-	 */
-	if (chgvar && info && fbi->fb.changevar)
-		fbi->fb.changevar(con);
-
-	/* If the current console is selected, activate the new var. */
-	if (con != fbi->fb.currcon)
-		return 0;
-
-	sa1100fb_hw_set_var(dvar, fbi);
-
-	return 0;
-}
-
-static int
-__do_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-	      struct fb_info *info)
-{
-	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	struct fb_cmap *dcmap = get_con_cmap(info, con);
-	int err = 0;
-
-	if (con == -1)
-		con = info->currcon;
-
-	/* no colormap allocated? (we always have "this" colour map allocated) */
-	if (con >= 0)
-		err = fb_alloc_cmap(&fb_display[con].cmap, fbi->palette_size, 0);
-
-	if (!err && con == info->currcon)
-		err = fb_set_cmap(cmap, kspc, info);
-
-	if (!err)
-		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
-
-	return err;
+	return ret;
 }
 
 static int
 sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 		  struct fb_info *info)
 {
-	struct display *disp = get_con_display(info, con);
+	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
+	struct display *disp = (con < 0) ? info->disp : (fb_display + con);
 
-	if (disp->visual == FB_VISUAL_TRUECOLOR ||
-	    disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
+	/*
+	 * Make sure the user isn't doing something stupid.
+	 */
+	if (!kspc && (disp->var.bits_per_pixel == 16 || fbi->cmap_static))
 		return -EINVAL;
 
-	return __do_set_cmap(cmap, kspc, con, info);
-}
-
-static int
-sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
-{
-	struct display *display = get_con_display(info, con);
-
-	*fix = info->fix;
-
-	fix->line_length = display->line_length;
-	fix->visual	 = display->visual;
-	return 0;
-}
-
-static int
-sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
-{
-	*var = *get_con_var(info, con);
-	return 0;
-}
-
-static int
-sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
-{
-	struct fb_cmap *dcmap = get_con_cmap(info, con);
-	fb_copy_cmap(dcmap, cmap, kspc ? 0 : 2);
-	return 0;
+	return gen_set_cmap(cmap, kspc, con, info);
 }
 
 /*
@@ -1312,16 +1136,16 @@ static int sa1100fb_blank(int blank, struct fb_info *info)
 	case VESA_POWERDOWN:
 	case VESA_VSYNC_SUSPEND:
 	case VESA_HSYNC_SUSPEND:
-		if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
-		    fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
+		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
+		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
 			for (i = 0; i < fbi->palette_size; i++)
 				sa1100fb_setpalettereg(i, 0, 0, 0, 0, info);
 		sa1100fb_schedule_task(fbi, C_DISABLE);
 		break;
 
 	case VESA_NO_BLANKING:
-		if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
-		    fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
+		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
+		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
 			fb_set_cmap(&fbi->fb.cmap, 1, info);
 		sa1100fb_schedule_task(fbi, C_ENABLE);
 	}
@@ -1329,71 +1153,19 @@ static int sa1100fb_blank(int blank, struct fb_info *info)
 }
 
 static struct fb_ops sa1100fb_ops = {
-	owner:		THIS_MODULE,
-	fb_get_fix:	sa1100fb_get_fix,
-	fb_get_var:	sa1100fb_get_var,
-	fb_set_var:	sa1100fb_set_var,
-	fb_get_cmap:	sa1100fb_get_cmap,
-	fb_set_cmap:	sa1100fb_set_cmap,
-	fb_setcolreg:	sa1100fb_setcolreg,
-	fb_blank:	sa1100fb_blank,
+	.owner		= THIS_MODULE,
+	.fb_check_var	= sa1100fb_check_var,
+	.fb_set_par	= sa1100fb_set_par,
+	.fb_set_var	= sa1100fb_set_var,
+	.fb_get_cmap	= gen_get_cmap,
+	.fb_set_cmap	= sa1100fb_set_cmap,
+	.fb_setcolreg	= sa1100fb_setcolreg,
+	.fb_blank	= sa1100fb_blank,
 };
 
-/*
- *  sa1100fb_switch():       
- *	Change to the specified console.  Palette and video mode
- *      are changed to the console's stored parameters.
- *
- *	Uh oh, this can be called from a tasklet (IRQ)
- */
-static int sa1100fb_switch(int con, struct fb_info *info)
-{
-	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	struct display *disp;
-	struct fb_cmap *cmap;
-
-	DPRINTK("con=%d info->modename=%s\n", con, fbi->fb.modename);
-
-	if (con == info->currcon)
-		return 0;
-
-	if (info->currcon >= 0) {
-		disp = fb_display + info->currcon;
-
-		/*
-		 * Save the old colormap and video mode.
-		 */
-		disp->var = fbi->fb.var;
-
-		if (disp->cmap.len)
-			fb_copy_cmap(&fbi->fb.cmap, &disp->cmap, 0);
-	}
-
-	info->currcon = con;
-	disp = fb_display + con;
-
-	/*
-	 * Make sure that our colourmap contains 256 entries.
-	 */
-	fb_alloc_cmap(&fbi->fb.cmap, 256, 0);
-
-	if (disp->cmap.len)
-		cmap = &disp->cmap;
-	else
-		cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
-
-	fb_copy_cmap(cmap, &fbi->fb.cmap, 0);
-
-	fbi->fb.var = disp->var;
-	fbi->fb.var.activate = FB_ACTIVATE_NOW;
-
-	sa1100fb_set_var(&fbi->fb.var, con, info);
-	return 0;
-}
-
 static int sa1100fb_updatevar(int con, struct fb_info *info)
 {
-	DPRINTK("entered\n");
+	/* we don't support panning nor scrolling */
 	return 0;
 }
 
@@ -1401,34 +1173,14 @@ static int sa1100fb_updatevar(int con, struct fb_info *info)
  * Calculate the PCD value from the clock rate (in picoseconds).
  * We take account of the PPCR clock setting.
  */
-static inline int get_pcd(unsigned int pixclock)
+static inline unsigned int get_pcd(unsigned int pixclock, unsigned int cpuclock)
 {
-	unsigned int pcd;
-
-	if (pixclock) {
-		pcd = cpufreq_get(0) / 100;
-		pcd *= pixclock;
-		pcd /= 10000000;
-		pcd += 1;	/* make up for integer math truncations */
-	} else {
-		/*
-		 * People seem to be missing this message.  Make it big.
-		 * Make it stand out.  Make sure people see it.
-		 */
-		printk(KERN_WARNING "******************************************************\n");
-		printk(KERN_WARNING "**            ZERO PIXEL CLOCK DETECTED             **\n");
-		printk(KERN_WARNING "** You are using a zero pixclock.  This means that  **\n");
-		printk(KERN_WARNING "** clock scaling will not be able to adjust your    **\n");
-		printk(KERN_WARNING "** your timing parameters appropriately, and the    **\n");
-		printk(KERN_WARNING "** bandwidth calculations will fail to work.  This  **\n");
-		printk(KERN_WARNING "** will shortly become an error condition, which    **\n");
-		printk(KERN_WARNING "** will prevent your LCD display working.  Please   **\n");
-		printk(KERN_WARNING "** send your patches in as soon as possible to shut **\n");
-		printk(KERN_WARNING "** this message up.                                 **\n");
-		printk(KERN_WARNING "******************************************************\n");
-		pcd = 0;
-	}
-	return pcd;
+	unsigned int pcd = cpuclock / 100;
+
+	pcd *= pixclock;
+	pcd /= 10000000;
+
+	return pcd + 1;	/* make up for integer math truncations */
 }
 
 /*
@@ -1439,7 +1191,7 @@ static inline int get_pcd(unsigned int pixclock)
 static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
 {
 	struct sa1100fb_lcd_reg new_regs;
-	u_int half_screen_size, yres, pcd = get_pcd(var->pixclock);
+	u_int half_screen_size, yres, pcd;
 	u_long flags;
 
 	DPRINTK("Configuring SA1100 LCD\n");
@@ -1502,13 +1254,10 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
 		LCCR2_BegFrmDel(var->upper_margin) +
 		LCCR2_EndFrmDel(var->lower_margin);
 
-	new_regs.lccr3 = fbi->lccr3 |
+	pcd = get_pcd(var->pixclock, cpufreq_get(0));
+	new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->lccr3 |
 		(var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
-		(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL) |
-		LCCR3_ACBsCntOff;
-
-	if (pcd)
-		new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);
+		(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
 
 	DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
 	DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
@@ -1547,61 +1296,20 @@ static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_
  * to ensure that things happen in the right way 100% of time time.
  *	-- rmk
  */
-
-/*
- * FIXME: move LCD power stuff into sa1100fb_power_up_lcd()
- * Also, I'm expecting that the backlight stuff should
- * be handled differently.
- */
-static inline void sa1100fb_backlight_on(struct sa1100fb_info *fbi)
-{
-	DPRINTK("backlight on\n");
-
-	if (sa1100fb_backlight_power)
-		sa1100fb_backlight_power(1);
-}
-
-/*
- * FIXME: move LCD power stuf into sa1100fb_power_down_lcd()
- * Also, I'm expecting that the backlight stuff should
- * be handled differently.
- */
-static inline void sa1100fb_backlight_off(struct sa1100fb_info *fbi)
+static inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on)
 {
-	DPRINTK("backlight off\n");
+	DPRINTK("backlight o%s\n", on ? "n" : "ff");
 
 	if (sa1100fb_backlight_power)
-		sa1100fb_backlight_power(0);
+		sa1100fb_backlight_power(on);
 }
 
-static inline void sa1100fb_power_up_lcd(struct sa1100fb_info *fbi)
+static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on)
 {
-	DPRINTK("LCD power on\n");
+	DPRINTK("LCD power o%s\n", on ? "n" : "ff");
 
 	if (sa1100fb_lcd_power)
-		sa1100fb_lcd_power(1);
-
-#ifdef CONFIG_SA1100_STORK
-	if (machine_is_stork()) {
-		storkSetLCDCPLD(0, 1);
-		storkSetLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
- 	}
-#endif
-}
-
-static inline void sa1100fb_power_down_lcd(struct sa1100fb_info *fbi)
-{
-	DPRINTK("LCD power off\n");
-
-	if (sa1100fb_lcd_power)
-		sa1100fb_lcd_power(0);
-
-#ifdef CONFIG_SA1100_STORK
-	if (machine_is_stork()) {
-		storkSetLCDCPLD(0, 0);
-		storkClearLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
-	}
-#endif
+		sa1100fb_lcd_power(on);
 }
 
 static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
@@ -1630,38 +1338,9 @@ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
 
 	}
 
-	if (machine_is_cerf()) {
-		/* GPIO15 is used as a bypass for 3.8" displays */
+	/* GPIO15 is used as a bypass for 3.8" displays */
+	if (machine_is_cerf())
 		mask |= GPIO_GPIO15;
-#ifdef CONFIG_SA1100_CERF
-#warning Read Me Now!
-#endif
-#if 0 /* if this causes you problems, mail <rmk@arm.linux.org.uk> please. */
-      /*
-       * This was enabled for the 72_A version only, which is a _color_
-       * _dual_ LCD.  Now look at the generic test above, and calculate
-       * the mask value for a colour dual display...
-       *
-       * I therefore conclude that the code below is redundant, and will
-       * be killed at the start of November 2001.
-       */
-		/* FIXME: why is this? The Cerf's display doesn't seem
-		 * to be dual scan or active. I just leave it here,
-		 * but in my opinion this is definitively wrong.
-		 *  -- Erik <J.A.K.Mouw@its.tudelft.nl>
-		 */
-
-		/* REPLY: Umm.. Well to be honest, the 5.7" LCD which
-		 * this was used for does not use these pins, but
-		 * apparently all hell breaks loose if they are not
-		 * set on the Cerf, so we decided to leave them in ;)
-		 *  -- Daniel Chemko <dchemko@intrinsyc.com>
-		 */
-		/* color {dual/single} passive */
-		mask |= GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 |
-			GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
-#endif
-	}
 
 	if (mask) {
 		GPDR |= mask;
@@ -1733,7 +1412,7 @@ static void sa1100fb_disable_controller(struct sa1100fb_info *fbi)
 	}
 #endif
 #ifdef CONFIG_SA1100_HUW_WEBPANEL
-#error Move me into sa1100fb_power_up_lcd and/or sa1100fb_backlight_on
+#error Move me into __sa1100fb_lcd_power and/or __sa1100fb_backlight_power
 	if (machine_is_huw_webpanel()) {
 		// dont forget to set the control lines to zero (?)
 		DPRINTK("ShutDown HuW LCD controller\n");
@@ -1806,10 +1485,10 @@ static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state)
 		if (old_state != C_DISABLE) {
 			fbi->state = state;
 
-			sa1100fb_backlight_off(fbi);
+			__sa1100fb_backlight_power(fbi, 0);
 			if (old_state != C_DISABLE_CLKCHANGE)
 				sa1100fb_disable_controller(fbi);
-			sa1100fb_power_down_lcd(fbi);
+			__sa1100fb_lcd_power(fbi, 0);
 		}
 		break;
 
@@ -1855,9 +1534,9 @@ static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state)
 		if (old_state != C_ENABLE) {
 			fbi->state = C_ENABLE;
 			sa1100fb_setup_gpio(fbi);
-			sa1100fb_power_up_lcd(fbi);
+			__sa1100fb_lcd_power(fbi, 1);
 			sa1100fb_enable_controller(fbi);
-			sa1100fb_backlight_on(fbi);
+			__sa1100fb_backlight_power(fbi, 1);
 		}
 		break;
 	}
@@ -1888,18 +1567,19 @@ static unsigned int sa1100fb_min_dma_period(struct sa1100fb_info *fbi)
 	int i;
 
 	for (i = 0; i < MAX_NR_CONSOLES; i++) {
+		struct display *disp = &fb_display[i];
 		unsigned int period;
 
 		/*
 		 * Do we own this display?
 		 */
-		if (fb_display[i].fb_info != &fbi->fb)
+		if (disp->fb_info != &fbi->fb)
 			continue;
 
 		/*
 		 * Ok, calculate its DMA period
 		 */
-		period = sa1100fb_display_dma_period(get_con_var(&fbi->fb, i));
+		period = sa1100fb_display_dma_period(&disp->var);
 		if (period < min_period)
 			min_period = period;
 	}
@@ -1913,33 +1593,42 @@ static unsigned int sa1100fb_min_dma_period(struct sa1100fb_info *fbi)
  * subsystem.
  */
 static int
-sa1100fb_clkchg_notifier(struct notifier_block *nb, unsigned long val,
+sa1100fb_freq_transition(struct notifier_block *nb, unsigned long val,
 			 void *data)
 {
-	struct sa1100fb_info *fbi = TO_INF(nb, clockchg);
-	struct cpufreq_minmax *mm = data;
+	struct sa1100fb_info *fbi = TO_INF(nb, freq_transition);
+	struct cpufreq_freqs *f = data;
 	u_int pcd;
 
 	switch (val) {
-	case CPUFREQ_MINMAX:
-		printk(KERN_DEBUG "min dma period: %d ps, old clock %d kHz, "
-			"new clock %d kHz\n", sa1100fb_min_dma_period(fbi),
-			mm->cur_freq, mm->new_freq);
-		/* todo: fill in min/max values */
-		break;
-
 	case CPUFREQ_PRECHANGE:
 		set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
 		break;
 
 	case CPUFREQ_POSTCHANGE:
-		pcd = get_pcd(fbi->fb.var.pixclock);
+		pcd = get_pcd(fbi->fb.var.pixclock, f->new);
 		fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
 		set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
 		break;
 	}
 	return 0;
 }
+
+static int
+sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val,
+		     void *data)
+{
+	struct sa1100fb_info *fbi = TO_INF(nb, freq_policy);
+	struct cpufreq_policy *policy = data;
+
+	if (val == CPUFREQ_INCOMPATIBLE) {
+		printk(KERN_DEBUG "min dma period: %d ps, "
+			"new clock %d kHz\n", sa1100fb_min_dma_period(fbi),
+			policy->max);
+		/* todo: fill in min/max values */
+	}
+	return 0;
+}
 #endif
 
 #ifdef CONFIG_PM
@@ -2015,8 +1704,6 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(void)
 
 	memset(fbi, 0, sizeof(struct sa1100fb_info) + sizeof(struct display));
 
-	fbi->fb.currcon		= -1;
-
 	strcpy(fbi->fb.fix.id, SA1100_NAME);
 
 	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
@@ -2024,6 +1711,7 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(void)
 	fbi->fb.fix.xpanstep	= 0;
 	fbi->fb.fix.ypanstep	= 0;
 	fbi->fb.fix.ywrapstep	= 0;
+	fbi->fb.fix.line_length	= 0;
 	fbi->fb.fix.accel	= FB_ACCEL_NONE;
 
 	fbi->fb.var.nonstd	= 0;
@@ -2038,7 +1726,7 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(void)
 
 	fbi->fb.fbops		= &sa1100fb_ops;
 	fbi->fb.changevar	= NULL;
-	fbi->fb.switch_con	= sa1100fb_switch;
+	fbi->fb.switch_con	= gen_switch;
 	fbi->fb.updatevar	= sa1100fb_updatevar;
 	fbi->fb.flags		= FBINFO_FLAG_DEFAULT;
 	fbi->fb.node		= NODEV;
@@ -2052,6 +1740,16 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(void)
 
 	inf = sa1100fb_get_machine_info(fbi);
 
+	/*
+	 * People just don't seem to get this.  We don't support
+	 * anything but correct entries now, so panic if someone
+	 * does something stupid.
+	 */
+	if (inf->lccr3 & (LCCR3_VrtSnchL|LCCR3_HorSnchL|0xff) ||
+	    inf->pixclock == 0)
+		panic("sa1100fb error: invalid LCCR3 fields set or zero "
+			"pixclock.");
+
 	fbi->max_xres			= inf->xres;
 	fbi->fb.var.xres		= inf->xres;
 	fbi->fb.var.xres_virtual	= inf->xres;
@@ -2077,6 +1775,7 @@ static struct sa1100fb_info * __init sa1100fb_init_fbinfo(void)
 	fbi->task_state			= (u_char)-1;
 	fbi->fb.fix.smem_len		= fbi->max_xres * fbi->max_yres *
 					  fbi->max_bpp / 8;
+	fbi->fb.disp->inverse		= inf->cmap_inverse;
 
 	init_waitqueue_head(&fbi->ctrlr_wait);
 	INIT_TQUEUE(&fbi->task, sa1100fb_task, fbi);
@@ -2116,7 +1815,7 @@ int __init sa1100fb_init(void)
 #endif
 
 #ifdef CONFIG_SA1100_FREEBIRD
-#error Please move this into sa1100fb_power_up_lcd
+#error Please move this into __sa1100fb_lcd_power
 	if (machine_is_freebird()) {
 		BCR_set(BCR_FREEBIRD_LCD_DISP);
 		mdelay(20);
@@ -2141,8 +1840,10 @@ int __init sa1100fb_init(void)
 		fbi->pm->data = fbi;
 #endif
 #ifdef CONFIG_CPU_FREQ
-	fbi->clockchg.notifier_call = sa1100fb_clkchg_notifier;
-	cpufreq_register_notifier(&fbi->clockchg);
+	fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
+	fbi->freq_policy.notifier_call = sa1100fb_freq_policy;
+	cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
+	cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);
 #endif
 
 	/*
diff --git a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h
index 96e79705be5cc5488046e68a7fa674f34851f51a..9b3b4a402a51d0564f59d21a79b6a8dd95e361b6 100644
--- a/drivers/video/sa1100fb.h
+++ b/drivers/video/sa1100fb.h
@@ -107,7 +107,8 @@ struct sa1100fb_info {
 	struct pm_dev		*pm;
 #endif
 #ifdef CONFIG_CPU_FREQ
-	struct notifier_block	clockchg;
+	struct notifier_block	freq_transition;
+	struct notifier_block	freq_policy;
 #endif
 };
 
diff --git a/include/asm-arm/arch-sa1100/h3600.h b/include/asm-arm/arch-sa1100/h3600.h
index d0fc9198c9eb08bdcbc5a7f7966afbc7dbf9a3e2..1b6355971574a0b2be21a881e4dedb8f45c8d92a 100644
--- a/include/asm-arm/arch-sa1100/h3600.h
+++ b/include/asm-arm/arch-sa1100/h3600.h
@@ -16,7 +16,7 @@
  *
  * History:
  *
- * 2001-10-??   Andrew Christian   Added support for iPAQ H3800
+ * 2001-10-??	Andrew Christian   Added support for iPAQ H3800
  *
  */
 
@@ -26,6 +26,11 @@
 /* generalized support for H3xxx series Compaq Pocket PC's */
 #define machine_is_h3xxx() (machine_is_h3100() || machine_is_h3600() || machine_is_h3800())
 
+/* Physical memory regions corresponding to chip selects */
+#define H3600_EGPIO_PHYS     (SA1100_CS5_PHYS + 0x01000000)
+#define H3600_BANK_2_PHYS    SA1100_CS2_PHYS
+#define H3600_BANK_4_PHYS    SA1100_CS4_PHYS
+
 /* Virtual memory regions corresponding to chip selects 2 & 4 (used on sleeves) */
 #define H3600_EGPIO_VIRT     0xf0000000
 #define H3600_BANK_2_VIRT    0xf1000000
@@ -36,8 +41,7 @@
    --- these are common across all current iPAQ platforms
 */
 
-#define GPIO_H3600_NPOWER_BUTTON	GPIO_GPIO (0)   /* Also known as the "off button"  */
-#define GPIO_H3600_MICROCONTROLLER	GPIO_GPIO (1)   /* From ASIC2 on H3800 */
+#define GPIO_H3600_NPOWER_BUTTON	GPIO_GPIO (0)	/* Also known as the "off button"  */
 
 #define GPIO_H3600_PCMCIA_CD1		GPIO_GPIO (10)
 #define GPIO_H3600_PCMCIA_IRQ1		GPIO_GPIO (11)
@@ -56,83 +60,105 @@
 #define GPIO_H3600_COM_CTS		GPIO_GPIO (25)
 #define GPIO_H3600_COM_RTS		GPIO_GPIO (26)
 
-#define IRQ_GPIO_H3600_NPOWER_BUTTON    IRQ_GPIO0
-#define IRQ_GPIO_H3600_MICROCONTROLLER  IRQ_GPIO1
+#define IRQ_GPIO_H3600_NPOWER_BUTTON	IRQ_GPIO0
 #define IRQ_GPIO_H3600_PCMCIA_CD1	IRQ_GPIO10
 #define IRQ_GPIO_H3600_PCMCIA_IRQ1	IRQ_GPIO11
 #define IRQ_GPIO_H3600_PCMCIA_CD0	IRQ_GPIO17
 #define IRQ_GPIO_H3600_PCMCIA_IRQ0	IRQ_GPIO21
-#define IRQ_GPIO_H3600_COM_DCD          IRQ_GPIO23
+#define IRQ_GPIO_H3600_COM_DCD		IRQ_GPIO23
 #define IRQ_GPIO_H3600_OPT_IRQ		IRQ_GPIO24
-#define IRQ_GPIO_H3600_COM_CTS          IRQ_GPIO25
+#define IRQ_GPIO_H3600_COM_CTS		IRQ_GPIO25
 
 
 #ifndef __ASSEMBLY__
-enum ipaq_model {
-	IPAQ_H3100,
-	IPAQ_H3600,
-	IPAQ_H3800
-};
 
 enum ipaq_egpio_type {
-	IPAQ_EGPIO_LCD_ON,        /* Power to the LCD panel */
+	IPAQ_EGPIO_LCD_POWER,	  /* Power to the LCD panel */
 	IPAQ_EGPIO_CODEC_NRESET,  /* Clear to reset the audio codec (remember to return high) */
-	IPAQ_EGPIO_AUDIO_ON,      /* Audio power */
-	IPAQ_EGPIO_QMUTE,         /* Audio muting */
+	IPAQ_EGPIO_AUDIO_ON,	  /* Audio power */
+	IPAQ_EGPIO_QMUTE,	  /* Audio muting */
 	IPAQ_EGPIO_OPT_NVRAM_ON,  /* Non-volatile RAM on extension sleeves (SPI interface) */
-	IPAQ_EGPIO_OPT_ON,        /* Power to extension sleeves */
-	IPAQ_EGPIO_CARD_RESET,    /* Reset PCMCIA cards on extension sleeve (???) */
-	IPAQ_EGPIO_OPT_RESET,     /* Reset option pack (???) */
-	IPAQ_EGPIO_IR_ON,         /* IR sensor/emitter power */
-	IPAQ_EGPIO_IR_FSEL,       /* IR speed selection 1->fast, 0->slow */
-	IPAQ_EGPIO_RS232_ON,      /* Maxim RS232 chip power */
-	IPAQ_EGPIO_VPP_ON,        /* Turn on power to flash programming */
+	IPAQ_EGPIO_OPT_ON,	  /* Power to extension sleeves */
+	IPAQ_EGPIO_CARD_RESET,	  /* Reset PCMCIA cards on extension sleeve (???) */
+	IPAQ_EGPIO_OPT_RESET,	  /* Reset option pack (???) */
+	IPAQ_EGPIO_IR_ON,	  /* IR sensor/emitter power */
+	IPAQ_EGPIO_IR_FSEL,	  /* IR speed selection 1->fast, 0->slow */
+	IPAQ_EGPIO_RS232_ON,	  /* Maxim RS232 chip power */
+	IPAQ_EGPIO_VPP_ON,	  /* Turn on power to flash programming */
+	IPAQ_EGPIO_LCD_ENABLE,	  /* Enable/disable LCD controller */
 };
 
 struct ipaq_model_ops {
-	enum ipaq_model model;
 	const char     *generic_name;
-	void          (*initialize)(void);
-	void          (*control)(enum ipaq_egpio_type, int);
+	void	      (*control)(enum ipaq_egpio_type, int);
 	unsigned long (*read)(void);
+	void	      (*blank_callback)(int blank);
+	int	      (*pm_callback)(int req);	    /* Primary model callback */
+	int	      (*pm_callback_aux)(int req);  /* Secondary callback (used by HAL modules) */
 };
 
 extern struct ipaq_model_ops ipaq_model_ops;
 
-static __inline__ enum ipaq_model h3600_model( void ) {
-	return ipaq_model_ops.model;
-}
-
-static __inline__ const char * h3600_generic_name( void ) {
+static __inline__ const char * h3600_generic_name(void)
+{
 	return ipaq_model_ops.generic_name;
 }
 
-static __inline__ void init_h3600_egpio( void ) {
-	if (ipaq_model_ops.initialize)
-		ipaq_model_ops.initialize();
-}
-
-static __inline__ void assign_h3600_egpio( enum ipaq_egpio_type x, int level ) {
+static __inline__ void assign_h3600_egpio(enum ipaq_egpio_type x, int level)
+{
 	if (ipaq_model_ops.control)
 		ipaq_model_ops.control(x,level);
 }
 
-static __inline__ void clr_h3600_egpio( enum ipaq_egpio_type x ) {
+static __inline__ void clr_h3600_egpio(enum ipaq_egpio_type x)
+{
 	if (ipaq_model_ops.control)
 		ipaq_model_ops.control(x,0);
 }
 
-static __inline__ void set_h3600_egpio( enum ipaq_egpio_type x ) {
+static __inline__ void set_h3600_egpio(enum ipaq_egpio_type x)
+{
 	if (ipaq_model_ops.control)
 		ipaq_model_ops.control(x,1);
 }
 
-static __inline__ unsigned long read_h3600_egpio( void ) {
+static __inline__ unsigned long read_h3600_egpio(void)
+{
 	if (ipaq_model_ops.read)
 		return ipaq_model_ops.read();
 	return 0;
 }
 
+static __inline__ int  h3600_register_blank_callback(void (*f)(int))
+{
+	ipaq_model_ops.blank_callback = f;
+	return 0;
+}
+
+static __inline__ void h3600_unregister_blank_callback(void (*f)(int))
+{
+	ipaq_model_ops.blank_callback = NULL;
+}
+
+
+static __inline__ int  h3600_register_pm_callback(int (*f)(int))
+{
+	ipaq_model_ops.pm_callback_aux = f;
+	return 0;
+}
+
+static __inline__ void h3600_unregister_pm_callback(int (*f)(int))
+{
+	ipaq_model_ops.pm_callback_aux = NULL;
+}
+
+static __inline__ int h3600_power_management(int req)
+{
+	if (ipaq_model_ops.pm_callback)
+		return ipaq_model_ops.pm_callback(req);
+	return 0;
+}
+
 #endif /* ASSEMBLY */
 
 #endif /* _INCLUDE_H3600_H_ */
diff --git a/include/asm-arm/arch-sa1100/irqs.h b/include/asm-arm/arch-sa1100/irqs.h
index cc5dcda895c601e4911d7e88d06c49bd28388096..66e4f596c739ec74d1f48e28d0179f88674537ad 100644
--- a/include/asm-arm/arch-sa1100/irqs.h
+++ b/include/asm-arm/arch-sa1100/irqs.h
@@ -108,18 +108,18 @@
 #define AUDDTS			(IRQ_BOARD_END + 40)
 #define AUDRDD			(IRQ_BOARD_END + 41)
 #define AUDSTO			(IRQ_BOARD_END + 42)
-#define USBPWR			(IRQ_BOARD_END + 43)
-#define NIRQHCIM		(IRQ_BOARD_END + 44)
-#define IRQHCIBUFFACC		(IRQ_BOARD_END + 45)
-#define IRQHCIRMTWKP		(IRQ_BOARD_END + 46)
-#define NHCIMFCIR		(IRQ_BOARD_END + 47)
-#define USB_PORT_RESUME		(IRQ_BOARD_END + 48)
-#define S0_READY_NINT		(IRQ_BOARD_END + 49)
-#define S1_READY_NINT		(IRQ_BOARD_END + 50)
-#define S0_CD_VALID		(IRQ_BOARD_END + 51)
-#define S1_CD_VALID		(IRQ_BOARD_END + 52)
-#define S0_BVD1_STSCHG		(IRQ_BOARD_END + 53)
-#define S1_BVD1_STSCHG		(IRQ_BOARD_END + 54)
+#define IRQ_USBPWR		(IRQ_BOARD_END + 43)
+#define IRQ_NHCIM		(IRQ_BOARD_END + 44)
+#define IRQ_HCIBUFFACC		(IRQ_BOARD_END + 45)
+#define IRQ_HCIRMTWKP		(IRQ_BOARD_END + 46)
+#define IRQ_NHCIMFCIR		(IRQ_BOARD_END + 47)
+#define IRQ_USB_PORT_RESUME	(IRQ_BOARD_END + 48)
+#define IRQ_S0_READY_NINT	(IRQ_BOARD_END + 49)
+#define IRQ_S1_READY_NINT	(IRQ_BOARD_END + 50)
+#define IRQ_S0_CD_VALID		(IRQ_BOARD_END + 51)
+#define IRQ_S1_CD_VALID		(IRQ_BOARD_END + 52)
+#define IRQ_S0_BVD1_STSCHG	(IRQ_BOARD_END + 53)
+#define IRQ_S1_BVD1_STSCHG	(IRQ_BOARD_END + 54)
 
 /*
  * Figure out the MAX IRQ number.
@@ -129,7 +129,7 @@
  * Otherwise, we have the standard IRQs only.
  */
 #ifdef CONFIG_SA1111
-#define NR_IRQS			(S1_BVD1_STSCHG + 1)
+#define NR_IRQS			(IRQ_S1_BVD1_STSCHG + 1)
 #elif defined(CONFIG_SA1100_GRAPHICSCLIENT) || \
       defined(CONFIG_SA1100_GRAPHICSMASTER) || \
       defined(CONFIG_SA1100_H3800)
diff --git a/include/asm-arm/hardware/sa1111.h b/include/asm-arm/hardware/sa1111.h
index c334e869f8ef22e9fb39ee88884c0fd1d632a712..311e4832eb2fbc0639763d818a380d2df0b17028 100644
--- a/include/asm-arm/hardware/sa1111.h
+++ b/include/asm-arm/hardware/sa1111.h
@@ -64,18 +64,10 @@
 #define SA1111_SMCR	0x0004
 #define SA1111_SKID	0x0008
 
-#define _SBI_SKCR	_SA1111(SA1111_SKCR)
-#define _SBI_SMCR	_SA1111(SA1111_SMCR)
-#define _SBI_SKID	_SA1111(SA1111_SKID)
-
-#if LANGUAGE == C
-
 #define SBI_SKCR	__CCREG(SA1111_SKCR)
 #define SBI_SMCR	__CCREG(SA1111_SMCR)
 #define SBI_SKID	__CCREG(SA1111_SKID)
 
-#endif  /* LANGUAGE == C */
-
 #define SKCR_PLL_BYPASS	(1<<0)
 #define SKCR_RCLKEN	(1<<1)
 #define SKCR_SLEEP	(1<<2)
@@ -135,22 +127,10 @@
 #define SA1111_SKPMC	0x020c
 #define SA1111_SKPTC	0x0210
 #define SA1111_SKPEN0	0x0214
-#define SA1111_SKPWN0	0x0218
+#define SA1111_SKPWM0	0x0218
 #define SA1111_SKPEN1	0x021c
 #define SA1111_SKPWM1	0x0220
 
-#define _SKPCR		_SA1111(SA1111_SKPCR)
-#define _SKCDR		_SA1111(SA1111_SKCDR)
-#define _SKAUD		_SA1111(SA1111_SKAUD)
-#define _SKPMC		_SA1111(SA1111_SKPMC)
-#define _SKPTC		_SA1111(SA1111_SKPTC)
-#define _SKPEN0		_SA1111(SA1111_SKPEN0)
-#define _SKPWM0		_SA1111(SA1111_SKPWM0)
-#define _SKPEN1		_SA1111(SA1111_SKPEN1)
-#define _SKPWM1		_SA1111(SA1111_SKPWM1)
-
-#if LANGUAGE == C
-
 #define SKPCR		__CCREG(SA1111_SKPCR)
 #define SKCDR		__CCREG(SA1111_SKCDR)
 #define SKAUD		__CCREG(SA1111_SKAUD)
@@ -161,8 +141,6 @@
 #define SKPEN1		__CCREG(SA1111_SKPEN1)
 #define SKPWM1		__CCREG(SA1111_SKPWM1)
 
-#endif  /* LANGUAGE == C */
-
 #define SKPCR_UCLKEN	(1<<0)
 #define SKPCR_ACCLKEN	(1<<1)
 #define SKPCR_I2SCLKEN	(1<<2)
@@ -176,21 +154,14 @@
 /*
  * USB Host controller
  */
-#define _USB_OHCI_OP_BASE	_SA1111( 0x400 )
-#define _USB_STATUS		_SA1111( 0x518 )
-#define _USB_RESET		_SA1111( 0x51c )
-#define _USB_INTERRUPTEST	_SA1111( 0x520 )
-
-#define _USB_EXTENT		(_USB_INTERRUPTEST - _USB_OHCI_OP_BASE + 4)
-
-#if LANGUAGE == C
-
-#define USB_OHCI_OP_BASE	__CCREG(0x0400)
-#define USB_STATUS		__CCREG(0x0518)
-#define USB_RESET		__CCREG(0x051c)
-#define USB_INTERRUPTEST	__CCReG(0x0520)
+#define SA1111_USB		0x0400
 
-#endif  /* LANGUAGE == C */
+/*
+ * Offsets from SA1111_USB_BASE
+ */
+#define SA1111_USB_STATUS	0x0118
+#define SA1111_USB_RESET	0x011c
+#define SA1111_USB_IRQTEST	0x0120
 
 #define USB_RESET_FORCEIFRESET	(1 << 0)
 #define USB_RESET_FORCEHCRESET	(1 << 1)
@@ -451,82 +422,56 @@
  *    WAKE_POL0		Wake-up polarity selection 0
  *    WAKE_POL1		Wake-up polarity selection 1
  */
+#define SA1111_INTC		0x1600
 
-#define SA1111_INTTEST0		0x1600
-#define SA1111_INTTEST1		0x1604
-#define SA1111_INTEN0		0x1608
-#define SA1111_INTEN1		0x160c
-#define SA1111_INTPOL0		0x1610
-#define SA1111_INTPOL1		0x1614
-#define SA1111_INTTSTSEL	0x1618
-#define SA1111_INTSTATCLR0	0x161c
-#define SA1111_INTSTATCLR1	0x1620
-#define SA1111_INTSET0		0x1624
-#define SA1111_INTSET1		0x1628
-#define SA1111_WAKE_EN0		0x162c
-#define SA1111_WAKE_EN1		0x1630
-#define SA1111_WAKE_POL0	0x1634
-#define SA1111_WAKE_POL1	0x1638
-
-#define _INTTEST0	_SA1111(SA1111_INTTEST0)
-#define _INTTEST1	_SA1111(SA1111_INTTEST1)
-#define _INTEN0		_SA1111(SA1111_INTEN0)
-#define _INTEN1		_SA1111(SA1111_INTEN1)
-#define _INTPOL0	_SA1111(SA1111_INTPOL0)
-#define _INTPOL1	_SA1111(SA1111_INTPOL1)
-#define _INTTSTSEL	_SA1111(SA1111_INTTSTSEL)
-#define _INTSTATCLR0	_SA1111(SA1111_INTSTATCLR0)
-#define _INTSTATCLR1	_SA1111(SA1111_INTSTATCLR1)
-#define _INTSET0	_SA1111(SA1111_INTSET0)
-#define _INTSET1	_SA1111(SA1111_INTSET1)
-#define _WAKE_EN0	_SA1111(SA1111_WAKE_EN0)
-#define _WAKE_EN1	_SA1111(SA1111_WAKE_EN1)
-#define _WAKE_POL0	_SA1111(SA1111_WAKE_POL0)
-#define _WAKE_POL1	_SA1111(SA1111_WAKE_POL1)
-
-#if LANGUAGE == C
-
-#define INTTEST0	__CCREG(SA1111_INTTEST0)
-#define INTTEST1	__CCREG(SA1111_INTTEST1)
-#define INTEN0		__CCREG(SA1111_INTEN0)
-#define INTEN1		__CCREG(SA1111_INTEN1)
-#define INTPOL0		__CCREG(SA1111_INTPOL0)
-#define INTPOL1		__CCREG(SA1111_INTPOL1)
-#define INTTSTSEL	__CCREG(SA1111_INTTSTSEL)
-#define INTSTATCLR0	__CCREG(SA1111_INTSTATCLR0)
-#define INTSTATCLR1	__CCREG(SA1111_INTSTATCLR1)
-#define INTSET0		__CCREG(SA1111_INTSET0)
-#define INTSET1		__CCREG(SA1111_INTSET1)
-#define WAKE_EN0	__CCREG(SA1111_WAKE_EN0)
-#define WAKE_EN1	__CCREG(SA1111_WAKE_EN1)
-#define WAKE_POL0	__CCREG(SA1111_WAKE_POL0)
-#define WAKE_POL1	__CCREG(SA1111_WAKE_POL1)
-
-#endif  /* LANGUAGE == C */
+/*
+ * These are offsets from the above base.
+ */
+#define SA1111_INTTEST0		0x0000
+#define SA1111_INTTEST1		0x0004
+#define SA1111_INTEN0		0x0008
+#define SA1111_INTEN1		0x000c
+#define SA1111_INTPOL0		0x0010
+#define SA1111_INTPOL1		0x0014
+#define SA1111_INTTSTSEL	0x0018
+#define SA1111_INTSTATCLR0	0x001c
+#define SA1111_INTSTATCLR1	0x0020
+#define SA1111_INTSET0		0x0024
+#define SA1111_INTSET1		0x0028
+#define SA1111_WAKEEN0		0x002c
+#define SA1111_WAKEEN1		0x0030
+#define SA1111_WAKEPOL0		0x0034
+#define SA1111_WAKEPOL1		0x0038
+
+#define INTTEST0	__CCREG(SA1111_INTC + SA1111_INTTEST0)
+#define INTTEST1	__CCREG(SA1111_INTC + SA1111_INTTEST1)
+#define INTEN0		__CCREG(SA1111_INTC + SA1111_INTEN0)
+#define INTEN1		__CCREG(SA1111_INTC + SA1111_INTEN1)
+#define INTPOL0		__CCREG(SA1111_INTC + SA1111_INTPOL0)
+#define INTPOL1		__CCREG(SA1111_INTC + SA1111_INTPOL1)
+#define INTTSTSEL	__CCREG(SA1111_INTC + SA1111_INTTSTSEL)
+#define INTSTATCLR0	__CCREG(SA1111_INTC + SA1111_INTSTATCLR0)
+#define INTSTATCLR1	__CCREG(SA1111_INTC + SA1111_INTSTATCLR1)
+#define INTSET0		__CCREG(SA1111_INTC + SA1111_INTSET0)
+#define INTSET1		__CCREG(SA1111_INTC + SA1111_INTSET1)
+#define WAKE_EN0	__CCREG(SA1111_INTC + SA1111_WAKEEN0)
+#define WAKE_EN1	__CCREG(SA1111_INTC + SA1111_WAKEEN1)
+#define WAKE_POL0	__CCREG(SA1111_INTC + SA1111_WAKEPOL0)
+#define WAKE_POL1	__CCREG(SA1111_INTC + SA1111_WAKEPOL1)
 
 /*
  * PS/2 Trackpad and Mouse Interfaces
  *
- * Registers   (prefix kbd applies to trackpad interface, mse to mouse)
- *    KBDCR     Control Register
- *    KBDSTAT       Status Register
- *    KBDDATA       Transmit/Receive Data register
- *    KBDCLKDIV     Clock Division Register
- *    KBDPRECNT     Clock Precount Register
- *    KBDTEST1      Test register 1
- *    KBDTEST2      Test register 2
- *    KBDTEST3      Test register 3
- *    KBDTEST4      Test register 4
- *    MSECR
- *    MSESTAT
- *    MSEDATA
- *    MSECLKDIV
- *    MSEPRECNT
- *    MSETEST1
- *    MSETEST2
- *    MSETEST3
- *    MSETEST4
- *
+ * Registers
+ *    PS2CR		Control Register
+ *    PS2STAT		Status Register
+ *    PS2DATA		Transmit/Receive Data register
+ *    PS2CLKDIV		Clock Division Register
+ *    PS2PRECNT		Clock Precount Register
+ *    PS2TEST1		Test register 1
+ *    PS2TEST2		Test register 2
+ *    PS2TEST3		Test register 3
+ *    PS2TEST4		Test register 4
  */
 
 #define SA1111_KBD		0x0a00
@@ -564,17 +509,14 @@
  *    PCSSR	Sleep State Register
  */
 
-#define _PCCR		_SA1111( 0x1800 )
-#define _PCSSR		_SA1111( 0x1804 )
-#define _PCSR		_SA1111( 0x1808 )
-
-#if LANGUAGE == C
-
-#define PCCR		__CCREG(0x1800)
-#define PCSSR		__CCREG(0x1804)
-#define PCSR		__CCREG(0x1808)
+#define SA1111_PCMCIA	0x1600
 
-#endif  /* LANGUAGE == C */
+/*
+ * These are offsets from the above base.
+ */
+#define SA1111_PCCR	0x0000
+#define SA1111_PCSSR	0x0004
+#define SA1111_PCSR	0x0008
 
 #define PCSR_S0_READY	(1<<0)
 #define PCSR_S1_READY	(1<<1)
@@ -603,21 +545,61 @@
 #define PCSSR_S0_SLEEP	(1<<0)
 #define PCSSR_S1_SLEEP	(1<<1)
 
-struct sa1111_device {
+
+
+
+extern struct bus_type sa1111_bus_type;
+
+#define SA1111_DEVID_SBI	0
+#define SA1111_DEVID_SK		1
+#define SA1111_DEVID_USB	2
+#define SA1111_DEVID_SAC	3
+#define SA1111_DEVID_SSP	4
+#define SA1111_DEVID_PS2	5
+#define SA1111_DEVID_GPIO	6
+#define SA1111_DEVID_INT	7
+#define SA1111_DEVID_PCMCIA	8
+
+struct sa1111_dev {
 	struct device	dev;
-	struct resource	resource;
-	void		*base;
+	unsigned int	devid;
+	struct resource	res;
+	void		*mapbase;
+	unsigned int	skpcr_mask;
+	unsigned int	irq[6];
 };
 
-extern struct sa1111_device *sa1111;
+#define SA1111_DEV(_d)	container_of((_d), struct sa1111_dev, dev)
 
-int sa1111_check_dma_bug(dma_addr_t addr);
+struct sa1111_driver {
+	struct device_driver	drv;
+	unsigned int		devid;
+};
+
+#define SA1111_DRV(_d)	container_of((_d), struct sa1111_driver, drv)
+
+#define SA1111_DRIVER_NAME(_sadev) ((_sadev)->dev.driver->name)
+
+/*
+ * Probe for a SA1111 chip.
+ */
+extern int sa1111_init(unsigned long phys, unsigned int irq);
 
 /*
  * These frob the SKPCR register.
  */
-void sa1111_enable_device(unsigned int mask);
-void sa1111_disable_device(unsigned int mask);
+void sa1111_enable_device(struct sa1111_dev *);
+void sa1111_disable_device(struct sa1111_dev *);
+
+unsigned int sa1111_pll_clock(struct sa1111_dev *);
 
+#define SA1111_AUDIO_ACLINK	0
+#define SA1111_AUDIO_I2S	1
+
+void sa1111_select_audio_mode(struct sa1111_dev *sadev, int mode);
+int sa1111_set_audio_rate(struct sa1111_dev *sadev, int rate);
+int sa1111_get_audio_rate(struct sa1111_dev *sadev);
+
+int sa1111_check_dma_bug(dma_addr_t addr);
 
 #endif  /* _ASM_ARCH_SA1111 */
diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h
index 70fcb5c4d021e0309a954d6c722c42936b4c215d..3a7310558dd2a7957ff9c728b3e858973c1816a9 100644
--- a/include/asm-arm/pci.h
+++ b/include/asm-arm/pci.h
@@ -27,15 +27,11 @@ void sa1111_dma_sync_sg(struct pci_dev *, struct scatterlist *, int, int);
 #ifdef CONFIG_SA1111
 
 #define SA1111_FAKE_PCIDEV ((struct pci_dev *) 1111)
-
-static inline int dev_is_sa1111(const struct pci_dev *dev)
-{
-	return (dev == SA1111_FAKE_PCIDEV);
-}
+#define dev_is_sa1111(dev) (dev == SA1111_FAKE_PCIDEV)
 
 #else
 
-static inline int dev_is_sa1111(const struct pci_dev *dev) { return 0; }
+#define dev_is_sa1111(dev) (0)
 
 #endif