2017-12-18 19:59:07 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2015 - 2017 Pengutronix , Uwe Kleine - König < kernel @ pengutronix . de >
*/
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/sysfs.h>
# include "siox.h"
/*
* The lowest bit in the SIOX status word signals if the in - device watchdog is
* ok . If the bit is set , the device is functional .
*
* On writing the watchdog timer is reset when this bit toggles .
*/
# define SIOX_STATUS_WDG 0x01
/*
* Bits 1 to 3 of the status word read as the bitwise negation of what was
* clocked in before . The value clocked in is changed in each cycle and so
* allows to detect transmit / receive problems .
*/
# define SIOX_STATUS_COUNTER 0x0e
/*
* Each Siox - Device has a 4 bit type number that is neither 0 nor 15. This is
* available in the upper nibble of the read status .
*
* On write these bits are DC .
*/
# define SIOX_STATUS_TYPE 0xf0
2017-12-19 12:00:12 +03:00
# define CREATE_TRACE_POINTS
# include <trace/events/siox.h>
2017-12-18 19:59:07 +03:00
static bool siox_is_registered ;
static void siox_master_lock ( struct siox_master * smaster )
{
mutex_lock ( & smaster - > lock ) ;
}
static void siox_master_unlock ( struct siox_master * smaster )
{
mutex_unlock ( & smaster - > lock ) ;
}
static inline u8 siox_status_clean ( u8 status_read , u8 status_written )
{
/*
* bits 3 : 1 of status sample the respective bit in the status
* byte written in the previous cycle but inverted . So if you wrote the
* status word as 0xa before ( counter = 0 b101 ) , it is expected to get
* back the counter bits as 0 b010 .
*
* So given the last status written this function toggles the there
* unset counter bits in the read value such that the counter bits in
* the return value are all zero iff the bits were read as expected to
* simplify error detection .
*/
return status_read ^ ( ~ status_written & 0xe ) ;
}
static bool siox_device_counter_error ( struct siox_device * sdevice ,
u8 status_clean )
{
return ( status_clean & SIOX_STATUS_COUNTER ) ! = 0 ;
}
static bool siox_device_type_error ( struct siox_device * sdevice , u8 status_clean )
{
u8 statustype = ( status_clean & SIOX_STATUS_TYPE ) > > 4 ;
/*
* If the device knows which value the type bits should have , check
* against this value otherwise just rule out the invalid values 0 b0000
* and 0 b1111 .
*/
if ( sdevice - > statustype ) {
if ( statustype ! = sdevice - > statustype )
return true ;
} else {
switch ( statustype ) {
case 0 :
case 0xf :
return true ;
}
}
return false ;
}
static bool siox_device_wdg_error ( struct siox_device * sdevice , u8 status_clean )
{
return ( status_clean & SIOX_STATUS_WDG ) = = 0 ;
}
/*
* If there is a type or counter error the device is called " unsynced " .
*/
bool siox_device_synced ( struct siox_device * sdevice )
{
if ( siox_device_type_error ( sdevice , sdevice - > status_read_clean ) )
return false ;
return ! siox_device_counter_error ( sdevice , sdevice - > status_read_clean ) ;
}
EXPORT_SYMBOL_GPL ( siox_device_synced ) ;
/*
* A device is called " connected " if it is synced and the watchdog is not
* asserted .
*/
bool siox_device_connected ( struct siox_device * sdevice )
{
if ( ! siox_device_synced ( sdevice ) )
return false ;
return ! siox_device_wdg_error ( sdevice , sdevice - > status_read_clean ) ;
}
EXPORT_SYMBOL_GPL ( siox_device_connected ) ;
static void siox_poll ( struct siox_master * smaster )
{
struct siox_device * sdevice ;
size_t i = smaster - > setbuf_len ;
2017-12-19 12:00:12 +03:00
unsigned int devno = 0 ;
2017-12-18 19:59:07 +03:00
int unsync_error = 0 ;
smaster - > last_poll = jiffies ;
/*
* The counter bits change in each second cycle , the watchdog bit
* toggles each time .
* The counter bits hold values from [ 0 , 6 ] . 7 would be possible
* theoretically but the protocol designer considered that a bad idea
* for reasons unknown today . ( Maybe that ' s because then the status read
* back has only zeros in the counter bits then which might be confused
* with a stuck - at - 0 error . But for the same reason ( with s / 0 / 1 / ) 0
* could be skipped . )
*/
if ( + + smaster - > status > 0x0d )
smaster - > status = 0 ;
memset ( smaster - > buf , 0 , smaster - > setbuf_len ) ;
/* prepare data pushed out to devices in buf[0..setbuf_len) */
list_for_each_entry ( sdevice , & smaster - > devices , node ) {
struct siox_driver * sdriver =
to_siox_driver ( sdevice - > dev . driver ) ;
sdevice - > status_written = smaster - > status ;
i - = sdevice - > inbytes ;
/*
* If the device or a previous one is unsynced , don ' t pet the
* watchdog . This is done to ensure that the device is kept in
* reset when something is wrong .
*/
if ( ! siox_device_synced ( sdevice ) )
unsync_error = 1 ;
if ( sdriver & & ! unsync_error )
sdriver - > set_data ( sdevice , sdevice - > status_written ,
& smaster - > buf [ i + 1 ] ) ;
else
/*
* Don ' t trigger watchdog if there is no driver or a
* sync problem
*/
sdevice - > status_written & = ~ SIOX_STATUS_WDG ;
smaster - > buf [ i ] = sdevice - > status_written ;
2017-12-19 12:00:12 +03:00
trace_siox_set_data ( smaster , sdevice , devno , i ) ;
devno + + ;
2017-12-18 19:59:07 +03:00
}
smaster - > pushpull ( smaster , smaster - > setbuf_len , smaster - > buf ,
smaster - > getbuf_len ,
smaster - > buf + smaster - > setbuf_len ) ;
unsync_error = 0 ;
/* interpret data pulled in from devices in buf[setbuf_len..] */
2017-12-19 12:00:12 +03:00
devno = 0 ;
2017-12-18 19:59:07 +03:00
i = smaster - > setbuf_len ;
list_for_each_entry ( sdevice , & smaster - > devices , node ) {
struct siox_driver * sdriver =
to_siox_driver ( sdevice - > dev . driver ) ;
u8 status = smaster - > buf [ i + sdevice - > outbytes - 1 ] ;
u8 status_clean ;
u8 prev_status_clean = sdevice - > status_read_clean ;
bool synced = true ;
bool connected = true ;
if ( ! siox_device_synced ( sdevice ) )
unsync_error = 1 ;
/*
* If the watchdog bit wasn ' t toggled in this cycle , report the
* watchdog as active to give a consistent view for drivers and
* sysfs consumers .
*/
if ( ! sdriver | | unsync_error )
status & = ~ SIOX_STATUS_WDG ;
status_clean =
siox_status_clean ( status ,
sdevice - > status_written_lastcycle ) ;
2018-06-19 12:38:54 +03:00
/* Check counter and type bits */
if ( siox_device_counter_error ( sdevice , status_clean ) | |
siox_device_type_error ( sdevice , status_clean ) ) {
bool prev_error ;
2017-12-18 19:59:07 +03:00
synced = false ;
/* only report a new error if the last cycle was ok */
2018-06-19 12:38:54 +03:00
prev_error =
2017-12-18 19:59:07 +03:00
siox_device_counter_error ( sdevice ,
2018-06-19 12:38:54 +03:00
prev_status_clean ) | |
siox_device_type_error ( sdevice ,
prev_status_clean ) ;
if ( ! prev_error ) {
2017-12-18 19:59:07 +03:00
sdevice - > status_errors + + ;
sysfs_notify_dirent ( sdevice - > status_errors_kn ) ;
}
}
/* If the device is unsynced report the watchdog as active */
if ( ! synced ) {
status & = ~ SIOX_STATUS_WDG ;
status_clean & = ~ SIOX_STATUS_WDG ;
}
if ( siox_device_wdg_error ( sdevice , status_clean ) )
connected = false ;
/* The watchdog state changed just now */
if ( ( status_clean ^ prev_status_clean ) & SIOX_STATUS_WDG ) {
sysfs_notify_dirent ( sdevice - > watchdog_kn ) ;
if ( siox_device_wdg_error ( sdevice , status_clean ) ) {
struct kernfs_node * wd_errs =
sdevice - > watchdog_errors_kn ;
sdevice - > watchdog_errors + + ;
sysfs_notify_dirent ( wd_errs ) ;
}
}
if ( connected ! = sdevice - > connected )
sysfs_notify_dirent ( sdevice - > connected_kn ) ;
sdevice - > status_read_clean = status_clean ;
sdevice - > status_written_lastcycle = sdevice - > status_written ;
sdevice - > connected = connected ;
2017-12-19 12:00:12 +03:00
trace_siox_get_data ( smaster , sdevice , devno , status_clean , i ) ;
2017-12-18 19:59:07 +03:00
/* only give data read to driver if the device is connected */
if ( sdriver & & connected )
sdriver - > get_data ( sdevice , & smaster - > buf [ i ] ) ;
2017-12-19 12:00:12 +03:00
devno + + ;
2017-12-18 19:59:07 +03:00
i + = sdevice - > outbytes ;
}
}
static int siox_poll_thread ( void * data )
{
struct siox_master * smaster = data ;
signed long timeout = 0 ;
get_device ( & smaster - > dev ) ;
for ( ; ; ) {
if ( kthread_should_stop ( ) ) {
put_device ( & smaster - > dev ) ;
return 0 ;
}
siox_master_lock ( smaster ) ;
if ( smaster - > active ) {
unsigned long next_poll =
smaster - > last_poll + smaster - > poll_interval ;
if ( time_is_before_eq_jiffies ( next_poll ) )
siox_poll ( smaster ) ;
timeout = smaster - > poll_interval -
( jiffies - smaster - > last_poll ) ;
} else {
timeout = MAX_SCHEDULE_TIMEOUT ;
}
/*
* Set the task to idle while holding the lock . This makes sure
* that we don ' t sleep too long when the bus is reenabled before
* schedule_timeout is reached .
*/
if ( timeout > 0 )
set_current_state ( TASK_IDLE ) ;
siox_master_unlock ( smaster ) ;
if ( timeout > 0 )
schedule_timeout ( timeout ) ;
/*
* I ' m not clear if / why it is important to set the state to
* RUNNING again , but it fixes a " do not call blocking ops when
* ! TASK_RUNNING ; " -warning.
*/
set_current_state ( TASK_RUNNING ) ;
}
}
static int __siox_start ( struct siox_master * smaster )
{
if ( ! ( smaster - > setbuf_len + smaster - > getbuf_len ) )
return - ENODEV ;
if ( ! smaster - > buf )
return - ENOMEM ;
if ( smaster - > active )
return 0 ;
smaster - > active = 1 ;
wake_up_process ( smaster - > poll_thread ) ;
return 1 ;
}
static int siox_start ( struct siox_master * smaster )
{
int ret ;
siox_master_lock ( smaster ) ;
ret = __siox_start ( smaster ) ;
siox_master_unlock ( smaster ) ;
return ret ;
}
static int __siox_stop ( struct siox_master * smaster )
{
if ( smaster - > active ) {
struct siox_device * sdevice ;
smaster - > active = 0 ;
list_for_each_entry ( sdevice , & smaster - > devices , node ) {
if ( sdevice - > connected )
sysfs_notify_dirent ( sdevice - > connected_kn ) ;
sdevice - > connected = false ;
}
return 1 ;
}
return 0 ;
}
static int siox_stop ( struct siox_master * smaster )
{
int ret ;
siox_master_lock ( smaster ) ;
ret = __siox_stop ( smaster ) ;
siox_master_unlock ( smaster ) ;
return ret ;
}
static ssize_t type_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
return sprintf ( buf , " %s \n " , sdev - > type ) ;
}
static DEVICE_ATTR_RO ( type ) ;
static ssize_t inbytes_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
return sprintf ( buf , " %zu \n " , sdev - > inbytes ) ;
}
static DEVICE_ATTR_RO ( inbytes ) ;
static ssize_t outbytes_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
return sprintf ( buf , " %zu \n " , sdev - > outbytes ) ;
}
static DEVICE_ATTR_RO ( outbytes ) ;
static ssize_t status_errors_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
unsigned int status_errors ;
siox_master_lock ( sdev - > smaster ) ;
status_errors = sdev - > status_errors ;
siox_master_unlock ( sdev - > smaster ) ;
return sprintf ( buf , " %u \n " , status_errors ) ;
}
static DEVICE_ATTR_RO ( status_errors ) ;
static ssize_t connected_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
bool connected ;
siox_master_lock ( sdev - > smaster ) ;
connected = sdev - > connected ;
siox_master_unlock ( sdev - > smaster ) ;
return sprintf ( buf , " %u \n " , connected ) ;
}
static DEVICE_ATTR_RO ( connected ) ;
static ssize_t watchdog_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
u8 status ;
siox_master_lock ( sdev - > smaster ) ;
status = sdev - > status_read_clean ;
siox_master_unlock ( sdev - > smaster ) ;
return sprintf ( buf , " %d \n " , status & SIOX_STATUS_WDG ) ;
}
static DEVICE_ATTR_RO ( watchdog ) ;
static ssize_t watchdog_errors_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_device * sdev = to_siox_device ( dev ) ;
unsigned int watchdog_errors ;
siox_master_lock ( sdev - > smaster ) ;
watchdog_errors = sdev - > watchdog_errors ;
siox_master_unlock ( sdev - > smaster ) ;
return sprintf ( buf , " %u \n " , watchdog_errors ) ;
}
static DEVICE_ATTR_RO ( watchdog_errors ) ;
static struct attribute * siox_device_attrs [ ] = {
& dev_attr_type . attr ,
& dev_attr_inbytes . attr ,
& dev_attr_outbytes . attr ,
& dev_attr_status_errors . attr ,
& dev_attr_connected . attr ,
& dev_attr_watchdog . attr ,
& dev_attr_watchdog_errors . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( siox_device ) ;
static void siox_device_release ( struct device * dev )
{
struct siox_device * sdevice = to_siox_device ( dev ) ;
kfree ( sdevice ) ;
}
static struct device_type siox_device_type = {
. groups = siox_device_groups ,
. release = siox_device_release ,
} ;
static int siox_match ( struct device * dev , struct device_driver * drv )
{
if ( dev - > type ! = & siox_device_type )
return 0 ;
/* up to now there is only a single driver so keeping this simple */
return 1 ;
}
static struct bus_type siox_bus_type = {
. name = " siox " ,
. match = siox_match ,
} ;
static int siox_driver_probe ( struct device * dev )
{
struct siox_driver * sdriver = to_siox_driver ( dev - > driver ) ;
struct siox_device * sdevice = to_siox_device ( dev ) ;
int ret ;
ret = sdriver - > probe ( sdevice ) ;
return ret ;
}
static int siox_driver_remove ( struct device * dev )
{
struct siox_driver * sdriver =
container_of ( dev - > driver , struct siox_driver , driver ) ;
struct siox_device * sdevice = to_siox_device ( dev ) ;
int ret ;
ret = sdriver - > remove ( sdevice ) ;
return ret ;
}
static void siox_driver_shutdown ( struct device * dev )
{
struct siox_driver * sdriver =
container_of ( dev - > driver , struct siox_driver , driver ) ;
struct siox_device * sdevice = to_siox_device ( dev ) ;
sdriver - > shutdown ( sdevice ) ;
}
static ssize_t active_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
return sprintf ( buf , " %d \n " , smaster - > active ) ;
}
static ssize_t active_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
int ret ;
int active ;
ret = kstrtoint ( buf , 0 , & active ) ;
if ( ret < 0 )
return ret ;
if ( active )
ret = siox_start ( smaster ) ;
else
ret = siox_stop ( smaster ) ;
if ( ret < 0 )
return ret ;
return count ;
}
static DEVICE_ATTR_RW ( active ) ;
static struct siox_device * siox_device_add ( struct siox_master * smaster ,
const char * type , size_t inbytes ,
size_t outbytes , u8 statustype ) ;
static ssize_t device_add_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
int ret ;
char type [ 20 ] = " " ;
size_t inbytes = 0 , outbytes = 0 ;
u8 statustype = 0 ;
2018-02-14 17:25:02 +03:00
ret = sscanf ( buf , " %19s %zu %zu %hhu " , type , & inbytes ,
2017-12-18 19:59:07 +03:00
& outbytes , & statustype ) ;
if ( ret ! = 3 & & ret ! = 4 )
return - EINVAL ;
if ( strcmp ( type , " siox-12x8 " ) | | inbytes ! = 2 | | outbytes ! = 4 )
return - EINVAL ;
siox_device_add ( smaster , " siox-12x8 " , inbytes , outbytes , statustype ) ;
return count ;
}
static DEVICE_ATTR_WO ( device_add ) ;
static void siox_device_remove ( struct siox_master * smaster ) ;
static ssize_t device_remove_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
/* XXX? require to write <type> <inbytes> <outbytes> */
siox_device_remove ( smaster ) ;
return count ;
}
static DEVICE_ATTR_WO ( device_remove ) ;
static ssize_t poll_interval_ns_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
return sprintf ( buf , " %lld \n " , jiffies_to_nsecs ( smaster - > poll_interval ) ) ;
}
static ssize_t poll_interval_ns_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
int ret ;
u64 val ;
ret = kstrtou64 ( buf , 0 , & val ) ;
if ( ret < 0 )
return ret ;
siox_master_lock ( smaster ) ;
smaster - > poll_interval = nsecs_to_jiffies ( val ) ;
siox_master_unlock ( smaster ) ;
return count ;
}
static DEVICE_ATTR_RW ( poll_interval_ns ) ;
static struct attribute * siox_master_attrs [ ] = {
& dev_attr_active . attr ,
& dev_attr_device_add . attr ,
& dev_attr_device_remove . attr ,
& dev_attr_poll_interval_ns . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( siox_master ) ;
static void siox_master_release ( struct device * dev )
{
struct siox_master * smaster = to_siox_master ( dev ) ;
kfree ( smaster ) ;
}
static struct device_type siox_master_type = {
. groups = siox_master_groups ,
. release = siox_master_release ,
} ;
struct siox_master * siox_master_alloc ( struct device * dev ,
size_t size )
{
struct siox_master * smaster ;
if ( ! dev )
return NULL ;
smaster = kzalloc ( sizeof ( * smaster ) + size , GFP_KERNEL ) ;
if ( ! smaster )
return NULL ;
device_initialize ( & smaster - > dev ) ;
smaster - > busno = - 1 ;
smaster - > dev . bus = & siox_bus_type ;
smaster - > dev . type = & siox_master_type ;
smaster - > dev . parent = dev ;
smaster - > poll_interval = DIV_ROUND_UP ( HZ , 40 ) ;
dev_set_drvdata ( & smaster - > dev , & smaster [ 1 ] ) ;
return smaster ;
}
EXPORT_SYMBOL_GPL ( siox_master_alloc ) ;
int siox_master_register ( struct siox_master * smaster )
{
int ret ;
if ( ! siox_is_registered )
return - EPROBE_DEFER ;
if ( ! smaster - > pushpull )
return - EINVAL ;
dev_set_name ( & smaster - > dev , " siox-%d " , smaster - > busno ) ;
2018-06-28 10:57:42 +03:00
mutex_init ( & smaster - > lock ) ;
INIT_LIST_HEAD ( & smaster - > devices ) ;
2017-12-18 19:59:07 +03:00
smaster - > last_poll = jiffies ;
2018-06-28 10:57:42 +03:00
smaster - > poll_thread = kthread_run ( siox_poll_thread , smaster ,
" siox-%d " , smaster - > busno ) ;
2017-12-18 19:59:07 +03:00
if ( IS_ERR ( smaster - > poll_thread ) ) {
smaster - > active = 0 ;
return PTR_ERR ( smaster - > poll_thread ) ;
}
ret = device_add ( & smaster - > dev ) ;
if ( ret )
kthread_stop ( smaster - > poll_thread ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( siox_master_register ) ;
void siox_master_unregister ( struct siox_master * smaster )
{
/* remove device */
device_del ( & smaster - > dev ) ;
siox_master_lock ( smaster ) ;
__siox_stop ( smaster ) ;
while ( smaster - > num_devices ) {
struct siox_device * sdevice ;
sdevice = container_of ( smaster - > devices . prev ,
struct siox_device , node ) ;
list_del ( & sdevice - > node ) ;
smaster - > num_devices - - ;
siox_master_unlock ( smaster ) ;
device_unregister ( & sdevice - > dev ) ;
siox_master_lock ( smaster ) ;
}
siox_master_unlock ( smaster ) ;
put_device ( & smaster - > dev ) ;
}
EXPORT_SYMBOL_GPL ( siox_master_unregister ) ;
static struct siox_device * siox_device_add ( struct siox_master * smaster ,
const char * type , size_t inbytes ,
size_t outbytes , u8 statustype )
{
struct siox_device * sdevice ;
int ret ;
size_t buf_len ;
sdevice = kzalloc ( sizeof ( * sdevice ) , GFP_KERNEL ) ;
if ( ! sdevice )
return ERR_PTR ( - ENOMEM ) ;
sdevice - > type = type ;
sdevice - > inbytes = inbytes ;
sdevice - > outbytes = outbytes ;
sdevice - > statustype = statustype ;
sdevice - > smaster = smaster ;
sdevice - > dev . parent = & smaster - > dev ;
sdevice - > dev . bus = & siox_bus_type ;
sdevice - > dev . type = & siox_device_type ;
siox_master_lock ( smaster ) ;
dev_set_name ( & sdevice - > dev , " siox-%d-%d " ,
smaster - > busno , smaster - > num_devices ) ;
buf_len = smaster - > setbuf_len + inbytes +
smaster - > getbuf_len + outbytes ;
if ( smaster - > buf_len < buf_len ) {
u8 * buf = krealloc ( smaster - > buf , buf_len , GFP_KERNEL ) ;
if ( ! buf ) {
dev_err ( & smaster - > dev ,
" failed to realloc buffer to %zu \n " , buf_len ) ;
ret = - ENOMEM ;
goto err_buf_alloc ;
}
smaster - > buf_len = buf_len ;
smaster - > buf = buf ;
}
ret = device_register ( & sdevice - > dev ) ;
if ( ret ) {
dev_err ( & smaster - > dev , " failed to register device: %d \n " , ret ) ;
goto err_device_register ;
}
smaster - > num_devices + + ;
list_add_tail ( & sdevice - > node , & smaster - > devices ) ;
smaster - > setbuf_len + = sdevice - > inbytes ;
smaster - > getbuf_len + = sdevice - > outbytes ;
sdevice - > status_errors_kn = sysfs_get_dirent ( sdevice - > dev . kobj . sd ,
" status_errors " ) ;
sdevice - > watchdog_kn = sysfs_get_dirent ( sdevice - > dev . kobj . sd ,
" watchdog " ) ;
sdevice - > watchdog_errors_kn = sysfs_get_dirent ( sdevice - > dev . kobj . sd ,
" watchdog_errors " ) ;
sdevice - > connected_kn = sysfs_get_dirent ( sdevice - > dev . kobj . sd ,
" connected " ) ;
siox_master_unlock ( smaster ) ;
return sdevice ;
err_device_register :
/* don't care to make the buffer smaller again */
err_buf_alloc :
siox_master_unlock ( smaster ) ;
kfree ( sdevice ) ;
return ERR_PTR ( ret ) ;
}
static void siox_device_remove ( struct siox_master * smaster )
{
struct siox_device * sdevice ;
siox_master_lock ( smaster ) ;
if ( ! smaster - > num_devices ) {
siox_master_unlock ( smaster ) ;
return ;
}
sdevice = container_of ( smaster - > devices . prev , struct siox_device , node ) ;
list_del ( & sdevice - > node ) ;
smaster - > num_devices - - ;
smaster - > setbuf_len - = sdevice - > inbytes ;
smaster - > getbuf_len - = sdevice - > outbytes ;
if ( ! smaster - > num_devices )
__siox_stop ( smaster ) ;
siox_master_unlock ( smaster ) ;
/*
* This must be done without holding the master lock because we ' re
* called from device_remove_store which also holds a sysfs mutex .
* device_unregister tries to aquire the same lock .
*/
device_unregister ( & sdevice - > dev ) ;
}
int __siox_driver_register ( struct siox_driver * sdriver , struct module * owner )
{
int ret ;
if ( unlikely ( ! siox_is_registered ) )
return - EPROBE_DEFER ;
if ( ! sdriver - > set_data & & ! sdriver - > get_data ) {
pr_err ( " Driver %s doesn't provide needed callbacks \n " ,
sdriver - > driver . name ) ;
return - EINVAL ;
}
sdriver - > driver . owner = owner ;
sdriver - > driver . bus = & siox_bus_type ;
if ( sdriver - > probe )
sdriver - > driver . probe = siox_driver_probe ;
if ( sdriver - > remove )
sdriver - > driver . remove = siox_driver_remove ;
if ( sdriver - > shutdown )
sdriver - > driver . shutdown = siox_driver_shutdown ;
ret = driver_register ( & sdriver - > driver ) ;
if ( ret )
pr_err ( " Failed to register siox driver %s (%d) \n " ,
sdriver - > driver . name , ret ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( __siox_driver_register ) ;
static int __init siox_init ( void )
{
int ret ;
ret = bus_register ( & siox_bus_type ) ;
if ( ret ) {
pr_err ( " Registration of SIOX bus type failed: %d \n " , ret ) ;
return ret ;
}
siox_is_registered = true ;
return 0 ;
}
subsys_initcall ( siox_init ) ;
static void __exit siox_exit ( void )
{
bus_unregister ( & siox_bus_type ) ;
}
module_exit ( siox_exit ) ;
MODULE_AUTHOR ( " Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> " ) ;
MODULE_DESCRIPTION ( " Eckelmann SIOX driver core " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;