linux/arch/arm/common/amba.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/common/amba.c
   3 *
   4 *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/device.h>
  13
  14#include <asm/io.h>
  15#include <asm/irq.h>
  16#include <asm/hardware/amba.h>
  17#include <asm/sizes.h>
  18
  19#define to_amba_device(d)       container_of(d, struct amba_device, dev)
  20#define to_amba_driver(d)       container_of(d, struct amba_driver, drv)
  21
  22static struct amba_id *
  23amba_lookup(struct amba_id *table, struct amba_device *dev)
  24{
  25        int ret = 0;
  26
  27        while (table->mask) {
  28                ret = (dev->periphid & table->mask) == table->id;
  29                if (ret)
  30                        break;
  31                table++;
  32        }
  33
  34        return ret ? table : NULL;
  35}
  36
  37static int amba_match(struct device *dev, struct device_driver *drv)
  38{
  39        struct amba_device *pcdev = to_amba_device(dev);
  40        struct amba_driver *pcdrv = to_amba_driver(drv);
  41
  42        return amba_lookup(pcdrv->id_table, pcdev) != NULL;
  43}
  44
  45#ifdef CONFIG_HOTPLUG
  46static int amba_hotplug(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
  47{
  48        struct amba_device *pcdev = to_amba_device(dev);
  49
  50        if (nr_env < 2)
  51                return -ENOMEM;
  52
  53        snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid);
  54        *envp++ = buf;
  55        *envp++ = NULL;
  56        return 0;
  57}
  58#else
  59#define amba_hotplug NULL
  60#endif
  61
  62static int amba_suspend(struct device *dev, pm_message_t state)
  63{
  64        struct amba_driver *drv = to_amba_driver(dev->driver);
  65        int ret = 0;
  66
  67        if (dev->driver && drv->suspend)
  68                ret = drv->suspend(to_amba_device(dev), state);
  69        return ret;
  70}
  71
  72static int amba_resume(struct device *dev)
  73{
  74        struct amba_driver *drv = to_amba_driver(dev->driver);
  75        int ret = 0;
  76
  77        if (dev->driver && drv->resume)
  78                ret = drv->resume(to_amba_device(dev));
  79        return ret;
  80}
  81
  82/*
  83 * Primecells are part of the Advanced Microcontroller Bus Architecture,
  84 * so we call the bus "amba".
  85 */
  86static struct bus_type amba_bustype = {
  87        .name           = "amba",
  88        .match          = amba_match,
  89        .hotplug        = amba_hotplug,
  90        .suspend        = amba_suspend,
  91        .resume         = amba_resume,
  92};
  93
  94static int __init amba_init(void)
  95{
  96        return bus_register(&amba_bustype);
  97}
  98
  99postcore_initcall(amba_init);
 100
 101/*
 102 * These are the device model conversion veneers; they convert the
 103 * device model structures to our more specific structures.
 104 */
 105static int amba_probe(struct device *dev)
 106{
 107        struct amba_device *pcdev = to_amba_device(dev);
 108        struct amba_driver *pcdrv = to_amba_driver(dev->driver);
 109        struct amba_id *id;
 110
 111        id = amba_lookup(pcdrv->id_table, pcdev);
 112
 113        return pcdrv->probe(pcdev, id);
 114}
 115
 116static int amba_remove(struct device *dev)
 117{
 118        struct amba_driver *drv = to_amba_driver(dev->driver);
 119        return drv->remove(to_amba_device(dev));
 120}
 121
 122static void amba_shutdown(struct device *dev)
 123{
 124        struct amba_driver *drv = to_amba_driver(dev->driver);
 125        drv->shutdown(to_amba_device(dev));
 126}
 127
 128/**
 129 *      amba_driver_register - register an AMBA device driver
 130 *      @drv: amba device driver structure
 131 *
 132 *      Register an AMBA device driver with the Linux device model
 133 *      core.  If devices pre-exist, the drivers probe function will
 134 *      be called.
 135 */
 136int amba_driver_register(struct amba_driver *drv)
 137{
 138        drv->drv.bus = &amba_bustype;
 139
 140#define SETFN(fn)       if (drv->fn) drv->drv.fn = amba_##fn
 141        SETFN(probe);
 142        SETFN(remove);
 143        SETFN(shutdown);
 144
 145        return driver_register(&drv->drv);
 146}
 147
 148/**
 149 *      amba_driver_unregister - remove an AMBA device driver
 150 *      @drv: AMBA device driver structure to remove
 151 *
 152 *      Unregister an AMBA device driver from the Linux device
 153 *      model.  The device model will call the drivers remove function
 154 *      for each device the device driver is currently handling.
 155 */
 156void amba_driver_unregister(struct amba_driver *drv)
 157{
 158        driver_unregister(&drv->drv);
 159}
 160
 161
 162static void amba_device_release(struct device *dev)
 163{
 164        struct amba_device *d = to_amba_device(dev);
 165
 166        if (d->res.parent)
 167                release_resource(&d->res);
 168        kfree(d);
 169}
 170
 171#define amba_attr(name,fmt,arg...)                              \
 172static ssize_t show_##name(struct device *_dev, char *buf)      \
 173{                                                               \
 174        struct amba_device *dev = to_amba_device(_dev);         \
 175        return sprintf(buf, fmt, arg);                          \
 176}                                                               \
 177static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
 178
 179amba_attr(id, "%08x\n", dev->periphid);
 180amba_attr(irq0, "%u\n", dev->irq[0]);
 181amba_attr(irq1, "%u\n", dev->irq[1]);
 182amba_attr(resource, "\t%08lx\t%08lx\t%08lx\n",
 183          dev->res.start, dev->res.end, dev->res.flags);
 184
 185/**
 186 *      amba_device_register - register an AMBA device
 187 *      @dev: AMBA device to register
 188 *      @parent: parent memory resource
 189 *
 190 *      Setup the AMBA device, reading the cell ID if present.
 191 *      Claim the resource, and register the AMBA device with
 192 *      the Linux device manager.
 193 */
 194int amba_device_register(struct amba_device *dev, struct resource *parent)
 195{
 196        u32 pid, cid;
 197        void __iomem *tmp;
 198        int i, ret;
 199
 200        dev->dev.release = amba_device_release;
 201        dev->dev.bus = &amba_bustype;
 202        dev->dev.dma_mask = &dev->dma_mask;
 203        dev->res.name = dev->dev.bus_id;
 204
 205        if (!dev->dev.coherent_dma_mask && dev->dma_mask)
 206                dev_warn(&dev->dev, "coherent dma mask is unset\n");
 207
 208        ret = request_resource(parent, &dev->res);
 209        if (ret == 0) {
 210                tmp = ioremap(dev->res.start, SZ_4K);
 211                if (!tmp) {
 212                        ret = -ENOMEM;
 213                        goto out;
 214                }
 215
 216                for (pid = 0, i = 0; i < 4; i++)
 217                        pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8);
 218                for (cid = 0, i = 0; i < 4; i++)
 219                        cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8);
 220
 221                iounmap(tmp);
 222
 223                if (cid == 0xb105f00d)
 224                        dev->periphid = pid;
 225
 226                if (dev->periphid)
 227                        ret = device_register(&dev->dev);
 228                else
 229                        ret = -ENODEV;
 230
 231                if (ret == 0) {
 232                        device_create_file(&dev->dev, &dev_attr_id);
 233                        if (dev->irq[0] != NO_IRQ)
 234                                device_create_file(&dev->dev, &dev_attr_irq0);
 235                        if (dev->irq[1] != NO_IRQ)
 236                                device_create_file(&dev->dev, &dev_attr_irq1);
 237                        device_create_file(&dev->dev, &dev_attr_resource);
 238                } else {
 239 out:
 240                        release_resource(&dev->res);
 241                }
 242        }
 243        return ret;
 244}
 245
 246/**
 247 *      amba_device_unregister - unregister an AMBA device
 248 *      @dev: AMBA device to remove
 249 *
 250 *      Remove the specified AMBA device from the Linux device
 251 *      manager.  All files associated with this object will be
 252 *      destroyed, and device drivers notified that the device has
 253 *      been removed.  The AMBA device's resources including
 254 *      the amba_device structure will be freed once all
 255 *      references to it have been dropped.
 256 */
 257void amba_device_unregister(struct amba_device *dev)
 258{
 259        device_unregister(&dev->dev);
 260}
 261
 262
 263struct find_data {
 264        struct amba_device *dev;
 265        struct device *parent;
 266        const char *busid;
 267        unsigned int id;
 268        unsigned int mask;
 269};
 270
 271static int amba_find_match(struct device *dev, void *data)
 272{
 273        struct find_data *d = data;
 274        struct amba_device *pcdev = to_amba_device(dev);
 275        int r;
 276
 277        r = (pcdev->periphid & d->mask) == d->id;
 278        if (d->parent)
 279                r &= d->parent == dev->parent;
 280        if (d->busid)
 281                r &= strcmp(dev->bus_id, d->busid) == 0;
 282
 283        if (r) {
 284                get_device(dev);
 285                d->dev = pcdev;
 286        }
 287
 288        return r;
 289}
 290
 291/**
 292 *      amba_find_device - locate an AMBA device given a bus id
 293 *      @busid: bus id for device (or NULL)
 294 *      @parent: parent device (or NULL)
 295 *      @id: peripheral ID (or 0)
 296 *      @mask: peripheral ID mask (or 0)
 297 *
 298 *      Return the AMBA device corresponding to the supplied parameters.
 299 *      If no device matches, returns NULL.
 300 *
 301 *      NOTE: When a valid device is found, its refcount is
 302 *      incremented, and must be decremented before the returned
 303 *      reference.
 304 */
 305struct amba_device *
 306amba_find_device(const char *busid, struct device *parent, unsigned int id,
 307                 unsigned int mask)
 308{
 309        struct find_data data;
 310
 311        data.dev = NULL;
 312        data.parent = parent;
 313        data.busid = busid;
 314        data.id = id;
 315        data.mask = mask;
 316
 317        bus_for_each_dev(&amba_bustype, NULL, &data, amba_find_match);
 318
 319        return data.dev;
 320}
 321
 322/**
 323 *      amba_request_regions - request all mem regions associated with device
 324 *      @dev: amba_device structure for device
 325 *      @name: name, or NULL to use driver name
 326 */
 327int amba_request_regions(struct amba_device *dev, const char *name)
 328{
 329        int ret = 0;
 330
 331        if (!name)
 332                name = dev->dev.driver->name;
 333
 334        if (!request_mem_region(dev->res.start, SZ_4K, name))
 335                ret = -EBUSY;
 336
 337        return ret;
 338}
 339
 340/**
 341 *      amba_release_regions - release mem regions assoicated with device
 342 *      @dev: amba_device structure for device
 343 *
 344 *      Release regions claimed by a successful call to amba_request_regions.
 345 */
 346void amba_release_regions(struct amba_device *dev)
 347{
 348        release_mem_region(dev->res.start, SZ_4K);
 349}
 350
 351EXPORT_SYMBOL(amba_driver_register);
 352EXPORT_SYMBOL(amba_driver_unregister);
 353EXPORT_SYMBOL(amba_device_register);
 354EXPORT_SYMBOL(amba_device_unregister);
 355EXPORT_SYMBOL(amba_find_device);
 356EXPORT_SYMBOL(amba_request_regions);
 357EXPORT_SYMBOL(amba_release_regions);
 358
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.