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