summaryrefslogtreecommitdiffstats
path: root/arch/arm/common
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/common')
-rw-r--r--arch/arm/common/Kconfig35
-rw-r--r--arch/arm/common/Makefile19
-rw-r--r--arch/arm/common/dmabounce.c539
-rw-r--r--arch/arm/common/gic.c263
-rw-r--r--arch/arm/common/icst307.c161
-rw-r--r--arch/arm/common/icst525.c160
-rw-r--r--arch/arm/common/it8152.c374
-rw-r--r--arch/arm/common/locomo.c1217
-rw-r--r--arch/arm/common/sa1111.c1305
-rw-r--r--arch/arm/common/scoop.c314
-rw-r--r--arch/arm/common/sharpsl_param.c62
-rw-r--r--arch/arm/common/sharpsl_pm.c859
-rw-r--r--arch/arm/common/time-acorn.c95
-rw-r--r--arch/arm/common/uengine.c507
-rw-r--r--arch/arm/common/via82c505.c92
-rw-r--r--arch/arm/common/vic.c98
16 files changed, 6100 insertions, 0 deletions
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig
new file mode 100644
index 0000000..86b5e69
--- /dev/null
+++ b/arch/arm/common/Kconfig
@@ -0,0 +1,35 @@
+config ARM_GIC
+ bool
+
+config ARM_VIC
+ bool
+
+config ICST525
+ bool
+
+config ICST307
+ bool
+
+config SA1111
+ bool
+ select DMABOUNCE if !ARCH_PXA
+
+config DMABOUNCE
+ bool
+ select ZONE_DMA
+
+config TIMER_ACORN
+ bool
+
+config SHARP_LOCOMO
+ bool
+
+config SHARP_PARAM
+ bool
+
+config SHARPSL_PM
+ bool
+ select APM_EMULATION
+
+config SHARP_SCOOP
+ bool
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
new file mode 100644
index 0000000..325e4b6
--- /dev/null
+++ b/arch/arm/common/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux kernel.
+#
+
+obj-$(CONFIG_ARM_GIC) += gic.o
+obj-$(CONFIG_ARM_VIC) += vic.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_SHARPSL_PM) += sharpsl_pm.o
+obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+obj-$(CONFIG_ARCH_IXP2000) += uengine.o
+obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
+obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
new file mode 100644
index 0000000..f030f07
--- /dev/null
+++ b/arch/arm/common/dmabounce.c
@@ -0,0 +1,539 @@
+/*
+ * 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, the remainder of memory is at the top and the DMA memory
+ * can be marked as ZONE_DMA. Anything beyond that such as discontiguous
+ * 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>
+#include <linux/scatterlist.h>
+
+#include <asm/cacheflush.h>
+
+#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 dmabounce_pool *pool;
+ void *safe;
+ dma_addr_t safe_dma_addr;
+};
+
+struct dmabounce_pool {
+ unsigned long size;
+ struct dma_pool *pool;
+#ifdef STATS
+ unsigned long allocs;
+#endif
+};
+
+struct dmabounce_device_info {
+ struct device *dev;
+ struct list_head safe_buffers;
+#ifdef STATS
+ unsigned long total_allocs;
+ unsigned long map_op_count;
+ unsigned long bounce_count;
+ int attr_res;
+#endif
+ struct dmabounce_pool small;
+ struct dmabounce_pool large;
+
+ rwlock_t lock;
+};
+
+#ifdef STATS
+static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
+ return sprintf(buf, "%lu %lu %lu %lu %lu %lu\n",
+ device_info->small.allocs,
+ device_info->large.allocs,
+ device_info->total_allocs - device_info->small.allocs -
+ device_info->large.allocs,
+ device_info->total_allocs,
+ device_info->map_op_count,
+ device_info->bounce_count);
+}
+
+static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
+#endif
+
+
+/* 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 dmabounce_pool *pool;
+ struct device *dev = device_info->dev;
+ unsigned long flags;
+
+ dev_dbg(dev, "%s(ptr=%p, size=%d, dir=%d)\n",
+ __func__, ptr, size, dir);
+
+ if (size <= device_info->small.size) {
+ pool = &device_info->small;
+ } else if (size <= device_info->large.size) {
+ pool = &device_info->large;
+ } else {
+ pool = NULL;
+ }
+
+ buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
+ if (buf == NULL) {
+ dev_warn(dev, "%s: kmalloc failed\n", __func__);
+ return NULL;
+ }
+
+ buf->ptr = ptr;
+ buf->size = size;
+ buf->direction = dir;
+ buf->pool = pool;
+
+ if (pool) {
+ buf->safe = dma_pool_alloc(pool->pool, GFP_ATOMIC,
+ &buf->safe_dma_addr);
+ } else {
+ buf->safe = dma_alloc_coherent(dev, size, &buf->safe_dma_addr,
+ GFP_ATOMIC);
+ }
+
+ if (buf->safe == NULL) {
+ dev_warn(dev,
+ "%s: could not alloc dma memory (size=%d)\n",
+ __func__, size);
+ kfree(buf);
+ return NULL;
+ }
+
+#ifdef STATS
+ if (pool)
+ pool->allocs++;
+ device_info->total_allocs++;
+#endif
+
+ write_lock_irqsave(&device_info->lock, flags);
+ list_add(&buf->node, &device_info->safe_buffers);
+ write_unlock_irqrestore(&device_info->lock, flags);
+
+ 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 safe_buffer *b, *rb = NULL;
+ unsigned long flags;
+
+ read_lock_irqsave(&device_info->lock, flags);
+
+ list_for_each_entry(b, &device_info->safe_buffers, node)
+ if (b->safe_dma_addr == safe_dma_addr) {
+ rb = b;
+ break;
+ }
+
+ read_unlock_irqrestore(&device_info->lock, flags);
+ return rb;
+}
+
+static inline void
+free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf)
+{
+ unsigned long flags;
+
+ dev_dbg(device_info->dev, "%s(buf=%p)\n", __func__, buf);
+
+ write_lock_irqsave(&device_info->lock, flags);
+
+ list_del(&buf->node);
+
+ write_unlock_irqrestore(&device_info->lock, flags);
+
+ if (buf->pool)
+ dma_pool_free(buf->pool->pool, buf->safe, buf->safe_dma_addr);
+ else
+ dma_free_coherent(device_info->dev, buf->size, buf->safe,
+ buf->safe_dma_addr);
+
+ kfree(buf);
+}
+
+/* ************************************************** */
+
+static struct safe_buffer *find_safe_buffer_dev(struct device *dev,
+ dma_addr_t dma_addr, const char *where)
+{
+ if (!dev || !dev->archdata.dmabounce)
+ return NULL;
+ if (dma_mapping_error(dev, dma_addr)) {
+ if (dev)
+ dev_err(dev, "Trying to %s invalid mapping\n", where);
+ else
+ pr_err("unknown device: Trying to %s invalid mapping\n", where);
+ return NULL;
+ }
+ return find_safe_buffer(dev->archdata.dmabounce, dma_addr);
+}
+
+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 = dev->archdata.dmabounce;
+ 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 (dma=%#x) mapped to %p (dma=%#x)\n",
+ __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
+ buf->safe, 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);
+ }
+ ptr = buf->safe;
+
+ dma_addr = buf->safe_dma_addr;
+ } else {
+ /*
+ * We don't need to sync the DMA buffer since
+ * it was allocated via the coherent allocators.
+ */
+ dma_cache_maint(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 safe_buffer *buf = find_safe_buffer_dev(dev, dma_addr, "unmap");
+
+ if (buf) {
+ BUG_ON(buf->size != size);
+ BUG_ON(buf->direction != dir);
+
+ dev_dbg(dev,
+ "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
+ __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
+ buf->safe, buf->safe_dma_addr);
+
+ DO_STATS(dev->archdata.dmabounce->bounce_count++);
+
+ if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
+ void *ptr = buf->ptr;
+
+ dev_dbg(dev,
+ "%s: copy back safe %p to unsafe %p size %d\n",
+ __func__, buf->safe, ptr, size);
+ memcpy(ptr, buf->safe, size);
+
+ /*
+ * DMA buffers must have the same cache properties
+ * as if they were really used for DMA - which means
+ * data must be written back to RAM. Note that
+ * we don't use dmac_flush_range() here for the
+ * bidirectional case because we know the cache
+ * lines will be coherent with the data written.
+ */
+ dmac_clean_range(ptr, ptr + size);
+ outer_clean_range(__pa(ptr), __pa(ptr) + size);
+ }
+ free_safe_buffer(dev->archdata.dmabounce, buf);
+ }
+}
+
+/* ************************************************** */
+
+/*
+ * 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)
+{
+ dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
+ __func__, ptr, size, dir);
+
+ BUG_ON(!valid_dma_direction(dir));
+
+ return map_single(dev, ptr, size, dir);
+}
+EXPORT_SYMBOL(dma_map_single);
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir)
+{
+ dev_dbg(dev, "%s(page=%p,off=%#lx,size=%zx,dir=%x)\n",
+ __func__, page, offset, size, dir);
+
+ BUG_ON(!valid_dma_direction(dir));
+
+ return map_single(dev, page_address(page) + offset, size, dir);
+}
+EXPORT_SYMBOL(dma_map_page);
+
+/*
+ * 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)
+{
+ dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
+ __func__, (void *) dma_addr, size, dir);
+
+ unmap_single(dev, dma_addr, size, dir);
+}
+EXPORT_SYMBOL(dma_unmap_single);
+
+int dmabounce_sync_for_cpu(struct device *dev, dma_addr_t addr,
+ unsigned long off, size_t sz, enum dma_data_direction dir)
+{
+ struct safe_buffer *buf;
+
+ dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n",
+ __func__, addr, off, sz, dir);
+
+ buf = find_safe_buffer_dev(dev, addr, __func__);
+ if (!buf)
+ return 1;
+
+ BUG_ON(buf->direction != dir);
+
+ dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
+ __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
+ buf->safe, buf->safe_dma_addr);
+
+ DO_STATS(dev->archdata.dmabounce->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 + off, buf->ptr + off, sz);
+ memcpy(buf->ptr + off, buf->safe + off, sz);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(dmabounce_sync_for_cpu);
+
+int dmabounce_sync_for_device(struct device *dev, dma_addr_t addr,
+ unsigned long off, size_t sz, enum dma_data_direction dir)
+{
+ struct safe_buffer *buf;
+
+ dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)\n",
+ __func__, addr, off, sz, dir);
+
+ buf = find_safe_buffer_dev(dev, addr, __func__);
+ if (!buf)
+ return 1;
+
+ BUG_ON(buf->direction != dir);
+
+ dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)\n",
+ __func__, buf->ptr, virt_to_dma(dev, buf->ptr),
+ buf->safe, buf->safe_dma_addr);
+
+ DO_STATS(dev->archdata.dmabounce->bounce_count++);
+
+ if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) {
+ dev_dbg(dev, "%s: copy out unsafe %p to safe %p, size %d\n",
+ __func__,buf->ptr + off, buf->safe + off, sz);
+ memcpy(buf->safe + off, buf->ptr + off, sz);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(dmabounce_sync_for_device);
+
+static int dmabounce_init_pool(struct dmabounce_pool *pool, struct device *dev,
+ const char *name, unsigned long size)
+{
+ pool->size = size;
+ DO_STATS(pool->allocs = 0);
+ pool->pool = dma_pool_create(name, dev, size,
+ 0 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+
+ return pool->pool ? 0 : -ENOMEM;
+}
+
+int dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
+ unsigned long large_buffer_size)
+{
+ struct dmabounce_device_info *device_info;
+ int ret;
+
+ device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
+ if (!device_info) {
+ dev_err(dev,
+ "Could not allocated dmabounce_device_info\n");
+ return -ENOMEM;
+ }
+
+ ret = dmabounce_init_pool(&device_info->small, dev,
+ "small_dmabounce_pool", small_buffer_size);
+ if (ret) {
+ dev_err(dev,
+ "dmabounce: could not allocate DMA pool for %ld byte objects\n",
+ small_buffer_size);
+ goto err_free;
+ }
+
+ if (large_buffer_size) {
+ ret = dmabounce_init_pool(&device_info->large, dev,
+ "large_dmabounce_pool",
+ large_buffer_size);
+ if (ret) {
+ dev_err(dev,
+ "dmabounce: could not allocate DMA pool for %ld byte objects\n",
+ large_buffer_size);
+ goto err_destroy;
+ }
+ }
+
+ device_info->dev = dev;
+ INIT_LIST_HEAD(&device_info->safe_buffers);
+ rwlock_init(&device_info->lock);
+
+#ifdef STATS
+ device_info->total_allocs = 0;
+ device_info->map_op_count = 0;
+ device_info->bounce_count = 0;
+ device_info->attr_res = device_create_file(dev, &dev_attr_dmabounce_stats);
+#endif
+
+ dev->archdata.dmabounce = device_info;
+
+ dev_info(dev, "dmabounce: registered device\n");
+
+ return 0;
+
+ err_destroy:
+ dma_pool_destroy(device_info->small.pool);
+ err_free:
+ kfree(device_info);
+ return ret;
+}
+EXPORT_SYMBOL(dmabounce_register_dev);
+
+void dmabounce_unregister_dev(struct device *dev)
+{
+ struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
+
+ dev->archdata.dmabounce = NULL;
+
+ if (!device_info) {
+ dev_warn(dev,
+ "Never registered with dmabounce but attempting"
+ "to unregister!\n");
+ return;
+ }
+
+ if (!list_empty(&device_info->safe_buffers)) {
+ dev_err(dev,
+ "Removing from dmabounce with pending buffers!\n");
+ BUG();
+ }
+
+ if (device_info->small.pool)
+ dma_pool_destroy(device_info->small.pool);
+ if (device_info->large.pool)
+ dma_pool_destroy(device_info->large.pool);
+
+#ifdef STATS
+ if (device_info->attr_res == 0)
+ device_remove_file(dev, &dev_attr_dmabounce_stats);
+#endif
+
+ kfree(device_info);
+
+ dev_info(dev, "dmabounce: device unregistered\n");
+}
+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/gic.c b/arch/arm/common/gic.c
new file mode 100644
index 0000000..7fc9860
--- /dev/null
+++ b/arch/arm/common/gic.c
@@ -0,0 +1,263 @@
+/*
+ * linux/arch/arm/common/gic.c
+ *
+ * Copyright (C) 2002 ARM Limited, 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.
+ *
+ * Interrupt architecture for the GIC:
+ *
+ * o There is one Interrupt Distributor, which receives interrupts
+ * from system devices and sends them to the Interrupt Controllers.
+ *
+ * o There is one CPU Interface per CPU, which sends interrupts sent
+ * by the Distributor, and interrupts generated locally, to the
+ * associated CPU. The base address of the CPU interface is usually
+ * aliased so that the same address points to different chips depending
+ * on the CPU it is accessed from.
+ *
+ * Note that IRQs 0-31 are special - they are local to each CPU.
+ * As such, the enable set/clear, pending set/clear and active bit
+ * registers are banked per-cpu for these sources.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/smp.h>
+#include <linux/cpumask.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/hardware/gic.h>
+
+static DEFINE_SPINLOCK(irq_controller_lock);
+
+struct gic_chip_data {
+ unsigned int irq_offset;
+ void __iomem *dist_base;
+ void __iomem *cpu_base;
+};
+
+#ifndef MAX_GIC_NR
+#define MAX_GIC_NR 1
+#endif
+
+static struct gic_chip_data gic_data[MAX_GIC_NR];
+
+static inline void __iomem *gic_dist_base(unsigned int irq)
+{
+ struct gic_chip_data *gic_data = get_irq_chip_data(irq);
+ return gic_data->dist_base;
+}
+
+static inline void __iomem *gic_cpu_base(unsigned int irq)
+{
+ struct gic_chip_data *gic_data = get_irq_chip_data(irq);
+ return gic_data->cpu_base;
+}
+
+static inline unsigned int gic_irq(unsigned int irq)
+{
+ struct gic_chip_data *gic_data = get_irq_chip_data(irq);
+ return irq - gic_data->irq_offset;
+}
+
+/*
+ * Routines to acknowledge, disable and enable interrupts
+ *
+ * Linux assumes that when we're done with an interrupt we need to
+ * unmask it, in the same way we need to unmask an interrupt when
+ * we first enable it.
+ *
+ * The GIC has a separate notion of "end of interrupt" to re-enable
+ * an interrupt after handling, in order to support hardware
+ * prioritisation.
+ *
+ * We can make the GIC behave in the way that Linux expects by making
+ * our "acknowledge" routine disable the interrupt, then mark it as
+ * complete.
+ */
+static void gic_ack_irq(unsigned int irq)
+{
+ u32 mask = 1 << (irq % 32);
+
+ spin_lock(&irq_controller_lock);
+ writel(mask, gic_dist_base(irq) + GIC_DIST_ENABLE_CLEAR + (gic_irq(irq) / 32) * 4);
+ writel(gic_irq(irq), gic_cpu_base(irq) + GIC_CPU_EOI);
+ spin_unlock(&irq_controller_lock);
+}
+
+static void gic_mask_irq(unsigned int irq)
+{
+ u32 mask = 1 << (irq % 32);
+
+ spin_lock(&irq_controller_lock);
+ writel(mask, gic_dist_base(irq) + GIC_DIST_ENABLE_CLEAR + (gic_irq(irq) / 32) * 4);
+ spin_unlock(&irq_controller_lock);
+}
+
+static void gic_unmask_irq(unsigned int irq)
+{
+ u32 mask = 1 << (irq % 32);
+
+ spin_lock(&irq_controller_lock);
+ writel(mask, gic_dist_base(irq) + GIC_DIST_ENABLE_SET + (gic_irq(irq) / 32) * 4);
+ spin_unlock(&irq_controller_lock);
+}
+
+#ifdef CONFIG_SMP
+static void gic_set_cpu(unsigned int irq, cpumask_t mask_val)
+{
+ void __iomem *reg = gic_dist_base(irq) + GIC_DIST_TARGET + (gic_irq(irq) & ~3);
+ unsigned int shift = (irq % 4) * 8;
+ unsigned int cpu = first_cpu(mask_val);
+ u32 val;
+
+ spin_lock(&irq_controller_lock);
+ irq_desc[irq].cpu = cpu;
+ val = readl(reg) & ~(0xff << shift);
+ val |= 1 << (cpu + shift);
+ writel(val, reg);
+ spin_unlock(&irq_controller_lock);
+}
+#endif
+
+static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct gic_chip_data *chip_data = get_irq_data(irq);
+ struct irq_chip *chip = get_irq_chip(irq);
+ unsigned int cascade_irq, gic_irq;
+ unsigned long status;
+
+ /* primary controller ack'ing */
+ chip->ack(irq);
+
+ spin_lock(&irq_controller_lock);
+ status = readl(chip_data->cpu_base + GIC_CPU_INTACK);
+ spin_unlock(&irq_controller_lock);
+
+ gic_irq = (status & 0x3ff);
+ if (gic_irq == 1023)
+ goto out;
+
+ cascade_irq = gic_irq + chip_data->irq_offset;
+ if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS))
+ do_bad_IRQ(cascade_irq, desc);
+ else
+ generic_handle_irq(cascade_irq);
+
+ out:
+ /* primary controller unmasking */
+ chip->unmask(irq);
+}
+
+static struct irq_chip gic_chip = {
+ .name = "GIC",
+ .ack = gic_ack_irq,
+ .mask = gic_mask_irq,
+ .unmask = gic_unmask_irq,
+#ifdef CONFIG_SMP
+ .set_affinity = gic_set_cpu,
+#endif
+};
+
+void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
+{
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+ if (set_irq_data(irq, &gic_data[gic_nr]) != 0)
+ BUG();
+ set_irq_chained_handler(irq, gic_handle_cascade_irq);
+}
+
+void __init gic_dist_init(unsigned int gic_nr, void __iomem *base,
+ unsigned int irq_start)
+{
+ unsigned int max_irq, i;
+ u32 cpumask = 1 << smp_processor_id();
+
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ cpumask |= cpumask << 8;
+ cpumask |= cpumask << 16;
+
+ gic_data[gic_nr].dist_base = base;
+ gic_data[gic_nr].irq_offset = (irq_start - 1) & ~31;
+
+ writel(0, base + GIC_DIST_CTRL);
+
+ /*
+ * Find out how many interrupts are supported.
+ */
+ max_irq = readl(base + GIC_DIST_CTR) & 0x1f;
+ max_irq = (max_irq + 1) * 32;
+
+ /*
+ * The GIC only supports up to 1020 interrupt sources.
+ * Limit this to either the architected maximum, or the
+ * platform maximum.
+ */
+ if (max_irq > max(1020, NR_IRQS))
+ max_irq = max(1020, NR_IRQS);
+
+ /*
+ * Set all global interrupts to be level triggered, active low.
+ */
+ for (i = 32; i < max_irq; i += 16)
+ writel(0, base + GIC_DIST_CONFIG + i * 4 / 16);
+
+ /*
+ * Set all global interrupts to this CPU only.
+ */
+ for (i = 32; i < max_irq; i += 4)
+ writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
+
+ /*
+ * Set priority on all interrupts.
+ */
+ for (i = 0; i < max_irq; i += 4)
+ writel(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
+
+ /*
+ * Disable all interrupts.
+ */
+ for (i = 0; i < max_irq; i += 32)
+ writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
+
+ /*
+ * Setup the Linux IRQ subsystem.
+ */
+ for (i = irq_start; i < gic_data[gic_nr].irq_offset + max_irq; i++) {
+ set_irq_chip(i, &gic_chip);
+ set_irq_chip_data(i, &gic_data[gic_nr]);
+ set_irq_handler(i, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+ }
+
+ writel(1, base + GIC_DIST_CTRL);
+}
+
+void __cpuinit gic_cpu_init(unsigned int gic_nr, void __iomem *base)
+{
+ if (gic_nr >= MAX_GIC_NR)
+ BUG();
+
+ gic_data[gic_nr].cpu_base = base;
+
+ writel(0xf0, base + GIC_CPU_PRIMASK);
+ writel(1, base + GIC_CPU_CTRL);
+}
+
+#ifdef CONFIG_SMP
+void gic_raise_softirq(cpumask_t cpumask, unsigned int irq)
+{
+ unsigned long map = *cpus_addr(cpumask);
+
+ /* this always happens on GIC0 */
+ writel(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT);
+}
+#endif
diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c
new file mode 100644
index 0000000..6d094c1
--- /dev/null
+++ b/arch/arm/common/icst307.c
@@ -0,0 +1,161 @@
+/*
+ * linux/arch/arm/common/icst307.c
+ *
+ * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Support functions for calculating clocks/divisors for the ICST307
+ * clock generators. See http://www.icst.com/ for more information
+ * on these devices.
+ *
+ * This is an almost identical implementation to the ICST525 clock generator.
+ * The s2div and idx2s files are different
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/hardware/icst307.h>
+
+/*
+ * Divisors for each OD setting.
+ */
+static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 };
+
+unsigned long icst307_khz(const struct icst307_params *p, struct icst307_vco vco)
+{
+ return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]);
+}
+
+EXPORT_SYMBOL(icst307_khz);
+
+/*
+ * Ascending divisor S values.
+ */
+static unsigned char idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 };
+
+struct icst307_vco
+icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq)
+{
+ struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+ unsigned long f;
+ unsigned int i = 0, rd, best = (unsigned int)-1;
+
+ /*
+ * First, find the PLL output divisor such
+ * that the PLL output is within spec.
+ */
+ do {
+ f = freq * s2div[idx2s[i]];
+
+ /*
+ * f must be between 6MHz and 200MHz (3.3 or 5V)
+ */
+ if (f > 6000 && f <= p->vco_max)
+ break;
+ } while (i < ARRAY_SIZE(idx2s));
+
+ if (i >= ARRAY_SIZE(idx2s))
+ return vco;
+
+ vco.s = idx2s[i];
+
+ /*
+ * Now find the closest divisor combination
+ * which gives a PLL output of 'f'.
+ */
+ for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+ unsigned long fref_div, f_pll;
+ unsigned int vd;
+ int f_diff;
+
+ fref_div = (2 * p->ref) / rd;
+
+ vd = (f + fref_div / 2) / fref_div;
+ if (vd < p->vd_min || vd > p->vd_max)
+ continue;
+
+ f_pll = fref_div * vd;
+ f_diff = f_pll - f;
+ if (f_diff < 0)
+ f_diff = -f_diff;
+
+ if ((unsigned)f_diff < best) {
+ vco.v = vd - 8;
+ vco.r = rd - 2;
+ if (f_diff == 0)
+ break;
+ best = f_diff;
+ }
+ }
+
+ return vco;
+}
+
+EXPORT_SYMBOL(icst307_khz_to_vco);
+
+struct icst307_vco
+icst307_ps_to_vco(const struct icst307_params *p, unsigned long period)
+{
+ struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+ unsigned long f, ps;
+ unsigned int i = 0, rd, best = (unsigned int)-1;
+
+ ps = 1000000000UL / p->vco_max;
+
+ /*
+ * First, find the PLL output divisor such
+ * that the PLL output is within spec.
+ */
+ do {
+ f = period / s2div[idx2s[i]];
+
+ /*
+ * f must be between 6MHz and 200MHz (3.3 or 5V)
+ */
+ if (f >= ps && f < 1000000000UL / 6000 + 1)
+ break;
+ } while (i < ARRAY_SIZE(idx2s));
+
+ if (i >= ARRAY_SIZE(idx2s))
+ return vco;
+
+ vco.s = idx2s[i];
+
+ ps = 500000000UL / p->ref;
+
+ /*
+ * Now find the closest divisor combination
+ * which gives a PLL output of 'f'.
+ */
+ for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+ unsigned long f_in_div, f_pll;
+ unsigned int vd;
+ int f_diff;
+
+ f_in_div = ps * rd;
+
+ vd = (f_in_div + f / 2) / f;
+ if (vd < p->vd_min || vd > p->vd_max)
+ continue;
+
+ f_pll = (f_in_div + vd / 2) / vd;
+ f_diff = f_pll - f;
+ if (f_diff < 0)
+ f_diff = -f_diff;
+
+ if ((unsigned)f_diff < best) {
+ vco.v = vd - 8;
+ vco.r = rd - 2;
+ if (f_diff == 0)
+ break;
+ best = f_diff;
+ }
+ }
+
+ return vco;
+}
+
+EXPORT_SYMBOL(icst307_ps_to_vco);
diff --git a/arch/arm/common/icst525.c b/arch/arm/common/icst525.c
new file mode 100644
index 0000000..3d377c5
--- /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/it8152.c b/arch/arm/common/it8152.c
new file mode 100644
index 0000000..2793447
--- /dev/null
+++ b/arch/arm/common/it8152.c
@@ -0,0 +1,374 @@
+/*
+ * linux/arch/arm/common/it8152.c
+ *
+ * Copyright Compulab Ltd, 2002-2007
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * The DMA bouncing part is taken from arch/arm/mach-ixp4xx/common-pci.c
+ * (see this file for respective copyrights)
+ *
+ * Thanks to Guennadi Liakhovetski <gl@dsa-ac.de> for IRQ enumberation
+ * and demux code.
+ *
+ * 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/sched.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/mach/pci.h>
+#include <asm/hardware/it8152.h>
+
+#define MAX_SLOTS 21
+
+static void it8152_mask_irq(unsigned int irq)
+{
+ if (irq >= IT8152_LD_IRQ(0)) {
+ __raw_writel((__raw_readl(IT8152_INTC_LDCNIMR) |
+ (1 << (irq - IT8152_LD_IRQ(0)))),
+ IT8152_INTC_LDCNIMR);
+ } else if (irq >= IT8152_LP_IRQ(0)) {
+ __raw_writel((__raw_readl(IT8152_INTC_LPCNIMR) |
+ (1 << (irq - IT8152_LP_IRQ(0)))),
+ IT8152_INTC_LPCNIMR);
+ } else if (irq >= IT8152_PD_IRQ(0)) {
+ __raw_writel((__raw_readl(IT8152_INTC_PDCNIMR) |
+ (1 << (irq - IT8152_PD_IRQ(0)))),
+ IT8152_INTC_PDCNIMR);
+ }
+}
+
+static void it8152_unmask_irq(unsigned int irq)
+{
+ if (irq >= IT8152_LD_IRQ(0)) {
+ __raw_writel((__raw_readl(IT8152_INTC_LDCNIMR) &
+ ~(1 << (irq - IT8152_LD_IRQ(0)))),
+ IT8152_INTC_LDCNIMR);
+ } else if (irq >= IT8152_LP_IRQ(0)) {
+ __raw_writel((__raw_readl(IT8152_INTC_LPCNIMR) &
+ ~(1 << (irq - IT8152_LP_IRQ(0)))),
+ IT8152_INTC_LPCNIMR);
+ } else if (irq >= IT8152_PD_IRQ(0)) {
+ __raw_writel((__raw_readl(IT8152_INTC_PDCNIMR) &
+ ~(1 << (irq - IT8152_PD_IRQ(0)))),
+ IT8152_INTC_PDCNIMR);
+ }
+}
+
+static struct irq_chip it8152_irq_chip = {
+ .name = "it8152",
+ .ack = it8152_mask_irq,
+ .mask = it8152_mask_irq,
+ .unmask = it8152_unmask_irq,
+};
+
+void it8152_init_irq(void)
+{
+ int irq;
+
+ __raw_writel((0xffff), IT8152_INTC_PDCNIMR);
+ __raw_writel((0), IT8152_INTC_PDCNIRR);
+ __raw_writel((0xffff), IT8152_INTC_LPCNIMR);
+ __raw_writel((0), IT8152_INTC_LPCNIRR);
+ __raw_writel((0xffff), IT8152_INTC_LDCNIMR);
+ __raw_writel((0), IT8152_INTC_LDCNIRR);
+
+ for (irq = IT8152_IRQ(0); irq <= IT8152_LAST_IRQ; irq++) {
+ set_irq_chip(irq, &it8152_irq_chip);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+}
+
+void it8152_irq_demux(unsigned int irq, struct irq_desc *desc)
+{
+ int bits_pd, bits_lp, bits_ld;
+ int i;
+
+ while (1) {
+ /* Read all */
+ bits_pd = __raw_readl(IT8152_INTC_PDCNIRR);
+ bits_lp = __raw_readl(IT8152_INTC_LPCNIRR);
+ bits_ld = __raw_readl(IT8152_INTC_LDCNIRR);
+
+ /* Ack */
+ __raw_writel((~bits_pd), IT8152_INTC_PDCNIRR);
+ __raw_writel((~bits_lp), IT8152_INTC_LPCNIRR);
+ __raw_writel((~bits_ld), IT8152_INTC_LDCNIRR);
+
+ if (!(bits_ld | bits_lp | bits_pd)) {
+ /* Re-read to guarantee, that there was a moment of
+ time, when they all three were 0. */
+ bits_pd = __raw_readl(IT8152_INTC_PDCNIRR);
+ bits_lp = __raw_readl(IT8152_INTC_LPCNIRR);
+ bits_ld = __raw_readl(IT8152_INTC_LDCNIRR);
+ if (!(bits_ld | bits_lp | bits_pd))
+ return;
+ }
+
+ bits_pd &= ((1 << IT8152_PD_IRQ_COUNT) - 1);
+ while (bits_pd) {
+ i = __ffs(bits_pd);
+ generic_handle_irq(IT8152_PD_IRQ(i));
+ bits_pd &= ~(1 << i);
+ }
+
+ bits_lp &= ((1 << IT8152_LP_IRQ_COUNT) - 1);
+ while (bits_lp) {
+ i = __ffs(bits_lp);
+ generic_handle_irq(IT8152_LP_IRQ(i));
+ bits_lp &= ~(1 << i);
+ }
+
+ bits_ld &= ((1 << IT8152_LD_IRQ_COUNT) - 1);
+ while (bits_ld) {
+ i = __ffs(bits_ld);
+ generic_handle_irq(IT8152_LD_IRQ(i));
+ bits_ld &= ~(1 << i);
+ }
+ }
+}
+
+/* mapping for on-chip devices */
+int __init it8152_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ if ((dev->vendor == PCI_VENDOR_ID_ITE) &&
+ (dev->device == PCI_DEVICE_ID_ITE_8152)) {
+ if ((dev->class >> 8) == PCI_CLASS_MULTIMEDIA_AUDIO)
+ return IT8152_AUDIO_INT;
+ if ((dev->class >> 8) == PCI_CLASS_SERIAL_USB)
+ return IT8152_USB_INT;
+ if ((dev->class >> 8) == PCI_CLASS_SYSTEM_DMA)
+ return IT8152_CDMA_INT;
+ }
+
+ return 0;
+}
+
+static unsigned long it8152_pci_dev_base_address(struct pci_bus *bus,
+ unsigned int devfn)
+{
+ unsigned long addr = 0;
+
+ if (bus->number == 0) {
+ if (devfn < PCI_DEVFN(MAX_SLOTS, 0))
+ addr = (devfn << 8);
+ } else
+ addr = (bus->number << 16) | (devfn << 8);
+
+ return addr;
+}
+
+static int it8152_pci_read_config(struct pci_bus *bus,
+ unsigned int devfn, int where,
+ int size, u32 *value)
+{
+ unsigned long addr = it8152_pci_dev_base_address(bus, devfn);
+ u32 v;
+ int shift;
+
+ shift = (where & 3);
+
+ __raw_writel((addr + where), IT8152_PCI_CFG_ADDR);
+ v = (__raw_readl(IT8152_PCI_CFG_DATA) >> (8 * (shift)));
+
+ *value = v;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int it8152_pci_write_config(struct pci_bus *bus,
+ unsigned int devfn, int where,
+ int size, u32 value)
+{
+ unsigned long addr = it8152_pci_dev_base_address(bus, devfn);
+ u32 v, vtemp, mask = 0;
+ int shift;
+
+ if (size == 1)
+ mask = 0xff;
+ if (size == 2)
+ mask = 0xffff;
+
+ shift = (where & 3);
+
+ __raw_writel((addr + where), IT8152_PCI_CFG_ADDR);
+ vtemp = __raw_readl(IT8152_PCI_CFG_DATA);
+
+ if (mask)
+ vtemp &= ~(mask << (8 * shift));
+ else
+ vtemp = 0;
+
+ v = (value << (8 * shift));
+ __raw_writel((addr + where), IT8152_PCI_CFG_ADDR);
+ __raw_writel((v | vtemp), IT8152_PCI_CFG_DATA);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops it8152_ops = {
+ .read = it8152_pci_read_config,
+ .write = it8152_pci_write_config,
+};
+
+static struct resource it8152_io = {
+ .name = "IT8152 PCI I/O region",
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource it8152_mem = {
+ .name = "IT8152 PCI memory region",
+ .start = 0x10000000,
+ .end = 0x13e00000,
+ .flags = IORESOURCE_MEM,
+};
+
+/*
+ * The following functions are needed for DMA bouncing.
+ * ITE8152 chip can addrees up to 64MByte, so all the devices
+ * connected to ITE8152 (PCI and USB) should have limited DMA window
+ */
+
+/*
+ * Setup DMA mask to 64MB on devices connected to ITE8152. Ignore all
+ * other devices.
+ */
+static int it8152_pci_platform_notify(struct device *dev)
+{
+ if (dev->bus == &pci_bus_type) {
+ if (dev->dma_mask)
+ *dev->dma_mask = (SZ_64M - 1) | PHYS_OFFSET;
+ dev->coherent_dma_mask = (SZ_64M - 1) | PHYS_OFFSET;
+ dmabounce_register_dev(dev, 2048, 4096);
+ }
+ return 0;
+}
+
+static int it8152_pci_platform_notify_remove(struct device *dev)
+{
+ if (dev->bus == &pci_bus_type)
+ dmabounce_unregister_dev(dev);
+
+ return 0;
+}
+
+int dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
+{
+ dev_dbg(dev, "%s: dma_addr %08x, size %08x\n",
+ __func__, dma_addr, size);
+ return (dev->bus == &pci_bus_type) &&
+ ((dma_addr + size - PHYS_OFFSET) >= SZ_64M);
+}
+
+/*
+ * We override these so we properly do dmabounce otherwise drivers
+ * are able to set the dma_mask to 0xffffffff and we can no longer
+ * trap bounces. :(
+ *
+ * We just return true on everyhing except for < 64MB in which case
+ * we will fail miseralby and die since we can't handle that case.
+ */
+int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{
+ dev_dbg(&dev->dev, "%s: %llx\n", __func__, mask);
+ if (mask >= PHYS_OFFSET + SZ_64M - 1)
+ return 0;
+
+ return -EIO;
+}
+
+int
+pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+ dev_dbg(&dev->dev, "%s: %llx\n", __func__, mask);
+ if (mask >= PHYS_OFFSET + SZ_64M - 1)
+ return 0;
+
+ return -EIO;
+}
+
+int __init it8152_pci_setup(int nr, struct pci_sys_data *sys)
+{
+ it8152_io.start = IT8152_IO_BASE + 0x12000;
+ it8152_io.end = IT8152_IO_BASE + 0x12000 + 0x100000;
+
+ sys->mem_offset = 0x10000000;
+ sys->io_offset = IT8152_IO_BASE;
+
+ if (request_resource(&ioport_resource, &it8152_io)) {
+ printk(KERN_ERR "PCI: unable to allocate IO region\n");
+ goto err0;
+ }
+ if (request_resource(&iomem_resource, &it8152_mem)) {
+ printk(KERN_ERR "PCI: unable to allocate memory region\n");
+ goto err1;
+ }
+
+ sys->resource[0] = &it8152_io;
+ sys->resource[1] = &it8152_mem;
+
+ if (platform_notify || platform_notify_remove) {
+ printk(KERN_ERR "PCI: Can't use platform_notify\n");
+ goto err2;
+ }
+
+ platform_notify = it8152_pci_platform_notify;
+ platform_notify_remove = it8152_pci_platform_notify_remove;
+
+ return 1;
+
+err2:
+ release_resource(&it8152_io);
+err1:
+ release_resource(&it8152_mem);
+err0:
+ return -EBUSY;
+}
+
+/*
+ * If we set up a device for bus mastering, we need to check the latency
+ * timer as we don't have even crappy BIOSes to set it properly.
+ * The implementation is from arch/i386/pci/i386.c
+ */
+unsigned int pcibios_max_latency = 255;
+
+void pcibios_set_master(struct pci_dev *dev)
+{
+ u8 lat;
+
+ /* no need to update on-chip OHCI controller */
+ if ((dev->vendor == PCI_VENDOR_ID_ITE) &&
+ (dev->device == PCI_DEVICE_ID_ITE_8152) &&
+ ((dev->class >> 8) == PCI_CLASS_SERIAL_USB))
+ return;
+
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ if (lat < 16)
+ lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
+ else if (lat > pcibios_max_latency)
+ lat = pcibios_max_latency;
+ else
+ return;
+ printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n",
+ pci_name(dev), lat);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
+
+
+struct pci_bus * __init it8152_pci_scan_bus(int nr, struct pci_sys_data *sys)
+{
+ return pci_scan_bus(nr, &it8152_ops, sys);
+}
+
diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c
new file mode 100644
index 0000000..7c6b4b9
--- /dev/null
+++ b/arch/arm/common/locomo.c
@@ -0,0 +1,1217 @@
+/*
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+
+#include <mach/hardware.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 __iomem *base;
+#ifdef CONFIG_PM
+ void *saved_state;
+#endif
+};
+
+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,
+ },
+ {
+ .devid = LOCOMO_DEVID_SPI,
+ .irq = {},
+ .name = "locomo-spi",
+ .offset = LOCOMO_SPI,
+ .length = 0x30,
+ },
+};
+
+
+/** 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 irq_desc *desc)
+{
+ int req, i;
+ void __iomem *mapbase = get_irq_chip_data(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;
+ for (i = 0; i <= 3; i++, irq++) {
+ if (req & (0x0100 << i)) {
+ generic_handle_irq(irq);
+ }
+
+ }
+ }
+}
+
+static void locomo_ack_irq(unsigned int irq)
+{
+}
+
+static void locomo_mask_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_ICR);
+ r |= (0x0010 << (irq - LOCOMO_IRQ_START));
+ locomo_writel(r, mapbase + LOCOMO_ICR);
+}
+
+static struct irq_chip locomo_chip = {
+ .name = "LOCOMO",
+ .ack = locomo_ack_irq,
+ .mask = locomo_mask_irq,
+ .unmask = locomo_unmask_irq,
+};
+
+static void locomo_key_handler(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *mapbase = get_irq_chip_data(irq);
+
+ if (locomo_readl(mapbase + LOCOMO_KEYBOARD + LOCOMO_KIC) & 0x0001) {
+ generic_handle_irq(LOCOMO_IRQ_KEY_START);
+ }
+}
+
+static void locomo_key_ack_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(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 irq_chip locomo_key_chip = {
+ .name = "LOCOMO-key",
+ .ack = locomo_key_ack_irq,
+ .mask = locomo_key_mask_irq,
+ .unmask = locomo_key_unmask_irq,
+};
+
+static void locomo_gpio_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int req, i;
+ void __iomem *mapbase = get_irq_chip_data(irq);
+
+ req = locomo_readl(mapbase + LOCOMO_GIR) &
+ locomo_readl(mapbase + LOCOMO_GPD) &
+ 0xffff;
+
+ if (req) {
+ irq = LOCOMO_IRQ_GPIO_START;
+ for (i = 0; i <= 15; i++, irq++) {
+ if (req & (0x0001 << i)) {
+ generic_handle_irq(irq);
+ }
+ }
+ }
+}
+
+static void locomo_gpio_ack_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_GIE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_GPIO_START));
+ locomo_writel(r, mapbase + LOCOMO_GIE);
+}
+
+static int GPIO_IRQ_rising_edge;
+static int GPIO_IRQ_falling_edge;
+
+static int locomo_gpio_type(unsigned int irq, unsigned int type)
+{
+ unsigned int mask;
+ void __iomem *mapbase = get_irq_chip_data(irq);
+
+ mask = 1 << (irq - LOCOMO_IRQ_GPIO_START);
+
+ if (type == IRQ_TYPE_PROBE) {
+ if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask)
+ return 0;
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ }
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ GPIO_IRQ_rising_edge |= mask;
+ else
+ GPIO_IRQ_rising_edge &= ~mask;
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ GPIO_IRQ_falling_edge |= mask;
+ else
+ GPIO_IRQ_falling_edge &= ~mask;
+ locomo_writel(GPIO_IRQ_rising_edge, mapbase + LOCOMO_GRIE);
+ locomo_writel(GPIO_IRQ_falling_edge, mapbase + LOCOMO_GFIE);
+
+ return 0;
+}
+
+static struct irq_chip locomo_gpio_chip = {
+ .name = "LOCOMO-gpio",
+ .ack = locomo_gpio_ack_irq,
+ .mask = locomo_gpio_mask_irq,
+ .unmask = locomo_gpio_unmask_irq,
+ .set_type = locomo_gpio_type,
+};
+
+static void locomo_lt_handler(unsigned int irq, struct irq_desc *desc)
+{
+ void __iomem *mapbase = get_irq_chip_data(irq);
+
+ if (locomo_readl(mapbase + LOCOMO_LTINT) & 0x0001) {
+ generic_handle_irq(LOCOMO_IRQ_LT_START);
+ }
+}
+
+static void locomo_lt_ack_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(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 __iomem *mapbase = get_irq_chip_data(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 irq_chip locomo_lt_chip = {
+ .name = "LOCOMO-lt",
+ .ack = locomo_lt_ack_irq,
+ .mask = locomo_lt_mask_irq,
+ .unmask = locomo_lt_unmask_irq,
+};
+
+static void locomo_spi_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int req, i;
+ void __iomem *mapbase = get_irq_chip_data(irq);
+
+ req = locomo_readl(mapbase + LOCOMO_SPI + LOCOMO_SPIIR) & 0x000F;
+ if (req) {
+ irq = LOCOMO_IRQ_SPI_START;
+
+ for (i = 0; i <= 3; i++, irq++) {
+ if (req & (0x0001 << i)) {
+ generic_handle_irq(irq);
+ }
+ }
+ }
+}
+
+static void locomo_spi_ack_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_SPI + LOCOMO_SPIWE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPI + LOCOMO_SPIWE);
+
+ r = locomo_readl(mapbase + LOCOMO_SPI + LOCOMO_SPIIS);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPI + LOCOMO_SPIIS);
+
+ r = locomo_readl(mapbase + LOCOMO_SPI + LOCOMO_SPIWE);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPI + LOCOMO_SPIWE);
+}
+
+static void locomo_spi_mask_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_SPI + LOCOMO_SPIIE);
+ r &= ~(0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPI + LOCOMO_SPIIE);
+}
+
+static void locomo_spi_unmask_irq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(irq);
+ unsigned int r;
+ r = locomo_readl(mapbase + LOCOMO_SPI + LOCOMO_SPIIE);
+ r |= (0x0001 << (irq - LOCOMO_IRQ_SPI_START));
+ locomo_writel(r, mapbase + LOCOMO_SPI + LOCOMO_SPIIE);
+}
+
+static struct irq_chip locomo_spi_chip = {
+ .name = "LOCOMO-spi",
+ .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 __iomem *irqbase = lchip->base;
+
+ /*
+ * Install handler for IRQ_LOCOMO_HW.
+ */
+ set_irq_type(lchip->irq, IRQ_TYPE_EDGE_FALLING);
+ set_irq_chip_data(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_chip_data(IRQ_LOCOMO_KEY_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_KEY_BASE, locomo_key_handler);
+
+ set_irq_chip(IRQ_LOCOMO_GPIO_BASE, &locomo_chip);
+ set_irq_chip_data(IRQ_LOCOMO_GPIO_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_GPIO_BASE, locomo_gpio_handler);
+
+ set_irq_chip(IRQ_LOCOMO_LT_BASE, &locomo_chip);
+ set_irq_chip_data(IRQ_LOCOMO_LT_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_LT_BASE, locomo_lt_handler);
+
+ set_irq_chip(IRQ_LOCOMO_SPI_BASE, &locomo_chip);
+ set_irq_chip_data(IRQ_LOCOMO_SPI_BASE, irqbase);
+ set_irq_chained_handler(IRQ_LOCOMO_SPI_BASE, locomo_spi_handler);
+
+ /* install handlers for IRQ_LOCOMO_KEY_BASE generated interrupts */
+ set_irq_chip(LOCOMO_IRQ_KEY_START, &locomo_key_chip);
+ set_irq_chip_data(LOCOMO_IRQ_KEY_START, irqbase);
+ set_irq_handler(LOCOMO_IRQ_KEY_START, handle_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_chip_data(irq, irqbase);
+ set_irq_handler(irq, handle_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_chip_data(LOCOMO_IRQ_LT_START, irqbase);
+ set_irq_handler(LOCOMO_IRQ_LT_START, handle_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 + 4; irq++) {
+ set_irq_chip(irq, &locomo_spi_chip);
+ set_irq_chip_data(irq, irqbase);
+ set_irq_handler(irq, handle_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 = kzalloc(sizeof(struct locomo_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * 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_set_name(&dev->dev, "%s", info->name);
+ 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;
+}
+
+#ifdef CONFIG_PM
+
+struct locomo_save_data {
+ u16 LCM_GPO;
+ u16 LCM_SPICT;
+ u16 LCM_GPE;
+ u16 LCM_ASD;
+ u16 LCM_SPIMD;
+};
+
+static int locomo_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct locomo *lchip = platform_get_drvdata(dev);
+ struct locomo_save_data *save;
+ unsigned long flags;
+
+ save = kmalloc(sizeof(struct locomo_save_data), GFP_KERNEL);
+ if (!save)
+ return -ENOMEM;
+
+ lchip->saved_state = save;
+
+ spin_lock_irqsave(&lchip->lock, flags);
+
+ save->LCM_GPO = locomo_readl(lchip->base + LOCOMO_GPO); /* GPIO */
+ locomo_writel(0x00, lchip->base + LOCOMO_GPO);
+ save->LCM_SPICT = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPICT); /* SPI */
+ locomo_writel(0x40, lchip->base + LOCOMO_SPICT);
+ save->LCM_GPE = locomo_readl(lchip->base + LOCOMO_GPE); /* GPIO */
+ locomo_writel(0x00, lchip->base + LOCOMO_GPE);
+ save->LCM_ASD = locomo_readl(lchip->base + LOCOMO_ASD); /* ADSTART */
+ locomo_writel(0x00, lchip->base + LOCOMO_ASD);
+ save->LCM_SPIMD = locomo_readl(lchip->base + LOCOMO_SPI + LOCOMO_SPIMD); /* SPI */
+ locomo_writel(0x3C14, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
+
+ locomo_writel(0x00, lchip->base + LOCOMO_PAIF);
+ locomo_writel(0x00, lchip->base + LOCOMO_DAC);
+ locomo_writel(0x00, lchip->base + LOCOMO_BACKLIGHT + LOCOMO_TC);
+
+ if ((locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT0) & 0x88) && (locomo_readl(lchip->base + LOCOMO_LED + LOCOMO_LPT1) & 0x88))
+ locomo_writel(0x00, lchip->base + LOCOMO_C32K); /* CLK32 off */
+ else
+ /* 18MHz already enabled, so no wait */
+ locomo_writel(0xc1, lchip->base + LOCOMO_C32K); /* CLK32 on */
+
+ locomo_writel(0x00, lchip->base + LOCOMO_TADC); /* 18MHz clock off*/
+ locomo_writel(0x00, lchip->base + LOCOMO_AUDIO + LOCOMO_ACC); /* 22MHz/24MHz clock off */
+ locomo_writel(0x00, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS); /* FL */
+
+ spin_unlock_irqrestore(&lchip->lock, flags);
+
+ return 0;
+}
+
+static int locomo_resume(struct platform_device *dev)
+{
+ struct locomo *lchip = platform_get_drvdata(dev);
+ struct locomo_save_data *save;
+ unsigned long r;
+ unsigned long flags;
+
+ save = lchip->saved_state;
+ if (!save)
+ return 0;
+
+ spin_lock_irqsave(&lchip->lock, flags);
+
+ locomo_writel(save->LCM_GPO, lchip->base + LOCOMO_GPO);
+ locomo_writel(save->LCM_SPICT, lchip->base + LOCOMO_SPI + LOCOMO_SPICT);
+ locomo_writel(save->LCM_GPE, lchip->base + LOCOMO_GPE);
+ locomo_writel(save->LCM_ASD, lchip->base + LOCOMO_ASD);
+ locomo_writel(save->LCM_SPIMD, lchip->base + LOCOMO_SPI + LOCOMO_SPIMD);
+
+ locomo_writel(0x00, lchip->base + LOCOMO_C32K);
+ locomo_writel(0x90, lchip->base + LOCOMO_TADC);
+
+ locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KSC);
+ r = locomo_readl(lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
+ r &= 0xFEFF;
+ locomo_writel(r, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC);
+ locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD);
+
+ spin_unlock_irqrestore(&lchip->lock, flags);
+
+ lchip->saved_state = NULL;
+ kfree(save);
+
+ return 0;
+}
+#endif
+
+
+/**
+ * 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 = kzalloc(sizeof(struct locomo), GFP_KERNEL);
+ if (!lchip)
+ return -ENOMEM;
+
+ 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(1) | LOCOMO_GPIO(2) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14))
+ , lchip->base + LOCOMO_GPE);
+ locomo_writel((LOCOMO_GPIO(1) | LOCOMO_GPIO(2) | 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 int locomo_remove_child(struct device *dev, void *data)
+{
+ device_unregister(dev);
+ return 0;
+}
+
+static void __locomo_remove(struct locomo *lchip)
+{
+ device_for_each_child(lchip->dev, NULL, locomo_remove_child);
+
+ 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 platform_device *dev)
+{
+ struct resource *mem;
+ int irq;
+
+ mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ return __locomo_probe(&dev->dev, mem, irq);
+}
+
+static int locomo_remove(struct platform_device *dev)
+{
+ struct locomo *lchip = platform_get_drvdata(dev);
+
+ if (lchip) {
+ __locomo_remove(lchip);
+ platform_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 platform_driver locomo_device_driver = {
+ .probe = locomo_probe,
+ .remove = locomo_remove,
+#ifdef CONFIG_PM
+ .suspend = locomo_suspend,
+ .resume = locomo_resume,
+#endif
+ .driver = {
+ .name = "locomo",
+ },
+};
+
+/*
+ * 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 device *dev, unsigned int bits, unsigned int dir)
+{
+ struct locomo *lchip = dev_get_drvdata(dev);
+ unsigned long flags;
+ unsigned int r;
+
+ if (!lchip)
+ return;
+
+ spin_lock_irqsave(&lchip->lock, flags);
+
+ r = locomo_readl(lchip->base + LOCOMO_GPD);
+ if (dir)
+ r |= bits;
+ else
+ 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);
+}
+
+int locomo_gpio_read_level(struct device *dev, unsigned int bits)
+{
+ struct locomo *lchip = dev_get_drvdata(dev);
+ unsigned long flags;
+ unsigned int ret;
+
+ if (!lchip)
+ return -ENODEV;
+
+ spin_lock_irqsave(&lchip->lock, flags);
+ ret = locomo_readl(lchip->base + LOCOMO_GPL);
+ spin_unlock_irqrestore(&lchip->lock, flags);
+
+ ret &= bits;
+ return ret;
+}
+
+int locomo_gpio_read_output(struct device *dev, unsigned int bits)
+{
+ struct locomo *lchip = dev_get_drvdata(dev);
+ unsigned long flags;
+ unsigned int ret;
+
+ if (!lchip)
+ return -ENODEV;
+
+ 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 device *dev, unsigned int bits, unsigned int set)
+{
+ struct locomo *lchip = dev_get_drvdata(dev);
+ unsigned long flags;
+ unsigned int r;
+
+ if (!lchip)
+ return;
+
+ 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);
+}
+
+/*
+ * Frontlight control
+ */
+
+static struct locomo *locomo_chip_driver(struct locomo_dev *ldev);
+
+void locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf)
+{
+ unsigned long flags;
+ struct locomo *lchip = locomo_chip_driver(dev);
+
+ if (vr)
+ locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 1);
+ else
+ locomo_gpio_write(dev->dev.parent, LOCOMO_GPIO_FL_VR, 0);
+
+ spin_lock_irqsave(&lchip->lock, flags);
+ locomo_writel(bpwf, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
+ udelay(100);
+ locomo_writel(duty, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALD);
+ locomo_writel(bpwf | LOCOMO_ALC_EN, lchip->base + LOCOMO_FRONTLIGHT + LOCOMO_ALS);
+ 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,
+ .probe = locomo_bus_probe,
+ .remove = locomo_bus_remove,
+ .suspend = locomo_bus_suspend,
+ .resume = locomo_bus_resume,
+};
+
+int locomo_driver_register(struct locomo_driver *driver)
+{
+ 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)
+ platform_driver_register(&locomo_device_driver);
+ return ret;
+}
+
+static void __exit locomo_exit(void)
+{
+ platform_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/sa1111.c b/arch/arm/common/sa1111.c
new file mode 100644
index 0000000..ef12794
--- /dev/null
+++ b/arch/arm/common/sa1111.c
@@ -0,0 +1,1305 @@
+/*
+ * linux/arch/arm/common/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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <asm/sizes.h>
+
+#include <asm/hardware/sa1111.h>
+
+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;
+ struct clk *clk;
+ unsigned long phys;
+ int irq;
+ spinlock_t lock;
+ void __iomem *base;
+#ifdef CONFIG_PM
+ void *saved_state;
+#endif
+};
+
+/*
+ * 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,
+ },
+ },
+};
+
+void __init sa1111_adjust_zones(int node, unsigned long *size, unsigned long *holes)
+{
+ unsigned int sz = SZ_1M >> PAGE_SHIFT;
+
+ if (node != 0)
+ sz = 0;
+
+ size[1] = size[0] - sz;
+ size[0] = sz;
+}
+
+/*
+ * 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 irq_desc *desc)
+{
+ unsigned int stat0, stat1, i;
+ void __iomem *base = get_irq_data(irq);
+
+ 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);
+ return;
+ }
+
+ for (i = IRQ_SA1111_START; stat0; i++, stat0 >>= 1)
+ if (stat0 & 1)
+ handle_edge_irq(i, irq_desc + i);
+
+ for (i = IRQ_SA1111_START + 32; stat1; i++, stat1 >>= 1)
+ if (stat1 & 1)
+ handle_edge_irq(i, irq_desc + i);
+
+ /* 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_chip_data(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_chip_data(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_chip_data(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_chip_data(irq);
+ unsigned long ip0;
+
+ if (flags == IRQ_TYPE_PROBE)
+ return 0;
+
+ if ((!(flags & IRQ_TYPE_EDGE_RISING) ^ !(flags & IRQ_TYPE_EDGE_FALLING)) == 0)
+ return -EINVAL;
+
+ ip0 = sa1111_readl(mapbase + SA1111_INTPOL0);
+ if (flags & IRQ_TYPE_EDGE_RISING)
+ 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_chip_data(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 irq_chip sa1111_low_chip = {
+ .name = "SA1111-l",
+ .ack = sa1111_ack_irq,
+ .mask = sa1111_mask_lowirq,
+ .unmask = sa1111_unmask_lowirq,
+ .retrigger = sa1111_retrigger_lowirq,
+ .set_type = sa1111_type_lowirq,
+ .set_wake = sa1111_wake_lowirq,
+};
+
+static void sa1111_mask_highirq(unsigned int irq)
+{
+ void __iomem *mapbase = get_irq_chip_data(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_chip_data(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_chip_data(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_chip_data(irq);
+ unsigned long ip1;
+
+ if (flags == IRQ_TYPE_PROBE)
+ return 0;
+
+ if ((!(flags & IRQ_TYPE_EDGE_RISING) ^ !(flags & IRQ_TYPE_EDGE_FALLING)) == 0)
+ return -EINVAL;
+
+ ip1 = sa1111_readl(mapbase + SA1111_INTPOL1);
+ if (flags & IRQ_TYPE_EDGE_RISING)
+ 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_chip_data(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 irq_chip sa1111_high_chip = {
+ .name = "SA1111-h",
+ .ack = sa1111_ack_irq,
+ .mask = sa1111_mask_highirq,
+ .unmask = sa1111_unmask_highirq,
+ .retrigger = sa1111_retrigger_highirq,
+ .set_type = sa1111_type_highirq,
+ .set_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_chip_data(irq, irqbase);
+ set_irq_handler(irq, handle_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_chip_data(irq, irqbase);
+ set_irq_handler(irq, handle_edge_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ /*
+ * Register SA1111 interrupt
+ */
+ set_irq_type(sachip->irq, IRQ_TYPE_EDGE_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);
+
+ clk_enable(sachip->clk);
+
+ /*
+ * 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 = kzalloc(sizeof(struct sa1111_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dev_set_name(&dev->dev, "%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_name(&dev->dev);
+ 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);
+ dev_set_name(&dev->dev, NULL);
+ kfree(dev);
+ goto out;
+ }
+
+
+ ret = device_register(&dev->dev);
+ if (ret) {
+ release_resource(&dev->res);
+ kfree(dev);
+ goto out;
+ }
+
+#ifdef CONFIG_DMABOUNCE
+ /*
+ * 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) {
+ dev_err(&dev->dev, "SA1111: Failed to register"
+ " with dmabounce\n");
+ device_unregister(&dev->dev);
+ }
+ }
+ }
+#endif
+
+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;
+ int i, ret = -ENODEV;
+
+ sachip = kzalloc(sizeof(struct sa1111), GFP_KERNEL);
+ if (!sachip)
+ return -ENOMEM;
+
+ sachip->clk = clk_get(me, "SA1111_CLK");
+ if (IS_ERR(sachip->clk)) {
+ ret = PTR_ERR(sachip->clk);
+ goto err_free;
+ }
+
+ 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 err_clkput;
+ }
+
+ /*
+ * 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 err_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
+ {
+ unsigned int val;
+
+ /*
+ * 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;
+
+ err_unmap:
+ iounmap(sachip->base);
+ err_clkput:
+ clk_put(sachip->clk);
+ err_free:
+ kfree(sachip);
+ return ret;
+}
+
+static int sa1111_remove_one(struct device *dev, void *data)
+{
+ device_unregister(dev);
+ return 0;
+}
+
+static void __sa1111_remove(struct sa1111 *sachip)
+{
+ void __iomem *irqbase = sachip->base + SA1111_INTC;
+
+ device_for_each_child(sachip->dev, NULL, sa1111_remove_one);
+
+ /* 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);
+
+ clk_disable(sachip->clk);
+
+ 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);
+ clk_put(sachip->clk);
+ 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 platform_device *dev, pm_message_t state)
+{
+ struct sa1111 *sachip = platform_get_drvdata(dev);
+ struct sa1111_save_data *save;
+ unsigned long flags;
+ unsigned int val;
+ void __iomem *base;
+
+ save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL);
+ if (!save)
+ return -ENOMEM;
+ sachip->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);
+
+ clk_disable(sachip->clk);
+
+ spin_unlock_irqrestore(&sachip->lock, flags);
+
+ return 0;
+}
+
+/*
+ * sa1111_resume - Restore the SA1111 device state.
+ * @dev: device to restore
+ *
+ * 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 platform_device *dev)
+{
+ struct sa1111 *sachip = platform_get_drvdata(dev);
+ struct sa1111_save_data *save;
+ unsigned long flags, id;
+ void __iomem *base;
+
+ save = sachip->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);
+ platform_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);
+
+ sachip->saved_state = NULL;
+ kfree(save);
+
+ return 0;
+}
+
+#else
+#define sa1111_suspend NULL
+#define sa1111_resume NULL
+#endif
+
+static int __devinit sa1111_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ int irq;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENXIO;
+
+ return __sa1111_probe(&pdev->dev, mem, irq);
+}
+
+static int sa1111_remove(struct platform_device *pdev)
+{
+ struct sa1111 *sachip = platform_get_drvdata(pdev);
+
+ if (sachip) {
+ __sa1111_remove(sachip);
+ platform_set_drvdata(pdev, NULL);
+
+#ifdef CONFIG_PM
+ kfree(sachip->saved_state);
+ sachip->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 platform_driver sa1111_device_driver = {
+ .probe = sa1111_probe,
+ .remove = sa1111_remove,
+ .suspend = sa1111_suspend,
+ .resume = sa1111_resume,
+ .driver = {
+ .name = "sa1111",
+ },
+};
+
+/*
+ * 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,
+ .probe = sa1111_bus_probe,
+ .remove = sa1111_bus_remove,
+ .suspend = sa1111_bus_suspend,
+ .resume = sa1111_bus_resume,
+};
+
+int sa1111_driver_register(struct sa1111_driver *driver)
+{
+ 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)
+ platform_driver_register(&sa1111_device_driver);
+ return ret;
+}
+
+static void __exit sa1111_exit(void)
+{
+ platform_driver_unregister(&sa1111_device_driver);
+ bus_unregister(&sa1111_bus_type);
+}
+
+subsys_initcall(sa1111_init);
+module_exit(sa1111_exit);
+
+MODULE_DESCRIPTION("Intel Corporation SA1111 core driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sa1111_select_audio_mode);
+EXPORT_SYMBOL(sa1111_set_audio_rate);
+EXPORT_SYMBOL(sa1111_get_audio_rate);
+EXPORT_SYMBOL(sa1111_set_io_dir);
+EXPORT_SYMBOL(sa1111_set_io);
+EXPORT_SYMBOL(sa1111_set_sleep_io);
+EXPORT_SYMBOL(sa1111_enable_device);
+EXPORT_SYMBOL(sa1111_disable_device);
+EXPORT_SYMBOL(sa1111_pll_clock);
+EXPORT_SYMBOL(sa1111_bus_type);
+EXPORT_SYMBOL(sa1111_driver_register);
+EXPORT_SYMBOL(sa1111_driver_unregister);
diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c
new file mode 100644
index 0000000..697c649
--- /dev/null
+++ b/arch/arm/common/scoop.c
@@ -0,0 +1,314 @@
+/*
+ * 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 <linux/string.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <asm/gpio.h>
+#include <asm/hardware/scoop.h>
+
+/* PCMCIA to Scoop linkage
+
+ There is no easy way to link multiple scoop devices into one
+ single entity for the pxa2xx_pcmcia device so this structure
+ is used which is setup by the platform code.
+
+ This file is never modular so this symbol is always
+ accessile to the board support files.
+*/
+struct scoop_pcmcia_config *platform_scoop_config;
+EXPORT_SYMBOL(platform_scoop_config);
+
+struct scoop_dev {
+ void __iomem *base;
+ struct gpio_chip gpio;
+ spinlock_t scoop_lock;
+ unsigned short suspend_clr;
+ unsigned short suspend_set;
+ u32 scoop_gpwr;
+};
+
+void reset_scoop(struct device *dev)
+{
+ struct scoop_dev *sdev = dev_get_drvdata(dev);
+
+ iowrite16(0x0100, sdev->base + SCOOP_MCR); // 00
+ iowrite16(0x0000, sdev->base + SCOOP_CDR); // 04
+ iowrite16(0x0000, sdev->base + SCOOP_CCR); // 10
+ iowrite16(0x0000, sdev->base + SCOOP_IMR); // 18
+ iowrite16(0x00FF, sdev->base + SCOOP_IRM); // 14
+ iowrite16(0x0000, sdev->base + SCOOP_ISR); // 1C
+ iowrite16(0x0000, sdev->base + SCOOP_IRM);
+}
+
+static void __scoop_gpio_set(struct scoop_dev *sdev,
+ unsigned offset, int value)
+{
+ unsigned short gpwr;
+
+ gpwr = ioread16(sdev->base + SCOOP_GPWR);
+ if (value)
+ gpwr |= 1 << (offset + 1);
+ else
+ gpwr &= ~(1 << (offset + 1));
+ iowrite16(gpwr, sdev->base + SCOOP_GPWR);
+}
+
+static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdev->scoop_lock, flags);
+
+ __scoop_gpio_set(sdev, offset, value);
+
+ spin_unlock_irqrestore(&sdev->scoop_lock, flags);
+}
+
+static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
+
+ /* XXX: I'm usure, but it seems so */
+ return ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1));
+}
+
+static int scoop_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
+ unsigned long flags;
+ unsigned short gpcr;
+
+ spin_lock_irqsave(&sdev->scoop_lock, flags);
+
+ gpcr = ioread16(sdev->base + SCOOP_GPCR);
+ gpcr &= ~(1 << (offset + 1));
+ iowrite16(gpcr, sdev->base + SCOOP_GPCR);
+
+ spin_unlock_irqrestore(&sdev->scoop_lock, flags);
+
+ return 0;
+}
+
+static int scoop_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
+ unsigned long flags;
+ unsigned short gpcr;
+
+ spin_lock_irqsave(&sdev->scoop_lock, flags);
+
+ __scoop_gpio_set(sdev, offset, value);
+
+ gpcr = ioread16(sdev->base + SCOOP_GPCR);
+ gpcr |= 1 << (offset + 1);
+ iowrite16(gpcr, sdev->base + SCOOP_GPCR);
+
+ spin_unlock_irqrestore(&sdev->scoop_lock, flags);
+
+ return 0;
+}
+
+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 = ioread16(sdev->base + SCOOP_GPWR) | bit;
+ iowrite16(gpio_bit, sdev->base + SCOOP_GPWR);
+ 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 = ioread16(sdev->base + SCOOP_GPWR) & ~bit;
+ iowrite16(gpio_bit, sdev->base + SCOOP_GPWR);
+ 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 ioread16(sdev->base + reg);
+}
+
+void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
+{
+ struct scoop_dev *sdev = dev_get_drvdata(dev);
+ iowrite16(data, sdev->base + reg);
+}
+
+EXPORT_SYMBOL(reset_scoop);
+EXPORT_SYMBOL(read_scoop_reg);
+EXPORT_SYMBOL(write_scoop_reg);
+
+static void check_scoop_reg(struct scoop_dev *sdev)
+{
+ unsigned short mcr;
+
+ mcr = ioread16(sdev->base + SCOOP_MCR);
+ if ((mcr & 0x100) == 0)
+ iowrite16(0x0101, sdev->base + SCOOP_MCR);
+}
+
+#ifdef CONFIG_PM
+static int scoop_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct scoop_dev *sdev = platform_get_drvdata(dev);
+
+ check_scoop_reg(sdev);
+ sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
+ iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
+
+ return 0;
+}
+
+static int scoop_resume(struct platform_device *dev)
+{
+ struct scoop_dev *sdev = platform_get_drvdata(dev);
+
+ check_scoop_reg(sdev);
+ iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
+
+ return 0;
+}
+#else
+#define scoop_suspend NULL
+#define scoop_resume NULL
+#endif
+
+static int __devinit scoop_probe(struct platform_device *pdev)
+{
+ struct scoop_dev *devptr;
+ struct scoop_config *inf;
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret;
+ int temp;
+
+ if (!mem)
+ return -EINVAL;
+
+ devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
+ if (!devptr)
+ return -ENOMEM;
+
+ spin_lock_init(&devptr->scoop_lock);
+
+ inf = pdev->dev.platform_data;
+ devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
+
+ if (!devptr->base) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ platform_set_drvdata(pdev, devptr);
+
+ printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
+
+ iowrite16(0x0140, devptr->base + SCOOP_MCR);
+ reset_scoop(&pdev->dev);
+ iowrite16(0x0000, devptr->base + SCOOP_CPR);
+ iowrite16(inf->io_dir & 0xffff, devptr->base + SCOOP_GPCR);
+ iowrite16(inf->io_out & 0xffff, devptr->base + SCOOP_GPWR);
+
+ devptr->suspend_clr = inf->suspend_clr;
+ devptr->suspend_set = inf->suspend_set;
+
+ devptr->gpio.base = -1;
+
+ if (inf->gpio_base != 0) {
+ devptr->gpio.label = dev_name(&pdev->dev);
+ devptr->gpio.base = inf->gpio_base;
+ devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */
+ devptr->gpio.set = scoop_gpio_set;
+ devptr->gpio.get = scoop_gpio_get;
+ devptr->gpio.direction_input = scoop_gpio_direction_input;
+ devptr->gpio.direction_output = scoop_gpio_direction_output;
+
+ ret = gpiochip_add(&devptr->gpio);
+ if (ret)
+ goto err_gpio;
+ }
+
+ return 0;
+
+ if (devptr->gpio.base != -1)
+ temp = gpiochip_remove(&devptr->gpio);
+err_gpio:
+ platform_set_drvdata(pdev, NULL);
+err_ioremap:
+ iounmap(devptr->base);
+ kfree(devptr);
+
+ return ret;
+}
+
+static int __devexit scoop_remove(struct platform_device *pdev)
+{
+ struct scoop_dev *sdev = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!sdev)
+ return -EINVAL;
+
+ if (sdev->gpio.base != -1) {
+ ret = gpiochip_remove(&sdev->gpio);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't remove gpio chip: %d\n", ret);
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ iounmap(sdev->base);
+ kfree(sdev);
+
+ return 0;
+}
+
+static struct platform_driver scoop_driver = {
+ .probe = scoop_probe,
+ .remove = __devexit_p(scoop_remove),
+ .suspend = scoop_suspend,
+ .resume = scoop_resume,
+ .driver = {
+ .name = "sharp-scoop",
+ },
+};
+
+static int __init scoop_init(void)
+{
+ return platform_driver_register(&scoop_driver);
+}
+
+subsys_initcall(scoop_init);
diff --git a/arch/arm/common/sharpsl_param.c b/arch/arm/common/sharpsl_param.c
new file mode 100644
index 0000000..d56c932
--- /dev/null
+++ b/arch/arm/common/sharpsl_param.c
@@ -0,0 +1,62 @@
+/*
+ * 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/module.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 parameters but the format is common to all.
+ */
+#ifdef CONFIG_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;
+EXPORT_SYMBOL(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/sharpsl_pm.c b/arch/arm/common/sharpsl_pm.c
new file mode 100644
index 0000000..780bbf7
--- /dev/null
+++ b/arch/arm/common/sharpsl_pm.c
@@ -0,0 +1,859 @@
+/*
+ * Battery and Power Management code for the Sharp SL-C7xx and SL-Cxx00
+ * series of PDAs
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on code written by Sharp 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.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/apm_bios.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/apm-emulation.h>
+#include <linux/suspend.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <mach/pm.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-regs.h>
+#include <mach/sharpsl.h>
+#include <asm/hardware/sharpsl_pm.h>
+
+/*
+ * Constants
+ */
+#define SHARPSL_CHARGE_ON_TIME_INTERVAL (msecs_to_jiffies(1*60*1000)) /* 1 min */
+#define SHARPSL_CHARGE_FINISH_TIME (msecs_to_jiffies(10*60*1000)) /* 10 min */
+#define SHARPSL_BATCHK_TIME (msecs_to_jiffies(15*1000)) /* 15 sec */
+#define SHARPSL_BATCHK_TIME_SUSPEND (60*10) /* 10 min */
+
+#define SHARPSL_WAIT_CO_TIME 15 /* 15 sec */
+#define SHARPSL_WAIT_DISCHARGE_ON 100 /* 100 msec */
+#define SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP 10 /* 10 msec */
+#define SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT 10 /* 10 msec */
+#define SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN 10 /* 10 msec */
+#define SHARPSL_CHARGE_WAIT_TIME 15 /* 15 msec */
+#define SHARPSL_CHARGE_CO_CHECK_TIME 5 /* 5 msec */
+#define SHARPSL_CHARGE_RETRY_CNT 1 /* eqv. 10 min */
+
+/*
+ * Prototypes
+ */
+#ifdef CONFIG_PM
+static int sharpsl_off_charge_battery(void);
+static int sharpsl_check_battery_voltage(void);
+static int sharpsl_fatal_check(void);
+#endif
+static int sharpsl_check_battery_temp(void);
+static int sharpsl_ac_check(void);
+static int sharpsl_average_value(int ad);
+static void sharpsl_average_clear(void);
+static void sharpsl_charge_toggle(struct work_struct *private_);
+static void sharpsl_battery_thread(struct work_struct *private_);
+
+
+/*
+ * Variables
+ */
+struct sharpsl_pm_status sharpsl_pm;
+DECLARE_DELAYED_WORK(toggle_charger, sharpsl_charge_toggle);
+DECLARE_DELAYED_WORK(sharpsl_bat, sharpsl_battery_thread);
+DEFINE_LED_TRIGGER(sharpsl_charge_led_trigger);
+
+
+static int get_percentage(int voltage)
+{
+ int i = sharpsl_pm.machinfo->bat_levels - 1;
+ int bl_status = sharpsl_pm.machinfo->backlight_get_status ? sharpsl_pm.machinfo->backlight_get_status() : 0;
+ struct battery_thresh *thresh;
+
+ if (sharpsl_pm.charge_mode == CHRG_ON)
+ thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_acin_bl : sharpsl_pm.machinfo->bat_levels_acin;
+ else
+ thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_noac_bl : sharpsl_pm.machinfo->bat_levels_noac;
+
+ while (i > 0 && (voltage > thresh[i].voltage))
+ i--;
+
+ return thresh[i].percentage;
+}
+
+static int get_apm_status(int voltage)
+{
+ int low_thresh, high_thresh;
+
+ if (sharpsl_pm.charge_mode == CHRG_ON) {
+ high_thresh = sharpsl_pm.machinfo->status_high_acin;
+ low_thresh = sharpsl_pm.machinfo->status_low_acin;
+ } else {
+ high_thresh = sharpsl_pm.machinfo->status_high_noac;
+ low_thresh = sharpsl_pm.machinfo->status_low_noac;
+ }
+
+ if (voltage >= high_thresh)
+ return APM_BATTERY_STATUS_HIGH;
+ if (voltage >= low_thresh)
+ return APM_BATTERY_STATUS_LOW;
+ return APM_BATTERY_STATUS_CRITICAL;
+}
+
+void sharpsl_battery_kick(void)
+{
+ schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125));
+}
+EXPORT_SYMBOL(sharpsl_battery_kick);
+
+
+static void sharpsl_battery_thread(struct work_struct *private_)
+{
+ int voltage, percent, apm_status, i = 0;
+
+ if (!sharpsl_pm.machinfo)
+ return;
+
+ sharpsl_pm.battstat.ac_status = (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN) ? APM_AC_ONLINE : APM_AC_OFFLINE);
+
+ /* Corgi cannot confirm when battery fully charged so periodically kick! */
+ if (!sharpsl_pm.machinfo->batfull_irq && (sharpsl_pm.charge_mode == CHRG_ON)
+ && time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_ON_TIME_INTERVAL))
+ schedule_delayed_work(&toggle_charger, 0);
+
+ while(1) {
+ voltage = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT);
+
+ if (voltage > 0) break;
+ if (i++ > 5) {
+ voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage;
+ dev_warn(sharpsl_pm.dev, "Warning: Cannot read main battery!\n");
+ break;
+ }
+ }
+
+ voltage = sharpsl_average_value(voltage);
+ apm_status = get_apm_status(voltage);
+ percent = get_percentage(voltage);
+
+ /* At low battery voltages, the voltage has a tendency to start
+ creeping back up so we try to avoid this here */
+ if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE) || (apm_status == APM_BATTERY_STATUS_HIGH) || percent <= sharpsl_pm.battstat.mainbat_percent) {
+ sharpsl_pm.battstat.mainbat_voltage = voltage;
+ sharpsl_pm.battstat.mainbat_status = apm_status;
+ sharpsl_pm.battstat.mainbat_percent = percent;
+ }
+
+ dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %ld\n", voltage,
+ sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies);
+
+#ifdef CONFIG_BACKLIGHT_CORGI
+ /* If battery is low. limit backlight intensity to save power. */
+ if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
+ && ((sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_LOW) ||
+ (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL))) {
+ if (!(sharpsl_pm.flags & SHARPSL_BL_LIMIT)) {
+ sharpsl_pm.machinfo->backlight_limit(1);
+ sharpsl_pm.flags |= SHARPSL_BL_LIMIT;
+ }
+ } else if (sharpsl_pm.flags & SHARPSL_BL_LIMIT) {
+ sharpsl_pm.machinfo->backlight_limit(0);
+ sharpsl_pm.flags &= ~SHARPSL_BL_LIMIT;
+ }
+#endif
+
+ /* Suspend if critical battery level */
+ if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
+ && (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL)
+ && !(sharpsl_pm.flags & SHARPSL_APM_QUEUED)) {
+ sharpsl_pm.flags |= SHARPSL_APM_QUEUED;
+ dev_err(sharpsl_pm.dev, "Fatal Off\n");
+ apm_queue_event(APM_CRITICAL_SUSPEND);
+ }
+
+ schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME);
+}
+
+void sharpsl_pm_led(int val)
+{
+ if (val == SHARPSL_LED_ERROR) {
+ dev_err(sharpsl_pm.dev, "Charging Error!\n");
+ } else if (val == SHARPSL_LED_ON) {
+ dev_dbg(sharpsl_pm.dev, "Charge LED On\n");
+ led_trigger_event(sharpsl_charge_led_trigger, LED_FULL);
+ } else {
+ dev_dbg(sharpsl_pm.dev, "Charge LED Off\n");
+ led_trigger_event(sharpsl_charge_led_trigger, LED_OFF);
+ }
+}
+
+static void sharpsl_charge_on(void)
+{
+ dev_dbg(sharpsl_pm.dev, "Turning Charger On\n");
+
+ sharpsl_pm.full_count = 0;
+ sharpsl_pm.charge_mode = CHRG_ON;
+ schedule_delayed_work(&toggle_charger, msecs_to_jiffies(250));
+ schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(500));
+}
+
+static void sharpsl_charge_off(void)
+{
+ dev_dbg(sharpsl_pm.dev, "Turning Charger Off\n");
+
+ sharpsl_pm.machinfo->charge(0);
+ sharpsl_pm_led(SHARPSL_LED_OFF);
+ sharpsl_pm.charge_mode = CHRG_OFF;
+
+ schedule_delayed_work(&sharpsl_bat, 0);
+}
+
+static void sharpsl_charge_error(void)
+{
+ sharpsl_pm_led(SHARPSL_LED_ERROR);
+ sharpsl_pm.machinfo->charge(0);
+ sharpsl_pm.charge_mode = CHRG_ERROR;
+}
+
+static void sharpsl_charge_toggle(struct work_struct *private_)
+{
+ dev_dbg(sharpsl_pm.dev, "Toogling Charger at time: %lx\n", jiffies);
+
+ if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
+ sharpsl_charge_off();
+ return;
+ } else if ((sharpsl_check_battery_temp() < 0) || (sharpsl_ac_check() < 0)) {
+ sharpsl_charge_error();
+ return;
+ }
+
+ sharpsl_pm_led(SHARPSL_LED_ON);
+ sharpsl_pm.machinfo->charge(0);
+ mdelay(SHARPSL_CHARGE_WAIT_TIME);
+ sharpsl_pm.machinfo->charge(1);
+
+ sharpsl_pm.charge_start_time = jiffies;
+}
+
+static void sharpsl_ac_timer(unsigned long data)
+{
+ int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN);
+
+ dev_dbg(sharpsl_pm.dev, "AC Status: %d\n",acin);
+
+ sharpsl_average_clear();
+ if (acin && (sharpsl_pm.charge_mode != CHRG_ON))
+ sharpsl_charge_on();
+ else if (sharpsl_pm.charge_mode == CHRG_ON)
+ sharpsl_charge_off();
+
+ schedule_delayed_work(&sharpsl_bat, 0);
+}
+
+
+irqreturn_t sharpsl_ac_isr(int irq, void *dev_id)
+{
+ /* Delay the event slightly to debounce */
+ /* Must be a smaller delay than the chrg_full_isr below */
+ mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+static void sharpsl_chrg_full_timer(unsigned long data)
+{
+ dev_dbg(sharpsl_pm.dev, "Charge Full at time: %lx\n", jiffies);
+
+ sharpsl_pm.full_count++;
+
+ if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
+ dev_dbg(sharpsl_pm.dev, "Charge Full: AC removed - stop charging!\n");
+ if (sharpsl_pm.charge_mode == CHRG_ON)
+ sharpsl_charge_off();
+ } else if (sharpsl_pm.full_count < 2) {
+ dev_dbg(sharpsl_pm.dev, "Charge Full: Count too low\n");
+ schedule_delayed_work(&toggle_charger, 0);
+ } else if (time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_FINISH_TIME)) {
+ dev_dbg(sharpsl_pm.dev, "Charge Full: Interrupt generated too slowly - retry.\n");
+ schedule_delayed_work(&toggle_charger, 0);
+ } else {
+ sharpsl_charge_off();
+ sharpsl_pm.charge_mode = CHRG_DONE;
+ dev_dbg(sharpsl_pm.dev, "Charge Full: Charging Finished\n");
+ }
+}
+
+/* Charging Finished Interrupt (Not present on Corgi) */
+/* Can trigger at the same time as an AC status change so
+ delay until after that has been processed */
+irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id)
+{
+ if (sharpsl_pm.flags & SHARPSL_SUSPENDED)
+ return IRQ_HANDLED;
+
+ /* delay until after any ac interrupt */
+ mod_timer(&sharpsl_pm.chrg_full_timer, jiffies + msecs_to_jiffies(500));
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id)
+{
+ int is_fatal = 0;
+
+ if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) {
+ dev_err(sharpsl_pm.dev, "Battery now Unlocked! Suspending.\n");
+ is_fatal = 1;
+ }
+
+ if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL)) {
+ dev_err(sharpsl_pm.dev, "Fatal Batt Error! Suspending.\n");
+ is_fatal = 1;
+ }
+
+ if (!(sharpsl_pm.flags & SHARPSL_APM_QUEUED) && is_fatal) {
+ sharpsl_pm.flags |= SHARPSL_APM_QUEUED;
+ apm_queue_event(APM_CRITICAL_SUSPEND);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Maintain an average of the last 10 readings
+ */
+#define SHARPSL_CNV_VALUE_NUM 10
+static int sharpsl_ad_index;
+
+static void sharpsl_average_clear(void)
+{
+ sharpsl_ad_index = 0;
+}
+
+static int sharpsl_average_value(int ad)
+{
+ int i, ad_val = 0;
+ static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1];
+
+ if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) {
+ sharpsl_ad_index = 0;
+ return ad;
+ }
+
+ sharpsl_ad[sharpsl_ad_index] = ad;
+ sharpsl_ad_index++;
+ if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) {
+ for (i=0; i < (SHARPSL_CNV_VALUE_NUM-1); i++)
+ sharpsl_ad[i] = sharpsl_ad[i+1];
+ sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM - 1;
+ }
+ for (i=0; i < sharpsl_ad_index; i++)
+ ad_val += sharpsl_ad[i];
+
+ return (ad_val / sharpsl_ad_index);
+}
+
+/*
+ * Take an array of 5 integers, remove the maximum and minimum values
+ * and return the average.
+ */
+static int get_select_val(int *val)
+{
+ int i, j, k, temp, sum = 0;
+
+ /* Find MAX val */
+ temp = val[0];
+ j=0;
+ for (i=1; i<5; i++) {
+ if (temp < val[i]) {
+ temp = val[i];
+ j = i;
+ }
+ }
+
+ /* Find MIN val */
+ temp = val[4];
+ k=4;
+ for (i=3; i>=0; i--) {
+ if (temp > val[i]) {
+ temp = val[i];
+ k = i;
+ }
+ }
+
+ for (i=0; i<5; i++)
+ if (i != j && i != k )
+ sum += val[i];
+
+ dev_dbg(sharpsl_pm.dev, "Average: %d from values: %d, %d, %d, %d, %d\n", sum/3, val[0], val[1], val[2], val[3], val[4]);
+
+ return (sum/3);
+}
+
+static int sharpsl_check_battery_temp(void)
+{
+ int val, i, buff[5];
+
+ /* Check battery temperature */
+ for (i=0; i<5; i++) {
+ mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
+ sharpsl_pm.machinfo->measure_temp(1);
+ mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
+ buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_TEMP);
+ sharpsl_pm.machinfo->measure_temp(0);
+ }
+
+ val = get_select_val(buff);
+
+ dev_dbg(sharpsl_pm.dev, "Temperature: %d\n", val);
+ if (val > sharpsl_pm.machinfo->charge_on_temp) {
+ printk(KERN_WARNING "Not charging: temperature out of limits.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sharpsl_check_battery_voltage(void)
+{
+ int val, i, buff[5];
+
+ /* disable charge, enable discharge */
+ sharpsl_pm.machinfo->charge(0);
+ sharpsl_pm.machinfo->discharge(1);
+ mdelay(SHARPSL_WAIT_DISCHARGE_ON);
+
+ if (sharpsl_pm.machinfo->discharge1)
+ sharpsl_pm.machinfo->discharge1(1);
+
+ /* Check battery voltage */
+ for (i=0; i<5; i++) {
+ buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT);
+ mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
+ }
+
+ if (sharpsl_pm.machinfo->discharge1)
+ sharpsl_pm.machinfo->discharge1(0);
+
+ sharpsl_pm.machinfo->discharge(0);
+
+ val = get_select_val(buff);
+ dev_dbg(sharpsl_pm.dev, "Battery Voltage: %d\n", val);
+
+ if (val < sharpsl_pm.machinfo->charge_on_volt)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static int sharpsl_ac_check(void)
+{
+ int temp, i, buff[5];
+
+ for (i=0; i<5; i++) {
+ buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_ACIN_VOLT);
+ mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN);
+ }
+
+ temp = get_select_val(buff);
+ dev_dbg(sharpsl_pm.dev, "AC Voltage: %d\n",temp);
+
+ if ((temp > sharpsl_pm.machinfo->charge_acin_high) || (temp < sharpsl_pm.machinfo->charge_acin_low)) {
+ dev_err(sharpsl_pm.dev, "Error: AC check failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sharpsl_pm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ sharpsl_pm.flags |= SHARPSL_SUSPENDED;
+ flush_scheduled_work();
+
+ if (sharpsl_pm.charge_mode == CHRG_ON)
+ sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
+ else
+ sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG;
+
+ return 0;
+}
+
+static int sharpsl_pm_resume(struct platform_device *pdev)
+{
+ /* Clear the reset source indicators as they break the bootloader upon reboot */
+ RCSR = 0x0f;
+ sharpsl_average_clear();
+ sharpsl_pm.flags &= ~SHARPSL_APM_QUEUED;
+ sharpsl_pm.flags &= ~SHARPSL_SUSPENDED;
+
+ return 0;
+}
+
+static void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
+{
+ dev_dbg(sharpsl_pm.dev, "Time is: %08x\n",RCNR);
+
+ dev_dbg(sharpsl_pm.dev, "Offline Charge Activate = %d\n",sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG);
+ /* not charging and AC-IN! */
+
+ if ((sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG) && (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN))) {
+ dev_dbg(sharpsl_pm.dev, "Activating Offline Charger...\n");
+ sharpsl_pm.charge_mode = CHRG_OFF;
+ sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG;
+ sharpsl_off_charge_battery();
+ }
+
+ sharpsl_pm.machinfo->presuspend();
+
+ PEDR = 0xffffffff; /* clear it */
+
+ sharpsl_pm.flags &= ~SHARPSL_ALARM_ACTIVE;
+ if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (SHARPSL_BATCHK_TIME_SUSPEND + 30))) || !alarm_enable)) {
+ RTSR &= RTSR_ALE;
+ RTAR = RCNR + SHARPSL_BATCHK_TIME_SUSPEND;
+ dev_dbg(sharpsl_pm.dev, "Charging alarm at: %08x\n",RTAR);
+ sharpsl_pm.flags |= SHARPSL_ALARM_ACTIVE;
+ } else if (alarm_enable) {
+ RTSR &= RTSR_ALE;
+ RTAR = alarm_time;
+ dev_dbg(sharpsl_pm.dev, "User alarm at: %08x\n",RTAR);
+ } else {
+ dev_dbg(sharpsl_pm.dev, "No alarms set.\n");
+ }
+
+ pxa_pm_enter(state);
+
+ sharpsl_pm.machinfo->postsuspend();
+
+ dev_dbg(sharpsl_pm.dev, "Corgi woken up from suspend: %08x\n",PEDR);
+}
+
+static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
+{
+ if (!sharpsl_pm.machinfo->should_wakeup(!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE) && alarm_enable) )
+ {
+ if (!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE)) {
+ dev_dbg(sharpsl_pm.dev, "No user triggered wakeup events and not charging. Strange. Suspend.\n");
+ corgi_goto_sleep(alarm_time, alarm_enable, state);
+ return 1;
+ }
+ if(sharpsl_off_charge_battery()) {
+ dev_dbg(sharpsl_pm.dev, "Charging. Suspend...\n");
+ corgi_goto_sleep(alarm_time, alarm_enable, state);
+ return 1;
+ }
+ dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n");
+ }
+
+ if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) || (sharpsl_fatal_check() < 0) )
+ {
+ dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n");
+ corgi_goto_sleep(alarm_time, alarm_enable, state);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int corgi_pxa_pm_enter(suspend_state_t state)
+{
+ unsigned long alarm_time = RTAR;
+ unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0);
+
+ dev_dbg(sharpsl_pm.dev, "SharpSL suspending for first time.\n");
+
+ corgi_goto_sleep(alarm_time, alarm_status, state);
+
+ while (corgi_enter_suspend(alarm_time,alarm_status,state))
+ {}
+
+ if (sharpsl_pm.machinfo->earlyresume)
+ sharpsl_pm.machinfo->earlyresume();
+
+ dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n");
+
+ return 0;
+}
+
+/*
+ * Check for fatal battery errors
+ * Fatal returns -1
+ */
+static int sharpsl_fatal_check(void)
+{
+ int buff[5], temp, i, acin;
+
+ dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check entered\n");
+
+ /* Check AC-Adapter */
+ acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN);
+
+ if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) {
+ sharpsl_pm.machinfo->charge(0);
+ udelay(100);
+ sharpsl_pm.machinfo->discharge(1); /* enable discharge */
+ mdelay(SHARPSL_WAIT_DISCHARGE_ON);
+ }
+
+ if (sharpsl_pm.machinfo->discharge1)
+ sharpsl_pm.machinfo->discharge1(1);
+
+ /* Check battery : check inserting battery ? */
+ for (i=0; i<5; i++) {
+ buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT);
+ mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
+ }
+
+ if (sharpsl_pm.machinfo->discharge1)
+ sharpsl_pm.machinfo->discharge1(0);
+
+ if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) {
+ udelay(100);
+ sharpsl_pm.machinfo->charge(1);
+ sharpsl_pm.machinfo->discharge(0);
+ }
+
+ temp = get_select_val(buff);
+ dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check: acin: %d, discharge voltage: %d, no discharge: %ld\n", acin, temp, sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT));
+
+ if ((acin && (temp < sharpsl_pm.machinfo->fatal_acin_volt)) ||
+ (!acin && (temp < sharpsl_pm.machinfo->fatal_noacin_volt)))
+ return -1;
+ return 0;
+}
+
+static int sharpsl_off_charge_error(void)
+{
+ dev_err(sharpsl_pm.dev, "Offline Charger: Error occurred.\n");
+ sharpsl_pm.machinfo->charge(0);
+ sharpsl_pm_led(SHARPSL_LED_ERROR);
+ sharpsl_pm.charge_mode = CHRG_ERROR;
+ return 1;
+}
+
+/*
+ * Charging Control while suspended
+ * Return 1 - go straight to sleep
+ * Return 0 - sleep or wakeup depending on other factors
+ */
+static int sharpsl_off_charge_battery(void)
+{
+ int time;
+
+ dev_dbg(sharpsl_pm.dev, "Charge Mode: %d\n", sharpsl_pm.charge_mode);
+
+ if (sharpsl_pm.charge_mode == CHRG_OFF) {
+ dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 1\n");
+
+ /* AC Check */
+ if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery_temp() < 0))
+ return sharpsl_off_charge_error();
+
+ /* Start Charging */
+ sharpsl_pm_led(SHARPSL_LED_ON);
+ sharpsl_pm.machinfo->charge(0);
+ mdelay(SHARPSL_CHARGE_WAIT_TIME);
+ sharpsl_pm.machinfo->charge(1);
+
+ sharpsl_pm.charge_mode = CHRG_ON;
+ sharpsl_pm.full_count = 0;
+
+ return 1;
+ } else if (sharpsl_pm.charge_mode != CHRG_ON) {
+ return 1;
+ }
+
+ if (sharpsl_pm.full_count == 0) {
+ int time;
+
+ dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 2\n");
+
+ if ((sharpsl_check_battery_temp() < 0) || (sharpsl_check_battery_voltage() < 0))
+ return sharpsl_off_charge_error();
+
+ sharpsl_pm.machinfo->charge(0);
+ mdelay(SHARPSL_CHARGE_WAIT_TIME);
+ sharpsl_pm.machinfo->charge(1);
+ sharpsl_pm.charge_mode = CHRG_ON;
+
+ mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
+
+ time = RCNR;
+ while(1) {
+ /* Check if any wakeup event had occurred */
+ if (sharpsl_pm.machinfo->charger_wakeup() != 0)
+ return 0;
+ /* Check for timeout */
+ if ((RCNR - time) > SHARPSL_WAIT_CO_TIME)
+ return 1;
+ if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) {
+ dev_dbg(sharpsl_pm.dev, "Offline Charger: Charge full occurred. Retrying to check\n");
+ sharpsl_pm.full_count++;
+ sharpsl_pm.machinfo->charge(0);
+ mdelay(SHARPSL_CHARGE_WAIT_TIME);
+ sharpsl_pm.machinfo->charge(1);
+ return 1;
+ }
+ }
+ }
+
+ dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 3\n");
+
+ mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
+
+ time = RCNR;
+ while(1) {
+ /* Check if any wakeup event had occurred */
+ if (sharpsl_pm.machinfo->charger_wakeup() != 0)
+ return 0;
+ /* Check for timeout */
+ if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) {
+ if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) {
+ dev_dbg(sharpsl_pm.dev, "Offline Charger: Not charged sufficiently. Retrying.\n");
+ sharpsl_pm.full_count = 0;
+ }
+ sharpsl_pm.full_count++;
+ return 1;
+ }
+ if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) {
+ dev_dbg(sharpsl_pm.dev, "Offline Charger: Charging complete.\n");
+ sharpsl_pm_led(SHARPSL_LED_OFF);
+ sharpsl_pm.machinfo->charge(0);
+ sharpsl_pm.charge_mode = CHRG_DONE;
+ return 1;
+ }
+ }
+}
+#else
+#define sharpsl_pm_suspend NULL
+#define sharpsl_pm_resume NULL
+#endif
+
+static ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_percent);
+}
+
+static ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_voltage);
+}
+
+static DEVICE_ATTR(battery_percentage, 0444, battery_percentage_show, NULL);
+static DEVICE_ATTR(battery_voltage, 0444, battery_voltage_show, NULL);
+
+extern void (*apm_get_power_status)(struct apm_power_info *);
+
+static void sharpsl_apm_get_power_status(struct apm_power_info *info)
+{
+ info->ac_line_status = sharpsl_pm.battstat.ac_status;
+
+ if (sharpsl_pm.charge_mode == CHRG_ON)
+ info->battery_status = APM_BATTERY_STATUS_CHARGING;
+ else
+ info->battery_status = sharpsl_pm.battstat.mainbat_status;
+
+ info->battery_flag = (1 << info->battery_status);
+ info->battery_life = sharpsl_pm.battstat.mainbat_percent;
+}
+
+#ifdef CONFIG_PM
+static struct platform_suspend_ops sharpsl_pm_ops = {
+ .enter = corgi_pxa_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+#endif
+
+static int __init sharpsl_pm_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!pdev->dev.platform_data)
+ return -EINVAL;
+
+ sharpsl_pm.dev = &pdev->dev;
+ sharpsl_pm.machinfo = pdev->dev.platform_data;
+ sharpsl_pm.charge_mode = CHRG_OFF;
+ sharpsl_pm.flags = 0;
+
+ init_timer(&sharpsl_pm.ac_timer);
+ sharpsl_pm.ac_timer.function = sharpsl_ac_timer;
+
+ init_timer(&sharpsl_pm.chrg_full_timer);
+ sharpsl_pm.chrg_full_timer.function = sharpsl_chrg_full_timer;
+
+ led_trigger_register_simple("sharpsl-charge", &sharpsl_charge_led_trigger);
+
+ sharpsl_pm.machinfo->init();
+
+ ret = device_create_file(&pdev->dev, &dev_attr_battery_percentage);
+ ret |= device_create_file(&pdev->dev, &dev_attr_battery_voltage);
+ if (ret != 0)
+ dev_warn(&pdev->dev, "Failed to register attributes (%d)\n", ret);
+
+ apm_get_power_status = sharpsl_apm_get_power_status;
+
+#ifdef CONFIG_PM
+ suspend_set_ops(&sharpsl_pm_ops);
+#endif
+
+ mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
+
+ return 0;
+}
+
+static int sharpsl_pm_remove(struct platform_device *pdev)
+{
+ suspend_set_ops(NULL);
+
+ device_remove_file(&pdev->dev, &dev_attr_battery_percentage);
+ device_remove_file(&pdev->dev, &dev_attr_battery_voltage);
+
+ led_trigger_unregister_simple(sharpsl_charge_led_trigger);
+
+ sharpsl_pm.machinfo->exit();
+
+ del_timer_sync(&sharpsl_pm.chrg_full_timer);
+ del_timer_sync(&sharpsl_pm.ac_timer);
+
+ return 0;
+}
+
+static struct platform_driver sharpsl_pm_driver = {
+ .probe = sharpsl_pm_probe,
+ .remove = sharpsl_pm_remove,
+ .suspend = sharpsl_pm_suspend,
+ .resume = sharpsl_pm_resume,
+ .driver = {
+ .name = "sharpsl-pm",
+ },
+};
+
+static int __devinit sharpsl_pm_init(void)
+{
+ return platform_driver_register(&sharpsl_pm_driver);
+}
+
+static void sharpsl_pm_exit(void)
+{
+ platform_driver_unregister(&sharpsl_pm_driver);
+}
+
+late_initcall(sharpsl_pm_init);
+module_exit(sharpsl_pm_exit);
diff --git a/arch/arm/common/time-acorn.c b/arch/arm/common/time-acorn.c
new file mode 100644
index 0000000..deeed56
--- /dev/null
+++ b/arch/arm/common/time-acorn.c
@@ -0,0 +1,95 @@
+/*
+ * 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 <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/hardware.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)
+{
+ timer_tick();
+ return IRQ_HANDLED;
+}
+
+static struct irqaction ioc_timer_irq = {
+ .name = "timer",
+ .flags = IRQF_DISABLED,
+ .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/uengine.c b/arch/arm/common/uengine.c
new file mode 100644
index 0000000..b520e56
--- /dev/null
+++ b/arch/arm/common/uengine.c
@@ -0,0 +1,507 @@
+/*
+ * Generic library functions for the microengines found on the Intel
+ * IXP2000 series of network processors.
+ *
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/hardware/uengine.h>
+
+#if defined(CONFIG_ARCH_IXP2000)
+#define IXP_UENGINE_CSR_VIRT_BASE IXP2000_UENGINE_CSR_VIRT_BASE
+#define IXP_PRODUCT_ID IXP2000_PRODUCT_ID
+#define IXP_MISC_CONTROL IXP2000_MISC_CONTROL
+#define IXP_RESET1 IXP2000_RESET1
+#else
+#if defined(CONFIG_ARCH_IXP23XX)
+#define IXP_UENGINE_CSR_VIRT_BASE IXP23XX_UENGINE_CSR_VIRT_BASE
+#define IXP_PRODUCT_ID IXP23XX_PRODUCT_ID
+#define IXP_MISC_CONTROL IXP23XX_MISC_CONTROL
+#define IXP_RESET1 IXP23XX_RESET1
+#else
+#error unknown platform
+#endif
+#endif
+
+#define USTORE_ADDRESS 0x000
+#define USTORE_DATA_LOWER 0x004
+#define USTORE_DATA_UPPER 0x008
+#define CTX_ENABLES 0x018
+#define CC_ENABLE 0x01c
+#define CSR_CTX_POINTER 0x020
+#define INDIRECT_CTX_STS 0x040
+#define ACTIVE_CTX_STS 0x044
+#define INDIRECT_CTX_SIG_EVENTS 0x048
+#define INDIRECT_CTX_WAKEUP_EVENTS 0x050
+#define NN_PUT 0x080
+#define NN_GET 0x084
+#define TIMESTAMP_LOW 0x0c0
+#define TIMESTAMP_HIGH 0x0c4
+#define T_INDEX_BYTE_INDEX 0x0f4
+#define LOCAL_CSR_STATUS 0x180
+
+u32 ixp2000_uengine_mask;
+
+static void *ixp2000_uengine_csr_area(int uengine)
+{
+ return ((void *)IXP_UENGINE_CSR_VIRT_BASE) + (uengine << 10);
+}
+
+/*
+ * LOCAL_CSR_STATUS=1 after a read or write to a microengine's CSR
+ * space means that the microengine we tried to access was also trying
+ * to access its own CSR space on the same clock cycle as we did. When
+ * this happens, we lose the arbitration process by default, and the
+ * read or write we tried to do was not actually performed, so we try
+ * again until it succeeds.
+ */
+u32 ixp2000_uengine_csr_read(int uengine, int offset)
+{
+ void *uebase;
+ u32 *local_csr_status;
+ u32 *reg;
+ u32 value;
+
+ uebase = ixp2000_uengine_csr_area(uengine);
+
+ local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS);
+ reg = (u32 *)(uebase + offset);
+ do {
+ value = ixp2000_reg_read(reg);
+ } while (ixp2000_reg_read(local_csr_status) & 1);
+
+ return value;
+}
+EXPORT_SYMBOL(ixp2000_uengine_csr_read);
+
+void ixp2000_uengine_csr_write(int uengine, int offset, u32 value)
+{
+ void *uebase;
+ u32 *local_csr_status;
+ u32 *reg;
+
+ uebase = ixp2000_uengine_csr_area(uengine);
+
+ local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS);
+ reg = (u32 *)(uebase + offset);
+ do {
+ ixp2000_reg_write(reg, value);
+ } while (ixp2000_reg_read(local_csr_status) & 1);
+}
+EXPORT_SYMBOL(ixp2000_uengine_csr_write);
+
+void ixp2000_uengine_reset(u32 uengine_mask)
+{
+ u32 value;
+
+ value = ixp2000_reg_read(IXP_RESET1) & ~ixp2000_uengine_mask;
+
+ uengine_mask &= ixp2000_uengine_mask;
+ ixp2000_reg_wrb(IXP_RESET1, value | uengine_mask);
+ ixp2000_reg_wrb(IXP_RESET1, value);
+}
+EXPORT_SYMBOL(ixp2000_uengine_reset);
+
+void ixp2000_uengine_set_mode(int uengine, u32 mode)
+{
+ /*
+ * CTL_STR_PAR_EN: unconditionally enable parity checking on
+ * control store.
+ */
+ mode |= 0x10000000;
+ ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mode);
+
+ /*
+ * Enable updating of condition codes.
+ */
+ ixp2000_uengine_csr_write(uengine, CC_ENABLE, 0x00002000);
+
+ /*
+ * Initialise other per-microengine registers.
+ */
+ ixp2000_uengine_csr_write(uengine, NN_PUT, 0x00);
+ ixp2000_uengine_csr_write(uengine, NN_GET, 0x00);
+ ixp2000_uengine_csr_write(uengine, T_INDEX_BYTE_INDEX, 0);
+}
+EXPORT_SYMBOL(ixp2000_uengine_set_mode);
+
+static int make_even_parity(u32 x)
+{
+ return hweight32(x) & 1;
+}
+
+static void ustore_write(int uengine, u64 insn)
+{
+ /*
+ * Generate even parity for top and bottom 20 bits.
+ */
+ insn |= (u64)make_even_parity((insn >> 20) & 0x000fffff) << 41;
+ insn |= (u64)make_even_parity(insn & 0x000fffff) << 40;
+
+ /*
+ * Write to microstore. The second write auto-increments
+ * the USTORE_ADDRESS index register.
+ */
+ ixp2000_uengine_csr_write(uengine, USTORE_DATA_LOWER, (u32)insn);
+ ixp2000_uengine_csr_write(uengine, USTORE_DATA_UPPER, (u32)(insn >> 32));
+}
+
+void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns)
+{
+ int i;
+
+ /*
+ * Start writing to microstore at address 0.
+ */
+ ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x80000000);
+ for (i = 0; i < insns; i++) {
+ u64 insn;
+
+ insn = (((u64)ucode[0]) << 32) |
+ (((u64)ucode[1]) << 24) |
+ (((u64)ucode[2]) << 16) |
+ (((u64)ucode[3]) << 8) |
+ ((u64)ucode[4]);
+ ucode += 5;
+
+ ustore_write(uengine, insn);
+ }
+
+ /*
+ * Pad with a few NOPs at the end (to avoid the microengine
+ * aborting as it prefetches beyond the last instruction), unless
+ * we run off the end of the instruction store first, at which
+ * point the address register will wrap back to zero.
+ */
+ for (i = 0; i < 4; i++) {
+ u32 addr;
+
+ addr = ixp2000_uengine_csr_read(uengine, USTORE_ADDRESS);
+ if (addr == 0x80000000)
+ break;
+ ustore_write(uengine, 0xf0000c0300ULL);
+ }
+
+ /*
+ * End programming.
+ */
+ ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x00000000);
+}
+EXPORT_SYMBOL(ixp2000_uengine_load_microcode);
+
+void ixp2000_uengine_init_context(int uengine, int context, int pc)
+{
+ /*
+ * Select the right context for indirect access.
+ */
+ ixp2000_uengine_csr_write(uengine, CSR_CTX_POINTER, context);
+
+ /*
+ * Initialise signal masks to immediately go to Ready state.
+ */
+ ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_SIG_EVENTS, 1);
+ ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_WAKEUP_EVENTS, 1);
+
+ /*
+ * Set program counter.
+ */
+ ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_STS, pc);
+}
+EXPORT_SYMBOL(ixp2000_uengine_init_context);
+
+void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask)
+{
+ u32 mask;
+
+ /*
+ * Enable the specified context to go to Executing state.
+ */
+ mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES);
+ mask |= ctx_mask << 8;
+ ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask);
+}
+EXPORT_SYMBOL(ixp2000_uengine_start_contexts);
+
+void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask)
+{
+ u32 mask;
+
+ /*
+ * Disable the Ready->Executing transition. Note that this
+ * does not stop the context until it voluntarily yields.
+ */
+ mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES);
+ mask &= ~(ctx_mask << 8);
+ ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask);
+}
+EXPORT_SYMBOL(ixp2000_uengine_stop_contexts);
+
+static int check_ixp_type(struct ixp2000_uengine_code *c)
+{
+ u32 product_id;
+ u32 rev;
+
+ product_id = ixp2000_reg_read(IXP_PRODUCT_ID);
+ if (((product_id >> 16) & 0x1f) != 0)
+ return 0;
+
+ switch ((product_id >> 8) & 0xff) {
+#ifdef CONFIG_ARCH_IXP2000
+ case 0: /* IXP2800 */
+ if (!(c->cpu_model_bitmask & 4))
+ return 0;
+ break;
+
+ case 1: /* IXP2850 */
+ if (!(c->cpu_model_bitmask & 8))
+ return 0;
+ break;
+
+ case 2: /* IXP2400 */
+ if (!(c->cpu_model_bitmask & 2))
+ return 0;
+ break;
+#endif
+
+#ifdef CONFIG_ARCH_IXP23XX
+ case 4: /* IXP23xx */
+ if (!(c->cpu_model_bitmask & 0x3f0))
+ return 0;
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ rev = product_id & 0xff;
+ if (rev < c->cpu_min_revision || rev > c->cpu_max_revision)
+ return 0;
+
+ return 1;
+}
+
+static void generate_ucode(u8 *ucode, u32 *gpr_a, u32 *gpr_b)
+{
+ int offset;
+ int i;
+
+ offset = 0;
+
+ for (i = 0; i < 128; i++) {
+ u8 b3;
+ u8 b2;
+ u8 b1;
+ u8 b0;
+
+ b3 = (gpr_a[i] >> 24) & 0xff;
+ b2 = (gpr_a[i] >> 16) & 0xff;
+ b1 = (gpr_a[i] >> 8) & 0xff;
+ b0 = gpr_a[i] & 0xff;
+
+ // immed[@ai, (b1 << 8) | b0]
+ // 11110000 0000VVVV VVVV11VV VVVVVV00 1IIIIIII
+ ucode[offset++] = 0xf0;
+ ucode[offset++] = (b1 >> 4);
+ ucode[offset++] = (b1 << 4) | 0x0c | (b0 >> 6);
+ ucode[offset++] = (b0 << 2);
+ ucode[offset++] = 0x80 | i;
+
+ // immed_w1[@ai, (b3 << 8) | b2]
+ // 11110100 0100VVVV VVVV11VV VVVVVV00 1IIIIIII
+ ucode[offset++] = 0xf4;
+ ucode[offset++] = 0x40 | (b3 >> 4);
+ ucode[offset++] = (b3 << 4) | 0x0c | (b2 >> 6);
+ ucode[offset++] = (b2 << 2);
+ ucode[offset++] = 0x80 | i;
+ }
+
+ for (i = 0; i < 128; i++) {
+ u8 b3;
+ u8 b2;
+ u8 b1;
+ u8 b0;
+
+ b3 = (gpr_b[i] >> 24) & 0xff;
+ b2 = (gpr_b[i] >> 16) & 0xff;
+ b1 = (gpr_b[i] >> 8) & 0xff;
+ b0 = gpr_b[i] & 0xff;
+
+ // immed[@bi, (b1 << 8) | b0]
+ // 11110000 0000VVVV VVVV001I IIIIII11 VVVVVVVV
+ ucode[offset++] = 0xf0;
+ ucode[offset++] = (b1 >> 4);
+ ucode[offset++] = (b1 << 4) | 0x02 | (i >> 6);
+ ucode[offset++] = (i << 2) | 0x03;
+ ucode[offset++] = b0;
+
+ // immed_w1[@bi, (b3 << 8) | b2]
+ // 11110100 0100VVVV VVVV001I IIIIII11 VVVVVVVV
+ ucode[offset++] = 0xf4;
+ ucode[offset++] = 0x40 | (b3 >> 4);
+ ucode[offset++] = (b3 << 4) | 0x02 | (i >> 6);
+ ucode[offset++] = (i << 2) | 0x03;
+ ucode[offset++] = b2;
+ }
+
+ // ctx_arb[kill]
+ ucode[offset++] = 0xe0;
+ ucode[offset++] = 0x00;
+ ucode[offset++] = 0x01;
+ ucode[offset++] = 0x00;
+ ucode[offset++] = 0x00;
+}
+
+static int set_initial_registers(int uengine, struct ixp2000_uengine_code *c)
+{
+ int per_ctx_regs;
+ u32 *gpr_a;
+ u32 *gpr_b;
+ u8 *ucode;
+ int i;
+
+ gpr_a = kzalloc(128 * sizeof(u32), GFP_KERNEL);
+ gpr_b = kzalloc(128 * sizeof(u32), GFP_KERNEL);
+ ucode = kmalloc(513 * 5, GFP_KERNEL);
+ if (gpr_a == NULL || gpr_b == NULL || ucode == NULL) {
+ kfree(ucode);
+ kfree(gpr_b);
+ kfree(gpr_a);
+ return 1;
+ }
+
+ per_ctx_regs = 16;
+ if (c->uengine_parameters & IXP2000_UENGINE_4_CONTEXTS)
+ per_ctx_regs = 32;
+
+ for (i = 0; i < 256; i++) {
+ struct ixp2000_reg_value *r = c->initial_reg_values + i;
+ u32 *bank;
+ int inc;
+ int j;
+
+ if (r->reg == -1)
+ break;
+
+ bank = (r->reg & 0x400) ? gpr_b : gpr_a;
+ inc = (r->reg & 0x80) ? 128 : per_ctx_regs;
+
+ j = r->reg & 0x7f;
+ while (j < 128) {
+ bank[j] = r->value;
+ j += inc;
+ }
+ }
+
+ generate_ucode(ucode, gpr_a, gpr_b);
+ ixp2000_uengine_load_microcode(uengine, ucode, 513);
+ ixp2000_uengine_init_context(uengine, 0, 0);
+ ixp2000_uengine_start_contexts(uengine, 0x01);
+ for (i = 0; i < 100; i++) {
+ u32 status;
+
+ status = ixp2000_uengine_csr_read(uengine, ACTIVE_CTX_STS);
+ if (!(status & 0x80000000))
+ break;
+ }
+ ixp2000_uengine_stop_contexts(uengine, 0x01);
+
+ kfree(ucode);
+ kfree(gpr_b);
+ kfree(gpr_a);
+
+ return !!(i == 100);
+}
+
+int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c)
+{
+ int ctx;
+
+ if (!check_ixp_type(c))
+ return 1;
+
+ if (!(ixp2000_uengine_mask & (1 << uengine)))
+ return 1;
+
+ ixp2000_uengine_reset(1 << uengine);
+ ixp2000_uengine_set_mode(uengine, c->uengine_parameters);
+ if (set_initial_registers(uengine, c))
+ return 1;
+ ixp2000_uengine_load_microcode(uengine, c->insns, c->num_insns);
+
+ for (ctx = 0; ctx < 8; ctx++)
+ ixp2000_uengine_init_context(uengine, ctx, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(ixp2000_uengine_load);
+
+
+static int __init ixp2000_uengine_init(void)
+{
+ int uengine;
+ u32 value;
+
+ /*
+ * Determine number of microengines present.
+ */
+ switch ((ixp2000_reg_read(IXP_PRODUCT_ID) >> 8) & 0x1fff) {
+#ifdef CONFIG_ARCH_IXP2000
+ case 0: /* IXP2800 */
+ case 1: /* IXP2850 */
+ ixp2000_uengine_mask = 0x00ff00ff;
+ break;
+
+ case 2: /* IXP2400 */
+ ixp2000_uengine_mask = 0x000f000f;
+ break;
+#endif
+
+#ifdef CONFIG_ARCH_IXP23XX
+ case 4: /* IXP23xx */
+ ixp2000_uengine_mask = (*IXP23XX_EXP_CFG_FUSE >> 8) & 0xf;
+ break;
+#endif
+
+ default:
+ printk(KERN_INFO "Detected unknown IXP2000 model (%.8x)\n",
+ (unsigned int)ixp2000_reg_read(IXP_PRODUCT_ID));
+ ixp2000_uengine_mask = 0x00000000;
+ break;
+ }
+
+ /*
+ * Reset microengines.
+ */
+ ixp2000_uengine_reset(ixp2000_uengine_mask);
+
+ /*
+ * Synchronise timestamp counters across all microengines.
+ */
+ value = ixp2000_reg_read(IXP_MISC_CONTROL);
+ ixp2000_reg_wrb(IXP_MISC_CONTROL, value & ~0x80);
+ for (uengine = 0; uengine < 32; uengine++) {
+ if (ixp2000_uengine_mask & (1 << uengine)) {
+ ixp2000_uengine_csr_write(uengine, TIMESTAMP_LOW, 0);
+ ixp2000_uengine_csr_write(uengine, TIMESTAMP_HIGH, 0);
+ }
+ }
+ ixp2000_reg_wrb(IXP_MISC_CONTROL, value | 0x80);
+
+ return 0;
+}
+
+subsys_initcall(ixp2000_uengine_init);
diff --git a/arch/arm/common/via82c505.c b/arch/arm/common/via82c505.c
new file mode 100644
index 0000000..8421d39
--- /dev/null
+++ b/arch/arm/common/via82c505.c
@@ -0,0 +1,92 @@
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/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;
+}
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c
new file mode 100644
index 0000000..f1e4b8f
--- /dev/null
+++ b/arch/arm/common/vic.c
@@ -0,0 +1,98 @@
+/*
+ * linux/arch/arm/common/vic.c
+ *
+ * Copyright (C) 1999 - 2003 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/io.h>
+
+#include <asm/mach/irq.h>
+#include <asm/hardware/vic.h>
+
+static void vic_mask_irq(unsigned int irq)
+{
+ void __iomem *base = get_irq_chip_data(irq);
+ irq &= 31;
+ writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
+}
+
+static void vic_unmask_irq(unsigned int irq)
+{
+ void __iomem *base = get_irq_chip_data(irq);
+ irq &= 31;
+ writel(1 << irq, base + VIC_INT_ENABLE);
+}
+
+static struct irq_chip vic_chip = {
+ .name = "VIC",
+ .ack = vic_mask_irq,
+ .mask = vic_mask_irq,
+ .unmask = vic_unmask_irq,
+};
+
+/**
+ * vic_init - initialise a vectored interrupt controller
+ * @base: iomem base address
+ * @irq_start: starting interrupt number, must be muliple of 32
+ * @vic_sources: bitmask of interrupt sources to allow
+ */
+void __init vic_init(void __iomem *base, unsigned int irq_start,
+ u32 vic_sources)
+{
+ unsigned int i;
+
+ /* Disable all interrupts initially. */
+
+ writel(0, base + VIC_INT_SELECT);
+ writel(0, base + VIC_INT_ENABLE);
+ writel(~0, base + VIC_INT_ENABLE_CLEAR);
+ writel(0, base + VIC_IRQ_STATUS);
+ writel(0, base + VIC_ITCR);
+ writel(~0, base + VIC_INT_SOFT_CLEAR);
+
+ /*
+ * Make sure we clear all existing interrupts
+ */
+ writel(0, base + VIC_VECT_ADDR);
+ for (i = 0; i < 19; i++) {
+ unsigned int value;
+
+ value = readl(base + VIC_VECT_ADDR);
+ writel(value, base + VIC_VECT_ADDR);
+ }
+
+ for (i = 0; i < 16; i++) {
+ void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
+ writel(VIC_VECT_CNTL_ENABLE | i, reg);
+ }
+
+ writel(32, base + VIC_DEF_VECT_ADDR);
+
+ for (i = 0; i < 32; i++) {
+ unsigned int irq = irq_start + i;
+
+ set_irq_chip(irq, &vic_chip);
+ set_irq_chip_data(irq, base);
+
+ if (vic_sources & (1 << i)) {
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ }
+}
OpenPOWER on IntegriCloud