2016-10-13 22:06:04 +02:00
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc .
*
* Lorenzo Bianconi < lorenzo . bianconi @ st . com >
*
* Licensed under the GPL - 2.
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/irqreturn.h>
2018-01-08 23:12:30 +01:00
# include <linux/regmap.h>
# include <linux/bitfield.h>
2016-10-13 22:06:04 +02: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 19:39:03 +02:00
# include <linux/platform_data/st_sensors_pdata.h>
2016-10-13 22:06:04 +02:00
# include "hts221.h"
2017-07-17 19:39:01 +02:00
# define HTS221_REG_DRDY_HL_ADDR 0x22
# define HTS221_REG_DRDY_HL_MASK BIT(7)
2017-07-17 19:39:03 +02:00
# define HTS221_REG_DRDY_PP_OD_ADDR 0x22
# define HTS221_REG_DRDY_PP_OD_MASK BIT(6)
2017-07-17 19:39:05 +02:00
# define HTS221_REG_DRDY_EN_ADDR 0x22
# define HTS221_REG_DRDY_EN_MASK BIT(2)
2016-10-13 22:06:04 +02: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 19:39:05 +02:00
2018-01-08 23:12:30 +01: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 22:06:04 +02: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 19:44:15 +05:30
struct hts221_hw * hw = private ;
2018-01-08 23:12:30 +01:00
int err , status ;
2016-10-13 22:06:04 +02:00
2018-01-08 23:12:30 +01:00
err = regmap_read ( hw - > regmap , HTS221_REG_STATUS_ADDR , & status ) ;
2016-10-13 22:06:04 +02:00
if ( err < 0 )
return IRQ_HANDLED ;
2018-01-08 23:12:29 +01:00
/*
2016-10-13 22:06:04 +02: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 ;
iio_trigger_poll_chained ( hw - > trig ) ;
return IRQ_HANDLED ;
}
int hts221_allocate_trigger ( struct hts221_hw * hw )
{
struct iio_dev * iio_dev = iio_priv_to_dev ( hw ) ;
2017-07-17 19:39:03 +02:00
bool irq_active_low = false , open_drain = false ;
struct device_node * np = hw - > dev - > of_node ;
struct st_sensors_platform_data * pdata ;
2016-10-13 22:06:04 +02: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 19:39:01 +02:00
case IRQF_TRIGGER_LOW :
case IRQF_TRIGGER_FALLING :
irq_active_low = true ;
break ;
2016-10-13 22:06:04 +02:00
default :
dev_info ( hw - > dev ,
" mode %lx unsupported, using IRQF_TRIGGER_RISING \n " ,
irq_type ) ;
irq_type = IRQF_TRIGGER_RISING ;
break ;
}
2018-01-08 23:12:30 +01: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 19:39:01 +02:00
if ( err < 0 )
return err ;
2017-07-17 19:39:03 +02:00
pdata = ( struct st_sensors_platform_data * ) hw - > dev - > platform_data ;
if ( ( np & & of_property_read_bool ( np , " drive-open-drain " ) ) | |
( pdata & & pdata - > open_drain ) ) {
irq_type | = IRQF_SHARED ;
open_drain = true ;
}
2018-01-08 23:12:30 +01: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 19:39:03 +02:00
if ( err < 0 )
return err ;
2016-10-13 22:06:04 +02: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 ;
hw - > trig - > dev . parent = hw - > dev ;
iio_dev - > trig = iio_trigger_get ( hw - > trig ) ;
return devm_iio_trigger_register ( hw - > dev , hw - > trig ) ;
}
static int hts221_buffer_preenable ( struct iio_dev * iio_dev )
{
2017-07-17 19:39:00 +02:00
return hts221_set_enable ( iio_priv ( iio_dev ) , true ) ;
2016-10-13 22:06:04 +02:00
}
static int hts221_buffer_postdisable ( struct iio_dev * iio_dev )
{
2017-07-17 19:39:00 +02:00
return hts221_set_enable ( iio_priv ( iio_dev ) , false ) ;
2016-10-13 22:06:04 +02:00
}
static const struct iio_buffer_setup_ops hts221_buffer_ops = {
. preenable = hts221_buffer_preenable ,
. postenable = iio_triggered_buffer_postenable ,
. predisable = iio_triggered_buffer_predisable ,
. postdisable = hts221_buffer_postdisable ,
} ;
static irqreturn_t hts221_buffer_handler_thread ( int irq , void * p )
{
u8 buffer [ ALIGN ( 2 * HTS221_DATA_SIZE , sizeof ( s64 ) ) + sizeof ( s64 ) ] ;
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-08 23:12:30 +01:00
err = regmap_bulk_read ( hw - > regmap , ch - > address ,
buffer , HTS221_DATA_SIZE ) ;
2016-10-13 22:06:04 +02:00
if ( err < 0 )
goto out ;
/* temperature data */
ch = & iio_dev - > channels [ HTS221_SENSOR_T ] ;
2018-01-08 23:12:30 +01:00
err = regmap_bulk_read ( hw - > regmap , ch - > address ,
buffer + HTS221_DATA_SIZE , HTS221_DATA_SIZE ) ;
2016-10-13 22:06:04 +02:00
if ( err < 0 )
goto out ;
iio_push_to_buffers_with_timestamp ( iio_dev , buffer ,
iio_get_time_ns ( iio_dev ) ) ;
out :
iio_trigger_notify_done ( hw - > trig ) ;
return IRQ_HANDLED ;
}
int hts221_allocate_buffers ( struct hts221_hw * hw )
{
return devm_iio_triggered_buffer_setup ( hw - > dev , iio_priv_to_dev ( hw ) ,
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 " ) ;