2019-05-28 19:57:06 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-10-13 23:06:04 +03:00
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc .
*
* Lorenzo Bianconi < lorenzo . bianconi @ st . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/irqreturn.h>
2022-06-10 11:45:26 +03:00
# include <linux/property.h>
2018-01-09 01:12:30 +03:00
# include <linux/regmap.h>
# include <linux/bitfield.h>
2016-10-13 23:06:04 +03:00
# include <linux/iio/iio.h>
# include <linux/iio/trigger.h>
# include <linux/iio/events.h>
# include <linux/iio/trigger_consumer.h>
# include <linux/iio/triggered_buffer.h>
# include <linux/iio/buffer.h>
2017-07-17 20:39:03 +03:00
# include <linux/platform_data/st_sensors_pdata.h>
2016-10-13 23:06:04 +03:00
# include "hts221.h"
2017-07-17 20:39:01 +03:00
# define HTS221_REG_DRDY_HL_ADDR 0x22
# define HTS221_REG_DRDY_HL_MASK BIT(7)
2017-07-17 20:39:03 +03:00
# define HTS221_REG_DRDY_PP_OD_ADDR 0x22
# define HTS221_REG_DRDY_PP_OD_MASK BIT(6)
2017-07-17 20:39:05 +03:00
# define HTS221_REG_DRDY_EN_ADDR 0x22
# define HTS221_REG_DRDY_EN_MASK BIT(2)
2016-10-13 23:06:04 +03:00
# define HTS221_REG_STATUS_ADDR 0x27
# define HTS221_RH_DRDY_MASK BIT(1)
# define HTS221_TEMP_DRDY_MASK BIT(0)
static int hts221_trig_set_state ( struct iio_trigger * trig , bool state )
{
struct iio_dev * iio_dev = iio_trigger_get_drvdata ( trig ) ;
struct hts221_hw * hw = iio_priv ( iio_dev ) ;
2017-07-17 20:39:05 +03:00
2018-01-09 01:12:30 +03:00
return regmap_update_bits ( hw - > regmap , HTS221_REG_DRDY_EN_ADDR ,
HTS221_REG_DRDY_EN_MASK ,
FIELD_PREP ( HTS221_REG_DRDY_EN_MASK , state ) ) ;
2016-10-13 23:06:04 +03:00
}
static const struct iio_trigger_ops hts221_trigger_ops = {
. set_trigger_state = hts221_trig_set_state ,
} ;
static irqreturn_t hts221_trigger_handler_thread ( int irq , void * private )
{
2017-04-01 17:14:15 +03:00
struct hts221_hw * hw = private ;
2018-01-09 01:12:30 +03:00
int err , status ;
2016-10-13 23:06:04 +03:00
2018-01-09 01:12:30 +03:00
err = regmap_read ( hw - > regmap , HTS221_REG_STATUS_ADDR , & status ) ;
2016-10-13 23:06:04 +03:00
if ( err < 0 )
return IRQ_HANDLED ;
2018-01-09 01:12:29 +03:00
/*
2016-10-13 23:06:04 +03:00
* H_DA bit ( humidity data available ) is routed to DRDY line .
* Humidity sample is computed after temperature one .
* Here we can assume data channels are both available if H_DA bit
* is set in status register
*/
if ( ! ( status & HTS221_RH_DRDY_MASK ) )
return IRQ_NONE ;
2023-03-02 16:04:36 +03:00
iio_trigger_poll_nested ( hw - > trig ) ;
2016-10-13 23:06:04 +03:00
return IRQ_HANDLED ;
}
2020-05-22 09:56:16 +03:00
int hts221_allocate_trigger ( struct iio_dev * iio_dev )
2016-10-13 23:06:04 +03:00
{
2020-05-22 09:56:16 +03:00
struct hts221_hw * hw = iio_priv ( iio_dev ) ;
2020-03-13 13:49:53 +03:00
struct st_sensors_platform_data * pdata = dev_get_platdata ( hw - > dev ) ;
2017-07-17 20:39:03 +03:00
bool irq_active_low = false , open_drain = false ;
2016-10-13 23:06:04 +03:00
unsigned long irq_type ;
int err ;
irq_type = irqd_get_trigger_type ( irq_get_irq_data ( hw - > irq ) ) ;
switch ( irq_type ) {
case IRQF_TRIGGER_HIGH :
case IRQF_TRIGGER_RISING :
break ;
2017-07-17 20:39:01 +03:00
case IRQF_TRIGGER_LOW :
case IRQF_TRIGGER_FALLING :
irq_active_low = true ;
break ;
2016-10-13 23:06:04 +03:00
default :
dev_info ( hw - > dev ,
" mode %lx unsupported, using IRQF_TRIGGER_RISING \n " ,
irq_type ) ;
irq_type = IRQF_TRIGGER_RISING ;
break ;
}
2018-01-09 01:12:30 +03:00
err = regmap_update_bits ( hw - > regmap , HTS221_REG_DRDY_HL_ADDR ,
HTS221_REG_DRDY_HL_MASK ,
FIELD_PREP ( HTS221_REG_DRDY_HL_MASK ,
irq_active_low ) ) ;
2017-07-17 20:39:01 +03:00
if ( err < 0 )
return err ;
2017-07-17 20:39:03 +03:00
2020-03-13 13:49:54 +03:00
if ( device_property_read_bool ( hw - > dev , " drive-open-drain " ) | |
2017-07-17 20:39:03 +03:00
( pdata & & pdata - > open_drain ) ) {
irq_type | = IRQF_SHARED ;
open_drain = true ;
}
2018-01-09 01:12:30 +03:00
err = regmap_update_bits ( hw - > regmap , HTS221_REG_DRDY_PP_OD_ADDR ,
HTS221_REG_DRDY_PP_OD_MASK ,
FIELD_PREP ( HTS221_REG_DRDY_PP_OD_MASK ,
open_drain ) ) ;
2017-07-17 20:39:03 +03:00
if ( err < 0 )
return err ;
2016-10-13 23:06:04 +03:00
err = devm_request_threaded_irq ( hw - > dev , hw - > irq , NULL ,
hts221_trigger_handler_thread ,
irq_type | IRQF_ONESHOT ,
hw - > name , hw ) ;
if ( err ) {
dev_err ( hw - > dev , " failed to request trigger irq %d \n " ,
hw - > irq ) ;
return err ;
}
hw - > trig = devm_iio_trigger_alloc ( hw - > dev , " %s-trigger " ,
iio_dev - > name ) ;
if ( ! hw - > trig )
return - ENOMEM ;
iio_trigger_set_drvdata ( hw - > trig , iio_dev ) ;
hw - > trig - > ops = & hts221_trigger_ops ;
2022-05-24 21:14:46 +03:00
err = devm_iio_trigger_register ( hw - > dev , hw - > trig ) ;
2016-10-13 23:06:04 +03:00
iio_dev - > trig = iio_trigger_get ( hw - > trig ) ;
2022-05-24 21:14:46 +03:00
return err ;
2016-10-13 23:06:04 +03:00
}
static int hts221_buffer_preenable ( struct iio_dev * iio_dev )
{
2017-07-17 20:39:00 +03:00
return hts221_set_enable ( iio_priv ( iio_dev ) , true ) ;
2016-10-13 23:06:04 +03:00
}
static int hts221_buffer_postdisable ( struct iio_dev * iio_dev )
{
2017-07-17 20:39:00 +03:00
return hts221_set_enable ( iio_priv ( iio_dev ) , false ) ;
2016-10-13 23:06:04 +03:00
}
static const struct iio_buffer_setup_ops hts221_buffer_ops = {
. preenable = hts221_buffer_preenable ,
. postdisable = hts221_buffer_postdisable ,
} ;
static irqreturn_t hts221_buffer_handler_thread ( int irq , void * p )
{
struct iio_poll_func * pf = p ;
struct iio_dev * iio_dev = pf - > indio_dev ;
struct hts221_hw * hw = iio_priv ( iio_dev ) ;
struct iio_chan_spec const * ch ;
int err ;
/* humidity data */
ch = & iio_dev - > channels [ HTS221_SENSOR_H ] ;
2018-01-09 01:12:30 +03:00
err = regmap_bulk_read ( hw - > regmap , ch - > address ,
2020-06-07 18:53:53 +03:00
& hw - > scan . channels [ 0 ] ,
sizeof ( hw - > scan . channels [ 0 ] ) ) ;
2016-10-13 23:06:04 +03:00
if ( err < 0 )
goto out ;
/* temperature data */
ch = & iio_dev - > channels [ HTS221_SENSOR_T ] ;
2018-01-09 01:12:30 +03:00
err = regmap_bulk_read ( hw - > regmap , ch - > address ,
2020-06-07 18:53:53 +03:00
& hw - > scan . channels [ 1 ] ,
sizeof ( hw - > scan . channels [ 1 ] ) ) ;
2016-10-13 23:06:04 +03:00
if ( err < 0 )
goto out ;
2020-06-07 18:53:53 +03:00
iio_push_to_buffers_with_timestamp ( iio_dev , & hw - > scan ,
2016-10-13 23:06:04 +03:00
iio_get_time_ns ( iio_dev ) ) ;
out :
iio_trigger_notify_done ( hw - > trig ) ;
return IRQ_HANDLED ;
}
2020-05-22 09:56:16 +03:00
int hts221_allocate_buffers ( struct iio_dev * iio_dev )
2016-10-13 23:06:04 +03:00
{
2020-05-22 09:56:16 +03:00
struct hts221_hw * hw = iio_priv ( iio_dev ) ;
return devm_iio_triggered_buffer_setup ( hw - > dev , iio_dev ,
2016-10-13 23:06:04 +03:00
NULL , hts221_buffer_handler_thread ,
& hts221_buffer_ops ) ;
}
MODULE_AUTHOR ( " Lorenzo Bianconi <lorenzo.bianconi@st.com> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics hts221 buffer driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;