linux/drivers/staging/greybus/core.c
<<
>>
Prefs
   1/*
   2 * Greybus "Core"
   3 *
   4 * Copyright 2014-2015 Google Inc.
   5 * Copyright 2014-2015 Linaro Ltd.
   6 *
   7 * Released under the GPLv2 only.
   8 */
   9
  10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11
  12#define CREATE_TRACE_POINTS
  13#include "greybus.h"
  14#include "greybus_trace.h"
  15
  16#define GB_BUNDLE_AUTOSUSPEND_MS        3000
  17
  18/* Allow greybus to be disabled at boot if needed */
  19static bool nogreybus;
  20#ifdef MODULE
  21module_param(nogreybus, bool, 0444);
  22#else
  23core_param(nogreybus, nogreybus, bool, 0444);
  24#endif
  25int greybus_disabled(void)
  26{
  27        return nogreybus;
  28}
  29EXPORT_SYMBOL_GPL(greybus_disabled);
  30
  31static bool greybus_match_one_id(struct gb_bundle *bundle,
  32                                     const struct greybus_bundle_id *id)
  33{
  34        if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) &&
  35            (id->vendor != bundle->intf->vendor_id))
  36                return false;
  37
  38        if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) &&
  39            (id->product != bundle->intf->product_id))
  40                return false;
  41
  42        if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) &&
  43            (id->class != bundle->class))
  44                return false;
  45
  46        return true;
  47}
  48
  49static const struct greybus_bundle_id *
  50greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id)
  51{
  52        if (id == NULL)
  53                return NULL;
  54
  55        for (; id->vendor || id->product || id->class || id->driver_info;
  56                                                                        id++) {
  57                if (greybus_match_one_id(bundle, id))
  58                        return id;
  59        }
  60
  61        return NULL;
  62}
  63
  64static int greybus_match_device(struct device *dev, struct device_driver *drv)
  65{
  66        struct greybus_driver *driver = to_greybus_driver(drv);
  67        struct gb_bundle *bundle;
  68        const struct greybus_bundle_id *id;
  69
  70        if (!is_gb_bundle(dev))
  71                return 0;
  72
  73        bundle = to_gb_bundle(dev);
  74
  75        id = greybus_match_id(bundle, driver->id_table);
  76        if (id)
  77                return 1;
  78        /* FIXME - Dynamic ids? */
  79        return 0;
  80}
  81
  82static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
  83{
  84        struct gb_host_device *hd;
  85        struct gb_module *module = NULL;
  86        struct gb_interface *intf = NULL;
  87        struct gb_control *control = NULL;
  88        struct gb_bundle *bundle = NULL;
  89        struct gb_svc *svc = NULL;
  90
  91        if (is_gb_host_device(dev)) {
  92                hd = to_gb_host_device(dev);
  93        } else if (is_gb_module(dev)) {
  94                module = to_gb_module(dev);
  95                hd = module->hd;
  96        } else if (is_gb_interface(dev)) {
  97                intf = to_gb_interface(dev);
  98                module = intf->module;
  99                hd = intf->hd;
 100        } else if (is_gb_control(dev)) {
 101                control = to_gb_control(dev);
 102                intf = control->intf;
 103                module = intf->module;
 104                hd = intf->hd;
 105        } else if (is_gb_bundle(dev)) {
 106                bundle = to_gb_bundle(dev);
 107                intf = bundle->intf;
 108                module = intf->module;
 109                hd = intf->hd;
 110        } else if (is_gb_svc(dev)) {
 111                svc = to_gb_svc(dev);
 112                hd = svc->hd;
 113        } else {
 114                dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
 115                return -EINVAL;
 116        }
 117
 118        if (add_uevent_var(env, "BUS=%u", hd->bus_id))
 119                return -ENOMEM;
 120
 121        if (module) {
 122                if (add_uevent_var(env, "MODULE=%u", module->module_id))
 123                        return -ENOMEM;
 124        }
 125
 126        if (intf) {
 127                if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
 128                        return -ENOMEM;
 129                if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x",
 130                                   intf->vendor_id, intf->product_id))
 131                        return -ENOMEM;
 132        }
 133
 134        if (bundle) {
 135                // FIXME
 136                // add a uevent that can "load" a bundle type
 137                // This is what we need to bind a driver to so use the info
 138                // in gmod here as well
 139
 140                if (add_uevent_var(env, "BUNDLE=%u", bundle->id))
 141                        return -ENOMEM;
 142                if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class))
 143                        return -ENOMEM;
 144        }
 145
 146        return 0;
 147}
 148
 149static void greybus_shutdown(struct device *dev)
 150{
 151        if (is_gb_host_device(dev)) {
 152                struct gb_host_device *hd;
 153
 154                hd = to_gb_host_device(dev);
 155                gb_hd_shutdown(hd);
 156        }
 157}
 158
 159struct bus_type greybus_bus_type = {
 160        .name =         "greybus",
 161        .match =        greybus_match_device,
 162        .uevent =       greybus_uevent,
 163        .shutdown =     greybus_shutdown,
 164};
 165
 166static int greybus_probe(struct device *dev)
 167{
 168        struct greybus_driver *driver = to_greybus_driver(dev->driver);
 169        struct gb_bundle *bundle = to_gb_bundle(dev);
 170        const struct greybus_bundle_id *id;
 171        int retval;
 172
 173        /* match id */
 174        id = greybus_match_id(bundle, driver->id_table);
 175        if (!id)
 176                return -ENODEV;
 177
 178        retval = pm_runtime_get_sync(&bundle->intf->dev);
 179        if (retval < 0) {
 180                pm_runtime_put_noidle(&bundle->intf->dev);
 181                return retval;
 182        }
 183
 184        retval = gb_control_bundle_activate(bundle->intf->control, bundle->id);
 185        if (retval) {
 186                pm_runtime_put(&bundle->intf->dev);
 187                return retval;
 188        }
 189
 190        /*
 191         * Unbound bundle devices are always deactivated. During probe, the
 192         * Runtime PM is set to enabled and active and the usage count is
 193         * incremented. If the driver supports runtime PM, it should call
 194         * pm_runtime_put() in its probe routine and pm_runtime_get_sync()
 195         * in remove routine.
 196         */
 197        pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS);
 198        pm_runtime_use_autosuspend(dev);
 199        pm_runtime_get_noresume(dev);
 200        pm_runtime_set_active(dev);
 201        pm_runtime_enable(dev);
 202
 203        retval = driver->probe(bundle, id);
 204        if (retval) {
 205                /*
 206                 * Catch buggy drivers that fail to destroy their connections.
 207                 */
 208                WARN_ON(!list_empty(&bundle->connections));
 209
 210                gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
 211
 212                pm_runtime_disable(dev);
 213                pm_runtime_set_suspended(dev);
 214                pm_runtime_put_noidle(dev);
 215                pm_runtime_dont_use_autosuspend(dev);
 216                pm_runtime_put(&bundle->intf->dev);
 217
 218                return retval;
 219        }
 220
 221        pm_runtime_put(&bundle->intf->dev);
 222
 223        return 0;
 224}
 225
 226static int greybus_remove(struct device *dev)
 227{
 228        struct greybus_driver *driver = to_greybus_driver(dev->driver);
 229        struct gb_bundle *bundle = to_gb_bundle(dev);
 230        struct gb_connection *connection;
 231        int retval;
 232
 233        retval = pm_runtime_get_sync(dev);
 234        if (retval < 0)
 235                dev_err(dev, "failed to resume bundle: %d\n", retval);
 236
 237        /*
 238         * Disable (non-offloaded) connections early in case the interface is
 239         * already gone to avoid unceccessary operation timeouts during
 240         * driver disconnect. Otherwise, only disable incoming requests.
 241         */
 242        list_for_each_entry(connection, &bundle->connections, bundle_links) {
 243                if (gb_connection_is_offloaded(connection))
 244                        continue;
 245
 246                if (bundle->intf->disconnected)
 247                        gb_connection_disable_forced(connection);
 248                else
 249                        gb_connection_disable_rx(connection);
 250        }
 251
 252        driver->disconnect(bundle);
 253
 254        /* Catch buggy drivers that fail to destroy their connections. */
 255        WARN_ON(!list_empty(&bundle->connections));
 256
 257        if (!bundle->intf->disconnected)
 258                gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
 259
 260        pm_runtime_put_noidle(dev);
 261        pm_runtime_disable(dev);
 262        pm_runtime_set_suspended(dev);
 263        pm_runtime_dont_use_autosuspend(dev);
 264        pm_runtime_put_noidle(dev);
 265
 266        return 0;
 267}
 268
 269int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
 270                const char *mod_name)
 271{
 272        int retval;
 273
 274        if (greybus_disabled())
 275                return -ENODEV;
 276
 277        driver->driver.bus = &greybus_bus_type;
 278        driver->driver.name = driver->name;
 279        driver->driver.probe = greybus_probe;
 280        driver->driver.remove = greybus_remove;
 281        driver->driver.owner = owner;
 282        driver->driver.mod_name = mod_name;
 283
 284        retval = driver_register(&driver->driver);
 285        if (retval)
 286                return retval;
 287
 288        pr_info("registered new driver %s\n", driver->name);
 289        return 0;
 290}
 291EXPORT_SYMBOL_GPL(greybus_register_driver);
 292
 293void greybus_deregister_driver(struct greybus_driver *driver)
 294{
 295        driver_unregister(&driver->driver);
 296}
 297EXPORT_SYMBOL_GPL(greybus_deregister_driver);
 298
 299static int __init gb_init(void)
 300{
 301        int retval;
 302
 303        if (greybus_disabled())
 304                return -ENODEV;
 305
 306        BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
 307
 308        gb_debugfs_init();
 309
 310        retval = bus_register(&greybus_bus_type);
 311        if (retval) {
 312                pr_err("bus_register failed (%d)\n", retval);
 313                goto error_bus;
 314        }
 315
 316        retval = gb_hd_init();
 317        if (retval) {
 318                pr_err("gb_hd_init failed (%d)\n", retval);
 319                goto error_hd;
 320        }
 321
 322        retval = gb_operation_init();
 323        if (retval) {
 324                pr_err("gb_operation_init failed (%d)\n", retval);
 325                goto error_operation;
 326        }
 327        return 0;       /* Success */
 328
 329error_operation:
 330        gb_hd_exit();
 331error_hd:
 332        bus_unregister(&greybus_bus_type);
 333error_bus:
 334        gb_debugfs_cleanup();
 335
 336        return retval;
 337}
 338module_init(gb_init);
 339
 340static void __exit gb_exit(void)
 341{
 342        gb_operation_exit();
 343        gb_hd_exit();
 344        bus_unregister(&greybus_bus_type);
 345        gb_debugfs_cleanup();
 346        tracepoint_synchronize_unregister();
 347}
 348module_exit(gb_exit);
 349MODULE_LICENSE("GPL v2");
 350MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
 351