18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/kernel.h> 68c2ecf20Sopenharmony_ci#include <linux/device.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "siox.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * The lowest bit in the SIOX status word signals if the in-device watchdog is 158c2ecf20Sopenharmony_ci * ok. If the bit is set, the device is functional. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * On writing the watchdog timer is reset when this bit toggles. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#define SIOX_STATUS_WDG 0x01 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Bits 1 to 3 of the status word read as the bitwise negation of what was 238c2ecf20Sopenharmony_ci * clocked in before. The value clocked in is changed in each cycle and so 248c2ecf20Sopenharmony_ci * allows to detect transmit/receive problems. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci#define SIOX_STATUS_COUNTER 0x0e 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Each Siox-Device has a 4 bit type number that is neither 0 nor 15. This is 308c2ecf20Sopenharmony_ci * available in the upper nibble of the read status. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * On write these bits are DC. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define SIOX_STATUS_TYPE 0xf0 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 378c2ecf20Sopenharmony_ci#include <trace/events/siox.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic bool siox_is_registered; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void siox_master_lock(struct siox_master *smaster) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci mutex_lock(&smaster->lock); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void siox_master_unlock(struct siox_master *smaster) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci mutex_unlock(&smaster->lock); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic inline u8 siox_status_clean(u8 status_read, u8 status_written) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * bits 3:1 of status sample the respective bit in the status 558c2ecf20Sopenharmony_ci * byte written in the previous cycle but inverted. So if you wrote the 568c2ecf20Sopenharmony_ci * status word as 0xa before (counter = 0b101), it is expected to get 578c2ecf20Sopenharmony_ci * back the counter bits as 0b010. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * So given the last status written this function toggles the there 608c2ecf20Sopenharmony_ci * unset counter bits in the read value such that the counter bits in 618c2ecf20Sopenharmony_ci * the return value are all zero iff the bits were read as expected to 628c2ecf20Sopenharmony_ci * simplify error detection. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return status_read ^ (~status_written & 0xe); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic bool siox_device_counter_error(struct siox_device *sdevice, 698c2ecf20Sopenharmony_ci u8 status_clean) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci return (status_clean & SIOX_STATUS_COUNTER) != 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic bool siox_device_type_error(struct siox_device *sdevice, u8 status_clean) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci u8 statustype = (status_clean & SIOX_STATUS_TYPE) >> 4; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * If the device knows which value the type bits should have, check 808c2ecf20Sopenharmony_ci * against this value otherwise just rule out the invalid values 0b0000 818c2ecf20Sopenharmony_ci * and 0b1111. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci if (sdevice->statustype) { 848c2ecf20Sopenharmony_ci if (statustype != sdevice->statustype) 858c2ecf20Sopenharmony_ci return true; 868c2ecf20Sopenharmony_ci } else { 878c2ecf20Sopenharmony_ci switch (statustype) { 888c2ecf20Sopenharmony_ci case 0: 898c2ecf20Sopenharmony_ci case 0xf: 908c2ecf20Sopenharmony_ci return true; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return false; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic bool siox_device_wdg_error(struct siox_device *sdevice, u8 status_clean) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return (status_clean & SIOX_STATUS_WDG) == 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * If there is a type or counter error the device is called "unsynced". 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_cibool siox_device_synced(struct siox_device *sdevice) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci if (siox_device_type_error(sdevice, sdevice->status_read_clean)) 1088c2ecf20Sopenharmony_ci return false; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return !siox_device_counter_error(sdevice, sdevice->status_read_clean); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_device_synced); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * A device is called "connected" if it is synced and the watchdog is not 1178c2ecf20Sopenharmony_ci * asserted. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cibool siox_device_connected(struct siox_device *sdevice) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci if (!siox_device_synced(sdevice)) 1228c2ecf20Sopenharmony_ci return false; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return !siox_device_wdg_error(sdevice, sdevice->status_read_clean); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_device_connected); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void siox_poll(struct siox_master *smaster) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct siox_device *sdevice; 1318c2ecf20Sopenharmony_ci size_t i = smaster->setbuf_len; 1328c2ecf20Sopenharmony_ci unsigned int devno = 0; 1338c2ecf20Sopenharmony_ci int unsync_error = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci smaster->last_poll = jiffies; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * The counter bits change in each second cycle, the watchdog bit 1398c2ecf20Sopenharmony_ci * toggles each time. 1408c2ecf20Sopenharmony_ci * The counter bits hold values from [0, 6]. 7 would be possible 1418c2ecf20Sopenharmony_ci * theoretically but the protocol designer considered that a bad idea 1428c2ecf20Sopenharmony_ci * for reasons unknown today. (Maybe that's because then the status read 1438c2ecf20Sopenharmony_ci * back has only zeros in the counter bits then which might be confused 1448c2ecf20Sopenharmony_ci * with a stuck-at-0 error. But for the same reason (with s/0/1/) 0 1458c2ecf20Sopenharmony_ci * could be skipped.) 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci if (++smaster->status > 0x0d) 1488c2ecf20Sopenharmony_ci smaster->status = 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci memset(smaster->buf, 0, smaster->setbuf_len); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* prepare data pushed out to devices in buf[0..setbuf_len) */ 1538c2ecf20Sopenharmony_ci list_for_each_entry(sdevice, &smaster->devices, node) { 1548c2ecf20Sopenharmony_ci struct siox_driver *sdriver = 1558c2ecf20Sopenharmony_ci to_siox_driver(sdevice->dev.driver); 1568c2ecf20Sopenharmony_ci sdevice->status_written = smaster->status; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci i -= sdevice->inbytes; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * If the device or a previous one is unsynced, don't pet the 1628c2ecf20Sopenharmony_ci * watchdog. This is done to ensure that the device is kept in 1638c2ecf20Sopenharmony_ci * reset when something is wrong. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci if (!siox_device_synced(sdevice)) 1668c2ecf20Sopenharmony_ci unsync_error = 1; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (sdriver && !unsync_error) 1698c2ecf20Sopenharmony_ci sdriver->set_data(sdevice, sdevice->status_written, 1708c2ecf20Sopenharmony_ci &smaster->buf[i + 1]); 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Don't trigger watchdog if there is no driver or a 1748c2ecf20Sopenharmony_ci * sync problem 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci sdevice->status_written &= ~SIOX_STATUS_WDG; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci smaster->buf[i] = sdevice->status_written; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci trace_siox_set_data(smaster, sdevice, devno, i); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci devno++; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci smaster->pushpull(smaster, smaster->setbuf_len, smaster->buf, 1868c2ecf20Sopenharmony_ci smaster->getbuf_len, 1878c2ecf20Sopenharmony_ci smaster->buf + smaster->setbuf_len); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci unsync_error = 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* interpret data pulled in from devices in buf[setbuf_len..] */ 1928c2ecf20Sopenharmony_ci devno = 0; 1938c2ecf20Sopenharmony_ci i = smaster->setbuf_len; 1948c2ecf20Sopenharmony_ci list_for_each_entry(sdevice, &smaster->devices, node) { 1958c2ecf20Sopenharmony_ci struct siox_driver *sdriver = 1968c2ecf20Sopenharmony_ci to_siox_driver(sdevice->dev.driver); 1978c2ecf20Sopenharmony_ci u8 status = smaster->buf[i + sdevice->outbytes - 1]; 1988c2ecf20Sopenharmony_ci u8 status_clean; 1998c2ecf20Sopenharmony_ci u8 prev_status_clean = sdevice->status_read_clean; 2008c2ecf20Sopenharmony_ci bool synced = true; 2018c2ecf20Sopenharmony_ci bool connected = true; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (!siox_device_synced(sdevice)) 2048c2ecf20Sopenharmony_ci unsync_error = 1; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * If the watchdog bit wasn't toggled in this cycle, report the 2088c2ecf20Sopenharmony_ci * watchdog as active to give a consistent view for drivers and 2098c2ecf20Sopenharmony_ci * sysfs consumers. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci if (!sdriver || unsync_error) 2128c2ecf20Sopenharmony_ci status &= ~SIOX_STATUS_WDG; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci status_clean = 2158c2ecf20Sopenharmony_ci siox_status_clean(status, 2168c2ecf20Sopenharmony_ci sdevice->status_written_lastcycle); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Check counter and type bits */ 2198c2ecf20Sopenharmony_ci if (siox_device_counter_error(sdevice, status_clean) || 2208c2ecf20Sopenharmony_ci siox_device_type_error(sdevice, status_clean)) { 2218c2ecf20Sopenharmony_ci bool prev_error; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci synced = false; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* only report a new error if the last cycle was ok */ 2268c2ecf20Sopenharmony_ci prev_error = 2278c2ecf20Sopenharmony_ci siox_device_counter_error(sdevice, 2288c2ecf20Sopenharmony_ci prev_status_clean) || 2298c2ecf20Sopenharmony_ci siox_device_type_error(sdevice, 2308c2ecf20Sopenharmony_ci prev_status_clean); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (!prev_error) { 2338c2ecf20Sopenharmony_ci sdevice->status_errors++; 2348c2ecf20Sopenharmony_ci sysfs_notify_dirent(sdevice->status_errors_kn); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* If the device is unsynced report the watchdog as active */ 2398c2ecf20Sopenharmony_ci if (!synced) { 2408c2ecf20Sopenharmony_ci status &= ~SIOX_STATUS_WDG; 2418c2ecf20Sopenharmony_ci status_clean &= ~SIOX_STATUS_WDG; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (siox_device_wdg_error(sdevice, status_clean)) 2458c2ecf20Sopenharmony_ci connected = false; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* The watchdog state changed just now */ 2488c2ecf20Sopenharmony_ci if ((status_clean ^ prev_status_clean) & SIOX_STATUS_WDG) { 2498c2ecf20Sopenharmony_ci sysfs_notify_dirent(sdevice->watchdog_kn); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (siox_device_wdg_error(sdevice, status_clean)) { 2528c2ecf20Sopenharmony_ci struct kernfs_node *wd_errs = 2538c2ecf20Sopenharmony_ci sdevice->watchdog_errors_kn; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci sdevice->watchdog_errors++; 2568c2ecf20Sopenharmony_ci sysfs_notify_dirent(wd_errs); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (connected != sdevice->connected) 2618c2ecf20Sopenharmony_ci sysfs_notify_dirent(sdevice->connected_kn); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci sdevice->status_read_clean = status_clean; 2648c2ecf20Sopenharmony_ci sdevice->status_written_lastcycle = sdevice->status_written; 2658c2ecf20Sopenharmony_ci sdevice->connected = connected; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci trace_siox_get_data(smaster, sdevice, devno, status_clean, i); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* only give data read to driver if the device is connected */ 2708c2ecf20Sopenharmony_ci if (sdriver && connected) 2718c2ecf20Sopenharmony_ci sdriver->get_data(sdevice, &smaster->buf[i]); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci devno++; 2748c2ecf20Sopenharmony_ci i += sdevice->outbytes; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int siox_poll_thread(void *data) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct siox_master *smaster = data; 2818c2ecf20Sopenharmony_ci signed long timeout = 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci get_device(&smaster->dev); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci for (;;) { 2868c2ecf20Sopenharmony_ci if (kthread_should_stop()) { 2878c2ecf20Sopenharmony_ci put_device(&smaster->dev); 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci siox_master_lock(smaster); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (smaster->active) { 2948c2ecf20Sopenharmony_ci unsigned long next_poll = 2958c2ecf20Sopenharmony_ci smaster->last_poll + smaster->poll_interval; 2968c2ecf20Sopenharmony_ci if (time_is_before_eq_jiffies(next_poll)) 2978c2ecf20Sopenharmony_ci siox_poll(smaster); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci timeout = smaster->poll_interval - 3008c2ecf20Sopenharmony_ci (jiffies - smaster->last_poll); 3018c2ecf20Sopenharmony_ci } else { 3028c2ecf20Sopenharmony_ci timeout = MAX_SCHEDULE_TIMEOUT; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * Set the task to idle while holding the lock. This makes sure 3078c2ecf20Sopenharmony_ci * that we don't sleep too long when the bus is reenabled before 3088c2ecf20Sopenharmony_ci * schedule_timeout is reached. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci if (timeout > 0) 3118c2ecf20Sopenharmony_ci set_current_state(TASK_IDLE); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (timeout > 0) 3168c2ecf20Sopenharmony_ci schedule_timeout(timeout); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* 3198c2ecf20Sopenharmony_ci * I'm not clear if/why it is important to set the state to 3208c2ecf20Sopenharmony_ci * RUNNING again, but it fixes a "do not call blocking ops when 3218c2ecf20Sopenharmony_ci * !TASK_RUNNING;"-warning. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int __siox_start(struct siox_master *smaster) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci if (!(smaster->setbuf_len + smaster->getbuf_len)) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!smaster->buf) 3338c2ecf20Sopenharmony_ci return -ENOMEM; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (smaster->active) 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci smaster->active = 1; 3398c2ecf20Sopenharmony_ci wake_up_process(smaster->poll_thread); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 1; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int siox_start(struct siox_master *smaster) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci siox_master_lock(smaster); 3498c2ecf20Sopenharmony_ci ret = __siox_start(smaster); 3508c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int __siox_stop(struct siox_master *smaster) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci if (smaster->active) { 3588c2ecf20Sopenharmony_ci struct siox_device *sdevice; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci smaster->active = 0; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci list_for_each_entry(sdevice, &smaster->devices, node) { 3638c2ecf20Sopenharmony_ci if (sdevice->connected) 3648c2ecf20Sopenharmony_ci sysfs_notify_dirent(sdevice->connected_kn); 3658c2ecf20Sopenharmony_ci sdevice->connected = false; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 1; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic int siox_stop(struct siox_master *smaster) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci int ret; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci siox_master_lock(smaster); 3788c2ecf20Sopenharmony_ci ret = __siox_stop(smaster); 3798c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic ssize_t type_show(struct device *dev, 3858c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", sdev->type); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(type); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic ssize_t inbytes_show(struct device *dev, 3958c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return sprintf(buf, "%zu\n", sdev->inbytes); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(inbytes); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic ssize_t outbytes_show(struct device *dev, 4058c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return sprintf(buf, "%zu\n", sdev->outbytes); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(outbytes); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic ssize_t status_errors_show(struct device *dev, 4158c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 4188c2ecf20Sopenharmony_ci unsigned int status_errors; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci siox_master_lock(sdev->smaster); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci status_errors = sdev->status_errors; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci siox_master_unlock(sdev->smaster); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", status_errors); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(status_errors); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic ssize_t connected_show(struct device *dev, 4328c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 4358c2ecf20Sopenharmony_ci bool connected; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci siox_master_lock(sdev->smaster); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci connected = sdev->connected; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci siox_master_unlock(sdev->smaster); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", connected); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(connected); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic ssize_t watchdog_show(struct device *dev, 4498c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 4528c2ecf20Sopenharmony_ci u8 status; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci siox_master_lock(sdev->smaster); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci status = sdev->status_read_clean; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci siox_master_unlock(sdev->smaster); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", status & SIOX_STATUS_WDG); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(watchdog); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic ssize_t watchdog_errors_show(struct device *dev, 4668c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 4698c2ecf20Sopenharmony_ci unsigned int watchdog_errors; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci siox_master_lock(sdev->smaster); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci watchdog_errors = sdev->watchdog_errors; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci siox_master_unlock(sdev->smaster); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", watchdog_errors); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(watchdog_errors); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic struct attribute *siox_device_attrs[] = { 4838c2ecf20Sopenharmony_ci &dev_attr_type.attr, 4848c2ecf20Sopenharmony_ci &dev_attr_inbytes.attr, 4858c2ecf20Sopenharmony_ci &dev_attr_outbytes.attr, 4868c2ecf20Sopenharmony_ci &dev_attr_status_errors.attr, 4878c2ecf20Sopenharmony_ci &dev_attr_connected.attr, 4888c2ecf20Sopenharmony_ci &dev_attr_watchdog.attr, 4898c2ecf20Sopenharmony_ci &dev_attr_watchdog_errors.attr, 4908c2ecf20Sopenharmony_ci NULL 4918c2ecf20Sopenharmony_ci}; 4928c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(siox_device); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void siox_device_release(struct device *dev) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci kfree(sdevice); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic struct device_type siox_device_type = { 5028c2ecf20Sopenharmony_ci .groups = siox_device_groups, 5038c2ecf20Sopenharmony_ci .release = siox_device_release, 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int siox_match(struct device *dev, struct device_driver *drv) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci if (dev->type != &siox_device_type) 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* up to now there is only a single driver so keeping this simple */ 5128c2ecf20Sopenharmony_ci return 1; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic struct bus_type siox_bus_type = { 5168c2ecf20Sopenharmony_ci .name = "siox", 5178c2ecf20Sopenharmony_ci .match = siox_match, 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int siox_driver_probe(struct device *dev) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct siox_driver *sdriver = to_siox_driver(dev->driver); 5238c2ecf20Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 5248c2ecf20Sopenharmony_ci int ret; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ret = sdriver->probe(sdevice); 5278c2ecf20Sopenharmony_ci return ret; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic int siox_driver_remove(struct device *dev) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct siox_driver *sdriver = 5338c2ecf20Sopenharmony_ci container_of(dev->driver, struct siox_driver, driver); 5348c2ecf20Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 5358c2ecf20Sopenharmony_ci int ret; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ret = sdriver->remove(sdevice); 5388c2ecf20Sopenharmony_ci return ret; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void siox_driver_shutdown(struct device *dev) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct siox_driver *sdriver = 5448c2ecf20Sopenharmony_ci container_of(dev->driver, struct siox_driver, driver); 5458c2ecf20Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci sdriver->shutdown(sdevice); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic ssize_t active_show(struct device *dev, 5518c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", smaster->active); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic ssize_t active_store(struct device *dev, 5598c2ecf20Sopenharmony_ci struct device_attribute *attr, 5608c2ecf20Sopenharmony_ci const char *buf, size_t count) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 5638c2ecf20Sopenharmony_ci int ret; 5648c2ecf20Sopenharmony_ci int active; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci ret = kstrtoint(buf, 0, &active); 5678c2ecf20Sopenharmony_ci if (ret < 0) 5688c2ecf20Sopenharmony_ci return ret; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (active) 5718c2ecf20Sopenharmony_ci ret = siox_start(smaster); 5728c2ecf20Sopenharmony_ci else 5738c2ecf20Sopenharmony_ci ret = siox_stop(smaster); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (ret < 0) 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return count; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(active); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic struct siox_device *siox_device_add(struct siox_master *smaster, 5848c2ecf20Sopenharmony_ci const char *type, size_t inbytes, 5858c2ecf20Sopenharmony_ci size_t outbytes, u8 statustype); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic ssize_t device_add_store(struct device *dev, 5888c2ecf20Sopenharmony_ci struct device_attribute *attr, 5898c2ecf20Sopenharmony_ci const char *buf, size_t count) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 5928c2ecf20Sopenharmony_ci int ret; 5938c2ecf20Sopenharmony_ci char type[20] = ""; 5948c2ecf20Sopenharmony_ci size_t inbytes = 0, outbytes = 0; 5958c2ecf20Sopenharmony_ci u8 statustype = 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci ret = sscanf(buf, "%19s %zu %zu %hhu", type, &inbytes, 5988c2ecf20Sopenharmony_ci &outbytes, &statustype); 5998c2ecf20Sopenharmony_ci if (ret != 3 && ret != 4) 6008c2ecf20Sopenharmony_ci return -EINVAL; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (strcmp(type, "siox-12x8") || inbytes != 2 || outbytes != 4) 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci siox_device_add(smaster, "siox-12x8", inbytes, outbytes, statustype); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return count; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(device_add); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic void siox_device_remove(struct siox_master *smaster); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic ssize_t device_remove_store(struct device *dev, 6158c2ecf20Sopenharmony_ci struct device_attribute *attr, 6168c2ecf20Sopenharmony_ci const char *buf, size_t count) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* XXX? require to write <type> <inbytes> <outbytes> */ 6218c2ecf20Sopenharmony_ci siox_device_remove(smaster); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return count; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(device_remove); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic ssize_t poll_interval_ns_show(struct device *dev, 6298c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return sprintf(buf, "%lld\n", jiffies_to_nsecs(smaster->poll_interval)); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic ssize_t poll_interval_ns_store(struct device *dev, 6378c2ecf20Sopenharmony_ci struct device_attribute *attr, 6388c2ecf20Sopenharmony_ci const char *buf, size_t count) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 6418c2ecf20Sopenharmony_ci int ret; 6428c2ecf20Sopenharmony_ci u64 val; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ret = kstrtou64(buf, 0, &val); 6458c2ecf20Sopenharmony_ci if (ret < 0) 6468c2ecf20Sopenharmony_ci return ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci siox_master_lock(smaster); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci smaster->poll_interval = nsecs_to_jiffies(val); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci return count; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(poll_interval_ns); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic struct attribute *siox_master_attrs[] = { 6608c2ecf20Sopenharmony_ci &dev_attr_active.attr, 6618c2ecf20Sopenharmony_ci &dev_attr_device_add.attr, 6628c2ecf20Sopenharmony_ci &dev_attr_device_remove.attr, 6638c2ecf20Sopenharmony_ci &dev_attr_poll_interval_ns.attr, 6648c2ecf20Sopenharmony_ci NULL 6658c2ecf20Sopenharmony_ci}; 6668c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(siox_master); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void siox_master_release(struct device *dev) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci kfree(smaster); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic struct device_type siox_master_type = { 6768c2ecf20Sopenharmony_ci .groups = siox_master_groups, 6778c2ecf20Sopenharmony_ci .release = siox_master_release, 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistruct siox_master *siox_master_alloc(struct device *dev, 6818c2ecf20Sopenharmony_ci size_t size) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci struct siox_master *smaster; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (!dev) 6868c2ecf20Sopenharmony_ci return NULL; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci smaster = kzalloc(sizeof(*smaster) + size, GFP_KERNEL); 6898c2ecf20Sopenharmony_ci if (!smaster) 6908c2ecf20Sopenharmony_ci return NULL; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci device_initialize(&smaster->dev); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci smaster->busno = -1; 6958c2ecf20Sopenharmony_ci smaster->dev.bus = &siox_bus_type; 6968c2ecf20Sopenharmony_ci smaster->dev.type = &siox_master_type; 6978c2ecf20Sopenharmony_ci smaster->dev.parent = dev; 6988c2ecf20Sopenharmony_ci smaster->poll_interval = DIV_ROUND_UP(HZ, 40); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci dev_set_drvdata(&smaster->dev, &smaster[1]); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci return smaster; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_master_alloc); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ciint siox_master_register(struct siox_master *smaster) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci int ret; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (!siox_is_registered) 7118c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (!smaster->pushpull) 7148c2ecf20Sopenharmony_ci return -EINVAL; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci dev_set_name(&smaster->dev, "siox-%d", smaster->busno); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci mutex_init(&smaster->lock); 7198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&smaster->devices); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci smaster->last_poll = jiffies; 7228c2ecf20Sopenharmony_ci smaster->poll_thread = kthread_run(siox_poll_thread, smaster, 7238c2ecf20Sopenharmony_ci "siox-%d", smaster->busno); 7248c2ecf20Sopenharmony_ci if (IS_ERR(smaster->poll_thread)) { 7258c2ecf20Sopenharmony_ci smaster->active = 0; 7268c2ecf20Sopenharmony_ci return PTR_ERR(smaster->poll_thread); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci ret = device_add(&smaster->dev); 7308c2ecf20Sopenharmony_ci if (ret) 7318c2ecf20Sopenharmony_ci kthread_stop(smaster->poll_thread); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return ret; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_master_register); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_civoid siox_master_unregister(struct siox_master *smaster) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci /* remove device */ 7408c2ecf20Sopenharmony_ci device_del(&smaster->dev); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci siox_master_lock(smaster); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci __siox_stop(smaster); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci while (smaster->num_devices) { 7478c2ecf20Sopenharmony_ci struct siox_device *sdevice; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci sdevice = container_of(smaster->devices.prev, 7508c2ecf20Sopenharmony_ci struct siox_device, node); 7518c2ecf20Sopenharmony_ci list_del(&sdevice->node); 7528c2ecf20Sopenharmony_ci smaster->num_devices--; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci device_unregister(&sdevice->dev); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci siox_master_lock(smaster); 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci put_device(&smaster->dev); 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_master_unregister); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic struct siox_device *siox_device_add(struct siox_master *smaster, 7688c2ecf20Sopenharmony_ci const char *type, size_t inbytes, 7698c2ecf20Sopenharmony_ci size_t outbytes, u8 statustype) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct siox_device *sdevice; 7728c2ecf20Sopenharmony_ci int ret; 7738c2ecf20Sopenharmony_ci size_t buf_len; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci sdevice = kzalloc(sizeof(*sdevice), GFP_KERNEL); 7768c2ecf20Sopenharmony_ci if (!sdevice) 7778c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci sdevice->type = type; 7808c2ecf20Sopenharmony_ci sdevice->inbytes = inbytes; 7818c2ecf20Sopenharmony_ci sdevice->outbytes = outbytes; 7828c2ecf20Sopenharmony_ci sdevice->statustype = statustype; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci sdevice->smaster = smaster; 7858c2ecf20Sopenharmony_ci sdevice->dev.parent = &smaster->dev; 7868c2ecf20Sopenharmony_ci sdevice->dev.bus = &siox_bus_type; 7878c2ecf20Sopenharmony_ci sdevice->dev.type = &siox_device_type; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci siox_master_lock(smaster); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci dev_set_name(&sdevice->dev, "siox-%d-%d", 7928c2ecf20Sopenharmony_ci smaster->busno, smaster->num_devices); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci buf_len = smaster->setbuf_len + inbytes + 7958c2ecf20Sopenharmony_ci smaster->getbuf_len + outbytes; 7968c2ecf20Sopenharmony_ci if (smaster->buf_len < buf_len) { 7978c2ecf20Sopenharmony_ci u8 *buf = krealloc(smaster->buf, buf_len, GFP_KERNEL); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (!buf) { 8008c2ecf20Sopenharmony_ci dev_err(&smaster->dev, 8018c2ecf20Sopenharmony_ci "failed to realloc buffer to %zu\n", buf_len); 8028c2ecf20Sopenharmony_ci ret = -ENOMEM; 8038c2ecf20Sopenharmony_ci goto err_buf_alloc; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci smaster->buf_len = buf_len; 8078c2ecf20Sopenharmony_ci smaster->buf = buf; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ret = device_register(&sdevice->dev); 8118c2ecf20Sopenharmony_ci if (ret) { 8128c2ecf20Sopenharmony_ci dev_err(&smaster->dev, "failed to register device: %d\n", ret); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci goto err_device_register; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci smaster->num_devices++; 8188c2ecf20Sopenharmony_ci list_add_tail(&sdevice->node, &smaster->devices); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci smaster->setbuf_len += sdevice->inbytes; 8218c2ecf20Sopenharmony_ci smaster->getbuf_len += sdevice->outbytes; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci sdevice->status_errors_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 8248c2ecf20Sopenharmony_ci "status_errors"); 8258c2ecf20Sopenharmony_ci sdevice->watchdog_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 8268c2ecf20Sopenharmony_ci "watchdog"); 8278c2ecf20Sopenharmony_ci sdevice->watchdog_errors_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 8288c2ecf20Sopenharmony_ci "watchdog_errors"); 8298c2ecf20Sopenharmony_ci sdevice->connected_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 8308c2ecf20Sopenharmony_ci "connected"); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci return sdevice; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cierr_device_register: 8378c2ecf20Sopenharmony_ci /* don't care to make the buffer smaller again */ 8388c2ecf20Sopenharmony_ci put_device(&sdevice->dev); 8398c2ecf20Sopenharmony_ci sdevice = NULL; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cierr_buf_alloc: 8428c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci kfree(sdevice); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return ERR_PTR(ret); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void siox_device_remove(struct siox_master *smaster) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct siox_device *sdevice; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci siox_master_lock(smaster); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (!smaster->num_devices) { 8568c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 8578c2ecf20Sopenharmony_ci return; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci sdevice = container_of(smaster->devices.prev, struct siox_device, node); 8618c2ecf20Sopenharmony_ci list_del(&sdevice->node); 8628c2ecf20Sopenharmony_ci smaster->num_devices--; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci smaster->setbuf_len -= sdevice->inbytes; 8658c2ecf20Sopenharmony_ci smaster->getbuf_len -= sdevice->outbytes; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (!smaster->num_devices) 8688c2ecf20Sopenharmony_ci __siox_stop(smaster); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci siox_master_unlock(smaster); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* 8738c2ecf20Sopenharmony_ci * This must be done without holding the master lock because we're 8748c2ecf20Sopenharmony_ci * called from device_remove_store which also holds a sysfs mutex. 8758c2ecf20Sopenharmony_ci * device_unregister tries to aquire the same lock. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci device_unregister(&sdevice->dev); 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ciint __siox_driver_register(struct siox_driver *sdriver, struct module *owner) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci int ret; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (unlikely(!siox_is_registered)) 8858c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (!sdriver->set_data && !sdriver->get_data) { 8888c2ecf20Sopenharmony_ci pr_err("Driver %s doesn't provide needed callbacks\n", 8898c2ecf20Sopenharmony_ci sdriver->driver.name); 8908c2ecf20Sopenharmony_ci return -EINVAL; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci sdriver->driver.owner = owner; 8948c2ecf20Sopenharmony_ci sdriver->driver.bus = &siox_bus_type; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (sdriver->probe) 8978c2ecf20Sopenharmony_ci sdriver->driver.probe = siox_driver_probe; 8988c2ecf20Sopenharmony_ci if (sdriver->remove) 8998c2ecf20Sopenharmony_ci sdriver->driver.remove = siox_driver_remove; 9008c2ecf20Sopenharmony_ci if (sdriver->shutdown) 9018c2ecf20Sopenharmony_ci sdriver->driver.shutdown = siox_driver_shutdown; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci ret = driver_register(&sdriver->driver); 9048c2ecf20Sopenharmony_ci if (ret) 9058c2ecf20Sopenharmony_ci pr_err("Failed to register siox driver %s (%d)\n", 9068c2ecf20Sopenharmony_ci sdriver->driver.name, ret); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci return ret; 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__siox_driver_register); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic int __init siox_init(void) 9138c2ecf20Sopenharmony_ci{ 9148c2ecf20Sopenharmony_ci int ret; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci ret = bus_register(&siox_bus_type); 9178c2ecf20Sopenharmony_ci if (ret) { 9188c2ecf20Sopenharmony_ci pr_err("Registration of SIOX bus type failed: %d\n", ret); 9198c2ecf20Sopenharmony_ci return ret; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci siox_is_registered = true; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return 0; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_cisubsys_initcall(siox_init); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic void __exit siox_exit(void) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci bus_unregister(&siox_bus_type); 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_cimodule_exit(siox_exit); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); 9358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Eckelmann SIOX driver core"); 9368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 937