2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-10-14 19:34:14 +04:00
/**
* Copyright ( c ) 2011 Jonathan Cameron
*
* Companion module to the iio simple dummy example driver .
* The purpose of this is to generate ' fake ' event interrupts thus
* allowing that driver ' s code to be as close as possible to that of
* a normal driver talking to hardware . The approach used here
* is not intended to be general and just happens to work for this
* particular use case .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/mutex.h>
# include <linux/module.h>
# include <linux/sysfs.h>
# include "iio_dummy_evgen.h"
2012-04-25 18:54:58 +04:00
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
2017-09-28 13:56:41 +03:00
# include <linux/irq_sim.h>
2011-10-14 19:34:14 +04:00
/* Fiddly bit of faking and irq without hardware */
# define IIO_EVENTGEN_NO 10
2015-09-11 16:59:30 +03:00
/**
2014-11-10 15:45:29 +03:00
* @ regs : irq regs we are faking
2011-10-14 19:34:14 +04:00
* @ lock : protect the evgen state
2017-09-28 13:56:41 +03:00
* @ inuse : mask of which irqs are connected
* @ irq_sim : interrupt simulator
* @ base : base of irq range
2011-10-14 19:34:14 +04:00
*/
struct iio_dummy_eventgen {
2014-11-10 15:45:29 +03:00
struct iio_dummy_regs regs [ IIO_EVENTGEN_NO ] ;
2011-10-14 19:34:14 +04:00
struct mutex lock ;
2017-09-28 13:56:41 +03:00
bool inuse [ IIO_EVENTGEN_NO ] ;
struct irq_sim irq_sim ;
int base ;
2011-10-14 19:34:14 +04:00
} ;
/* We can only ever have one instance of this 'device' */
static struct iio_dummy_eventgen * iio_evgen ;
2015-09-11 16:59:30 +03:00
2011-10-14 19:34:14 +04:00
static int iio_dummy_evgen_create ( void )
{
2017-09-28 13:56:41 +03:00
int ret ;
2011-10-14 19:34:14 +04:00
iio_evgen = kzalloc ( sizeof ( * iio_evgen ) , GFP_KERNEL ) ;
2015-03-31 12:51:10 +03:00
if ( ! iio_evgen )
2011-10-14 19:34:14 +04:00
return - ENOMEM ;
2017-09-28 13:56:41 +03:00
ret = irq_sim_init ( & iio_evgen - > irq_sim , IIO_EVENTGEN_NO ) ;
2017-11-27 13:54:49 +03:00
if ( ret < 0 ) {
2011-10-14 19:34:14 +04:00
kfree ( iio_evgen ) ;
return ret ;
}
2017-09-28 13:56:41 +03:00
iio_evgen - > base = irq_sim_irqnum ( & iio_evgen - > irq_sim , 0 ) ;
2011-10-14 19:34:14 +04:00
mutex_init ( & iio_evgen - > lock ) ;
2017-09-28 13:56:41 +03:00
2011-10-14 19:34:14 +04:00
return 0 ;
}
/**
* iio_dummy_evgen_get_irq ( ) - get an evgen provided irq for a device
*
* This function will give a free allocated irq to a client device .
* That irq can then be caused to ' fire ' by using the associated sysfs file .
*/
int iio_dummy_evgen_get_irq ( void )
{
int i , ret = 0 ;
2011-11-22 10:02:21 +04:00
2015-03-31 12:51:10 +03:00
if ( ! iio_evgen )
2011-11-22 10:02:21 +04:00
return - ENODEV ;
2011-10-14 19:34:14 +04:00
mutex_lock ( & iio_evgen - > lock ) ;
2017-09-28 13:56:41 +03:00
for ( i = 0 ; i < IIO_EVENTGEN_NO ; i + + ) {
2012-10-18 18:43:00 +04:00
if ( ! iio_evgen - > inuse [ i ] ) {
2017-09-28 13:56:41 +03:00
ret = irq_sim_irqnum ( & iio_evgen - > irq_sim , i ) ;
2011-10-14 19:34:14 +04:00
iio_evgen - > inuse [ i ] = true ;
break ;
}
2017-09-28 13:56:41 +03:00
}
2011-10-14 19:34:14 +04:00
mutex_unlock ( & iio_evgen - > lock ) ;
if ( i = = IIO_EVENTGEN_NO )
return - ENOMEM ;
2017-09-28 13:56:41 +03:00
2011-10-14 19:34:14 +04:00
return ret ;
}
EXPORT_SYMBOL_GPL ( iio_dummy_evgen_get_irq ) ;
/**
* iio_dummy_evgen_release_irq ( ) - give the irq back .
* @ irq : irq being returned to the pool
*
* Used by client driver instances to give the irqs back when they disconnect
*/
2015-05-30 11:20:16 +03:00
void iio_dummy_evgen_release_irq ( int irq )
2011-10-14 19:34:14 +04:00
{
mutex_lock ( & iio_evgen - > lock ) ;
iio_evgen - > inuse [ irq - iio_evgen - > base ] = false ;
mutex_unlock ( & iio_evgen - > lock ) ;
}
EXPORT_SYMBOL_GPL ( iio_dummy_evgen_release_irq ) ;
2014-11-10 15:45:29 +03:00
struct iio_dummy_regs * iio_dummy_evgen_get_regs ( int irq )
{
return & iio_evgen - > regs [ irq - iio_evgen - > base ] ;
}
EXPORT_SYMBOL_GPL ( iio_dummy_evgen_get_regs ) ;
2011-10-14 19:34:14 +04:00
static void iio_dummy_evgen_free ( void )
{
2017-09-28 13:56:41 +03:00
irq_sim_fini ( & iio_evgen - > irq_sim ) ;
2011-10-14 19:34:14 +04:00
kfree ( iio_evgen ) ;
}
static void iio_evgen_release ( struct device * dev )
{
iio_dummy_evgen_free ( ) ;
}
static ssize_t iio_evgen_poke ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t len )
{
struct iio_dev_attr * this_attr = to_iio_dev_attr ( attr ) ;
2014-11-10 15:45:29 +03:00
unsigned long event ;
int ret ;
ret = kstrtoul ( buf , 10 , & event ) ;
if ( ret )
return ret ;
iio_evgen - > regs [ this_attr - > address ] . reg_id = this_attr - > address ;
iio_evgen - > regs [ this_attr - > address ] . reg_data = event ;
2011-10-14 19:34:14 +04:00
2017-09-28 13:56:41 +03:00
irq_sim_fire ( & iio_evgen - > irq_sim , this_attr - > address ) ;
2011-10-14 19:34:14 +04:00
return len ;
}
static IIO_DEVICE_ATTR ( poke_ev0 , S_IWUSR , NULL , & iio_evgen_poke , 0 ) ;
static IIO_DEVICE_ATTR ( poke_ev1 , S_IWUSR , NULL , & iio_evgen_poke , 1 ) ;
static IIO_DEVICE_ATTR ( poke_ev2 , S_IWUSR , NULL , & iio_evgen_poke , 2 ) ;
static IIO_DEVICE_ATTR ( poke_ev3 , S_IWUSR , NULL , & iio_evgen_poke , 3 ) ;
static IIO_DEVICE_ATTR ( poke_ev4 , S_IWUSR , NULL , & iio_evgen_poke , 4 ) ;
static IIO_DEVICE_ATTR ( poke_ev5 , S_IWUSR , NULL , & iio_evgen_poke , 5 ) ;
static IIO_DEVICE_ATTR ( poke_ev6 , S_IWUSR , NULL , & iio_evgen_poke , 6 ) ;
static IIO_DEVICE_ATTR ( poke_ev7 , S_IWUSR , NULL , & iio_evgen_poke , 7 ) ;
static IIO_DEVICE_ATTR ( poke_ev8 , S_IWUSR , NULL , & iio_evgen_poke , 8 ) ;
static IIO_DEVICE_ATTR ( poke_ev9 , S_IWUSR , NULL , & iio_evgen_poke , 9 ) ;
static struct attribute * iio_evgen_attrs [ ] = {
& iio_dev_attr_poke_ev0 . dev_attr . attr ,
& iio_dev_attr_poke_ev1 . dev_attr . attr ,
& iio_dev_attr_poke_ev2 . dev_attr . attr ,
& iio_dev_attr_poke_ev3 . dev_attr . attr ,
& iio_dev_attr_poke_ev4 . dev_attr . attr ,
& iio_dev_attr_poke_ev5 . dev_attr . attr ,
& iio_dev_attr_poke_ev6 . dev_attr . attr ,
& iio_dev_attr_poke_ev7 . dev_attr . attr ,
& iio_dev_attr_poke_ev8 . dev_attr . attr ,
& iio_dev_attr_poke_ev9 . dev_attr . attr ,
NULL ,
} ;
static const struct attribute_group iio_evgen_group = {
. attrs = iio_evgen_attrs ,
} ;
static const struct attribute_group * iio_evgen_groups [ ] = {
& iio_evgen_group ,
NULL
} ;
static struct device iio_evgen_dev = {
. bus = & iio_bus_type ,
. groups = iio_evgen_groups ,
. release = & iio_evgen_release ,
} ;
2015-07-10 17:10:21 +03:00
2011-10-14 19:34:14 +04:00
static __init int iio_dummy_evgen_init ( void )
{
int ret = iio_dummy_evgen_create ( ) ;
2014-09-19 01:55:06 +04:00
2011-10-14 19:34:14 +04:00
if ( ret < 0 )
return ret ;
device_initialize ( & iio_evgen_dev ) ;
dev_set_name ( & iio_evgen_dev , " iio_evgen " ) ;
2019-04-18 06:02:56 +03:00
ret = device_add ( & iio_evgen_dev ) ;
if ( ret )
put_device ( & iio_evgen_dev ) ;
return ret ;
2011-10-14 19:34:14 +04:00
}
module_init ( iio_dummy_evgen_init ) ;
static __exit void iio_dummy_evgen_exit ( void )
{
device_unregister ( & iio_evgen_dev ) ;
}
module_exit ( iio_dummy_evgen_exit ) ;
2012-09-03 00:34:59 +04:00
MODULE_AUTHOR ( " Jonathan Cameron <jic23@kernel.org> " ) ;
2011-10-14 19:34:14 +04:00
MODULE_DESCRIPTION ( " IIO dummy driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;