Commit | Line | Data |
---|---|---|
bbecb07f UKK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2015-2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> | |
4 | */ | |
5 | #include <linux/kernel.h> | |
6 | #include <linux/device.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/slab.h> | |
9 | #include <linux/sysfs.h> | |
10 | ||
11 | #include "siox.h" | |
12 | ||
13 | /* | |
14 | * The lowest bit in the SIOX status word signals if the in-device watchdog is | |
15 | * ok. If the bit is set, the device is functional. | |
16 | * | |
17 | * On writing the watchdog timer is reset when this bit toggles. | |
18 | */ | |
19 | #define SIOX_STATUS_WDG 0x01 | |
20 | ||
21 | /* | |
22 | * Bits 1 to 3 of the status word read as the bitwise negation of what was | |
23 | * clocked in before. The value clocked in is changed in each cycle and so | |
24 | * allows to detect transmit/receive problems. | |
25 | */ | |
26 | #define SIOX_STATUS_COUNTER 0x0e | |
27 | ||
28 | /* | |
29 | * Each Siox-Device has a 4 bit type number that is neither 0 nor 15. This is | |
30 | * available in the upper nibble of the read status. | |
31 | * | |
32 | * On write these bits are DC. | |
33 | */ | |
34 | #define SIOX_STATUS_TYPE 0xf0 | |
35 | ||
36 | static bool siox_is_registered; | |
37 | ||
38 | static void siox_master_lock(struct siox_master *smaster) | |
39 | { | |
40 | mutex_lock(&smaster->lock); | |
41 | } | |
42 | ||
43 | static void siox_master_unlock(struct siox_master *smaster) | |
44 | { | |
45 | mutex_unlock(&smaster->lock); | |
46 | } | |
47 | ||
48 | static inline u8 siox_status_clean(u8 status_read, u8 status_written) | |
49 | { | |
50 | /* | |
51 | * bits 3:1 of status sample the respective bit in the status | |
52 | * byte written in the previous cycle but inverted. So if you wrote the | |
53 | * status word as 0xa before (counter = 0b101), it is expected to get | |
54 | * back the counter bits as 0b010. | |
55 | * | |
56 | * So given the last status written this function toggles the there | |
57 | * unset counter bits in the read value such that the counter bits in | |
58 | * the return value are all zero iff the bits were read as expected to | |
59 | * simplify error detection. | |
60 | */ | |
61 | ||
62 | return status_read ^ (~status_written & 0xe); | |
63 | } | |
64 | ||
65 | static bool siox_device_counter_error(struct siox_device *sdevice, | |
66 | u8 status_clean) | |
67 | { | |
68 | return (status_clean & SIOX_STATUS_COUNTER) != 0; | |
69 | } | |
70 | ||
71 | static bool siox_device_type_error(struct siox_device *sdevice, u8 status_clean) | |
72 | { | |
73 | u8 statustype = (status_clean & SIOX_STATUS_TYPE) >> 4; | |
74 | ||
75 | /* | |
76 | * If the device knows which value the type bits should have, check | |
77 | * against this value otherwise just rule out the invalid values 0b0000 | |
78 | * and 0b1111. | |
79 | */ | |
80 | if (sdevice->statustype) { | |
81 | if (statustype != sdevice->statustype) | |
82 | return true; | |
83 | } else { | |
84 | switch (statustype) { | |
85 | case 0: | |
86 | case 0xf: | |
87 | return true; | |
88 | } | |
89 | } | |
90 | ||
91 | return false; | |
92 | } | |
93 | ||
94 | static bool siox_device_wdg_error(struct siox_device *sdevice, u8 status_clean) | |
95 | { | |
96 | return (status_clean & SIOX_STATUS_WDG) == 0; | |
97 | } | |
98 | ||
99 | /* | |
100 | * If there is a type or counter error the device is called "unsynced". | |
101 | */ | |
102 | bool siox_device_synced(struct siox_device *sdevice) | |
103 | { | |
104 | if (siox_device_type_error(sdevice, sdevice->status_read_clean)) | |
105 | return false; | |
106 | ||
107 | return !siox_device_counter_error(sdevice, sdevice->status_read_clean); | |
108 | ||
109 | } | |
110 | EXPORT_SYMBOL_GPL(siox_device_synced); | |
111 | ||
112 | /* | |
113 | * A device is called "connected" if it is synced and the watchdog is not | |
114 | * asserted. | |
115 | */ | |
116 | bool siox_device_connected(struct siox_device *sdevice) | |
117 | { | |
118 | if (!siox_device_synced(sdevice)) | |
119 | return false; | |
120 | ||
121 | return !siox_device_wdg_error(sdevice, sdevice->status_read_clean); | |
122 | } | |
123 | EXPORT_SYMBOL_GPL(siox_device_connected); | |
124 | ||
125 | static void siox_poll(struct siox_master *smaster) | |
126 | { | |
127 | struct siox_device *sdevice; | |
128 | size_t i = smaster->setbuf_len; | |
129 | int unsync_error = 0; | |
130 | ||
131 | smaster->last_poll = jiffies; | |
132 | ||
133 | /* | |
134 | * The counter bits change in each second cycle, the watchdog bit | |
135 | * toggles each time. | |
136 | * The counter bits hold values from [0, 6]. 7 would be possible | |
137 | * theoretically but the protocol designer considered that a bad idea | |
138 | * for reasons unknown today. (Maybe that's because then the status read | |
139 | * back has only zeros in the counter bits then which might be confused | |
140 | * with a stuck-at-0 error. But for the same reason (with s/0/1/) 0 | |
141 | * could be skipped.) | |
142 | */ | |
143 | if (++smaster->status > 0x0d) | |
144 | smaster->status = 0; | |
145 | ||
146 | memset(smaster->buf, 0, smaster->setbuf_len); | |
147 | ||
148 | /* prepare data pushed out to devices in buf[0..setbuf_len) */ | |
149 | list_for_each_entry(sdevice, &smaster->devices, node) { | |
150 | struct siox_driver *sdriver = | |
151 | to_siox_driver(sdevice->dev.driver); | |
152 | sdevice->status_written = smaster->status; | |
153 | ||
154 | i -= sdevice->inbytes; | |
155 | ||
156 | /* | |
157 | * If the device or a previous one is unsynced, don't pet the | |
158 | * watchdog. This is done to ensure that the device is kept in | |
159 | * reset when something is wrong. | |
160 | */ | |
161 | if (!siox_device_synced(sdevice)) | |
162 | unsync_error = 1; | |
163 | ||
164 | if (sdriver && !unsync_error) | |
165 | sdriver->set_data(sdevice, sdevice->status_written, | |
166 | &smaster->buf[i + 1]); | |
167 | else | |
168 | /* | |
169 | * Don't trigger watchdog if there is no driver or a | |
170 | * sync problem | |
171 | */ | |
172 | sdevice->status_written &= ~SIOX_STATUS_WDG; | |
173 | ||
174 | smaster->buf[i] = sdevice->status_written; | |
175 | } | |
176 | ||
177 | smaster->pushpull(smaster, smaster->setbuf_len, smaster->buf, | |
178 | smaster->getbuf_len, | |
179 | smaster->buf + smaster->setbuf_len); | |
180 | ||
181 | unsync_error = 0; | |
182 | ||
183 | /* interpret data pulled in from devices in buf[setbuf_len..] */ | |
184 | i = smaster->setbuf_len; | |
185 | list_for_each_entry(sdevice, &smaster->devices, node) { | |
186 | struct siox_driver *sdriver = | |
187 | to_siox_driver(sdevice->dev.driver); | |
188 | u8 status = smaster->buf[i + sdevice->outbytes - 1]; | |
189 | u8 status_clean; | |
190 | u8 prev_status_clean = sdevice->status_read_clean; | |
191 | bool synced = true; | |
192 | bool connected = true; | |
193 | ||
194 | if (!siox_device_synced(sdevice)) | |
195 | unsync_error = 1; | |
196 | ||
197 | /* | |
198 | * If the watchdog bit wasn't toggled in this cycle, report the | |
199 | * watchdog as active to give a consistent view for drivers and | |
200 | * sysfs consumers. | |
201 | */ | |
202 | if (!sdriver || unsync_error) | |
203 | status &= ~SIOX_STATUS_WDG; | |
204 | ||
205 | status_clean = | |
206 | siox_status_clean(status, | |
207 | sdevice->status_written_lastcycle); | |
208 | ||
209 | /* Check counter bits */ | |
210 | if (siox_device_counter_error(sdevice, status_clean)) { | |
211 | bool prev_counter_error; | |
212 | ||
213 | synced = false; | |
214 | ||
215 | /* only report a new error if the last cycle was ok */ | |
216 | prev_counter_error = | |
217 | siox_device_counter_error(sdevice, | |
218 | prev_status_clean); | |
219 | if (!prev_counter_error) { | |
220 | sdevice->status_errors++; | |
221 | sysfs_notify_dirent(sdevice->status_errors_kn); | |
222 | } | |
223 | } | |
224 | ||
225 | /* Check type bits */ | |
226 | if (siox_device_type_error(sdevice, status_clean)) | |
227 | synced = false; | |
228 | ||
229 | /* If the device is unsynced report the watchdog as active */ | |
230 | if (!synced) { | |
231 | status &= ~SIOX_STATUS_WDG; | |
232 | status_clean &= ~SIOX_STATUS_WDG; | |
233 | } | |
234 | ||
235 | if (siox_device_wdg_error(sdevice, status_clean)) | |
236 | connected = false; | |
237 | ||
238 | /* The watchdog state changed just now */ | |
239 | if ((status_clean ^ prev_status_clean) & SIOX_STATUS_WDG) { | |
240 | sysfs_notify_dirent(sdevice->watchdog_kn); | |
241 | ||
242 | if (siox_device_wdg_error(sdevice, status_clean)) { | |
243 | struct kernfs_node *wd_errs = | |
244 | sdevice->watchdog_errors_kn; | |
245 | ||
246 | sdevice->watchdog_errors++; | |
247 | sysfs_notify_dirent(wd_errs); | |
248 | } | |
249 | } | |
250 | ||
251 | if (connected != sdevice->connected) | |
252 | sysfs_notify_dirent(sdevice->connected_kn); | |
253 | ||
254 | sdevice->status_read_clean = status_clean; | |
255 | sdevice->status_written_lastcycle = sdevice->status_written; | |
256 | sdevice->connected = connected; | |
257 | ||
258 | /* only give data read to driver if the device is connected */ | |
259 | if (sdriver && connected) | |
260 | sdriver->get_data(sdevice, &smaster->buf[i]); | |
261 | ||
262 | i += sdevice->outbytes; | |
263 | } | |
264 | } | |
265 | ||
266 | static int siox_poll_thread(void *data) | |
267 | { | |
268 | struct siox_master *smaster = data; | |
269 | signed long timeout = 0; | |
270 | ||
271 | get_device(&smaster->dev); | |
272 | ||
273 | for (;;) { | |
274 | if (kthread_should_stop()) { | |
275 | put_device(&smaster->dev); | |
276 | return 0; | |
277 | } | |
278 | ||
279 | siox_master_lock(smaster); | |
280 | ||
281 | if (smaster->active) { | |
282 | unsigned long next_poll = | |
283 | smaster->last_poll + smaster->poll_interval; | |
284 | if (time_is_before_eq_jiffies(next_poll)) | |
285 | siox_poll(smaster); | |
286 | ||
287 | timeout = smaster->poll_interval - | |
288 | (jiffies - smaster->last_poll); | |
289 | } else { | |
290 | timeout = MAX_SCHEDULE_TIMEOUT; | |
291 | } | |
292 | ||
293 | /* | |
294 | * Set the task to idle while holding the lock. This makes sure | |
295 | * that we don't sleep too long when the bus is reenabled before | |
296 | * schedule_timeout is reached. | |
297 | */ | |
298 | if (timeout > 0) | |
299 | set_current_state(TASK_IDLE); | |
300 | ||
301 | siox_master_unlock(smaster); | |
302 | ||
303 | if (timeout > 0) | |
304 | schedule_timeout(timeout); | |
305 | ||
306 | /* | |
307 | * I'm not clear if/why it is important to set the state to | |
308 | * RUNNING again, but it fixes a "do not call blocking ops when | |
309 | * !TASK_RUNNING;"-warning. | |
310 | */ | |
311 | set_current_state(TASK_RUNNING); | |
312 | } | |
313 | } | |
314 | ||
315 | static int __siox_start(struct siox_master *smaster) | |
316 | { | |
317 | if (!(smaster->setbuf_len + smaster->getbuf_len)) | |
318 | return -ENODEV; | |
319 | ||
320 | if (!smaster->buf) | |
321 | return -ENOMEM; | |
322 | ||
323 | if (smaster->active) | |
324 | return 0; | |
325 | ||
326 | smaster->active = 1; | |
327 | wake_up_process(smaster->poll_thread); | |
328 | ||
329 | return 1; | |
330 | } | |
331 | ||
332 | static int siox_start(struct siox_master *smaster) | |
333 | { | |
334 | int ret; | |
335 | ||
336 | siox_master_lock(smaster); | |
337 | ret = __siox_start(smaster); | |
338 | siox_master_unlock(smaster); | |
339 | ||
340 | return ret; | |
341 | } | |
342 | ||
343 | static int __siox_stop(struct siox_master *smaster) | |
344 | { | |
345 | if (smaster->active) { | |
346 | struct siox_device *sdevice; | |
347 | ||
348 | smaster->active = 0; | |
349 | ||
350 | list_for_each_entry(sdevice, &smaster->devices, node) { | |
351 | if (sdevice->connected) | |
352 | sysfs_notify_dirent(sdevice->connected_kn); | |
353 | sdevice->connected = false; | |
354 | } | |
355 | ||
356 | return 1; | |
357 | } | |
358 | return 0; | |
359 | } | |
360 | ||
361 | static int siox_stop(struct siox_master *smaster) | |
362 | { | |
363 | int ret; | |
364 | ||
365 | siox_master_lock(smaster); | |
366 | ret = __siox_stop(smaster); | |
367 | siox_master_unlock(smaster); | |
368 | ||
369 | return ret; | |
370 | } | |
371 | ||
372 | static ssize_t type_show(struct device *dev, | |
373 | struct device_attribute *attr, char *buf) | |
374 | { | |
375 | struct siox_device *sdev = to_siox_device(dev); | |
376 | ||
377 | return sprintf(buf, "%s\n", sdev->type); | |
378 | } | |
379 | ||
380 | static DEVICE_ATTR_RO(type); | |
381 | ||
382 | static ssize_t inbytes_show(struct device *dev, | |
383 | struct device_attribute *attr, char *buf) | |
384 | { | |
385 | struct siox_device *sdev = to_siox_device(dev); | |
386 | ||
387 | return sprintf(buf, "%zu\n", sdev->inbytes); | |
388 | } | |
389 | ||
390 | static DEVICE_ATTR_RO(inbytes); | |
391 | ||
392 | static ssize_t outbytes_show(struct device *dev, | |
393 | struct device_attribute *attr, char *buf) | |
394 | { | |
395 | struct siox_device *sdev = to_siox_device(dev); | |
396 | ||
397 | return sprintf(buf, "%zu\n", sdev->outbytes); | |
398 | } | |
399 | ||
400 | static DEVICE_ATTR_RO(outbytes); | |
401 | ||
402 | static ssize_t status_errors_show(struct device *dev, | |
403 | struct device_attribute *attr, char *buf) | |
404 | { | |
405 | struct siox_device *sdev = to_siox_device(dev); | |
406 | unsigned int status_errors; | |
407 | ||
408 | siox_master_lock(sdev->smaster); | |
409 | ||
410 | status_errors = sdev->status_errors; | |
411 | ||
412 | siox_master_unlock(sdev->smaster); | |
413 | ||
414 | return sprintf(buf, "%u\n", status_errors); | |
415 | } | |
416 | ||
417 | static DEVICE_ATTR_RO(status_errors); | |
418 | ||
419 | static ssize_t connected_show(struct device *dev, | |
420 | struct device_attribute *attr, char *buf) | |
421 | { | |
422 | struct siox_device *sdev = to_siox_device(dev); | |
423 | bool connected; | |
424 | ||
425 | siox_master_lock(sdev->smaster); | |
426 | ||
427 | connected = sdev->connected; | |
428 | ||
429 | siox_master_unlock(sdev->smaster); | |
430 | ||
431 | return sprintf(buf, "%u\n", connected); | |
432 | } | |
433 | ||
434 | static DEVICE_ATTR_RO(connected); | |
435 | ||
436 | static ssize_t watchdog_show(struct device *dev, | |
437 | struct device_attribute *attr, char *buf) | |
438 | { | |
439 | struct siox_device *sdev = to_siox_device(dev); | |
440 | u8 status; | |
441 | ||
442 | siox_master_lock(sdev->smaster); | |
443 | ||
444 | status = sdev->status_read_clean; | |
445 | ||
446 | siox_master_unlock(sdev->smaster); | |
447 | ||
448 | return sprintf(buf, "%d\n", status & SIOX_STATUS_WDG); | |
449 | } | |
450 | ||
451 | static DEVICE_ATTR_RO(watchdog); | |
452 | ||
453 | static ssize_t watchdog_errors_show(struct device *dev, | |
454 | struct device_attribute *attr, char *buf) | |
455 | { | |
456 | struct siox_device *sdev = to_siox_device(dev); | |
457 | unsigned int watchdog_errors; | |
458 | ||
459 | siox_master_lock(sdev->smaster); | |
460 | ||
461 | watchdog_errors = sdev->watchdog_errors; | |
462 | ||
463 | siox_master_unlock(sdev->smaster); | |
464 | ||
465 | return sprintf(buf, "%u\n", watchdog_errors); | |
466 | } | |
467 | ||
468 | static DEVICE_ATTR_RO(watchdog_errors); | |
469 | ||
470 | static struct attribute *siox_device_attrs[] = { | |
471 | &dev_attr_type.attr, | |
472 | &dev_attr_inbytes.attr, | |
473 | &dev_attr_outbytes.attr, | |
474 | &dev_attr_status_errors.attr, | |
475 | &dev_attr_connected.attr, | |
476 | &dev_attr_watchdog.attr, | |
477 | &dev_attr_watchdog_errors.attr, | |
478 | NULL | |
479 | }; | |
480 | ATTRIBUTE_GROUPS(siox_device); | |
481 | ||
482 | static void siox_device_release(struct device *dev) | |
483 | { | |
484 | struct siox_device *sdevice = to_siox_device(dev); | |
485 | ||
486 | kfree(sdevice); | |
487 | } | |
488 | ||
489 | static struct device_type siox_device_type = { | |
490 | .groups = siox_device_groups, | |
491 | .release = siox_device_release, | |
492 | }; | |
493 | ||
494 | static int siox_match(struct device *dev, struct device_driver *drv) | |
495 | { | |
496 | if (dev->type != &siox_device_type) | |
497 | return 0; | |
498 | ||
499 | /* up to now there is only a single driver so keeping this simple */ | |
500 | return 1; | |
501 | } | |
502 | ||
503 | static struct bus_type siox_bus_type = { | |
504 | .name = "siox", | |
505 | .match = siox_match, | |
506 | }; | |
507 | ||
508 | static int siox_driver_probe(struct device *dev) | |
509 | { | |
510 | struct siox_driver *sdriver = to_siox_driver(dev->driver); | |
511 | struct siox_device *sdevice = to_siox_device(dev); | |
512 | int ret; | |
513 | ||
514 | ret = sdriver->probe(sdevice); | |
515 | return ret; | |
516 | } | |
517 | ||
518 | static int siox_driver_remove(struct device *dev) | |
519 | { | |
520 | struct siox_driver *sdriver = | |
521 | container_of(dev->driver, struct siox_driver, driver); | |
522 | struct siox_device *sdevice = to_siox_device(dev); | |
523 | int ret; | |
524 | ||
525 | ret = sdriver->remove(sdevice); | |
526 | return ret; | |
527 | } | |
528 | ||
529 | static void siox_driver_shutdown(struct device *dev) | |
530 | { | |
531 | struct siox_driver *sdriver = | |
532 | container_of(dev->driver, struct siox_driver, driver); | |
533 | struct siox_device *sdevice = to_siox_device(dev); | |
534 | ||
535 | sdriver->shutdown(sdevice); | |
536 | } | |
537 | ||
538 | static ssize_t active_show(struct device *dev, | |
539 | struct device_attribute *attr, char *buf) | |
540 | { | |
541 | struct siox_master *smaster = to_siox_master(dev); | |
542 | ||
543 | return sprintf(buf, "%d\n", smaster->active); | |
544 | } | |
545 | ||
546 | static ssize_t active_store(struct device *dev, | |
547 | struct device_attribute *attr, | |
548 | const char *buf, size_t count) | |
549 | { | |
550 | struct siox_master *smaster = to_siox_master(dev); | |
551 | int ret; | |
552 | int active; | |
553 | ||
554 | ret = kstrtoint(buf, 0, &active); | |
555 | if (ret < 0) | |
556 | return ret; | |
557 | ||
558 | if (active) | |
559 | ret = siox_start(smaster); | |
560 | else | |
561 | ret = siox_stop(smaster); | |
562 | ||
563 | if (ret < 0) | |
564 | return ret; | |
565 | ||
566 | return count; | |
567 | } | |
568 | ||
569 | static DEVICE_ATTR_RW(active); | |
570 | ||
571 | static struct siox_device *siox_device_add(struct siox_master *smaster, | |
572 | const char *type, size_t inbytes, | |
573 | size_t outbytes, u8 statustype); | |
574 | ||
575 | static ssize_t device_add_store(struct device *dev, | |
576 | struct device_attribute *attr, | |
577 | const char *buf, size_t count) | |
578 | { | |
579 | struct siox_master *smaster = to_siox_master(dev); | |
580 | int ret; | |
581 | char type[20] = ""; | |
582 | size_t inbytes = 0, outbytes = 0; | |
583 | u8 statustype = 0; | |
584 | ||
585 | ret = sscanf(buf, "%20s %zu %zu %hhu", type, &inbytes, | |
586 | &outbytes, &statustype); | |
587 | if (ret != 3 && ret != 4) | |
588 | return -EINVAL; | |
589 | ||
590 | if (strcmp(type, "siox-12x8") || inbytes != 2 || outbytes != 4) | |
591 | return -EINVAL; | |
592 | ||
593 | siox_device_add(smaster, "siox-12x8", inbytes, outbytes, statustype); | |
594 | ||
595 | return count; | |
596 | } | |
597 | ||
598 | static DEVICE_ATTR_WO(device_add); | |
599 | ||
600 | static void siox_device_remove(struct siox_master *smaster); | |
601 | ||
602 | static ssize_t device_remove_store(struct device *dev, | |
603 | struct device_attribute *attr, | |
604 | const char *buf, size_t count) | |
605 | { | |
606 | struct siox_master *smaster = to_siox_master(dev); | |
607 | ||
608 | /* XXX? require to write <type> <inbytes> <outbytes> */ | |
609 | siox_device_remove(smaster); | |
610 | ||
611 | return count; | |
612 | } | |
613 | ||
614 | static DEVICE_ATTR_WO(device_remove); | |
615 | ||
616 | static ssize_t poll_interval_ns_show(struct device *dev, | |
617 | struct device_attribute *attr, char *buf) | |
618 | { | |
619 | struct siox_master *smaster = to_siox_master(dev); | |
620 | ||
621 | return sprintf(buf, "%lld\n", jiffies_to_nsecs(smaster->poll_interval)); | |
622 | } | |
623 | ||
624 | static ssize_t poll_interval_ns_store(struct device *dev, | |
625 | struct device_attribute *attr, | |
626 | const char *buf, size_t count) | |
627 | { | |
628 | struct siox_master *smaster = to_siox_master(dev); | |
629 | int ret; | |
630 | u64 val; | |
631 | ||
632 | ret = kstrtou64(buf, 0, &val); | |
633 | if (ret < 0) | |
634 | return ret; | |
635 | ||
636 | siox_master_lock(smaster); | |
637 | ||
638 | smaster->poll_interval = nsecs_to_jiffies(val); | |
639 | ||
640 | siox_master_unlock(smaster); | |
641 | ||
642 | return count; | |
643 | } | |
644 | ||
645 | static DEVICE_ATTR_RW(poll_interval_ns); | |
646 | ||
647 | static struct attribute *siox_master_attrs[] = { | |
648 | &dev_attr_active.attr, | |
649 | &dev_attr_device_add.attr, | |
650 | &dev_attr_device_remove.attr, | |
651 | &dev_attr_poll_interval_ns.attr, | |
652 | NULL | |
653 | }; | |
654 | ATTRIBUTE_GROUPS(siox_master); | |
655 | ||
656 | static void siox_master_release(struct device *dev) | |
657 | { | |
658 | struct siox_master *smaster = to_siox_master(dev); | |
659 | ||
660 | kfree(smaster); | |
661 | } | |
662 | ||
663 | static struct device_type siox_master_type = { | |
664 | .groups = siox_master_groups, | |
665 | .release = siox_master_release, | |
666 | }; | |
667 | ||
668 | struct siox_master *siox_master_alloc(struct device *dev, | |
669 | size_t size) | |
670 | { | |
671 | struct siox_master *smaster; | |
672 | ||
673 | if (!dev) | |
674 | return NULL; | |
675 | ||
676 | smaster = kzalloc(sizeof(*smaster) + size, GFP_KERNEL); | |
677 | if (!smaster) | |
678 | return NULL; | |
679 | ||
680 | device_initialize(&smaster->dev); | |
681 | ||
682 | smaster->busno = -1; | |
683 | smaster->dev.bus = &siox_bus_type; | |
684 | smaster->dev.type = &siox_master_type; | |
685 | smaster->dev.parent = dev; | |
686 | smaster->poll_interval = DIV_ROUND_UP(HZ, 40); | |
687 | ||
688 | dev_set_drvdata(&smaster->dev, &smaster[1]); | |
689 | ||
690 | return smaster; | |
691 | } | |
692 | EXPORT_SYMBOL_GPL(siox_master_alloc); | |
693 | ||
694 | int siox_master_register(struct siox_master *smaster) | |
695 | { | |
696 | int ret; | |
697 | ||
698 | if (!siox_is_registered) | |
699 | return -EPROBE_DEFER; | |
700 | ||
701 | if (!smaster->pushpull) | |
702 | return -EINVAL; | |
703 | ||
704 | dev_set_name(&smaster->dev, "siox-%d", smaster->busno); | |
705 | ||
706 | smaster->last_poll = jiffies; | |
707 | smaster->poll_thread = kthread_create(siox_poll_thread, smaster, | |
708 | "siox-%d", smaster->busno); | |
709 | if (IS_ERR(smaster->poll_thread)) { | |
710 | smaster->active = 0; | |
711 | return PTR_ERR(smaster->poll_thread); | |
712 | } | |
713 | ||
714 | mutex_init(&smaster->lock); | |
715 | INIT_LIST_HEAD(&smaster->devices); | |
716 | ||
717 | ret = device_add(&smaster->dev); | |
718 | if (ret) | |
719 | kthread_stop(smaster->poll_thread); | |
720 | ||
721 | return ret; | |
722 | } | |
723 | EXPORT_SYMBOL_GPL(siox_master_register); | |
724 | ||
725 | void siox_master_unregister(struct siox_master *smaster) | |
726 | { | |
727 | /* remove device */ | |
728 | device_del(&smaster->dev); | |
729 | ||
730 | siox_master_lock(smaster); | |
731 | ||
732 | __siox_stop(smaster); | |
733 | ||
734 | while (smaster->num_devices) { | |
735 | struct siox_device *sdevice; | |
736 | ||
737 | sdevice = container_of(smaster->devices.prev, | |
738 | struct siox_device, node); | |
739 | list_del(&sdevice->node); | |
740 | smaster->num_devices--; | |
741 | ||
742 | siox_master_unlock(smaster); | |
743 | ||
744 | device_unregister(&sdevice->dev); | |
745 | ||
746 | siox_master_lock(smaster); | |
747 | } | |
748 | ||
749 | siox_master_unlock(smaster); | |
750 | ||
751 | put_device(&smaster->dev); | |
752 | } | |
753 | EXPORT_SYMBOL_GPL(siox_master_unregister); | |
754 | ||
755 | static struct siox_device *siox_device_add(struct siox_master *smaster, | |
756 | const char *type, size_t inbytes, | |
757 | size_t outbytes, u8 statustype) | |
758 | { | |
759 | struct siox_device *sdevice; | |
760 | int ret; | |
761 | size_t buf_len; | |
762 | ||
763 | sdevice = kzalloc(sizeof(*sdevice), GFP_KERNEL); | |
764 | if (!sdevice) | |
765 | return ERR_PTR(-ENOMEM); | |
766 | ||
767 | sdevice->type = type; | |
768 | sdevice->inbytes = inbytes; | |
769 | sdevice->outbytes = outbytes; | |
770 | sdevice->statustype = statustype; | |
771 | ||
772 | sdevice->smaster = smaster; | |
773 | sdevice->dev.parent = &smaster->dev; | |
774 | sdevice->dev.bus = &siox_bus_type; | |
775 | sdevice->dev.type = &siox_device_type; | |
776 | ||
777 | siox_master_lock(smaster); | |
778 | ||
779 | dev_set_name(&sdevice->dev, "siox-%d-%d", | |
780 | smaster->busno, smaster->num_devices); | |
781 | ||
782 | buf_len = smaster->setbuf_len + inbytes + | |
783 | smaster->getbuf_len + outbytes; | |
784 | if (smaster->buf_len < buf_len) { | |
785 | u8 *buf = krealloc(smaster->buf, buf_len, GFP_KERNEL); | |
786 | ||
787 | if (!buf) { | |
788 | dev_err(&smaster->dev, | |
789 | "failed to realloc buffer to %zu\n", buf_len); | |
790 | ret = -ENOMEM; | |
791 | goto err_buf_alloc; | |
792 | } | |
793 | ||
794 | smaster->buf_len = buf_len; | |
795 | smaster->buf = buf; | |
796 | } | |
797 | ||
798 | ret = device_register(&sdevice->dev); | |
799 | if (ret) { | |
800 | dev_err(&smaster->dev, "failed to register device: %d\n", ret); | |
801 | ||
802 | goto err_device_register; | |
803 | } | |
804 | ||
805 | smaster->num_devices++; | |
806 | list_add_tail(&sdevice->node, &smaster->devices); | |
807 | ||
808 | smaster->setbuf_len += sdevice->inbytes; | |
809 | smaster->getbuf_len += sdevice->outbytes; | |
810 | ||
811 | sdevice->status_errors_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, | |
812 | "status_errors"); | |
813 | sdevice->watchdog_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, | |
814 | "watchdog"); | |
815 | sdevice->watchdog_errors_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, | |
816 | "watchdog_errors"); | |
817 | sdevice->connected_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, | |
818 | "connected"); | |
819 | ||
820 | siox_master_unlock(smaster); | |
821 | ||
822 | return sdevice; | |
823 | ||
824 | err_device_register: | |
825 | /* don't care to make the buffer smaller again */ | |
826 | ||
827 | err_buf_alloc: | |
828 | siox_master_unlock(smaster); | |
829 | ||
830 | kfree(sdevice); | |
831 | ||
832 | return ERR_PTR(ret); | |
833 | } | |
834 | ||
835 | static void siox_device_remove(struct siox_master *smaster) | |
836 | { | |
837 | struct siox_device *sdevice; | |
838 | ||
839 | siox_master_lock(smaster); | |
840 | ||
841 | if (!smaster->num_devices) { | |
842 | siox_master_unlock(smaster); | |
843 | return; | |
844 | } | |
845 | ||
846 | sdevice = container_of(smaster->devices.prev, struct siox_device, node); | |
847 | list_del(&sdevice->node); | |
848 | smaster->num_devices--; | |
849 | ||
850 | smaster->setbuf_len -= sdevice->inbytes; | |
851 | smaster->getbuf_len -= sdevice->outbytes; | |
852 | ||
853 | if (!smaster->num_devices) | |
854 | __siox_stop(smaster); | |
855 | ||
856 | siox_master_unlock(smaster); | |
857 | ||
858 | /* | |
859 | * This must be done without holding the master lock because we're | |
860 | * called from device_remove_store which also holds a sysfs mutex. | |
861 | * device_unregister tries to aquire the same lock. | |
862 | */ | |
863 | device_unregister(&sdevice->dev); | |
864 | } | |
865 | ||
866 | int __siox_driver_register(struct siox_driver *sdriver, struct module *owner) | |
867 | { | |
868 | int ret; | |
869 | ||
870 | if (unlikely(!siox_is_registered)) | |
871 | return -EPROBE_DEFER; | |
872 | ||
873 | if (!sdriver->set_data && !sdriver->get_data) { | |
874 | pr_err("Driver %s doesn't provide needed callbacks\n", | |
875 | sdriver->driver.name); | |
876 | return -EINVAL; | |
877 | } | |
878 | ||
879 | sdriver->driver.owner = owner; | |
880 | sdriver->driver.bus = &siox_bus_type; | |
881 | ||
882 | if (sdriver->probe) | |
883 | sdriver->driver.probe = siox_driver_probe; | |
884 | if (sdriver->remove) | |
885 | sdriver->driver.remove = siox_driver_remove; | |
886 | if (sdriver->shutdown) | |
887 | sdriver->driver.shutdown = siox_driver_shutdown; | |
888 | ||
889 | ret = driver_register(&sdriver->driver); | |
890 | if (ret) | |
891 | pr_err("Failed to register siox driver %s (%d)\n", | |
892 | sdriver->driver.name, ret); | |
893 | ||
894 | return ret; | |
895 | } | |
896 | EXPORT_SYMBOL_GPL(__siox_driver_register); | |
897 | ||
898 | static int __init siox_init(void) | |
899 | { | |
900 | int ret; | |
901 | ||
902 | ret = bus_register(&siox_bus_type); | |
903 | if (ret) { | |
904 | pr_err("Registration of SIOX bus type failed: %d\n", ret); | |
905 | return ret; | |
906 | } | |
907 | ||
908 | siox_is_registered = true; | |
909 | ||
910 | return 0; | |
911 | } | |
912 | subsys_initcall(siox_init); | |
913 | ||
914 | static void __exit siox_exit(void) | |
915 | { | |
916 | bus_unregister(&siox_bus_type); | |
917 | } | |
918 | module_exit(siox_exit); | |
919 | ||
920 | MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); | |
921 | MODULE_DESCRIPTION("Eckelmann SIOX driver core"); | |
922 | MODULE_LICENSE("GPL v2"); |