Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
24
arch/arm/common/Kconfig
Normal file
24
arch/arm/common/Kconfig
Normal file
@@ -0,0 +1,24 @@
|
||||
config ICST525
|
||||
bool
|
||||
|
||||
config ICST307
|
||||
bool
|
||||
|
||||
config SA1111
|
||||
bool
|
||||
select DMABOUNCE
|
||||
|
||||
config DMABOUNCE
|
||||
bool
|
||||
|
||||
config TIMER_ACORN
|
||||
bool
|
||||
|
||||
config SHARP_LOCOMO
|
||||
bool
|
||||
|
||||
config SHARP_PARAM
|
||||
bool
|
||||
|
||||
config SHARP_SCOOP
|
||||
bool
|
15
arch/arm/common/Makefile
Normal file
15
arch/arm/common/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
obj-y += rtctime.o
|
||||
obj-$(CONFIG_ARM_AMBA) += amba.o
|
||||
obj-$(CONFIG_ICST525) += icst525.o
|
||||
obj-$(CONFIG_ICST307) += icst307.o
|
||||
obj-$(CONFIG_SA1111) += sa1111.o
|
||||
obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o
|
||||
obj-$(CONFIG_DMABOUNCE) += dmabounce.o
|
||||
obj-$(CONFIG_TIMER_ACORN) += time-acorn.o
|
||||
obj-$(CONFIG_SHARP_LOCOMO) += locomo.o
|
||||
obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o
|
||||
obj-$(CONFIG_SHARP_SCOOP) += scoop.o
|
357
arch/arm/common/amba.c
Normal file
357
arch/arm/common/amba.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* linux/arch/arm/common/amba.c
|
||||
*
|
||||
* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware/amba.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#define to_amba_device(d) container_of(d, struct amba_device, dev)
|
||||
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
|
||||
|
||||
static struct amba_id *
|
||||
amba_lookup(struct amba_id *table, struct amba_device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (table->mask) {
|
||||
ret = (dev->periphid & table->mask) == table->id;
|
||||
if (ret)
|
||||
break;
|
||||
table++;
|
||||
}
|
||||
|
||||
return ret ? table : NULL;
|
||||
}
|
||||
|
||||
static int amba_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct amba_device *pcdev = to_amba_device(dev);
|
||||
struct amba_driver *pcdrv = to_amba_driver(drv);
|
||||
|
||||
return amba_lookup(pcdrv->id_table, pcdev) != NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG
|
||||
static int amba_hotplug(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
|
||||
{
|
||||
struct amba_device *pcdev = to_amba_device(dev);
|
||||
|
||||
if (nr_env < 2)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid);
|
||||
*envp++ = buf;
|
||||
*envp++ = NULL;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define amba_hotplug NULL
|
||||
#endif
|
||||
|
||||
static int amba_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct amba_driver *drv = to_amba_driver(dev->driver);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && drv->suspend)
|
||||
ret = drv->suspend(to_amba_device(dev), state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int amba_resume(struct device *dev)
|
||||
{
|
||||
struct amba_driver *drv = to_amba_driver(dev->driver);
|
||||
int ret = 0;
|
||||
|
||||
if (dev->driver && drv->resume)
|
||||
ret = drv->resume(to_amba_device(dev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Primecells are part of the Advanced Microcontroller Bus Architecture,
|
||||
* so we call the bus "amba".
|
||||
*/
|
||||
static struct bus_type amba_bustype = {
|
||||
.name = "amba",
|
||||
.match = amba_match,
|
||||
.hotplug = amba_hotplug,
|
||||
.suspend = amba_suspend,
|
||||
.resume = amba_resume,
|
||||
};
|
||||
|
||||
static int __init amba_init(void)
|
||||
{
|
||||
return bus_register(&amba_bustype);
|
||||
}
|
||||
|
||||
postcore_initcall(amba_init);
|
||||
|
||||
/*
|
||||
* These are the device model conversion veneers; they convert the
|
||||
* device model structures to our more specific structures.
|
||||
*/
|
||||
static int amba_probe(struct device *dev)
|
||||
{
|
||||
struct amba_device *pcdev = to_amba_device(dev);
|
||||
struct amba_driver *pcdrv = to_amba_driver(dev->driver);
|
||||
struct amba_id *id;
|
||||
|
||||
id = amba_lookup(pcdrv->id_table, pcdev);
|
||||
|
||||
return pcdrv->probe(pcdev, id);
|
||||
}
|
||||
|
||||
static int amba_remove(struct device *dev)
|
||||
{
|
||||
struct amba_driver *drv = to_amba_driver(dev->driver);
|
||||
return drv->remove(to_amba_device(dev));
|
||||
}
|
||||
|
||||
static void amba_shutdown(struct device *dev)
|
||||
{
|
||||
struct amba_driver *drv = to_amba_driver(dev->driver);
|
||||
drv->shutdown(to_amba_device(dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* amba_driver_register - register an AMBA device driver
|
||||
* @drv: amba device driver structure
|
||||
*
|
||||
* Register an AMBA device driver with the Linux device model
|
||||
* core. If devices pre-exist, the drivers probe function will
|
||||
* be called.
|
||||
*/
|
||||
int amba_driver_register(struct amba_driver *drv)
|
||||
{
|
||||
drv->drv.bus = &amba_bustype;
|
||||
|
||||
#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
|
||||
SETFN(probe);
|
||||
SETFN(remove);
|
||||
SETFN(shutdown);
|
||||
|
||||
return driver_register(&drv->drv);
|
||||
}
|
||||
|
||||
/**
|
||||
* amba_driver_unregister - remove an AMBA device driver
|
||||
* @drv: AMBA device driver structure to remove
|
||||
*
|
||||
* Unregister an AMBA device driver from the Linux device
|
||||
* model. The device model will call the drivers remove function
|
||||
* for each device the device driver is currently handling.
|
||||
*/
|
||||
void amba_driver_unregister(struct amba_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->drv);
|
||||
}
|
||||
|
||||
|
||||
static void amba_device_release(struct device *dev)
|
||||
{
|
||||
struct amba_device *d = to_amba_device(dev);
|
||||
|
||||
if (d->res.parent)
|
||||
release_resource(&d->res);
|
||||
kfree(d);
|
||||
}
|
||||
|
||||
#define amba_attr(name,fmt,arg...) \
|
||||
static ssize_t show_##name(struct device *_dev, char *buf) \
|
||||
{ \
|
||||
struct amba_device *dev = to_amba_device(_dev); \
|
||||
return sprintf(buf, fmt, arg); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
|
||||
|
||||
amba_attr(id, "%08x\n", dev->periphid);
|
||||
amba_attr(irq0, "%u\n", dev->irq[0]);
|
||||
amba_attr(irq1, "%u\n", dev->irq[1]);
|
||||
amba_attr(resource, "\t%08lx\t%08lx\t%08lx\n",
|
||||
dev->res.start, dev->res.end, dev->res.flags);
|
||||
|
||||
/**
|
||||
* amba_device_register - register an AMBA device
|
||||
* @dev: AMBA device to register
|
||||
* @parent: parent memory resource
|
||||
*
|
||||
* Setup the AMBA device, reading the cell ID if present.
|
||||
* Claim the resource, and register the AMBA device with
|
||||
* the Linux device manager.
|
||||
*/
|
||||
int amba_device_register(struct amba_device *dev, struct resource *parent)
|
||||
{
|
||||
u32 pid, cid;
|
||||
void __iomem *tmp;
|
||||
int i, ret;
|
||||
|
||||
dev->dev.release = amba_device_release;
|
||||
dev->dev.bus = &amba_bustype;
|
||||
dev->dev.dma_mask = &dev->dma_mask;
|
||||
dev->res.name = dev->dev.bus_id;
|
||||
|
||||
if (!dev->dev.coherent_dma_mask && dev->dma_mask)
|
||||
dev_warn(&dev->dev, "coherent dma mask is unset\n");
|
||||
|
||||
ret = request_resource(parent, &dev->res);
|
||||
if (ret == 0) {
|
||||
tmp = ioremap(dev->res.start, SZ_4K);
|
||||
if (!tmp) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (pid = 0, i = 0; i < 4; i++)
|
||||
pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8);
|
||||
for (cid = 0, i = 0; i < 4; i++)
|
||||
cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8);
|
||||
|
||||
iounmap(tmp);
|
||||
|
||||
if (cid == 0xb105f00d)
|
||||
dev->periphid = pid;
|
||||
|
||||
if (dev->periphid)
|
||||
ret = device_register(&dev->dev);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
if (ret == 0) {
|
||||
device_create_file(&dev->dev, &dev_attr_id);
|
||||
if (dev->irq[0] != NO_IRQ)
|
||||
device_create_file(&dev->dev, &dev_attr_irq0);
|
||||
if (dev->irq[1] != NO_IRQ)
|
||||
device_create_file(&dev->dev, &dev_attr_irq1);
|
||||
device_create_file(&dev->dev, &dev_attr_resource);
|
||||
} else {
|
||||
out:
|
||||
release_resource(&dev->res);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* amba_device_unregister - unregister an AMBA device
|
||||
* @dev: AMBA device to remove
|
||||
*
|
||||
* Remove the specified AMBA device from the Linux device
|
||||
* manager. All files associated with this object will be
|
||||
* destroyed, and device drivers notified that the device has
|
||||
* been removed. The AMBA device's resources including
|
||||
* the amba_device structure will be freed once all
|
||||
* references to it have been dropped.
|
||||
*/
|
||||
void amba_device_unregister(struct amba_device *dev)
|
||||
{
|
||||
device_unregister(&dev->dev);
|
||||
}
|
||||
|
||||
|
||||
struct find_data {
|
||||
struct amba_device *dev;
|
||||
struct device *parent;
|
||||
const char *busid;
|
||||
unsigned int id;
|
||||
unsigned int mask;
|
||||
};
|
||||
|
||||
static int amba_find_match(struct device *dev, void *data)
|
||||
{
|
||||
struct find_data *d = data;
|
||||
struct amba_device *pcdev = to_amba_device(dev);
|
||||
int r;
|
||||
|
||||
r = (pcdev->periphid & d->mask) == d->id;
|
||||
if (d->parent)
|
||||
r &= d->parent == dev->parent;
|
||||
if (d->busid)
|
||||
r &= strcmp(dev->bus_id, d->busid) == 0;
|
||||
|
||||
if (r) {
|
||||
get_device(dev);
|
||||
d->dev = pcdev;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* amba_find_device - locate an AMBA device given a bus id
|
||||
* @busid: bus id for device (or NULL)
|
||||
* @parent: parent device (or NULL)
|
||||
* @id: peripheral ID (or 0)
|
||||
* @mask: peripheral ID mask (or 0)
|
||||
*
|
||||
* Return the AMBA device corresponding to the supplied parameters.
|
||||
* If no device matches, returns NULL.
|
||||
*
|
||||
* NOTE: When a valid device is found, its refcount is
|
||||
* incremented, and must be decremented before the returned
|
||||
* reference.
|
||||
*/
|
||||
struct amba_device *
|
||||
amba_find_device(const char *busid, struct device *parent, unsigned int id,
|
||||
unsigned int mask)
|
||||
{
|
||||
struct find_data data;
|
||||
|
||||
data.dev = NULL;
|
||||
data.parent = parent;
|
||||
data.busid = busid;
|
||||
data.id = id;
|
||||
data.mask = mask;
|
||||
|
||||
bus_for_each_dev(&amba_bustype, NULL, &data, amba_find_match);
|
||||
|
||||
return data.dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* amba_request_regions - request all mem regions associated with device
|
||||
* @dev: amba_device structure for device
|
||||
* @name: name, or NULL to use driver name
|
||||
*/
|
||||
int amba_request_regions(struct amba_device *dev, const char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!name)
|
||||
name = dev->dev.driver->name;
|
||||
|
||||
if (!request_mem_region(dev->res.start, SZ_4K, name))
|
||||
ret = -EBUSY;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* amba_release_regions - release mem regions assoicated with device
|
||||
* @dev: amba_device structure for device
|
||||
*
|
||||
* Release regions claimed by a successful call to amba_request_regions.
|
||||
*/
|
||||
void amba_release_regions(struct amba_device *dev)
|
||||
{
|
||||
release_mem_region(dev->res.start, SZ_4K);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(amba_driver_register);
|
||||
EXPORT_SYMBOL(amba_driver_unregister);
|
||||
EXPORT_SYMBOL(amba_device_register);
|
||||
EXPORT_SYMBOL(amba_device_unregister);
|
||||
EXPORT_SYMBOL(amba_find_device);
|
||||
EXPORT_SYMBOL(amba_request_regions);
|
||||
EXPORT_SYMBOL(amba_release_regions);
|
682
arch/arm/common/dmabounce.c
Normal file
682
arch/arm/common/dmabounce.c
Normal file
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* arch/arm/common/dmabounce.c
|
||||
*
|
||||
* Special dma_{map/unmap/dma_sync}_* routines for systems that have
|
||||
* limited DMA windows. These functions utilize bounce buffers to
|
||||
* copy data to/from buffers located outside the DMA region. This
|
||||
* only works for systems in which DMA memory is at the bottom of
|
||||
* RAM and the remainder of memory is at the top an the DMA memory
|
||||
* can be marked as ZONE_DMA. Anything beyond that such as discontigous
|
||||
* DMA windows will require custom implementations that reserve memory
|
||||
* areas at early bootup.
|
||||
*
|
||||
* Original version by Brad Parker (brad@heeltoe.com)
|
||||
* Re-written by Christopher Hoover <ch@murgatroid.com>
|
||||
* Made generic by Deepak Saxena <dsaxena@plexity.net>
|
||||
*
|
||||
* Copyright (C) 2002 Hewlett Packard Company.
|
||||
* Copyright (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#undef STATS
|
||||
#ifdef STATS
|
||||
#define DO_STATS(X) do { X ; } while (0)
|
||||
#else
|
||||
#define DO_STATS(X) do { } while (0)
|
||||
#endif
|
||||
|
||||
/* ************************************************** */
|
||||
|
||||
struct safe_buffer {
|
||||
struct list_head node;
|
||||
|
||||
/* original request */
|
||||
void *ptr;
|
||||
size_t size;
|
||||
int direction;
|
||||
|
||||
/* safe buffer info */
|
||||
struct dma_pool *pool;
|
||||
void *safe;
|
||||
dma_addr_t safe_dma_addr;
|
||||
};
|
||||
|
||||
struct dmabounce_device_info {
|
||||
struct list_head node;
|
||||
|
||||
struct device *dev;
|
||||
struct dma_pool *small_buffer_pool;
|
||||
struct dma_pool *large_buffer_pool;
|
||||
struct list_head safe_buffers;
|
||||
unsigned long small_buffer_size, large_buffer_size;
|
||||
#ifdef STATS
|
||||
unsigned long sbp_allocs;
|
||||
unsigned long lbp_allocs;
|
||||
unsigned long total_allocs;
|
||||
unsigned long map_op_count;
|
||||
unsigned long bounce_count;
|
||||
#endif
|
||||
};
|
||||
|
||||
static LIST_HEAD(dmabounce_devs);
|
||||
|
||||
#ifdef STATS
|
||||
static void print_alloc_stats(struct dmabounce_device_info *device_info)
|
||||
{
|
||||
printk(KERN_INFO
|
||||
"%s: dmabounce: sbp: %lu, lbp: %lu, other: %lu, total: %lu\n",
|
||||
device_info->dev->bus_id,
|
||||
device_info->sbp_allocs, device_info->lbp_allocs,
|
||||
device_info->total_allocs - device_info->sbp_allocs -
|
||||
device_info->lbp_allocs,
|
||||
device_info->total_allocs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* find the given device in the dmabounce device list */
|
||||
static inline struct dmabounce_device_info *
|
||||
find_dmabounce_dev(struct device *dev)
|
||||
{
|
||||
struct list_head *entry;
|
||||
|
||||
list_for_each(entry, &dmabounce_devs) {
|
||||
struct dmabounce_device_info *d =
|
||||
list_entry(entry, struct dmabounce_device_info, node);
|
||||
|
||||
if (d->dev == dev)
|
||||
return d;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* allocate a 'safe' buffer and keep track of it */
|
||||
static inline struct safe_buffer *
|
||||
alloc_safe_buffer(struct dmabounce_device_info *device_info, void *ptr,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
struct safe_buffer *buf;
|
||||
struct dma_pool *pool;
|
||||
struct device *dev = device_info->dev;
|
||||
void *safe;
|
||||
dma_addr_t safe_dma_addr;
|
||||
|
||||
dev_dbg(dev, "%s(ptr=%p, size=%d, dir=%d)\n",
|
||||
__func__, ptr, size, dir);
|
||||
|
||||
DO_STATS ( device_info->total_allocs++ );
|
||||
|
||||
buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
|
||||
if (buf == NULL) {
|
||||
dev_warn(dev, "%s: kmalloc failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size <= device_info->small_buffer_size) {
|
||||
pool = device_info->small_buffer_pool;
|
||||
safe = dma_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr);
|
||||
|
||||
DO_STATS ( device_info->sbp_allocs++ );
|
||||
} else if (size <= device_info->large_buffer_size) {
|
||||
pool = device_info->large_buffer_pool;
|
||||
safe = dma_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr);
|
||||
|
||||
DO_STATS ( device_info->lbp_allocs++ );
|
||||
} else {
|
||||
pool = NULL;
|
||||
safe = dma_alloc_coherent(dev, size, &safe_dma_addr, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
if (safe == NULL) {
|
||||
dev_warn(device_info->dev,
|
||||
"%s: could not alloc dma memory (size=%d)\n",
|
||||
__func__, size);
|
||||
kfree(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef STATS
|
||||
if (device_info->total_allocs % 1000 == 0)
|
||||
print_alloc_stats(device_info);
|
||||
#endif
|
||||
|
||||
buf->ptr = ptr;
|
||||
buf->size = size;
|
||||
buf->direction = dir;
|
||||
buf->pool = pool;
|
||||
buf->safe = safe;
|
||||
buf->safe_dma_addr = safe_dma_addr;
|
||||
|
||||
list_add(&buf->node, &device_info->safe_buffers);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* determine if a buffer is from our "safe" pool */
|
||||
static inline struct safe_buffer *
|
||||
find_safe_buffer(struct dmabounce_device_info *device_info, dma_addr_t safe_dma_addr)
|
||||
{
|
||||
struct list_head *entry;
|
||||
|
||||
list_for_each(entry, &device_info->safe_buffers) {
|
||||
struct safe_buffer *b =
|
||||
list_entry(entry, struct safe_buffer, node);
|
||||
|
||||
if (b->safe_dma_addr == safe_dma_addr)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf)
|
||||
{
|
||||
dev_dbg(device_info->dev, "%s(buf=%p)\n", __func__, buf);
|
||||
|
||||
list_del(&buf->node);
|
||||
|
||||
if (buf->pool)
|
||||
dma_pool_free(buf->pool, buf->safe, buf->safe_dma_addr);
|
||||
else
|
||||
dma_free_coherent(device_info->dev, buf->size, buf->safe,
|
||||
buf->safe_dma_addr);
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/* ************************************************** */
|
||||
|
||||
#ifdef STATS
|
||||
|
||||
static void print_map_stats(struct dmabounce_device_info *device_info)
|
||||
{
|
||||
printk(KERN_INFO
|
||||
"%s: dmabounce: map_op_count=%lu, bounce_count=%lu\n",
|
||||
device_info->dev->bus_id,
|
||||
device_info->map_op_count, device_info->bounce_count);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline dma_addr_t
|
||||
map_single(struct device *dev, void *ptr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
|
||||
dma_addr_t dma_addr;
|
||||
int needs_bounce = 0;
|
||||
|
||||
if (device_info)
|
||||
DO_STATS ( device_info->map_op_count++ );
|
||||
|
||||
dma_addr = virt_to_dma(dev, ptr);
|
||||
|
||||
if (dev->dma_mask) {
|
||||
unsigned long mask = *dev->dma_mask;
|
||||
unsigned long limit;
|
||||
|
||||
limit = (mask + 1) & ~mask;
|
||||
if (limit && size > limit) {
|
||||
dev_err(dev, "DMA mapping too big (requested %#x "
|
||||
"mask %#Lx)\n", size, *dev->dma_mask);
|
||||
return ~0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out if we need to bounce from the DMA mask.
|
||||
*/
|
||||
needs_bounce = (dma_addr | (dma_addr + size - 1)) & ~mask;
|
||||
}
|
||||
|
||||
if (device_info && (needs_bounce || dma_needs_bounce(dev, dma_addr, size))) {
|
||||
struct safe_buffer *buf;
|
||||
|
||||
buf = alloc_safe_buffer(device_info, ptr, size, dir);
|
||||
if (buf == 0) {
|
||||
dev_err(dev, "%s: unable to map unsafe buffer %p!\n",
|
||||
__func__, ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
|
||||
__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
|
||||
buf->safe, (void *) buf->safe_dma_addr);
|
||||
|
||||
if ((dir == DMA_TO_DEVICE) ||
|
||||
(dir == DMA_BIDIRECTIONAL)) {
|
||||
dev_dbg(dev, "%s: copy unsafe %p to safe %p, size %d\n",
|
||||
__func__, ptr, buf->safe, size);
|
||||
memcpy(buf->safe, ptr, size);
|
||||
}
|
||||
consistent_sync(buf->safe, size, dir);
|
||||
|
||||
dma_addr = buf->safe_dma_addr;
|
||||
} else {
|
||||
consistent_sync(ptr, size, dir);
|
||||
}
|
||||
|
||||
return dma_addr;
|
||||
}
|
||||
|
||||
static inline void
|
||||
unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
|
||||
struct safe_buffer *buf = NULL;
|
||||
|
||||
/*
|
||||
* Trying to unmap an invalid mapping
|
||||
*/
|
||||
if (dma_addr == ~0) {
|
||||
dev_err(dev, "Trying to unmap invalid mapping\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (device_info)
|
||||
buf = find_safe_buffer(device_info, dma_addr);
|
||||
|
||||
if (buf) {
|
||||
BUG_ON(buf->size != size);
|
||||
|
||||
dev_dbg(dev,
|
||||
"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
|
||||
__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
|
||||
buf->safe, (void *) buf->safe_dma_addr);
|
||||
|
||||
|
||||
DO_STATS ( device_info->bounce_count++ );
|
||||
|
||||
if ((dir == DMA_FROM_DEVICE) ||
|
||||
(dir == DMA_BIDIRECTIONAL)) {
|
||||
dev_dbg(dev,
|
||||
"%s: copy back safe %p to unsafe %p size %d\n",
|
||||
__func__, buf->safe, buf->ptr, size);
|
||||
memcpy(buf->ptr, buf->safe, size);
|
||||
}
|
||||
free_safe_buffer(device_info, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
sync_single(struct device *dev, dma_addr_t dma_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
|
||||
struct safe_buffer *buf = NULL;
|
||||
|
||||
if (device_info)
|
||||
buf = find_safe_buffer(device_info, dma_addr);
|
||||
|
||||
if (buf) {
|
||||
/*
|
||||
* Both of these checks from original code need to be
|
||||
* commented out b/c some drivers rely on the following:
|
||||
*
|
||||
* 1) Drivers may map a large chunk of memory into DMA space
|
||||
* but only sync a small portion of it. Good example is
|
||||
* allocating a large buffer, mapping it, and then
|
||||
* breaking it up into small descriptors. No point
|
||||
* in syncing the whole buffer if you only have to
|
||||
* touch one descriptor.
|
||||
*
|
||||
* 2) Buffers that are mapped as DMA_BIDIRECTIONAL are
|
||||
* usually only synced in one dir at a time.
|
||||
*
|
||||
* See drivers/net/eepro100.c for examples of both cases.
|
||||
*
|
||||
* -ds
|
||||
*
|
||||
* BUG_ON(buf->size != size);
|
||||
* BUG_ON(buf->direction != dir);
|
||||
*/
|
||||
|
||||
dev_dbg(dev,
|
||||
"%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n",
|
||||
__func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr),
|
||||
buf->safe, (void *) buf->safe_dma_addr);
|
||||
|
||||
DO_STATS ( device_info->bounce_count++ );
|
||||
|
||||
switch (dir) {
|
||||
case DMA_FROM_DEVICE:
|
||||
dev_dbg(dev,
|
||||
"%s: copy back safe %p to unsafe %p size %d\n",
|
||||
__func__, buf->safe, buf->ptr, size);
|
||||
memcpy(buf->ptr, buf->safe, size);
|
||||
break;
|
||||
case DMA_TO_DEVICE:
|
||||
dev_dbg(dev,
|
||||
"%s: copy out unsafe %p to safe %p, size %d\n",
|
||||
__func__,buf->ptr, buf->safe, size);
|
||||
memcpy(buf->safe, buf->ptr, size);
|
||||
break;
|
||||
case DMA_BIDIRECTIONAL:
|
||||
BUG(); /* is this allowed? what does it mean? */
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
consistent_sync(buf->safe, size, dir);
|
||||
} else {
|
||||
consistent_sync(dma_to_virt(dev, dma_addr), size, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************************************** */
|
||||
|
||||
/*
|
||||
* see if a buffer address is in an 'unsafe' range. if it is
|
||||
* allocate a 'safe' buffer and copy the unsafe buffer into it.
|
||||
* substitute the safe buffer for the unsafe one.
|
||||
* (basically move the buffer from an unsafe area to a safe one)
|
||||
*/
|
||||
dma_addr_t
|
||||
dma_map_single(struct device *dev, void *ptr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
|
||||
__func__, ptr, size, dir);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
dma_addr = map_single(dev, ptr, size, dir);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return dma_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* see if a mapped address was really a "safe" buffer and if so, copy
|
||||
* the data from the safe buffer back to the unsafe buffer and free up
|
||||
* the safe buffer. (basically return things back to the way they
|
||||
* should be)
|
||||
*/
|
||||
|
||||
void
|
||||
dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
|
||||
__func__, (void *) dma_addr, size, dir);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
unmap_single(dev, dma_addr, size, dir);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
int
|
||||
dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
|
||||
__func__, sg, nents, dir);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
for (i = 0; i < nents; i++, sg++) {
|
||||
struct page *page = sg->page;
|
||||
unsigned int offset = sg->offset;
|
||||
unsigned int length = sg->length;
|
||||
void *ptr = page_address(page) + offset;
|
||||
|
||||
sg->dma_address =
|
||||
map_single(dev, ptr, length, dir);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return nents;
|
||||
}
|
||||
|
||||
void
|
||||
dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
|
||||
__func__, sg, nents, dir);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
for (i = 0; i < nents; i++, sg++) {
|
||||
dma_addr_t dma_addr = sg->dma_address;
|
||||
unsigned int length = sg->length;
|
||||
|
||||
unmap_single(dev, dma_addr, length, dir);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void
|
||||
dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
|
||||
__func__, (void *) dma_addr, size, dir);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
sync_single(dev, dma_addr, size, dir);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void
|
||||
dma_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n",
|
||||
__func__, (void *) dma_addr, size, dir);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
sync_single(dev, dma_addr, size, dir);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void
|
||||
dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
|
||||
__func__, sg, nents, dir);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
for (i = 0; i < nents; i++, sg++) {
|
||||
dma_addr_t dma_addr = sg->dma_address;
|
||||
unsigned int length = sg->length;
|
||||
|
||||
sync_single(dev, dma_addr, length, dir);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
void
|
||||
dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n",
|
||||
__func__, sg, nents, dir);
|
||||
|
||||
BUG_ON(dir == DMA_NONE);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
for (i = 0; i < nents; i++, sg++) {
|
||||
dma_addr_t dma_addr = sg->dma_address;
|
||||
unsigned int length = sg->length;
|
||||
|
||||
sync_single(dev, dma_addr, length, dir);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
int
|
||||
dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
|
||||
unsigned long large_buffer_size)
|
||||
{
|
||||
struct dmabounce_device_info *device_info;
|
||||
|
||||
device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
|
||||
if (!device_info) {
|
||||
printk(KERN_ERR
|
||||
"Could not allocated dmabounce_device_info for %s",
|
||||
dev->bus_id);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
device_info->small_buffer_pool =
|
||||
dma_pool_create("small_dmabounce_pool",
|
||||
dev,
|
||||
small_buffer_size,
|
||||
0 /* byte alignment */,
|
||||
0 /* no page-crossing issues */);
|
||||
if (!device_info->small_buffer_pool) {
|
||||
printk(KERN_ERR
|
||||
"dmabounce: could not allocate small DMA pool for %s\n",
|
||||
dev->bus_id);
|
||||
kfree(device_info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (large_buffer_size) {
|
||||
device_info->large_buffer_pool =
|
||||
dma_pool_create("large_dmabounce_pool",
|
||||
dev,
|
||||
large_buffer_size,
|
||||
0 /* byte alignment */,
|
||||
0 /* no page-crossing issues */);
|
||||
if (!device_info->large_buffer_pool) {
|
||||
printk(KERN_ERR
|
||||
"dmabounce: could not allocate large DMA pool for %s\n",
|
||||
dev->bus_id);
|
||||
dma_pool_destroy(device_info->small_buffer_pool);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
device_info->dev = dev;
|
||||
device_info->small_buffer_size = small_buffer_size;
|
||||
device_info->large_buffer_size = large_buffer_size;
|
||||
INIT_LIST_HEAD(&device_info->safe_buffers);
|
||||
|
||||
#ifdef STATS
|
||||
device_info->sbp_allocs = 0;
|
||||
device_info->lbp_allocs = 0;
|
||||
device_info->total_allocs = 0;
|
||||
device_info->map_op_count = 0;
|
||||
device_info->bounce_count = 0;
|
||||
#endif
|
||||
|
||||
list_add(&device_info->node, &dmabounce_devs);
|
||||
|
||||
printk(KERN_INFO "dmabounce: registered device %s on %s bus\n",
|
||||
dev->bus_id, dev->bus->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
dmabounce_unregister_dev(struct device *dev)
|
||||
{
|
||||
struct dmabounce_device_info *device_info = find_dmabounce_dev(dev);
|
||||
|
||||
if (!device_info) {
|
||||
printk(KERN_WARNING
|
||||
"%s: Never registered with dmabounce but attempting" \
|
||||
"to unregister!\n", dev->bus_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!list_empty(&device_info->safe_buffers)) {
|
||||
printk(KERN_ERR
|
||||
"%s: Removing from dmabounce with pending buffers!\n",
|
||||
dev->bus_id);
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (device_info->small_buffer_pool)
|
||||
dma_pool_destroy(device_info->small_buffer_pool);
|
||||
if (device_info->large_buffer_pool)
|
||||
dma_pool_destroy(device_info->large_buffer_pool);
|
||||
|
||||
#ifdef STATS
|
||||
print_alloc_stats(device_info);
|
||||
print_map_stats(device_info);
|
||||
#endif
|
||||
|
||||
list_del(&device_info->node);
|
||||
|
||||
kfree(device_info);
|
||||
|
||||
printk(KERN_INFO "dmabounce: device %s on %s bus unregistered\n",
|
||||
dev->bus_id, dev->bus->name);
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(dma_map_single);
|
||||
EXPORT_SYMBOL(dma_unmap_single);
|
||||
EXPORT_SYMBOL(dma_map_sg);
|
||||
EXPORT_SYMBOL(dma_unmap_sg);
|
||||
EXPORT_SYMBOL(dma_sync_single);
|
||||
EXPORT_SYMBOL(dma_sync_sg);
|
||||
EXPORT_SYMBOL(dmabounce_register_dev);
|
||||
EXPORT_SYMBOL(dmabounce_unregister_dev);
|
||||
|
||||
MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>");
|
||||
MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows");
|
||||
MODULE_LICENSE("GPL");
|
161
arch/arm/common/icst307.c
Normal file
161
arch/arm/common/icst307.c
Normal 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
160
arch/arm/common/icst525.c
Normal 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);
|
1058
arch/arm/common/locomo.c
Normal file
1058
arch/arm/common/locomo.c
Normal file
File diff suppressed because it is too large
Load Diff
506
arch/arm/common/rtctime.c
Normal file
506
arch/arm/common/rtctime.c
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* linux/arch/arm/common/rtctime.c
|
||||
*
|
||||
* Copyright (C) 2003 Deep Blue Solutions Ltd.
|
||||
* Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
|
||||
* Based on rtc.c by Paul Gortmaker
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
|
||||
static struct fasync_struct *rtc_async_queue;
|
||||
|
||||
/*
|
||||
* rtc_lock protects rtc_irq_data
|
||||
*/
|
||||
static DEFINE_SPINLOCK(rtc_lock);
|
||||
static unsigned long rtc_irq_data;
|
||||
|
||||
/*
|
||||
* rtc_sem protects rtc_inuse and rtc_ops
|
||||
*/
|
||||
static DECLARE_MUTEX(rtc_sem);
|
||||
static unsigned long rtc_inuse;
|
||||
static struct rtc_ops *rtc_ops;
|
||||
|
||||
#define rtc_epoch 1900UL
|
||||
|
||||
static const unsigned char days_in_month[] = {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
|
||||
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
|
||||
#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
|
||||
|
||||
static int month_days(unsigned int month, unsigned int year)
|
||||
{
|
||||
return days_in_month[month] + (LEAP_YEAR(year) && month == 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
|
||||
*/
|
||||
void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
|
||||
{
|
||||
int days, month, year;
|
||||
|
||||
days = time / 86400;
|
||||
time -= days * 86400;
|
||||
|
||||
tm->tm_wday = (days + 4) % 7;
|
||||
|
||||
year = 1970 + days / 365;
|
||||
days -= (year - 1970) * 365
|
||||
+ LEAPS_THRU_END_OF(year - 1)
|
||||
- LEAPS_THRU_END_OF(1970 - 1);
|
||||
if (days < 0) {
|
||||
year -= 1;
|
||||
days += 365 + LEAP_YEAR(year);
|
||||
}
|
||||
tm->tm_year = year - 1900;
|
||||
tm->tm_yday = days + 1;
|
||||
|
||||
for (month = 0; month < 11; month++) {
|
||||
int newdays;
|
||||
|
||||
newdays = days - month_days(month, year);
|
||||
if (newdays < 0)
|
||||
break;
|
||||
days = newdays;
|
||||
}
|
||||
tm->tm_mon = month;
|
||||
tm->tm_mday = days + 1;
|
||||
|
||||
tm->tm_hour = time / 3600;
|
||||
time -= tm->tm_hour * 3600;
|
||||
tm->tm_min = time / 60;
|
||||
tm->tm_sec = time - tm->tm_min * 60;
|
||||
}
|
||||
EXPORT_SYMBOL(rtc_time_to_tm);
|
||||
|
||||
/*
|
||||
* Does the rtc_time represent a valid date/time?
|
||||
*/
|
||||
int rtc_valid_tm(struct rtc_time *tm)
|
||||
{
|
||||
if (tm->tm_year < 70 ||
|
||||
tm->tm_mon >= 12 ||
|
||||
tm->tm_mday < 1 ||
|
||||
tm->tm_mday > month_days(tm->tm_mon, tm->tm_year + 1900) ||
|
||||
tm->tm_hour >= 24 ||
|
||||
tm->tm_min >= 60 ||
|
||||
tm->tm_sec >= 60)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rtc_valid_tm);
|
||||
|
||||
/*
|
||||
* Convert Gregorian date to seconds since 01-01-1970 00:00:00.
|
||||
*/
|
||||
int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
|
||||
{
|
||||
*time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rtc_tm_to_time);
|
||||
|
||||
/*
|
||||
* Calculate the next alarm time given the requested alarm time mask
|
||||
* and the current time.
|
||||
*
|
||||
* FIXME: for now, we just copy the alarm time because we're lazy (and
|
||||
* is therefore buggy - setting a 10am alarm at 8pm will not result in
|
||||
* the alarm triggering.)
|
||||
*/
|
||||
void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
|
||||
{
|
||||
next->tm_year = now->tm_year;
|
||||
next->tm_mon = now->tm_mon;
|
||||
next->tm_mday = now->tm_mday;
|
||||
next->tm_hour = alrm->tm_hour;
|
||||
next->tm_min = alrm->tm_min;
|
||||
next->tm_sec = alrm->tm_sec;
|
||||
}
|
||||
|
||||
static inline void rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm)
|
||||
{
|
||||
memset(tm, 0, sizeof(struct rtc_time));
|
||||
ops->read_time(tm);
|
||||
}
|
||||
|
||||
static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtc_valid_tm(tm);
|
||||
if (ret == 0)
|
||||
ret = ops->set_time(tm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
if (ops->read_alarm) {
|
||||
memset(alrm, 0, sizeof(struct rtc_wkalrm));
|
||||
ops->read_alarm(alrm);
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
if (ops->set_alarm)
|
||||
ret = ops->set_alarm(alrm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rtc_update(unsigned long num, unsigned long events)
|
||||
{
|
||||
spin_lock(&rtc_lock);
|
||||
rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
|
||||
spin_unlock(&rtc_lock);
|
||||
|
||||
wake_up_interruptible(&rtc_wait);
|
||||
kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
|
||||
}
|
||||
EXPORT_SYMBOL(rtc_update);
|
||||
|
||||
|
||||
static ssize_t
|
||||
rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
unsigned long data;
|
||||
ssize_t ret;
|
||||
|
||||
if (count < sizeof(unsigned long))
|
||||
return -EINVAL;
|
||||
|
||||
add_wait_queue(&rtc_wait, &wait);
|
||||
do {
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
data = rtc_irq_data;
|
||||
rtc_irq_data = 0;
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
if (data != 0) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
ret = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
schedule();
|
||||
} while (1);
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&rtc_wait, &wait);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = put_user(data, (unsigned long __user *)buf);
|
||||
if (ret == 0)
|
||||
ret = sizeof(unsigned long);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int rtc_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
unsigned long data;
|
||||
|
||||
poll_wait(file, &rtc_wait, wait);
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
data = rtc_irq_data;
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
return data != 0 ? POLLIN | POLLRDNORM : 0;
|
||||
}
|
||||
|
||||
static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct rtc_ops *ops = file->private_data;
|
||||
struct rtc_time tm;
|
||||
struct rtc_wkalrm alrm;
|
||||
void __user *uarg = (void __user *)arg;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_ALM_READ:
|
||||
ret = rtc_read_alarm(ops, &alrm);
|
||||
if (ret)
|
||||
break;
|
||||
ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
|
||||
case RTC_ALM_SET:
|
||||
ret = copy_from_user(&alrm.time, uarg, sizeof(tm));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
alrm.enabled = 0;
|
||||
alrm.pending = 0;
|
||||
alrm.time.tm_mday = -1;
|
||||
alrm.time.tm_mon = -1;
|
||||
alrm.time.tm_year = -1;
|
||||
alrm.time.tm_wday = -1;
|
||||
alrm.time.tm_yday = -1;
|
||||
alrm.time.tm_isdst = -1;
|
||||
ret = rtc_set_alarm(ops, &alrm);
|
||||
break;
|
||||
|
||||
case RTC_RD_TIME:
|
||||
rtc_read_time(ops, &tm);
|
||||
ret = copy_to_user(uarg, &tm, sizeof(tm));
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
|
||||
case RTC_SET_TIME:
|
||||
if (!capable(CAP_SYS_TIME)) {
|
||||
ret = -EACCES;
|
||||
break;
|
||||
}
|
||||
ret = copy_from_user(&tm, uarg, sizeof(tm));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = rtc_set_time(ops, &tm);
|
||||
break;
|
||||
|
||||
case RTC_EPOCH_SET:
|
||||
#ifndef rtc_epoch
|
||||
/*
|
||||
* There were no RTC clocks before 1900.
|
||||
*/
|
||||
if (arg < 1900) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!capable(CAP_SYS_TIME)) {
|
||||
ret = -EACCES;
|
||||
break;
|
||||
}
|
||||
rtc_epoch = arg;
|
||||
ret = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RTC_EPOCH_READ:
|
||||
ret = put_user(rtc_epoch, (unsigned long __user *)uarg);
|
||||
break;
|
||||
|
||||
case RTC_WKALM_SET:
|
||||
ret = copy_from_user(&alrm, uarg, sizeof(alrm));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ret = rtc_set_alarm(ops, &alrm);
|
||||
break;
|
||||
|
||||
case RTC_WKALM_RD:
|
||||
ret = rtc_read_alarm(ops, &alrm);
|
||||
if (ret)
|
||||
break;
|
||||
ret = copy_to_user(uarg, &alrm, sizeof(alrm));
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ops->ioctl)
|
||||
ret = ops->ioctl(cmd, arg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
down(&rtc_sem);
|
||||
|
||||
if (rtc_inuse) {
|
||||
ret = -EBUSY;
|
||||
} else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
|
||||
ret = -ENODEV;
|
||||
} else {
|
||||
file->private_data = rtc_ops;
|
||||
|
||||
ret = rtc_ops->open ? rtc_ops->open() : 0;
|
||||
if (ret == 0) {
|
||||
spin_lock_irq(&rtc_lock);
|
||||
rtc_irq_data = 0;
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
rtc_inuse = 1;
|
||||
}
|
||||
}
|
||||
up(&rtc_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct rtc_ops *ops = file->private_data;
|
||||
|
||||
if (ops->release)
|
||||
ops->release();
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
rtc_irq_data = 0;
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
module_put(rtc_ops->owner);
|
||||
rtc_inuse = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
return fasync_helper(fd, file, on, &rtc_async_queue);
|
||||
}
|
||||
|
||||
static struct file_operations rtc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = rtc_read,
|
||||
.poll = rtc_poll,
|
||||
.ioctl = rtc_ioctl,
|
||||
.open = rtc_open,
|
||||
.release = rtc_release,
|
||||
.fasync = rtc_fasync,
|
||||
};
|
||||
|
||||
static struct miscdevice rtc_miscdev = {
|
||||
.minor = RTC_MINOR,
|
||||
.name = "rtc",
|
||||
.fops = &rtc_fops,
|
||||
};
|
||||
|
||||
|
||||
static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
||||
{
|
||||
struct rtc_ops *ops = data;
|
||||
struct rtc_wkalrm alrm;
|
||||
struct rtc_time tm;
|
||||
char *p = page;
|
||||
|
||||
rtc_read_time(ops, &tm);
|
||||
|
||||
p += sprintf(p,
|
||||
"rtc_time\t: %02d:%02d:%02d\n"
|
||||
"rtc_date\t: %04d-%02d-%02d\n"
|
||||
"rtc_epoch\t: %04lu\n",
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec,
|
||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||
rtc_epoch);
|
||||
|
||||
if (rtc_read_alarm(ops, &alrm) == 0) {
|
||||
p += sprintf(p, "alrm_time\t: ");
|
||||
if ((unsigned int)alrm.time.tm_hour <= 24)
|
||||
p += sprintf(p, "%02d:", alrm.time.tm_hour);
|
||||
else
|
||||
p += sprintf(p, "**:");
|
||||
if ((unsigned int)alrm.time.tm_min <= 59)
|
||||
p += sprintf(p, "%02d:", alrm.time.tm_min);
|
||||
else
|
||||
p += sprintf(p, "**:");
|
||||
if ((unsigned int)alrm.time.tm_sec <= 59)
|
||||
p += sprintf(p, "%02d\n", alrm.time.tm_sec);
|
||||
else
|
||||
p += sprintf(p, "**\n");
|
||||
|
||||
p += sprintf(p, "alrm_date\t: ");
|
||||
if ((unsigned int)alrm.time.tm_year <= 200)
|
||||
p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
|
||||
else
|
||||
p += sprintf(p, "****-");
|
||||
if ((unsigned int)alrm.time.tm_mon <= 11)
|
||||
p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
|
||||
else
|
||||
p += sprintf(p, "**-");
|
||||
if ((unsigned int)alrm.time.tm_mday <= 31)
|
||||
p += sprintf(p, "%02d\n", alrm.time.tm_mday);
|
||||
else
|
||||
p += sprintf(p, "**\n");
|
||||
p += sprintf(p, "alrm_wakeup\t: %s\n",
|
||||
alrm.enabled ? "yes" : "no");
|
||||
p += sprintf(p, "alrm_pending\t: %s\n",
|
||||
alrm.pending ? "yes" : "no");
|
||||
}
|
||||
|
||||
if (ops->proc)
|
||||
p += ops->proc(p);
|
||||
|
||||
return p - page;
|
||||
}
|
||||
|
||||
int register_rtc(struct rtc_ops *ops)
|
||||
{
|
||||
int ret = -EBUSY;
|
||||
|
||||
down(&rtc_sem);
|
||||
if (rtc_ops == NULL) {
|
||||
rtc_ops = ops;
|
||||
|
||||
ret = misc_register(&rtc_miscdev);
|
||||
if (ret == 0)
|
||||
create_proc_read_entry("driver/rtc", 0, NULL,
|
||||
rtc_read_proc, ops);
|
||||
}
|
||||
up(&rtc_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(register_rtc);
|
||||
|
||||
void unregister_rtc(struct rtc_ops *rtc)
|
||||
{
|
||||
down(&rtc_sem);
|
||||
if (rtc == rtc_ops) {
|
||||
remove_proc_entry("driver/rtc", NULL);
|
||||
misc_deregister(&rtc_miscdev);
|
||||
rtc_ops = NULL;
|
||||
}
|
||||
up(&rtc_sem);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_rtc);
|
1292
arch/arm/common/sa1111.c
Normal file
1292
arch/arm/common/sa1111.c
Normal file
File diff suppressed because it is too large
Load Diff
176
arch/arm/common/scoop.c
Normal file
176
arch/arm/common/scoop.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Support code for the SCOOP interface found on various Sharp PDAs
|
||||
*
|
||||
* Copyright (c) 2004 Richard Purdie
|
||||
*
|
||||
* Based on code written by Sharp/Lineo for 2.4 kernels
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware/scoop.h>
|
||||
|
||||
#define SCOOP_REG(d,adr) (*(volatile unsigned short*)(d +(adr)))
|
||||
|
||||
struct scoop_dev {
|
||||
void *base;
|
||||
spinlock_t scoop_lock;
|
||||
u32 scoop_gpwr;
|
||||
};
|
||||
|
||||
void reset_scoop(struct device *dev)
|
||||
{
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
|
||||
SCOOP_REG(sdev->base,SCOOP_MCR) = 0x0100; // 00
|
||||
SCOOP_REG(sdev->base,SCOOP_CDR) = 0x0000; // 04
|
||||
SCOOP_REG(sdev->base,SCOOP_CPR) = 0x0000; // 0C
|
||||
SCOOP_REG(sdev->base,SCOOP_CCR) = 0x0000; // 10
|
||||
SCOOP_REG(sdev->base,SCOOP_IMR) = 0x0000; // 18
|
||||
SCOOP_REG(sdev->base,SCOOP_IRM) = 0x00FF; // 14
|
||||
SCOOP_REG(sdev->base,SCOOP_ISR) = 0x0000; // 1C
|
||||
SCOOP_REG(sdev->base,SCOOP_IRM) = 0x0000;
|
||||
}
|
||||
|
||||
unsigned short set_scoop_gpio(struct device *dev, unsigned short bit)
|
||||
{
|
||||
unsigned short gpio_bit;
|
||||
unsigned long flag;
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
|
||||
spin_lock_irqsave(&sdev->scoop_lock, flag);
|
||||
gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) | bit;
|
||||
SCOOP_REG(sdev->base, SCOOP_GPWR) = gpio_bit;
|
||||
spin_unlock_irqrestore(&sdev->scoop_lock, flag);
|
||||
|
||||
return gpio_bit;
|
||||
}
|
||||
|
||||
unsigned short reset_scoop_gpio(struct device *dev, unsigned short bit)
|
||||
{
|
||||
unsigned short gpio_bit;
|
||||
unsigned long flag;
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
|
||||
spin_lock_irqsave(&sdev->scoop_lock, flag);
|
||||
gpio_bit = SCOOP_REG(sdev->base, SCOOP_GPWR) & ~bit;
|
||||
SCOOP_REG(sdev->base,SCOOP_GPWR) = gpio_bit;
|
||||
spin_unlock_irqrestore(&sdev->scoop_lock, flag);
|
||||
|
||||
return gpio_bit;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(set_scoop_gpio);
|
||||
EXPORT_SYMBOL(reset_scoop_gpio);
|
||||
|
||||
unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
|
||||
{
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
return SCOOP_REG(sdev->base,reg);
|
||||
}
|
||||
|
||||
void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
|
||||
{
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
SCOOP_REG(sdev->base,reg)=data;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(reset_scoop);
|
||||
EXPORT_SYMBOL(read_scoop_reg);
|
||||
EXPORT_SYMBOL(write_scoop_reg);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int scoop_suspend(struct device *dev, uint32_t state, uint32_t level)
|
||||
{
|
||||
if (level == SUSPEND_POWER_DOWN) {
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
|
||||
sdev->scoop_gpwr = SCOOP_REG(sdev->base,SCOOP_GPWR);
|
||||
SCOOP_REG(sdev->base,SCOOP_GPWR) = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scoop_resume(struct device *dev, uint32_t level)
|
||||
{
|
||||
if (level == RESUME_POWER_ON) {
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
|
||||
SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define scoop_suspend NULL
|
||||
#define scoop_resume NULL
|
||||
#endif
|
||||
|
||||
int __init scoop_probe(struct device *dev)
|
||||
{
|
||||
struct scoop_dev *devptr;
|
||||
struct scoop_config *inf;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
if (!mem)
|
||||
return -EINVAL;
|
||||
|
||||
devptr = kmalloc(sizeof(struct scoop_dev), GFP_KERNEL);
|
||||
|
||||
if (!devptr)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(devptr, 0, sizeof(struct scoop_dev));
|
||||
spin_lock_init(&devptr->scoop_lock);
|
||||
|
||||
inf = dev->platform_data;
|
||||
devptr->base = ioremap(mem->start, mem->end - mem->start + 1);
|
||||
|
||||
if (!devptr->base) {
|
||||
kfree(devptr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, devptr);
|
||||
|
||||
printk("Sharp Scoop Device found at 0x%08x -> 0x%08x\n",(unsigned int)mem->start,(unsigned int)devptr->base);
|
||||
|
||||
SCOOP_REG(devptr->base, SCOOP_MCR) = 0x0140;
|
||||
reset_scoop(dev);
|
||||
SCOOP_REG(devptr->base, SCOOP_GPCR) = inf->io_dir & 0xffff;
|
||||
SCOOP_REG(devptr->base, SCOOP_GPWR) = inf->io_out & 0xffff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scoop_remove(struct device *dev)
|
||||
{
|
||||
struct scoop_dev *sdev = dev_get_drvdata(dev);
|
||||
if (sdev) {
|
||||
iounmap(sdev->base);
|
||||
kfree(sdev);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver scoop_driver = {
|
||||
.name = "sharp-scoop",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = scoop_probe,
|
||||
.remove = scoop_remove,
|
||||
.suspend = scoop_suspend,
|
||||
.resume = scoop_resume,
|
||||
};
|
||||
|
||||
int __init scoop_init(void)
|
||||
{
|
||||
return driver_register(&scoop_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(scoop_init);
|
60
arch/arm/common/sharpsl_param.c
Normal file
60
arch/arm/common/sharpsl_param.c
Normal 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 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;
|
||||
}
|
||||
|
||||
|
96
arch/arm/common/time-acorn.c
Normal file
96
arch/arm/common/time-acorn.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* linux/arch/arm/common/time-acorn.c
|
||||
*
|
||||
* Copyright (c) 1996-2000 Russell King.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Changelog:
|
||||
* 24-Sep-1996 RMK Created
|
||||
* 10-Oct-1996 RMK Brought up to date with arch-sa110eval
|
||||
* 04-Dec-1997 RMK Updated for new arch/arm/time.c
|
||||
* 13=Jun-2004 DS Moved to arch/arm/common b/c shared w/CLPS7500
|
||||
*/
|
||||
#include <linux/timex.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware/ioc.h>
|
||||
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
unsigned long ioc_timer_gettimeoffset(void)
|
||||
{
|
||||
unsigned int count1, count2, status;
|
||||
long offset;
|
||||
|
||||
ioc_writeb (0, IOC_T0LATCH);
|
||||
barrier ();
|
||||
count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
||||
barrier ();
|
||||
status = ioc_readb(IOC_IRQREQA);
|
||||
barrier ();
|
||||
ioc_writeb (0, IOC_T0LATCH);
|
||||
barrier ();
|
||||
count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
||||
|
||||
offset = count2;
|
||||
if (count2 < count1) {
|
||||
/*
|
||||
* We have not had an interrupt between reading count1
|
||||
* and count2.
|
||||
*/
|
||||
if (status & (1 << 5))
|
||||
offset -= LATCH;
|
||||
} else if (count2 > count1) {
|
||||
/*
|
||||
* We have just had another interrupt between reading
|
||||
* count1 and count2.
|
||||
*/
|
||||
offset -= LATCH;
|
||||
}
|
||||
|
||||
offset = (LATCH - offset) * (tick_nsec / 1000);
|
||||
return (offset + LATCH/2) / LATCH;
|
||||
}
|
||||
|
||||
void __init ioctime_init(void)
|
||||
{
|
||||
ioc_writeb(LATCH & 255, IOC_T0LTCHL);
|
||||
ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
|
||||
ioc_writeb(0, IOC_T0GO);
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
ioc_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
write_seqlock(&xtime_lock);
|
||||
timer_tick(regs);
|
||||
write_sequnlock(&xtime_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irqaction ioc_timer_irq = {
|
||||
.name = "timer",
|
||||
.flags = SA_INTERRUPT,
|
||||
.handler = ioc_timer_interrupt
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up timer interrupt.
|
||||
*/
|
||||
static void __init ioc_timer_init(void)
|
||||
{
|
||||
ioctime_init();
|
||||
setup_irq(IRQ_TIMER, &ioc_timer_irq);
|
||||
}
|
||||
|
||||
struct sys_timer ioc_timer = {
|
||||
.init = ioc_timer_init,
|
||||
.offset = ioc_timer_gettimeoffset,
|
||||
};
|
||||
|
94
arch/arm/common/via82c505.c
Normal file
94
arch/arm/common/via82c505.c
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#include <asm/mach/pci.h>
|
||||
|
||||
#define MAX_SLOTS 7
|
||||
|
||||
#define CONFIG_CMD(bus, devfn, where) (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
|
||||
|
||||
static int
|
||||
via82c505_read_config(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
int size, u32 *value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus,devfn,where),0xCF8);
|
||||
switch (size) {
|
||||
case 1:
|
||||
*value=inb(0xCFC + (where&3));
|
||||
break;
|
||||
case 2:
|
||||
*value=inw(0xCFC + (where&2));
|
||||
break;
|
||||
case 4:
|
||||
*value=inl(0xCFC);
|
||||
break;
|
||||
}
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static int
|
||||
via82c505_write_config(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
int size, u32 value)
|
||||
{
|
||||
outl(CONFIG_CMD(bus,devfn,where),0xCF8);
|
||||
switch (size) {
|
||||
case 1:
|
||||
outb(value, 0xCFC + (where&3));
|
||||
break;
|
||||
case 2:
|
||||
outw(value, 0xCFC + (where&2));
|
||||
break;
|
||||
case 4:
|
||||
outl(value, 0xCFC);
|
||||
break;
|
||||
}
|
||||
return PCIBIOS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
static struct pci_ops via82c505_ops = {
|
||||
.read = via82c505_read_config,
|
||||
.write = via82c505_write_config,
|
||||
};
|
||||
|
||||
void __init via82c505_preinit(void)
|
||||
{
|
||||
printk(KERN_DEBUG "PCI: VIA 82c505\n");
|
||||
if (!request_region(0xA8,2,"via config")) {
|
||||
printk(KERN_WARNING"VIA 82c505: Unable to request region 0xA8\n");
|
||||
return;
|
||||
}
|
||||
if (!request_region(0xCF8,8,"pci config")) {
|
||||
printk(KERN_WARNING"VIA 82c505: Unable to request region 0xCF8\n");
|
||||
release_region(0xA8, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable compatible Mode */
|
||||
outb(0x96,0xA8);
|
||||
outb(0x18,0xA9);
|
||||
outb(0x93,0xA8);
|
||||
outb(0xd0,0xA9);
|
||||
|
||||
}
|
||||
|
||||
int __init via82c505_setup(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
return (nr == 0);
|
||||
}
|
||||
|
||||
struct pci_bus * __init via82c505_scan_bus(int nr, struct pci_sys_data *sysdata)
|
||||
{
|
||||
if (nr == 0)
|
||||
return pci_scan_bus(0, &via82c505_ops, sysdata);
|
||||
|
||||
return NULL;
|
||||
}
|
Reference in New Issue
Block a user