linux/drivers/uwb/umc-bus.c
<<
>>
Prefs
   1/*
   2 * Bus for UWB Multi-interface Controller capabilities.
   3 *
   4 * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
   5 *
   6 * This file is released under the GNU GPL v2.
   7 */
   8#include <linux/kernel.h>
   9#include <linux/sysfs.h>
  10#include <linux/workqueue.h>
  11#include <linux/uwb/umc.h>
  12#include <linux/pci.h>
  13
  14static int umc_bus_unbind_helper(struct device *dev, void *data)
  15{
  16        struct device *parent = data;
  17
  18        if (dev->parent == parent && dev->driver)
  19                device_release_driver(dev);
  20        return 0;
  21}
  22
  23/**
  24 * umc_controller_reset - reset the whole UMC controller
  25 * @umc: the UMC device for the radio controller.
  26 *
  27 * Drivers will be unbound from all UMC devices belonging to the
  28 * controller and then the radio controller will be rebound.  The
  29 * radio controller is expected to do a full hardware reset when it is
  30 * probed.
  31 *
  32 * If this is called while a probe() or remove() is in progress it
  33 * will return -EAGAIN and not perform the reset.
  34 */
  35int umc_controller_reset(struct umc_dev *umc)
  36{
  37        struct device *parent = umc->dev.parent;
  38        int ret;
  39
  40        if (down_trylock(&parent->sem))
  41                return -EAGAIN;
  42        bus_for_each_dev(&umc_bus_type, NULL, parent, umc_bus_unbind_helper);
  43        ret = device_attach(&umc->dev);
  44        if (ret == 1)
  45                ret = 0;
  46        up(&parent->sem);
  47
  48        return ret;
  49}
  50EXPORT_SYMBOL_GPL(umc_controller_reset);
  51
  52/**
  53 * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device.
  54 * @umc_drv: umc driver with match_data pointing to a zero-terminated
  55 * table of pci_device_id's.
  56 * @umc: umc device whose parent is to be matched.
  57 */
  58int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)
  59{
  60        const struct pci_device_id *id_table = umc_drv->match_data;
  61        struct pci_dev *pci;
  62
  63        if (umc->dev.parent->bus != &pci_bus_type)
  64                return 0;
  65
  66        pci = to_pci_dev(umc->dev.parent);
  67        return pci_match_id(id_table, pci) != NULL;
  68}
  69EXPORT_SYMBOL_GPL(umc_match_pci_id);
  70
  71static int umc_bus_rescan_helper(struct device *dev, void *data)
  72{
  73        int ret = 0;
  74
  75        if (!dev->driver)
  76                ret = device_attach(dev);
  77
  78        return ret < 0 ? ret : 0;
  79}
  80
  81static void umc_bus_rescan(void)
  82{
  83        int err;
  84
  85        /*
  86         * We can't use bus_rescan_devices() here as it deadlocks when
  87         * it tries to retake the dev->parent semaphore.
  88         */
  89        err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper);
  90        if (err < 0)
  91                printk(KERN_WARNING "%s: rescan of bus failed: %d\n",
  92                       KBUILD_MODNAME, err);
  93}
  94
  95static int umc_bus_match(struct device *dev, struct device_driver *drv)
  96{
  97        struct umc_dev *umc = to_umc_dev(dev);
  98        struct umc_driver *umc_driver = to_umc_driver(drv);
  99
 100        if (umc->cap_id == umc_driver->cap_id) {
 101                if (umc_driver->match)
 102                        return umc_driver->match(umc_driver, umc);
 103                else
 104                        return 1;
 105        }
 106        return 0;
 107}
 108
 109static int umc_device_probe(struct device *dev)
 110{
 111        struct umc_dev *umc;
 112        struct umc_driver *umc_driver;
 113        int err;
 114
 115        umc_driver = to_umc_driver(dev->driver);
 116        umc = to_umc_dev(dev);
 117
 118        get_device(dev);
 119        err = umc_driver->probe(umc);
 120        if (err)
 121                put_device(dev);
 122        else
 123                umc_bus_rescan();
 124
 125        return err;
 126}
 127
 128static int umc_device_remove(struct device *dev)
 129{
 130        struct umc_dev *umc;
 131        struct umc_driver *umc_driver;
 132
 133        umc_driver = to_umc_driver(dev->driver);
 134        umc = to_umc_dev(dev);
 135
 136        umc_driver->remove(umc);
 137        put_device(dev);
 138        return 0;
 139}
 140
 141static int umc_device_suspend(struct device *dev, pm_message_t state)
 142{
 143        struct umc_dev *umc;
 144        struct umc_driver *umc_driver;
 145        int err = 0;
 146
 147        umc = to_umc_dev(dev);
 148
 149        if (dev->driver) {
 150                umc_driver = to_umc_driver(dev->driver);
 151                if (umc_driver->suspend)
 152                        err = umc_driver->suspend(umc, state);
 153        }
 154        return err;
 155}
 156
 157static int umc_device_resume(struct device *dev)
 158{
 159        struct umc_dev *umc;
 160        struct umc_driver *umc_driver;
 161        int err = 0;
 162
 163        umc = to_umc_dev(dev);
 164
 165        if (dev->driver) {
 166                umc_driver = to_umc_driver(dev->driver);
 167                if (umc_driver->resume)
 168                        err = umc_driver->resume(umc);
 169        }
 170        return err;
 171}
 172
 173static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf)
 174{
 175        struct umc_dev *umc = to_umc_dev(dev);
 176
 177        return sprintf(buf, "0x%02x\n", umc->cap_id);
 178}
 179
 180static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)
 181{
 182        struct umc_dev *umc = to_umc_dev(dev);
 183
 184        return sprintf(buf, "0x%04x\n", umc->version);
 185}
 186
 187static struct device_attribute umc_dev_attrs[] = {
 188        __ATTR_RO(capability_id),
 189        __ATTR_RO(version),
 190        __ATTR_NULL,
 191};
 192
 193struct bus_type umc_bus_type = {
 194        .name           = "umc",
 195        .match          = umc_bus_match,
 196        .probe          = umc_device_probe,
 197        .remove         = umc_device_remove,
 198        .suspend        = umc_device_suspend,
 199        .resume         = umc_device_resume,
 200        .dev_attrs      = umc_dev_attrs,
 201};
 202EXPORT_SYMBOL_GPL(umc_bus_type);
 203
 204static int __init umc_bus_init(void)
 205{
 206        return bus_register(&umc_bus_type);
 207}
 208module_init(umc_bus_init);
 209
 210static void __exit umc_bus_exit(void)
 211{
 212        bus_unregister(&umc_bus_type);
 213}
 214module_exit(umc_bus_exit);
 215
 216MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus");
 217MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
 218MODULE_LICENSE("GPL");
 219