162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015-2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/sysfs.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "siox.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * The lowest bit in the SIOX status word signals if the in-device watchdog is 1562306a36Sopenharmony_ci * ok. If the bit is set, the device is functional. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * On writing the watchdog timer is reset when this bit toggles. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#define SIOX_STATUS_WDG 0x01 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * Bits 1 to 3 of the status word read as the bitwise negation of what was 2362306a36Sopenharmony_ci * clocked in before. The value clocked in is changed in each cycle and so 2462306a36Sopenharmony_ci * allows to detect transmit/receive problems. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci#define SIOX_STATUS_COUNTER 0x0e 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Each Siox-Device has a 4 bit type number that is neither 0 nor 15. This is 3062306a36Sopenharmony_ci * available in the upper nibble of the read status. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * On write these bits are DC. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci#define SIOX_STATUS_TYPE 0xf0 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 3762306a36Sopenharmony_ci#include <trace/events/siox.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool siox_is_registered; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void siox_master_lock(struct siox_master *smaster) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci mutex_lock(&smaster->lock); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void siox_master_unlock(struct siox_master *smaster) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci mutex_unlock(&smaster->lock); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic inline u8 siox_status_clean(u8 status_read, u8 status_written) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci /* 5462306a36Sopenharmony_ci * bits 3:1 of status sample the respective bit in the status 5562306a36Sopenharmony_ci * byte written in the previous cycle but inverted. So if you wrote the 5662306a36Sopenharmony_ci * status word as 0xa before (counter = 0b101), it is expected to get 5762306a36Sopenharmony_ci * back the counter bits as 0b010. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * So given the last status written this function toggles the there 6062306a36Sopenharmony_ci * unset counter bits in the read value such that the counter bits in 6162306a36Sopenharmony_ci * the return value are all zero iff the bits were read as expected to 6262306a36Sopenharmony_ci * simplify error detection. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return status_read ^ (~status_written & 0xe); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic bool siox_device_counter_error(struct siox_device *sdevice, 6962306a36Sopenharmony_ci u8 status_clean) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return (status_clean & SIOX_STATUS_COUNTER) != 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic bool siox_device_type_error(struct siox_device *sdevice, u8 status_clean) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci u8 statustype = (status_clean & SIOX_STATUS_TYPE) >> 4; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * If the device knows which value the type bits should have, check 8062306a36Sopenharmony_ci * against this value otherwise just rule out the invalid values 0b0000 8162306a36Sopenharmony_ci * and 0b1111. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (sdevice->statustype) { 8462306a36Sopenharmony_ci if (statustype != sdevice->statustype) 8562306a36Sopenharmony_ci return true; 8662306a36Sopenharmony_ci } else { 8762306a36Sopenharmony_ci switch (statustype) { 8862306a36Sopenharmony_ci case 0: 8962306a36Sopenharmony_ci case 0xf: 9062306a36Sopenharmony_ci return true; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return false; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic bool siox_device_wdg_error(struct siox_device *sdevice, u8 status_clean) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return (status_clean & SIOX_STATUS_WDG) == 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * If there is a type or counter error the device is called "unsynced". 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cibool siox_device_synced(struct siox_device *sdevice) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci if (siox_device_type_error(sdevice, sdevice->status_read_clean)) 10862306a36Sopenharmony_ci return false; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return !siox_device_counter_error(sdevice, sdevice->status_read_clean); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_device_synced); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * A device is called "connected" if it is synced and the watchdog is not 11762306a36Sopenharmony_ci * asserted. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cibool siox_device_connected(struct siox_device *sdevice) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci if (!siox_device_synced(sdevice)) 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return !siox_device_wdg_error(sdevice, sdevice->status_read_clean); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_device_connected); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void siox_poll(struct siox_master *smaster) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct siox_device *sdevice; 13162306a36Sopenharmony_ci size_t i = smaster->setbuf_len; 13262306a36Sopenharmony_ci unsigned int devno = 0; 13362306a36Sopenharmony_ci int unsync_error = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci smaster->last_poll = jiffies; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * The counter bits change in each second cycle, the watchdog bit 13962306a36Sopenharmony_ci * toggles each time. 14062306a36Sopenharmony_ci * The counter bits hold values from [0, 6]. 7 would be possible 14162306a36Sopenharmony_ci * theoretically but the protocol designer considered that a bad idea 14262306a36Sopenharmony_ci * for reasons unknown today. (Maybe that's because then the status read 14362306a36Sopenharmony_ci * back has only zeros in the counter bits then which might be confused 14462306a36Sopenharmony_ci * with a stuck-at-0 error. But for the same reason (with s/0/1/) 0 14562306a36Sopenharmony_ci * could be skipped.) 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (++smaster->status > 0x0d) 14862306a36Sopenharmony_ci smaster->status = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci memset(smaster->buf, 0, smaster->setbuf_len); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* prepare data pushed out to devices in buf[0..setbuf_len) */ 15362306a36Sopenharmony_ci list_for_each_entry(sdevice, &smaster->devices, node) { 15462306a36Sopenharmony_ci struct siox_driver *sdriver = 15562306a36Sopenharmony_ci to_siox_driver(sdevice->dev.driver); 15662306a36Sopenharmony_ci sdevice->status_written = smaster->status; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci i -= sdevice->inbytes; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * If the device or a previous one is unsynced, don't pet the 16262306a36Sopenharmony_ci * watchdog. This is done to ensure that the device is kept in 16362306a36Sopenharmony_ci * reset when something is wrong. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci if (!siox_device_synced(sdevice)) 16662306a36Sopenharmony_ci unsync_error = 1; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (sdriver && !unsync_error) 16962306a36Sopenharmony_ci sdriver->set_data(sdevice, sdevice->status_written, 17062306a36Sopenharmony_ci &smaster->buf[i + 1]); 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * Don't trigger watchdog if there is no driver or a 17462306a36Sopenharmony_ci * sync problem 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci sdevice->status_written &= ~SIOX_STATUS_WDG; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci smaster->buf[i] = sdevice->status_written; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci trace_siox_set_data(smaster, sdevice, devno, i); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci devno++; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci smaster->pushpull(smaster, smaster->setbuf_len, smaster->buf, 18662306a36Sopenharmony_ci smaster->getbuf_len, 18762306a36Sopenharmony_ci smaster->buf + smaster->setbuf_len); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci unsync_error = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* interpret data pulled in from devices in buf[setbuf_len..] */ 19262306a36Sopenharmony_ci devno = 0; 19362306a36Sopenharmony_ci i = smaster->setbuf_len; 19462306a36Sopenharmony_ci list_for_each_entry(sdevice, &smaster->devices, node) { 19562306a36Sopenharmony_ci struct siox_driver *sdriver = 19662306a36Sopenharmony_ci to_siox_driver(sdevice->dev.driver); 19762306a36Sopenharmony_ci u8 status = smaster->buf[i + sdevice->outbytes - 1]; 19862306a36Sopenharmony_ci u8 status_clean; 19962306a36Sopenharmony_ci u8 prev_status_clean = sdevice->status_read_clean; 20062306a36Sopenharmony_ci bool synced = true; 20162306a36Sopenharmony_ci bool connected = true; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!siox_device_synced(sdevice)) 20462306a36Sopenharmony_ci unsync_error = 1; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * If the watchdog bit wasn't toggled in this cycle, report the 20862306a36Sopenharmony_ci * watchdog as active to give a consistent view for drivers and 20962306a36Sopenharmony_ci * sysfs consumers. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci if (!sdriver || unsync_error) 21262306a36Sopenharmony_ci status &= ~SIOX_STATUS_WDG; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci status_clean = 21562306a36Sopenharmony_ci siox_status_clean(status, 21662306a36Sopenharmony_ci sdevice->status_written_lastcycle); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Check counter and type bits */ 21962306a36Sopenharmony_ci if (siox_device_counter_error(sdevice, status_clean) || 22062306a36Sopenharmony_ci siox_device_type_error(sdevice, status_clean)) { 22162306a36Sopenharmony_ci bool prev_error; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci synced = false; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* only report a new error if the last cycle was ok */ 22662306a36Sopenharmony_ci prev_error = 22762306a36Sopenharmony_ci siox_device_counter_error(sdevice, 22862306a36Sopenharmony_ci prev_status_clean) || 22962306a36Sopenharmony_ci siox_device_type_error(sdevice, 23062306a36Sopenharmony_ci prev_status_clean); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (!prev_error) { 23362306a36Sopenharmony_ci sdevice->status_errors++; 23462306a36Sopenharmony_ci sysfs_notify_dirent(sdevice->status_errors_kn); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* If the device is unsynced report the watchdog as active */ 23962306a36Sopenharmony_ci if (!synced) { 24062306a36Sopenharmony_ci status &= ~SIOX_STATUS_WDG; 24162306a36Sopenharmony_ci status_clean &= ~SIOX_STATUS_WDG; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (siox_device_wdg_error(sdevice, status_clean)) 24562306a36Sopenharmony_ci connected = false; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* The watchdog state changed just now */ 24862306a36Sopenharmony_ci if ((status_clean ^ prev_status_clean) & SIOX_STATUS_WDG) { 24962306a36Sopenharmony_ci sysfs_notify_dirent(sdevice->watchdog_kn); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (siox_device_wdg_error(sdevice, status_clean)) { 25262306a36Sopenharmony_ci struct kernfs_node *wd_errs = 25362306a36Sopenharmony_ci sdevice->watchdog_errors_kn; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci sdevice->watchdog_errors++; 25662306a36Sopenharmony_ci sysfs_notify_dirent(wd_errs); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (connected != sdevice->connected) 26162306a36Sopenharmony_ci sysfs_notify_dirent(sdevice->connected_kn); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sdevice->status_read_clean = status_clean; 26462306a36Sopenharmony_ci sdevice->status_written_lastcycle = sdevice->status_written; 26562306a36Sopenharmony_ci sdevice->connected = connected; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci trace_siox_get_data(smaster, sdevice, devno, status_clean, i); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* only give data read to driver if the device is connected */ 27062306a36Sopenharmony_ci if (sdriver && connected) 27162306a36Sopenharmony_ci sdriver->get_data(sdevice, &smaster->buf[i]); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci devno++; 27462306a36Sopenharmony_ci i += sdevice->outbytes; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int siox_poll_thread(void *data) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct siox_master *smaster = data; 28162306a36Sopenharmony_ci signed long timeout = 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci get_device(&smaster->dev); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (;;) { 28662306a36Sopenharmony_ci if (kthread_should_stop()) { 28762306a36Sopenharmony_ci put_device(&smaster->dev); 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci siox_master_lock(smaster); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (smaster->active) { 29462306a36Sopenharmony_ci unsigned long next_poll = 29562306a36Sopenharmony_ci smaster->last_poll + smaster->poll_interval; 29662306a36Sopenharmony_ci if (time_is_before_eq_jiffies(next_poll)) 29762306a36Sopenharmony_ci siox_poll(smaster); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci timeout = smaster->poll_interval - 30062306a36Sopenharmony_ci (jiffies - smaster->last_poll); 30162306a36Sopenharmony_ci } else { 30262306a36Sopenharmony_ci timeout = MAX_SCHEDULE_TIMEOUT; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * Set the task to idle while holding the lock. This makes sure 30762306a36Sopenharmony_ci * that we don't sleep too long when the bus is reenabled before 30862306a36Sopenharmony_ci * schedule_timeout is reached. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci if (timeout > 0) 31162306a36Sopenharmony_ci set_current_state(TASK_IDLE); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci siox_master_unlock(smaster); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (timeout > 0) 31662306a36Sopenharmony_ci schedule_timeout(timeout); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * I'm not clear if/why it is important to set the state to 32062306a36Sopenharmony_ci * RUNNING again, but it fixes a "do not call blocking ops when 32162306a36Sopenharmony_ci * !TASK_RUNNING;"-warning. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int __siox_start(struct siox_master *smaster) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci if (!(smaster->setbuf_len + smaster->getbuf_len)) 33062306a36Sopenharmony_ci return -ENODEV; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!smaster->buf) 33362306a36Sopenharmony_ci return -ENOMEM; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (smaster->active) 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci smaster->active = 1; 33962306a36Sopenharmony_ci wake_up_process(smaster->poll_thread); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 1; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int siox_start(struct siox_master *smaster) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci int ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci siox_master_lock(smaster); 34962306a36Sopenharmony_ci ret = __siox_start(smaster); 35062306a36Sopenharmony_ci siox_master_unlock(smaster); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int __siox_stop(struct siox_master *smaster) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci if (smaster->active) { 35862306a36Sopenharmony_ci struct siox_device *sdevice; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci smaster->active = 0; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci list_for_each_entry(sdevice, &smaster->devices, node) { 36362306a36Sopenharmony_ci if (sdevice->connected) 36462306a36Sopenharmony_ci sysfs_notify_dirent(sdevice->connected_kn); 36562306a36Sopenharmony_ci sdevice->connected = false; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return 1; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int siox_stop(struct siox_master *smaster) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci int ret; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci siox_master_lock(smaster); 37862306a36Sopenharmony_ci ret = __siox_stop(smaster); 37962306a36Sopenharmony_ci siox_master_unlock(smaster); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic ssize_t type_show(struct device *dev, 38562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return sprintf(buf, "%s\n", sdev->type); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(type); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic ssize_t inbytes_show(struct device *dev, 39562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return sprintf(buf, "%zu\n", sdev->inbytes); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(inbytes); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic ssize_t outbytes_show(struct device *dev, 40562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return sprintf(buf, "%zu\n", sdev->outbytes); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(outbytes); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic ssize_t status_errors_show(struct device *dev, 41562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 41862306a36Sopenharmony_ci unsigned int status_errors; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci siox_master_lock(sdev->smaster); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci status_errors = sdev->status_errors; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci siox_master_unlock(sdev->smaster); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return sprintf(buf, "%u\n", status_errors); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(status_errors); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic ssize_t connected_show(struct device *dev, 43262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 43562306a36Sopenharmony_ci bool connected; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci siox_master_lock(sdev->smaster); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci connected = sdev->connected; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci siox_master_unlock(sdev->smaster); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return sprintf(buf, "%u\n", connected); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(connected); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic ssize_t watchdog_show(struct device *dev, 44962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 45262306a36Sopenharmony_ci u8 status; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci siox_master_lock(sdev->smaster); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci status = sdev->status_read_clean; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci siox_master_unlock(sdev->smaster); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return sprintf(buf, "%d\n", status & SIOX_STATUS_WDG); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(watchdog); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic ssize_t watchdog_errors_show(struct device *dev, 46662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct siox_device *sdev = to_siox_device(dev); 46962306a36Sopenharmony_ci unsigned int watchdog_errors; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci siox_master_lock(sdev->smaster); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci watchdog_errors = sdev->watchdog_errors; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci siox_master_unlock(sdev->smaster); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return sprintf(buf, "%u\n", watchdog_errors); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(watchdog_errors); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic struct attribute *siox_device_attrs[] = { 48362306a36Sopenharmony_ci &dev_attr_type.attr, 48462306a36Sopenharmony_ci &dev_attr_inbytes.attr, 48562306a36Sopenharmony_ci &dev_attr_outbytes.attr, 48662306a36Sopenharmony_ci &dev_attr_status_errors.attr, 48762306a36Sopenharmony_ci &dev_attr_connected.attr, 48862306a36Sopenharmony_ci &dev_attr_watchdog.attr, 48962306a36Sopenharmony_ci &dev_attr_watchdog_errors.attr, 49062306a36Sopenharmony_ci NULL 49162306a36Sopenharmony_ci}; 49262306a36Sopenharmony_ciATTRIBUTE_GROUPS(siox_device); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void siox_device_release(struct device *dev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci kfree(sdevice); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic struct device_type siox_device_type = { 50262306a36Sopenharmony_ci .groups = siox_device_groups, 50362306a36Sopenharmony_ci .release = siox_device_release, 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic int siox_match(struct device *dev, struct device_driver *drv) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci if (dev->type != &siox_device_type) 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* up to now there is only a single driver so keeping this simple */ 51262306a36Sopenharmony_ci return 1; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int siox_probe(struct device *dev) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct siox_driver *sdriver = to_siox_driver(dev->driver); 51862306a36Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return sdriver->probe(sdevice); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void siox_remove(struct device *dev) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct siox_driver *sdriver = 52662306a36Sopenharmony_ci container_of(dev->driver, struct siox_driver, driver); 52762306a36Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (sdriver->remove) 53062306a36Sopenharmony_ci sdriver->remove(sdevice); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void siox_shutdown(struct device *dev) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct siox_device *sdevice = to_siox_device(dev); 53662306a36Sopenharmony_ci struct siox_driver *sdriver; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (!dev->driver) 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci sdriver = container_of(dev->driver, struct siox_driver, driver); 54262306a36Sopenharmony_ci if (sdriver->shutdown) 54362306a36Sopenharmony_ci sdriver->shutdown(sdevice); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic struct bus_type siox_bus_type = { 54762306a36Sopenharmony_ci .name = "siox", 54862306a36Sopenharmony_ci .match = siox_match, 54962306a36Sopenharmony_ci .probe = siox_probe, 55062306a36Sopenharmony_ci .remove = siox_remove, 55162306a36Sopenharmony_ci .shutdown = siox_shutdown, 55262306a36Sopenharmony_ci}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic ssize_t active_show(struct device *dev, 55562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return sprintf(buf, "%d\n", smaster->active); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic ssize_t active_store(struct device *dev, 56362306a36Sopenharmony_ci struct device_attribute *attr, 56462306a36Sopenharmony_ci const char *buf, size_t count) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 56762306a36Sopenharmony_ci int ret; 56862306a36Sopenharmony_ci int active; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ret = kstrtoint(buf, 0, &active); 57162306a36Sopenharmony_ci if (ret < 0) 57262306a36Sopenharmony_ci return ret; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (active) 57562306a36Sopenharmony_ci ret = siox_start(smaster); 57662306a36Sopenharmony_ci else 57762306a36Sopenharmony_ci ret = siox_stop(smaster); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (ret < 0) 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return count; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(active); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic struct siox_device *siox_device_add(struct siox_master *smaster, 58862306a36Sopenharmony_ci const char *type, size_t inbytes, 58962306a36Sopenharmony_ci size_t outbytes, u8 statustype); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic ssize_t device_add_store(struct device *dev, 59262306a36Sopenharmony_ci struct device_attribute *attr, 59362306a36Sopenharmony_ci const char *buf, size_t count) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 59662306a36Sopenharmony_ci int ret; 59762306a36Sopenharmony_ci char type[20] = ""; 59862306a36Sopenharmony_ci size_t inbytes = 0, outbytes = 0; 59962306a36Sopenharmony_ci u8 statustype = 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci ret = sscanf(buf, "%19s %zu %zu %hhu", type, &inbytes, 60262306a36Sopenharmony_ci &outbytes, &statustype); 60362306a36Sopenharmony_ci if (ret != 3 && ret != 4) 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (strcmp(type, "siox-12x8") || inbytes != 2 || outbytes != 4) 60762306a36Sopenharmony_ci return -EINVAL; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci siox_device_add(smaster, "siox-12x8", inbytes, outbytes, statustype); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return count; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(device_add); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void siox_device_remove(struct siox_master *smaster); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic ssize_t device_remove_store(struct device *dev, 61962306a36Sopenharmony_ci struct device_attribute *attr, 62062306a36Sopenharmony_ci const char *buf, size_t count) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* XXX? require to write <type> <inbytes> <outbytes> */ 62562306a36Sopenharmony_ci siox_device_remove(smaster); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return count; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic DEVICE_ATTR_WO(device_remove); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic ssize_t poll_interval_ns_show(struct device *dev, 63362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return sprintf(buf, "%lld\n", jiffies_to_nsecs(smaster->poll_interval)); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic ssize_t poll_interval_ns_store(struct device *dev, 64162306a36Sopenharmony_ci struct device_attribute *attr, 64262306a36Sopenharmony_ci const char *buf, size_t count) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 64562306a36Sopenharmony_ci int ret; 64662306a36Sopenharmony_ci u64 val; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ret = kstrtou64(buf, 0, &val); 64962306a36Sopenharmony_ci if (ret < 0) 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci siox_master_lock(smaster); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci smaster->poll_interval = nsecs_to_jiffies(val); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci siox_master_unlock(smaster); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return count; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(poll_interval_ns); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic struct attribute *siox_master_attrs[] = { 66462306a36Sopenharmony_ci &dev_attr_active.attr, 66562306a36Sopenharmony_ci &dev_attr_device_add.attr, 66662306a36Sopenharmony_ci &dev_attr_device_remove.attr, 66762306a36Sopenharmony_ci &dev_attr_poll_interval_ns.attr, 66862306a36Sopenharmony_ci NULL 66962306a36Sopenharmony_ci}; 67062306a36Sopenharmony_ciATTRIBUTE_GROUPS(siox_master); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void siox_master_release(struct device *dev) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct siox_master *smaster = to_siox_master(dev); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci kfree(smaster); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic struct device_type siox_master_type = { 68062306a36Sopenharmony_ci .groups = siox_master_groups, 68162306a36Sopenharmony_ci .release = siox_master_release, 68262306a36Sopenharmony_ci}; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistruct siox_master *siox_master_alloc(struct device *dev, 68562306a36Sopenharmony_ci size_t size) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct siox_master *smaster; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!dev) 69062306a36Sopenharmony_ci return NULL; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci smaster = kzalloc(sizeof(*smaster) + size, GFP_KERNEL); 69362306a36Sopenharmony_ci if (!smaster) 69462306a36Sopenharmony_ci return NULL; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci device_initialize(&smaster->dev); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci smaster->busno = -1; 69962306a36Sopenharmony_ci smaster->dev.bus = &siox_bus_type; 70062306a36Sopenharmony_ci smaster->dev.type = &siox_master_type; 70162306a36Sopenharmony_ci smaster->dev.parent = dev; 70262306a36Sopenharmony_ci smaster->poll_interval = DIV_ROUND_UP(HZ, 40); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci dev_set_drvdata(&smaster->dev, &smaster[1]); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return smaster; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_master_alloc); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ciint siox_master_register(struct siox_master *smaster) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci int ret; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (!siox_is_registered) 71562306a36Sopenharmony_ci return -EPROBE_DEFER; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!smaster->pushpull) 71862306a36Sopenharmony_ci return -EINVAL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci dev_set_name(&smaster->dev, "siox-%d", smaster->busno); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci mutex_init(&smaster->lock); 72362306a36Sopenharmony_ci INIT_LIST_HEAD(&smaster->devices); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci smaster->last_poll = jiffies; 72662306a36Sopenharmony_ci smaster->poll_thread = kthread_run(siox_poll_thread, smaster, 72762306a36Sopenharmony_ci "siox-%d", smaster->busno); 72862306a36Sopenharmony_ci if (IS_ERR(smaster->poll_thread)) { 72962306a36Sopenharmony_ci smaster->active = 0; 73062306a36Sopenharmony_ci return PTR_ERR(smaster->poll_thread); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = device_add(&smaster->dev); 73462306a36Sopenharmony_ci if (ret) 73562306a36Sopenharmony_ci kthread_stop(smaster->poll_thread); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_master_register); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_civoid siox_master_unregister(struct siox_master *smaster) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci /* remove device */ 74462306a36Sopenharmony_ci device_del(&smaster->dev); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci siox_master_lock(smaster); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci __siox_stop(smaster); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci while (smaster->num_devices) { 75162306a36Sopenharmony_ci struct siox_device *sdevice; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci sdevice = container_of(smaster->devices.prev, 75462306a36Sopenharmony_ci struct siox_device, node); 75562306a36Sopenharmony_ci list_del(&sdevice->node); 75662306a36Sopenharmony_ci smaster->num_devices--; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci siox_master_unlock(smaster); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci device_unregister(&sdevice->dev); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci siox_master_lock(smaster); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci siox_master_unlock(smaster); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci put_device(&smaster->dev); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(siox_master_unregister); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic struct siox_device *siox_device_add(struct siox_master *smaster, 77262306a36Sopenharmony_ci const char *type, size_t inbytes, 77362306a36Sopenharmony_ci size_t outbytes, u8 statustype) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct siox_device *sdevice; 77662306a36Sopenharmony_ci int ret; 77762306a36Sopenharmony_ci size_t buf_len; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci sdevice = kzalloc(sizeof(*sdevice), GFP_KERNEL); 78062306a36Sopenharmony_ci if (!sdevice) 78162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci sdevice->type = type; 78462306a36Sopenharmony_ci sdevice->inbytes = inbytes; 78562306a36Sopenharmony_ci sdevice->outbytes = outbytes; 78662306a36Sopenharmony_ci sdevice->statustype = statustype; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci sdevice->smaster = smaster; 78962306a36Sopenharmony_ci sdevice->dev.parent = &smaster->dev; 79062306a36Sopenharmony_ci sdevice->dev.bus = &siox_bus_type; 79162306a36Sopenharmony_ci sdevice->dev.type = &siox_device_type; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci siox_master_lock(smaster); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci dev_set_name(&sdevice->dev, "siox-%d-%d", 79662306a36Sopenharmony_ci smaster->busno, smaster->num_devices); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci buf_len = smaster->setbuf_len + inbytes + 79962306a36Sopenharmony_ci smaster->getbuf_len + outbytes; 80062306a36Sopenharmony_ci if (smaster->buf_len < buf_len) { 80162306a36Sopenharmony_ci u8 *buf = krealloc(smaster->buf, buf_len, GFP_KERNEL); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (!buf) { 80462306a36Sopenharmony_ci dev_err(&smaster->dev, 80562306a36Sopenharmony_ci "failed to realloc buffer to %zu\n", buf_len); 80662306a36Sopenharmony_ci ret = -ENOMEM; 80762306a36Sopenharmony_ci goto err_buf_alloc; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci smaster->buf_len = buf_len; 81162306a36Sopenharmony_ci smaster->buf = buf; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci ret = device_register(&sdevice->dev); 81562306a36Sopenharmony_ci if (ret) { 81662306a36Sopenharmony_ci dev_err(&smaster->dev, "failed to register device: %d\n", ret); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci goto err_device_register; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci smaster->num_devices++; 82262306a36Sopenharmony_ci list_add_tail(&sdevice->node, &smaster->devices); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci smaster->setbuf_len += sdevice->inbytes; 82562306a36Sopenharmony_ci smaster->getbuf_len += sdevice->outbytes; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci sdevice->status_errors_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 82862306a36Sopenharmony_ci "status_errors"); 82962306a36Sopenharmony_ci sdevice->watchdog_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 83062306a36Sopenharmony_ci "watchdog"); 83162306a36Sopenharmony_ci sdevice->watchdog_errors_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 83262306a36Sopenharmony_ci "watchdog_errors"); 83362306a36Sopenharmony_ci sdevice->connected_kn = sysfs_get_dirent(sdevice->dev.kobj.sd, 83462306a36Sopenharmony_ci "connected"); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci siox_master_unlock(smaster); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return sdevice; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cierr_device_register: 84162306a36Sopenharmony_ci /* don't care to make the buffer smaller again */ 84262306a36Sopenharmony_ci put_device(&sdevice->dev); 84362306a36Sopenharmony_ci sdevice = NULL; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cierr_buf_alloc: 84662306a36Sopenharmony_ci siox_master_unlock(smaster); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci kfree(sdevice); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return ERR_PTR(ret); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic void siox_device_remove(struct siox_master *smaster) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct siox_device *sdevice; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci siox_master_lock(smaster); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (!smaster->num_devices) { 86062306a36Sopenharmony_ci siox_master_unlock(smaster); 86162306a36Sopenharmony_ci return; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci sdevice = container_of(smaster->devices.prev, struct siox_device, node); 86562306a36Sopenharmony_ci list_del(&sdevice->node); 86662306a36Sopenharmony_ci smaster->num_devices--; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci smaster->setbuf_len -= sdevice->inbytes; 86962306a36Sopenharmony_ci smaster->getbuf_len -= sdevice->outbytes; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (!smaster->num_devices) 87262306a36Sopenharmony_ci __siox_stop(smaster); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci siox_master_unlock(smaster); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* 87762306a36Sopenharmony_ci * This must be done without holding the master lock because we're 87862306a36Sopenharmony_ci * called from device_remove_store which also holds a sysfs mutex. 87962306a36Sopenharmony_ci * device_unregister tries to aquire the same lock. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci device_unregister(&sdevice->dev); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ciint __siox_driver_register(struct siox_driver *sdriver, struct module *owner) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci int ret; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (unlikely(!siox_is_registered)) 88962306a36Sopenharmony_ci return -EPROBE_DEFER; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (!sdriver->probe || 89262306a36Sopenharmony_ci (!sdriver->set_data && !sdriver->get_data)) { 89362306a36Sopenharmony_ci pr_err("Driver %s doesn't provide needed callbacks\n", 89462306a36Sopenharmony_ci sdriver->driver.name); 89562306a36Sopenharmony_ci return -EINVAL; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci sdriver->driver.owner = owner; 89962306a36Sopenharmony_ci sdriver->driver.bus = &siox_bus_type; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci ret = driver_register(&sdriver->driver); 90262306a36Sopenharmony_ci if (ret) 90362306a36Sopenharmony_ci pr_err("Failed to register siox driver %s (%d)\n", 90462306a36Sopenharmony_ci sdriver->driver.name, ret); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci return ret; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__siox_driver_register); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic int __init siox_init(void) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci int ret; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci ret = bus_register(&siox_bus_type); 91562306a36Sopenharmony_ci if (ret) { 91662306a36Sopenharmony_ci pr_err("Registration of SIOX bus type failed: %d\n", ret); 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci siox_is_registered = true; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_cisubsys_initcall(siox_init); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic void __exit siox_exit(void) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci bus_unregister(&siox_bus_type); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_cimodule_exit(siox_exit); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ciMODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); 93362306a36Sopenharmony_ciMODULE_DESCRIPTION("Eckelmann SIOX driver core"); 93462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 935