123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /*
- * Bus for UWB Multi-interface Controller capabilities.
- *
- * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
- *
- * This file is released under the GNU GPL v2.
- */
- #include <linux/kernel.h>
- #include <linux/sysfs.h>
- #include <linux/workqueue.h>
- #include <linux/uwb/umc.h>
- #include <linux/pci.h>
- static int umc_bus_pre_reset_helper(struct device *dev, void *data)
- {
- int ret = 0;
- if (dev->driver) {
- struct umc_dev *umc = to_umc_dev(dev);
- struct umc_driver *umc_drv = to_umc_driver(dev->driver);
- if (umc_drv->pre_reset)
- ret = umc_drv->pre_reset(umc);
- else
- device_release_driver(dev);
- }
- return ret;
- }
- static int umc_bus_post_reset_helper(struct device *dev, void *data)
- {
- int ret = 0;
- if (dev->driver) {
- struct umc_dev *umc = to_umc_dev(dev);
- struct umc_driver *umc_drv = to_umc_driver(dev->driver);
- if (umc_drv->post_reset)
- ret = umc_drv->post_reset(umc);
- } else
- ret = device_attach(dev);
- return ret;
- }
- /**
- * umc_controller_reset - reset the whole UMC controller
- * @umc: the UMC device for the radio controller.
- *
- * Drivers or all capabilities of the controller will have their
- * pre_reset methods called or be unbound from their device. Then all
- * post_reset methods will be called or the drivers will be rebound.
- *
- * Radio controllers must provide pre_reset and post_reset methods and
- * reset the hardware in their start method.
- *
- * If this is called while a probe() or remove() is in progress it
- * will return -EAGAIN and not perform the reset.
- */
- int umc_controller_reset(struct umc_dev *umc)
- {
- struct device *parent = umc->dev.parent;
- int ret = 0;
- if (device_trylock(parent))
- return -EAGAIN;
- ret = device_for_each_child(parent, parent, umc_bus_pre_reset_helper);
- if (ret >= 0)
- ret = device_for_each_child(parent, parent, umc_bus_post_reset_helper);
- device_unlock(parent);
- return ret;
- }
- EXPORT_SYMBOL_GPL(umc_controller_reset);
- /**
- * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device.
- * @umc_drv: umc driver with match_data pointing to a zero-terminated
- * table of pci_device_id's.
- * @umc: umc device whose parent is to be matched.
- */
- int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)
- {
- const struct pci_device_id *id_table = umc_drv->match_data;
- struct pci_dev *pci;
- if (umc->dev.parent->bus != &pci_bus_type)
- return 0;
- pci = to_pci_dev(umc->dev.parent);
- return pci_match_id(id_table, pci) != NULL;
- }
- EXPORT_SYMBOL_GPL(umc_match_pci_id);
- static int umc_bus_rescan_helper(struct device *dev, void *data)
- {
- int ret = 0;
- if (!dev->driver)
- ret = device_attach(dev);
- return ret;
- }
- static void umc_bus_rescan(struct device *parent)
- {
- int err;
- /*
- * We can't use bus_rescan_devices() here as it deadlocks when
- * it tries to retake the dev->parent semaphore.
- */
- err = device_for_each_child(parent, NULL, umc_bus_rescan_helper);
- if (err < 0)
- printk(KERN_WARNING "%s: rescan of bus failed: %d\n",
- KBUILD_MODNAME, err);
- }
- static int umc_bus_match(struct device *dev, struct device_driver *drv)
- {
- struct umc_dev *umc = to_umc_dev(dev);
- struct umc_driver *umc_driver = to_umc_driver(drv);
- if (umc->cap_id == umc_driver->cap_id) {
- if (umc_driver->match)
- return umc_driver->match(umc_driver, umc);
- else
- return 1;
- }
- return 0;
- }
- static int umc_device_probe(struct device *dev)
- {
- struct umc_dev *umc;
- struct umc_driver *umc_driver;
- int err;
- umc_driver = to_umc_driver(dev->driver);
- umc = to_umc_dev(dev);
- get_device(dev);
- err = umc_driver->probe(umc);
- if (err)
- put_device(dev);
- else
- umc_bus_rescan(dev->parent);
- return err;
- }
- static int umc_device_remove(struct device *dev)
- {
- struct umc_dev *umc;
- struct umc_driver *umc_driver;
- umc_driver = to_umc_driver(dev->driver);
- umc = to_umc_dev(dev);
- umc_driver->remove(umc);
- put_device(dev);
- return 0;
- }
- static int umc_device_suspend(struct device *dev, pm_message_t state)
- {
- struct umc_dev *umc;
- struct umc_driver *umc_driver;
- int err = 0;
- umc = to_umc_dev(dev);
- if (dev->driver) {
- umc_driver = to_umc_driver(dev->driver);
- if (umc_driver->suspend)
- err = umc_driver->suspend(umc, state);
- }
- return err;
- }
- static int umc_device_resume(struct device *dev)
- {
- struct umc_dev *umc;
- struct umc_driver *umc_driver;
- int err = 0;
- umc = to_umc_dev(dev);
- if (dev->driver) {
- umc_driver = to_umc_driver(dev->driver);
- if (umc_driver->resume)
- err = umc_driver->resume(umc);
- }
- return err;
- }
- static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct umc_dev *umc = to_umc_dev(dev);
- return sprintf(buf, "0x%02x\n", umc->cap_id);
- }
- static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct umc_dev *umc = to_umc_dev(dev);
- return sprintf(buf, "0x%04x\n", umc->version);
- }
- static struct device_attribute umc_dev_attrs[] = {
- __ATTR_RO(capability_id),
- __ATTR_RO(version),
- __ATTR_NULL,
- };
- struct bus_type umc_bus_type = {
- .name = "umc",
- .match = umc_bus_match,
- .probe = umc_device_probe,
- .remove = umc_device_remove,
- .suspend = umc_device_suspend,
- .resume = umc_device_resume,
- .dev_attrs = umc_dev_attrs,
- };
- EXPORT_SYMBOL_GPL(umc_bus_type);
- static int __init umc_bus_init(void)
- {
- return bus_register(&umc_bus_type);
- }
- module_init(umc_bus_init);
- static void __exit umc_bus_exit(void)
- {
- bus_unregister(&umc_bus_type);
- }
- module_exit(umc_bus_exit);
- MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus");
- MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
- MODULE_LICENSE("GPL");
|