forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
5.2 KiB
202 lines
5.2 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* STMicroelectronics hts221 sensor driver |
|
* |
|
* Copyright 2016 STMicroelectronics Inc. |
|
* |
|
* Lorenzo Bianconi <[email protected]> |
|
*/ |
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/device.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/irqreturn.h> |
|
#include <linux/regmap.h> |
|
#include <linux/bitfield.h> |
|
|
|
#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> |
|
|
|
#include <linux/platform_data/st_sensors_pdata.h> |
|
|
|
#include "hts221.h" |
|
|
|
#define HTS221_REG_DRDY_HL_ADDR 0x22 |
|
#define HTS221_REG_DRDY_HL_MASK BIT(7) |
|
#define HTS221_REG_DRDY_PP_OD_ADDR 0x22 |
|
#define HTS221_REG_DRDY_PP_OD_MASK BIT(6) |
|
#define HTS221_REG_DRDY_EN_ADDR 0x22 |
|
#define HTS221_REG_DRDY_EN_MASK BIT(2) |
|
#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); |
|
|
|
return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR, |
|
HTS221_REG_DRDY_EN_MASK, |
|
FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state)); |
|
} |
|
|
|
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) |
|
{ |
|
struct hts221_hw *hw = private; |
|
int err, status; |
|
|
|
err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status); |
|
if (err < 0) |
|
return IRQ_HANDLED; |
|
|
|
/* |
|
* 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 iio_dev *iio_dev) |
|
{ |
|
struct hts221_hw *hw = iio_priv(iio_dev); |
|
struct st_sensors_platform_data *pdata = dev_get_platdata(hw->dev); |
|
bool irq_active_low = false, open_drain = false; |
|
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; |
|
case IRQF_TRIGGER_LOW: |
|
case IRQF_TRIGGER_FALLING: |
|
irq_active_low = true; |
|
break; |
|
default: |
|
dev_info(hw->dev, |
|
"mode %lx unsupported, using IRQF_TRIGGER_RISING\n", |
|
irq_type); |
|
irq_type = IRQF_TRIGGER_RISING; |
|
break; |
|
} |
|
|
|
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)); |
|
if (err < 0) |
|
return err; |
|
|
|
if (device_property_read_bool(hw->dev, "drive-open-drain") || |
|
(pdata && pdata->open_drain)) { |
|
irq_type |= IRQF_SHARED; |
|
open_drain = true; |
|
} |
|
|
|
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)); |
|
if (err < 0) |
|
return err; |
|
|
|
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) |
|
{ |
|
return hts221_set_enable(iio_priv(iio_dev), true); |
|
} |
|
|
|
static int hts221_buffer_postdisable(struct iio_dev *iio_dev) |
|
{ |
|
return hts221_set_enable(iio_priv(iio_dev), false); |
|
} |
|
|
|
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]; |
|
err = regmap_bulk_read(hw->regmap, ch->address, |
|
&hw->scan.channels[0], |
|
sizeof(hw->scan.channels[0])); |
|
if (err < 0) |
|
goto out; |
|
|
|
/* temperature data */ |
|
ch = &iio_dev->channels[HTS221_SENSOR_T]; |
|
err = regmap_bulk_read(hw->regmap, ch->address, |
|
&hw->scan.channels[1], |
|
sizeof(hw->scan.channels[1])); |
|
if (err < 0) |
|
goto out; |
|
|
|
iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan, |
|
iio_get_time_ns(iio_dev)); |
|
|
|
out: |
|
iio_trigger_notify_done(hw->trig); |
|
|
|
return IRQ_HANDLED; |
|
} |
|
|
|
int hts221_allocate_buffers(struct iio_dev *iio_dev) |
|
{ |
|
struct hts221_hw *hw = iio_priv(iio_dev); |
|
return devm_iio_triggered_buffer_setup(hw->dev, iio_dev, |
|
NULL, hts221_buffer_handler_thread, |
|
&hts221_buffer_ops); |
|
} |
|
|
|
MODULE_AUTHOR("Lorenzo Bianconi <[email protected]>"); |
|
MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver"); |
|
MODULE_LICENSE("GPL v2");
|
|
|