2018-02-17 21:36:46 -08:00
// SPDX-License-Identifier: GPL-2.0+
2014-12-03 12:53:00 +00:00
/*
* as3935 . c - Support for AS3935 Franklin lightning sensor
*
2018-02-17 21:36:46 -08:00
* Copyright ( C ) 2014 , 2017 - 2018
* Author : Matt Ranostay < matt . ranostay @ konsulko . com >
2014-12-03 12:53:00 +00:00
*/
# include <linux/module.h>
2020-09-10 18:32:36 +01:00
# include <linux/mod_devicetable.h>
2014-12-03 12:53:00 +00:00
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/workqueue.h>
2022-02-13 13:30:11 +01:00
# include <linux/devm-helpers.h>
2014-12-03 12:53:00 +00:00
# include <linux/mutex.h>
# include <linux/err.h>
# include <linux/irq.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>
# 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-24 22:52:29 -07:00
# define AS3935_NFLWDTH 0x01
# define AS3935_NFLWDTH_MASK 0x7f
2014-12-03 12:53:00 +00:00
# define AS3935_INT 0x03
2017-04-27 00:52:32 -07:00
# define AS3935_INT_MASK 0x0f
2017-05-24 22:52:29 -07:00
# define AS3935_DISTURB_INT BIT(2)
2014-12-03 12:53:00 +00:00
# define AS3935_EVENT_INT BIT(3)
2017-04-27 00:52:32 -07:00
# define AS3935_NOISE_INT BIT(0)
2014-12-03 12:53:00 +00:00
# define AS3935_DATA 0x07
# define AS3935_DATA_MASK 0x3F
# define AS3935_TUNE_CAP 0x08
2017-05-24 22:52:29 -07:00
# define AS3935_DEFAULTS 0x3C
2014-12-03 12:53:00 +00: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-24 22:52:29 -07:00
unsigned long noise_tripped ;
2014-12-03 12:53:00 +00:00
u32 tune_cap ;
2017-05-24 22:52:29 -07:00
u32 nflwdth_reg ;
2021-05-01 18:01:16 +01:00
/* Ensure timestamp is naturally aligned */
struct {
u8 chan ;
s64 timestamp __aligned ( 8 ) ;
} scan ;
2022-05-08 18:57:06 +01:00
u8 buf [ 2 ] __aligned ( IIO_DMA_MINALIGN ) ;
2014-12-03 12:53:00 +00:00
} ;
static const struct iio_chan_spec as3935_channels [ ] = {
{
. type = IIO_PROXIMITY ,
. info_mask_separate =
BIT ( IIO_CHAN_INFO_RAW ) |
2016-05-21 20:01:01 -07:00
BIT ( IIO_CHAN_INFO_PROCESSED ) |
BIT ( IIO_CHAN_INFO_SCALE ) ,
2014-12-03 12:53:00 +00: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 10:44:23 -05:00
}
2014-12-03 12:53:00 +00:00
static int as3935_write ( struct as3935_state * st ,
unsigned int reg ,
unsigned int val )
{
u8 * buf = st - > buf ;
2017-04-13 23:21:56 -07:00
buf [ 0 ] = AS3935_ADDRESS ( reg ) > > 8 ;
2014-12-03 12:53:00 +00:00
buf [ 1 ] = val ;
return spi_write ( st - > spi , buf , 2 ) ;
2014-10-31 10:44:23 -05:00
}
2014-12-03 12:53:00 +00: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 ;
2021-12-16 19:52:09 +01:00
return sysfs_emit ( buf , " %d \n " , val ) ;
2014-10-31 10:44:23 -05:00
}
2014-12-03 12:53:00 +00: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 ;
2021-12-09 17:17:28 +01:00
ret = kstrtoul ( buf , 10 , & val ) ;
2014-12-03 12:53:00 +00:00
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 10:44:23 -05:00
}
2014-12-03 12:53:00 +00:00
2017-05-24 22:52:29 -07: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 ) ;
2021-12-16 19:52:09 +01:00
ret = sysfs_emit ( buf , " %d \n " , ! time_after ( jiffies , st - > noise_tripped + HZ ) ) ;
2017-05-24 22:52:29 -07:00
mutex_unlock ( & st - > lock ) ;
return ret ;
}
2014-12-03 12:53:00 +00:00
static IIO_DEVICE_ATTR ( sensor_sensitivity , S_IRUGO | S_IWUSR ,
as3935_sensor_sensitivity_show , as3935_sensor_sensitivity_store , 0 ) ;
2017-05-24 22:52:29 -07:00
static IIO_DEVICE_ATTR ( noise_level_tripped , S_IRUGO ,
as3935_noise_level_tripped_show , NULL , 0 ) ;
2014-12-03 12:53:00 +00:00
static struct attribute * as3935_attributes [ ] = {
& iio_dev_attr_sensor_sensitivity . dev_attr . attr ,
2017-05-24 22:52:29 -07:00
& iio_dev_attr_noise_level_tripped . dev_attr . attr ,
2014-12-03 12:53:00 +00:00
NULL ,
} ;
2017-04-01 14:09:55 +05:30
static const struct attribute_group as3935_attribute_group = {
2014-12-03 12:53:00 +00: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-21 20:01:01 -07:00
2017-04-26 22:30:00 -07:00
if ( m = = IIO_CHAN_INFO_RAW )
return IIO_VAL_INT ;
2016-05-21 20:01:01 -07:00
if ( m = = IIO_CHAN_INFO_PROCESSED )
* val * = 1000 ;
break ;
case IIO_CHAN_INFO_SCALE :
* val = 1000 ;
2014-12-03 12:53:00 +00: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 ;
2021-05-01 18:01:16 +01:00
st - > scan . chan = val & AS3935_DATA_MASK ;
iio_push_to_buffers_with_timestamp ( indio_dev , & st - > scan ,
2017-05-04 17:32:19 -07:00
iio_get_time_ns ( indio_dev ) ) ;
2014-12-03 12:53:00 +00:00
err_read :
iio_trigger_notify_done ( indio_dev - > trig ) ;
return IRQ_HANDLED ;
2014-10-31 10:44:23 -05:00
}
2014-12-03 12:53:00 +00:00
static void as3935_event_work ( struct work_struct * work )
{
struct as3935_state * st ;
int val ;
2016-05-30 16:52:04 +02:00
int ret ;
2014-12-03 12:53:00 +00:00
st = container_of ( work , struct as3935_state , work . work ) ;
2016-05-30 16:52:04 +02:00
ret = as3935_read ( st , AS3935_INT , & val ) ;
if ( ret ) {
dev_warn ( & st - > spi - > dev , " read error \n " ) ;
return ;
}
2014-12-03 12:53:00 +00:00
val & = AS3935_INT_MASK ;
switch ( val ) {
case AS3935_EVENT_INT :
2017-05-04 17:32:19 -07:00
iio_trigger_poll_chained ( st - > trig ) ;
2014-12-03 12:53:00 +00:00
break ;
2017-05-24 22:52:29 -07:00
case AS3935_DISTURB_INT :
2014-12-03 12:53:00 +00:00
case AS3935_NOISE_INT :
2017-05-24 22:52:29 -07:00
mutex_lock ( & st - > lock ) ;
st - > noise_tripped = jiffies ;
mutex_unlock ( & st - > lock ) ;
2016-05-30 16:52:04 +02:00
dev_warn ( & st - > spi - > dev , " noise level is too high \n " ) ;
2014-12-03 12:53:00 +00:00
break ;
}
2014-10-31 10:44:23 -05:00
}
2014-12-03 12:53:00 +00: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-24 22:52:29 -07:00
as3935_write ( st , AS3935_DEFAULTS , 0x96 ) ;
2014-12-03 12:53:00 +00: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-24 22:52:29 -07:00
as3935_write ( st , AS3935_NFLWDTH , st - > nflwdth_reg ) ;
2014-12-03 12:53:00 +00:00
}
2015-01-11 15:37:04 +01:00
static int as3935_suspend ( struct device * dev )
2014-12-03 12:53:00 +00:00
{
2015-01-11 15:37:04 +01:00
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
2014-12-03 12:53:00 +00: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 15:37:04 +01:00
static int as3935_resume ( struct device * dev )
2014-12-03 12:53:00 +00:00
{
2015-01-11 15:37:04 +01:00
struct iio_dev * indio_dev = dev_get_drvdata ( dev ) ;
2014-12-03 12:53:00 +00: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-14 16:38:19 -07:00
calibrate_as3935 ( st ) ;
2014-12-03 12:53:00 +00:00
err_resume :
mutex_unlock ( & st - > lock ) ;
return ret ;
}
2015-01-11 15:37:04 +01:00
2022-01-30 19:31:30 +00:00
static DEFINE_SIMPLE_DEV_PM_OPS ( as3935_pm_ops , as3935_suspend , as3935_resume ) ;
2014-12-03 12:53:00 +00:00
static int as3935_probe ( struct spi_device * spi )
{
2020-09-10 18:32:35 +01:00
struct device * dev = & spi - > dev ;
2014-12-03 12:53:00 +00:00
struct iio_dev * indio_dev ;
struct iio_trigger * trig ;
struct as3935_state * st ;
int ret ;
/* Be sure lightning event interrupt is specified */
if ( ! spi - > irq ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " unable to get event interrupt \n " ) ;
2014-12-03 12:53:00 +00:00
return - EINVAL ;
}
2020-09-10 18:32:35 +01:00
indio_dev = devm_iio_device_alloc ( dev , sizeof ( * st ) ) ;
2014-12-03 12:53:00 +00:00
if ( ! indio_dev )
return - ENOMEM ;
st = iio_priv ( indio_dev ) ;
st - > spi = spi ;
spi_set_drvdata ( spi , indio_dev ) ;
mutex_init ( & st - > lock ) ;
2020-09-10 18:32:36 +01:00
ret = device_property_read_u32 ( dev ,
2014-12-03 12:53:00 +00:00
" ams,tuning-capacitor-pf " , & st - > tune_cap ) ;
if ( ret ) {
st - > tune_cap = 0 ;
2020-09-10 18:32:35 +01:00
dev_warn ( dev , " no tuning-capacitor-pf set, defaulting to %d " ,
2014-12-03 12:53:00 +00:00
st - > tune_cap ) ;
}
if ( st - > tune_cap > MAX_PF_CAP ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " wrong tuning-capacitor-pf setting of %d \n " ,
2014-12-03 12:53:00 +00:00
st - > tune_cap ) ;
return - EINVAL ;
}
2020-09-10 18:32:36 +01:00
ret = device_property_read_u32 ( dev ,
2017-05-24 22:52:29 -07:00
" ams,nflwdth " , & st - > nflwdth_reg ) ;
if ( ! ret & & st - > nflwdth_reg > AS3935_NFLWDTH_MASK ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " invalid nflwdth setting of %d \n " ,
2017-05-24 22:52:29 -07:00
st - > nflwdth_reg ) ;
return - EINVAL ;
}
2014-12-03 12:53:00 +00:00
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 ;
2020-09-10 18:32:35 +01:00
trig = devm_iio_trigger_alloc ( dev , " %s-dev%d " ,
2021-04-26 18:49:03 +01:00
indio_dev - > name ,
iio_device_id ( indio_dev ) ) ;
2014-12-03 12:53:00 +00:00
if ( ! trig )
return - ENOMEM ;
st - > trig = trig ;
2017-05-24 22:52:29 -07:00
st - > noise_tripped = jiffies - HZ ;
2014-12-03 12:53:00 +00:00
iio_trigger_set_drvdata ( trig , indio_dev ) ;
2020-09-10 18:32:35 +01:00
ret = devm_iio_trigger_register ( dev , trig ) ;
2014-12-03 12:53:00 +00:00
if ( ret ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " failed to register trigger \n " ) ;
2014-12-03 12:53:00 +00:00
return ret ;
}
2020-09-10 18:32:35 +01:00
ret = devm_iio_triggered_buffer_setup ( dev , indio_dev ,
2019-03-08 12:59:35 -05:00
iio_pollfunc_store_time ,
as3935_trigger_handler , NULL ) ;
2014-12-03 12:53:00 +00:00
if ( ret ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " cannot setup iio trigger \n " ) ;
2019-03-08 12:59:35 -05:00
return ret ;
2014-12-03 12:53:00 +00:00
}
calibrate_as3935 ( st ) ;
2022-02-13 13:30:11 +01:00
ret = devm_delayed_work_autocancel ( dev , & st - > work , as3935_event_work ) ;
2019-03-08 12:59:35 -05:00
if ( ret )
return ret ;
2020-09-10 18:32:35 +01:00
ret = devm_request_irq ( dev , spi - > irq ,
2014-12-03 12:53:00 +00:00
& as3935_interrupt_handler ,
IRQF_TRIGGER_RISING ,
2020-09-10 18:32:35 +01:00
dev_name ( dev ) ,
2014-12-03 12:53:00 +00:00
indio_dev ) ;
if ( ret ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " unable to request irq \n " ) ;
2019-03-08 12:59:35 -05:00
return ret ;
2014-12-03 12:53:00 +00:00
}
2020-09-10 18:32:35 +01:00
ret = devm_iio_device_register ( dev , indio_dev ) ;
2014-12-03 12:53:00 +00:00
if ( ret < 0 ) {
2020-09-10 18:32:35 +01:00
dev_err ( dev , " unable to register device \n " ) ;
2019-03-08 12:59:35 -05:00
return ret ;
2014-12-03 12:53:00 +00:00
}
return 0 ;
2014-10-31 10:44:23 -05:00
}
2014-12-03 12:53:00 +00:00
2015-08-20 09:07:27 +02:00
static const struct of_device_id as3935_of_match [ ] = {
{ . compatible = " ams,as3935 " , } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , as3935_of_match ) ;
2014-12-03 12:53:00 +00: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 " ,
2020-09-10 18:32:36 +01:00
. of_match_table = as3935_of_match ,
2022-01-30 19:31:30 +00:00
. pm = pm_sleep_ptr ( & as3935_pm_ops ) ,
2014-12-03 12:53:00 +00:00
} ,
. probe = as3935_probe ,
. id_table = as3935_id ,
} ;
module_spi_driver ( as3935_driver ) ;
2018-02-17 21:36:46 -08:00
MODULE_AUTHOR ( " Matt Ranostay <matt.ranostay@konsulko.com> " ) ;
2014-12-03 12:53:00 +00:00
MODULE_DESCRIPTION ( " AS3935 lightning sensor " ) ;
MODULE_LICENSE ( " GPL " ) ;