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