brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · 313eb65 Raw
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