Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 1 | /* |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 2 | * Greybus bundles |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -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 | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 6 | * |
| 7 | * Released under the GPLv2 only. |
| 8 | */ |
| 9 | |
| 10 | #include "greybus.h" |
| 11 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 12 | static void gb_bundle_connections_exit(struct gb_bundle *bundle); |
| 13 | static int gb_bundle_connections_init(struct gb_bundle *bundle); |
| 14 | |
| 15 | |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 16 | static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, |
| 17 | char *buf) |
| 18 | { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 19 | struct gb_bundle *bundle = to_gb_bundle(dev); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 20 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 21 | return sprintf(buf, "%d", bundle->device_id); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 22 | } |
| 23 | static DEVICE_ATTR_RO(device_id); |
| 24 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 25 | static struct attribute *bundle_attrs[] = { |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 26 | &dev_attr_device_id.attr, |
| 27 | NULL, |
| 28 | }; |
| 29 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 30 | ATTRIBUTE_GROUPS(bundle); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 31 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 32 | static void gb_bundle_release(struct device *dev) |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 33 | { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 34 | struct gb_bundle *bundle = to_gb_bundle(dev); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 35 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 36 | kfree(bundle); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 37 | } |
| 38 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 39 | struct device_type greybus_bundle_type = { |
| 40 | .name = "greybus_bundle", |
| 41 | .release = gb_bundle_release, |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 42 | }; |
| 43 | |
| 44 | |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 45 | /* XXX This could be per-host device or per-module */ |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 46 | static DEFINE_SPINLOCK(gb_bundles_lock); |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 47 | |
| 48 | /* |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 49 | * Create a gb_bundle structure to represent a discovered |
| 50 | * bundle. Returns a pointer to the new bundle or a null |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 51 | * pointer if a failure occurs due to memory exhaustion. |
| 52 | */ |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 53 | struct gb_bundle *gb_bundle_create(struct gb_interface_block *gb_ib, u8 interface_id) |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 54 | { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 55 | struct gb_bundle *bundle; |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 56 | int retval; |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 57 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 58 | bundle = kzalloc(sizeof(*bundle), GFP_KERNEL); |
| 59 | if (!bundle) |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 60 | return NULL; |
| 61 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 62 | bundle->gb_ib = gb_ib; |
| 63 | bundle->id = interface_id; |
| 64 | bundle->device_id = 0xff; /* Invalid device id to start with */ |
| 65 | INIT_LIST_HEAD(&bundle->connections); |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 66 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 67 | /* Build up the bundle device structures and register it with the |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 68 | * driver core */ |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 69 | bundle->dev.parent = &gb_ib->dev; |
| 70 | bundle->dev.bus = &greybus_bus_type; |
| 71 | bundle->dev.type = &greybus_bundle_type; |
| 72 | bundle->dev.groups = bundle_groups; |
| 73 | device_initialize(&bundle->dev); |
| 74 | dev_set_name(&bundle->dev, "%d:%d", gb_ib->module_id, interface_id); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 75 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 76 | retval = device_add(&bundle->dev); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 77 | if (retval) { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 78 | pr_err("failed to add bundle device for id 0x%02hhx\n", |
Alex Elder | 6b09938 | 2014-11-05 16:03:12 -0600 | [diff] [blame] | 79 | interface_id); |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 80 | put_device(&bundle->dev); |
| 81 | kfree(bundle); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 82 | return NULL; |
| 83 | } |
| 84 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 85 | spin_lock_irq(&gb_bundles_lock); |
| 86 | list_add_tail(&bundle->links, &gb_ib->interfaces); |
| 87 | spin_unlock_irq(&gb_bundles_lock); |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 88 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 89 | return bundle; |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | /* |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 93 | * Tear down a previously set up bundle. |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 94 | */ |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 95 | void gb_bundle_destroy(struct gb_interface_block *gb_ib) |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 96 | { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 97 | struct gb_bundle *bundle; |
| 98 | struct gb_bundle *temp; |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 99 | |
Greg Kroah-Hartman | 4ec7b07 | 2014-12-11 17:10:56 -0500 | [diff] [blame] | 100 | if (WARN_ON(!gb_ib)) |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 101 | return; |
| 102 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 103 | spin_lock_irq(&gb_bundles_lock); |
| 104 | list_for_each_entry_safe(bundle, temp, &gb_ib->interfaces, links) { |
| 105 | list_del(&bundle->links); |
| 106 | gb_bundle_connections_exit(bundle); |
| 107 | device_del(&bundle->dev); |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 108 | } |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 109 | spin_unlock_irq(&gb_bundles_lock); |
Alex Elder | 8c12cde | 2014-10-01 21:54:12 -0500 | [diff] [blame] | 110 | } |
Alex Elder | 574341c | 2014-10-16 06:35:35 -0500 | [diff] [blame] | 111 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 112 | int gb_bundle_init(struct gb_interface_block *gb_ib, u8 bundle_id, u8 device_id) |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 113 | { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 114 | struct gb_bundle *bundle; |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 115 | int ret; |
| 116 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 117 | bundle = gb_bundle_find(gb_ib, bundle_id); |
| 118 | if (!bundle) { |
| 119 | dev_err(gb_ib->hd->parent, "bundle %hhu not found\n", |
| 120 | bundle_id); |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 121 | return -ENOENT; |
| 122 | } |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 123 | bundle->device_id = device_id; |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 124 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 125 | ret = svc_set_route_send(bundle, gb_ib->hd); |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 126 | if (ret) { |
Greg Kroah-Hartman | 4ec7b07 | 2014-12-11 17:10:56 -0500 | [diff] [blame] | 127 | dev_err(gb_ib->hd->parent, "failed to set route (%d)\n", ret); |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 128 | return ret; |
| 129 | } |
| 130 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 131 | ret = gb_bundle_connections_init(bundle); |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 132 | if (ret) { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 133 | dev_err(gb_ib->hd->parent, "interface bundle init error %d\n", |
Viresh Kumar | 2206ea9 | 2014-11-14 17:25:08 +0530 | [diff] [blame] | 134 | ret); |
| 135 | /* XXX clear route */ |
| 136 | return ret; |
| 137 | } |
| 138 | |
| 139 | return 0; |
| 140 | } |
| 141 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 142 | struct gb_bundle *gb_bundle_find(struct gb_interface_block *gb_ib, u8 bundle_id) |
Matt Porter | 1a4c013 | 2014-10-21 22:43:30 -0400 | [diff] [blame] | 143 | { |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 144 | struct gb_bundle *bundle; |
Matt Porter | 1a4c013 | 2014-10-21 22:43:30 -0400 | [diff] [blame] | 145 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 146 | spin_lock_irq(&gb_bundles_lock); |
| 147 | list_for_each_entry(bundle, &gb_ib->interfaces, links) |
| 148 | if (bundle->id == bundle_id) { |
| 149 | spin_unlock_irq(&gb_bundles_lock); |
| 150 | return bundle; |
Greg Kroah-Hartman | f0f61b9 | 2014-10-24 17:34:46 +0800 | [diff] [blame] | 151 | } |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 152 | spin_unlock_irq(&gb_bundles_lock); |
Matt Porter | 1a4c013 | 2014-10-21 22:43:30 -0400 | [diff] [blame] | 153 | |
| 154 | return NULL; |
| 155 | } |
| 156 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 157 | static int gb_bundle_connections_init(struct gb_bundle *bundle) |
Alex Elder | 574341c | 2014-10-16 06:35:35 -0500 | [diff] [blame] | 158 | { |
| 159 | struct gb_connection *connection; |
| 160 | int ret = 0; |
| 161 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 162 | list_for_each_entry(connection, &bundle->connections, bundle_links) { |
Alex Elder | 574341c | 2014-10-16 06:35:35 -0500 | [diff] [blame] | 163 | ret = gb_connection_init(connection); |
| 164 | if (ret) |
| 165 | break; |
| 166 | } |
| 167 | |
| 168 | return ret; |
| 169 | } |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 170 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 171 | static void gb_bundle_connections_exit(struct gb_bundle *bundle) |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 172 | { |
| 173 | struct gb_connection *connection; |
| 174 | struct gb_connection *next; |
| 175 | |
Greg Kroah-Hartman | 1db0a5f | 2014-12-12 17:10:17 -0500 | [diff] [blame^] | 176 | list_for_each_entry_safe(connection, next, &bundle->connections, |
| 177 | bundle_links) { |
Alex Elder | 697e55d | 2014-10-20 23:01:04 -0500 | [diff] [blame] | 178 | gb_connection_exit(connection); |
| 179 | gb_connection_destroy(connection); |
| 180 | } |
| 181 | } |