Linux-2.6.12-rc2

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

Let it rip!
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
new file mode 100644
index 0000000..692af6b
--- /dev/null
+++ b/arch/arm/common/Kconfig
@@ -0,0 +1,24 @@
+config ICST525
+	bool
+
+config ICST307
+	bool
+
+config SA1111
+	bool
+	select DMABOUNCE
+
+config DMABOUNCE
+	bool
+
+config TIMER_ACORN
+	bool
+
+config SHARP_LOCOMO
+	bool
+
+config SHARP_PARAM
+	bool
+
+config SHARP_SCOOP
+	bool
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
new file mode 100644
index 0000000..11f20a4
--- /dev/null
+++ b/arch/arm/common/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux kernel.
+#
+
+obj-y				+= rtctime.o
+obj-$(CONFIG_ARM_AMBA)		+= amba.o
+obj-$(CONFIG_ICST525)		+= icst525.o
+obj-$(CONFIG_ICST307)		+= icst307.o
+obj-$(CONFIG_SA1111)		+= sa1111.o
+obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o
+obj-$(CONFIG_DMABOUNCE)		+= dmabounce.o
+obj-$(CONFIG_TIMER_ACORN)	+= time-acorn.o
+obj-$(CONFIG_SHARP_LOCOMO)	+= locomo.o
+obj-$(CONFIG_SHARP_PARAM)	+= sharpsl_param.o
+obj-$(CONFIG_SHARP_SCOOP)	+= scoop.o
diff --git a/arch/arm/common/amba.c b/arch/arm/common/amba.c
new file mode 100644
index 0000000..a0507f8
--- /dev/null
+++ b/arch/arm/common/amba.c
@@ -0,0 +1,357 @@
+/*
+ *  linux/arch/arm/common/amba.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+#include <asm/sizes.h>
+
+#define to_amba_device(d)	container_of(d, struct amba_device, dev)
+#define to_amba_driver(d)	container_of(d, struct amba_driver, drv)
+
+static struct amba_id *
+amba_lookup(struct amba_id *table, struct amba_device *dev)
+{
+	int ret = 0;
+
+	while (table->mask) {
+		ret = (dev->periphid & table->mask) == table->id;
+		if (ret)
+			break;
+		table++;
+	}
+
+	return ret ? table : NULL;
+}
+
+static int amba_match(struct device *dev, struct device_driver *drv)
+{
+	struct amba_device *pcdev = to_amba_device(dev);
+	struct amba_driver *pcdrv = to_amba_driver(drv);
+
+	return amba_lookup(pcdrv->id_table, pcdev) != NULL;
+}
+
+#ifdef CONFIG_HOTPLUG
+static int amba_hotplug(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
+{
+	struct amba_device *pcdev = to_amba_device(dev);
+
+	if (nr_env < 2)
+		return -ENOMEM;
+
+	snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid);
+	*envp++ = buf;
+	*envp++ = NULL;
+	return 0;
+}
+#else
+#define amba_hotplug NULL
+#endif
+
+static int amba_suspend(struct device *dev, pm_message_t state)
+{
+	struct amba_driver *drv = to_amba_driver(dev->driver);
+	int ret = 0;
+
+	if (dev->driver && drv->suspend)
+		ret = drv->suspend(to_amba_device(dev), state);
+	return ret;
+}
+
+static int amba_resume(struct device *dev)
+{
+	struct amba_driver *drv = to_amba_driver(dev->driver);
+	int ret = 0;
+
+	if (dev->driver && drv->resume)
+		ret = drv->resume(to_amba_device(dev));
+	return ret;
+}
+
+/*
+ * Primecells are part of the Advanced Microcontroller Bus Architecture,
+ * so we call the bus "amba".
+ */
+static struct bus_type amba_bustype = {
+	.name		= "amba",
+	.match		= amba_match,
+	.hotplug	= amba_hotplug,
+	.suspend	= amba_suspend,
+	.resume		= amba_resume,
+};
+
+static int __init amba_init(void)
+{
+	return bus_register(&amba_bustype);
+}
+
+postcore_initcall(amba_init);
+
+/*
+ * These are the device model conversion veneers; they convert the
+ * device model structures to our more specific structures.
+ */
+static int amba_probe(struct device *dev)
+{
+	struct amba_device *pcdev = to_amba_device(dev);
+	struct amba_driver *pcdrv = to_amba_driver(dev->driver);
+	struct amba_id *id;
+
+	id = amba_lookup(pcdrv->id_table, pcdev);
+
+	return pcdrv->probe(pcdev, id);
+}
+
+static int amba_remove(struct device *dev)
+{
+	struct amba_driver *drv = to_amba_driver(dev->driver);
+	return drv->remove(to_amba_device(dev));
+}
+
+static void amba_shutdown(struct device *dev)
+{
+	struct amba_driver *drv = to_amba_driver(dev->driver);
+	drv->shutdown(to_amba_device(dev));
+}
+
+/**
+ *	amba_driver_register - register an AMBA device driver
+ *	@drv: amba device driver structure
+ *
+ *	Register an AMBA device driver with the Linux device model
+ *	core.  If devices pre-exist, the drivers probe function will
+ *	be called.
+ */
+int amba_driver_register(struct amba_driver *drv)
+{
+	drv->drv.bus = &amba_bustype;
+
+#define SETFN(fn)	if (drv->fn) drv->drv.fn = amba_##fn
+	SETFN(probe);
+	SETFN(remove);
+	SETFN(shutdown);
+
+	return driver_register(&drv->drv);
+}
+
+/**
+ *	amba_driver_unregister - remove an AMBA device driver
+ *	@drv: AMBA device driver structure to remove
+ *
+ *	Unregister an AMBA device driver from the Linux device
+ *	model.  The device model will call the drivers remove function
+ *	for each device the device driver is currently handling.
+ */
+void amba_driver_unregister(struct amba_driver *drv)
+{
+	driver_unregister(&drv->drv);
+}
+
+
+static void amba_device_release(struct device *dev)
+{
+	struct amba_device *d = to_amba_device(dev);
+
+	if (d->res.parent)
+		release_resource(&d->res);
+	kfree(d);
+}
+
+#define amba_attr(name,fmt,arg...)				\
+static ssize_t show_##name(struct device *_dev, char *buf)	\
+{								\
+	struct amba_device *dev = to_amba_device(_dev);		\
+	return sprintf(buf, fmt, arg);				\
+}								\
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
+
+amba_attr(id, "%08x\n", dev->periphid);
+amba_attr(irq0, "%u\n", dev->irq[0]);
+amba_attr(irq1, "%u\n", dev->irq[1]);
+amba_attr(resource, "\t%08lx\t%08lx\t%08lx\n",
+	  dev->res.start, dev->res.end, dev->res.flags);
+
+/**
+ *	amba_device_register - register an AMBA device
+ *	@dev: AMBA device to register
+ *	@parent: parent memory resource
+ *
+ *	Setup the AMBA device, reading the cell ID if present.
+ *	Claim the resource, and register the AMBA device with
+ *	the Linux device manager.
+ */
+int amba_device_register(struct amba_device *dev, struct resource *parent)
+{
+	u32 pid, cid;
+	void __iomem *tmp;
+	int i, ret;
+
+	dev->dev.release = amba_device_release;
+	dev->dev.bus = &amba_bustype;
+	dev->dev.dma_mask = &dev->dma_mask;
+	dev->res.name = dev->dev.bus_id;
+
+	if (!dev->dev.coherent_dma_mask && dev->dma_mask)
+		dev_warn(&dev->dev, "coherent dma mask is unset\n");
+
+	ret = request_resource(parent, &dev->res);
+	if (ret == 0) {
+		tmp = ioremap(dev->res.start, SZ_4K);
+		if (!tmp) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		for (pid = 0, i = 0; i < 4; i++)
+			pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8);
+		for (cid = 0, i = 0; i < 4; i++)
+			cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8);
+
+		iounmap(tmp);
+
+		if (cid == 0xb105f00d)
+			dev->periphid = pid;
+
+		if (dev->periphid)
+			ret = device_register(&dev->dev);
+		else
+			ret = -ENODEV;
+
+		if (ret == 0) {
+			device_create_file(&dev->dev, &dev_attr_id);
+			if (dev->irq[0] != NO_IRQ)
+				device_create_file(&dev->dev, &dev_attr_irq0);
+			if (dev->irq[1] != NO_IRQ)
+				device_create_file(&dev->dev, &dev_attr_irq1);
+			device_create_file(&dev->dev, &dev_attr_resource);
+		} else {
+ out:
+			release_resource(&dev->res);
+		}
+	}
+	return ret;
+}
+
+/**
+ *	amba_device_unregister - unregister an AMBA device
+ *	@dev: AMBA device to remove
+ *
+ *	Remove the specified AMBA device from the Linux device
+ *	manager.  All files associated with this object will be
+ *	destroyed, and device drivers notified that the device has
+ *	been removed.  The AMBA device's resources including
+ *	the amba_device structure will be freed once all
+ *	references to it have been dropped.
+ */
+void amba_device_unregister(struct amba_device *dev)
+{
+	device_unregister(&dev->dev);
+}
+
+
+struct find_data {
+	struct amba_device *dev;
+	struct device *parent;
+	const char *busid;
+	unsigned int id;
+	unsigned int mask;
+};
+
+static int amba_find_match(struct device *dev, void *data)
+{
+	struct find_data *d = data;
+	struct amba_device *pcdev = to_amba_device(dev);
+	int r;
+
+	r = (pcdev->periphid & d->mask) == d->id;
+	if (d->parent)
+		r &= d->parent == dev->parent;
+	if (d->busid)
+		r &= strcmp(dev->bus_id, d->busid) == 0;
+
+	if (r) {
+		get_device(dev);
+		d->dev = pcdev;
+	}
+
+	return r;
+}
+
+/**
+ *	amba_find_device - locate an AMBA device given a bus id
+ *	@busid: bus id for device (or NULL)
+ *	@parent: parent device (or NULL)
+ *	@id: peripheral ID (or 0)
+ *	@mask: peripheral ID mask (or 0)
+ *
+ *	Return the AMBA device corresponding to the supplied parameters.
+ *	If no device matches, returns NULL.
+ *
+ *	NOTE: When a valid device is found, its refcount is
+ *	incremented, and must be decremented before the returned
+ *	reference.
+ */
+struct amba_device *
+amba_find_device(const char *busid, struct device *parent, unsigned int id,
+		 unsigned int mask)
+{
+	struct find_data data;
+
+	data.dev = NULL;
+	data.parent = parent;
+	data.busid = busid;
+	data.id = id;
+	data.mask = mask;
+
+	bus_for_each_dev(&amba_bustype, NULL, &data, amba_find_match);
+
+	return data.dev;
+}
+
+/**
+ *	amba_request_regions - request all mem regions associated with device
+ *	@dev: amba_device structure for device
+ *	@name: name, or NULL to use driver name
+ */
+int amba_request_regions(struct amba_device *dev, const char *name)
+{
+	int ret = 0;
+
+	if (!name)
+		name = dev->dev.driver->name;
+
+	if (!request_mem_region(dev->res.start, SZ_4K, name))
+		ret = -EBUSY;
+
+	return ret;
+}
+
+/**
+ *	amba_release_regions - release mem regions assoicated with device
+ *	@dev: amba_device structure for device
+ *
+ *	Release regions claimed by a successful call to amba_request_regions.
+ */
+void amba_release_regions(struct amba_device *dev)
+{
+	release_mem_region(dev->res.start, SZ_4K);
+}
+
+EXPORT_SYMBOL(amba_driver_register);
+EXPORT_SYMBOL(amba_driver_unregister);
+EXPORT_SYMBOL(amba_device_register);
+EXPORT_SYMBOL(amba_device_unregister);
+EXPORT_SYMBOL(amba_find_device);
+EXPORT_SYMBOL(amba_request_regions);
+EXPORT_SYMBOL(amba_release_regions);
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
new file mode 100644
index 0000000..5797b1b
--- /dev/null
+++ b/arch/arm/common/dmabounce.c
@@ -0,0 +1,682 @@
+/*
+ *  arch/arm/common/dmabounce.c
+ *
+ *  Special dma_{map/unmap/dma_sync}_* routines for systems that have
+ *  limited DMA windows. These functions utilize bounce buffers to
+ *  copy data to/from buffers located outside the DMA region. This
+ *  only works for systems in which DMA memory is at the bottom of
+ *  RAM and the remainder of memory is at the top an the DMA memory
+ *  can be marked as ZONE_DMA. Anything beyond that such as discontigous
+ *  DMA windows will require custom implementations that reserve memory
+ *  areas at early bootup.
+ *
+ *  Original version by Brad Parker (brad@heeltoe.com)
+ *  Re-written by Christopher Hoover <ch@murgatroid.com>
+ *  Made generic by Deepak Saxena <dsaxena@plexity.net>
+ *
+ *  Copyright (C) 2002 Hewlett Packard Company.
+ *  Copyright (C) 2004 MontaVista Software, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/list.h>
+
+#undef DEBUG
+
+#undef STATS
+#ifdef STATS
+#define DO_STATS(X) do { X ; } while (0)
+#else
+#define DO_STATS(X) do { } while (0)
+#endif
+
+/* ************************************************** */
+
+struct safe_buffer {
+	struct list_head node;
+
+	/* original request */
+	void		*ptr;
+	size_t		size;
+	int		direction;
+
+	/* safe buffer info */
+	struct dma_pool *pool;
+	void		*safe;
+	dma_addr_t	safe_dma_addr;
+};
+
+struct dmabounce_device_info {
+	struct list_head node;
+
+	struct device *dev;
+	struct dma_pool *small_buffer_pool;
+	struct dma_pool *large_buffer_pool;
+	struct list_head safe_buffers;
+	unsigned long small_buffer_size, large_buffer_size;
+#ifdef STATS
+	unsigned long sbp_allocs;
+	unsigned long lbp_allocs;
+	unsigned long total_allocs;
+	unsigned long map_op_count;
+	unsigned long bounce_count;
+#endif
+};
+
+static LIST_HEAD(dmabounce_devs);
+
+#ifdef STATS
+static void print_alloc_stats(struct dmabounce_device_info *device_info)
+{
+	printk(KERN_INFO
+		"%s: dmabounce: sbp: %lu, lbp: %lu, other: %lu, total: %lu\n",
+		device_info->dev->bus_id,
+		device_info->sbp_allocs, device_info->lbp_allocs,
+		device_info->total_allocs - device_info->sbp_allocs -
+			device_info->lbp_allocs,
+		device_info->total_allocs);
+}
+#endif
+
+/* find the given device in the dmabounce device list */
+static inline struct dmabounce_device_info *
+find_dmabounce_dev(struct device *dev)
+{
+	struct list_head *entry;
+
+	list_for_each(entry, &dmabounce_devs) {
+		struct dmabounce_device_info *d =
+			list_entry(entry, struct dmabounce_device_info, node);
+
+		if (d->dev == dev)
+			return d;
+	}
+	return NULL;
+}
+
+
+/* allocate a 'safe' buffer and keep track of it */
+static inline struct safe_buffer *
+alloc_safe_buffer(struct dmabounce_device_info *device_info, void *ptr,
+			size_t size, enum dma_data_direction dir)
+{
+	struct safe_buffer *buf;
+	struct dma_pool *pool;
+	struct device *dev = device_info->dev;
+	void *safe;
+	dma_addr_t safe_dma_addr;
+
+	dev_dbg(dev, "%s(ptr=%p, size=%d, dir=%d)\n",
+		__func__, ptr, size, dir);
+
+	DO_STATS ( device_info->total_allocs++ );
+
+	buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
+	if (buf == NULL) {
+		dev_warn(dev, "%s: kmalloc failed\n", __func__);
+		return NULL;
+	}
+
+	if (size <= device_info->small_buffer_size) {
+		pool = device_info->small_buffer_pool;
+		safe = dma_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr);
+
+		DO_STATS ( device_info->sbp_allocs++ );
+	} else if (size <= device_info->large_buffer_size) {
+		pool = device_info->large_buffer_pool;
+		safe = dma_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr);
+
+		DO_STATS ( device_info->lbp_allocs++ );
+	} else {
+		pool = NULL;
+		safe = dma_alloc_coherent(dev, size, &safe_dma_addr, GFP_ATOMIC);
+	}
+
+	if (safe == NULL) {
+		dev_warn(device_info->dev,
+			"%s: could not alloc dma memory (size=%d)\n",
+		       __func__, size);
+		kfree(buf);
+		return NULL;
+	}
+
+#ifdef STATS
+	if (device_info->total_allocs % 1000 == 0)
+		print_alloc_stats(device_info);
+#endif
+
+	buf->ptr = ptr;
+	buf->size = size;
+	buf->direction = dir;
+	buf->pool = pool;
+	buf->safe = safe;
+	buf->safe_dma_addr = safe_dma_addr;
+
+	list_add(&buf->node, &device_info->safe_buffers);
+
+	return buf;
+}
+
+/* determine if a buffer is from our "safe" pool */
+static inline struct safe_buffer *
+find_safe_buffer(struct dmabounce_device_info *device_info, dma_addr_t safe_dma_addr)
+{
+	struct list_head *entry;
+
+	list_for_each(entry, &device_info->safe_buffers) {
+		struct safe_buffer *b =
+			list_entry(entry, struct safe_buffer, node);
+
+		if (b->safe_dma_addr == safe_dma_addr)
+			return b;
+	}
+
+	return NULL;
+}
+
+static inline void
+free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf)
+{
+	dev_dbg(device_info->dev, "%s(buf=%p)\n", __func__, buf);
+
+	list_del(&buf->node);
+
+	if (buf->pool)
+		dma_pool_free(buf->pool, buf->safe, buf->safe_dma_addr);
+	else
+		dma_free_coherent(device_info->dev, buf->size, buf->safe,
+				    buf->safe_dma_addr);
+
+	kfree(buf);
+}
+
+/* ************************************************** */
+
+#ifdef STATS
+
+static void print_map_stats(struct dmabounce_device_info *device_info)
+{
+	printk(KERN_INFO
+		"%s: dmabounce: map_op_count=%lu, bounce_count=%lu\n",
+		device_info->dev->bus_id,
+		device_info->map_op_count, device_info->bounce_count);
+}
+#endif
+
+static inline dma_addr_t
+map_single(struct device *dev, void *ptr, size_t size,
+		enum dma_data_direction dir)
+{
+	struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
+	dma_addr_t dma_addr;
+	int needs_bounce = 0;
+
+	if (device_info)
+		DO_STATS ( device_info->map_op_count++ );
+
+	dma_addr = virt_to_dma(dev, ptr);
+
+	if (dev->dma_mask) {
+		unsigned long mask = *dev->dma_mask;
+		unsigned long limit;
+
+		limit = (mask + 1) & ~mask;
+		if (limit && size > limit) {
+			dev_err(dev, "DMA mapping too big (requested %#x "
+				"mask %#Lx)\n", size, *dev->dma_mask);
+			return ~0;
+		}
+
+		/*
+		 * Figure out if we need to bounce from the DMA mask.
+		 */
+		needs_bounce = (dma_addr | (dma_addr + size - 1)) & ~mask;
+	}
+
+	if (device_info && (needs_bounce || dma_needs_bounce(dev, dma_addr, size))) {
+		struct safe_buffer *buf;
+
+		buf = alloc_safe_buffer(device_info, ptr, size, dir);
+		if (buf == 0) {
+			dev_err(dev, "%s: unable to map unsafe buffer %p!\n",
+			       __func__, ptr);
+			return 0;
+		}
+
+		dev_dbg(dev,
+			"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
+			__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
+			buf->safe, (void *) buf->safe_dma_addr);
+
+		if ((dir == DMA_TO_DEVICE) ||
+		    (dir == DMA_BIDIRECTIONAL)) {
+			dev_dbg(dev, "%s: copy unsafe %p to safe %p, size %d\n",
+				__func__, ptr, buf->safe, size);
+			memcpy(buf->safe, ptr, size);
+		}
+		consistent_sync(buf->safe, size, dir);
+
+		dma_addr = buf->safe_dma_addr;
+	} else {
+		consistent_sync(ptr, size, dir);
+	}
+
+	return dma_addr;
+}
+
+static inline void
+unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+		enum dma_data_direction dir)
+{
+	struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
+	struct safe_buffer *buf = NULL;
+
+	/*
+	 * Trying to unmap an invalid mapping
+	 */
+	if (dma_addr == ~0) {
+		dev_err(dev, "Trying to unmap invalid mapping\n");
+		return;
+	}
+
+	if (device_info)
+		buf = find_safe_buffer(device_info, dma_addr);
+
+	if (buf) {
+		BUG_ON(buf->size != size);
+
+		dev_dbg(dev,
+			"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
+			__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
+			buf->safe, (void *) buf->safe_dma_addr);
+
+
+		DO_STATS ( device_info->bounce_count++ );
+
+		if ((dir == DMA_FROM_DEVICE) ||
+		    (dir == DMA_BIDIRECTIONAL)) {
+			dev_dbg(dev,
+				"%s: copy back safe %p to unsafe %p size %d\n",
+				__func__, buf->safe, buf->ptr, size);
+			memcpy(buf->ptr, buf->safe, size);
+		}
+		free_safe_buffer(device_info, buf);
+	}
+}
+
+static inline void
+sync_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+		enum dma_data_direction dir)
+{
+	struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
+	struct safe_buffer *buf = NULL;
+
+	if (device_info)
+		buf = find_safe_buffer(device_info, dma_addr);
+
+	if (buf) {
+		/*
+		 * Both of these checks from original code need to be
+		 * commented out b/c some drivers rely on the following:
+		 *
+		 * 1) Drivers may map a large chunk of memory into DMA space
+		 *    but only sync a small portion of it. Good example is
+		 *    allocating a large buffer, mapping it, and then
+		 *    breaking it up into small descriptors. No point
+		 *    in syncing the whole buffer if you only have to
+		 *    touch one descriptor.
+		 *
+		 * 2) Buffers that are mapped as DMA_BIDIRECTIONAL are
+		 *    usually only synced in one dir at a time.
+		 *
+		 * See drivers/net/eepro100.c for examples of both cases.
+		 *
+		 * -ds
+		 *
+		 * BUG_ON(buf->size != size);
+		 * BUG_ON(buf->direction != dir);
+		 */
+
+		dev_dbg(dev,
+			"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
+			__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
+			buf->safe, (void *) buf->safe_dma_addr);
+
+		DO_STATS ( device_info->bounce_count++ );
+
+		switch (dir) {
+		case DMA_FROM_DEVICE:
+			dev_dbg(dev,
+				"%s: copy back safe %p to unsafe %p size %d\n",
+				__func__, buf->safe, buf->ptr, size);
+			memcpy(buf->ptr, buf->safe, size);
+			break;
+		case DMA_TO_DEVICE:
+			dev_dbg(dev,
+				"%s: copy out unsafe %p to safe %p, size %d\n",
+				__func__,buf->ptr, buf->safe, size);
+			memcpy(buf->safe, buf->ptr, size);
+			break;
+		case DMA_BIDIRECTIONAL:
+			BUG();	/* is this allowed?  what does it mean? */
+		default:
+			BUG();
+		}
+		consistent_sync(buf->safe, size, dir);
+	} else {
+		consistent_sync(dma_to_virt(dev, dma_addr), size, dir);
+	}
+}
+
+/* ************************************************** */
+
+/*
+ * see if a buffer address is in an 'unsafe' range.  if it is
+ * allocate a 'safe' buffer and copy the unsafe buffer into it.
+ * substitute the safe buffer for the unsafe one.
+ * (basically move the buffer from an unsafe area to a safe one)
+ */
+dma_addr_t
+dma_map_single(struct device *dev, void *ptr, size_t size,
+		enum dma_data_direction dir)
+{
+	unsigned long flags;
+	dma_addr_t dma_addr;
+
+	dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
+		__func__, ptr, size, dir);
+
+	BUG_ON(dir == DMA_NONE);
+
+	local_irq_save(flags);
+
+	dma_addr = map_single(dev, ptr, size, dir);
+
+	local_irq_restore(flags);
+
+	return dma_addr;
+}
+
+/*
+ * see if a mapped address was really a "safe" buffer and if so, copy
+ * the data from the safe buffer back to the unsafe buffer and free up
+ * the safe buffer.  (basically return things back to the way they
+ * should be)
+ */
+
+void
+dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+			enum dma_data_direction dir)
+{
+	unsigned long flags;
+
+	dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
+		__func__, (void *) dma_addr, size, dir);
+
+	BUG_ON(dir == DMA_NONE);
+
+	local_irq_save(flags);
+
+	unmap_single(dev, dma_addr, size, dir);
+
+	local_irq_restore(flags);
+}
+
+int
+dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+		enum dma_data_direction dir)
+{
+	unsigned long flags;
+	int i;
+
+	dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
+		__func__, sg, nents, dir);
+
+	BUG_ON(dir == DMA_NONE);
+
+	local_irq_save(flags);
+
+	for (i = 0; i < nents; i++, sg++) {
+		struct page *page = sg->page;
+		unsigned int offset = sg->offset;
+		unsigned int length = sg->length;
+		void *ptr = page_address(page) + offset;
+
+		sg->dma_address =
+			map_single(dev, ptr, length, dir);
+	}
+
+	local_irq_restore(flags);
+
+	return nents;
+}
+
+void
+dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
+		enum dma_data_direction dir)
+{
+	unsigned long flags;
+	int i;
+
+	dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
+		__func__, sg, nents, dir);
+
+	BUG_ON(dir == DMA_NONE);
+
+	local_irq_save(flags);
+
+	for (i = 0; i < nents; i++, sg++) {
+		dma_addr_t dma_addr = sg->dma_address;
+		unsigned int length = sg->length;
+
+		unmap_single(dev, dma_addr, length, dir);
+	}
+
+	local_irq_restore(flags);
+}
+
+void
+dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, size_t size,
+				enum dma_data_direction dir)
+{
+	unsigned long flags;
+
+	dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
+		__func__, (void *) dma_addr, size, dir);
+
+	local_irq_save(flags);
+
+	sync_single(dev, dma_addr, size, dir);
+
+	local_irq_restore(flags);
+}
+
+void
+dma_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, size_t size,
+				enum dma_data_direction dir)
+{
+	unsigned long flags;
+
+	dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
+		__func__, (void *) dma_addr, size, dir);
+
+	local_irq_save(flags);
+
+	sync_single(dev, dma_addr, size, dir);
+
+	local_irq_restore(flags);
+}
+
+void
+dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents,
+			enum dma_data_direction dir)
+{
+	unsigned long flags;
+	int i;
+
+	dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
+		__func__, sg, nents, dir);
+
+	BUG_ON(dir == DMA_NONE);
+
+	local_irq_save(flags);
+
+	for (i = 0; i < nents; i++, sg++) {
+		dma_addr_t dma_addr = sg->dma_address;
+		unsigned int length = sg->length;
+
+		sync_single(dev, dma_addr, length, dir);
+	}
+
+	local_irq_restore(flags);
+}
+
+void
+dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents,
+			enum dma_data_direction dir)
+{
+	unsigned long flags;
+	int i;
+
+	dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
+		__func__, sg, nents, dir);
+
+	BUG_ON(dir == DMA_NONE);
+
+	local_irq_save(flags);
+
+	for (i = 0; i < nents; i++, sg++) {
+		dma_addr_t dma_addr = sg->dma_address;
+		unsigned int length = sg->length;
+
+		sync_single(dev, dma_addr, length, dir);
+	}
+
+	local_irq_restore(flags);
+}
+
+int
+dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
+			unsigned long large_buffer_size)
+{
+	struct dmabounce_device_info *device_info;
+
+	device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
+	if (!device_info) {
+		printk(KERN_ERR
+			"Could not allocated dmabounce_device_info for %s",
+			dev->bus_id);
+		return -ENOMEM;
+	}
+
+	device_info->small_buffer_pool =
+		dma_pool_create("small_dmabounce_pool",
+				dev,
+				small_buffer_size,
+				0 /* byte alignment */,
+				0 /* no page-crossing issues */);
+	if (!device_info->small_buffer_pool) {
+		printk(KERN_ERR
+			"dmabounce: could not allocate small DMA pool for %s\n",
+			dev->bus_id);
+		kfree(device_info);
+		return -ENOMEM;
+	}
+
+	if (large_buffer_size) {
+		device_info->large_buffer_pool =
+			dma_pool_create("large_dmabounce_pool",
+					dev,
+					large_buffer_size,
+					0 /* byte alignment */,
+					0 /* no page-crossing issues */);
+		if (!device_info->large_buffer_pool) {
+		printk(KERN_ERR
+			"dmabounce: could not allocate large DMA pool for %s\n",
+			dev->bus_id);
+			dma_pool_destroy(device_info->small_buffer_pool);
+
+			return -ENOMEM;
+		}
+	}
+
+	device_info->dev = dev;
+	device_info->small_buffer_size = small_buffer_size;
+	device_info->large_buffer_size = large_buffer_size;
+	INIT_LIST_HEAD(&device_info->safe_buffers);
+
+#ifdef STATS
+	device_info->sbp_allocs = 0;
+	device_info->lbp_allocs = 0;
+	device_info->total_allocs = 0;
+	device_info->map_op_count = 0;
+	device_info->bounce_count = 0;
+#endif
+
+	list_add(&device_info->node, &dmabounce_devs);
+
+	printk(KERN_INFO "dmabounce: registered device %s on %s bus\n",
+		dev->bus_id, dev->bus->name);
+
+	return 0;
+}
+
+void
+dmabounce_unregister_dev(struct device *dev)
+{
+	struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
+
+	if (!device_info) {
+		printk(KERN_WARNING
+			"%s: Never registered with dmabounce but attempting" \
+			"to unregister!\n", dev->bus_id);
+		return;
+	}
+
+	if (!list_empty(&device_info->safe_buffers)) {
+		printk(KERN_ERR
+			"%s: Removing from dmabounce with pending buffers!\n",
+			dev->bus_id);
+		BUG();
+	}
+
+	if (device_info->small_buffer_pool)
+		dma_pool_destroy(device_info->small_buffer_pool);
+	if (device_info->large_buffer_pool)
+		dma_pool_destroy(device_info->large_buffer_pool);
+
+#ifdef STATS
+	print_alloc_stats(device_info);
+	print_map_stats(device_info);
+#endif
+
+	list_del(&device_info->node);
+
+	kfree(device_info);
+
+	printk(KERN_INFO "dmabounce: device %s on %s bus unregistered\n",
+		dev->bus_id, dev->bus->name);
+}
+
+
+EXPORT_SYMBOL(dma_map_single);
+EXPORT_SYMBOL(dma_unmap_single);
+EXPORT_SYMBOL(dma_map_sg);
+EXPORT_SYMBOL(dma_unmap_sg);
+EXPORT_SYMBOL(dma_sync_single);
+EXPORT_SYMBOL(dma_sync_sg);
+EXPORT_SYMBOL(dmabounce_register_dev);
+EXPORT_SYMBOL(dmabounce_unregister_dev);
+
+MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>");
+MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c
new file mode 100644
index 0000000..bafe8b1
--- /dev/null
+++ b/arch/arm/common/icst307.c
@@ -0,0 +1,161 @@
+/*
+ *  linux/arch/arm/common/icst307.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Support functions for calculating clocks/divisors for the ICST307
+ *  clock generators.  See http://www.icst.com/ for more information
+ *  on these devices.
+ *
+ *  This is an almost identical implementation to the ICST525 clock generator.
+ *  The s2div and idx2s files are different
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/hardware/icst307.h>
+
+/*
+ * Divisors for each OD setting.
+ */
+static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 };
+
+unsigned long icst307_khz(const struct icst307_params *p, struct icst307_vco vco)
+{
+	return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]);
+}
+
+EXPORT_SYMBOL(icst307_khz);
+
+/*
+ * Ascending divisor S values.
+ */
+static unsigned char idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 };
+
+struct icst307_vco
+icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq)
+{
+	struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+	unsigned long f;
+	unsigned int i = 0, rd, best = (unsigned int)-1;
+
+	/*
+	 * First, find the PLL output divisor such
+	 * that the PLL output is within spec.
+	 */
+	do {
+		f = freq * s2div[idx2s[i]];
+
+		/*
+		 * f must be between 6MHz and 200MHz (3.3 or 5V)
+		 */
+		if (f > 6000 && f <= p->vco_max)
+			break;
+	} while (i < ARRAY_SIZE(idx2s));
+
+	if (i > ARRAY_SIZE(idx2s))
+		return vco;
+
+	vco.s = idx2s[i];
+
+	/*
+	 * Now find the closest divisor combination
+	 * which gives a PLL output of 'f'.
+	 */
+	for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+		unsigned long fref_div, f_pll;
+		unsigned int vd;
+		int f_diff;
+
+		fref_div = (2 * p->ref) / rd;
+
+		vd = (f + fref_div / 2) / fref_div;
+		if (vd < p->vd_min || vd > p->vd_max)
+			continue;
+
+		f_pll = fref_div * vd;
+		f_diff = f_pll - f;
+		if (f_diff < 0)
+			f_diff = -f_diff;
+
+		if ((unsigned)f_diff < best) {
+			vco.v = vd - 8;
+			vco.r = rd - 2;
+			if (f_diff == 0)
+				break;
+			best = f_diff;
+		}
+	}
+
+	return vco;
+}
+
+EXPORT_SYMBOL(icst307_khz_to_vco);
+
+struct icst307_vco
+icst307_ps_to_vco(const struct icst307_params *p, unsigned long period)
+{
+	struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+	unsigned long f, ps;
+	unsigned int i = 0, rd, best = (unsigned int)-1;
+
+	ps = 1000000000UL / p->vco_max;
+
+	/*
+	 * First, find the PLL output divisor such
+	 * that the PLL output is within spec.
+	 */
+	do {
+		f = period / s2div[idx2s[i]];
+
+		/*
+		 * f must be between 6MHz and 200MHz (3.3 or 5V)
+		 */
+		if (f >= ps && f < 1000000000UL / 6000 + 1)
+			break;
+	} while (i < ARRAY_SIZE(idx2s));
+
+	if (i > ARRAY_SIZE(idx2s))
+		return vco;
+
+	vco.s = idx2s[i];
+
+	ps = 500000000UL / p->ref;
+
+	/*
+	 * Now find the closest divisor combination
+	 * which gives a PLL output of 'f'.
+	 */
+	for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+		unsigned long f_in_div, f_pll;
+		unsigned int vd;
+		int f_diff;
+
+		f_in_div = ps * rd;
+
+		vd = (f_in_div + f / 2) / f;
+		if (vd < p->vd_min || vd > p->vd_max)
+			continue;
+
+		f_pll = (f_in_div + vd / 2) / vd;
+		f_diff = f_pll - f;
+		if (f_diff < 0)
+			f_diff = -f_diff;
+
+		if ((unsigned)f_diff < best) {
+			vco.v = vd - 8;
+			vco.r = rd - 2;
+			if (f_diff == 0)
+				break;
+			best = f_diff;
+		}
+	}
+
+	return vco;
+}
+
+EXPORT_SYMBOL(icst307_ps_to_vco);
diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c
new file mode 100644
index 0000000..943ef88
--- /dev/null
+++ b/arch/arm/common/icst525.c
@@ -0,0 +1,160 @@
+/*
+ *  linux/arch/arm/common/icst525.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Support functions for calculating clocks/divisors for the ICST525
+ *  clock generators.  See http://www.icst.com/ for more information
+ *  on these devices.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/hardware/icst525.h>
+
+/*
+ * Divisors for each OD setting.
+ */
+static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 };
+
+unsigned long icst525_khz(const struct icst525_params *p, struct icst525_vco vco)
+{
+	return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]);
+}
+
+EXPORT_SYMBOL(icst525_khz);
+
+/*
+ * Ascending divisor S values.
+ */
+static unsigned char idx2s[] = { 1, 3, 4, 7, 5, 2, 6, 0 };
+
+struct icst525_vco
+icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq)
+{
+	struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+	unsigned long f;
+	unsigned int i = 0, rd, best = (unsigned int)-1;
+
+	/*
+	 * First, find the PLL output divisor such
+	 * that the PLL output is within spec.
+	 */
+	do {
+		f = freq * s2div[idx2s[i]];
+
+		/*
+		 * f must be between 10MHz and
+		 *  320MHz (5V) or 200MHz (3V)
+		 */
+		if (f > 10000 && f <= p->vco_max)
+			break;
+	} while (i < ARRAY_SIZE(idx2s));
+
+	if (i > ARRAY_SIZE(idx2s))
+		return vco;
+
+	vco.s = idx2s[i];
+
+	/*
+	 * Now find the closest divisor combination
+	 * which gives a PLL output of 'f'.
+	 */
+	for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+		unsigned long fref_div, f_pll;
+		unsigned int vd;
+		int f_diff;
+
+		fref_div = (2 * p->ref) / rd;
+
+		vd = (f + fref_div / 2) / fref_div;
+		if (vd < p->vd_min || vd > p->vd_max)
+			continue;
+
+		f_pll = fref_div * vd;
+		f_diff = f_pll - f;
+		if (f_diff < 0)
+			f_diff = -f_diff;
+
+		if ((unsigned)f_diff < best) {
+			vco.v = vd - 8;
+			vco.r = rd - 2;
+			if (f_diff == 0)
+				break;
+			best = f_diff;
+		}
+	}
+
+	return vco;
+}
+
+EXPORT_SYMBOL(icst525_khz_to_vco);
+
+struct icst525_vco
+icst525_ps_to_vco(const struct icst525_params *p, unsigned long period)
+{
+	struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+	unsigned long f, ps;
+	unsigned int i = 0, rd, best = (unsigned int)-1;
+
+	ps = 1000000000UL / p->vco_max;
+
+	/*
+	 * First, find the PLL output divisor such
+	 * that the PLL output is within spec.
+	 */
+	do {
+		f = period / s2div[idx2s[i]];
+
+		/*
+		 * f must be between 10MHz and
+		 *  320MHz (5V) or 200MHz (3V)
+		 */
+		if (f >= ps && f < 100000)
+			break;
+	} while (i < ARRAY_SIZE(idx2s));
+
+	if (i > ARRAY_SIZE(idx2s))
+		return vco;
+
+	vco.s = idx2s[i];
+
+	ps = 500000000UL / p->ref;
+
+	/*
+	 * Now find the closest divisor combination
+	 * which gives a PLL output of 'f'.
+	 */
+	for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+		unsigned long f_in_div, f_pll;
+		unsigned int vd;
+		int f_diff;
+
+		f_in_div = ps * rd;
+
+		vd = (f_in_div + f / 2) / f;
+		if (vd < p->vd_min || vd > p->vd_max)
+			continue;
+
+		f_pll = (f_in_div + vd / 2) / vd;
+		f_diff = f_pll - f;
+		if (f_diff < 0)
+			f_diff = -f_diff;
+
+		if ((unsigned)f_diff < best) {
+			vco.v = vd - 8;
+			vco.r = rd - 2;
+			if (f_diff == 0)
+				break;
+			best = f_diff;
+		}
+	}
+
+	return vco;
+}
+
+EXPORT_SYMBOL(icst525_ps_to_vco);
diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c
new file mode 100644
index 0000000..41f1265
--- /dev/null
+++ b/arch/arm/common/locomo.c
@@ -0,0 +1,1058 @@
+/*
+ * linux/arch/arm/common/locomo.c
+ *
+ * Sharp LoCoMo support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains all generic LoCoMo support.
+ *
+ * All initialization functions provided here are intended to be called
+ * from machine specific code with proper arguments when required.
+ *
+ * Based on sa1111.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#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>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware/locomo.h>
+
+/* M62332 output channel selection */
+#define M62332_EVR_CH	1	/* M62332 volume channel number  */
+				/*   0 : CH.1 , 1 : CH. 2        */
+/* DAC send data */
+#define	M62332_SLAVE_ADDR	0x4e	/* Slave address  */
+#define	M62332_W_BIT		0x00	/* W bit (0 only) */
+#define	M62332_SUB_ADDR		0x00	/* Sub address    */
+#define	M62332_A_BIT		0x00	/* A bit (0 only) */
+
+/* DAC setup and hold times (expressed in us) */
+#define DAC_BUS_FREE_TIME	5	/*   4.7 us */
+#define DAC_START_SETUP_TIME	5	/*   4.7 us */
+#define DAC_STOP_SETUP_TIME	4	/*   4.0 us */
+#define DAC_START_HOLD_TIME	5	/*   4.7 us */
+#define DAC_SCL_LOW_HOLD_TIME	5	/*   4.7 us */
+#define DAC_SCL_HIGH_HOLD_TIME	4	/*   4.0 us */
+#define DAC_DATA_SETUP_TIME	1	/*   250 ns */
+#define DAC_DATA_HOLD_TIME	1	/*   300 ns */
+#define DAC_LOW_SETUP_TIME	1	/*   300 ns */
+#define DAC_HIGH_SETUP_TIME	1	/*  1000 ns */
+
+/* the following is the overall data for the locomo chip */
+struct locomo {
+	struct device *dev;
+	unsigned long phys;
+	unsigned int irq;
+	spinlock_t lock;
+	void *base;
+};
+
+struct locomo_dev_info {
+	unsigned long	offset;
+	unsigned long	length;
+	unsigned int	devid;
+	unsigned int	irq[1];
+	const char *	name;
+};
+
+/* All the locomo devices.  If offset is non-zero, the mapbase for the
+ * locomo_dev will be set to the chip base plus offset.  If offset is
+ * zero, then the mapbase for the locomo_dev will be set to zero.  An
+ * offset of zero means the device only uses GPIOs or other helper
+ * functions inside this file */
+static struct locomo_dev_info locomo_devices[] = {
+	{
+		.devid 		= LOCOMO_DEVID_KEYBOARD,
+		.irq = {
+			IRQ_LOCOMO_KEY,
+		},
+		.name		= "locomo-keyboard",
+		.offset		= LOCOMO_KEYBOARD,
+		.length		= 16,
+	},
+	{
+		.devid		= LOCOMO_DEVID_FRONTLIGHT,
+		.irq		= {},
+		.name		= "locomo-frontlight",
+		.offset		= LOCOMO_FRONTLIGHT,
+		.length		= 8,
+
+	},
+	{
+		.devid		= LOCOMO_DEVID_BACKLIGHT,
+		.irq		= {},
+		.name		= "locomo-backlight",
+		.offset		= LOCOMO_BACKLIGHT,
+		.length		= 8,
+	},
+	{
+		.devid		= LOCOMO_DEVID_AUDIO,
+		.irq		= {},
+		.name		= "locomo-audio",
+		.offset		= LOCOMO_AUDIO,
+		.length		= 4,
+	},
+	{
+		.devid		= LOCOMO_DEVID_LED,
+		.irq 		= {},
+		.name		= "locomo-led",
+		.offset		= LOCOMO_LED,
+		.length		= 8,
+	},
+	{
+		.devid		= LOCOMO_DEVID_UART,
+		.irq		= {},
+		.name		= "locomo-uart",
+		.offset		= 0,
+		.length		= 0,
+	},
+};
+
+
+/** LoCoMo interrupt handling stuff.
+ * NOTE: LoCoMo has a 1 to many mapping on all of its IRQs.
+ * that is, there is only one real hardware interrupt
+ * we determine which interrupt it is by reading some IO memory.
+ * We have two levels of expansion, first in the handler for the
+ * hardware interrupt we generate an interrupt
+ * IRQ_LOCOMO_*_BASE and those handlers generate more interrupts
+ *
+ * hardware irq reads LOCOMO_ICR & 0x0f00
+ *   IRQ_LOCOMO_KEY_BASE
+ *   IRQ_LOCOMO_GPIO_BASE
+ *   IRQ_LOCOMO_LT_BASE
+ *   IRQ_LOCOMO_SPI_BASE
+ * IRQ_LOCOMO_KEY_BASE reads LOCOMO_KIC & 0x0001
+ *   IRQ_LOCOMO_KEY
+ * IRQ_LOCOMO_GPIO_BASE reads LOCOMO_GIR & LOCOMO_GPD & 0xffff
+ *   IRQ_LOCOMO_GPIO[0-15]
+ * IRQ_LOCOMO_LT_BASE reads LOCOMO_LTINT & 0x0001
+ *   IRQ_LOCOMO_LT
+ * IRQ_LOCOMO_SPI_BASE reads LOCOMO_SPIIR & 0x000F
+ *   IRQ_LOCOMO_SPI_RFR
+ *   IRQ_LOCOMO_SPI_RFW
+ *   IRQ_LOCOMO_SPI_OVRN
+ *   IRQ_LOCOMO_SPI_TEND
+ */
+
+#define LOCOMO_IRQ_START	(IRQ_LOCOMO_KEY_BASE)
+#define LOCOMO_IRQ_KEY_START	(IRQ_LOCOMO_KEY)
+#define	LOCOMO_IRQ_GPIO_START	(IRQ_LOCOMO_GPIO0)
+#define	LOCOMO_IRQ_LT_START	(IRQ_LOCOMO_LT)
+#define	LOCOMO_IRQ_SPI_START	(IRQ_LOCOMO_SPI_RFR)
+
+static void locomo_handler(unsigned int irq, struct irqdesc *desc,
+			struct pt_regs *regs)
+{
+	int req, i;
+	struct irqdesc *d;
+	void *mapbase = get_irq_chipdata(irq);
+
+	/* Acknowledge the parent IRQ */
+	desc->chip->ack(irq);
+
+	/* check why this interrupt was generated */
+	req = locomo_readl(mapbase + LOCOMO_ICR) & 0x0f00;
+
+	if (req) {
+		/* generate the next interrupt(s) */
+		irq = LOCOMO_IRQ_START;
+		d = irq_desc + irq;
+		for (i = 0; i <= 3; i++, d++, irq++) {
+			if (req & (0x0100 << i)) {
+				d->handle(irq, d, regs);
+			}
+
+		}
+	}
+}
+
+static void locomo_ack_irq(unsigned int irq)
+{
+}
+
+static void locomo_mask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_ICR);
+	r &= ~(0x0010 << (irq - LOCOMO_IRQ_START));
+	locomo_writel(r, mapbase + LOCOMO_ICR);
+}
+
+static void locomo_unmask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_ICR);
+	r |= (0x0010 << (irq - LOCOMO_IRQ_START));
+	locomo_writel(r, mapbase + LOCOMO_ICR);
+}
+
+static struct irqchip locomo_chip = {
+	.ack	= locomo_ack_irq,
+	.mask	= locomo_mask_irq,
+	.unmask	= locomo_unmask_irq,
+};
+
+static void locomo_key_handler(unsigned int irq, struct irqdesc *desc,
+			    struct pt_regs *regs)
+{
+	struct irqdesc *d;
+	void *mapbase = get_irq_chipdata(irq);
+
+	if (locomo_readl(mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC) & 0x0001) {
+		d = irq_desc + LOCOMO_IRQ_KEY_START;
+		d->handle(LOCOMO_IRQ_KEY_START, d, regs);
+	}
+}
+
+static void locomo_key_ack_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC);
+	r &= ~(0x0100 << (irq - LOCOMO_IRQ_KEY_START));
+	locomo_writel(r, mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC);
+}
+
+static void locomo_key_mask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC);
+	r &= ~(0x0010 << (irq - LOCOMO_IRQ_KEY_START));
+	locomo_writel(r, mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC);
+}
+
+static void locomo_key_unmask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC);
+	r |= (0x0010 << (irq - LOCOMO_IRQ_KEY_START));
+	locomo_writel(r, mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC);
+}
+
+static struct irqchip locomo_key_chip = {
+	.ack	= locomo_key_ack_irq,
+	.mask	= locomo_key_mask_irq,
+	.unmask	= locomo_key_unmask_irq,
+};
+
+static void locomo_gpio_handler(unsigned int irq, struct irqdesc *desc,
+			     struct pt_regs *regs)
+{
+	int req, i;
+	struct irqdesc *d;
+	void *mapbase = get_irq_chipdata(irq);
+
+	req = 	locomo_readl(mapbase + LOCOMO_GIR) &
+		locomo_readl(mapbase + LOCOMO_GPD) &
+		0xffff;
+
+	if (req) {
+		irq = LOCOMO_IRQ_GPIO_START;
+		d = irq_desc + LOCOMO_IRQ_GPIO_START;
+		for (i = 0; i <= 15; i++, irq++, d++) {
+			if (req & (0x0001 << i)) {
+				d->handle(irq, d, regs);
+			}
+		}
+	}
+}
+
+static void locomo_gpio_ack_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_GWE);
+	r |= (0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+	locomo_writel(r, mapbase + LOCOMO_GWE);
+
+	r = locomo_readl(mapbase + LOCOMO_GIS);
+	r &= ~(0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+	locomo_writel(r, mapbase + LOCOMO_GIS);
+
+	r = locomo_readl(mapbase + LOCOMO_GWE);
+	r &= ~(0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+	locomo_writel(r, mapbase + LOCOMO_GWE);
+}
+
+static void locomo_gpio_mask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_GIE);
+	r &= ~(0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+	locomo_writel(r, mapbase + LOCOMO_GIE);
+}
+
+static void locomo_gpio_unmask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_GIE);
+	r |= (0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+	locomo_writel(r, mapbase + LOCOMO_GIE);
+}
+
+static struct irqchip locomo_gpio_chip = {
+	.ack	= locomo_gpio_ack_irq,
+	.mask	= locomo_gpio_mask_irq,
+	.unmask	= locomo_gpio_unmask_irq,
+};
+
+static void locomo_lt_handler(unsigned int irq, struct irqdesc *desc,
+			   struct pt_regs *regs)
+{
+	struct irqdesc *d;
+	void *mapbase = get_irq_chipdata(irq);
+
+	if (locomo_readl(mapbase + LOCOMO_LTINT) & 0x0001) {
+		d = irq_desc + LOCOMO_IRQ_LT_START;
+		d->handle(LOCOMO_IRQ_LT_START, d, regs);
+	}
+}
+
+static void locomo_lt_ack_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_LTINT);
+	r &= ~(0x0100 << (irq - LOCOMO_IRQ_LT_START));
+	locomo_writel(r, mapbase + LOCOMO_LTINT);
+}
+
+static void locomo_lt_mask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_LTINT);
+	r &= ~(0x0010 << (irq - LOCOMO_IRQ_LT_START));
+	locomo_writel(r, mapbase + LOCOMO_LTINT);
+}
+
+static void locomo_lt_unmask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_LTINT);
+	r |= (0x0010 << (irq - LOCOMO_IRQ_LT_START));
+	locomo_writel(r, mapbase + LOCOMO_LTINT);
+}
+
+static struct irqchip locomo_lt_chip = {
+	.ack	= locomo_lt_ack_irq,
+	.mask	= locomo_lt_mask_irq,
+	.unmask	= locomo_lt_unmask_irq,
+};
+
+static void locomo_spi_handler(unsigned int irq, struct irqdesc *desc,
+			    struct pt_regs *regs)
+{
+	int req, i;
+	struct irqdesc *d;
+	void *mapbase = get_irq_chipdata(irq);
+
+	req = locomo_readl(mapbase + LOCOMO_SPIIR) & 0x000F;
+	if (req) {
+		irq = LOCOMO_IRQ_SPI_START;
+		d = irq_desc + irq;
+
+		for (i = 0; i <= 3; i++, irq++, d++) {
+			if (req & (0x0001 << i)) {
+				d->handle(irq, d, regs);
+			}
+		}
+	}
+}
+
+static void locomo_spi_ack_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_SPIWE);
+	r |= (0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+	locomo_writel(r, mapbase + LOCOMO_SPIWE);
+
+	r = locomo_readl(mapbase + LOCOMO_SPIIS);
+	r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+	locomo_writel(r, mapbase + LOCOMO_SPIIS);
+
+	r = locomo_readl(mapbase + LOCOMO_SPIWE);
+	r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+	locomo_writel(r, mapbase + LOCOMO_SPIWE);
+}
+
+static void locomo_spi_mask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_SPIIE);
+	r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+	locomo_writel(r, mapbase + LOCOMO_SPIIE);
+}
+
+static void locomo_spi_unmask_irq(unsigned int irq)
+{
+	void *mapbase = get_irq_chipdata(irq);
+	unsigned int r;
+	r = locomo_readl(mapbase + LOCOMO_SPIIE);
+	r |= (0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+	locomo_writel(r, mapbase + LOCOMO_SPIIE);
+}
+
+static struct irqchip locomo_spi_chip = {
+	.ack	= locomo_spi_ack_irq,
+	.mask	= locomo_spi_mask_irq,
+	.unmask	= locomo_spi_unmask_irq,
+};
+
+static void locomo_setup_irq(struct locomo *lchip)
+{
+	int irq;
+	void *irqbase = lchip->base;
+
+	/*
+	 * Install handler for IRQ_LOCOMO_HW.
+	 */
+	set_irq_type(lchip->irq, IRQT_FALLING);
+	set_irq_chipdata(lchip->irq, irqbase);
+	set_irq_chained_handler(lchip->irq, locomo_handler);
+
+	/* Install handlers for IRQ_LOCOMO_*_BASE */
+	set_irq_chip(IRQ_LOCOMO_KEY_BASE, &locomo_chip);
+	set_irq_chipdata(IRQ_LOCOMO_KEY_BASE, irqbase);
+	set_irq_chained_handler(IRQ_LOCOMO_KEY_BASE, locomo_key_handler);
+	set_irq_flags(IRQ_LOCOMO_KEY_BASE, IRQF_VALID | IRQF_PROBE);
+
+	set_irq_chip(IRQ_LOCOMO_GPIO_BASE, &locomo_chip);
+	set_irq_chipdata(IRQ_LOCOMO_GPIO_BASE, irqbase);
+	set_irq_chained_handler(IRQ_LOCOMO_GPIO_BASE, locomo_gpio_handler);
+	set_irq_flags(IRQ_LOCOMO_GPIO_BASE, IRQF_VALID | IRQF_PROBE);
+
+	set_irq_chip(IRQ_LOCOMO_LT_BASE, &locomo_chip);
+	set_irq_chipdata(IRQ_LOCOMO_LT_BASE, irqbase);
+	set_irq_chained_handler(IRQ_LOCOMO_LT_BASE, locomo_lt_handler);
+	set_irq_flags(IRQ_LOCOMO_LT_BASE, IRQF_VALID | IRQF_PROBE);
+
+	set_irq_chip(IRQ_LOCOMO_SPI_BASE, &locomo_chip);
+	set_irq_chipdata(IRQ_LOCOMO_SPI_BASE, irqbase);
+	set_irq_chained_handler(IRQ_LOCOMO_SPI_BASE, locomo_spi_handler);
+	set_irq_flags(IRQ_LOCOMO_SPI_BASE, IRQF_VALID | IRQF_PROBE);
+
+	/* install handlers for IRQ_LOCOMO_KEY_BASE generated interrupts */
+	set_irq_chip(LOCOMO_IRQ_KEY_START, &locomo_key_chip);
+	set_irq_chipdata(LOCOMO_IRQ_KEY_START, irqbase);
+	set_irq_handler(LOCOMO_IRQ_KEY_START, do_edge_IRQ);
+	set_irq_flags(LOCOMO_IRQ_KEY_START, IRQF_VALID | IRQF_PROBE);
+
+	/* install handlers for IRQ_LOCOMO_GPIO_BASE generated interrupts */
+	for (irq = LOCOMO_IRQ_GPIO_START; irq < LOCOMO_IRQ_GPIO_START + 16; irq++) {
+		set_irq_chip(irq, &locomo_gpio_chip);
+		set_irq_chipdata(irq, irqbase);
+		set_irq_handler(irq, do_edge_IRQ);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	/* install handlers for IRQ_LOCOMO_LT_BASE generated interrupts */
+	set_irq_chip(LOCOMO_IRQ_LT_START, &locomo_lt_chip);
+	set_irq_chipdata(LOCOMO_IRQ_LT_START, irqbase);
+	set_irq_handler(LOCOMO_IRQ_LT_START, do_edge_IRQ);
+	set_irq_flags(LOCOMO_IRQ_LT_START, IRQF_VALID | IRQF_PROBE);
+
+	/* install handlers for IRQ_LOCOMO_SPI_BASE generated interrupts */
+	for (irq = LOCOMO_IRQ_SPI_START; irq < LOCOMO_IRQ_SPI_START + 3; irq++) {
+		set_irq_chip(irq, &locomo_spi_chip);
+		set_irq_chipdata(irq, irqbase);
+		set_irq_handler(irq, do_edge_IRQ);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+}
+
+
+static void locomo_dev_release(struct device *_dev)
+{
+	struct locomo_dev *dev = LOCOMO_DEV(_dev);
+
+	kfree(dev);
+}
+
+static int
+locomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info)
+{
+	struct locomo_dev *dev;
+	int ret;
+
+	dev = kmalloc(sizeof(struct locomo_dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	memset(dev, 0, sizeof(struct locomo_dev));
+
+	strncpy(dev->dev.bus_id,info->name,sizeof(dev->dev.bus_id));
+	/*
+	 * If the parent device has a DMA mask associated with it,
+	 * propagate it down to the children.
+	 */
+	if (lchip->dev->dma_mask) {
+		dev->dma_mask = *lchip->dev->dma_mask;
+		dev->dev.dma_mask = &dev->dma_mask;
+	}
+
+	dev->devid	 = info->devid;
+	dev->dev.parent  = lchip->dev;
+	dev->dev.bus     = &locomo_bus_type;
+	dev->dev.release = locomo_dev_release;
+	dev->dev.coherent_dma_mask = lchip->dev->coherent_dma_mask;
+
+	if (info->offset)
+		dev->mapbase = lchip->base + info->offset;
+	else
+		dev->mapbase = 0;
+	dev->length = info->length;
+
+	memmove(dev->irq, info->irq, sizeof(dev->irq));
+
+	ret = device_register(&dev->dev);
+	if (ret) {
+ out:
+		kfree(dev);
+	}
+	return ret;
+}
+
+/**
+ *	locomo_probe - probe for a single LoCoMo chip.
+ *	@phys_addr: physical address of device.
+ *
+ *	Probe for a LoCoMo chip.  This must be called
+ *	before any other locomo-specific code.
+ *
+ *	Returns:
+ *	%-ENODEV	device not found.
+ *	%-EBUSY		physical address already marked in-use.
+ *	%0		successful.
+ */
+static int
+__locomo_probe(struct device *me, struct resource *mem, int irq)
+{
+	struct locomo *lchip;
+	unsigned long r;
+	int i, ret = -ENODEV;
+
+	lchip = kmalloc(sizeof(struct locomo), GFP_KERNEL);
+	if (!lchip)
+		return -ENOMEM;
+
+	memset(lchip, 0, sizeof(struct locomo));
+
+	spin_lock_init(&lchip->lock);
+
+	lchip->dev = me;
+	dev_set_drvdata(lchip->dev, lchip);
+
+	lchip->phys = mem->start;
+	lchip->irq = irq;
+
+	/*
+	 * Map the whole region.  This also maps the
+	 * registers for our children.
+	 */
+	lchip->base = ioremap(mem->start, PAGE_SIZE);
+	if (!lchip->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* locomo initialize */
+	locomo_writel(0, lchip->base + LOCOMO_ICR);
+	/* KEYBOARD */
+	locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
+
+	/* GPIO */
+	locomo_writel(0, lchip->base + LOCOMO_GPO);
+	locomo_writel( (LOCOMO_GPIO(2) | LOCOMO_GPIO(3) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+			, lchip->base + LOCOMO_GPE);
+	locomo_writel( (LOCOMO_GPIO(2) | LOCOMO_GPIO(3) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+			, lchip->base + LOCOMO_GPD);
+	locomo_writel(0, lchip->base + LOCOMO_GIE);
+
+	/* FrontLight */
+	locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
+	locomo_writel(0, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
+	/* Longtime timer */
+	locomo_writel(0, lchip->base + LOCOMO_LTINT);
+	/* SPI */
+	locomo_writel(0, lchip->base + LOCOMO_SPIIE);
+
+	locomo_writel(6 + 8 + 320 + 30 - 10, lchip->base + LOCOMO_ASD);
+	r = locomo_readl(lchip->base + LOCOMO_ASD);
+	r |= 0x8000;
+	locomo_writel(r, lchip->base + LOCOMO_ASD);
+
+	locomo_writel(6 + 8 + 320 + 30 - 10 - 128 + 4, lchip->base + LOCOMO_HSD);
+	r = locomo_readl(lchip->base + LOCOMO_HSD);
+	r |= 0x8000;
+	locomo_writel(r, lchip->base + LOCOMO_HSD);
+
+	locomo_writel(128 / 8, lchip->base + LOCOMO_HSC);
+
+	/* XON */
+	locomo_writel(0x80, lchip->base + LOCOMO_TADC);
+	udelay(1000);
+	/* CLK9MEN */
+	r = locomo_readl(lchip->base + LOCOMO_TADC);
+	r |= 0x10;
+	locomo_writel(r, lchip->base + LOCOMO_TADC);
+	udelay(100);
+
+	/* init DAC */
+	r = locomo_readl(lchip->base + LOCOMO_DAC);
+	r |= LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
+	locomo_writel(r, lchip->base + LOCOMO_DAC);
+
+	r = locomo_readl(lchip->base + LOCOMO_VER);
+	printk(KERN_INFO "LoCoMo Chip: %lu%lu\n", (r >> 8), (r & 0xff));
+
+	/*
+	 * The interrupt controller must be initialised before any
+	 * other device to ensure that the interrupts are available.
+	 */
+	if (lchip->irq != NO_IRQ)
+		locomo_setup_irq(lchip);
+
+	for (i = 0; i < ARRAY_SIZE(locomo_devices); i++)
+		locomo_init_one_child(lchip, &locomo_devices[i]);
+
+	return 0;
+
+ out:
+	kfree(lchip);
+	return ret;
+}
+
+static void __locomo_remove(struct locomo *lchip)
+{
+	struct list_head *l, *n;
+
+	list_for_each_safe(l, n, &lchip->dev->children) {
+		struct device *d = list_to_dev(l);
+
+		device_unregister(d);
+	}
+
+	if (lchip->irq != NO_IRQ) {
+		set_irq_chained_handler(lchip->irq, NULL);
+		set_irq_data(lchip->irq, NULL);
+	}
+
+	iounmap(lchip->base);
+	kfree(lchip);
+}
+
+static int locomo_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *mem;
+	int irq;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem)
+		return -EINVAL;
+	irq = platform_get_irq(pdev, 0);
+
+	return __locomo_probe(dev, mem, irq);
+}
+
+static int locomo_remove(struct device *dev)
+{
+	struct locomo *lchip = dev_get_drvdata(dev);
+
+	if (lchip) {
+		__locomo_remove(lchip);
+		dev_set_drvdata(dev, NULL);
+	}
+
+	return 0;
+}
+
+/*
+ *	Not sure if this should be on the system bus or not yet.
+ *	We really want some way to register a system device at
+ *	the per-machine level, and then have this driver pick
+ *	up the registered devices.
+ */
+static struct device_driver locomo_device_driver = {
+	.name		= "locomo",
+	.bus		= &platform_bus_type,
+	.probe		= locomo_probe,
+	.remove		= locomo_remove,
+};
+
+/*
+ *	Get the parent device driver (us) structure
+ *	from a child function device
+ */
+static inline struct locomo *locomo_chip_driver(struct locomo_dev *ldev)
+{
+	return (struct locomo *)dev_get_drvdata(ldev->dev.parent);
+}
+
+void locomo_gpio_set_dir(struct locomo_dev *ldev, unsigned int bits, unsigned int dir)
+{
+	struct locomo *lchip = locomo_chip_driver(ldev);
+	unsigned long flags;
+	unsigned int r;
+
+	spin_lock_irqsave(&lchip->lock, flags);
+
+	r = locomo_readl(lchip->base + LOCOMO_GPD);
+	r &= ~bits;
+	locomo_writel(r, lchip->base + LOCOMO_GPD);
+
+	r = locomo_readl(lchip->base + LOCOMO_GPE);
+	if (dir)
+		r |= bits;
+	else
+		r &= ~bits;
+	locomo_writel(r, lchip->base + LOCOMO_GPE);
+
+	spin_unlock_irqrestore(&lchip->lock, flags);
+}
+
+unsigned int locomo_gpio_read_level(struct locomo_dev *ldev, unsigned int bits)
+{
+	struct locomo *lchip = locomo_chip_driver(ldev);
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&lchip->lock, flags);
+	ret = locomo_readl(lchip->base + LOCOMO_GPL);
+	spin_unlock_irqrestore(&lchip->lock, flags);
+
+	ret &= bits;
+	return ret;
+}
+
+unsigned int locomo_gpio_read_output(struct locomo_dev *ldev, unsigned int bits)
+{
+	struct locomo *lchip = locomo_chip_driver(ldev);
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&lchip->lock, flags);
+	ret = locomo_readl(lchip->base + LOCOMO_GPO);
+	spin_unlock_irqrestore(&lchip->lock, flags);
+
+	ret &= bits;
+	return ret;
+}
+
+void locomo_gpio_write(struct locomo_dev *ldev, unsigned int bits, unsigned int set)
+{
+	struct locomo *lchip = locomo_chip_driver(ldev);
+	unsigned long flags;
+	unsigned int r;
+
+	spin_lock_irqsave(&lchip->lock, flags);
+
+	r = locomo_readl(lchip->base + LOCOMO_GPO);
+	if (set)
+		r |= bits;
+	else
+		r &= ~bits;
+	locomo_writel(r, lchip->base + LOCOMO_GPO);
+
+	spin_unlock_irqrestore(&lchip->lock, flags);
+}
+
+static void locomo_m62332_sendbit(void *mapbase, int bit)
+{
+	unsigned int r;
+
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SCLOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	udelay(DAC_DATA_HOLD_TIME);	/* 300 nsec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SCLOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
+
+	if (bit & 1) {
+		r = locomo_readl(mapbase + LOCOMO_DAC);
+		r |=  LOCOMO_DAC_SDAOEB;
+		locomo_writel(r, mapbase + LOCOMO_DAC);
+		udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	} else {
+		r = locomo_readl(mapbase + LOCOMO_DAC);
+		r &=  ~(LOCOMO_DAC_SDAOEB);
+		locomo_writel(r, mapbase + LOCOMO_DAC);
+		udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	}
+
+	udelay(DAC_DATA_SETUP_TIME);	/* 250 nsec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/*  4.0 usec */
+}
+
+void locomo_m62332_senddata(struct locomo_dev *ldev, unsigned int dac_data, int channel)
+{
+	struct locomo *lchip = locomo_chip_driver(ldev);
+	int i;
+	unsigned char data;
+	unsigned int r;
+	void *mapbase = lchip->base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lchip->lock, flags);
+
+	/* Start */
+	udelay(DAC_BUS_FREE_TIME);	/* 5.0 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.0 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SDAOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_START_HOLD_TIME);	/* 5.0 usec */
+	udelay(DAC_DATA_HOLD_TIME);	/* 300 nsec */
+
+	/* Send slave address and W bit (LSB is W bit) */
+	data = (M62332_SLAVE_ADDR << 1) | M62332_W_BIT;
+	for (i = 1; i <= 8; i++) {
+		locomo_m62332_sendbit(mapbase, data >> (8 - i));
+	}
+
+	/* Check A bit */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SCLOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SDAOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
+	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
+		printk(KERN_WARNING "locomo: m62332_senddata Error 1\n");
+		return;
+	}
+
+	/* Send Sub address (LSB is channel select) */
+	/*    channel = 0 : ch1 select              */
+	/*            = 1 : ch2 select              */
+	data = M62332_SUB_ADDR + channel;
+	for (i = 1; i <= 8; i++) {
+		locomo_m62332_sendbit(mapbase, data >> (8 - i));
+	}
+
+	/* Check A bit */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SCLOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SDAOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
+	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
+		printk(KERN_WARNING "locomo: m62332_senddata Error 2\n");
+		return;
+	}
+
+	/* Send DAC data */
+	for (i = 1; i <= 8; i++) {
+		locomo_m62332_sendbit(mapbase, dac_data >> (8 - i));
+	}
+
+	/* Check A bit */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SCLOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SDAOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4.7 usec */
+	if (locomo_readl(mapbase + LOCOMO_DAC) & LOCOMO_DAC_SDAOEB) {	/* High is error */
+		printk(KERN_WARNING "locomo: m62332_senddata Error 3\n");
+		return;
+	}
+
+	/* stop */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r &=  ~(LOCOMO_DAC_SCLOEB);
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 300 nsec */
+	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4 usec */
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SDAOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_HIGH_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_HIGH_HOLD_TIME);	/* 4 usec */
+
+	r = locomo_readl(mapbase + LOCOMO_DAC);
+	r |=  LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB;
+	locomo_writel(r, mapbase + LOCOMO_DAC);
+	udelay(DAC_LOW_SETUP_TIME);	/* 1000 nsec */
+	udelay(DAC_SCL_LOW_HOLD_TIME);	/* 4.7 usec */
+
+	spin_unlock_irqrestore(&lchip->lock, flags);
+}
+
+/*
+ *	LoCoMo "Register Access Bus."
+ *
+ *	We model this as a regular bus type, and hang devices directly
+ *	off this.
+ */
+static int locomo_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct locomo_dev *dev = LOCOMO_DEV(_dev);
+	struct locomo_driver *drv = LOCOMO_DRV(_drv);
+
+	return dev->devid == drv->devid;
+}
+
+static int locomo_bus_suspend(struct device *dev, pm_message_t state)
+{
+	struct locomo_dev *ldev = LOCOMO_DEV(dev);
+	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+	int ret = 0;
+
+	if (drv && drv->suspend)
+		ret = drv->suspend(ldev, state);
+	return ret;
+}
+
+static int locomo_bus_resume(struct device *dev)
+{
+	struct locomo_dev *ldev = LOCOMO_DEV(dev);
+	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+	int ret = 0;
+
+	if (drv && drv->resume)
+		ret = drv->resume(ldev);
+	return ret;
+}
+
+static int locomo_bus_probe(struct device *dev)
+{
+	struct locomo_dev *ldev = LOCOMO_DEV(dev);
+	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+	int ret = -ENODEV;
+
+	if (drv->probe)
+		ret = drv->probe(ldev);
+	return ret;
+}
+
+static int locomo_bus_remove(struct device *dev)
+{
+	struct locomo_dev *ldev = LOCOMO_DEV(dev);
+	struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
+	int ret = 0;
+
+	if (drv->remove)
+		ret = drv->remove(ldev);
+	return ret;
+}
+
+struct bus_type locomo_bus_type = {
+	.name		= "locomo-bus",
+	.match		= locomo_match,
+	.suspend	= locomo_bus_suspend,
+	.resume		= locomo_bus_resume,
+};
+
+int locomo_driver_register(struct locomo_driver *driver)
+{
+	driver->drv.probe = locomo_bus_probe;
+	driver->drv.remove = locomo_bus_remove;
+	driver->drv.bus = &locomo_bus_type;
+	return driver_register(&driver->drv);
+}
+
+void locomo_driver_unregister(struct locomo_driver *driver)
+{
+	driver_unregister(&driver->drv);
+}
+
+static int __init locomo_init(void)
+{
+	int ret = bus_register(&locomo_bus_type);
+	if (ret == 0)
+		driver_register(&locomo_device_driver);
+	return ret;
+}
+
+static void __exit locomo_exit(void)
+{
+	driver_unregister(&locomo_device_driver);
+	bus_unregister(&locomo_bus_type);
+}
+
+module_init(locomo_init);
+module_exit(locomo_exit);
+
+MODULE_DESCRIPTION("Sharp LoCoMo core driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+
+EXPORT_SYMBOL(locomo_driver_register);
+EXPORT_SYMBOL(locomo_driver_unregister);
+EXPORT_SYMBOL(locomo_gpio_set_dir);
+EXPORT_SYMBOL(locomo_gpio_read_level);
+EXPORT_SYMBOL(locomo_gpio_read_output);
+EXPORT_SYMBOL(locomo_gpio_write);
+EXPORT_SYMBOL(locomo_m62332_senddata);
diff --git a/arch/arm/common/rtctime.c b/arch/arm/common/rtctime.c
new file mode 100644
index 0000000..c397e71
--- /dev/null
+++ b/arch/arm/common/rtctime.c
@@ -0,0 +1,506 @@
+/*
+ *  linux/arch/arm/common/rtctime.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd.
+ *  Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
+ *  Based on rtc.c by Paul Gortmaker
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <asm/rtc.h>
+#include <asm/semaphore.h>
+
+static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
+static struct fasync_struct *rtc_async_queue;
+
+/*
+ * rtc_lock protects rtc_irq_data
+ */
+static DEFINE_SPINLOCK(rtc_lock);
+static unsigned long rtc_irq_data;
+
+/*
+ * rtc_sem protects rtc_inuse and rtc_ops
+ */
+static DECLARE_MUTEX(rtc_sem);
+static unsigned long rtc_inuse;
+static struct rtc_ops *rtc_ops;
+
+#define rtc_epoch 1900UL
+
+static const unsigned char days_in_month[] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+static int month_days(unsigned int month, unsigned int year)
+{
+	return days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+}
+
+/*
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+{
+	int days, month, year;
+
+	days = time / 86400;
+	time -= days * 86400;
+
+	tm->tm_wday = (days + 4) % 7;
+
+	year = 1970 + days / 365;
+	days -= (year - 1970) * 365
+	        + LEAPS_THRU_END_OF(year - 1)
+	        - LEAPS_THRU_END_OF(1970 - 1);
+	if (days < 0) {
+		year -= 1;
+		days += 365 + LEAP_YEAR(year);
+	}
+	tm->tm_year = year - 1900;
+	tm->tm_yday = days + 1;
+
+	for (month = 0; month < 11; month++) {
+		int newdays;
+
+		newdays = days - month_days(month, year);
+		if (newdays < 0)
+			break;
+		days = newdays;
+	}
+	tm->tm_mon = month;
+	tm->tm_mday = days + 1;
+
+	tm->tm_hour = time / 3600;
+	time -= tm->tm_hour * 3600;
+	tm->tm_min = time / 60;
+	tm->tm_sec = time - tm->tm_min * 60;
+}
+EXPORT_SYMBOL(rtc_time_to_tm);
+
+/*
+ * Does the rtc_time represent a valid date/time?
+ */
+int rtc_valid_tm(struct rtc_time *tm)
+{
+	if (tm->tm_year < 70 ||
+	    tm->tm_mon >= 12 ||
+	    tm->tm_mday < 1 ||
+	    tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) ||
+	    tm->tm_hour >= 24 ||
+	    tm->tm_min >= 60 ||
+	    tm->tm_sec >= 60)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(rtc_valid_tm);
+
+/*
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
+{
+	*time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+		       tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+	return 0;
+}
+EXPORT_SYMBOL(rtc_tm_to_time);
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ *
+ * FIXME: for now, we just copy the alarm time because we're lazy (and
+ * is therefore buggy - setting a 10am alarm at 8pm will not result in
+ * the alarm triggering.)
+ */
+void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
+{
+	next->tm_year = now->tm_year;
+	next->tm_mon = now->tm_mon;
+	next->tm_mday = now->tm_mday;
+	next->tm_hour = alrm->tm_hour;
+	next->tm_min = alrm->tm_min;
+	next->tm_sec = alrm->tm_sec;
+}
+
+static inline void rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm)
+{
+	memset(tm, 0, sizeof(struct rtc_time));
+	ops->read_time(tm);
+}
+
+static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm)
+{
+	int ret;
+
+	ret = rtc_valid_tm(tm);
+	if (ret == 0)
+		ret = ops->set_time(tm);
+
+	return ret;
+}
+
+static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+{
+	int ret = -EINVAL;
+	if (ops->read_alarm) {
+		memset(alrm, 0, sizeof(struct rtc_wkalrm));
+		ops->read_alarm(alrm);
+		ret = 0;
+	}
+	return ret;
+}
+
+static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+{
+	int ret = -EINVAL;
+	if (ops->set_alarm)
+		ret = ops->set_alarm(alrm);
+	return ret;
+}
+
+void rtc_update(unsigned long num, unsigned long events)
+{
+	spin_lock(&rtc_lock);
+	rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
+	spin_unlock(&rtc_lock);
+
+	wake_up_interruptible(&rtc_wait);
+	kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
+}
+EXPORT_SYMBOL(rtc_update);
+
+
+static ssize_t
+rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long data;
+	ssize_t ret;
+
+	if (count < sizeof(unsigned long))
+		return -EINVAL;
+
+	add_wait_queue(&rtc_wait, &wait);
+	do {
+		__set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irq(&rtc_lock);
+		data = rtc_irq_data;
+		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+
+		if (data != 0) {
+			ret = 0;
+			break;
+		}
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	} while (1);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rtc_wait, &wait);
+
+	if (ret == 0) {
+		ret = put_user(data, (unsigned long __user *)buf);
+		if (ret == 0)
+			ret = sizeof(unsigned long);
+	}
+	return ret;
+}
+
+static unsigned int rtc_poll(struct file *file, poll_table *wait)
+{
+	unsigned long data;
+
+	poll_wait(file, &rtc_wait, wait);
+
+	spin_lock_irq(&rtc_lock);
+	data = rtc_irq_data;
+	spin_unlock_irq(&rtc_lock);
+
+	return data != 0 ? POLLIN | POLLRDNORM : 0;
+}
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		     unsigned long arg)
+{
+	struct rtc_ops *ops = file->private_data;
+	struct rtc_time tm;
+	struct rtc_wkalrm alrm;
+	void __user *uarg = (void __user *)arg;
+	int ret = -EINVAL;
+
+	switch (cmd) {
+	case RTC_ALM_READ:
+		ret = rtc_read_alarm(ops, &alrm);
+		if (ret)
+			break;
+		ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
+		if (ret)
+			ret = -EFAULT;
+		break;
+
+	case RTC_ALM_SET:
+		ret = copy_from_user(&alrm.time, uarg, sizeof(tm));
+		if (ret) {
+			ret = -EFAULT;
+			break;
+		}
+		alrm.enabled = 0;
+		alrm.pending = 0;
+		alrm.time.tm_mday = -1;
+		alrm.time.tm_mon = -1;
+		alrm.time.tm_year = -1;
+		alrm.time.tm_wday = -1;
+		alrm.time.tm_yday = -1;
+		alrm.time.tm_isdst = -1;
+		ret = rtc_set_alarm(ops, &alrm);
+		break;
+
+	case RTC_RD_TIME:
+		rtc_read_time(ops, &tm);
+		ret = copy_to_user(uarg, &tm, sizeof(tm));
+		if (ret)
+			ret = -EFAULT;
+		break;
+
+	case RTC_SET_TIME:
+		if (!capable(CAP_SYS_TIME)) {
+			ret = -EACCES;
+			break;
+		}
+		ret = copy_from_user(&tm, uarg, sizeof(tm));
+		if (ret) {
+			ret = -EFAULT;
+			break;
+		}
+		ret = rtc_set_time(ops, &tm);
+		break;
+
+	case RTC_EPOCH_SET:
+#ifndef rtc_epoch
+		/*
+		 * There were no RTC clocks before 1900.
+		 */
+		if (arg < 1900) {
+			ret = -EINVAL;
+			break;
+		}
+		if (!capable(CAP_SYS_TIME)) {
+			ret = -EACCES;
+			break;
+		}
+		rtc_epoch = arg;
+		ret = 0;
+#endif
+		break;
+
+	case RTC_EPOCH_READ:
+		ret = put_user(rtc_epoch, (unsigned long __user *)uarg);
+		break;
+
+	case RTC_WKALM_SET:
+		ret = copy_from_user(&alrm, uarg, sizeof(alrm));
+		if (ret) {
+			ret = -EFAULT;
+			break;
+		}
+		ret = rtc_set_alarm(ops, &alrm);
+		break;
+
+	case RTC_WKALM_RD:
+		ret = rtc_read_alarm(ops, &alrm);
+		if (ret)
+			break;
+		ret = copy_to_user(uarg, &alrm, sizeof(alrm));
+		if (ret)
+			ret = -EFAULT;
+		break;
+
+	default:
+		if (ops->ioctl)
+			ret = ops->ioctl(cmd, arg);
+		break;
+	}
+	return ret;
+}
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+	int ret;
+
+	down(&rtc_sem);
+
+	if (rtc_inuse) {
+		ret = -EBUSY;
+	} else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
+		ret = -ENODEV;
+	} else {
+		file->private_data = rtc_ops;
+
+		ret = rtc_ops->open ? rtc_ops->open() : 0;
+		if (ret == 0) {
+			spin_lock_irq(&rtc_lock);
+			rtc_irq_data = 0;
+			spin_unlock_irq(&rtc_lock);
+
+			rtc_inuse = 1;
+		}
+	}
+	up(&rtc_sem);
+
+	return ret;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+	struct rtc_ops *ops = file->private_data;
+
+	if (ops->release)
+		ops->release();
+
+	spin_lock_irq(&rtc_lock);
+	rtc_irq_data = 0;
+	spin_unlock_irq(&rtc_lock);
+
+	module_put(rtc_ops->owner);
+	rtc_inuse = 0;
+
+	return 0;
+}
+
+static int rtc_fasync(int fd, struct file *file, int on)
+{
+	return fasync_helper(fd, file, on, &rtc_async_queue);
+}
+
+static struct file_operations rtc_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= rtc_read,
+	.poll		= rtc_poll,
+	.ioctl		= rtc_ioctl,
+	.open		= rtc_open,
+	.release	= rtc_release,
+	.fasync		= rtc_fasync,
+};
+
+static struct miscdevice rtc_miscdev = {
+	.minor		= RTC_MINOR,
+	.name		= "rtc",
+	.fops		= &rtc_fops,
+};
+
+
+static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct rtc_ops *ops = data;
+	struct rtc_wkalrm alrm;
+	struct rtc_time tm;
+	char *p = page;
+
+	rtc_read_time(ops, &tm);
+
+	p += sprintf(p,
+		"rtc_time\t: %02d:%02d:%02d\n"
+		"rtc_date\t: %04d-%02d-%02d\n"
+		"rtc_epoch\t: %04lu\n",
+		tm.tm_hour, tm.tm_min, tm.tm_sec,
+		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+		rtc_epoch);
+
+	if (rtc_read_alarm(ops, &alrm) == 0) {
+		p += sprintf(p, "alrm_time\t: ");
+		if ((unsigned int)alrm.time.tm_hour <= 24)
+			p += sprintf(p, "%02d:", alrm.time.tm_hour);
+		else
+			p += sprintf(p, "**:");
+		if ((unsigned int)alrm.time.tm_min <= 59)
+			p += sprintf(p, "%02d:", alrm.time.tm_min);
+		else
+			p += sprintf(p, "**:");
+		if ((unsigned int)alrm.time.tm_sec <= 59)
+			p += sprintf(p, "%02d\n", alrm.time.tm_sec);
+		else
+			p += sprintf(p, "**\n");
+
+		p += sprintf(p, "alrm_date\t: ");
+		if ((unsigned int)alrm.time.tm_year <= 200)
+			p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
+		else
+			p += sprintf(p, "****-");
+		if ((unsigned int)alrm.time.tm_mon <= 11)
+			p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
+		else
+			p += sprintf(p, "**-");
+		if ((unsigned int)alrm.time.tm_mday <= 31)
+			p += sprintf(p, "%02d\n", alrm.time.tm_mday);
+		else
+			p += sprintf(p, "**\n");
+		p += sprintf(p, "alrm_wakeup\t: %s\n",
+			     alrm.enabled ? "yes" : "no");
+		p += sprintf(p, "alrm_pending\t: %s\n",
+			     alrm.pending ? "yes" : "no");
+	}
+
+	if (ops->proc)
+		p += ops->proc(p);
+
+	return p - page;
+}
+
+int register_rtc(struct rtc_ops *ops)
+{
+	int ret = -EBUSY;
+
+	down(&rtc_sem);
+	if (rtc_ops == NULL) {
+		rtc_ops = ops;
+
+		ret = misc_register(&rtc_miscdev);
+		if (ret == 0)
+			create_proc_read_entry("driver/rtc", 0, NULL,
+					       rtc_read_proc, ops);
+	}
+	up(&rtc_sem);
+
+	return ret;
+}
+EXPORT_SYMBOL(register_rtc);
+
+void unregister_rtc(struct rtc_ops *rtc)
+{
+	down(&rtc_sem);
+	if (rtc == rtc_ops) {
+		remove_proc_entry("driver/rtc", NULL);
+		misc_deregister(&rtc_miscdev);
+		rtc_ops = NULL;
+	}
+	up(&rtc_sem);
+}
+EXPORT_SYMBOL(unregister_rtc);
diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c
new file mode 100644
index 0000000..21fce34
--- /dev/null
+++ b/arch/arm/common/sa1111.c
@@ -0,0 +1,1292 @@
+/*
+ * linux/arch/arm/mach-sa1100/sa1111.c
+ *
+ * SA1111 support
+ *
+ * Original code by John Dorsey
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This file contains all generic SA1111 support.
+ *
+ * All initialization functions provided here are intended to be called
+ * from machine specific code with proper arguments when required.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware/sa1111.h>
+
+#ifdef CONFIG_ARCH_PXA
+#include <asm/arch/pxa-regs.h>
+#endif
+
+extern void __init sa1110_mb_enable(void);
+
+/*
+ * 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;
+	unsigned long	phys;
+	int		irq;
+	spinlock_t	lock;
+	void __iomem	*base;
+};
+
+/*
+ * We _really_ need to eliminate this.  Its only users
+ * are the PWM and DMA checking code.
+ */
+static struct sa1111 *g_sa1111;
+
+struct sa1111_dev_info {
+	unsigned long	offset;
+	unsigned long	skpcr_mask;
+	unsigned int	devid;
+	unsigned int	irq[6];
+};
+
+static struct sa1111_dev_info sa1111_devices[] = {
+	{
+		.offset		= SA1111_USB,
+		.skpcr_mask	= SKPCR_UCLKEN,
+		.devid		= SA1111_DEVID_USB,
+		.irq = {
+			IRQ_USBPWR,
+			IRQ_HCIM,
+			IRQ_HCIBUFFACC,
+			IRQ_HCIRMTWKP,
+			IRQ_NHCIMFCIR,
+			IRQ_USB_PORT_RESUME
+		},
+	},
+	{
+		.offset		= 0x0600,
+		.skpcr_mask	= SKPCR_I2SCLKEN | SKPCR_L3CLKEN,
+		.devid		= SA1111_DEVID_SAC,
+		.irq = {
+			AUDXMTDMADONEA,
+			AUDXMTDMADONEB,
+			AUDRCVDMADONEA,
+			AUDRCVDMADONEB
+		},
+	},
+	{
+		.offset		= 0x0800,
+		.skpcr_mask	= SKPCR_SCLKEN,
+		.devid		= SA1111_DEVID_SSP,
+	},
+	{
+		.offset		= SA1111_KBD,
+		.skpcr_mask	= SKPCR_PTCLKEN,
+		.devid		= SA1111_DEVID_PS2,
+		.irq = {
+			IRQ_TPRXINT,
+			IRQ_TPTXINT
+		},
+	},
+	{
+		.offset		= SA1111_MSE,
+		.skpcr_mask	= SKPCR_PMCLKEN,
+		.devid		= SA1111_DEVID_PS2,
+		.irq = {
+			IRQ_MSRXINT,
+			IRQ_MSTXINT
+		},
+	},
+	{
+		.offset		= 0x1800,
+		.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,
+		},
+	},
+};
+
+/*
+ * SA1111 interrupt support.  Since clearing an IRQ while there are
+ * active IRQs causes the interrupt output to pulse, the upper levels
+ * will call us again if there are more interrupts to process.
+ */
+static void
+sa1111_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs)
+{
+	unsigned int stat0, stat1, i;
+	void __iomem *base = desc->data;
+
+	stat0 = sa1111_readl(base + SA1111_INTSTATCLR0);
+	stat1 = sa1111_readl(base + SA1111_INTSTATCLR1);
+
+	sa1111_writel(stat0, base + SA1111_INTSTATCLR0);
+
+	desc->chip->ack(irq);
+
+	sa1111_writel(stat1, base + SA1111_INTSTATCLR1);
+
+	if (stat0 == 0 && stat1 == 0) {
+		do_bad_IRQ(irq, desc, regs);
+		return;
+	}
+
+	for (i = IRQ_SA1111_START; stat0; i++, stat0 >>= 1)
+		if (stat0 & 1)
+			do_edge_IRQ(i, irq_desc + i, regs);
+
+	for (i = IRQ_SA1111_START + 32; stat1; i++, stat1 >>= 1)
+		if (stat1 & 1)
+			do_edge_IRQ(i, irq_desc + i, regs);
+
+	/* For level-based interrupts */
+	desc->chip->unmask(irq);
+}
+
+#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_irq(unsigned int irq)
+{
+}
+
+static void sa1111_mask_lowirq(unsigned int irq)
+{
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ie0;
+
+	ie0 = sa1111_readl(mapbase + SA1111_INTEN0);
+	ie0 &= ~SA1111_IRQMASK_LO(irq);
+	writel(ie0, mapbase + SA1111_INTEN0);
+}
+
+static void sa1111_unmask_lowirq(unsigned int irq)
+{
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ie0;
+
+	ie0 = sa1111_readl(mapbase + SA1111_INTEN0);
+	ie0 |= SA1111_IRQMASK_LO(irq);
+	sa1111_writel(ie0, mapbase + SA1111_INTEN0);
+}
+
+/*
+ * Attempt to re-trigger the interrupt.  The SA1111 contains a register
+ * (INTSET) which claims to do this.  However, in practice no amount of
+ * manipulation of INTEN and INTSET guarantees that the interrupt will
+ * be triggered.  In fact, its very difficult, if not impossible to get
+ * INTSET to re-trigger the interrupt.
+ */
+static int sa1111_retrigger_lowirq(unsigned int irq)
+{
+	unsigned int mask = SA1111_IRQMASK_LO(irq);
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ip0;
+	int i;
+
+	ip0 = sa1111_readl(mapbase + SA1111_INTPOL0);
+	for (i = 0; i < 8; i++) {
+		sa1111_writel(ip0 ^ mask, mapbase + SA1111_INTPOL0);
+		sa1111_writel(ip0, mapbase + SA1111_INTPOL0);
+		if (sa1111_readl(mapbase + SA1111_INTSTATCLR1) & mask)
+			break;
+	}
+
+	if (i == 8)
+		printk(KERN_ERR "Danger Will Robinson: failed to "
+			"re-trigger IRQ%d\n", irq);
+	return i == 8 ? -1 : 0;
+}
+
+static int sa1111_type_lowirq(unsigned int irq, unsigned int flags)
+{
+	unsigned int mask = SA1111_IRQMASK_LO(irq);
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ip0;
+
+	if (flags == IRQT_PROBE)
+		return 0;
+
+	if ((!(flags & __IRQT_RISEDGE) ^ !(flags & __IRQT_FALEDGE)) == 0)
+		return -EINVAL;
+
+	ip0 = sa1111_readl(mapbase + SA1111_INTPOL0);
+	if (flags & __IRQT_RISEDGE)
+		ip0 &= ~mask;
+	else
+		ip0 |= mask;
+	sa1111_writel(ip0, mapbase + SA1111_INTPOL0);
+	sa1111_writel(ip0, mapbase + SA1111_WAKEPOL0);
+
+	return 0;
+}
+
+static int sa1111_wake_lowirq(unsigned int irq, unsigned int on)
+{
+	unsigned int mask = SA1111_IRQMASK_LO(irq);
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long we0;
+
+	we0 = sa1111_readl(mapbase + SA1111_WAKEEN0);
+	if (on)
+		we0 |= mask;
+	else
+		we0 &= ~mask;
+	sa1111_writel(we0, mapbase + SA1111_WAKEEN0);
+
+	return 0;
+}
+
+static struct irqchip sa1111_low_chip = {
+	.ack		= sa1111_ack_irq,
+	.mask		= sa1111_mask_lowirq,
+	.unmask		= sa1111_unmask_lowirq,
+	.retrigger	= sa1111_retrigger_lowirq,
+	.type		= sa1111_type_lowirq,
+	.wake		= sa1111_wake_lowirq,
+};
+
+static void sa1111_mask_highirq(unsigned int irq)
+{
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ie1;
+
+	ie1 = sa1111_readl(mapbase + SA1111_INTEN1);
+	ie1 &= ~SA1111_IRQMASK_HI(irq);
+	sa1111_writel(ie1, mapbase + SA1111_INTEN1);
+}
+
+static void sa1111_unmask_highirq(unsigned int irq)
+{
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ie1;
+
+	ie1 = sa1111_readl(mapbase + SA1111_INTEN1);
+	ie1 |= SA1111_IRQMASK_HI(irq);
+	sa1111_writel(ie1, mapbase + SA1111_INTEN1);
+}
+
+/*
+ * Attempt to re-trigger the interrupt.  The SA1111 contains a register
+ * (INTSET) which claims to do this.  However, in practice no amount of
+ * manipulation of INTEN and INTSET guarantees that the interrupt will
+ * be triggered.  In fact, its very difficult, if not impossible to get
+ * INTSET to re-trigger the interrupt.
+ */
+static int sa1111_retrigger_highirq(unsigned int irq)
+{
+	unsigned int mask = SA1111_IRQMASK_HI(irq);
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ip1;
+	int i;
+
+	ip1 = sa1111_readl(mapbase + SA1111_INTPOL1);
+	for (i = 0; i < 8; i++) {
+		sa1111_writel(ip1 ^ mask, mapbase + SA1111_INTPOL1);
+		sa1111_writel(ip1, mapbase + SA1111_INTPOL1);
+		if (sa1111_readl(mapbase + SA1111_INTSTATCLR1) & mask)
+			break;
+	}
+
+	if (i == 8)
+		printk(KERN_ERR "Danger Will Robinson: failed to "
+			"re-trigger IRQ%d\n", irq);
+	return i == 8 ? -1 : 0;
+}
+
+static int sa1111_type_highirq(unsigned int irq, unsigned int flags)
+{
+	unsigned int mask = SA1111_IRQMASK_HI(irq);
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long ip1;
+
+	if (flags == IRQT_PROBE)
+		return 0;
+
+	if ((!(flags & __IRQT_RISEDGE) ^ !(flags & __IRQT_FALEDGE)) == 0)
+		return -EINVAL;
+
+	ip1 = sa1111_readl(mapbase + SA1111_INTPOL1);
+	if (flags & __IRQT_RISEDGE)
+		ip1 &= ~mask;
+	else
+		ip1 |= mask;
+	sa1111_writel(ip1, mapbase + SA1111_INTPOL1);
+	sa1111_writel(ip1, mapbase + SA1111_WAKEPOL1);
+
+	return 0;
+}
+
+static int sa1111_wake_highirq(unsigned int irq, unsigned int on)
+{
+	unsigned int mask = SA1111_IRQMASK_HI(irq);
+	void __iomem *mapbase = get_irq_chipdata(irq);
+	unsigned long we1;
+
+	we1 = sa1111_readl(mapbase + SA1111_WAKEEN1);
+	if (on)
+		we1 |= mask;
+	else
+		we1 &= ~mask;
+	sa1111_writel(we1, mapbase + SA1111_WAKEEN1);
+
+	return 0;
+}
+
+static struct irqchip sa1111_high_chip = {
+	.ack		= sa1111_ack_irq,
+	.mask		= sa1111_mask_highirq,
+	.unmask		= sa1111_unmask_highirq,
+	.retrigger	= sa1111_retrigger_highirq,
+	.type		= sa1111_type_highirq,
+	.wake		= sa1111_wake_highirq,
+};
+
+static void sa1111_setup_irq(struct sa1111 *sachip)
+{
+	void __iomem *irqbase = sachip->base + SA1111_INTC;
+	unsigned int irq;
+
+	/*
+	 * We're guaranteed that this region hasn't been taken.
+	 */
+	request_mem_region(sachip->phys + SA1111_INTC, 512, "irq");
+
+	/* disable all IRQs */
+	sa1111_writel(0, irqbase + SA1111_INTEN0);
+	sa1111_writel(0, irqbase + SA1111_INTEN1);
+	sa1111_writel(0, irqbase + SA1111_WAKEEN0);
+	sa1111_writel(0, irqbase + SA1111_WAKEEN1);
+
+	/*
+	 * detect on rising edge.  Note: Feb 2001 Errata for SA1111
+	 * specifies that S0ReadyInt and S1ReadyInt should be '1'.
+	 */
+	sa1111_writel(0, irqbase + SA1111_INTPOL0);
+	sa1111_writel(SA1111_IRQMASK_HI(IRQ_S0_READY_NINT) |
+		      SA1111_IRQMASK_HI(IRQ_S1_READY_NINT),
+		      irqbase + SA1111_INTPOL1);
+
+	/* clear all IRQs */
+	sa1111_writel(~0, irqbase + SA1111_INTSTATCLR0);
+	sa1111_writel(~0, irqbase + SA1111_INTSTATCLR1);
+
+	for (irq = IRQ_GPAIN0; irq <= SSPROR; irq++) {
+		set_irq_chip(irq, &sa1111_low_chip);
+		set_irq_chipdata(irq, irqbase);
+		set_irq_handler(irq, do_edge_IRQ);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	for (irq = AUDXMTDMADONEA; irq <= IRQ_S1_BVD1_STSCHG; irq++) {
+		set_irq_chip(irq, &sa1111_high_chip);
+		set_irq_chipdata(irq, irqbase);
+		set_irq_handler(irq, do_edge_IRQ);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	/*
+	 * Register SA1111 interrupt
+	 */
+	set_irq_type(sachip->irq, IRQT_RISING);
+	set_irq_data(sachip->irq, irqbase);
+	set_irq_chained_handler(sachip->irq, sa1111_irq_handler);
+}
+
+/*
+ * Bring the SA1111 out of reset.  This requires a set procedure:
+ *  1. nRESET asserted (by hardware)
+ *  2. CLK turned on from SA1110
+ *  3. nRESET deasserted
+ *  4. VCO turned on, PLL_BYPASS turned off
+ *  5. Wait lock time, then assert RCLKEn
+ *  7. PCR set to allow clocking of individual functions
+ *
+ * Until we've done this, the only registers we can access are:
+ *   SBI_SKCR
+ *   SBI_SMCR
+ *   SBI_SKID
+ */
+static void sa1111_wake(struct sa1111 *sachip)
+{
+	unsigned long flags, r;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+
+#ifdef CONFIG_ARCH_SA1100
+	/*
+	 * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
+	 * (SA-1110 Developer's Manual, section 9.1.2.1)
+	 */
+	GAFR |= GPIO_32_768kHz;
+	GPDR |= GPIO_32_768kHz;
+	TUCR = TUCR_3_6864MHz;
+#elif CONFIG_ARCH_PXA
+	pxa_gpio_mode(GPIO11_3_6MHz_MD);
+#else
+#error missing clock setup
+#endif
+
+	/*
+	 * Turn VCO on, and disable PLL Bypass.
+	 */
+	r = sa1111_readl(sachip->base + SA1111_SKCR);
+	r &= ~SKCR_VCO_OFF;
+	sa1111_writel(r, sachip->base + SA1111_SKCR);
+	r |= SKCR_PLL_BYPASS | SKCR_OE_EN;
+	sa1111_writel(r, sachip->base + SA1111_SKCR);
+
+	/*
+	 * Wait lock time.  SA1111 manual _doesn't_
+	 * specify a figure for this!  We choose 100us.
+	 */
+	udelay(100);
+
+	/*
+	 * Enable RCLK.  We also ensure that RDYEN is set.
+	 */
+	r |= SKCR_RCLKEN | SKCR_RDYEN;
+	sa1111_writel(r, sachip->base + SA1111_SKCR);
+
+	/*
+	 * Wait 14 RCLK cycles for the chip to finish coming out
+	 * of reset. (RCLK=24MHz).  This is 590ns.
+	 */
+	udelay(1);
+
+	/*
+	 * Ensure all clocks are initially off.
+	 */
+	sa1111_writel(0, sachip->base + SA1111_SKPCR);
+
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+#ifdef CONFIG_ARCH_SA1100
+
+static u32 sa1111_dma_mask[] = {
+	~0,
+	~(1 << 20),
+	~(1 << 23),
+	~(1 << 24),
+	~(1 << 25),
+	~(1 << 20),
+	~(1 << 20),
+	0,
+};
+
+/*
+ * Configure the SA1111 shared memory controller.
+ */
+void
+sa1111_configure_smc(struct sa1111 *sachip, int sdram, unsigned int drac,
+		     unsigned int cas_latency)
+{
+	unsigned int smcr = SMCR_DTIM | SMCR_MBGE | FInsrt(drac, SMCR_DRAC);
+
+	if (cas_latency == 3)
+		smcr |= SMCR_CLAT;
+
+	sa1111_writel(smcr, sachip->base + SA1111_SMCR);
+
+	/*
+	 * Now clear the bits in the DMA mask to work around the SA1111
+	 * DMA erratum (Intel StrongARM SA-1111 Microprocessor Companion
+	 * Chip Specification Update, June 2000, Erratum #7).
+	 */
+	if (sachip->dev->dma_mask)
+		*sachip->dev->dma_mask &= sa1111_dma_mask[drac >> 2];
+
+	sachip->dev->coherent_dma_mask &= sa1111_dma_mask[drac >> 2];
+}
+
+#endif
+
+static void sa1111_dev_release(struct device *_dev)
+{
+	struct sa1111_dev *dev = SA1111_DEV(_dev);
+
+	release_resource(&dev->res);
+	kfree(dev);
+}
+
+static int
+sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,
+		      struct sa1111_dev_info *info)
+{
+	struct sa1111_dev *dev;
+	int ret;
+
+	dev = kmalloc(sizeof(struct sa1111_dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	memset(dev, 0, sizeof(struct sa1111_dev));
+
+	snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
+		 "%4.4lx", info->offset);
+
+	dev->devid	 = info->devid;
+	dev->dev.parent  = sachip->dev;
+	dev->dev.bus     = &sa1111_bus_type;
+	dev->dev.release = sa1111_dev_release;
+	dev->dev.coherent_dma_mask = sachip->dev->coherent_dma_mask;
+	dev->res.start   = sachip->phys + info->offset;
+	dev->res.end     = dev->res.start + 511;
+	dev->res.name    = dev->dev.bus_id;
+	dev->res.flags   = IORESOURCE_MEM;
+	dev->mapbase     = sachip->base + info->offset;
+	dev->skpcr_mask  = info->skpcr_mask;
+	memmove(dev->irq, info->irq, sizeof(dev->irq));
+
+	ret = request_resource(parent, &dev->res);
+	if (ret) {
+		printk("SA1111: failed to allocate resource for %s\n",
+			dev->res.name);
+		kfree(dev);
+		goto out;
+	}
+
+
+	ret = device_register(&dev->dev);
+	if (ret) {
+		release_resource(&dev->res);
+		kfree(dev);
+		goto out;
+	}
+
+	/*
+	 * If the parent device has a DMA mask associated with it,
+	 * propagate it down to the children.
+	 */
+	if (sachip->dev->dma_mask) {
+		dev->dma_mask = *sachip->dev->dma_mask;
+		dev->dev.dma_mask = &dev->dma_mask;
+
+		if (dev->dma_mask != 0xffffffffUL) {
+			ret = dmabounce_register_dev(&dev->dev, 1024, 4096);
+			if (ret) {
+				printk("SA1111: Failed to register %s with dmabounce", dev->dev.bus_id);
+				device_unregister(&dev->dev);
+			}
+		}
+	}
+
+out:
+	return ret;
+}
+
+/**
+ *	sa1111_probe - probe for a single SA1111 chip.
+ *	@phys_addr: physical address of device.
+ *
+ *	Probe for a SA1111 chip.  This must be called
+ *	before any other SA1111-specific code.
+ *
+ *	Returns:
+ *	%-ENODEV	device not found.
+ *	%-EBUSY		physical address already marked in-use.
+ *	%0		successful.
+ */
+static int
+__sa1111_probe(struct device *me, struct resource *mem, int irq)
+{
+	struct sa1111 *sachip;
+	unsigned long id;
+	unsigned int has_devs, val;
+	int i, ret = -ENODEV;
+
+	sachip = kmalloc(sizeof(struct sa1111), GFP_KERNEL);
+	if (!sachip)
+		return -ENOMEM;
+
+	memset(sachip, 0, sizeof(struct sa1111));
+
+	spin_lock_init(&sachip->lock);
+
+	sachip->dev = me;
+	dev_set_drvdata(sachip->dev, sachip);
+
+	sachip->phys = mem->start;
+	sachip->irq = irq;
+
+	/*
+	 * Map the whole region.  This also maps the
+	 * registers for our children.
+	 */
+	sachip->base = ioremap(mem->start, PAGE_SIZE * 2);
+	if (!sachip->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Probe for the chip.  Only touch the SBI registers.
+	 */
+	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;
+		goto unmap;
+	}
+
+	printk(KERN_INFO "SA1111 Microprocessor Companion Chip: "
+		"silicon revision %lx, metal revision %lx\n",
+		(id & SKID_SIREV_MASK)>>4, (id & SKID_MTREV_MASK));
+
+	/*
+	 * We found it.  Wake the chip up, and initialise.
+	 */
+	sa1111_wake(sachip);
+
+#ifdef CONFIG_ARCH_SA1100
+	/*
+	 * The SDRAM configuration of the SA1110 and the SA1111 must
+	 * match.  This is very important to ensure that SA1111 accesses
+	 * don't corrupt the SDRAM.  Note that this ungates the SA1111's
+	 * MBGNT signal, so we must have called sa1110_mb_disable()
+	 * beforehand.
+	 */
+	sa1111_configure_smc(sachip, 1,
+			     FExtr(MDCNFG, MDCNFG_SA1110_DRAC0),
+			     FExtr(MDCNFG, MDCNFG_SA1110_TDL0));
+
+	/*
+	 * We only need to turn on DCLK whenever we want to use the
+	 * DMA.  It can otherwise be held firmly in the off position.
+	 * (currently, we always enable it.)
+	 */
+	val = sa1111_readl(sachip->base + SA1111_SKPCR);
+	sa1111_writel(val | SKPCR_DCLKEN, sachip->base + SA1111_SKPCR);
+
+	/*
+	 * Enable the SA1110 memory bus request and grant signals.
+	 */
+	sa1110_mb_enable();
+#endif
+
+	/*
+	 * The interrupt controller must be initialised before any
+	 * other device to ensure that the interrupts are available.
+	 */
+	if (sachip->irq != NO_IRQ)
+		sa1111_setup_irq(sachip);
+
+	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(sa1111_devices); i++)
+		if (has_devs & (1 << i))
+			sa1111_init_one_child(sachip, mem, &sa1111_devices[i]);
+
+	return 0;
+
+ unmap:
+	iounmap(sachip->base);
+ out:
+	kfree(sachip);
+	return ret;
+}
+
+static void __sa1111_remove(struct sa1111 *sachip)
+{
+	struct list_head *l, *n;
+	void __iomem *irqbase = sachip->base + SA1111_INTC;
+
+	list_for_each_safe(l, n, &sachip->dev->children) {
+		struct device *d = list_to_dev(l);
+
+		device_unregister(d);
+	}
+
+	/* disable all IRQs */
+	sa1111_writel(0, irqbase + SA1111_INTEN0);
+	sa1111_writel(0, irqbase + SA1111_INTEN1);
+	sa1111_writel(0, irqbase + SA1111_WAKEEN0);
+	sa1111_writel(0, irqbase + SA1111_WAKEEN1);
+
+	if (sachip->irq != NO_IRQ) {
+		set_irq_chained_handler(sachip->irq, NULL);
+		set_irq_data(sachip->irq, NULL);
+
+		release_mem_region(sachip->phys + SA1111_INTC, 512);
+	}
+
+	iounmap(sachip->base);
+	kfree(sachip);
+}
+
+/*
+ * According to the "Intel StrongARM SA-1111 Microprocessor Companion
+ * Chip Specification Update" (June 2000), erratum #7, there is a
+ * 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.
+ *
+ * This routine only identifies whether or not a given DMA address
+ * is susceptible to the bug.
+ *
+ * This should only get called for sa1111_device types due to the
+ * way we configure our device dma_masks.
+ */
+int dma_needs_bounce(struct device *dev, dma_addr_t addr, size_t size)
+{
+	/*
+	 * Section 4.6 of the "Intel StrongARM SA-1111 Development Module
+	 * User's Guide" mentions that jumpers R51 and R52 control the
+	 * target of SA-1111 DMA (either SDRAM bank 0 on Assabet, or
+	 * SDRAM bank 1 on Neponset). The default configuration selects
+	 * Assabet, so any address in bank 1 is necessarily invalid.
+	 */
+	return ((machine_is_assabet() || machine_is_pfs168()) &&
+		(addr >= 0xc8000000 || (addr + size) >= 0xc8000000));
+}
+
+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;
+};
+
+#ifdef CONFIG_PM
+
+static int sa1111_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+	struct sa1111 *sachip = dev_get_drvdata(dev);
+	struct sa1111_save_data *save;
+	unsigned long flags;
+	unsigned int val;
+	void __iomem *base;
+
+	if (level != SUSPEND_DISABLE)
+		return 0;
+
+	save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
+	if (!save)
+		return -ENOMEM;
+	dev->power.saved_state = save;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+
+	/*
+	 * Save state.
+	 */
+	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);
+
+	/*
+	 * Disable.
+	 */
+	val = sa1111_readl(sachip->base + SA1111_SKCR);
+	sa1111_writel(val | SKCR_SLEEP, sachip->base + SA1111_SKCR);
+	sa1111_writel(0, sachip->base + SA1111_SKPWM0);
+	sa1111_writel(0, sachip->base + SA1111_SKPWM1);
+
+	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_get_drvdata(dev);
+	struct sa1111_save_data *save;
+	unsigned long flags, id;
+	void __iomem *base;
+
+	if (level != RESUME_ENABLE)
+		return 0;
+
+	save = (struct sa1111_save_data *)dev->power.saved_state;
+	if (!save)
+		return 0;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+
+	/*
+	 * Ensure that the SA1111 is still here.
+	 * FIXME: shouldn't do this here.
+	 */
+	id = sa1111_readl(sachip->base + SA1111_SKID);
+	if ((id & SKID_ID_MASK) != SKID_SA1111_ID) {
+		__sa1111_remove(sachip);
+		dev_set_drvdata(dev, NULL);
+		kfree(save);
+		return 0;
+	}
+
+	/*
+	 * First of all, wake up the chip.
+	 */
+	sa1111_wake(sachip);
+	sa1111_writel(0, sachip->base + SA1111_INTC + SA1111_INTEN0);
+	sa1111_writel(0, sachip->base + SA1111_INTC + SA1111_INTEN1);
+
+	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);
+
+	dev->power.saved_state = NULL;
+	kfree(save);
+
+	return 0;
+}
+
+#else
+#define sa1111_suspend NULL
+#define sa1111_resume  NULL
+#endif
+
+static int sa1111_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *mem;
+	int irq;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem)
+		return -EINVAL;
+	irq = platform_get_irq(pdev, 0);
+
+	return __sa1111_probe(dev, mem, irq);
+}
+
+static int sa1111_remove(struct device *dev)
+{
+	struct sa1111 *sachip = dev_get_drvdata(dev);
+
+	if (sachip) {
+		__sa1111_remove(sachip);
+		dev_set_drvdata(dev, NULL);
+
+#ifdef CONFIG_PM
+		kfree(dev->power.saved_state);
+		dev->power.saved_state = NULL;
+#endif
+	}
+
+	return 0;
+}
+
+/*
+ *	Not sure if this should be on the system bus or not yet.
+ *	We really want some way to register a system device at
+ *	the per-machine level, and then have this driver pick
+ *	up the registered devices.
+ *
+ *	We also need to handle the SDRAM configuration for
+ *	PXA250/SA1110 machine classes.
+ */
+static struct device_driver sa1111_device_driver = {
+	.name		= "sa1111",
+	.bus		= &platform_bus_type,
+	.probe		= sa1111_probe,
+	.remove		= sa1111_remove,
+	.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 *)dev_get_drvdata(sadev->dev.parent);
+}
+
+/*
+ * 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);
+}
+
+void sa1111_set_io_dir(struct sa1111_dev *sadev,
+		       unsigned int bits, unsigned int dir,
+		       unsigned int sleep_dir)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long flags;
+	unsigned int val;
+	void __iomem *gpio = sachip->base + SA1111_GPIO;
+
+#define MODIFY_BITS(port, mask, dir)		\
+	if (mask) {				\
+		val = sa1111_readl(port);	\
+		val &= ~(mask);			\
+		val |= (dir) & (mask);		\
+		sa1111_writel(val, port);	\
+	}
+
+	spin_lock_irqsave(&sachip->lock, flags);
+	MODIFY_BITS(gpio + SA1111_GPIO_PADDR, bits & 15, dir);
+	MODIFY_BITS(gpio + SA1111_GPIO_PBDDR, (bits >> 8) & 255, dir >> 8);
+	MODIFY_BITS(gpio + SA1111_GPIO_PCDDR, (bits >> 16) & 255, dir >> 16);
+
+	MODIFY_BITS(gpio + SA1111_GPIO_PASDR, bits & 15, sleep_dir);
+	MODIFY_BITS(gpio + SA1111_GPIO_PBSDR, (bits >> 8) & 255, sleep_dir >> 8);
+	MODIFY_BITS(gpio + SA1111_GPIO_PCSDR, (bits >> 16) & 255, sleep_dir >> 16);
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+void sa1111_set_io(struct sa1111_dev *sadev, unsigned int bits, unsigned int v)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long flags;
+	unsigned int val;
+	void __iomem *gpio = sachip->base + SA1111_GPIO;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+	MODIFY_BITS(gpio + SA1111_GPIO_PADWR, bits & 15, v);
+	MODIFY_BITS(gpio + SA1111_GPIO_PBDWR, (bits >> 8) & 255, v >> 8);
+	MODIFY_BITS(gpio + SA1111_GPIO_PCDWR, (bits >> 16) & 255, v >> 16);
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+void sa1111_set_sleep_io(struct sa1111_dev *sadev, unsigned int bits, unsigned int v)
+{
+	struct sa1111 *sachip = sa1111_chip_driver(sadev);
+	unsigned long flags;
+	unsigned int val;
+	void __iomem *gpio = sachip->base + SA1111_GPIO;
+
+	spin_lock_irqsave(&sachip->lock, flags);
+	MODIFY_BITS(gpio + SA1111_GPIO_PASSR, bits & 15, v);
+	MODIFY_BITS(gpio + SA1111_GPIO_PBSSR, (bits >> 8) & 255, v >> 8);
+	MODIFY_BITS(gpio + SA1111_GPIO_PCSSR, (bits >> 16) & 255, v >> 16);
+	spin_unlock_irqrestore(&sachip->lock, flags);
+}
+
+/*
+ * 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;
+}
+
+static int sa1111_bus_suspend(struct device *dev, pm_message_t state)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+	struct sa1111_driver *drv = SA1111_DRV(dev->driver);
+	int ret = 0;
+
+	if (drv && drv->suspend)
+		ret = drv->suspend(sadev, state);
+	return ret;
+}
+
+static int sa1111_bus_resume(struct device *dev)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+	struct sa1111_driver *drv = SA1111_DRV(dev->driver);
+	int ret = 0;
+
+	if (drv && drv->resume)
+		ret = drv->resume(sadev);
+	return ret;
+}
+
+static int sa1111_bus_probe(struct device *dev)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+	struct sa1111_driver *drv = SA1111_DRV(dev->driver);
+	int ret = -ENODEV;
+
+	if (drv->probe)
+		ret = drv->probe(sadev);
+	return ret;
+}
+
+static int sa1111_bus_remove(struct device *dev)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(dev);
+	struct sa1111_driver *drv = SA1111_DRV(dev->driver);
+	int ret = 0;
+
+	if (drv->remove)
+		ret = drv->remove(sadev);
+	return ret;
+}
+
+struct bus_type sa1111_bus_type = {
+	.name		= "sa1111-rab",
+	.match		= sa1111_match,
+	.suspend	= sa1111_bus_suspend,
+	.resume		= sa1111_bus_resume,
+};
+
+int sa1111_driver_register(struct sa1111_driver *driver)
+{
+	driver->drv.probe = sa1111_bus_probe;
+	driver->drv.remove = sa1111_bus_remove;
+	driver->drv.bus = &sa1111_bus_type;
+	return driver_register(&driver->drv);
+}
+
+void sa1111_driver_unregister(struct sa1111_driver *driver)
+{
+	driver_unregister(&driver->drv);
+}
+
+static int __init sa1111_init(void)
+{
+	int ret = bus_register(&sa1111_bus_type);
+	if (ret == 0)
+		driver_register(&sa1111_device_driver);
+	return ret;
+}
+
+static void __exit sa1111_exit(void)
+{
+	driver_unregister(&sa1111_device_driver);
+	bus_unregister(&sa1111_bus_type);
+}
+
+module_init(sa1111_init);
+module_exit(sa1111_exit);
+
+MODULE_DESCRIPTION("Intel Corporation SA1111 core driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sa1111_select_audio_mode);
+EXPORT_SYMBOL(sa1111_set_audio_rate);
+EXPORT_SYMBOL(sa1111_get_audio_rate);
+EXPORT_SYMBOL(sa1111_set_io_dir);
+EXPORT_SYMBOL(sa1111_set_io);
+EXPORT_SYMBOL(sa1111_set_sleep_io);
+EXPORT_SYMBOL(sa1111_enable_device);
+EXPORT_SYMBOL(sa1111_disable_device);
+EXPORT_SYMBOL(sa1111_pll_clock);
+EXPORT_SYMBOL(sa1111_bus_type);
+EXPORT_SYMBOL(sa1111_driver_register);
+EXPORT_SYMBOL(sa1111_driver_unregister);
diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c
new file mode 100644
index 0000000..cfd0d3e
--- /dev/null
+++ b/arch/arm/common/scoop.c
@@ -0,0 +1,176 @@
+/*
+ * Support code for the SCOOP interface found on various Sharp PDAs
+ *
+ * Copyright (c) 2004 Richard Purdie
+ *
+ *	Based on code written by Sharp/Lineo for 2.4 kernels
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/device.h>
+#include <asm/io.h>
+#include <asm/hardware/scoop.h>
+
+#define SCOOP_REG(d,adr) (*(volatile unsigned short*)(d +(adr)))
+
+struct  scoop_dev {
+	void  *base;
+	spinlock_t scoop_lock;
+	u32 scoop_gpwr;
+};
+
+void reset_scoop(struct device *dev)
+{
+	struct scoop_dev *sdev = dev_get_drvdata(dev);
+
+	SCOOP_REG(sdev->base,SCOOP_MCR) = 0x0100;  // 00
+	SCOOP_REG(sdev->base,SCOOP_CDR) = 0x0000;  // 04
+	SCOOP_REG(sdev->base,SCOOP_CPR) = 0x0000;  // 0C
+	SCOOP_REG(sdev->base,SCOOP_CCR) = 0x0000;  // 10
+	SCOOP_REG(sdev->base,SCOOP_IMR) = 0x0000;  // 18
+	SCOOP_REG(sdev->base,SCOOP_IRM) = 0x00FF;  // 14
+	SCOOP_REG(sdev->base,SCOOP_ISR) = 0x0000;  // 1C
+	SCOOP_REG(sdev->base,SCOOP_IRM) = 0x0000;
+}
+
+unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
+{
+	unsigned short gpio_bit;
+	unsigned long flag;
+	struct scoop_dev *sdev = dev_get_drvdata(dev);
+
+	spin_lock_irqsave(&sdev->scoop_lock, flag);
+	gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) | bit;
+	SCOOP_REG(sdev->base, SCOOP_GPWR) = gpio_bit;
+	spin_unlock_irqrestore(&sdev->scoop_lock, flag);
+
+	return gpio_bit;
+}
+
+unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
+{
+	unsigned short gpio_bit;
+	unsigned long flag;
+	struct scoop_dev *sdev = dev_get_drvdata(dev);
+
+	spin_lock_irqsave(&sdev->scoop_lock, flag);
+	gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) & ~bit;
+	SCOOP_REG(sdev->base,SCOOP_GPWR) = gpio_bit;
+	spin_unlock_irqrestore(&sdev->scoop_lock, flag);
+
+	return gpio_bit;
+}
+
+EXPORT_SYMBOL(set_scoop_gpio);
+EXPORT_SYMBOL(reset_scoop_gpio);
+
+unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
+{
+	struct scoop_dev *sdev = dev_get_drvdata(dev);
+	return SCOOP_REG(sdev->base,reg);
+}
+
+void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
+{
+	struct scoop_dev *sdev = dev_get_drvdata(dev);
+	SCOOP_REG(sdev->base,reg)=data;
+}
+
+EXPORT_SYMBOL(reset_scoop);
+EXPORT_SYMBOL(read_scoop_reg);
+EXPORT_SYMBOL(write_scoop_reg);
+
+#ifdef CONFIG_PM
+static int scoop_suspend(struct device *dev, uint32_t state, uint32_t level)
+{
+	if (level == SUSPEND_POWER_DOWN) {
+		struct scoop_dev *sdev = dev_get_drvdata(dev);
+
+		sdev->scoop_gpwr = SCOOP_REG(sdev->base,SCOOP_GPWR);
+		SCOOP_REG(sdev->base,SCOOP_GPWR) = 0;
+	}
+	return 0;
+}
+
+static int scoop_resume(struct device *dev, uint32_t level)
+{
+	if (level == RESUME_POWER_ON) {
+		struct scoop_dev *sdev = dev_get_drvdata(dev);
+
+		SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr;
+	}
+	return 0;
+}
+#else
+#define scoop_suspend	NULL
+#define scoop_resume	NULL
+#endif
+
+int __init scoop_probe(struct device *dev)
+{
+	struct scoop_dev *devptr;
+	struct scoop_config *inf;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!mem)
+		return -EINVAL;
+
+	devptr = kmalloc(sizeof(struct scoop_dev), GFP_KERNEL);
+
+	if (!devptr)
+		return  -ENOMEM;
+
+	memset(devptr, 0, sizeof(struct scoop_dev));
+	spin_lock_init(&devptr->scoop_lock);
+
+	inf = dev->platform_data;
+	devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
+
+	if (!devptr->base) {
+		kfree(devptr);
+		return -ENOMEM;
+	}
+
+	dev_set_drvdata(dev, devptr);
+
+	printk("Sharp Scoop Device found at 0x%08x -> 0x%08x\n",(unsigned int)mem->start,(unsigned int)devptr->base);
+
+	SCOOP_REG(devptr->base, SCOOP_MCR) = 0x0140;
+	reset_scoop(dev);
+	SCOOP_REG(devptr->base, SCOOP_GPCR) = inf->io_dir & 0xffff;
+	SCOOP_REG(devptr->base, SCOOP_GPWR) = inf->io_out & 0xffff;
+
+	return 0;
+}
+
+static int scoop_remove(struct device *dev)
+{
+	struct scoop_dev *sdev = dev_get_drvdata(dev);
+	if (sdev) {
+		iounmap(sdev->base);
+		kfree(sdev);
+		dev_set_drvdata(dev, NULL);
+	}
+	return 0;
+}
+
+static struct device_driver scoop_driver = {
+	.name		= "sharp-scoop",
+	.bus		= &platform_bus_type,
+	.probe		= scoop_probe,
+	.remove 	= scoop_remove,
+	.suspend	= scoop_suspend,
+	.resume		= scoop_resume,
+};
+
+int __init scoop_init(void)
+{
+	return driver_register(&scoop_driver);
+}
+
+subsys_initcall(scoop_init);
diff --git a/arch/arm/common/sharpsl_param.c b/arch/arm/common/sharpsl_param.c
new file mode 100644
index 0000000..c2c557a
--- /dev/null
+++ b/arch/arm/common/sharpsl_param.c
@@ -0,0 +1,60 @@
+/*
+ * Hardware parameter area specific to Sharp SL series devices
+ *
+ * Copyright (c) 2005 Richard Purdie
+ *
+ * Based on Sharp's 2.4 kernel patches
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/mach/sharpsl_param.h>
+
+/*
+ * Certain hardware parameters determined at the time of device manufacture,
+ * typically including LCD parameters are loaded by the bootloader at the
+ * address PARAM_BASE. As the kernel will overwrite them, we need to store
+ * them early in the boot process, then pass them to the appropriate drivers.
+ * Not all devices use all paramaters but the format is common to all.
+ */
+#ifdef ARCH_SA1100
+#define PARAM_BASE	0xe8ffc000
+#else
+#define PARAM_BASE	0xa0000a00
+#endif
+#define MAGIC_CHG(a,b,c,d) ( ( d << 24 ) | ( c << 16 )  | ( b << 8 ) | a )
+
+#define COMADJ_MAGIC	MAGIC_CHG('C','M','A','D')
+#define UUID_MAGIC	MAGIC_CHG('U','U','I','D')
+#define TOUCH_MAGIC	MAGIC_CHG('T','U','C','H')
+#define AD_MAGIC	MAGIC_CHG('B','V','A','D')
+#define PHAD_MAGIC	MAGIC_CHG('P','H','A','D')
+
+struct sharpsl_param_info sharpsl_param;
+
+void sharpsl_save_param(void)
+{
+	memcpy(&sharpsl_param, (void *)PARAM_BASE, sizeof(struct sharpsl_param_info));
+
+	if (sharpsl_param.comadj_keyword != COMADJ_MAGIC)
+		sharpsl_param.comadj=-1;
+
+	if (sharpsl_param.phad_keyword != PHAD_MAGIC)
+		sharpsl_param.phadadj=-1;
+
+	if (sharpsl_param.uuid_keyword != UUID_MAGIC)
+		sharpsl_param.uuid[0]=-1;
+
+	if (sharpsl_param.touch_keyword != TOUCH_MAGIC)
+		sharpsl_param.touch_xp=-1;
+
+	if (sharpsl_param.adadj_keyword != AD_MAGIC)
+		sharpsl_param.adadj=-1;
+}
+
+
diff --git a/arch/arm/common/time-acorn.c b/arch/arm/common/time-acorn.c
new file mode 100644
index 0000000..486add8
--- /dev/null
+++ b/arch/arm/common/time-acorn.c
@@ -0,0 +1,96 @@
+/*
+ *  linux/arch/arm/common/time-acorn.c
+ *
+ *  Copyright (c) 1996-2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Changelog:
+ *   24-Sep-1996	RMK	Created
+ *   10-Oct-1996	RMK	Brought up to date with arch-sa110eval
+ *   04-Dec-1997	RMK	Updated for new arch/arm/time.c
+ *   13=Jun-2004	DS	Moved to arch/arm/common b/c shared w/CLPS7500
+ */
+#include <linux/timex.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/hardware/ioc.h>
+
+#include <asm/mach/time.h>
+
+unsigned long ioc_timer_gettimeoffset(void)
+{
+	unsigned int count1, count2, status;
+	long offset;
+
+	ioc_writeb (0, IOC_T0LATCH);
+	barrier ();
+	count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
+	barrier ();
+	status = ioc_readb(IOC_IRQREQA);
+	barrier ();
+	ioc_writeb (0, IOC_T0LATCH);
+	barrier ();
+	count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
+
+	offset = count2;
+	if (count2 < count1) {
+		/*
+		 * We have not had an interrupt between reading count1
+		 * and count2.
+		 */
+		if (status & (1 << 5))
+			offset -= LATCH;
+	} else if (count2 > count1) {
+		/*
+		 * We have just had another interrupt between reading
+		 * count1 and count2.
+		 */
+		offset -= LATCH;
+	}
+
+	offset = (LATCH - offset) * (tick_nsec / 1000);
+	return (offset + LATCH/2) / LATCH;
+}
+
+void __init ioctime_init(void)
+{
+	ioc_writeb(LATCH & 255, IOC_T0LTCHL);
+	ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
+	ioc_writeb(0, IOC_T0GO);
+}
+
+static irqreturn_t
+ioc_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	write_seqlock(&xtime_lock);
+	timer_tick(regs);
+	write_sequnlock(&xtime_lock);
+	return IRQ_HANDLED;
+}
+
+static struct irqaction ioc_timer_irq = {
+	.name		= "timer",
+	.flags		= SA_INTERRUPT,
+	.handler	= ioc_timer_interrupt
+};
+
+/*
+ * Set up timer interrupt.
+ */
+static void __init ioc_timer_init(void)
+{
+	ioctime_init();
+	setup_irq(IRQ_TIMER, &ioc_timer_irq);
+}
+
+struct sys_timer ioc_timer = {
+	.init		= ioc_timer_init,
+	.offset		= ioc_timer_gettimeoffset,
+};
+
diff --git a/arch/arm/common/via82c505.c b/arch/arm/common/via82c505.c
new file mode 100644
index 0000000..ef716a5
--- /dev/null
+++ b/arch/arm/common/via82c505.c
@@ -0,0 +1,94 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <asm/mach/pci.h>
+
+#define MAX_SLOTS		7
+
+#define CONFIG_CMD(bus, devfn, where)   (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
+
+static int
+via82c505_read_config(struct pci_bus *bus, unsigned int devfn, int where,
+		      int size, u32 *value)
+{
+	outl(CONFIG_CMD(bus,devfn,where),0xCF8);
+	switch (size) {
+	case 1:
+		*value=inb(0xCFC + (where&3));
+		break;
+	case 2:
+		*value=inw(0xCFC + (where&2));
+		break;
+	case 4:
+		*value=inl(0xCFC);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+via82c505_write_config(struct pci_bus *bus, unsigned int devfn, int where,
+		       int size, u32 value)
+{
+	outl(CONFIG_CMD(bus,devfn,where),0xCF8);
+	switch (size) {
+	case 1:
+		outb(value, 0xCFC + (where&3));
+		break;
+	case 2:
+		outw(value, 0xCFC + (where&2));
+		break;
+	case 4:
+		outl(value, 0xCFC);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops via82c505_ops = {
+	.read	= via82c505_read_config,
+	.write	= via82c505_write_config,
+};
+
+void __init via82c505_preinit(void)
+{
+	printk(KERN_DEBUG "PCI: VIA 82c505\n");
+	if (!request_region(0xA8,2,"via config")) {
+		printk(KERN_WARNING"VIA 82c505: Unable to request region 0xA8\n");
+		return;
+	}
+	if (!request_region(0xCF8,8,"pci config")) {
+		printk(KERN_WARNING"VIA 82c505: Unable to request region 0xCF8\n");
+		release_region(0xA8, 2);
+		return;
+	}
+
+	/* Enable compatible Mode */
+	outb(0x96,0xA8);
+	outb(0x18,0xA9);
+	outb(0x93,0xA8);
+	outb(0xd0,0xA9);
+
+}
+
+int __init via82c505_setup(int nr, struct pci_sys_data *sys)
+{
+	return (nr == 0);
+}
+
+struct pci_bus * __init via82c505_scan_bus(int nr, struct pci_sys_data *sysdata)
+{
+	if (nr == 0)
+		return pci_scan_bus(0, &via82c505_ops, sysdata);
+
+	return NULL;
+}