Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

34
arch/arm/common/Kconfig Normal file
View File

@@ -0,0 +1,34 @@
config ARM_GIC
bool
config ARM_VIC
bool
config ICST525
bool
config ICST307
bool
config SA1111
bool
select DMABOUNCE
config DMABOUNCE
bool
config TIMER_ACORN
bool
config SHARP_LOCOMO
bool
config SHARP_PARAM
bool
config SHARPSL_PM
bool
select APM_EMULATION
config SHARP_SCOOP
bool

19
arch/arm/common/Makefile Normal file
View File

@@ -0,0 +1,19 @@
#
# Makefile for the linux kernel.
#
obj-y += rtctime.o
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

658
arch/arm/common/dmabounce.c Normal file
View File

@@ -0,0 +1,658 @@
/*
* 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 discontigous
* DMA windows will require custom implementations that reserve memory
* areas at early bootup.
*
* Original version by Brad Parker (brad@heeltoe.com)
* Re-written by Christopher Hoover <ch@murgatroid.com>
* Made generic by Deepak Saxena <dsaxena@plexity.net>
*
* Copyright (C) 2002 Hewlett Packard Company.
* Copyright (C) 2004 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/list.h>
#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 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 (phy=%p) mapped to %p (phy=%p)\n",
__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
buf->safe, (void *) buf->safe_dma_addr);
if ((dir == DMA_TO_DEVICE) ||
(dir == DMA_BIDIRECTIONAL)) {
dev_dbg(dev, "%s: copy unsafe %p to safe %p, size %d\n",
__func__, ptr, buf->safe, size);
memcpy(buf->safe, ptr, size);
}
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.
*/
consistent_sync(ptr, size, dir);
}
return dma_addr;
}
static inline void
unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
enum dma_data_direction dir)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
struct safe_buffer *buf = NULL;
/*
* Trying to unmap an invalid mapping
*/
if (dma_mapping_error(dma_addr)) {
dev_err(dev, "Trying to unmap invalid mapping\n");
return;
}
if (device_info)
buf = find_safe_buffer(device_info, dma_addr);
if (buf) {
BUG_ON(buf->size != size);
dev_dbg(dev,
"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
buf->safe, (void *) buf->safe_dma_addr);
DO_STATS ( device_info->bounce_count++ );
if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
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(device_info, buf);
}
}
static inline void
sync_single(struct device *dev, dma_addr_t dma_addr, size_t size,
enum dma_data_direction dir)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
struct safe_buffer *buf = NULL;
if (device_info)
buf = find_safe_buffer(device_info, dma_addr);
if (buf) {
/*
* Both of these checks from original code need to be
* commented out b/c some drivers rely on the following:
*
* 1) Drivers may map a large chunk of memory into DMA space
* but only sync a small portion of it. Good example is
* allocating a large buffer, mapping it, and then
* breaking it up into small descriptors. No point
* in syncing the whole buffer if you only have to
* touch one descriptor.
*
* 2) Buffers that are mapped as DMA_BIDIRECTIONAL are
* usually only synced in one dir at a time.
*
* See drivers/net/eepro100.c for examples of both cases.
*
* -ds
*
* BUG_ON(buf->size != size);
* BUG_ON(buf->direction != dir);
*/
dev_dbg(dev,
"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
buf->safe, (void *) buf->safe_dma_addr);
DO_STATS ( device_info->bounce_count++ );
switch (dir) {
case DMA_FROM_DEVICE:
dev_dbg(dev,
"%s: copy back safe %p to unsafe %p size %d\n",
__func__, buf->safe, buf->ptr, size);
memcpy(buf->ptr, buf->safe, size);
break;
case DMA_TO_DEVICE:
dev_dbg(dev,
"%s: copy out unsafe %p to safe %p, size %d\n",
__func__,buf->ptr, buf->safe, size);
memcpy(buf->safe, buf->ptr, size);
break;
case DMA_BIDIRECTIONAL:
BUG(); /* is this allowed? what does it mean? */
default:
BUG();
}
/*
* No need to sync the safe buffer - it was allocated
* via the coherent allocators.
*/
} else {
consistent_sync(dma_to_virt(dev, dma_addr), size, dir);
}
}
/* ************************************************** */
/*
* see if a buffer address is in an 'unsafe' range. if it is
* allocate a 'safe' buffer and copy the unsafe buffer into it.
* substitute the safe buffer for the unsafe one.
* (basically move the buffer from an unsafe area to a safe one)
*/
dma_addr_t
dma_map_single(struct device *dev, void *ptr, size_t size,
enum dma_data_direction dir)
{
dma_addr_t dma_addr;
dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
__func__, ptr, size, dir);
BUG_ON(dir == DMA_NONE);
dma_addr = map_single(dev, ptr, size, dir);
return dma_addr;
}
/*
* see if a mapped address was really a "safe" buffer and if so, copy
* the data from the safe buffer back to the unsafe buffer and free up
* the safe buffer. (basically return things back to the way they
* should be)
*/
void
dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
enum dma_data_direction dir)
{
dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
__func__, (void *) dma_addr, size, dir);
BUG_ON(dir == DMA_NONE);
unmap_single(dev, dma_addr, size, dir);
}
int
dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir)
{
int i;
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
__func__, sg, nents, dir);
BUG_ON(dir == DMA_NONE);
for (i = 0; i < nents; i++, sg++) {
struct page *page = sg->page;
unsigned int offset = sg->offset;
unsigned int length = sg->length;
void *ptr = page_address(page) + offset;
sg->dma_address =
map_single(dev, ptr, length, dir);
}
return nents;
}
void
dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir)
{
int i;
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
__func__, sg, nents, dir);
BUG_ON(dir == DMA_NONE);
for (i = 0; i < nents; i++, sg++) {
dma_addr_t dma_addr = sg->dma_address;
unsigned int length = sg->length;
unmap_single(dev, dma_addr, length, dir);
}
}
void
dma_sync_single_for_cpu(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);
sync_single(dev, dma_addr, size, dir);
}
void
dma_sync_single_for_device(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);
sync_single(dev, dma_addr, size, dir);
}
void
dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir)
{
int i;
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
__func__, sg, nents, dir);
BUG_ON(dir == DMA_NONE);
for (i = 0; i < nents; i++, sg++) {
dma_addr_t dma_addr = sg->dma_address;
unsigned int length = sg->length;
sync_single(dev, dma_addr, length, dir);
}
}
void
dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir)
{
int i;
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
__func__, sg, nents, dir);
BUG_ON(dir == DMA_NONE);
for (i = 0; i < nents; i++, sg++) {
dma_addr_t dma_addr = sg->dma_address;
unsigned int length = sg->length;
sync_single(dev, dma_addr, length, dir);
}
}
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) {
printk(KERN_ERR
"Could not allocated dmabounce_device_info for %s",
dev->bus_id);
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;
printk(KERN_INFO "dmabounce: registered device %s on %s bus\n",
dev->bus_id, dev->bus->name);
return 0;
err_destroy:
dma_pool_destroy(device_info->small.pool);
err_free:
kfree(device_info);
return ret;
}
void
dmabounce_unregister_dev(struct device *dev)
{
struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
dev->archdata.dmabounce = NULL;
if (!device_info) {
printk(KERN_WARNING
"%s: Never registered with dmabounce but attempting" \
"to unregister!\n", dev->bus_id);
return;
}
if (!list_empty(&device_info->safe_buffers)) {
printk(KERN_ERR
"%s: Removing from dmabounce with pending buffers!\n",
dev->bus_id);
BUG();
}
if (device_info->small.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);
printk(KERN_INFO "dmabounce: device %s on %s bus unregistered\n",
dev->bus_id, dev->bus->name);
}
EXPORT_SYMBOL(dma_map_single);
EXPORT_SYMBOL(dma_unmap_single);
EXPORT_SYMBOL(dma_map_sg);
EXPORT_SYMBOL(dma_unmap_sg);
EXPORT_SYMBOL(dma_sync_single_for_cpu);
EXPORT_SYMBOL(dma_sync_single_for_device);
EXPORT_SYMBOL(dma_sync_sg);
EXPORT_SYMBOL(dmabounce_register_dev);
EXPORT_SYMBOL(dmabounce_unregister_dev);
MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>");
MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows");
MODULE_LICENSE("GPL");

265
arch/arm/common/gic.c Normal file
View File

@@ -0,0 +1,265 @@
/*
* 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 <asm/irq.h>
#include <asm/io.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 seperate 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 fastcall 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;
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);
cascade_irq = (status & 0x3ff);
if (cascade_irq > 1020)
goto out;
if (cascade_irq < 32 || cascade_irq >= NR_IRQS) {
do_bad_IRQ(cascade_irq, desc);
goto out;
}
cascade_irq += chip_data->irq_offset;
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

161
arch/arm/common/icst307.c Normal file
View File

@@ -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);

160
arch/arm/common/icst525.c Normal file
View File

@@ -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);

1192
arch/arm/common/locomo.c Normal file

File diff suppressed because it is too large Load Diff

436
arch/arm/common/rtctime.c Normal file
View File

@@ -0,0 +1,436 @@
/*
* linux/arch/arm/common/rtctime.c
*
* Copyright (C) 2003 Deep Blue Solutions Ltd.
* Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
* Based on rtc.c by Paul Gortmaker
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/rtc.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/capability.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/rtc.h>
#include <asm/rtc.h>
#include <asm/semaphore.h>
static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
static struct fasync_struct *rtc_async_queue;
/*
* rtc_lock protects rtc_irq_data
*/
static DEFINE_SPINLOCK(rtc_lock);
static unsigned long rtc_irq_data;
/*
* rtc_sem protects rtc_inuse and rtc_ops
*/
static DEFINE_MUTEX(rtc_mutex);
static unsigned long rtc_inuse;
static struct rtc_ops *rtc_ops;
#define rtc_epoch 1900UL
/*
* Calculate the next alarm time given the requested alarm time mask
* and the current time.
*/
void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
{
unsigned long next_time;
unsigned long now_time;
next->tm_year = now->tm_year;
next->tm_mon = now->tm_mon;
next->tm_mday = now->tm_mday;
next->tm_hour = alrm->tm_hour;
next->tm_min = alrm->tm_min;
next->tm_sec = alrm->tm_sec;
rtc_tm_to_time(now, &now_time);
rtc_tm_to_time(next, &next_time);
if (next_time < now_time) {
/* Advance one day */
next_time += 60 * 60 * 24;
rtc_time_to_tm(next_time, next);
}
}
EXPORT_SYMBOL(rtc_next_alarm_time);
static inline int rtc_arm_read_time(struct rtc_ops *ops, struct rtc_time *tm)
{
memset(tm, 0, sizeof(struct rtc_time));
return ops->read_time(tm);
}
static inline int rtc_arm_set_time(struct rtc_ops *ops, struct rtc_time *tm)
{
int ret;
ret = rtc_valid_tm(tm);
if (ret == 0)
ret = ops->set_time(tm);
return ret;
}
static inline int rtc_arm_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
{
int ret = -EINVAL;
if (ops->read_alarm) {
memset(alrm, 0, sizeof(struct rtc_wkalrm));
ret = ops->read_alarm(alrm);
}
return ret;
}
static inline int rtc_arm_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
{
int ret = -EINVAL;
if (ops->set_alarm)
ret = ops->set_alarm(alrm);
return ret;
}
void rtc_update(unsigned long num, unsigned long events)
{
spin_lock(&rtc_lock);
rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
spin_unlock(&rtc_lock);
wake_up_interruptible(&rtc_wait);
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
}
EXPORT_SYMBOL(rtc_update);
static ssize_t
rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
ssize_t ret;
if (count < sizeof(unsigned long))
return -EINVAL;
add_wait_queue(&rtc_wait, &wait);
do {
__set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irq(&rtc_lock);
data = rtc_irq_data;
rtc_irq_data = 0;
spin_unlock_irq(&rtc_lock);
if (data != 0) {
ret = 0;
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
schedule();
} while (1);
set_current_state(TASK_RUNNING);
remove_wait_queue(&rtc_wait, &wait);
if (ret == 0) {
ret = put_user(data, (unsigned long __user *)buf);
if (ret == 0)
ret = sizeof(unsigned long);
}
return ret;
}
static unsigned int rtc_poll(struct file *file, poll_table *wait)
{
unsigned long data;
poll_wait(file, &rtc_wait, wait);
spin_lock_irq(&rtc_lock);
data = rtc_irq_data;
spin_unlock_irq(&rtc_lock);
return data != 0 ? POLLIN | POLLRDNORM : 0;
}
static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct rtc_ops *ops = file->private_data;
struct rtc_time tm;
struct rtc_wkalrm alrm;
void __user *uarg = (void __user *)arg;
int ret = -EINVAL;
switch (cmd) {
case RTC_ALM_READ:
ret = rtc_arm_read_alarm(ops, &alrm);
if (ret)
break;
ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
if (ret)
ret = -EFAULT;
break;
case RTC_ALM_SET:
ret = copy_from_user(&alrm.time, uarg, sizeof(tm));
if (ret) {
ret = -EFAULT;
break;
}
alrm.enabled = 0;
alrm.pending = 0;
alrm.time.tm_mday = -1;
alrm.time.tm_mon = -1;
alrm.time.tm_year = -1;
alrm.time.tm_wday = -1;
alrm.time.tm_yday = -1;
alrm.time.tm_isdst = -1;
ret = rtc_arm_set_alarm(ops, &alrm);
break;
case RTC_RD_TIME:
ret = rtc_arm_read_time(ops, &tm);
if (ret)
break;
ret = copy_to_user(uarg, &tm, sizeof(tm));
if (ret)
ret = -EFAULT;
break;
case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME)) {
ret = -EACCES;
break;
}
ret = copy_from_user(&tm, uarg, sizeof(tm));
if (ret) {
ret = -EFAULT;
break;
}
ret = rtc_arm_set_time(ops, &tm);
break;
case RTC_EPOCH_SET:
#ifndef rtc_epoch
/*
* There were no RTC clocks before 1900.
*/
if (arg < 1900) {
ret = -EINVAL;
break;
}
if (!capable(CAP_SYS_TIME)) {
ret = -EACCES;
break;
}
rtc_epoch = arg;
ret = 0;
#endif
break;
case RTC_EPOCH_READ:
ret = put_user(rtc_epoch, (unsigned long __user *)uarg);
break;
case RTC_WKALM_SET:
ret = copy_from_user(&alrm, uarg, sizeof(alrm));
if (ret) {
ret = -EFAULT;
break;
}
ret = rtc_arm_set_alarm(ops, &alrm);
break;
case RTC_WKALM_RD:
ret = rtc_arm_read_alarm(ops, &alrm);
if (ret)
break;
ret = copy_to_user(uarg, &alrm, sizeof(alrm));
if (ret)
ret = -EFAULT;
break;
default:
if (ops->ioctl)
ret = ops->ioctl(cmd, arg);
break;
}
return ret;
}
static int rtc_open(struct inode *inode, struct file *file)
{
int ret;
mutex_lock(&rtc_mutex);
if (rtc_inuse) {
ret = -EBUSY;
} else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
ret = -ENODEV;
} else {
file->private_data = rtc_ops;
ret = rtc_ops->open ? rtc_ops->open() : 0;
if (ret == 0) {
spin_lock_irq(&rtc_lock);
rtc_irq_data = 0;
spin_unlock_irq(&rtc_lock);
rtc_inuse = 1;
}
}
mutex_unlock(&rtc_mutex);
return ret;
}
static int rtc_release(struct inode *inode, struct file *file)
{
struct rtc_ops *ops = file->private_data;
if (ops->release)
ops->release();
spin_lock_irq(&rtc_lock);
rtc_irq_data = 0;
spin_unlock_irq(&rtc_lock);
module_put(rtc_ops->owner);
rtc_inuse = 0;
return 0;
}
static int rtc_fasync(int fd, struct file *file, int on)
{
return fasync_helper(fd, file, on, &rtc_async_queue);
}
static const struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_read,
.poll = rtc_poll,
.ioctl = rtc_ioctl,
.open = rtc_open,
.release = rtc_release,
.fasync = rtc_fasync,
};
static struct miscdevice rtc_miscdev = {
.minor = RTC_MINOR,
.name = "rtc",
.fops = &rtc_fops,
};
static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
struct rtc_ops *ops = data;
struct rtc_wkalrm alrm;
struct rtc_time tm;
char *p = page;
if (rtc_arm_read_time(ops, &tm) == 0) {
p += sprintf(p,
"rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n"
"rtc_epoch\t: %04lu\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
rtc_epoch);
}
if (rtc_arm_read_alarm(ops, &alrm) == 0) {
p += sprintf(p, "alrm_time\t: ");
if ((unsigned int)alrm.time.tm_hour <= 24)
p += sprintf(p, "%02d:", alrm.time.tm_hour);
else
p += sprintf(p, "**:");
if ((unsigned int)alrm.time.tm_min <= 59)
p += sprintf(p, "%02d:", alrm.time.tm_min);
else
p += sprintf(p, "**:");
if ((unsigned int)alrm.time.tm_sec <= 59)
p += sprintf(p, "%02d\n", alrm.time.tm_sec);
else
p += sprintf(p, "**\n");
p += sprintf(p, "alrm_date\t: ");
if ((unsigned int)alrm.time.tm_year <= 200)
p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
else
p += sprintf(p, "****-");
if ((unsigned int)alrm.time.tm_mon <= 11)
p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
else
p += sprintf(p, "**-");
if ((unsigned int)alrm.time.tm_mday <= 31)
p += sprintf(p, "%02d\n", alrm.time.tm_mday);
else
p += sprintf(p, "**\n");
p += sprintf(p, "alrm_wakeup\t: %s\n",
alrm.enabled ? "yes" : "no");
p += sprintf(p, "alrm_pending\t: %s\n",
alrm.pending ? "yes" : "no");
}
if (ops->proc)
p += ops->proc(p);
return p - page;
}
int register_rtc(struct rtc_ops *ops)
{
int ret = -EBUSY;
mutex_lock(&rtc_mutex);
if (rtc_ops == NULL) {
rtc_ops = ops;
ret = misc_register(&rtc_miscdev);
if (ret == 0)
create_proc_read_entry("driver/rtc", 0, NULL,
rtc_read_proc, ops);
}
mutex_unlock(&rtc_mutex);
return ret;
}
EXPORT_SYMBOL(register_rtc);
void unregister_rtc(struct rtc_ops *rtc)
{
mutex_lock(&rtc_mutex);
if (rtc == rtc_ops) {
remove_proc_entry("driver/rtc", NULL);
misc_deregister(&rtc_miscdev);
rtc_ops = NULL;
}
mutex_unlock(&rtc_mutex);
}
EXPORT_SYMBOL(unregister_rtc);

1301
arch/arm/common/sa1111.c Normal file

File diff suppressed because it is too large Load Diff

203
arch/arm/common/scoop.c Normal file
View File

@@ -0,0 +1,203 @@
/*
* 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 <asm/io.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);
#define SCOOP_REG(d,adr) (*(volatile unsigned short*)(d +(adr)))
struct scoop_dev {
void *base;
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);
SCOOP_REG(sdev->base,SCOOP_MCR) = 0x0100; // 00
SCOOP_REG(sdev->base,SCOOP_CDR) = 0x0000; // 04
SCOOP_REG(sdev->base,SCOOP_CCR) = 0x0000; // 10
SCOOP_REG(sdev->base,SCOOP_IMR) = 0x0000; // 18
SCOOP_REG(sdev->base,SCOOP_IRM) = 0x00FF; // 14
SCOOP_REG(sdev->base,SCOOP_ISR) = 0x0000; // 1C
SCOOP_REG(sdev->base,SCOOP_IRM) = 0x0000;
}
unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
{
unsigned short gpio_bit;
unsigned long flag;
struct scoop_dev *sdev = dev_get_drvdata(dev);
spin_lock_irqsave(&sdev->scoop_lock, flag);
gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) | bit;
SCOOP_REG(sdev->base, SCOOP_GPWR) = gpio_bit;
spin_unlock_irqrestore(&sdev->scoop_lock, flag);
return gpio_bit;
}
unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
{
unsigned short gpio_bit;
unsigned long flag;
struct scoop_dev *sdev = dev_get_drvdata(dev);
spin_lock_irqsave(&sdev->scoop_lock, flag);
gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) & ~bit;
SCOOP_REG(sdev->base,SCOOP_GPWR) = gpio_bit;
spin_unlock_irqrestore(&sdev->scoop_lock, flag);
return gpio_bit;
}
EXPORT_SYMBOL(set_scoop_gpio);
EXPORT_SYMBOL(reset_scoop_gpio);
unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
{
struct scoop_dev *sdev = dev_get_drvdata(dev);
return SCOOP_REG(sdev->base,reg);
}
void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
{
struct scoop_dev *sdev = dev_get_drvdata(dev);
SCOOP_REG(sdev->base,reg)=data;
}
EXPORT_SYMBOL(reset_scoop);
EXPORT_SYMBOL(read_scoop_reg);
EXPORT_SYMBOL(write_scoop_reg);
static void check_scoop_reg(struct scoop_dev *sdev)
{
unsigned short mcr;
mcr = SCOOP_REG(sdev->base, SCOOP_MCR);
if ((mcr & 0x100) == 0)
SCOOP_REG(sdev->base, SCOOP_MCR) = 0x0101;
}
#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 = SCOOP_REG(sdev->base, SCOOP_GPWR);
SCOOP_REG(sdev->base, SCOOP_GPWR) = (sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set;
return 0;
}
static int scoop_resume(struct platform_device *dev)
{
struct scoop_dev *sdev = platform_get_drvdata(dev);
check_scoop_reg(sdev);
SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr;
return 0;
}
#else
#define scoop_suspend NULL
#define scoop_resume NULL
#endif
int __init scoop_probe(struct platform_device *pdev)
{
struct scoop_dev *devptr;
struct scoop_config *inf;
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
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) {
kfree(devptr);
return -ENOMEM;
}
platform_set_drvdata(pdev, devptr);
printk("Sharp Scoop Device found at 0x%08x -> 0x%08x\n",(unsigned int)mem->start,(unsigned int)devptr->base);
SCOOP_REG(devptr->base, SCOOP_MCR) = 0x0140;
reset_scoop(&pdev->dev);
SCOOP_REG(devptr->base, SCOOP_CPR) = 0x0000;
SCOOP_REG(devptr->base, SCOOP_GPCR) = inf->io_dir & 0xffff;
SCOOP_REG(devptr->base, SCOOP_GPWR) = inf->io_out & 0xffff;
devptr->suspend_clr = inf->suspend_clr;
devptr->suspend_set = inf->suspend_set;
return 0;
}
static int scoop_remove(struct platform_device *pdev)
{
struct scoop_dev *sdev = platform_get_drvdata(pdev);
if (sdev) {
iounmap(sdev->base);
kfree(sdev);
platform_set_drvdata(pdev, NULL);
}
return 0;
}
static struct platform_driver scoop_driver = {
.probe = scoop_probe,
.remove = scoop_remove,
.suspend = scoop_suspend,
.resume = scoop_resume,
.driver = {
.name = "sharp-scoop",
},
};
int __init scoop_init(void)
{
return platform_driver_register(&scoop_driver);
}
subsys_initcall(scoop_init);

View File

@@ -0,0 +1,60 @@
/*
* Hardware parameter area specific to Sharp SL series devices
*
* Copyright (c) 2005 Richard Purdie
*
* Based on Sharp's 2.4 kernel patches
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/mach/sharpsl_param.h>
/*
* Certain hardware parameters determined at the time of device manufacture,
* typically including LCD parameters are loaded by the bootloader at the
* address PARAM_BASE. As the kernel will overwrite them, we need to store
* them early in the boot process, then pass them to the appropriate drivers.
* Not all devices use all paramaters but the format is common to all.
*/
#ifdef 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;
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;
}

View File

@@ -0,0 +1,845 @@
/*
* 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 <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
#include <asm/arch/pm.h>
#include <asm/arch/pxa-regs.h>
#include <asm/arch/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
*/
static int sharpsl_off_charge_battery(void);
static int sharpsl_check_battery_temp(void);
static int sharpsl_check_battery_voltage(void);
static int sharpsl_ac_check(void);
static int sharpsl_fatal_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: %d\n", voltage,
sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies);
/* 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;
}
/* 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 staus 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;
}
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;
}
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;
}
#endif
/*
* 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: %d\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 occured.\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 occured */
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 occured. 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 occured */
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;
}
}
}
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;
}
static struct pm_ops sharpsl_pm_ops = {
.pm_disk_mode = PM_DISK_FIRMWARE,
.prepare = pxa_pm_prepare,
.enter = corgi_pxa_pm_enter,
.finish = pxa_pm_finish,
};
static int __init sharpsl_pm_probe(struct platform_device *pdev)
{
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();
device_create_file(&pdev->dev, &dev_attr_battery_percentage);
device_create_file(&pdev->dev, &dev_attr_battery_voltage);
apm_get_power_status = sharpsl_apm_get_power_status;
pm_set_ops(&sharpsl_pm_ops);
mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
return 0;
}
static int sharpsl_pm_remove(struct platform_device *pdev)
{
pm_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);

View File

@@ -0,0 +1,97 @@
/*
* 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 <asm/hardware.h>
#include <asm/io.h>
#include <asm/hardware/ioc.h>
#include <asm/mach/time.h>
unsigned long ioc_timer_gettimeoffset(void)
{
unsigned int count1, count2, status;
long offset;
ioc_writeb (0, IOC_T0LATCH);
barrier ();
count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
barrier ();
status = ioc_readb(IOC_IRQREQA);
barrier ();
ioc_writeb (0, IOC_T0LATCH);
barrier ();
count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
offset = count2;
if (count2 < count1) {
/*
* We have not had an interrupt between reading count1
* and count2.
*/
if (status & (1 << 5))
offset -= LATCH;
} else if (count2 > count1) {
/*
* We have just had another interrupt between reading
* count1 and count2.
*/
offset -= LATCH;
}
offset = (LATCH - offset) * (tick_nsec / 1000);
return (offset + LATCH/2) / LATCH;
}
void __init ioctime_init(void)
{
ioc_writeb(LATCH & 255, IOC_T0LTCHL);
ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
ioc_writeb(0, IOC_T0GO);
}
static irqreturn_t
ioc_timer_interrupt(int irq, void *dev_id)
{
write_seqlock(&xtime_lock);
timer_tick();
write_sequnlock(&xtime_lock);
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,
};

510
arch/arm/common/uengine.c Normal file
View File

@@ -0,0 +1,510 @@
/*
* 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 <asm/hardware.h>
#include <asm/arch/hardware.h>
#include <asm/hardware/uengine.h>
#include <asm/io.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 = kmalloc(128 * sizeof(u32), GFP_KERNEL);
gpr_b = kmalloc(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;
memset(gpr_a, 0, sizeof(gpr_a));
memset(gpr_b, 0, sizeof(gpr_b));
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);

View File

@@ -0,0 +1,93 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/mach/pci.h>
#define MAX_SLOTS 7
#define CONFIG_CMD(bus, devfn, where) (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
static int
via82c505_read_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 *value)
{
outl(CONFIG_CMD(bus,devfn,where),0xCF8);
switch (size) {
case 1:
*value=inb(0xCFC + (where&3));
break;
case 2:
*value=inw(0xCFC + (where&2));
break;
case 4:
*value=inl(0xCFC);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int
via82c505_write_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 value)
{
outl(CONFIG_CMD(bus,devfn,where),0xCF8);
switch (size) {
case 1:
outb(value, 0xCFC + (where&3));
break;
case 2:
outw(value, 0xCFC + (where&2));
break;
case 4:
outl(value, 0xCFC);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops via82c505_ops = {
.read = via82c505_read_config,
.write = via82c505_write_config,
};
void __init via82c505_preinit(void)
{
printk(KERN_DEBUG "PCI: VIA 82c505\n");
if (!request_region(0xA8,2,"via config")) {
printk(KERN_WARNING"VIA 82c505: Unable to request region 0xA8\n");
return;
}
if (!request_region(0xCF8,8,"pci config")) {
printk(KERN_WARNING"VIA 82c505: Unable to request region 0xCF8\n");
release_region(0xA8, 2);
return;
}
/* Enable compatible Mode */
outb(0x96,0xA8);
outb(0x18,0xA9);
outb(0x93,0xA8);
outb(0xd0,0xA9);
}
int __init via82c505_setup(int nr, struct pci_sys_data *sys)
{
return (nr == 0);
}
struct pci_bus * __init via82c505_scan_bus(int nr, struct pci_sys_data *sysdata)
{
if (nr == 0)
return pci_scan_bus(0, &via82c505_ops, sysdata);
return NULL;
}

98
arch/arm/common/vic.c Normal file
View File

@@ -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 <asm/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);
}
}
}