greybus: svc: Read and clear module's boot status
[linux-2.6-block.git] / drivers / staging / greybus / interface.c
CommitLineData
e1e9dbdd 1/*
4ab9b3c2 2 * Greybus interface code
e1e9dbdd
AE
3 *
4 * Copyright 2014 Google Inc.
a46e9671 5 * Copyright 2014 Linaro Ltd.
e1e9dbdd
AE
6 *
7 * Released under the GPLv2 only.
8 */
9
10#include "greybus.h"
11
4ab9b3c2
GKH
12/* interface sysfs attributes */
13#define gb_interface_attr(field, type) \
14static ssize_t field##_show(struct device *dev, \
15 struct device_attribute *attr, \
16 char *buf) \
ab88eb58 17{ \
4ab9b3c2 18 struct gb_interface *intf = to_gb_interface(dev); \
505c9d27 19 return scnprintf(buf, PAGE_SIZE, "%"#type"\n", intf->field); \
ab88eb58
GKH
20} \
21static DEVICE_ATTR_RO(field)
22
c3add788 23gb_interface_attr(device_id, d);
4ab9b3c2
GKH
24gb_interface_attr(vendor, x);
25gb_interface_attr(product, x);
26gb_interface_attr(unique_id, llX);
27gb_interface_attr(vendor_string, s);
28gb_interface_attr(product_string, s);
ab88eb58 29
4ab9b3c2 30static struct attribute *interface_attrs[] = {
c3add788 31 &dev_attr_device_id.attr,
ab88eb58
GKH
32 &dev_attr_vendor.attr,
33 &dev_attr_product.attr,
34 &dev_attr_unique_id.attr,
35 &dev_attr_vendor_string.attr,
36 &dev_attr_product_string.attr,
37 NULL,
38};
4ab9b3c2 39ATTRIBUTE_GROUPS(interface);
ab88eb58
GKH
40
41
e1e9dbdd 42/* XXX This could be per-host device */
4901175f 43static DEFINE_SPINLOCK(gb_interfaces_lock);
e1e9dbdd 44
df671553
GKH
45// FIXME, odds are you don't want to call this function, rework the caller to
46// not need it please.
4ab9b3c2 47struct gb_interface *gb_interface_find(struct greybus_host_device *hd,
c9d9d0d4 48 u8 interface_id)
9ca4d62f 49{
4ab9b3c2 50 struct gb_interface *intf;
9ca4d62f 51
1cd56a80 52 list_for_each_entry(intf, &hd->interfaces, links)
c9d9d0d4 53 if (intf->interface_id == interface_id)
4ab9b3c2 54 return intf;
9ca4d62f
VK
55
56 return NULL;
57}
58
51b5d8d7 59static void gb_interface_release(struct device *dev)
697e55d3 60{
4ab9b3c2 61 struct gb_interface *intf = to_gb_interface(dev);
697e55d3 62
4ab9b3c2 63 kfree(intf);
697e55d3
AE
64}
65
4ab9b3c2
GKH
66struct device_type greybus_interface_type = {
67 .name = "greybus_interface",
51b5d8d7 68 .release = gb_interface_release,
f0f61b90
GKH
69};
70
6c68da26
VK
71/*
72 * Create kernel structures corresponding to a bundle and connection for
7a24a3f6 73 * managing control/svc CPort.
6c68da26 74 */
7a24a3f6 75int gb_create_bundle_connection(struct gb_interface *intf, u8 class)
6c68da26
VK
76{
77 struct gb_bundle *bundle;
7a24a3f6
VK
78 u32 ida_start, ida_end;
79 u8 bundle_id, protocol_id;
80 u16 cport_id;
81
82 if (class == GREYBUS_CLASS_CONTROL) {
83 protocol_id = GREYBUS_PROTOCOL_CONTROL;
84 bundle_id = GB_CONTROL_BUNDLE_ID;
85 cport_id = GB_CONTROL_CPORT_ID;
86 ida_start = 0;
144670c2 87 ida_end = intf->hd->num_cports - 1;
7a24a3f6
VK
88 } else if (class == GREYBUS_CLASS_SVC) {
89 protocol_id = GREYBUS_PROTOCOL_SVC;
90 bundle_id = GB_SVC_BUNDLE_ID;
91 cport_id = GB_SVC_CPORT_ID;
92 ida_start = GB_SVC_CPORT_ID;
93 ida_end = GB_SVC_CPORT_ID + 1;
94 } else {
95 WARN_ON(1);
96 return -EINVAL;
97 }
6c68da26 98
7a24a3f6 99 bundle = gb_bundle_create(intf, bundle_id, class);
6c68da26
VK
100 if (!bundle)
101 return -EINVAL;
102
7a24a3f6
VK
103 if (!gb_connection_create_range(bundle->intf->hd, bundle, &bundle->dev,
104 cport_id, protocol_id, ida_start,
105 ida_end))
6c68da26
VK
106 return -EINVAL;
107
6c68da26
VK
108 return 0;
109}
110
e1e9dbdd 111/*
464dc8cb 112 * A Greybus module represents a user-replaceable component on an Ara
4ab9b3c2
GKH
113 * phone. An interface is the physical connection on that module. A
114 * module may have more than one interface.
e1e9dbdd 115 *
c9d9d0d4
VK
116 * Create a gb_interface structure to represent a discovered interface.
117 * The position of interface within the Endo is encoded in "interface_id"
118 * argument.
119 *
df671553 120 * Returns a pointer to the new interfce or a null pointer if a
e1e9dbdd
AE
121 * failure occurs due to memory exhaustion.
122 */
6c68da26
VK
123struct gb_interface *gb_interface_create(struct greybus_host_device *hd,
124 u8 interface_id)
e1e9dbdd 125{
df671553 126 struct gb_module *module;
4ab9b3c2 127 struct gb_interface *intf;
f0f61b90 128 int retval;
e1e9dbdd 129
df671553 130 intf = gb_interface_find(hd, interface_id);
4ab9b3c2 131 if (intf) {
c9d9d0d4
VK
132 dev_err(hd->parent, "Duplicate interface with interface-id: %d will not be created\n",
133 interface_id);
066799c1
GKH
134 return NULL;
135 }
136
51e93aea 137 module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id));
df671553
GKH
138 if (!module)
139 return NULL;
140
4ab9b3c2
GKH
141 intf = kzalloc(sizeof(*intf), GFP_KERNEL);
142 if (!intf)
71e49380 143 goto put_module;
e1e9dbdd 144
4ab9b3c2 145 intf->hd = hd; /* XXX refcount? */
df671553 146 intf->module = module;
c9d9d0d4 147 intf->interface_id = interface_id;
4ab9b3c2 148 INIT_LIST_HEAD(&intf->bundles);
86cad666 149 INIT_LIST_HEAD(&intf->manifest_descs);
e1e9dbdd 150
c3add788
VK
151 /* Invalid device id to start with */
152 intf->device_id = GB_DEVICE_ID_BAD;
153
df671553 154 intf->dev.parent = &module->dev;
4ab9b3c2
GKH
155 intf->dev.bus = &greybus_bus_type;
156 intf->dev.type = &greybus_interface_type;
157 intf->dev.groups = interface_groups;
158 intf->dev.dma_mask = hd->parent->dma_mask;
159 device_initialize(&intf->dev);
df671553 160 dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id);
f0f61b90 161
4ab9b3c2 162 retval = device_add(&intf->dev);
f0f61b90 163 if (retval) {
c9d9d0d4
VK
164 pr_err("failed to add interface device for id 0x%02hhx\n",
165 interface_id);
71e49380 166 goto free_intf;
f0f61b90
GKH
167 }
168
4901175f 169 spin_lock_irq(&gb_interfaces_lock);
928f2abd 170 list_add(&intf->links, &hd->interfaces);
4901175f 171 spin_unlock_irq(&gb_interfaces_lock);
0a68a16b 172
4ab9b3c2 173 return intf;
71e49380
VK
174
175free_intf:
176 put_device(&intf->dev);
177 kfree(intf);
178put_module:
179 put_device(&module->dev);
180 return NULL;
e1e9dbdd
AE
181}
182
183/*
184 * Tear down a previously set up module.
185 */
d45b1b86 186static void interface_destroy(struct gb_interface *intf)
e1e9dbdd 187{
2352a732 188 struct gb_module *module;
fe53b45c
AE
189 struct gb_bundle *bundle;
190 struct gb_bundle *next;
2352a732 191
4ab9b3c2 192 if (WARN_ON(!intf))
e1e9dbdd
AE
193 return;
194
4901175f 195 spin_lock_irq(&gb_interfaces_lock);
4ab9b3c2 196 list_del(&intf->links);
4901175f 197 spin_unlock_irq(&gb_interfaces_lock);
e1e9dbdd 198
fe53b45c
AE
199 list_for_each_entry_safe(bundle, next, &intf->bundles, links)
200 gb_bundle_destroy(bundle);
697e55d3 201
4ab9b3c2
GKH
202 kfree(intf->product_string);
203 kfree(intf->vendor_string);
e1e9dbdd 204
2352a732
VK
205 module = intf->module;
206 device_unregister(&intf->dev);
a4d9150c 207 put_device(&module->dev);
574341c6
AE
208}
209
676daaf4 210/**
b950dc28 211 * gb_interface_init
676daaf4 212 *
6c68da26
VK
213 * Create connection for control CPort and then request/parse manifest.
214 * Finally initialize all the bundles to set routes via SVC and initialize all
215 * connections.
676daaf4 216 */
6c68da26 217int gb_interface_init(struct gb_interface *intf, u8 device_id)
676daaf4 218{
6c68da26
VK
219 int ret, size;
220 void *manifest;
221
c3add788
VK
222 intf->device_id = device_id;
223
6c68da26 224 /* Establish control CPort connection */
7a24a3f6 225 ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_CONTROL);
6c68da26
VK
226 if (ret) {
227 dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret);
228 return ret;
229 }
676daaf4 230
6c68da26
VK
231 /* Get manifest size using control protocol on CPort */
232 size = gb_control_get_manifest_size_operation(intf);
233 if (size <= 0) {
234 dev_err(&intf->dev, "%s: Failed to get manifest size (%d)\n",
235 __func__, size);
236 if (size)
237 return size;
238 else
239 return -EINVAL;
240 }
241
242 manifest = kmalloc(size, GFP_KERNEL);
243 if (!manifest)
244 return -ENOMEM;
245
246 /* Get manifest using control protocol on CPort */
247 ret = gb_control_get_manifest_operation(intf, manifest, size);
248 if (ret) {
249 dev_err(&intf->dev, "%s: Failed to get manifest\n", __func__);
250 goto free_manifest;
676daaf4
VK
251 }
252
253 /*
6c68da26
VK
254 * Parse the manifest and build up our data structures representing
255 * what's in it.
676daaf4 256 */
6c68da26
VK
257 if (!gb_manifest_parse(intf, manifest, size)) {
258 dev_err(&intf->dev, "%s: Failed to parse manifest\n", __func__);
259 ret = -EINVAL;
260 goto free_manifest;
676daaf4
VK
261 }
262
263 /*
264 * XXX
265 * We've successfully parsed the manifest. Now we need to
266 * allocate CPort Id's for connecting to the CPorts found on
267 * other modules. For each of these, establish a connection
268 * between the local and remote CPorts (including
269 * configuring the switch to allow them to communicate).
270 */
271
6c68da26
VK
272free_manifest:
273 kfree(manifest);
274 return ret;
676daaf4
VK
275}
276
51b5d8d7 277void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id)
676daaf4 278{
c9d9d0d4 279 struct gb_interface *intf = gb_interface_find(hd, interface_id);
676daaf4 280
4ab9b3c2 281 if (intf)
d45b1b86 282 interface_destroy(intf);
676daaf4 283 else
c9d9d0d4
VK
284 dev_err(hd->parent, "interface id %d not found\n",
285 interface_id);
676daaf4
VK
286}
287
51b5d8d7 288void gb_interfaces_remove(struct greybus_host_device *hd)
676daaf4 289{
4ab9b3c2 290 struct gb_interface *intf, *temp;
676daaf4 291
1cd56a80 292 list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
d45b1b86 293 interface_destroy(intf);
676daaf4 294}