summaryrefslogtreecommitdiff
path: root/arch/arm/common
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/common')
-rw-r--r--arch/arm/common/Kconfig24
-rw-r--r--arch/arm/common/Makefile15
-rw-r--r--arch/arm/common/amba.c357
-rw-r--r--arch/arm/common/dmabounce.c682
-rw-r--r--arch/arm/common/icst307.c161
-rw-r--r--arch/arm/common/icst525.c160
-rw-r--r--arch/arm/common/locomo.c1058
-rw-r--r--arch/arm/common/rtctime.c506
-rw-r--r--arch/arm/common/sa1111.c1292
-rw-r--r--arch/arm/common/scoop.c176
-rw-r--r--arch/arm/common/sharpsl_param.c60
-rw-r--r--arch/arm/common/time-acorn.c96
-rw-r--r--arch/arm/common/via82c505.c94
13 files changed, 4681 insertions, 0 deletions
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
new file mode 100644
index 00000000000..692af6b5e8f
--- /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 00000000000..11f20a43ee3
--- /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 00000000000..a0507f8c33f
--- /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 00000000000..5797b1b100a
--- /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 00000000000..bafe8b19be8
--- /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 00000000000..943ef88c037
--- /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 00000000000..41f12658c8b
--- /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 00000000000..c397e71f938
--- /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 00000000000..21fce3414ed
--- /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 00000000000..cfd0d3e550d
--- /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 00000000000..c2c557a224c
--- /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 00000000000..486add853fb
--- /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 00000000000..ef716a5b07a
--- /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;
+}