greybus: fix tracepoint-related whitespace issues
[cascardo/linux.git] / drivers / staging / greybus / module.c
index 9cf98cd..7f0ed9f 100644 (file)
 /*
- * Greybus modules
+ * Greybus Module code
  *
- * Copyright 2014 Google Inc.
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
  *
  * Released under the GPLv2 only.
  */
 
 #include "greybus.h"
+#include "greybus_trace.h"
 
-/* XXX This could be per-host device */
-static DEFINE_SPINLOCK(gb_modules_lock);
 
-static int gb_module_match_one_id(struct gb_module *gmod,
-                               const struct greybus_module_id *id)
+static ssize_t eject_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t len)
 {
-       if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) &&
-           (id->vendor != gmod->vendor))
-               return 0;
+       struct gb_module *module = to_gb_module(dev);
+       struct gb_interface *intf;
+       size_t i;
+       long val;
+       int ret;
+
+       ret = kstrtol(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       if (!val)
+               return len;
 
-       if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) &&
-           (id->product != gmod->product))
-               return 0;
+       for (i = 0; i < module->num_interfaces; ++i) {
+               intf = module->interfaces[i];
 
-       if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) &&
-           (id->unique_id != gmod->unique_id))
-               return 0;
+               mutex_lock(&intf->mutex);
+               /* Set flag to prevent concurrent activation. */
+               intf->ejected = true;
+               gb_interface_disable(intf);
+               gb_interface_deactivate(intf);
+               mutex_unlock(&intf->mutex);
+       }
+
+       /* Tell the SVC to eject the primary interface. */
+       ret = gb_svc_intf_eject(module->hd->svc, module->module_id);
+       if (ret)
+               return ret;
 
-       return 1;
+       return len;
 }
+static DEVICE_ATTR_WO(eject);
 
-const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod,
-                               const struct greybus_module_id *id)
+static ssize_t module_id_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
 {
-       if (id == NULL)
-               return NULL;
+       struct gb_module *module = to_gb_module(dev);
 
-       for (; id->vendor || id->product || id->unique_id ||
-                       id->driver_info; id++) {
-               if (gb_module_match_one_id(gmod, id))
-                       return id;
-       }
+       return sprintf(buf, "%u\n", module->module_id);
+}
+static DEVICE_ATTR_RO(module_id);
 
-       return NULL;
+static ssize_t num_interfaces_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct gb_module *module = to_gb_module(dev);
+
+       return sprintf(buf, "%zu\n", module->num_interfaces);
 }
+static DEVICE_ATTR_RO(num_interfaces);
 
-static void greybus_module_release(struct device *dev)
+static struct attribute *module_attrs[] = {
+       &dev_attr_eject.attr,
+       &dev_attr_module_id.attr,
+       &dev_attr_num_interfaces.attr,
+       NULL,
+};
+ATTRIBUTE_GROUPS(module);
+
+static void gb_module_release(struct device *dev)
 {
-       struct gb_module *gmod = to_gb_module(dev);
+       struct gb_module *module = to_gb_module(dev);
+
+       trace_gb_module_release(module);
 
-       kfree(gmod);
+       kfree(module);
 }
 
-static struct device_type greybus_module_type = {
-       .name =         "greybus_module",
-       .release =      greybus_module_release,
+struct device_type greybus_module_type = {
+       .name           = "greybus_module",
+       .release        = gb_module_release,
 };
 
-/*
- * A Greybus module represents a user-replacable component on an Ara
- * phone.
- *
- * Create a gb_module structure to represent a discovered module.
- * The position within the Endo is encoded in the "module_id" argument.
- * Returns a pointer to the new module or a null pointer if a
- * failure occurs due to memory exhaustion.
- */
-struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
+struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id,
+                                       size_t num_interfaces)
 {
-       struct gb_module *gmod;
-       int retval;
+       struct gb_interface *intf;
+       struct gb_module *module;
+       int i;
 
-       gmod = gb_module_find(hd, module_id);
-       if (gmod) {
-               dev_err(hd->parent, "Duplicate module id %d will not be created\n",
-                       module_id);
+       module = kzalloc(sizeof(*module) + num_interfaces * sizeof(intf),
+                               GFP_KERNEL);
+       if (!module)
                return NULL;
+
+       module->hd = hd;
+       module->module_id = module_id;
+       module->num_interfaces = num_interfaces;
+
+       module->dev.parent = &hd->dev;
+       module->dev.bus = &greybus_bus_type;
+       module->dev.type = &greybus_module_type;
+       module->dev.groups = module_groups;
+       module->dev.dma_mask = hd->dev.dma_mask;
+       device_initialize(&module->dev);
+       dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id);
+
+       trace_gb_module_create(module);
+
+       for (i = 0; i < num_interfaces; ++i) {
+               intf = gb_interface_create(module, module_id + i);
+               if (!intf) {
+                       dev_err(&module->dev, "failed to create interface %u\n",
+                                       module_id + i);
+                       goto err_put_interfaces;
+               }
+               module->interfaces[i] = intf;
        }
 
-       gmod = kzalloc(sizeof(*gmod), GFP_KERNEL);
-       if (!gmod)
-               return NULL;
+       return module;
 
-       gmod->hd = hd;          /* XXX refcount? */
-       gmod->module_id = module_id;
-       INIT_LIST_HEAD(&gmod->interfaces);
-
-       spin_lock_irq(&gb_modules_lock);
-       list_add_tail(&gmod->links, &hd->modules);
-       spin_unlock_irq(&gb_modules_lock);
-
-       gmod->dev.parent = hd->parent;
-       gmod->dev.driver = NULL;
-       gmod->dev.bus = &greybus_bus_type;
-       gmod->dev.type = &greybus_module_type;
-       gmod->dev.groups = greybus_module_groups;
-       gmod->dev.dma_mask = hd->parent->dma_mask;
-       device_initialize(&gmod->dev);
-       dev_set_name(&gmod->dev, "%d", module_id);
-
-       retval = device_add(&gmod->dev);
-       if (retval) {
-               pr_err("failed to add module device for id 0x%02hhx\n",
-                       module_id);
-               put_device(&gmod->dev);
-               return NULL;
-       }
+err_put_interfaces:
+       for (--i; i > 0; --i)
+               gb_interface_put(module->interfaces[i]);
 
-       return gmod;
+       put_device(&module->dev);
+
+       return NULL;
 }
 
 /*
- * Tear down a previously set up module.
+ * Register and enable an interface after first attempting to activate it.
  */
-void gb_module_destroy(struct gb_module *gmod)
+static void gb_module_register_interface(struct gb_interface *intf)
 {
-       if (WARN_ON(!gmod))
-               return;
+       struct gb_module *module = intf->module;
+       u8 intf_id = intf->interface_id;
+       int ret;
+       int retries = 3;
 
-       spin_lock_irq(&gb_modules_lock);
-       list_del(&gmod->links);
-       spin_unlock_irq(&gb_modules_lock);
+       mutex_lock(&intf->mutex);
 
-       /* XXX Do something with gmod->gb_tty */
+       while (retries--) {
+               ret = gb_interface_activate(intf);
+               if (ret != -EAGAIN)
+                       break;
+       }
+       if (ret) {
+               dev_err(&module->dev, "failed to activate interface %u: %d\n",
+                               intf_id, ret);
+
+               /*
+                * -EAGAIN indicates that the Greybus operation
+                * interface_activate determined the remote interface to be
+                * UniPro-only.  At present, we assume a UniPro-only module
+                * to be a Greybus module that failed to send its mailbox
+                * poke.  There is some reason to believe that this is
+                * because of a bug in the ES3 bootrom.  If we exhause our
+                * retries trying to activate such an interface, convert
+                * the error code back into a "no device" error.
+                */
+               if (ret == -EAGAIN)
+                       ret = -ENODEV;
+
+               gb_interface_add(intf);
+               goto err_unlock;
+       }
+
+       ret = gb_interface_add(intf);
+       if (ret)
+               goto err_interface_deactivate;
+
+       ret = gb_interface_enable(intf);
+       if (ret) {
+               dev_err(&module->dev, "failed to enable interface %u: %d\n",
+                               intf_id, ret);
+               goto err_interface_deactivate;
+       }
 
-       gb_interface_destroy(gmod);
+       mutex_unlock(&intf->mutex);
 
-       kfree(gmod->product_string);
-       kfree(gmod->vendor_string);
-       /* kref_put(module->hd); */
+       return;
 
-       device_del(&gmod->dev);
+err_interface_deactivate:
+       gb_interface_deactivate(intf);
+err_unlock:
+       mutex_unlock(&intf->mutex);
 }
 
-struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id)
+static void gb_module_deregister_interface(struct gb_interface *intf)
 {
-       struct gb_module *module;
+       /* Mark as disconnected to prevent I/O during disable. */
+       if (intf->module->disconnected)
+               intf->disconnected = true;
 
-       list_for_each_entry(module, &hd->modules, links)
-               if (module->module_id == module_id)
-                       return module;
+       mutex_lock(&intf->mutex);
+       gb_interface_disable(intf);
+       gb_interface_deactivate(intf);
+       mutex_unlock(&intf->mutex);
 
-       return NULL;
+       gb_interface_del(intf);
 }
 
-int
-gb_module_interface_init(struct gb_module *gmod, u8 interface_id, u8 device_id)
+/* Register a module and its interfaces. */
+int gb_module_add(struct gb_module *module)
 {
-       struct gb_interface *interface;
+       size_t i;
        int ret;
 
-       interface = gb_interface_find(gmod, interface_id);
-       if (!interface) {
-               dev_err(gmod->hd->parent, "module %hhu not found\n",
-                       interface_id);
-               return -ENOENT;
-       }
-       interface->device_id = device_id;
-
-       ret = svc_set_route_send(interface, gmod->hd);
+       ret = device_add(&module->dev);
        if (ret) {
-               dev_err(gmod->hd->parent, "failed to set route (%d)\n", ret);
+               dev_err(&module->dev, "failed to register module: %d\n", ret);
                return ret;
        }
 
-       ret = gb_interface_connections_init(interface);
-       if (ret) {
-               dev_err(gmod->hd->parent, "module interface init error %d\n",
-                       ret);
-               /* XXX clear route */
-               return ret;
-       }
+       trace_gb_module_add(module);
+
+       for (i = 0; i < module->num_interfaces; ++i)
+               gb_module_register_interface(module->interfaces[i]);
 
        return 0;
 }
+
+/* Deregister a module and its interfaces. */
+void gb_module_del(struct gb_module *module)
+{
+       size_t i;
+
+       for (i = 0; i < module->num_interfaces; ++i)
+               gb_module_deregister_interface(module->interfaces[i]);
+
+       trace_gb_module_del(module);
+
+       device_del(&module->dev);
+}
+
+void gb_module_put(struct gb_module *module)
+{
+       size_t i;
+
+       for (i = 0; i < module->num_interfaces; ++i)
+               gb_interface_put(module->interfaces[i]);
+
+       put_device(&module->dev);
+}