From e54b106dd1be50377fe8365392466e080b659ab6 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Thu, 19 May 2016 08:52:39 -0700 Subject: greybus: gpbridge: rename 'gpbridge' to 'gbphy' everywhere The 'gpbridge' name didn't relaly reflect what the bus is; which is a bus for bridged-phy devices. So, rename all instances of 'gpbridge' to more appropriate 'gbphy' Testing Done: Build and boot tested. 'lsgb' will stop displaying 'GPBridge' devices until I change the library to reflect this change. Signed-off-by: Sandeep Patil Suggested-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/gbphy.c | 330 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 drivers/staging/greybus/gbphy.c (limited to 'drivers/staging/greybus/gbphy.c') diff --git a/drivers/staging/greybus/gbphy.c b/drivers/staging/greybus/gbphy.c new file mode 100644 index 000000000000..c11a1368d073 --- /dev/null +++ b/drivers/staging/greybus/gbphy.c @@ -0,0 +1,330 @@ +/* + * Greybus Bridged-Phy Bus driver + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "greybus.h" +#include "gbphy.h" + +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_simple_remove(&gbphy_id, gbphy_dev->id); + kfree(gbphy_dev); +} + +static struct device_type greybus_gbphy_dev_type = { + .name = "gbphy_device", + .release = gbphy_dev_release, +}; + +static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); + struct greybus_descriptor_cport *cport_desc = gbphy_dev->cport_desc; + struct gb_bundle *bundle = gbphy_dev->bundle; + struct gb_interface *intf = bundle->intf; + struct gb_module *module = intf->module; + 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; + + id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); + if (!id) + return -ENODEV; + + return gbphy_drv->probe(gbphy_dev, id); +} + +static int 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); + return 0; +} + +static 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); + +int gb_gbphy_get_version(struct gb_connection *connection) +{ + struct gb_protocol_version_request request; + struct gb_protocol_version_response response; + int retval; + + request.major = 1; + request.minor = 0; + + retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, + &request, sizeof(request), &response, + sizeof(response)); + if (retval) + return retval; + + /* FIXME - do proper version negotiation here someday... */ + + connection->module_major = response.major; + connection->module_minor = response.minor; + + dev_dbg(&connection->hd->dev, "%s: v%u.%u\n", connection->name, + response.major, response.minor); + + return 0; +} +EXPORT_SYMBOL_GPL(gb_gbphy_get_version); + +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_simple_get(&gbphy_id, 1, 0, GFP_KERNEL); + if (id < 0) + return ERR_PTR(id); + + gbphy_dev = kzalloc(sizeof(*gbphy_dev), GFP_KERNEL); + if (!gbphy_dev) { + ida_simple_remove(&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; + + 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); + } + + 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"); -- cgit v1.2.3-55-g7522