The "root" greybus device for the Greybus device tree, or bus,
                where N is a dynamically assigned 1-based id.
 
-What:          /sys/bus/greybus/device/N-I
+What:          /sys/bus/greybus/device/N-M
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               A Module M on the bus N, where M is the 1-byte interface
+               ID of the module's primary interface.
+
+What:          /sys/bus/greybus/device/N-M/module_id
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The ID of a Greybus module, corresponding to the ID of its
+               primary interface.
+
+What:          /sys/bus/greybus/device/N-M/num_interfaces
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The number of interfaces of a module.
+
+What:          /sys/bus/greybus/device/N-M.I
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
-               An Interface I on the bus N, where I is the 1-byte interface
-               ID.
+               An Interface I on the bus N and module N-M, where I is the
+               1-byte interface ID.
 
-What:          /sys/bus/greybus/device/N-I/current_now
+What:          /sys/bus/greybus/device/N-M.I/current_now
 Date:          March 2016
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Current measurement of the interface in microamps (uA)
 
-What:          /sys/bus/greybus/device/N-I/ddbl1_manufacturer_id
+What:          /sys/bus/greybus/device/N-M.I/ddbl1_manufacturer_id
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
                Unipro Device Descriptor Block Level 1 manufacturer ID for the
                greybus Interface.
 
-What:          /sys/bus/greybus/device/N-I/ddbl1_product_id
+What:          /sys/bus/greybus/device/N-M.I/ddbl1_product_id
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
                Unipro Device Descriptor Block Level 1 product ID for the
                greybus Interface.
 
-What:          /sys/bus/greybus/device/N-I/interface_id
+What:          /sys/bus/greybus/device/N-M.I/interface_id
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                The ID of a Greybus interface.
 
-What:          /sys/bus/greybus/device/N-I/power_now
+What:          /sys/bus/greybus/device/N-M.I/power_now
 Date:          March 2016
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Power measurement of the interface in microwatts (uW)
 
-What:          /sys/bus/greybus/device/N-I/product_id
+What:          /sys/bus/greybus/device/N-M.I/product_id
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Product ID of a Greybus interface.
 
-What:          /sys/bus/greybus/device/N-I/serial_number
+What:          /sys/bus/greybus/device/N-M.I/serial_number
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
                Serial Number of the Greybus interface, represented by a 64 bit
                hexadecimal number.
 
-What:          /sys/bus/greybus/device/N-I/vendor_id
+What:          /sys/bus/greybus/device/N-M.I/vendor_id
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Vendor ID of a Greybus interface.
 
-What:          /sys/bus/greybus/device/N-I/version
+What:          /sys/bus/greybus/device/N-M.I/version
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
                Interface version represented as <16 bit major number>.<16 bit
                minor number>.
 
-What:          /sys/bus/greybus/device/N-I/voltage_now
+What:          /sys/bus/greybus/device/N-M.I/voltage_now
 Date:          March 2016
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Voltage measurement of the interface in microvolts (uV)
 
-What:          /sys/bus/greybus/device/N-I.ctrl
+What:          /sys/bus/greybus/device/N-M.I.ctrl
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
                Abstract control device for interface I that represents the
                current mode of an enumerated Greybus interface.
 
-What:          /sys/bus/greybus/device/N-I.ctrl/product_string
+What:          /sys/bus/greybus/device/N-M.I.ctrl/product_string
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Product ID string of a Greybus interface.
 
-What:          /sys/bus/greybus/device/N-I.ctrl/vendor_string
+What:          /sys/bus/greybus/device/N-M.I.ctrl/vendor_string
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                Vendor ID string of a Greybus interface.
 
-What:          /sys/bus/greybus/device/N-I.B
+What:          /sys/bus/greybus/device/N-M.I.B
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
                A bundle B on the Interface I, B is replaced by a 1-byte
                number representing the bundle.
 
-What:          /sys/bus/greybus/device/N-I.B/bundle_class
+What:          /sys/bus/greybus/device/N-M.I.B/bundle_class
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                The greybus class of the bundle B.
 
-What:          /sys/bus/greybus/device/N-I.B/bundle_id
+What:          /sys/bus/greybus/device/N-M.I.B/bundle_id
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 Description:
                The interface-unique id of the bundle B.
 
-What:          /sys/bus/greybus/device/N-I.B/state
+What:          /sys/bus/greybus/device/N-M.I.B/state
 Date:          October 2015
 KernelVersion: 4.XX
 Contact:       Greg Kroah-Hartman <greg@kroah.com>
 
                debugfs.o       \
                hd.o            \
                manifest.o      \
+               module.o        \
                interface.o     \
                bundle.o        \
                connection.o    \
 
 static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct gb_host_device *hd;
+       struct gb_module *module = NULL;
        struct gb_interface *intf = NULL;
        struct gb_control *control = NULL;
        struct gb_bundle *bundle = NULL;
 
        if (is_gb_host_device(dev)) {
                hd = to_gb_host_device(dev);
+       } else if (is_gb_module(dev)) {
+               module = to_gb_module(dev);
+               hd = module->hd;
        } else if (is_gb_interface(dev)) {
                intf = to_gb_interface(dev);
+               module = intf->module;
                hd = intf->hd;
        } else if (is_gb_control(dev)) {
                control = to_gb_control(dev);
        } else if (is_gb_bundle(dev)) {
                bundle = to_gb_bundle(dev);
                intf = bundle->intf;
+               module = intf->module;
                hd = intf->hd;
        } else if (is_gb_svc(dev)) {
                svc = to_gb_svc(dev);
        if (add_uevent_var(env, "BUS=%u", hd->bus_id))
                return -ENOMEM;
 
+       if (module) {
+               if (add_uevent_var(env, "MODULE=%u", module->module_id))
+                       return -ENOMEM;
+       }
+
        if (intf) {
                if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
                        return -ENOMEM;
 
 #include "hd.h"
 #include "svc.h"
 #include "control.h"
+#include "module.h"
 #include "interface.h"
 #include "bundle.h"
 #include "connection.h"
 extern struct bus_type greybus_bus_type;
 
 extern struct device_type greybus_hd_type;
+extern struct device_type greybus_module_type;
 extern struct device_type greybus_interface_type;
 extern struct device_type greybus_control_type;
 extern struct device_type greybus_bundle_type;
        return dev->type == &greybus_hd_type;
 }
 
+static inline int is_gb_module(const struct device *dev)
+{
+       return dev->type == &greybus_module_type;
+}
+
 static inline int is_gb_interface(const struct device *dev)
 {
        return dev->type == &greybus_interface_type;
 
        hd->bus_id = ret;
 
        hd->driver = driver;
-       INIT_LIST_HEAD(&hd->interfaces);
+       INIT_LIST_HEAD(&hd->modules);
        INIT_LIST_HEAD(&hd->connections);
        ida_init(&hd->cport_id_map);
        hd->buffer_size_max = buffer_size_max;
 
        int bus_id;
        const struct gb_hd_driver *driver;
 
-       struct list_head interfaces;
+       struct list_head modules;
        struct list_head connections;
        struct ida cport_id_map;
 
 
 };
 ATTRIBUTE_GROUPS(interface);
 
-
-// FIXME, odds are you don't want to call this function, rework the caller to
-// not need it please.
-struct gb_interface *gb_interface_find(struct gb_host_device *hd,
-                                      u8 interface_id)
-{
-       struct gb_interface *intf;
-
-       list_for_each_entry(intf, &hd->interfaces, links)
-               if (intf->interface_id == interface_id)
-                       return intf;
-
-       return NULL;
-}
-
 static void gb_interface_release(struct device *dev)
 {
        struct gb_interface *intf = to_gb_interface(dev);
  *
  * Returns a pointer to the new interfce or a null pointer if a
  * failure occurs due to memory exhaustion.
- *
- * Locking: Caller ensures serialisation with gb_interface_remove and
- * gb_interface_find.
  */
-struct gb_interface *gb_interface_create(struct gb_host_device *hd,
+struct gb_interface *gb_interface_create(struct gb_module *module,
                                         u8 interface_id)
 {
+       struct gb_host_device *hd = module->hd;
        struct gb_interface *intf;
 
        intf = kzalloc(sizeof(*intf), GFP_KERNEL);
                return NULL;
 
        intf->hd = hd;          /* XXX refcount? */
+       intf->module = module;
        intf->interface_id = interface_id;
        INIT_LIST_HEAD(&intf->bundles);
        INIT_LIST_HEAD(&intf->manifest_descs);
        /* Invalid device id to start with */
        intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
 
-       intf->dev.parent = &hd->dev;
+       intf->dev.parent = &module->dev;
        intf->dev.bus = &greybus_bus_type;
        intf->dev.type = &greybus_interface_type;
        intf->dev.groups = interface_groups;
-       intf->dev.dma_mask = hd->dev.dma_mask;
+       intf->dev.dma_mask = module->dev.dma_mask;
        device_initialize(&intf->dev);
-       dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id);
-
-       list_add(&intf->links, &hd->interfaces);
+       dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev),
+                       interface_id);
 
        return intf;
 }
        return 0;
 }
 
-/* Deregister an interface and drop its reference. */
-void gb_interface_remove(struct gb_interface *intf)
+/* Deregister an interface. */
+void gb_interface_del(struct gb_interface *intf)
 {
        if (device_is_registered(&intf->dev)) {
                device_del(&intf->dev);
                dev_info(&intf->dev, "Interface removed\n");
        }
+}
 
-       list_del(&intf->links);
-
+void gb_interface_put(struct gb_interface *intf)
+{
        put_device(&intf->dev);
 }
 
        struct gb_control *control;
 
        struct list_head bundles;
-       struct list_head links; /* gb_host_device->interfaces */
+       struct list_head module_node;
        struct list_head manifest_descs;
        u8 interface_id;        /* Physical location within the Endo */
        u8 device_id;
        u16 version_minor;
 
        struct gb_host_device *hd;
+       struct gb_module *module;
 
        unsigned long quirks;
 
 struct gb_interface *gb_interface_find(struct gb_host_device *hd,
                                       u8 interface_id);
 
-struct gb_interface *gb_interface_create(struct gb_host_device *hd,
+struct gb_interface *gb_interface_create(struct gb_module *module,
                                         u8 interface_id);
 int gb_interface_activate(struct gb_interface *intf);
 void gb_interface_deactivate(struct gb_interface *intf);
 int gb_interface_enable(struct gb_interface *intf);
 void gb_interface_disable(struct gb_interface *intf);
 int gb_interface_add(struct gb_interface *intf);
-void gb_interface_remove(struct gb_interface *intf);
+void gb_interface_del(struct gb_interface *intf);
+void gb_interface_put(struct gb_interface *intf);
 
 #endif /* __INTERFACE_H */
 
--- /dev/null
+/*
+ * Greybus Module code
+ *
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include "greybus.h"
+
+
+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_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);
+
+       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(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);
+
+       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;
+
+       ret = gb_interface_activate(intf);
+       if (ret) {
+               dev_err(&module->dev, "failed to activate interface %u: %d\n",
+                               intf_id, ret);
+               gb_interface_add(intf);
+               return;
+       }
+
+       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;
+       }
+
+       return;
+
+err_interface_deactivate:
+       gb_interface_deactivate(intf);
+}
+
+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;
+
+       gb_interface_disable(intf);
+       gb_interface_deactivate(intf);
+
+       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;
+       }
+
+       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]);
+
+       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);
+}
 
--- /dev/null
+/*
+ * Greybus Module code
+ *
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef __MODULE_H
+#define __MODULE_H
+
+struct gb_module {
+       struct device dev;
+       struct gb_host_device *hd;
+
+       struct list_head hd_node;
+
+       u8 module_id;
+       size_t num_interfaces;
+
+       bool disconnected;
+
+       struct gb_interface *interfaces[0];
+};
+#define to_gb_module(d) container_of(d, struct gb_module, dev)
+
+struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id,
+                                  size_t num_interfaces);
+int gb_module_add(struct gb_module *module);
+void gb_module_del(struct gb_module *module);
+void gb_module_put(struct gb_module *module);
+
+#endif /* __MODULE_H */
 
        return 0;
 }
 
+static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id)
+{
+       struct gb_host_device *hd = svc->hd;
+       struct gb_module *module;
+
+       list_for_each_entry(module, &hd->modules, hd_node) {
+               if (module->module_id == module_id)
+                       return module;
+       }
+
+       return NULL;
+}
+
 static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf)
 {
        int ret;
        struct gb_connection *connection = operation->connection;
        struct gb_svc *svc = gb_connection_get_data(connection);
        struct gb_host_device *hd = connection->hd;
-       struct gb_interface *intf;
+       struct gb_module *module;
        u8 intf_id;
        int ret;
 
 
        dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id);
 
-       intf = gb_interface_find(hd, intf_id);
-       if (intf) {
+       /* All modules are considered 1x2 for now */
+       module = gb_svc_module_lookup(svc, intf_id);
+       if (module) {
                dev_info(&svc->dev, "mode switch detected on interface %u\n",
                                intf_id);
 
-               return gb_svc_intf_reenable(svc, intf);
+               return gb_svc_intf_reenable(svc, module->interfaces[0]);
        }
 
-       intf = gb_interface_create(hd, intf_id);
-       if (!intf) {
-               dev_err(&svc->dev, "failed to create interface %u\n",
-                               intf_id);
+       module = gb_module_create(hd, intf_id, 1);
+       if (!module) {
+               dev_err(&svc->dev, "failed to create module\n");
                return;
        }
 
-       ret = gb_interface_activate(intf);
+       ret = gb_module_add(module);
        if (ret) {
-               dev_err(&svc->dev, "failed to activate interface %u: %d\n",
-                               intf_id, ret);
-               gb_interface_add(intf);
+               gb_module_put(module);
                return;
        }
 
-       ret = gb_interface_add(intf);
-       if (ret)
-               goto err_interface_deactivate;
-
-       ret = gb_interface_enable(intf);
-       if (ret) {
-               dev_err(&svc->dev, "failed to enable interface %u: %d\n",
-                               intf_id, ret);
-               goto err_interface_deactivate;
-       }
-
-       return;
-
-err_interface_deactivate:
-       gb_interface_deactivate(intf);
+       list_add(&module->hd_node, &hd->modules);
 }
 
 static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation)
 {
        struct gb_svc *svc = gb_connection_get_data(operation->connection);
        struct gb_svc_intf_hot_unplug_request *request;
-       struct gb_host_device *hd = operation->connection->hd;
-       struct gb_interface *intf;
+       struct gb_module *module;
        u8 intf_id;
 
        /* The request message size has already been verified. */
 
        dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id);
 
-       intf = gb_interface_find(hd, intf_id);
-       if (!intf) {
+       /* All modules are considered 1x2 for now */
+       module = gb_svc_module_lookup(svc, intf_id);
+       if (!module) {
                dev_warn(&svc->dev, "could not find hot-unplug interface %u\n",
                                intf_id);
                return;
        }
 
-       /* Mark as disconnected to prevent I/O during disable. */
-       intf->disconnected = true;
+       module->disconnected = true;
 
-       gb_interface_disable(intf);
-       gb_interface_deactivate(intf);
-       gb_interface_remove(intf);
+       gb_module_del(module);
+       list_del(&module->hd_node);
+       gb_module_put(module);
 }
 
 static void gb_svc_process_deferred_request(struct work_struct *work)
        return 0;
 }
 
-static void gb_svc_remove_interfaces(struct gb_svc *svc)
+static void gb_svc_remove_modules(struct gb_svc *svc)
 {
-       struct gb_interface *intf, *tmp;
+       struct gb_host_device *hd = svc->hd;
+       struct gb_module *module, *tmp;
 
-       list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) {
-               gb_interface_disable(intf);
-               gb_interface_deactivate(intf);
-               gb_interface_remove(intf);
+       list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) {
+               gb_module_del(module);
+               list_del(&module->hd_node);
+               gb_module_put(module);
        }
 }
 
 
        flush_workqueue(svc->wq);
 
-       gb_svc_remove_interfaces(svc);
+       gb_svc_remove_modules(svc);
 }
 
 void gb_svc_put(struct gb_svc *svc)