Commit f99d18fe authored by Russell King's avatar Russell King

[ARM] Update AMBA device/driver support

- Add methods to request/release regions.
- Add method to find AMBA devices.
- Only register devices whose ID is known.
- Devices may have two interrupt signals.
- Add hotplug method.
parent 1baafe58
......@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware/amba.h>
#include <asm/sizes.h>
......@@ -41,6 +42,23 @@ static int amba_match(struct device *dev, struct device_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=%08lx", pcdev->periphid);
*envp++ = buf;
*envp++ = NULL;
return 0;
}
#else
#define amba_hotplug NULL
#endif
static int amba_suspend(struct device *dev, u32 state)
{
struct amba_driver *drv = to_amba_driver(dev->driver);
......@@ -68,6 +86,7 @@ static int amba_resume(struct device *dev)
static struct bus_type amba_bustype = {
.name = "amba",
.match = amba_match,
.hotplug = amba_hotplug,
.suspend = amba_suspend,
.resume = amba_resume,
};
......@@ -149,27 +168,19 @@ static void amba_device_release(struct device *dev)
kfree(d);
}
static ssize_t show_id(struct device *_dev, char *buf)
{
struct amba_device *dev = to_amba_device(_dev);
return sprintf(buf, "%08x\n", dev->periphid);
}
static DEVICE_ATTR(id, S_IRUGO, show_id, NULL);
static ssize_t show_irq(struct device *_dev, char *buf)
{
struct amba_device *dev = to_amba_device(_dev);
return sprintf(buf, "%u\n", dev->irq);
}
static DEVICE_ATTR(irq, S_IRUGO, show_irq, NULL);
static ssize_t show_res(struct device *_dev, char *buf)
{
struct amba_device *dev = to_amba_device(_dev);
return sprintf(buf, "\t%08lx\t%08lx\t%08lx\n",
#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);
}
static DEVICE_ATTR(resource, S_IRUGO, show_res, NULL);
/**
* amba_device_register - register an AMBA device
......@@ -208,10 +219,17 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
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);
device_create_file(&dev->dev, &dev_attr_irq);
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:
......@@ -237,7 +255,85 @@ 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;
}
int amba_request_regions(struct amba_device *dev, const char *name)
{
int ret = 0;
if (!request_mem_region(dev->res.start, SZ_4K, name))
ret = -EBUSY;
return ret;
}
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);
......@@ -25,7 +25,7 @@ static struct amba_device rtc_device = {
.end = INTEGRATOR_RTC_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
.irq = IRQ_RTCINT,
.irq = { IRQ_RTCINT, NO_IRQ },
.periphid = 0x00041030,
};
......@@ -38,7 +38,7 @@ static struct amba_device uart0_device = {
.end = INTEGRATOR_UART0_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
.irq = IRQ_UARTINT0,
.irq = { IRQ_UARTINT0, NO_IRQ },
.periphid = 0x0041010,
};
......@@ -51,7 +51,7 @@ static struct amba_device uart1_device = {
.end = INTEGRATOR_UART1_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
.irq = IRQ_UARTINT1,
.irq = { IRQ_UARTINT1, NO_IRQ },
.periphid = 0x0041010,
};
......@@ -64,7 +64,7 @@ static struct amba_device kmi0_device = {
.end = KMI0_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
.irq = IRQ_KMIINT0,
.irq = { IRQ_KMIINT0, NO_IRQ },
.periphid = 0x00041050,
};
......@@ -77,7 +77,7 @@ static struct amba_device kmi1_device = {
.end = KMI1_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
.irq = IRQ_KMIINT1,
.irq = { IRQ_KMIINT1, NO_IRQ },
.periphid = 0x00041050,
};
......
......@@ -188,7 +188,8 @@ static int impd1_probe(struct lm_device *dev)
d->res.start = dev->resource.start + idev->offset;
d->res.end = d->res.start + SZ_4K - 1;
d->res.flags = IORESOURCE_MEM;
d->irq = dev->irq;
d->irq[0] = dev->irq;
d->irq[1] = dev->irq;
d->periphid = idev->id;
ret = amba_device_register(d, &dev->resource);
......
......@@ -10,11 +10,13 @@
#ifndef ASMARM_AMBA_H
#define ASMARM_AMBA_H
#define AMBA_NR_IRQS 2
struct amba_device {
struct device dev;
struct resource res;
unsigned int irq;
unsigned int periphid;
unsigned int irq[AMBA_NR_IRQS];
};
struct amba_id {
......@@ -40,5 +42,13 @@ int amba_driver_register(struct amba_driver *);
void amba_driver_unregister(struct amba_driver *);
int amba_device_register(struct amba_device *, struct resource *);
void amba_device_unregister(struct amba_device *);
struct amba_device *amba_find_device(const char *, struct device *, unsigned int, unsigned int);
int amba_request_regions(struct amba_device *, const char *);
void amba_release_regions(struct amba_device *);
#define amba_config(d) (((d)->periphid >> 24) & 0xff)
#define amba_rev(d) (((d)->periphid >> 20) & 0x0f)
#define amba_manf(d) (((d)->periphid >> 12) & 0xff)
#define amba_part(d) ((d)->periphid & 0xfff)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment