From: Greg Kroah-Hartman Date: Sun, 21 Dec 2014 22:10:26 +0000 (-0800) Subject: greybus: add module support X-Git-Tag: v5.15~12752^2~378^2~21^2~1748 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=df671553cbe286e885e61b61f8c126e034854a89;p=platform%2Fkernel%2Flinux-starfive.git greybus: add module support Modules in the greybus system sit above the interface, so insert them early in the sysfs tree. We dynamically create them when we have an interface that references a module, as we don't get a "module create" message directly. They also dynamically go away when the last interface associated with a module is removed. Naming scheme for modules/interfaces/bundles/connections is bumped up by one ':', and now looks like the following: /sys/bus/greybus $ tree . ├── devices │   ├── 7 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7 │   ├── 7:7 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7 │   ├── 7:7:0 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7/7:7:0 │   └── 7:7:0:1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1/1-1/7/7:7/7:7:0/7:7:0:1 ├── drivers ├── drivers_autoprobe ├── drivers_probe └── uevent 6 directories, 3 files /sys/bus/greybus $ grep . devices/*/uevent devices/7/uevent:DEVTYPE=greybus_module devices/7:7/uevent:DEVTYPE=greybus_interface devices/7:7:0/uevent:DEVTYPE=greybus_bundle devices/7:7:0:1/uevent:DEVTYPE=greybus_connection We still have some "confusion" about interface ids and module ids, which will be cleaned up later when the svc control protocol changes die down, right now we just name a module after the interface as we don't have any modules that have multiple interfaces in our systems. This has been tested with gbsim. Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 6c0b0ca..0d3977d 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -2,6 +2,7 @@ greybus-y := core.o \ debugfs.o \ ap.o \ manifest.o \ + module.o \ interface.o \ bundle.o \ connection.o \ diff --git a/drivers/staging/greybus/bundle.c b/drivers/staging/greybus/bundle.c index 2ac67a24..28a8222 100644 --- a/drivers/staging/greybus/bundle.c +++ b/drivers/staging/greybus/bundle.c @@ -71,7 +71,7 @@ struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 interface_id) bundle->dev.type = &greybus_bundle_type; bundle->dev.groups = bundle_groups; device_initialize(&bundle->dev); - dev_set_name(&bundle->dev, "%d:%d", intf->module_id, interface_id); + dev_set_name(&bundle->dev, "%s:%d", dev_name(&intf->dev), interface_id); retval = device_add(&bundle->dev); if (retval) { diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 2d61ee7..3f786bf 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -237,7 +237,7 @@ void gb_connection_err(struct gb_connection *connection, const char *fmt, ...) vaf.va = &args; pr_err("greybus: [%hhu:%hhu:%hu]: %pV\n", - connection->bundle->intf->module_id, + connection->bundle->intf->module->module_id, connection->bundle->id, connection->bundle_cport_id, &vaf); diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index c90f74c..f6ca89a 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -46,11 +46,14 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv) static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) { + struct gb_module *module = NULL; struct gb_interface *intf = NULL; struct gb_bundle *bundle = NULL; struct gb_connection *connection = NULL; - if (is_gb_interface(dev)) { + if (is_gb_module(dev)) { + module = to_gb_module(dev); + } else if (is_gb_interface(dev)) { intf = to_gb_interface(dev); } else if (is_gb_bundle(dev)) { bundle = to_gb_bundle(dev); diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index ced329a..68382b3 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -24,6 +24,7 @@ #include "greybus_id.h" #include "greybus_manifest.h" #include "manifest.h" +#include "module.h" #include "interface.h" #include "bundle.h" #include "connection.h" @@ -168,10 +169,16 @@ void gb_uart_device_exit(struct gb_connection *connection); int svc_set_route_send(struct gb_bundle *bundle, struct greybus_host_device *hd); +extern struct device_type greybus_module_type; extern struct device_type greybus_interface_type; extern struct device_type greybus_bundle_type; extern struct device_type greybus_connection_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; diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 96897f2..4b502c6 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -74,13 +74,15 @@ gb_interface_match_id(struct gb_interface *intf, return NULL; } +// 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 greybus_host_device *hd, u8 module_id) { struct gb_interface *intf; list_for_each_entry(intf, &hd->interfaces, links) - if (intf->module_id == module_id) + if (intf->module->module_id == module_id) return intf; return NULL; @@ -105,43 +107,51 @@ struct device_type greybus_interface_type = { * * Create a gb_interface 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 + * Returns a pointer to the new interfce or a null pointer if a * failure occurs due to memory exhaustion. */ static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, u8 module_id) { + struct gb_module *module; struct gb_interface *intf; int retval; + u8 interface_id = module_id; - intf = gb_interface_find(hd, module_id); + // FIXME we need an interface id here to check for this properly! + intf = gb_interface_find(hd, interface_id); if (intf) { dev_err(hd->parent, "Duplicate module id %d will not be created\n", module_id); return NULL; } + module = gb_module_find_or_create(hd, module_id); + if (!module) + return NULL; + intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) return NULL; intf->hd = hd; /* XXX refcount? */ - intf->module_id = module_id; + intf->module = module; INIT_LIST_HEAD(&intf->bundles); - intf->dev.parent = hd->parent; + 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->parent->dma_mask; device_initialize(&intf->dev); - dev_set_name(&intf->dev, "%d", module_id); + dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id); retval = device_add(&intf->dev); if (retval) { pr_err("failed to add module device for id 0x%02hhx\n", module_id); put_device(&intf->dev); + put_device(&module->dev); kfree(intf); return NULL; } @@ -169,6 +179,7 @@ static void gb_interface_destroy(struct gb_interface *intf) kfree(intf->product_string); kfree(intf->vendor_string); + put_device(&intf->module->dev); /* kref_put(module->hd); */ device_del(&intf->dev); diff --git a/drivers/staging/greybus/interface.h b/drivers/staging/greybus/interface.h index 5dd2c20..fd5001e 100644 --- a/drivers/staging/greybus/interface.h +++ b/drivers/staging/greybus/interface.h @@ -16,7 +16,7 @@ struct gb_interface { struct list_head bundles; struct list_head links; /* greybus_host_device->interfaces */ - u8 module_id; /* Physical location within the Endo */ + u8 interface_id; /* Physical location within the Endo */ /* Information taken from the manifest module descriptor */ u16 vendor; @@ -25,6 +25,7 @@ struct gb_interface { char *product_string; u64 unique_id; + struct gb_module *module; struct greybus_host_device *hd; }; #define to_gb_interface(d) container_of(d, struct gb_interface, dev) diff --git a/drivers/staging/greybus/kernel_ver.h b/drivers/staging/greybus/kernel_ver.h index 66c2713..f0010a8 100644 --- a/drivers/staging/greybus/kernel_ver.h +++ b/drivers/staging/greybus/kernel_ver.h @@ -21,6 +21,11 @@ } #endif +#ifndef __ATTR_RW +#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ + _name##_show, _name##_store) +#endif + #ifndef DEVICE_ATTR_RO #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) @@ -31,6 +36,11 @@ struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #endif +#ifndef DEVICE_ATTR_RW +#define DEVICE_ATTR_RW(_name) \ + struct device_attribute dev_attr_##_name = __ATTR_RW(_name) +#endif + #ifndef U8_MAX #define U8_MAX ((u8)~0U) #endif /* ! U8_MAX */ diff --git a/drivers/staging/greybus/module.c b/drivers/staging/greybus/module.c new file mode 100644 index 0000000..625e2d4 --- /dev/null +++ b/drivers/staging/greybus/module.c @@ -0,0 +1,140 @@ +/* + * Greybus module code + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#include "greybus.h" + + +/* + * List of modules in the system. We really should just walk the list the + * driver core provides us, but as we have lots of different things on the same + * "bus" at the same time, a single list of modules is simplest for now. + */ +static DEFINE_SPINLOCK(gb_modules_lock); +static LIST_HEAD(module_list); + +/* module sysfs attributes */ +#define gb_module_attr(field, type) \ +static ssize_t field##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gb_module *module = to_gb_module(dev); \ + return sprintf(buf, "%"#type"\n", module->field); \ +} \ +static DEVICE_ATTR_RO(field) + +// FIXME, do we really need this attribute? +gb_module_attr(module_id, x); + +static ssize_t epm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + // FIXME, implement something here + return sprintf(buf, "1\n"); +} + +static ssize_t epm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + // FIXME, implement something here. + return 0; +} +static DEVICE_ATTR_RW(epm); + +static struct attribute *module_attrs[] = { + &dev_attr_module_id.attr, + &dev_attr_epm.attr, + NULL, +}; +ATTRIBUTE_GROUPS(module); + +static void greybus_module_release(struct device *dev) +{ + struct gb_module *module = to_gb_module(dev); + + spin_lock(&gb_modules_lock); + list_del(&module->list); + spin_unlock(&gb_modules_lock); + + kfree(module); +} + +struct device_type greybus_module_type = { + .name = "greybus_module", + .release = greybus_module_release, +}; + +/* + * Search the list of modules in the system. If one is found, return it, with + * the reference count incremented. + */ +static struct gb_module *gb_module_find(u8 module_id) +{ + struct gb_module *module; + + spin_lock(&gb_modules_lock); + list_for_each_entry(module, &module_list, list) { + if (module->module_id == module_id) { + get_device(&module->dev); + goto exit; + } + } + module = NULL; +exit: + spin_unlock(&gb_modules_lock); + return module; +} + +static struct gb_module *gb_module_create(struct greybus_host_device *hd, + u8 module_id) +{ + struct gb_module *module; + int retval; + + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (!module) + return NULL; + + module->module_id = module_id; + module->dev.parent = hd->parent; + module->dev.bus = &greybus_bus_type; + module->dev.type = &greybus_module_type; + module->dev.groups = module_groups; + module->dev.dma_mask = hd->parent->dma_mask; + device_initialize(&module->dev); + dev_set_name(&module->dev, "%d", module_id); + + retval = device_add(&module->dev); + if (retval) { + pr_err("failed to add module device for id 0x%02hhx\n", + module_id); + put_device(&module->dev); + kfree(module); + return NULL; + } + + spin_lock(&gb_modules_lock); + list_add_tail(&module->list, &module_list); + spin_unlock(&gb_modules_lock); + + return module; +} + +struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, + u8 module_id) +{ + struct gb_module *module; + + module = gb_module_find(module_id); + if (module) + return module; + + return gb_module_create(hd, module_id); +} + diff --git a/drivers/staging/greybus/module.h b/drivers/staging/greybus/module.h new file mode 100644 index 0000000..9ca7c28 --- /dev/null +++ b/drivers/staging/greybus/module.h @@ -0,0 +1,28 @@ +/* + * Greybus module code + * + * Copyright 2014 Google Inc. + * + * Released under the GPLv2 only. + */ + +#ifndef __MODULE_H +#define __MODULE_H + +/* Greybus "public" definitions" */ +struct gb_module { + struct device dev; + + struct list_head list; + u8 module_id; /* Physical location within the Endo */ +}; +#define to_gb_module(d) container_of(d, struct gb_module, dev) + +struct greybus_host_device; + +/* Greybus "private" definitions */ +struct gb_module *gb_module_find_or_create(struct greybus_host_device *hd, + u8 module_id); + + +#endif /* __MODULE_H */