Index: linux/arch/arm/mach-sa1100/ucb1x00.c
===================================================================
--- /dev/null
+++ linux/arch/arm/mach-sa1100/ucb1x00.c
@@ -0,0 +1,242 @@
+/*
+ *  linux/arch/arm/mach-sa1100/ucb1x00.c
+ *
+ *  Copyright (C) 2005 John Lenz
+ *  Based on code written by Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ *  SA1100 UCB Register access driver.
+ *
+ *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/ucb1x00.h>
+
+#include <asm/hardware.h>
+#include <asm/system.h>
+
+struct ucb_sa1100_reg_access {
+	struct ucb1x00_reg_access reg_access;
+
+	unsigned int	rw_timeout;
+
+#ifdef CONFIG_PM
+	/* used for suspend and resume */
+	u32	mccr0;
+	u32	mccr1;
+#endif
+};
+#define UCB_SA1100_REG_ACCESS(access) container_of(access, struct ucb_sa1100_reg_access, reg_access)
+
+#define UCB_SA1100_SCLK_RATE 11981000
+
+static void
+ucb_sa1100_set_telecom_divisor(struct ucb1x00_reg_access *access, unsigned int divisor)
+{
+	unsigned int mccr0;
+
+	divisor /= 32;
+
+	mccr0 = Ser4MCCR0 & ~0x00007f00;
+	mccr0 |= divisor << 8;
+	Ser4MCCR0 = mccr0;
+}
+
+static void
+ucb_sa1100_set_audio_divisor(struct ucb1x00_reg_access *access, unsigned int divisor)
+{
+	unsigned int mccr0;
+
+	divisor /= 32;
+
+	mccr0 = Ser4MCCR0 & ~0x0000007f;
+	mccr0 |= divisor;
+	Ser4MCCR0 = mccr0;
+}
+
+/*
+ * Write data to the device.  The bit should be set after 3 subframe
+ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
+ * We really should try doing something more productive while we
+ * wait.
+ */
+static void
+ucb_sa1100_write(struct ucb1x00_reg_access *access, unsigned int reg, unsigned int val)
+{
+	int ret = -ETIME;
+	int i;
+	struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access);
+
+	Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
+
+	for (i = 0; i < 2; i++) {
+		udelay(ucb->rw_timeout);
+		if (Ser4MCSR & MCSR_CWC) {
+			ret = 0;
+			break;
+		}
+	}
+
+	if (ret < 0)
+		printk(KERN_WARNING "ucb sa1100: write timed out\n");
+}
+
+/*
+ * Read data from the device.  The bit should be set after 3 subframe
+ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
+ * We really should try doing something more productive while we
+ * wait.
+ */
+static unsigned int
+ucb_sa1100_read(struct ucb1x00_reg_access *access, unsigned int reg)
+{
+	int ret = -ETIME;
+	int i;
+	struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access);
+
+	Ser4MCDR2 = reg << 17 | MCDR2_Rd;
+
+	for (i = 0; i < 2; i++) {
+		udelay(ucb->rw_timeout);
+		if (Ser4MCSR & MCSR_CRC) {
+			ret = Ser4MCDR2 & 0xffff;
+			break;
+		}
+	}
+
+	if (ret < 0)
+		printk(KERN_WARNING "mcp: read timed out\n");
+
+	return ret;
+}
+
+static void ucb_sa1100_enable(struct ucb1x00_reg_access *access)
+{
+	Ser4MCSR = -1;
+	Ser4MCCR0 |= MCCR0_MCE;
+}
+
+static void ucb_sa1100_disable(struct ucb1x00_reg_access *access)
+{
+	Ser4MCCR0 &= ~MCCR0_MCE;
+}
+
+#ifdef CONFIG_PM
+static int ucb_sa1100_suspend(struct ucb1x00_reg_access *access, pm_message_t state, u32 level)
+{
+	struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access);
+
+	if (ucb) {
+		ucb->mccr0 = Ser4MCCR0;
+		ucb->mccr1 = Ser4MCCR1;
+	}
+
+	if (level == SUSPEND_DISABLE)
+		Ser4MCCR0 &= ~MCCR0_MCE;
+	return 0;
+}
+
+static int ucb_sa1100_resume(struct ucb1x00_reg_access *access, u32 level)
+{
+	struct ucb_sa1100_reg_access *ucb = UCB_SA1100_REG_ACCESS(access);
+
+	if (ucb && level == RESUME_RESTORE_STATE) {
+		Ser4MCCR1 = ucb->mccr1;
+		Ser4MCCR0 = ucb->mccr0;
+	}
+	return 0;
+}
+#else
+#define ucb_sa1100_suspend NULL
+#define ucb_sa1100_resume NULL
+#endif
+
+static struct ucb_sa1100_reg_access ucb_sa1100_reg_access = {
+	.reg_access	= {
+		.enable			= ucb_sa1100_enable,
+		.disable		= ucb_sa1100_disable,
+		.read			= ucb_sa1100_read,
+		.write			= ucb_sa1100_write,
+
+		.audio_divisor		= ucb_sa1100_set_audio_divisor,
+		.telecom_divisor	= ucb_sa1100_set_telecom_divisor,
+
+		.clkrate		= UCB_SA1100_SCLK_RATE,
+
+		.suspend		= ucb_sa1100_suspend,
+		.resume			= ucb_sa1100_resume,
+	},
+};
+
+static struct platform_device ucb_sa1100_dev = {
+	.name		= "ucb1x00",
+	.id		= 0,
+	.dev		= {
+		.platform_data	= &ucb_sa1100_reg_access,
+	},
+};
+
+int sa1100_ucb_init(struct resource *resources, int nr, int ext_clock)
+{
+	int ret;
+
+	ucb_sa1100_dev.resource = resources;
+	ucb_sa1100_dev.num_resources = nr;
+
+	if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
+		return -EBUSY;
+
+	/*if (machine_is_assabet()) {
+		ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+	}*/
+
+	/*
+	 * Setup the PPC unit correctly.
+	 */
+	PPDR &= ~PPC_RXD4;
+	PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+	PSDR |= PPC_RXD4;
+	PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+	PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
+	Ser4MCSR = -1;
+	Ser4MCCR1 = 0;
+	if (ext_clock) 
+		Ser4MCCR0 = 0x00007f7f | MCCR0_ADM | MCCR0_ExtClk;
+	else
+		Ser4MCCR0 = 0x00007f7f | MCCR0_ADM | MCCR0_IntClk;
+
+	/*
+	 * Calculate the read/write timeout (us) from the bit clock
+	 * rate.  This is the period for 3 64-bit frames.  Always
+	 * round this time up.
+	 */
+	ucb_sa1100_reg_access.rw_timeout = (64 * 3 * 1000000 + ucb_sa1100_reg_access.reg_access.clkrate - 1) /
+			  ucb_sa1100_reg_access.reg_access.clkrate;
+
+	ret = platform_device_register(&ucb_sa1100_dev);
+	if (ret)
+		printk(KERN_ERR "ucb sa1100: unable to register platform device\n");
+	
+	return ret;
+}
+
+/*void ucb_sa1100_exit(void)
+{
+	platform_device_unregister(&ucb_sa1100_dev);
+	release_mem_region(0x80060000, 0x60);
+}*/
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("SA11x0 UCB register access driver");
+MODULE_LICENSE("GPL");
Index: linux/arch/arm/mach-sa1100/generic.h
===================================================================
--- linux.orig/arch/arm/mach-sa1100/generic.h
+++ linux/arch/arm/mach-sa1100/generic.h
@@ -36,3 +36,5 @@
 
 struct irda_platform_data;
 void sa11x0_set_irda_data(struct irda_platform_data *irda);
+
+int sa1100_ucb_init(struct resource *resources, int nr, int ext_clock);
