162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * STMicroelectronics hts221 sensor driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2016 STMicroelectronics Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Lorenzo Bianconi <lorenzo.bianconi@st.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/irqreturn.h>
1462306a36Sopenharmony_ci#include <linux/property.h>
1562306a36Sopenharmony_ci#include <linux/regmap.h>
1662306a36Sopenharmony_ci#include <linux/bitfield.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/iio/iio.h>
1962306a36Sopenharmony_ci#include <linux/iio/trigger.h>
2062306a36Sopenharmony_ci#include <linux/iio/events.h>
2162306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h>
2262306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h>
2362306a36Sopenharmony_ci#include <linux/iio/buffer.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/platform_data/st_sensors_pdata.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "hts221.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define HTS221_REG_DRDY_HL_ADDR		0x22
3062306a36Sopenharmony_ci#define HTS221_REG_DRDY_HL_MASK		BIT(7)
3162306a36Sopenharmony_ci#define HTS221_REG_DRDY_PP_OD_ADDR	0x22
3262306a36Sopenharmony_ci#define HTS221_REG_DRDY_PP_OD_MASK	BIT(6)
3362306a36Sopenharmony_ci#define HTS221_REG_DRDY_EN_ADDR		0x22
3462306a36Sopenharmony_ci#define HTS221_REG_DRDY_EN_MASK		BIT(2)
3562306a36Sopenharmony_ci#define HTS221_REG_STATUS_ADDR		0x27
3662306a36Sopenharmony_ci#define HTS221_RH_DRDY_MASK		BIT(1)
3762306a36Sopenharmony_ci#define HTS221_TEMP_DRDY_MASK		BIT(0)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int hts221_trig_set_state(struct iio_trigger *trig, bool state)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
4262306a36Sopenharmony_ci	struct hts221_hw *hw = iio_priv(iio_dev);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR,
4562306a36Sopenharmony_ci				  HTS221_REG_DRDY_EN_MASK,
4662306a36Sopenharmony_ci				  FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state));
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const struct iio_trigger_ops hts221_trigger_ops = {
5062306a36Sopenharmony_ci	.set_trigger_state = hts221_trig_set_state,
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct hts221_hw *hw = private;
5662306a36Sopenharmony_ci	int err, status;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status);
5962306a36Sopenharmony_ci	if (err < 0)
6062306a36Sopenharmony_ci		return IRQ_HANDLED;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/*
6362306a36Sopenharmony_ci	 * H_DA bit (humidity data available) is routed to DRDY line.
6462306a36Sopenharmony_ci	 * Humidity sample is computed after temperature one.
6562306a36Sopenharmony_ci	 * Here we can assume data channels are both available if H_DA bit
6662306a36Sopenharmony_ci	 * is set in status register
6762306a36Sopenharmony_ci	 */
6862306a36Sopenharmony_ci	if (!(status & HTS221_RH_DRDY_MASK))
6962306a36Sopenharmony_ci		return IRQ_NONE;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	iio_trigger_poll_nested(hw->trig);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return IRQ_HANDLED;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciint hts221_allocate_trigger(struct iio_dev *iio_dev)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct hts221_hw *hw = iio_priv(iio_dev);
7962306a36Sopenharmony_ci	struct st_sensors_platform_data *pdata = dev_get_platdata(hw->dev);
8062306a36Sopenharmony_ci	bool irq_active_low = false, open_drain = false;
8162306a36Sopenharmony_ci	unsigned long irq_type;
8262306a36Sopenharmony_ci	int err;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	switch (irq_type) {
8762306a36Sopenharmony_ci	case IRQF_TRIGGER_HIGH:
8862306a36Sopenharmony_ci	case IRQF_TRIGGER_RISING:
8962306a36Sopenharmony_ci		break;
9062306a36Sopenharmony_ci	case IRQF_TRIGGER_LOW:
9162306a36Sopenharmony_ci	case IRQF_TRIGGER_FALLING:
9262306a36Sopenharmony_ci		irq_active_low = true;
9362306a36Sopenharmony_ci		break;
9462306a36Sopenharmony_ci	default:
9562306a36Sopenharmony_ci		dev_info(hw->dev,
9662306a36Sopenharmony_ci			 "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
9762306a36Sopenharmony_ci			 irq_type);
9862306a36Sopenharmony_ci		irq_type = IRQF_TRIGGER_RISING;
9962306a36Sopenharmony_ci		break;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR,
10362306a36Sopenharmony_ci				 HTS221_REG_DRDY_HL_MASK,
10462306a36Sopenharmony_ci				 FIELD_PREP(HTS221_REG_DRDY_HL_MASK,
10562306a36Sopenharmony_ci					    irq_active_low));
10662306a36Sopenharmony_ci	if (err < 0)
10762306a36Sopenharmony_ci		return err;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (device_property_read_bool(hw->dev, "drive-open-drain") ||
11062306a36Sopenharmony_ci	    (pdata && pdata->open_drain)) {
11162306a36Sopenharmony_ci		irq_type |= IRQF_SHARED;
11262306a36Sopenharmony_ci		open_drain = true;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR,
11662306a36Sopenharmony_ci				 HTS221_REG_DRDY_PP_OD_MASK,
11762306a36Sopenharmony_ci				 FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK,
11862306a36Sopenharmony_ci					    open_drain));
11962306a36Sopenharmony_ci	if (err < 0)
12062306a36Sopenharmony_ci		return err;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
12362306a36Sopenharmony_ci					hts221_trigger_handler_thread,
12462306a36Sopenharmony_ci					irq_type | IRQF_ONESHOT,
12562306a36Sopenharmony_ci					hw->name, hw);
12662306a36Sopenharmony_ci	if (err) {
12762306a36Sopenharmony_ci		dev_err(hw->dev, "failed to request trigger irq %d\n",
12862306a36Sopenharmony_ci			hw->irq);
12962306a36Sopenharmony_ci		return err;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
13362306a36Sopenharmony_ci					  iio_dev->name);
13462306a36Sopenharmony_ci	if (!hw->trig)
13562306a36Sopenharmony_ci		return -ENOMEM;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	iio_trigger_set_drvdata(hw->trig, iio_dev);
13862306a36Sopenharmony_ci	hw->trig->ops = &hts221_trigger_ops;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	err = devm_iio_trigger_register(hw->dev, hw->trig);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	iio_dev->trig = iio_trigger_get(hw->trig);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return err;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int hts221_buffer_preenable(struct iio_dev *iio_dev)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	return hts221_set_enable(iio_priv(iio_dev), true);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int hts221_buffer_postdisable(struct iio_dev *iio_dev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	return hts221_set_enable(iio_priv(iio_dev), false);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops hts221_buffer_ops = {
15862306a36Sopenharmony_ci	.preenable = hts221_buffer_preenable,
15962306a36Sopenharmony_ci	.postdisable = hts221_buffer_postdisable,
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct iio_poll_func *pf = p;
16562306a36Sopenharmony_ci	struct iio_dev *iio_dev = pf->indio_dev;
16662306a36Sopenharmony_ci	struct hts221_hw *hw = iio_priv(iio_dev);
16762306a36Sopenharmony_ci	struct iio_chan_spec const *ch;
16862306a36Sopenharmony_ci	int err;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* humidity data */
17162306a36Sopenharmony_ci	ch = &iio_dev->channels[HTS221_SENSOR_H];
17262306a36Sopenharmony_ci	err = regmap_bulk_read(hw->regmap, ch->address,
17362306a36Sopenharmony_ci			       &hw->scan.channels[0],
17462306a36Sopenharmony_ci			       sizeof(hw->scan.channels[0]));
17562306a36Sopenharmony_ci	if (err < 0)
17662306a36Sopenharmony_ci		goto out;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* temperature data */
17962306a36Sopenharmony_ci	ch = &iio_dev->channels[HTS221_SENSOR_T];
18062306a36Sopenharmony_ci	err = regmap_bulk_read(hw->regmap, ch->address,
18162306a36Sopenharmony_ci			       &hw->scan.channels[1],
18262306a36Sopenharmony_ci			       sizeof(hw->scan.channels[1]));
18362306a36Sopenharmony_ci	if (err < 0)
18462306a36Sopenharmony_ci		goto out;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan,
18762306a36Sopenharmony_ci					   iio_get_time_ns(iio_dev));
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ciout:
19062306a36Sopenharmony_ci	iio_trigger_notify_done(hw->trig);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return IRQ_HANDLED;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ciint hts221_allocate_buffers(struct iio_dev *iio_dev)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct hts221_hw *hw = iio_priv(iio_dev);
19862306a36Sopenharmony_ci	return devm_iio_triggered_buffer_setup(hw->dev, iio_dev,
19962306a36Sopenharmony_ci					NULL, hts221_buffer_handler_thread,
20062306a36Sopenharmony_ci					&hts221_buffer_ops);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ciMODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
20462306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
20562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
206