aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/greybus/module.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-09-18 11:05:34 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-18 11:05:34 -0700
commite6874fc29410fabfdbc8c12b467f41a16cbcfd2b (patch)
treebb869a9b481a6c05bf334a0a4db0b1397ce4ca90 /drivers/greybus/module.c
parente444d51b14c4795074f485c79debd234931f0e49 (diff)
parent3fb73eddba106ad2a265a5c5c29d14b0ed6aaee1 (diff)
downloadkernel_replicant_linux-e6874fc29410fabfdbc8c12b467f41a16cbcfd2b.tar.gz
kernel_replicant_linux-e6874fc29410fabfdbc8c12b467f41a16cbcfd2b.tar.bz2
kernel_replicant_linux-e6874fc29410fabfdbc8c12b467f41a16cbcfd2b.zip
Merge tag 'staging-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging and IIO driver updates from Greg KH: "Here is the big staging/iio driver update for 5.4-rc1. Lots of churn here, with a few driver/filesystems moving out of staging finally: - erofs moved out of staging - greybus core code moved out of staging Along with that, a new filesytem has been added: - extfat to provide support for those devices requiring that filesystem (i.e. transfer devices to/from windows systems or printers) Other than that, there a number of new IIO drivers, and lots and lots and lots of staging driver cleanups and minor fixes as people continue to dig into those for easy changes. All of these have been in linux-next for a while with no reported issues" * tag 'staging-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (453 commits) Staging: gasket: Use temporaries to reduce line length. Staging: octeon: Avoid several usecases of strcpy staging: vhciq_core: replace snprintf with scnprintf staging: wilc1000: avoid twice IRQ handler execution for each single interrupt staging: wilc1000: remove unused interrupt status handling code staging: fbtft: make several arrays static const, makes object smaller staging: rtl8188eu: make two arrays static const, makes object smaller staging: rtl8723bs: core: Remove Macro "IS_MAC_ADDRESS_BROADCAST" dt-bindings: anybus-controller: move to staging/ tree staging: emxx_udc: remove local TRUE/FALSE definition staging: wilc1000: look for rtc_clk clock staging: dt-bindings: wilc1000: add optional rtc_clk property staging: nvec: make use of devm_platform_ioremap_resource staging: exfat: drop unused function parameter Staging: exfat: Avoid use of strcpy staging: exfat: use integer constants staging: exfat: cleanup spacing for casts staging: exfat: cleanup spacing for operators staging: rtl8723bs: hal: remove redundant variable n staging: pi433: Fix typo in documentation ...
Diffstat (limited to 'drivers/greybus/module.c')
-rw-r--r--drivers/greybus/module.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/greybus/module.c b/drivers/greybus/module.c
new file mode 100644
index 000000000000..36f77f9e1d74
--- /dev/null
+++ b/drivers/greybus/module.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Greybus Module code
+ *
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ */
+
+#include <linux/greybus.h>
+#include "greybus_trace.h"
+
+static ssize_t eject_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ 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;
+
+ for (i = 0; i < module->num_interfaces; ++i) {
+ intf = module->interfaces[i];
+
+ 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 len;
+}
+static DEVICE_ATTR_WO(eject);
+
+static ssize_t module_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gb_module *module = to_gb_module(dev);
+
+ return sprintf(buf, "%u\n", module->module_id);
+}
+static DEVICE_ATTR_RO(module_id);
+
+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 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 *module = to_gb_module(dev);
+
+ trace_gb_module_release(module);
+
+ kfree(module);
+}
+
+struct device_type greybus_module_type = {
+ .name = "greybus_module",
+ .release = gb_module_release,
+};
+
+struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id,
+ size_t num_interfaces)
+{
+ struct gb_interface *intf;
+ struct gb_module *module;
+ int i;
+
+ module = kzalloc(struct_size(module, interfaces, num_interfaces),
+ 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;
+ }
+
+ return module;
+
+err_put_interfaces:
+ for (--i; i >= 0; --i)
+ gb_interface_put(module->interfaces[i]);
+
+ put_device(&module->dev);
+
+ return NULL;
+}
+
+/*
+ * Register and enable an interface after first attempting to activate it.
+ */
+static void gb_module_register_interface(struct gb_interface *intf)
+{
+ struct gb_module *module = intf->module;
+ u8 intf_id = intf->interface_id;
+ int ret;
+
+ mutex_lock(&intf->mutex);
+
+ ret = gb_interface_activate(intf);
+ if (ret) {
+ if (intf->type != GB_INTERFACE_TYPE_DUMMY) {
+ dev_err(&module->dev,
+ "failed to activate interface %u: %d\n",
+ intf_id, ret);
+ }
+
+ 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;
+ }
+
+ mutex_unlock(&intf->mutex);
+
+ return;
+
+err_interface_deactivate:
+ gb_interface_deactivate(intf);
+err_unlock:
+ mutex_unlock(&intf->mutex);
+}
+
+static void gb_module_deregister_interface(struct gb_interface *intf)
+{
+ /* Mark as disconnected to prevent I/O during disable. */
+ if (intf->module->disconnected)
+ intf->disconnected = true;
+
+ mutex_lock(&intf->mutex);
+ intf->removed = true;
+ gb_interface_disable(intf);
+ gb_interface_deactivate(intf);
+ mutex_unlock(&intf->mutex);
+
+ gb_interface_del(intf);
+}
+
+/* Register a module and its interfaces. */
+int gb_module_add(struct gb_module *module)
+{
+ size_t i;
+ int ret;
+
+ ret = device_add(&module->dev);
+ if (ret) {
+ dev_err(&module->dev, "failed to register module: %d\n", ret);
+ 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);
+}