diff options
author | Octavian Purdila <octavian.purdila@intel.com> | 2014-11-06 08:48:03 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2014-11-10 11:30:05 -0500 |
commit | 338a128142975439a19ab3c91480bc9d5a71f033 (patch) | |
tree | 429db757bfed482b0610b470d1e41e94b5206329 /drivers/mfd/dln2.c | |
parent | a7975473cc41773d9f6d8ea72b48c7656e6cd0f6 (diff) |
mfd: Add support for Diolan DLN-2 devices
This patch implements the USB part of the Diolan USB-I2C/SPI/GPIO
Master Adapter DLN-2. Details about the device can be found here:
https://www.diolan.com/i2c/i2c_interface.html.
Information about the USB protocol can be found in the Programmer's
Reference Manual [1], see section 1.7.
Because the hardware has a single transmit endpoint and a single
receive endpoint the communication between the various DLN2 drivers
and the hardware will be muxed/demuxed by this driver.
Each DLN2 module will be identified by the handle field within the DLN2
message header. If a DLN2 module issues multiple commands in parallel
they will be identified by the echo counter field in the message header.
The DLN2 modules can use the dln2_transfer() function to issue a
command and wait for its response. They can also register a callback
that is going to be called when a specific event id is generated by
the device (e.g. GPIO interrupts). The device uses handle 0 for
sending events.
[1] https://www.diolan.com/downloads/dln-api-manual.pdf
Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Reviewed-by: Johan Hovold <johan@kernel.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd/dln2.c')
-rw-r--r-- | drivers/mfd/dln2.c | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c new file mode 100644 index 000000000000..9765a174d2c5 --- /dev/null +++ b/drivers/mfd/dln2.c | |||
@@ -0,0 +1,763 @@ | |||
1 | /* | ||
2 | * Driver for the Diolan DLN-2 USB adapter | ||
3 | * | ||
4 | * Copyright (c) 2014 Intel Corporation | ||
5 | * | ||
6 | * Derived from: | ||
7 | * i2c-diolan-u2c.c | ||
8 | * Copyright (c) 2010-2011 Ericsson AB | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/mutex.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/mfd/core.h> | ||
24 | #include <linux/mfd/dln2.h> | ||
25 | #include <linux/rculist.h> | ||
26 | |||
27 | struct dln2_header { | ||
28 | __le16 size; | ||
29 | __le16 id; | ||
30 | __le16 echo; | ||
31 | __le16 handle; | ||
32 | }; | ||
33 | |||
34 | struct dln2_response { | ||
35 | struct dln2_header hdr; | ||
36 | __le16 result; | ||
37 | }; | ||
38 | |||
39 | #define DLN2_GENERIC_MODULE_ID 0x00 | ||
40 | #define DLN2_GENERIC_CMD(cmd) DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID) | ||
41 | #define CMD_GET_DEVICE_VER DLN2_GENERIC_CMD(0x30) | ||
42 | #define CMD_GET_DEVICE_SN DLN2_GENERIC_CMD(0x31) | ||
43 | |||
44 | #define DLN2_HW_ID 0x200 | ||
45 | #define DLN2_USB_TIMEOUT 200 /* in ms */ | ||
46 | #define DLN2_MAX_RX_SLOTS 16 | ||
47 | #define DLN2_MAX_URBS 16 | ||
48 | #define DLN2_RX_BUF_SIZE 512 | ||
49 | |||
50 | enum dln2_handle { | ||
51 | DLN2_HANDLE_EVENT = 0, /* don't change, hardware defined */ | ||
52 | DLN2_HANDLE_CTRL, | ||
53 | DLN2_HANDLE_GPIO, | ||
54 | DLN2_HANDLE_I2C, | ||
55 | DLN2_HANDLES | ||
56 | }; | ||
57 | |||
58 | /* | ||
59 | * Receive context used between the receive demultiplexer and the transfer | ||
60 | * routine. While sending a request the transfer routine will look for a free | ||
61 | * receive context and use it to wait for a response and to receive the URB and | ||
62 | * thus the response data. | ||
63 | */ | ||
64 | struct dln2_rx_context { | ||
65 | /* completion used to wait for a response */ | ||
66 | struct completion done; | ||
67 | |||
68 | /* if non-NULL the URB contains the response */ | ||
69 | struct urb *urb; | ||
70 | |||
71 | /* if true then this context is used to wait for a response */ | ||
72 | bool in_use; | ||
73 | }; | ||
74 | |||
75 | /* | ||
76 | * Receive contexts for a particular DLN2 module (i2c, gpio, etc.). We use the | ||
77 | * handle header field to identify the module in dln2_dev.mod_rx_slots and then | ||
78 | * the echo header field to index the slots field and find the receive context | ||
79 | * for a particular request. | ||
80 | */ | ||
81 | struct dln2_mod_rx_slots { | ||
82 | /* RX slots bitmap */ | ||
83 | DECLARE_BITMAP(bmap, DLN2_MAX_RX_SLOTS); | ||
84 | |||
85 | /* used to wait for a free RX slot */ | ||
86 | wait_queue_head_t wq; | ||
87 | |||
88 | /* used to wait for an RX operation to complete */ | ||
89 | struct dln2_rx_context slots[DLN2_MAX_RX_SLOTS]; | ||
90 | |||
91 | /* avoid races between alloc/free_rx_slot and dln2_rx_transfer */ | ||
92 | spinlock_t lock; | ||
93 | }; | ||
94 | |||
95 | struct dln2_dev { | ||
96 | struct usb_device *usb_dev; | ||
97 | struct usb_interface *interface; | ||
98 | u8 ep_in; | ||
99 | u8 ep_out; | ||
100 | |||
101 | struct urb *rx_urb[DLN2_MAX_URBS]; | ||
102 | void *rx_buf[DLN2_MAX_URBS]; | ||
103 | |||
104 | struct dln2_mod_rx_slots mod_rx_slots[DLN2_HANDLES]; | ||
105 | |||
106 | struct list_head event_cb_list; | ||
107 | spinlock_t event_cb_lock; | ||
108 | |||
109 | bool disconnect; | ||
110 | int active_transfers; | ||
111 | wait_queue_head_t disconnect_wq; | ||
112 | spinlock_t disconnect_lock; | ||
113 | }; | ||
114 | |||
115 | struct dln2_event_cb_entry { | ||
116 | struct list_head list; | ||
117 | u16 id; | ||
118 | struct platform_device *pdev; | ||
119 | dln2_event_cb_t callback; | ||
120 | }; | ||
121 | |||
122 | int dln2_register_event_cb(struct platform_device *pdev, u16 id, | ||
123 | dln2_event_cb_t event_cb) | ||
124 | { | ||
125 | struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); | ||
126 | struct dln2_event_cb_entry *i, *entry; | ||
127 | unsigned long flags; | ||
128 | int ret = 0; | ||
129 | |||
130 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
131 | if (!entry) | ||
132 | return -ENOMEM; | ||
133 | |||
134 | entry->id = id; | ||
135 | entry->callback = event_cb; | ||
136 | entry->pdev = pdev; | ||
137 | |||
138 | spin_lock_irqsave(&dln2->event_cb_lock, flags); | ||
139 | |||
140 | list_for_each_entry(i, &dln2->event_cb_list, list) { | ||
141 | if (i->id == id) { | ||
142 | ret = -EBUSY; | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | if (!ret) | ||
148 | list_add_rcu(&entry->list, &dln2->event_cb_list); | ||
149 | |||
150 | spin_unlock_irqrestore(&dln2->event_cb_lock, flags); | ||
151 | |||
152 | if (ret) | ||
153 | kfree(entry); | ||
154 | |||
155 | return ret; | ||
156 | } | ||
157 | EXPORT_SYMBOL(dln2_register_event_cb); | ||
158 | |||
159 | void dln2_unregister_event_cb(struct platform_device *pdev, u16 id) | ||
160 | { | ||
161 | struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); | ||
162 | struct dln2_event_cb_entry *i; | ||
163 | unsigned long flags; | ||
164 | bool found = false; | ||
165 | |||
166 | spin_lock_irqsave(&dln2->event_cb_lock, flags); | ||
167 | |||
168 | list_for_each_entry(i, &dln2->event_cb_list, list) { | ||
169 | if (i->id == id) { | ||
170 | list_del_rcu(&i->list); | ||
171 | found = true; | ||
172 | break; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | spin_unlock_irqrestore(&dln2->event_cb_lock, flags); | ||
177 | |||
178 | if (found) { | ||
179 | synchronize_rcu(); | ||
180 | kfree(i); | ||
181 | } | ||
182 | } | ||
183 | EXPORT_SYMBOL(dln2_unregister_event_cb); | ||
184 | |||
185 | /* | ||
186 | * Returns true if a valid transfer slot is found. In this case the URB must not | ||
187 | * be resubmitted immediately in dln2_rx as we need the data when dln2_transfer | ||
188 | * is woke up. It will be resubmitted there. | ||
189 | */ | ||
190 | static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb, | ||
191 | u16 handle, u16 rx_slot) | ||
192 | { | ||
193 | struct device *dev = &dln2->interface->dev; | ||
194 | struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; | ||
195 | struct dln2_rx_context *rxc; | ||
196 | bool valid_slot = false; | ||
197 | |||
198 | rxc = &rxs->slots[rx_slot]; | ||
199 | |||
200 | /* | ||
201 | * No need to disable interrupts as this lock is not taken in interrupt | ||
202 | * context elsewhere in this driver. This function (or its callers) are | ||
203 | * also not exported to other modules. | ||
204 | */ | ||
205 | spin_lock(&rxs->lock); | ||
206 | if (rxc->in_use && !rxc->urb) { | ||
207 | rxc->urb = urb; | ||
208 | complete(&rxc->done); | ||
209 | valid_slot = true; | ||
210 | } | ||
211 | spin_unlock(&rxs->lock); | ||
212 | |||
213 | if (!valid_slot) | ||
214 | dev_warn(dev, "bad/late response %d/%d\n", handle, rx_slot); | ||
215 | |||
216 | return valid_slot; | ||
217 | } | ||
218 | |||
219 | static void dln2_run_event_callbacks(struct dln2_dev *dln2, u16 id, u16 echo, | ||
220 | void *data, int len) | ||
221 | { | ||
222 | struct dln2_event_cb_entry *i; | ||
223 | |||
224 | rcu_read_lock(); | ||
225 | |||
226 | list_for_each_entry_rcu(i, &dln2->event_cb_list, list) { | ||
227 | if (i->id == id) { | ||
228 | i->callback(i->pdev, echo, data, len); | ||
229 | break; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | rcu_read_unlock(); | ||
234 | } | ||
235 | |||
236 | static void dln2_rx(struct urb *urb) | ||
237 | { | ||
238 | struct dln2_dev *dln2 = urb->context; | ||
239 | struct dln2_header *hdr = urb->transfer_buffer; | ||
240 | struct device *dev = &dln2->interface->dev; | ||
241 | u16 id, echo, handle, size; | ||
242 | u8 *data; | ||
243 | int len; | ||
244 | int err; | ||
245 | |||
246 | switch (urb->status) { | ||
247 | case 0: | ||
248 | /* success */ | ||
249 | break; | ||
250 | case -ECONNRESET: | ||
251 | case -ENOENT: | ||
252 | case -ESHUTDOWN: | ||
253 | case -EPIPE: | ||
254 | /* this urb is terminated, clean up */ | ||
255 | dev_dbg(dev, "urb shutting down with status %d\n", urb->status); | ||
256 | return; | ||
257 | default: | ||
258 | dev_dbg(dev, "nonzero urb status received %d\n", urb->status); | ||
259 | goto out; | ||
260 | } | ||
261 | |||
262 | if (urb->actual_length < sizeof(struct dln2_header)) { | ||
263 | dev_err(dev, "short response: %d\n", urb->actual_length); | ||
264 | goto out; | ||
265 | } | ||
266 | |||
267 | handle = le16_to_cpu(hdr->handle); | ||
268 | id = le16_to_cpu(hdr->id); | ||
269 | echo = le16_to_cpu(hdr->echo); | ||
270 | size = le16_to_cpu(hdr->size); | ||
271 | |||
272 | if (size != urb->actual_length) { | ||
273 | dev_err(dev, "size mismatch: handle %x cmd %x echo %x size %d actual %d\n", | ||
274 | handle, id, echo, size, urb->actual_length); | ||
275 | goto out; | ||
276 | } | ||
277 | |||
278 | if (handle >= DLN2_HANDLES) { | ||
279 | dev_warn(dev, "invalid handle %d\n", handle); | ||
280 | goto out; | ||
281 | } | ||
282 | |||
283 | data = urb->transfer_buffer + sizeof(struct dln2_header); | ||
284 | len = urb->actual_length - sizeof(struct dln2_header); | ||
285 | |||
286 | if (handle == DLN2_HANDLE_EVENT) { | ||
287 | dln2_run_event_callbacks(dln2, id, echo, data, len); | ||
288 | } else { | ||
289 | /* URB will be re-submitted in _dln2_transfer (free_rx_slot) */ | ||
290 | if (dln2_transfer_complete(dln2, urb, handle, echo)) | ||
291 | return; | ||
292 | } | ||
293 | |||
294 | out: | ||
295 | err = usb_submit_urb(urb, GFP_ATOMIC); | ||
296 | if (err < 0) | ||
297 | dev_err(dev, "failed to resubmit RX URB: %d\n", err); | ||
298 | } | ||
299 | |||
300 | static void *dln2_prep_buf(u16 handle, u16 cmd, u16 echo, const void *obuf, | ||
301 | int *obuf_len, gfp_t gfp) | ||
302 | { | ||
303 | int len; | ||
304 | void *buf; | ||
305 | struct dln2_header *hdr; | ||
306 | |||
307 | len = *obuf_len + sizeof(*hdr); | ||
308 | buf = kmalloc(len, gfp); | ||
309 | if (!buf) | ||
310 | return NULL; | ||
311 | |||
312 | hdr = (struct dln2_header *)buf; | ||
313 | hdr->id = cpu_to_le16(cmd); | ||
314 | hdr->size = cpu_to_le16(len); | ||
315 | hdr->echo = cpu_to_le16(echo); | ||
316 | hdr->handle = cpu_to_le16(handle); | ||
317 | |||
318 | memcpy(buf + sizeof(*hdr), obuf, *obuf_len); | ||
319 | |||
320 | *obuf_len = len; | ||
321 | |||
322 | return buf; | ||
323 | } | ||
324 | |||
325 | static int dln2_send_wait(struct dln2_dev *dln2, u16 handle, u16 cmd, u16 echo, | ||
326 | const void *obuf, int obuf_len) | ||
327 | { | ||
328 | int ret = 0; | ||
329 | int len = obuf_len; | ||
330 | void *buf; | ||
331 | int actual; | ||
332 | |||
333 | buf = dln2_prep_buf(handle, cmd, echo, obuf, &len, GFP_KERNEL); | ||
334 | if (!buf) | ||
335 | return -ENOMEM; | ||
336 | |||
337 | ret = usb_bulk_msg(dln2->usb_dev, | ||
338 | usb_sndbulkpipe(dln2->usb_dev, dln2->ep_out), | ||
339 | buf, len, &actual, DLN2_USB_TIMEOUT); | ||
340 | |||
341 | kfree(buf); | ||
342 | |||
343 | return ret; | ||
344 | } | ||
345 | |||
346 | static bool find_free_slot(struct dln2_dev *dln2, u16 handle, int *slot) | ||
347 | { | ||
348 | struct dln2_mod_rx_slots *rxs; | ||
349 | unsigned long flags; | ||
350 | |||
351 | if (dln2->disconnect) { | ||
352 | *slot = -ENODEV; | ||
353 | return true; | ||
354 | } | ||
355 | |||
356 | rxs = &dln2->mod_rx_slots[handle]; | ||
357 | |||
358 | spin_lock_irqsave(&rxs->lock, flags); | ||
359 | |||
360 | *slot = find_first_zero_bit(rxs->bmap, DLN2_MAX_RX_SLOTS); | ||
361 | |||
362 | if (*slot < DLN2_MAX_RX_SLOTS) { | ||
363 | struct dln2_rx_context *rxc = &rxs->slots[*slot]; | ||
364 | |||
365 | set_bit(*slot, rxs->bmap); | ||
366 | rxc->in_use = true; | ||
367 | } | ||
368 | |||
369 | spin_unlock_irqrestore(&rxs->lock, flags); | ||
370 | |||
371 | return *slot < DLN2_MAX_RX_SLOTS; | ||
372 | } | ||
373 | |||
374 | static int alloc_rx_slot(struct dln2_dev *dln2, u16 handle) | ||
375 | { | ||
376 | int ret; | ||
377 | int slot; | ||
378 | |||
379 | /* | ||
380 | * No need to timeout here, the wait is bounded by the timeout in | ||
381 | * _dln2_transfer. | ||
382 | */ | ||
383 | ret = wait_event_interruptible(dln2->mod_rx_slots[handle].wq, | ||
384 | find_free_slot(dln2, handle, &slot)); | ||
385 | if (ret < 0) | ||
386 | return ret; | ||
387 | |||
388 | return slot; | ||
389 | } | ||
390 | |||
391 | static void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot) | ||
392 | { | ||
393 | struct dln2_mod_rx_slots *rxs; | ||
394 | struct urb *urb = NULL; | ||
395 | unsigned long flags; | ||
396 | struct dln2_rx_context *rxc; | ||
397 | |||
398 | rxs = &dln2->mod_rx_slots[handle]; | ||
399 | |||
400 | spin_lock_irqsave(&rxs->lock, flags); | ||
401 | |||
402 | clear_bit(slot, rxs->bmap); | ||
403 | |||
404 | rxc = &rxs->slots[slot]; | ||
405 | rxc->in_use = false; | ||
406 | urb = rxc->urb; | ||
407 | rxc->urb = NULL; | ||
408 | reinit_completion(&rxc->done); | ||
409 | |||
410 | spin_unlock_irqrestore(&rxs->lock, flags); | ||
411 | |||
412 | if (urb) { | ||
413 | int err; | ||
414 | struct device *dev = &dln2->interface->dev; | ||
415 | |||
416 | err = usb_submit_urb(urb, GFP_KERNEL); | ||
417 | if (err < 0) | ||
418 | dev_err(dev, "failed to resubmit RX URB: %d\n", err); | ||
419 | } | ||
420 | |||
421 | wake_up_interruptible(&rxs->wq); | ||
422 | } | ||
423 | |||
424 | static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, | ||
425 | const void *obuf, unsigned obuf_len, | ||
426 | void *ibuf, unsigned *ibuf_len) | ||
427 | { | ||
428 | int ret = 0; | ||
429 | int rx_slot; | ||
430 | struct dln2_response *rsp; | ||
431 | struct dln2_rx_context *rxc; | ||
432 | struct device *dev = &dln2->interface->dev; | ||
433 | const unsigned long timeout = DLN2_USB_TIMEOUT * HZ / 1000; | ||
434 | struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; | ||
435 | |||
436 | spin_lock(&dln2->disconnect_lock); | ||
437 | if (!dln2->disconnect) | ||
438 | dln2->active_transfers++; | ||
439 | else | ||
440 | ret = -ENODEV; | ||
441 | spin_unlock(&dln2->disconnect_lock); | ||
442 | |||
443 | if (ret) | ||
444 | return ret; | ||
445 | |||
446 | rx_slot = alloc_rx_slot(dln2, handle); | ||
447 | if (rx_slot < 0) { | ||
448 | ret = rx_slot; | ||
449 | goto out_decr; | ||
450 | } | ||
451 | |||
452 | ret = dln2_send_wait(dln2, handle, cmd, rx_slot, obuf, obuf_len); | ||
453 | if (ret < 0) { | ||
454 | dev_err(dev, "USB write failed: %d\n", ret); | ||
455 | goto out_free_rx_slot; | ||
456 | } | ||
457 | |||
458 | rxc = &rxs->slots[rx_slot]; | ||
459 | |||
460 | ret = wait_for_completion_interruptible_timeout(&rxc->done, timeout); | ||
461 | if (ret <= 0) { | ||
462 | if (!ret) | ||
463 | ret = -ETIMEDOUT; | ||
464 | goto out_free_rx_slot; | ||
465 | } | ||
466 | |||
467 | if (dln2->disconnect) { | ||
468 | ret = -ENODEV; | ||
469 | goto out_free_rx_slot; | ||
470 | } | ||
471 | |||
472 | /* if we got here we know that the response header has been checked */ | ||
473 | rsp = rxc->urb->transfer_buffer; | ||
474 | |||
475 | if (rsp->hdr.size < sizeof(*rsp)) { | ||
476 | ret = -EPROTO; | ||
477 | goto out_free_rx_slot; | ||
478 | } | ||
479 | |||
480 | if (le16_to_cpu(rsp->result) > 0x80) { | ||
481 | dev_dbg(dev, "%d received response with error %d\n", | ||
482 | handle, le16_to_cpu(rsp->result)); | ||
483 | ret = -EREMOTEIO; | ||
484 | goto out_free_rx_slot; | ||
485 | } | ||
486 | |||
487 | if (!ibuf) { | ||
488 | ret = 0; | ||
489 | goto out_free_rx_slot; | ||
490 | } | ||
491 | |||
492 | if (*ibuf_len > rsp->hdr.size - sizeof(*rsp)) | ||
493 | *ibuf_len = rsp->hdr.size - sizeof(*rsp); | ||
494 | |||
495 | memcpy(ibuf, rsp + 1, *ibuf_len); | ||
496 | |||
497 | out_free_rx_slot: | ||
498 | free_rx_slot(dln2, handle, rx_slot); | ||
499 | out_decr: | ||
500 | spin_lock(&dln2->disconnect_lock); | ||
501 | dln2->active_transfers--; | ||
502 | spin_unlock(&dln2->disconnect_lock); | ||
503 | if (dln2->disconnect) | ||
504 | wake_up(&dln2->disconnect_wq); | ||
505 | |||
506 | return ret; | ||
507 | } | ||
508 | |||
509 | int dln2_transfer(struct platform_device *pdev, u16 cmd, | ||
510 | const void *obuf, unsigned obuf_len, | ||
511 | void *ibuf, unsigned *ibuf_len) | ||
512 | { | ||
513 | struct dln2_platform_data *dln2_pdata; | ||
514 | struct dln2_dev *dln2; | ||
515 | u16 handle; | ||
516 | |||
517 | dln2 = dev_get_drvdata(pdev->dev.parent); | ||
518 | dln2_pdata = dev_get_platdata(&pdev->dev); | ||
519 | handle = dln2_pdata->handle; | ||
520 | |||
521 | return _dln2_transfer(dln2, handle, cmd, obuf, obuf_len, ibuf, | ||
522 | ibuf_len); | ||
523 | } | ||
524 | EXPORT_SYMBOL(dln2_transfer); | ||
525 | |||
526 | static int dln2_check_hw(struct dln2_dev *dln2) | ||
527 | { | ||
528 | int ret; | ||
529 | __le32 hw_type; | ||
530 | int len = sizeof(hw_type); | ||
531 | |||
532 | ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_VER, | ||
533 | NULL, 0, &hw_type, &len); | ||
534 | if (ret < 0) | ||
535 | return ret; | ||
536 | if (len < sizeof(hw_type)) | ||
537 | return -EREMOTEIO; | ||
538 | |||
539 | if (le32_to_cpu(hw_type) != DLN2_HW_ID) { | ||
540 | dev_err(&dln2->interface->dev, "Device ID 0x%x not supported\n", | ||
541 | le32_to_cpu(hw_type)); | ||
542 | return -ENODEV; | ||
543 | } | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | static int dln2_print_serialno(struct dln2_dev *dln2) | ||
549 | { | ||
550 | int ret; | ||
551 | __le32 serial_no; | ||
552 | int len = sizeof(serial_no); | ||
553 | struct device *dev = &dln2->interface->dev; | ||
554 | |||
555 | ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_SN, NULL, 0, | ||
556 | &serial_no, &len); | ||
557 | if (ret < 0) | ||
558 | return ret; | ||
559 | if (len < sizeof(serial_no)) | ||
560 | return -EREMOTEIO; | ||
561 | |||
562 | dev_info(dev, "Diolan DLN2 serial %u\n", le32_to_cpu(serial_no)); | ||
563 | |||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static int dln2_hw_init(struct dln2_dev *dln2) | ||
568 | { | ||
569 | int ret; | ||
570 | |||
571 | ret = dln2_check_hw(dln2); | ||
572 | if (ret < 0) | ||
573 | return ret; | ||
574 | |||
575 | return dln2_print_serialno(dln2); | ||
576 | } | ||
577 | |||
578 | static void dln2_free_rx_urbs(struct dln2_dev *dln2) | ||
579 | { | ||
580 | int i; | ||
581 | |||
582 | for (i = 0; i < DLN2_MAX_URBS; i++) { | ||
583 | usb_kill_urb(dln2->rx_urb[i]); | ||
584 | usb_free_urb(dln2->rx_urb[i]); | ||
585 | kfree(dln2->rx_buf[i]); | ||
586 | } | ||
587 | } | ||
588 | |||
589 | static void dln2_free(struct dln2_dev *dln2) | ||
590 | { | ||
591 | dln2_free_rx_urbs(dln2); | ||
592 | usb_put_dev(dln2->usb_dev); | ||
593 | kfree(dln2); | ||
594 | } | ||
595 | |||
596 | static int dln2_setup_rx_urbs(struct dln2_dev *dln2, | ||
597 | struct usb_host_interface *hostif) | ||
598 | { | ||
599 | int i; | ||
600 | int ret; | ||
601 | const int rx_max_size = DLN2_RX_BUF_SIZE; | ||
602 | struct device *dev = &dln2->interface->dev; | ||
603 | |||
604 | for (i = 0; i < DLN2_MAX_URBS; i++) { | ||
605 | dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); | ||
606 | if (!dln2->rx_buf[i]) | ||
607 | return -ENOMEM; | ||
608 | |||
609 | dln2->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); | ||
610 | if (!dln2->rx_urb[i]) | ||
611 | return -ENOMEM; | ||
612 | |||
613 | usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev, | ||
614 | usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in), | ||
615 | dln2->rx_buf[i], rx_max_size, dln2_rx, dln2); | ||
616 | |||
617 | ret = usb_submit_urb(dln2->rx_urb[i], GFP_KERNEL); | ||
618 | if (ret < 0) { | ||
619 | dev_err(dev, "failed to submit RX URB: %d\n", ret); | ||
620 | return ret; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static struct dln2_platform_data dln2_pdata_gpio = { | ||
628 | .handle = DLN2_HANDLE_GPIO, | ||
629 | }; | ||
630 | |||
631 | /* Only one I2C port seems to be supported on current hardware */ | ||
632 | static struct dln2_platform_data dln2_pdata_i2c = { | ||
633 | .handle = DLN2_HANDLE_I2C, | ||
634 | .port = 0, | ||
635 | }; | ||
636 | |||
637 | static const struct mfd_cell dln2_devs[] = { | ||
638 | { | ||
639 | .name = "dln2-gpio", | ||
640 | .platform_data = &dln2_pdata_gpio, | ||
641 | .pdata_size = sizeof(struct dln2_platform_data), | ||
642 | }, | ||
643 | { | ||
644 | .name = "dln2-i2c", | ||
645 | .platform_data = &dln2_pdata_i2c, | ||
646 | .pdata_size = sizeof(struct dln2_platform_data), | ||
647 | }, | ||
648 | }; | ||
649 | |||
650 | static void dln2_disconnect(struct usb_interface *interface) | ||
651 | { | ||
652 | struct dln2_dev *dln2 = usb_get_intfdata(interface); | ||
653 | int i, j; | ||
654 | |||
655 | /* don't allow starting new transfers */ | ||
656 | spin_lock(&dln2->disconnect_lock); | ||
657 | dln2->disconnect = true; | ||
658 | spin_unlock(&dln2->disconnect_lock); | ||
659 | |||
660 | /* cancel in progress transfers */ | ||
661 | for (i = 0; i < DLN2_HANDLES; i++) { | ||
662 | struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[i]; | ||
663 | unsigned long flags; | ||
664 | |||
665 | spin_lock_irqsave(&rxs->lock, flags); | ||
666 | |||
667 | /* cancel all response waiters */ | ||
668 | for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) { | ||
669 | struct dln2_rx_context *rxc = &rxs->slots[j]; | ||
670 | |||
671 | if (rxc->in_use) | ||
672 | complete(&rxc->done); | ||
673 | } | ||
674 | |||
675 | spin_unlock_irqrestore(&rxs->lock, flags); | ||
676 | } | ||
677 | |||
678 | /* wait for transfers to end */ | ||
679 | wait_event(dln2->disconnect_wq, !dln2->active_transfers); | ||
680 | |||
681 | mfd_remove_devices(&interface->dev); | ||
682 | |||
683 | dln2_free(dln2); | ||
684 | } | ||
685 | |||
686 | static int dln2_probe(struct usb_interface *interface, | ||
687 | const struct usb_device_id *usb_id) | ||
688 | { | ||
689 | struct usb_host_interface *hostif = interface->cur_altsetting; | ||
690 | struct device *dev = &interface->dev; | ||
691 | struct dln2_dev *dln2; | ||
692 | int ret; | ||
693 | int i, j; | ||
694 | |||
695 | if (hostif->desc.bInterfaceNumber != 0 || | ||
696 | hostif->desc.bNumEndpoints < 2) | ||
697 | return -ENODEV; | ||
698 | |||
699 | dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); | ||
700 | if (!dln2) | ||
701 | return -ENOMEM; | ||
702 | |||
703 | dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress; | ||
704 | dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress; | ||
705 | dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); | ||
706 | dln2->interface = interface; | ||
707 | usb_set_intfdata(interface, dln2); | ||
708 | init_waitqueue_head(&dln2->disconnect_wq); | ||
709 | |||
710 | for (i = 0; i < DLN2_HANDLES; i++) { | ||
711 | init_waitqueue_head(&dln2->mod_rx_slots[i].wq); | ||
712 | spin_lock_init(&dln2->mod_rx_slots[i].lock); | ||
713 | for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) | ||
714 | init_completion(&dln2->mod_rx_slots[i].slots[j].done); | ||
715 | } | ||
716 | |||
717 | spin_lock_init(&dln2->event_cb_lock); | ||
718 | spin_lock_init(&dln2->disconnect_lock); | ||
719 | INIT_LIST_HEAD(&dln2->event_cb_list); | ||
720 | |||
721 | ret = dln2_setup_rx_urbs(dln2, hostif); | ||
722 | if (ret) | ||
723 | goto out_cleanup; | ||
724 | |||
725 | ret = dln2_hw_init(dln2); | ||
726 | if (ret < 0) { | ||
727 | dev_err(dev, "failed to initialize hardware\n"); | ||
728 | goto out_cleanup; | ||
729 | } | ||
730 | |||
731 | ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs)); | ||
732 | if (ret != 0) { | ||
733 | dev_err(dev, "failed to add mfd devices to core\n"); | ||
734 | goto out_cleanup; | ||
735 | } | ||
736 | |||
737 | return 0; | ||
738 | |||
739 | out_cleanup: | ||
740 | dln2_free(dln2); | ||
741 | |||
742 | return ret; | ||
743 | } | ||
744 | |||
745 | static const struct usb_device_id dln2_table[] = { | ||
746 | { USB_DEVICE(0xa257, 0x2013) }, | ||
747 | { } | ||
748 | }; | ||
749 | |||
750 | MODULE_DEVICE_TABLE(usb, dln2_table); | ||
751 | |||
752 | static struct usb_driver dln2_driver = { | ||
753 | .name = "dln2", | ||
754 | .probe = dln2_probe, | ||
755 | .disconnect = dln2_disconnect, | ||
756 | .id_table = dln2_table, | ||
757 | }; | ||
758 | |||
759 | module_usb_driver(dln2_driver); | ||
760 | |||
761 | MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); | ||
762 | MODULE_DESCRIPTION("Core driver for the Diolan DLN2 interface adapter"); | ||
763 | MODULE_LICENSE("GPL v2"); | ||