2018-02-18 08:36:46 +03:00
// SPDX-License-Identifier: GPL-2.0+
2014-12-03 15:53:00 +03:00
/*
* as3935 . c - Support for AS3935 Franklin lightning sensor
*
2018-02-18 08:36:46 +03:00
* Copyright ( C ) 2014 , 2017 - 2018
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2014-12-03 15:53:00 +03:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/workqueue.h>
# include <linux/mutex.h>
# include <linux/err.h>
# include <linux/irq.h>
# include <linux/gpio.h>
# include <linux/spi/spi.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/trigger.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/buffer.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/of_gpio.h>
# define AS3935_AFE_GAIN 0x00
# define AS3935_AFE_MASK 0x3F
# define AS3935_AFE_GAIN_MAX 0x1F
# define AS3935_AFE_PWR_BIT BIT(0)
2017-05-25 08:52:29 +03:00
# define AS3935_NFLWDTH 0x01
# define AS3935_NFLWDTH_MASK 0x7f
2014-12-03 15:53:00 +03:00
# define AS3935_INT 0x03
2017-04-27 10:52:32 +03:00
# define AS3935_INT_MASK 0x0f
2017-05-25 08:52:29 +03:00
# define AS3935_DISTURB_INT BIT(2)
2014-12-03 15:53:00 +03:00
# define AS3935_EVENT_INT BIT(3)
2017-04-27 10:52:32 +03:00
# define AS3935_NOISE_INT BIT(0)
2014-12-03 15:53:00 +03:00
# define AS3935_DATA 0x07
# define AS3935_DATA_MASK 0x3F
# define AS3935_TUNE_CAP 0x08
2017-05-25 08:52:29 +03:00
# define AS3935_DEFAULTS 0x3C
2014-12-03 15:53:00 +03:00
# define AS3935_CALIBRATE 0x3D
# define AS3935_READ_DATA BIT(14)
# define AS3935_ADDRESS(x) ((x) << 8)
# define MAX_PF_CAP 120
# define TUNE_CAP_DIV 8
struct as3935_state {
struct spi_device * spi ;
struct iio_trigger * trig ;
struct mutex lock ;
struct delayed_work work ;
2017-05-25 08:52:29 +03:00
unsigned long noise_tripped ;
2014-12-03 15:53:00 +03:00
u32 tune_cap ;
2017-05-25 08:52:29 +03:00
u32 nflwdth_reg ;
2016-05-22 06:01:03 +03:00
u8 buffer [ 16 ] ; /* 8-bit data + 56-bit padding + 64-bit timestamp */
2014-12-03 15:53:00 +03:00
u8 buf [ 2 ] ____cacheline_aligned ;
} ;
static const struct iio_chan_spec as3935_channels [ ] = {
{
. type = IIO_PROXIMITY ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) |
2016-05-22 06:01:01 +03:00
BIT ( IIO_CHAN_INFO_PROCESSED ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2014-12-03 15:53:00 +03:00
. scan_index = 0 ,
. scan_type = {
. sign = ' u ' ,
. realbits = 6 ,
. storagebits = 8 ,
} ,
} ,
IIO_CHAN_SOFT_TIMESTAMP ( 1 ) ,
} ;
static int as3935_read ( struct as3935_state * st , unsigned int reg , int * val )
{
u8 cmd ;
int ret ;
cmd = ( AS3935_READ_DATA | AS3935_ADDRESS ( reg ) ) > > 8 ;
ret = spi_w8r8 ( st - > spi , cmd ) ;
if ( ret < 0 )
return ret ;
* val = ret ;
return 0 ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
static int as3935_write ( struct as3935_state * st ,
unsigned int reg ,
unsigned int val )
{
u8 * buf = st - > buf ;
2017-04-14 09:21:56 +03:00
buf [ 0 ] = AS3935_ADDRESS ( reg ) > > 8 ;
2014-12-03 15:53:00 +03:00
buf [ 1 ] = val ;
return spi_write ( st - > spi , buf , 2 ) ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
static ssize_t as3935_sensor_sensitivity_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct as3935_state * st = iio_priv ( dev_to_iio_dev ( dev ) ) ;
int val , ret ;
ret = as3935_read ( st , AS3935_AFE_GAIN , & val ) ;
if ( ret )
return ret ;
val = ( val & AS3935_AFE_MASK ) > > 1 ;
return sprintf ( buf , " %d \n " , val ) ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
static ssize_t as3935_sensor_sensitivity_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
struct as3935_state * st = iio_priv ( dev_to_iio_dev ( dev ) ) ;
unsigned long val ;
int ret ;
ret = kstrtoul ( ( const char * ) buf , 10 , & val ) ;
if ( ret )
return - EINVAL ;
if ( val > AS3935_AFE_GAIN_MAX )
return - EINVAL ;
as3935_write ( st , AS3935_AFE_GAIN , val < < 1 ) ;
return len ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
2017-05-25 08:52:29 +03:00
static ssize_t as3935_noise_level_tripped_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct as3935_state * st = iio_priv ( dev_to_iio_dev ( dev ) ) ;
int ret ;
mutex_lock ( & st - > lock ) ;
ret = sprintf ( buf , " %d \n " , ! time_after ( jiffies , st - > noise_tripped + HZ ) ) ;
mutex_unlock ( & st - > lock ) ;
return ret ;
}
2014-12-03 15:53:00 +03:00
static IIO_DEVICE_ATTR ( sensor_sensitivity , S_IRUGO | S_IWUSR ,
as3935_sensor_sensitivity_show , as3935_sensor_sensitivity_store , 0 ) ;
2017-05-25 08:52:29 +03:00
static IIO_DEVICE_ATTR ( noise_level_tripped , S_IRUGO ,
as3935_noise_level_tripped_show , NULL , 0 ) ;
2014-12-03 15:53:00 +03:00
static struct attribute * as3935_attributes [ ] = {
& iio_dev_attr_sensor_sensitivity . dev_attr . attr ,
2017-05-25 08:52:29 +03:00
& iio_dev_attr_noise_level_tripped . dev_attr . attr ,
2014-12-03 15:53:00 +03:00
NULL ,
} ;
2017-04-01 11:39:55 +03:00
static const struct attribute_group as3935_attribute_group = {
2014-12-03 15:53:00 +03:00
. attrs = as3935_attributes ,
} ;
static int as3935_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int * val ,
int * val2 ,
long m )
{
struct as3935_state * st = iio_priv ( indio_dev ) ;
int ret ;
switch ( m ) {
case IIO_CHAN_INFO_PROCESSED :
case IIO_CHAN_INFO_RAW :
* val2 = 0 ;
ret = as3935_read ( st , AS3935_DATA , val ) ;
if ( ret )
return ret ;
/* storm out of range */
if ( * val = = AS3935_DATA_MASK )
return - EINVAL ;
2016-05-22 06:01:01 +03:00
2017-04-27 08:30:00 +03:00
if ( m = = IIO_CHAN_INFO_RAW )
return IIO_VAL_INT ;
2016-05-22 06:01:01 +03:00
if ( m = = IIO_CHAN_INFO_PROCESSED )
* val * = 1000 ;
break ;
case IIO_CHAN_INFO_SCALE :
* val = 1000 ;
2014-12-03 15:53:00 +03:00
break ;
default :
return - EINVAL ;
}
return IIO_VAL_INT ;
}
static const struct iio_info as3935_info = {
. attrs = & as3935_attribute_group ,
. read_raw = & as3935_read_raw ,
} ;
static irqreturn_t as3935_trigger_handler ( int irq , void * private )
{
struct iio_poll_func * pf = private ;
struct iio_dev * indio_dev = pf - > indio_dev ;
struct as3935_state * st = iio_priv ( indio_dev ) ;
int val , ret ;
ret = as3935_read ( st , AS3935_DATA , & val ) ;
if ( ret )
goto err_read ;
2016-05-22 06:01:03 +03:00
st - > buffer [ 0 ] = val & AS3935_DATA_MASK ;
iio_push_to_buffers_with_timestamp ( indio_dev , & st - > buffer ,
2017-05-05 03:32:19 +03:00
iio_get_time_ns ( indio_dev ) ) ;
2014-12-03 15:53:00 +03:00
err_read :
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
} ;
static void as3935_event_work ( struct work_struct * work )
{
struct as3935_state * st ;
int val ;
2016-05-30 17:52:04 +03:00
int ret ;
2014-12-03 15:53:00 +03:00
st = container_of ( work , struct as3935_state , work . work ) ;
2016-05-30 17:52:04 +03:00
ret = as3935_read ( st , AS3935_INT , & val ) ;
if ( ret ) {
dev_warn ( & st - > spi - > dev , " read error \n " ) ;
return ;
}
2014-12-03 15:53:00 +03:00
val & = AS3935_INT_MASK ;
switch ( val ) {
case AS3935_EVENT_INT :
2017-05-05 03:32:19 +03:00
iio_trigger_poll_chained ( st - > trig ) ;
2014-12-03 15:53:00 +03:00
break ;
2017-05-25 08:52:29 +03:00
case AS3935_DISTURB_INT :
2014-12-03 15:53:00 +03:00
case AS3935_NOISE_INT :
2017-05-25 08:52:29 +03:00
mutex_lock ( & st - > lock ) ;
st - > noise_tripped = jiffies ;
mutex_unlock ( & st - > lock ) ;
2016-05-30 17:52:04 +03:00
dev_warn ( & st - > spi - > dev , " noise level is too high \n " ) ;
2014-12-03 15:53:00 +03:00
break ;
}
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
static irqreturn_t as3935_interrupt_handler ( int irq , void * private )
{
struct iio_dev * indio_dev = private ;
struct as3935_state * st = iio_priv ( indio_dev ) ;
/*
* Delay work for > 2 milliseconds after an interrupt to allow
* estimated distance to recalculated .
*/
schedule_delayed_work ( & st - > work , msecs_to_jiffies ( 3 ) ) ;
return IRQ_HANDLED ;
}
static void calibrate_as3935 ( struct as3935_state * st )
{
2017-05-25 08:52:29 +03:00
as3935_write ( st , AS3935_DEFAULTS , 0x96 ) ;
2014-12-03 15:53:00 +03:00
as3935_write ( st , AS3935_CALIBRATE , 0x96 ) ;
as3935_write ( st , AS3935_TUNE_CAP ,
BIT ( 5 ) | ( st - > tune_cap / TUNE_CAP_DIV ) ) ;
mdelay ( 2 ) ;
as3935_write ( st , AS3935_TUNE_CAP , ( st - > tune_cap / TUNE_CAP_DIV ) ) ;
2017-05-25 08:52:29 +03:00
as3935_write ( st , AS3935_NFLWDTH , st - > nflwdth_reg ) ;
2014-12-03 15:53:00 +03:00
}
# ifdef CONFIG_PM_SLEEP
2015-01-11 17:37:04 +03:00
static int as3935_suspend ( struct device * dev )
2014-12-03 15:53:00 +03:00
{
2015-01-11 17:37:04 +03:00
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
2014-12-03 15:53:00 +03:00
struct as3935_state * st = iio_priv ( indio_dev ) ;
int val , ret ;
mutex_lock ( & st - > lock ) ;
ret = as3935_read ( st , AS3935_AFE_GAIN , & val ) ;
if ( ret )
goto err_suspend ;
val | = AS3935_AFE_PWR_BIT ;
ret = as3935_write ( st , AS3935_AFE_GAIN , val ) ;
err_suspend :
mutex_unlock ( & st - > lock ) ;
return ret ;
}
2015-01-11 17:37:04 +03:00
static int as3935_resume ( struct device * dev )
2014-12-03 15:53:00 +03:00
{
2015-01-11 17:37:04 +03:00
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
2014-12-03 15:53:00 +03:00
struct as3935_state * st = iio_priv ( indio_dev ) ;
int val , ret ;
mutex_lock ( & st - > lock ) ;
ret = as3935_read ( st , AS3935_AFE_GAIN , & val ) ;
if ( ret )
goto err_resume ;
val & = ~ AS3935_AFE_PWR_BIT ;
ret = as3935_write ( st , AS3935_AFE_GAIN , val ) ;
2017-04-15 02:38:19 +03:00
calibrate_as3935 ( st ) ;
2014-12-03 15:53:00 +03:00
err_resume :
mutex_unlock ( & st - > lock ) ;
return ret ;
}
2015-01-11 17:37:04 +03:00
static SIMPLE_DEV_PM_OPS ( as3935_pm_ops , as3935_suspend , as3935_resume ) ;
# define AS3935_PM_OPS (&as3935_pm_ops)
2014-12-03 15:53:00 +03:00
# else
2015-01-11 17:37:04 +03:00
# define AS3935_PM_OPS NULL
2014-12-03 15:53:00 +03:00
# endif
static int as3935_probe ( struct spi_device * spi )
{
struct iio_dev * indio_dev ;
struct iio_trigger * trig ;
struct as3935_state * st ;
struct device_node * np = spi - > dev . of_node ;
int ret ;
/* Be sure lightning event interrupt is specified */
if ( ! spi - > irq ) {
dev_err ( & spi - > dev , " unable to get event interrupt \n " ) ;
return - EINVAL ;
}
2014-10-31 18:44:00 +03:00
indio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * st ) ) ;
2014-12-03 15:53:00 +03:00
if ( ! indio_dev )
return - ENOMEM ;
st = iio_priv ( indio_dev ) ;
st - > spi = spi ;
spi_set_drvdata ( spi , indio_dev ) ;
mutex_init ( & st - > lock ) ;
INIT_DELAYED_WORK ( & st - > work , as3935_event_work ) ;
ret = of_property_read_u32 ( np ,
" ams,tuning-capacitor-pf " , & st - > tune_cap ) ;
if ( ret ) {
st - > tune_cap = 0 ;
dev_warn ( & spi - > dev ,
" no tuning-capacitor-pf set, defaulting to %d " ,
st - > tune_cap ) ;
}
if ( st - > tune_cap > MAX_PF_CAP ) {
dev_err ( & spi - > dev ,
" wrong tuning-capacitor-pf setting of %d \n " ,
st - > tune_cap ) ;
return - EINVAL ;
}
2017-05-25 08:52:29 +03:00
ret = of_property_read_u32 ( np ,
" ams,nflwdth " , & st - > nflwdth_reg ) ;
if ( ! ret & & st - > nflwdth_reg > AS3935_NFLWDTH_MASK ) {
dev_err ( & spi - > dev ,
" invalid nflwdth setting of %d \n " ,
st - > nflwdth_reg ) ;
return - EINVAL ;
}
2014-12-03 15:53:00 +03:00
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > name = spi_get_device_id ( spi ) - > name ;
indio_dev - > channels = as3935_channels ;
indio_dev - > num_channels = ARRAY_SIZE ( as3935_channels ) ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > info = & as3935_info ;
trig = devm_iio_trigger_alloc ( & spi - > dev , " %s-dev%d " ,
indio_dev - > name , indio_dev - > id ) ;
if ( ! trig )
return - ENOMEM ;
st - > trig = trig ;
2017-05-25 08:52:29 +03:00
st - > noise_tripped = jiffies - HZ ;
2014-12-03 15:53:00 +03:00
trig - > dev . parent = indio_dev - > dev . parent ;
iio_trigger_set_drvdata ( trig , indio_dev ) ;
trig - > ops = & iio_interrupt_trigger_ops ;
ret = iio_trigger_register ( trig ) ;
if ( ret ) {
dev_err ( & spi - > dev , " failed to register trigger \n " ) ;
return ret ;
}
2016-07-11 18:26:56 +03:00
ret = iio_triggered_buffer_setup ( indio_dev , iio_pollfunc_store_time ,
2014-12-03 15:53:00 +03:00
& as3935_trigger_handler , NULL ) ;
if ( ret ) {
dev_err ( & spi - > dev , " cannot setup iio trigger \n " ) ;
goto unregister_trigger ;
}
calibrate_as3935 ( st ) ;
ret = devm_request_irq ( & spi - > dev , spi - > irq ,
& as3935_interrupt_handler ,
IRQF_TRIGGER_RISING ,
dev_name ( & spi - > dev ) ,
indio_dev ) ;
if ( ret ) {
dev_err ( & spi - > dev , " unable to request irq \n " ) ;
goto unregister_buffer ;
}
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 ) {
dev_err ( & spi - > dev , " unable to register device \n " ) ;
goto unregister_buffer ;
}
return 0 ;
unregister_buffer :
iio_triggered_buffer_cleanup ( indio_dev ) ;
unregister_trigger :
iio_trigger_unregister ( st - > trig ) ;
return ret ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
static int as3935_remove ( struct spi_device * spi )
{
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
struct as3935_state * st = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
iio_triggered_buffer_cleanup ( indio_dev ) ;
iio_trigger_unregister ( st - > trig ) ;
return 0 ;
2014-10-31 18:44:23 +03:00
}
2014-12-03 15:53:00 +03:00
2015-08-20 10:07:27 +03:00
static const struct of_device_id as3935_of_match [ ] = {
{ . compatible = " ams,as3935 " , } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , as3935_of_match ) ;
2014-12-03 15:53:00 +03:00
static const struct spi_device_id as3935_id [ ] = {
{ " as3935 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( spi , as3935_id ) ;
static struct spi_driver as3935_driver = {
. driver = {
. name = " as3935 " ,
2015-08-20 10:07:27 +03:00
. of_match_table = of_match_ptr ( as3935_of_match ) ,
2015-01-11 17:37:04 +03:00
. pm = AS3935_PM_OPS ,
2014-12-03 15:53:00 +03:00
} ,
. probe = as3935_probe ,
. remove = as3935_remove ,
. id_table = as3935_id ,
} ;
module_spi_driver ( as3935_driver ) ;
2018-02-18 08:36:46 +03:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2014-12-03 15:53:00 +03:00
MODULE_DESCRIPTION ( " AS3935 lightning sensor " ) ;
MODULE_LICENSE ( " GPL " ) ;