Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

siox: new driver framework for eckelmann SIOX

SIOX is a bus system invented at Eckelmann AG to control their building
management and refrigeration systems. Traditionally the bus was
implemented on custom microcontrollers, today Linux based machines are
in use, too.

The topology on a SIOX bus looks as follows:

,------->--DCLK-->---------------+----------------------.
^ v v
,--------. ,----------------------. ,------
| | | ,--------------. | |
| |--->--DOUT-->---|->-|shift register|->-|--->---|
| | | `--------------' | |
| master | | device | | device
| | | ,--------------. | |
| |---<--DIN---<---|-<-|shift register|-<-|---<---|
| | | `--------------' | |
`--------' `----------------------' `------
v ^ ^
`----------DLD-------------------+----------------------'

There are two control lines (DCLK and DLD) driven from the bus master to
all devices in parallel and two daisy chained data lines, one for input
and one for output. DCLK is the clock to shift both chains by a single
bit. On an edge of DLD the devices latch both their input and output
shift registers.

This patch adds a framework for this bus type.

Acked-by: Gavin Schenk <g.schenk@eckelmann.de>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Uwe Kleine-König and committed by
Greg Kroah-Hartman
bbecb07f 0ba002bc

+1148
+87
Documentation/ABI/testing/sysfs-bus-siox
··· 1 + What: /sys/bus/siox/devices/siox-X/active 2 + KernelVersion: 4.16 3 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 4 + Description: 5 + On reading represents the current state of the bus. If it 6 + contains a "0" the bus is stopped and connected devices are 7 + expected to not do anything because their watchdog triggered. 8 + When the file contains a "1" the bus is operated and periodically 9 + does a push-pull cycle to write and read data from the 10 + connected devices. 11 + When writing a "0" or "1" the bus moves to the described state. 12 + 13 + What: /sys/bus/siox/devices/siox-X/device_add 14 + KernelVersion: 4.16 15 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 16 + Description: 17 + Write-only file. Write 18 + 19 + <type> <inbytes> <outbytes> <statustype> 20 + 21 + to add a new device dynamically. <type> is the name that is used to match 22 + to a driver (similar to the platform bus). <inbytes> and <outbytes> define 23 + the length of the input and output shift register in bytes respectively. 24 + <statustype> defines the 4 bit device type that is check to identify connection 25 + problems. 26 + The new device is added to the end of the existing chain. 27 + 28 + What: /sys/bus/siox/devices/siox-X/device_remove 29 + KernelVersion: 4.16 30 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 31 + Description: 32 + Write-only file. A single write removes the last device in the siox chain. 33 + 34 + What: /sys/bus/siox/devices/siox-X/poll_interval_ns 35 + KernelVersion: 4.16 36 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 37 + Description: 38 + Defines the interval between two poll cycles in nano seconds. 39 + Note this is rounded to jiffies on writing. On reading the current value 40 + is returned. 41 + 42 + What: /sys/bus/siox/devices/siox-X-Y/connected 43 + KernelVersion: 4.16 44 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 45 + Description: 46 + Read-only value. "0" means the Yth device on siox bus X isn't "connected" i.e. 47 + communication with it is not ensured. "1" signals a working connection. 48 + 49 + What: /sys/bus/siox/devices/siox-X-Y/inbytes 50 + KernelVersion: 4.16 51 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 52 + Description: 53 + Read-only value reporting the inbytes value provided to siox-X/device_add 54 + 55 + What: /sys/bus/siox/devices/siox-X-Y/status_errors 56 + KernelVersion: 4.16 57 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 58 + Description: 59 + Counts the number of time intervals when the read status byte doesn't yield the 60 + expected value. 61 + 62 + What: /sys/bus/siox/devices/siox-X-Y/type 63 + KernelVersion: 4.16 64 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 65 + Description: 66 + Read-only value reporting the type value provided to siox-X/device_add. 67 + 68 + What: /sys/bus/siox/devices/siox-X-Y/watchdog 69 + KernelVersion: 4.16 70 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 71 + Description: 72 + Read-only value reporting if the watchdog of the siox device is 73 + active. "0" means the watchdog is not active and the device is expected to 74 + be operational. "1" means the watchdog keeps the device in reset. 75 + 76 + What: /sys/bus/siox/devices/siox-X-Y/watchdog_errors 77 + KernelVersion: 4.16 78 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 79 + Description: 80 + Read-only value reporting the number to time intervals when the 81 + watchdog was active. 82 + 83 + What: /sys/bus/siox/devices/siox-X-Y/outbytes 84 + KernelVersion: 4.16 85 + Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> 86 + Description: 87 + Read-only value reporting the outbytes value provided to siox-X/device_add.
+2
drivers/Kconfig
··· 211 211 212 212 source "drivers/opp/Kconfig" 213 213 214 + source "drivers/siox/Kconfig" 215 + 214 216 endmenu
+1
drivers/Makefile
··· 184 184 obj-$(CONFIG_FSI) += fsi/ 185 185 obj-$(CONFIG_TEE) += tee/ 186 186 obj-$(CONFIG_MULTIPLEXER) += mux/ 187 + obj-$(CONFIG_SIOX) += siox/
+9
drivers/siox/Kconfig
··· 1 + menuconfig SIOX 2 + tristate "Eckelmann SIOX Support" 3 + help 4 + SIOX stands for Serial Input Output eXtension and is a synchronous 5 + bus system invented by Eckelmann AG. It is used in their control and 6 + remote monitoring systems for commercial and industrial refrigeration 7 + to drive additional I/O units. 8 + 9 + Unless you know better, it is probably safe to say "no" here.
+1
drivers/siox/Makefile
··· 1 + obj-$(CONFIG_SIOX) += siox-core.o
+922
drivers/siox/siox-core.c
··· 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");
+49
drivers/siox/siox.h
··· 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/kthread.h> 7 + #include <linux/siox.h> 8 + 9 + #define to_siox_master(_dev) container_of((_dev), struct siox_master, dev) 10 + struct siox_master { 11 + /* these fields should be initialized by the driver */ 12 + int busno; 13 + int (*pushpull)(struct siox_master *smaster, 14 + size_t setbuf_len, const u8 setbuf[], 15 + size_t getbuf_len, u8 getbuf[]); 16 + 17 + /* might be initialized by the driver, if 0 it is set to HZ / 40 */ 18 + unsigned long poll_interval; /* in jiffies */ 19 + 20 + /* framework private stuff */ 21 + struct mutex lock; 22 + bool active; 23 + struct module *owner; 24 + struct device dev; 25 + unsigned int num_devices; 26 + struct list_head devices; 27 + 28 + size_t setbuf_len, getbuf_len; 29 + size_t buf_len; 30 + u8 *buf; 31 + u8 status; 32 + 33 + unsigned long last_poll; 34 + struct task_struct *poll_thread; 35 + }; 36 + 37 + static inline void *siox_master_get_devdata(struct siox_master *smaster) 38 + { 39 + return dev_get_drvdata(&smaster->dev); 40 + } 41 + 42 + struct siox_master *siox_master_alloc(struct device *dev, size_t size); 43 + static inline void siox_master_put(struct siox_master *smaster) 44 + { 45 + put_device(&smaster->dev); 46 + } 47 + 48 + int siox_master_register(struct siox_master *smaster); 49 + void siox_master_unregister(struct siox_master *smaster);
+77
include/linux/siox.h
··· 1 + /* 2 + * Copyright (C) 2015 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> 3 + * 4 + * This program is free software; you can redistribute it and/or modify it under 5 + * the terms of the GNU General Public License version 2 as published by the 6 + * Free Software Foundation. 7 + */ 8 + 9 + #include <linux/device.h> 10 + 11 + #define to_siox_device(_dev) container_of((_dev), struct siox_device, dev) 12 + struct siox_device { 13 + struct list_head node; /* node in smaster->devices */ 14 + struct siox_master *smaster; 15 + struct device dev; 16 + 17 + const char *type; 18 + size_t inbytes; 19 + size_t outbytes; 20 + u8 statustype; 21 + 22 + u8 status_read_clean; 23 + u8 status_written; 24 + u8 status_written_lastcycle; 25 + bool connected; 26 + 27 + /* statistics */ 28 + unsigned int watchdog_errors; 29 + unsigned int status_errors; 30 + 31 + struct kernfs_node *status_errors_kn; 32 + struct kernfs_node *watchdog_kn; 33 + struct kernfs_node *watchdog_errors_kn; 34 + struct kernfs_node *connected_kn; 35 + }; 36 + 37 + bool siox_device_synced(struct siox_device *sdevice); 38 + bool siox_device_connected(struct siox_device *sdevice); 39 + 40 + struct siox_driver { 41 + int (*probe)(struct siox_device *sdevice); 42 + int (*remove)(struct siox_device *sdevice); 43 + void (*shutdown)(struct siox_device *sdevice); 44 + 45 + /* 46 + * buf is big enough to hold sdev->inbytes - 1 bytes, the status byte 47 + * is in the scope of the framework. 48 + */ 49 + int (*set_data)(struct siox_device *sdevice, u8 status, u8 buf[]); 50 + /* 51 + * buf is big enough to hold sdev->outbytes - 1 bytes, the status byte 52 + * is in the scope of the framework 53 + */ 54 + int (*get_data)(struct siox_device *sdevice, const u8 buf[]); 55 + 56 + struct device_driver driver; 57 + }; 58 + 59 + static inline struct siox_driver *to_siox_driver(struct device_driver *driver) 60 + { 61 + if (driver) 62 + return container_of(driver, struct siox_driver, driver); 63 + else 64 + return NULL; 65 + } 66 + 67 + int __siox_driver_register(struct siox_driver *sdriver, struct module *owner); 68 + 69 + static inline int siox_driver_register(struct siox_driver *sdriver) 70 + { 71 + return __siox_driver_register(sdriver, THIS_MODULE); 72 + } 73 + 74 + static inline void siox_driver_unregister(struct siox_driver *sdriver) 75 + { 76 + return driver_unregister(&sdriver->driver); 77 + }