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