summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/gbphy.c
diff options
context:
space:
mode:
authorSandeep Patil2016-05-19 17:52:39 +0200
committerGreg Kroah-Hartman2016-05-19 19:09:13 +0200
commite54b106dd1be50377fe8365392466e080b659ab6 (patch)
tree42857d8b2a4088717d76a6daff6000bb66cd2080 /drivers/staging/greybus/gbphy.c
parentgreybus: interface: retry enumeration of UniPro-only modules (diff)
downloadkernel-qcow2-linux-e54b106dd1be50377fe8365392466e080b659ab6.tar.gz
kernel-qcow2-linux-e54b106dd1be50377fe8365392466e080b659ab6.tar.xz
kernel-qcow2-linux-e54b106dd1be50377fe8365392466e080b659ab6.zip
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 <patil_sandeep@projectara.com> Suggested-by: Greg Kroah-Hartman <gregkh@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/gbphy.c')
-rw-r--r--drivers/staging/greybus/gbphy.c330
1 files changed, 330 insertions, 0 deletions
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 <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#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");