// SPDX-License-Identifier: GPL-2.0 /* * Greybus Bridged-Phy Bus driver * * Copyright 2014 Google Inc. * Copyright 2014 Linaro Ltd. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include "gbphy.h" #define GB_GBPHY_AUTOSUSPEND_MS 3000 struct gbphy_host { struct gb_bundle *bundle; struct list_head devices; }; static DEFINE_IDA(gbphy_id); static ssize_t protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); return sprintf(buf, "0x%02x\n", gbphy_dev->cport_desc->protocol_id); } static DEVICE_ATTR_RO(protocol_id); static struct attribute *gbphy_dev_attrs[] = { &dev_attr_protocol_id.attr, NULL, }; ATTRIBUTE_GROUPS(gbphy_dev); static void gbphy_dev_release(struct device *dev) { struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ida_free(&gbphy_id, gbphy_dev->id); kfree(gbphy_dev); } #ifdef CONFIG_PM static int gb_gbphy_idle(struct device *dev) { pm_runtime_mark_last_busy(dev); pm_request_autosuspend(dev); return 0; } #endif static const struct dev_pm_ops gb_gbphy_pm_ops = { SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, pm_generic_runtime_resume, gb_gbphy_idle) }; static const struct device_type greybus_gbphy_dev_type = { .name = "gbphy_device", .release = gbphy_dev_release, .pm = &gb_gbphy_pm_ops, }; static int gbphy_dev_uevent(const struct device *dev, struct kobj_uevent_env *env) { const struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); const struct greybus_descriptor_cport *cport_desc = gbphy_dev->cport_desc; const struct gb_bundle *bundle = gbphy_dev->bundle; const struct gb_interface *intf = bundle->intf; const struct gb_module *module = intf->module; const struct gb_host_device *hd = intf->hd; if (add_uevent_var(env, "BUS=%u", hd->bus_id)) return -ENOMEM; if (add_uevent_var(env, "MODULE=%u", module->module_id)) return -ENOMEM; if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) return -ENOMEM; if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", intf->vendor_id, intf->product_id)) return -ENOMEM; if (add_uevent_var(env, "BUNDLE=%u", gbphy_dev->bundle->id)) return -ENOMEM; if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) return -ENOMEM; if (add_uevent_var(env, "GBPHY=%u", gbphy_dev->id)) return -ENOMEM; if (add_uevent_var(env, "PROTOCOL_ID=%02x", cport_desc->protocol_id)) return -ENOMEM; return 0; } static const struct gbphy_device_id * gbphy_dev_match_id(struct gbphy_device *gbphy_dev, struct gbphy_driver *gbphy_drv) { const struct gbphy_device_id *id = gbphy_drv->id_table; if (!id) return NULL; for (; id->protocol_id; id++) if (id->protocol_id == gbphy_dev->cport_desc->protocol_id) return id; return NULL; } static int gbphy_dev_match(struct device *dev, struct device_driver *drv) { struct gbphy_driver *gbphy_drv = to_gbphy_driver(drv); struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); const struct gbphy_device_id *id; id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); if (id) return 1; return 0; } static int gbphy_dev_probe(struct device *dev) { struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); const struct gbphy_device_id *id; int ret; id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); if (!id) return -ENODEV; /* for old kernels we need get_sync to resume parent devices */ ret = gb_pm_runtime_get_sync(gbphy_dev->bundle); if (ret < 0) return ret; pm_runtime_set_autosuspend_delay(dev, GB_GBPHY_AUTOSUSPEND_MS); pm_runtime_use_autosuspend(dev); pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); /* * Drivers should call put on the gbphy dev before returning * from probe if they support runtime pm. */ ret = gbphy_drv->probe(gbphy_dev, id); if (ret) { pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); pm_runtime_dont_use_autosuspend(dev); } gb_pm_runtime_put_autosuspend(gbphy_dev->bundle); return ret; } static void gbphy_dev_remove(struct device *dev) { struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); gbphy_drv->remove(gbphy_dev); pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); pm_runtime_dont_use_autosuspend(dev); } static const struct bus_type gbphy_bus_type = { .name = "gbphy", .match = gbphy_dev_match, .probe = gbphy_dev_probe, .remove = gbphy_dev_remove, .uevent = gbphy_dev_uevent, }; int gb_gbphy_register_driver(struct gbphy_driver *driver, struct module *owner, const char *mod_name) { int retval; if (greybus_disabled()) return -ENODEV; driver->driver.bus = &gbphy_bus_type; driver->driver.name = driver->name; driver->driver.owner = owner; driver->driver.mod_name = mod_name; retval = driver_register(&driver->driver); if (retval) return retval; pr_info("registered new driver %s\n", driver->name); return 0; } EXPORT_SYMBOL_GPL(gb_gbphy_register_driver); void gb_gbphy_deregister_driver(struct gbphy_driver *driver) { driver_unregister(&driver->driver); } EXPORT_SYMBOL_GPL(gb_gbphy_deregister_driver); static struct gbphy_device *gb_gbphy_create_dev(struct gb_bundle *bundle, struct greybus_descriptor_cport *cport_desc) { struct gbphy_device *gbphy_dev; int retval; int id; id = ida_alloc_min(&gbphy_id, 1, GFP_KERNEL); if (id < 0) return ERR_PTR(id); gbphy_dev = kzalloc(sizeof(*gbphy_dev), GFP_KERNEL); if (!gbphy_dev) { ida_free(&gbphy_id, id); return ERR_PTR(-ENOMEM); } gbphy_dev->id = id; gbphy_dev->bundle = bundle; gbphy_dev->cport_desc = cport_desc; gbphy_dev->dev.parent = &bundle->dev; gbphy_dev->dev.bus = &gbphy_bus_type; gbphy_dev->dev.type = &greybus_gbphy_dev_type; gbphy_dev->dev.groups = gbphy_dev_groups; gbphy_dev->dev.dma_mask = bundle->dev.dma_mask; dev_set_name(&gbphy_dev->dev, "gbphy%d", id); retval = device_register(&gbphy_dev->dev); if (retval) { put_device(&gbphy_dev->dev); return ERR_PTR(retval); } return gbphy_dev; } static void gb_gbphy_disconnect(struct gb_bundle *bundle) { struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle); struct gbphy_device *gbphy_dev, *temp; int ret; ret = gb_pm_runtime_get_sync(bundle); if (ret < 0) gb_pm_runtime_get_noresume(bundle); list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) { list_del(&gbphy_dev->list); device_unregister(&gbphy_dev->dev); } kfree(gbphy_host); } static int gb_gbphy_probe(struct gb_bundle *bundle, const struct greybus_bundle_id *id) { struct gbphy_host *gbphy_host; struct gbphy_device *gbphy_dev; int i; if (bundle->num_cports == 0) return -ENODEV; gbphy_host = kzalloc(sizeof(*gbphy_host), GFP_KERNEL); if (!gbphy_host) return -ENOMEM; gbphy_host->bundle = bundle; INIT_LIST_HEAD(&gbphy_host->devices); greybus_set_drvdata(bundle, gbphy_host); /* * Create a bunch of children devices, one per cport, and bind the * bridged phy drivers to them. */ for (i = 0; i < bundle->num_cports; ++i) { gbphy_dev = gb_gbphy_create_dev(bundle, &bundle->cport_desc[i]); if (IS_ERR(gbphy_dev)) { gb_gbphy_disconnect(bundle); return PTR_ERR(gbphy_dev); } list_add(&gbphy_dev->list, &gbphy_host->devices); } gb_pm_runtime_put_autosuspend(bundle); return 0; } static const struct greybus_bundle_id gb_gbphy_id_table[] = { { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, { }, }; MODULE_DEVICE_TABLE(greybus, gb_gbphy_id_table); static struct greybus_driver gb_gbphy_driver = { .name = "gbphy", .probe = gb_gbphy_probe, .disconnect = gb_gbphy_disconnect, .id_table = gb_gbphy_id_table, }; static int __init gbphy_init(void) { int retval; retval = bus_register(&gbphy_bus_type); if (retval) { pr_err("gbphy bus register failed (%d)\n", retval); return retval; } retval = greybus_register(&gb_gbphy_driver); if (retval) { pr_err("error registering greybus driver\n"); goto error_gbphy; } return 0; error_gbphy: bus_unregister(&gbphy_bus_type); ida_destroy(&gbphy_id); return retval; } module_init(gbphy_init); static void __exit gbphy_exit(void) { greybus_deregister(&gb_gbphy_driver); bus_unregister(&gbphy_bus_type); ida_destroy(&gbphy_id); } module_exit(gbphy_exit); MODULE_LICENSE("GPL v2");