Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 1 | /* |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 2 | * Greybus interface code |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 3 | * |
| 4 | * Copyright 2014 Google Inc. |
Alex Elder | a46e967 | 2014-12-12 12:08:42 -0600 | [diff] [blame] | 5 | * Copyright 2014 Linaro Ltd. |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 6 | * |
| 7 | * Released under the GPLv2 only. |
| 8 | */ |
| 9 | |
| 10 | #include "greybus.h" |
| 11 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 12 | /* interface sysfs attributes */ |
| 13 | #define gb_interface_attr(field, type) \ |
| 14 | static ssize_t field##_show(struct device *dev, \ |
| 15 | struct device_attribute *attr, \ |
| 16 | char *buf) \ |
Greg Kroah-Hartman | ab88eb5 | 2014-12-11 17:10:59 -0500 | [diff] [blame] | 17 | { \ |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 18 | struct gb_interface *intf = to_gb_interface(dev); \ |
| 19 | return sprintf(buf, "%"#type"\n", intf->field); \ |
Greg Kroah-Hartman | ab88eb5 | 2014-12-11 17:10:59 -0500 | [diff] [blame] | 20 | } \ |
| 21 | static DEVICE_ATTR_RO(field) |
| 22 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 23 | gb_interface_attr(vendor, x); |
| 24 | gb_interface_attr(product, x); |
| 25 | gb_interface_attr(unique_id, llX); |
| 26 | gb_interface_attr(vendor_string, s); |
| 27 | gb_interface_attr(product_string, s); |
Greg Kroah-Hartman | ab88eb5 | 2014-12-11 17:10:59 -0500 | [diff] [blame] | 28 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 29 | static struct attribute *interface_attrs[] = { |
Greg Kroah-Hartman | ab88eb5 | 2014-12-11 17:10:59 -0500 | [diff] [blame] | 30 | &dev_attr_vendor.attr, |
| 31 | &dev_attr_product.attr, |
| 32 | &dev_attr_unique_id.attr, |
| 33 | &dev_attr_vendor_string.attr, |
| 34 | &dev_attr_product_string.attr, |
| 35 | NULL, |
| 36 | }; |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 37 | ATTRIBUTE_GROUPS(interface); |
Greg Kroah-Hartman | ab88eb5 | 2014-12-11 17:10:59 -0500 | [diff] [blame] | 38 | |
| 39 | |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 40 | /* XXX This could be per-host device */ |
Greg Kroah-Hartman | 4901175 | 2014-12-19 14:56:37 -0800 | [diff] [blame] | 41 | static DEFINE_SPINLOCK(gb_interfaces_lock); |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 42 | |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 43 | // FIXME, odds are you don't want to call this function, rework the caller to |
| 44 | // not need it please. |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 45 | struct gb_interface *gb_interface_find(struct greybus_host_device *hd, |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 46 | u8 interface_id) |
Viresh Kumar | 9ca4d62 | 2014-11-14 17:25:06 +0530 | [diff] [blame] | 47 | { |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 48 | struct gb_interface *intf; |
Viresh Kumar | 9ca4d62 | 2014-11-14 17:25:06 +0530 | [diff] [blame] | 49 | |
Greg Kroah-Hartman | 1cd56a8 | 2014-12-19 14:56:36 -0800 | [diff] [blame] | 50 | list_for_each_entry(intf, &hd->interfaces, links) |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 51 | if (intf->interface_id == interface_id) |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 52 | return intf; |
Viresh Kumar | 9ca4d62 | 2014-11-14 17:25:06 +0530 | [diff] [blame] | 53 | |
| 54 | return NULL; |
| 55 | } |
| 56 | |
Viresh Kumar | 51b5d8d | 2015-05-20 17:33:51 +0530 | [diff] [blame] | 57 | static void gb_interface_release(struct device *dev) |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 58 | { |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 59 | struct gb_interface *intf = to_gb_interface(dev); |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 60 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 61 | kfree(intf); |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 62 | } |
| 63 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 64 | struct device_type greybus_interface_type = { |
| 65 | .name = "greybus_interface", |
Viresh Kumar | 51b5d8d | 2015-05-20 17:33:51 +0530 | [diff] [blame] | 66 | .release = gb_interface_release, |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 67 | }; |
| 68 | |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 69 | /* |
Viresh Kumar | 696e0cc | 2014-11-21 11:26:30 +0530 | [diff] [blame] | 70 | * A Greybus module represents a user-replicable component on an Ara |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 71 | * phone. An interface is the physical connection on that module. A |
| 72 | * module may have more than one interface. |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 73 | * |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 74 | * Create a gb_interface structure to represent a discovered interface. |
| 75 | * The position of interface within the Endo is encoded in "interface_id" |
| 76 | * argument. |
| 77 | * |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 78 | * Returns a pointer to the new interfce or a null pointer if a |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 79 | * failure occurs due to memory exhaustion. |
| 80 | */ |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 81 | static struct gb_interface *gb_interface_create(struct greybus_host_device *hd, |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 82 | u8 interface_id) |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 83 | { |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 84 | struct gb_module *module; |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 85 | struct gb_interface *intf; |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 86 | int retval; |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 87 | |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 88 | intf = gb_interface_find(hd, interface_id); |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 89 | if (intf) { |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 90 | dev_err(hd->parent, "Duplicate interface with interface-id: %d will not be created\n", |
| 91 | interface_id); |
Greg Kroah-Hartman | 066799c | 2014-10-24 18:46:15 +0800 | [diff] [blame] | 92 | return NULL; |
| 93 | } |
| 94 | |
Viresh Kumar | 51e93ae | 2015-05-08 12:58:51 +0530 | [diff] [blame] | 95 | module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id)); |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 96 | if (!module) |
| 97 | return NULL; |
| 98 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 99 | intf = kzalloc(sizeof(*intf), GFP_KERNEL); |
| 100 | if (!intf) |
Viresh Kumar | 71e4938 | 2015-03-24 20:14:29 +0530 | [diff] [blame] | 101 | goto put_module; |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 102 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 103 | intf->hd = hd; /* XXX refcount? */ |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 104 | intf->module = module; |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 105 | intf->interface_id = interface_id; |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 106 | INIT_LIST_HEAD(&intf->bundles); |
Greg Kroah-Hartman | 86cad66 | 2014-12-23 15:16:50 -0800 | [diff] [blame] | 107 | INIT_LIST_HEAD(&intf->manifest_descs); |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 108 | |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 109 | intf->dev.parent = &module->dev; |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 110 | intf->dev.bus = &greybus_bus_type; |
| 111 | intf->dev.type = &greybus_interface_type; |
| 112 | intf->dev.groups = interface_groups; |
| 113 | intf->dev.dma_mask = hd->parent->dma_mask; |
| 114 | device_initialize(&intf->dev); |
Greg Kroah-Hartman | df67155 | 2014-12-21 14:10:26 -0800 | [diff] [blame] | 115 | dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 116 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 117 | retval = device_add(&intf->dev); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 118 | if (retval) { |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 119 | pr_err("failed to add interface device for id 0x%02hhx\n", |
| 120 | interface_id); |
Viresh Kumar | 71e4938 | 2015-03-24 20:14:29 +0530 | [diff] [blame] | 121 | goto free_intf; |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 122 | } |
| 123 | |
Greg Kroah-Hartman | 4901175 | 2014-12-19 14:56:37 -0800 | [diff] [blame] | 124 | spin_lock_irq(&gb_interfaces_lock); |
Greg Kroah-Hartman | 1cd56a8 | 2014-12-19 14:56:36 -0800 | [diff] [blame] | 125 | list_add_tail(&intf->links, &hd->interfaces); |
Greg Kroah-Hartman | 4901175 | 2014-12-19 14:56:37 -0800 | [diff] [blame] | 126 | spin_unlock_irq(&gb_interfaces_lock); |
Viresh Kumar | 0a68a16 | 2014-11-13 18:14:37 +0530 | [diff] [blame] | 127 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 128 | return intf; |
Viresh Kumar | 71e4938 | 2015-03-24 20:14:29 +0530 | [diff] [blame] | 129 | |
| 130 | free_intf: |
| 131 | put_device(&intf->dev); |
| 132 | kfree(intf); |
| 133 | put_module: |
| 134 | put_device(&module->dev); |
| 135 | return NULL; |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | /* |
| 139 | * Tear down a previously set up module. |
| 140 | */ |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 141 | static void gb_interface_destroy(struct gb_interface *intf) |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 142 | { |
Viresh Kumar | 2352a73 | 2015-04-02 17:53:47 +0530 | [diff] [blame] | 143 | struct gb_module *module; |
| 144 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 145 | if (WARN_ON(!intf)) |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 146 | return; |
| 147 | |
Greg Kroah-Hartman | 4901175 | 2014-12-19 14:56:37 -0800 | [diff] [blame] | 148 | spin_lock_irq(&gb_interfaces_lock); |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 149 | list_del(&intf->links); |
Greg Kroah-Hartman | 4901175 | 2014-12-19 14:56:37 -0800 | [diff] [blame] | 150 | spin_unlock_irq(&gb_interfaces_lock); |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 151 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 152 | gb_bundle_destroy(intf); |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 153 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 154 | kfree(intf->product_string); |
| 155 | kfree(intf->vendor_string); |
Alex Elder | e1e9dbd | 2014-10-01 21:54:11 -0500 | [diff] [blame] | 156 | /* kref_put(module->hd); */ |
| 157 | |
Viresh Kumar | 2352a73 | 2015-04-02 17:53:47 +0530 | [diff] [blame] | 158 | module = intf->module; |
| 159 | device_unregister(&intf->dev); |
Greg Kroah-Hartman | a4d9150 | 2015-04-07 20:27:15 +0200 | [diff] [blame] | 160 | put_device(&module->dev); |
Alex Elder | 574341c | 2014-10-16 06:35:35 -0500 | [diff] [blame] | 161 | } |
| 162 | |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 163 | /** |
Viresh Kumar | 51b5d8d | 2015-05-20 17:33:51 +0530 | [diff] [blame] | 164 | * gb_interface_add |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 165 | * |
Viresh Kumar | a93db2d | 2015-04-01 20:32:02 +0530 | [diff] [blame] | 166 | * Pass in a buffer that _should_ contain a Greybus manifest |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 167 | * and register a greybus device structure with the kernel core. |
| 168 | */ |
Viresh Kumar | 51b5d8d | 2015-05-20 17:33:51 +0530 | [diff] [blame] | 169 | void gb_interface_add(struct greybus_host_device *hd, u8 interface_id, u8 *data, |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 170 | int size) |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 171 | { |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 172 | struct gb_interface *intf; |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 173 | |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 174 | intf = gb_interface_create(hd, interface_id); |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 175 | if (!intf) { |
| 176 | dev_err(hd->parent, "failed to create interface\n"); |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 177 | return; |
| 178 | } |
| 179 | |
| 180 | /* |
| 181 | * Parse the manifest and build up our data structures |
| 182 | * representing what's in it. |
| 183 | */ |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 184 | if (!gb_manifest_parse(intf, data, size)) { |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 185 | dev_err(hd->parent, "manifest error\n"); |
Greg Kroah-Hartman | 13e6aac | 2014-12-19 14:56:35 -0800 | [diff] [blame] | 186 | goto err_parse; |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | /* |
| 190 | * XXX |
| 191 | * We've successfully parsed the manifest. Now we need to |
| 192 | * allocate CPort Id's for connecting to the CPorts found on |
| 193 | * other modules. For each of these, establish a connection |
| 194 | * between the local and remote CPorts (including |
| 195 | * configuring the switch to allow them to communicate). |
| 196 | */ |
| 197 | |
| 198 | return; |
| 199 | |
Greg Kroah-Hartman | 13e6aac | 2014-12-19 14:56:35 -0800 | [diff] [blame] | 200 | err_parse: |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 201 | gb_interface_destroy(intf); |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 202 | } |
| 203 | |
Viresh Kumar | 51b5d8d | 2015-05-20 17:33:51 +0530 | [diff] [blame] | 204 | void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 205 | { |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 206 | struct gb_interface *intf = gb_interface_find(hd, interface_id); |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 207 | |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 208 | if (intf) |
| 209 | gb_interface_destroy(intf); |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 210 | else |
Viresh Kumar | c9d9d0d | 2015-04-01 20:31:58 +0530 | [diff] [blame] | 211 | dev_err(hd->parent, "interface id %d not found\n", |
| 212 | interface_id); |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 213 | } |
| 214 | |
Viresh Kumar | 51b5d8d | 2015-05-20 17:33:51 +0530 | [diff] [blame] | 215 | void gb_interfaces_remove(struct greybus_host_device *hd) |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 216 | { |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 217 | struct gb_interface *intf, *temp; |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 218 | |
Greg Kroah-Hartman | 1cd56a8 | 2014-12-19 14:56:36 -0800 | [diff] [blame] | 219 | list_for_each_entry_safe(intf, temp, &hd->interfaces, links) |
Greg Kroah-Hartman | 4ab9b3c | 2014-12-19 14:56:31 -0800 | [diff] [blame] | 220 | gb_interface_destroy(intf); |
Viresh Kumar | 676daaf | 2014-11-14 17:25:07 +0530 | [diff] [blame] | 221 | } |