xref: /linux/drivers/iio/humidity/hts221_buffer.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e4a70e3eSLorenzo Bianconi /*
3e4a70e3eSLorenzo Bianconi  * STMicroelectronics hts221 sensor driver
4e4a70e3eSLorenzo Bianconi  *
5e4a70e3eSLorenzo Bianconi  * Copyright 2016 STMicroelectronics Inc.
6e4a70e3eSLorenzo Bianconi  *
7e4a70e3eSLorenzo Bianconi  * Lorenzo Bianconi <lorenzo.bianconi@st.com>
8e4a70e3eSLorenzo Bianconi  */
9e4a70e3eSLorenzo Bianconi #include <linux/kernel.h>
10e4a70e3eSLorenzo Bianconi #include <linux/module.h>
11e4a70e3eSLorenzo Bianconi #include <linux/device.h>
12e4a70e3eSLorenzo Bianconi #include <linux/interrupt.h>
13e4a70e3eSLorenzo Bianconi #include <linux/irqreturn.h>
1493018249SNuno Sá #include <linux/property.h>
1562177922SLorenzo Bianconi #include <linux/regmap.h>
1662177922SLorenzo Bianconi #include <linux/bitfield.h>
17e4a70e3eSLorenzo Bianconi 
18e4a70e3eSLorenzo Bianconi #include <linux/iio/iio.h>
19e4a70e3eSLorenzo Bianconi #include <linux/iio/trigger.h>
20e4a70e3eSLorenzo Bianconi #include <linux/iio/events.h>
21e4a70e3eSLorenzo Bianconi #include <linux/iio/trigger_consumer.h>
22e4a70e3eSLorenzo Bianconi #include <linux/iio/triggered_buffer.h>
23e4a70e3eSLorenzo Bianconi #include <linux/iio/buffer.h>
24e4a70e3eSLorenzo Bianconi 
259251f7aaSLorenzo Bianconi #include <linux/platform_data/st_sensors_pdata.h>
269251f7aaSLorenzo Bianconi 
27e4a70e3eSLorenzo Bianconi #include "hts221.h"
28e4a70e3eSLorenzo Bianconi 
29e7b4b45eSLorenzo Bianconi #define HTS221_REG_DRDY_HL_ADDR		0x22
30e7b4b45eSLorenzo Bianconi #define HTS221_REG_DRDY_HL_MASK		BIT(7)
319251f7aaSLorenzo Bianconi #define HTS221_REG_DRDY_PP_OD_ADDR	0x22
329251f7aaSLorenzo Bianconi #define HTS221_REG_DRDY_PP_OD_MASK	BIT(6)
33f3f0ae16SLorenzo Bianconi #define HTS221_REG_DRDY_EN_ADDR		0x22
34f3f0ae16SLorenzo Bianconi #define HTS221_REG_DRDY_EN_MASK		BIT(2)
35e4a70e3eSLorenzo Bianconi #define HTS221_REG_STATUS_ADDR		0x27
36e4a70e3eSLorenzo Bianconi #define HTS221_RH_DRDY_MASK		BIT(1)
37e4a70e3eSLorenzo Bianconi #define HTS221_TEMP_DRDY_MASK		BIT(0)
38e4a70e3eSLorenzo Bianconi 
hts221_trig_set_state(struct iio_trigger * trig,bool state)39e4a70e3eSLorenzo Bianconi static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
40e4a70e3eSLorenzo Bianconi {
41e4a70e3eSLorenzo Bianconi 	struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
42e4a70e3eSLorenzo Bianconi 	struct hts221_hw *hw = iio_priv(iio_dev);
43e4a70e3eSLorenzo Bianconi 
4462177922SLorenzo Bianconi 	return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR,
4562177922SLorenzo Bianconi 				  HTS221_REG_DRDY_EN_MASK,
4662177922SLorenzo Bianconi 				  FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state));
47e4a70e3eSLorenzo Bianconi }
48e4a70e3eSLorenzo Bianconi 
49e4a70e3eSLorenzo Bianconi static const struct iio_trigger_ops hts221_trigger_ops = {
50e4a70e3eSLorenzo Bianconi 	.set_trigger_state = hts221_trig_set_state,
51e4a70e3eSLorenzo Bianconi };
52e4a70e3eSLorenzo Bianconi 
hts221_trigger_handler_thread(int irq,void * private)53e4a70e3eSLorenzo Bianconi static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
54e4a70e3eSLorenzo Bianconi {
557ae6df68Ssimran singhal 	struct hts221_hw *hw = private;
5662177922SLorenzo Bianconi 	int err, status;
57e4a70e3eSLorenzo Bianconi 
5862177922SLorenzo Bianconi 	err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status);
59e4a70e3eSLorenzo Bianconi 	if (err < 0)
60e4a70e3eSLorenzo Bianconi 		return IRQ_HANDLED;
61e4a70e3eSLorenzo Bianconi 
62e4a70e3eSLorenzo Bianconi 	/*
63e4a70e3eSLorenzo Bianconi 	 * H_DA bit (humidity data available) is routed to DRDY line.
64e4a70e3eSLorenzo Bianconi 	 * Humidity sample is computed after temperature one.
65e4a70e3eSLorenzo Bianconi 	 * Here we can assume data channels are both available if H_DA bit
66e4a70e3eSLorenzo Bianconi 	 * is set in status register
67e4a70e3eSLorenzo Bianconi 	 */
68e4a70e3eSLorenzo Bianconi 	if (!(status & HTS221_RH_DRDY_MASK))
69e4a70e3eSLorenzo Bianconi 		return IRQ_NONE;
70e4a70e3eSLorenzo Bianconi 
71*f700e55eSMehdi Djait 	iio_trigger_poll_nested(hw->trig);
72e4a70e3eSLorenzo Bianconi 
73e4a70e3eSLorenzo Bianconi 	return IRQ_HANDLED;
74e4a70e3eSLorenzo Bianconi }
75e4a70e3eSLorenzo Bianconi 
hts221_allocate_trigger(struct iio_dev * iio_dev)767d17577dSAlexandru Ardelean int hts221_allocate_trigger(struct iio_dev *iio_dev)
77e4a70e3eSLorenzo Bianconi {
787d17577dSAlexandru Ardelean 	struct hts221_hw *hw = iio_priv(iio_dev);
794d8d2f09SAndy Shevchenko 	struct st_sensors_platform_data *pdata = dev_get_platdata(hw->dev);
809251f7aaSLorenzo Bianconi 	bool irq_active_low = false, open_drain = false;
81e4a70e3eSLorenzo Bianconi 	unsigned long irq_type;
82e4a70e3eSLorenzo Bianconi 	int err;
83e4a70e3eSLorenzo Bianconi 
84e4a70e3eSLorenzo Bianconi 	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
85e4a70e3eSLorenzo Bianconi 
86e4a70e3eSLorenzo Bianconi 	switch (irq_type) {
87e4a70e3eSLorenzo Bianconi 	case IRQF_TRIGGER_HIGH:
88e4a70e3eSLorenzo Bianconi 	case IRQF_TRIGGER_RISING:
89e4a70e3eSLorenzo Bianconi 		break;
90e7b4b45eSLorenzo Bianconi 	case IRQF_TRIGGER_LOW:
91e7b4b45eSLorenzo Bianconi 	case IRQF_TRIGGER_FALLING:
92e7b4b45eSLorenzo Bianconi 		irq_active_low = true;
93e7b4b45eSLorenzo Bianconi 		break;
94e4a70e3eSLorenzo Bianconi 	default:
95e4a70e3eSLorenzo Bianconi 		dev_info(hw->dev,
96e4a70e3eSLorenzo Bianconi 			 "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
97e4a70e3eSLorenzo Bianconi 			 irq_type);
98e4a70e3eSLorenzo Bianconi 		irq_type = IRQF_TRIGGER_RISING;
99e4a70e3eSLorenzo Bianconi 		break;
100e4a70e3eSLorenzo Bianconi 	}
101e4a70e3eSLorenzo Bianconi 
10262177922SLorenzo Bianconi 	err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR,
10362177922SLorenzo Bianconi 				 HTS221_REG_DRDY_HL_MASK,
10462177922SLorenzo Bianconi 				 FIELD_PREP(HTS221_REG_DRDY_HL_MASK,
10562177922SLorenzo Bianconi 					    irq_active_low));
106e7b4b45eSLorenzo Bianconi 	if (err < 0)
107e7b4b45eSLorenzo Bianconi 		return err;
1089251f7aaSLorenzo Bianconi 
10992c3e93bSAndy Shevchenko 	if (device_property_read_bool(hw->dev, "drive-open-drain") ||
1109251f7aaSLorenzo Bianconi 	    (pdata && pdata->open_drain)) {
1119251f7aaSLorenzo Bianconi 		irq_type |= IRQF_SHARED;
1129251f7aaSLorenzo Bianconi 		open_drain = true;
1139251f7aaSLorenzo Bianconi 	}
1149251f7aaSLorenzo Bianconi 
11562177922SLorenzo Bianconi 	err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR,
1169251f7aaSLorenzo Bianconi 				 HTS221_REG_DRDY_PP_OD_MASK,
11762177922SLorenzo Bianconi 				 FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK,
11862177922SLorenzo Bianconi 					    open_drain));
1199251f7aaSLorenzo Bianconi 	if (err < 0)
1209251f7aaSLorenzo Bianconi 		return err;
1219251f7aaSLorenzo Bianconi 
122e4a70e3eSLorenzo Bianconi 	err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
123e4a70e3eSLorenzo Bianconi 					hts221_trigger_handler_thread,
124e4a70e3eSLorenzo Bianconi 					irq_type | IRQF_ONESHOT,
125e4a70e3eSLorenzo Bianconi 					hw->name, hw);
126e4a70e3eSLorenzo Bianconi 	if (err) {
127e4a70e3eSLorenzo Bianconi 		dev_err(hw->dev, "failed to request trigger irq %d\n",
128e4a70e3eSLorenzo Bianconi 			hw->irq);
129e4a70e3eSLorenzo Bianconi 		return err;
130e4a70e3eSLorenzo Bianconi 	}
131e4a70e3eSLorenzo Bianconi 
132e4a70e3eSLorenzo Bianconi 	hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
133e4a70e3eSLorenzo Bianconi 					  iio_dev->name);
134e4a70e3eSLorenzo Bianconi 	if (!hw->trig)
135e4a70e3eSLorenzo Bianconi 		return -ENOMEM;
136e4a70e3eSLorenzo Bianconi 
137e4a70e3eSLorenzo Bianconi 	iio_trigger_set_drvdata(hw->trig, iio_dev);
138e4a70e3eSLorenzo Bianconi 	hw->trig->ops = &hts221_trigger_ops;
13910b9c2c3SDmitry Rokosov 
14010b9c2c3SDmitry Rokosov 	err = devm_iio_trigger_register(hw->dev, hw->trig);
14110b9c2c3SDmitry Rokosov 
142e4a70e3eSLorenzo Bianconi 	iio_dev->trig = iio_trigger_get(hw->trig);
143e4a70e3eSLorenzo Bianconi 
14410b9c2c3SDmitry Rokosov 	return err;
145e4a70e3eSLorenzo Bianconi }
146e4a70e3eSLorenzo Bianconi 
hts221_buffer_preenable(struct iio_dev * iio_dev)147e4a70e3eSLorenzo Bianconi static int hts221_buffer_preenable(struct iio_dev *iio_dev)
148e4a70e3eSLorenzo Bianconi {
149e3e25446SLorenzo Bianconi 	return hts221_set_enable(iio_priv(iio_dev), true);
150e4a70e3eSLorenzo Bianconi }
151e4a70e3eSLorenzo Bianconi 
hts221_buffer_postdisable(struct iio_dev * iio_dev)152e4a70e3eSLorenzo Bianconi static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
153e4a70e3eSLorenzo Bianconi {
154e3e25446SLorenzo Bianconi 	return hts221_set_enable(iio_priv(iio_dev), false);
155e4a70e3eSLorenzo Bianconi }
156e4a70e3eSLorenzo Bianconi 
157e4a70e3eSLorenzo Bianconi static const struct iio_buffer_setup_ops hts221_buffer_ops = {
158e4a70e3eSLorenzo Bianconi 	.preenable = hts221_buffer_preenable,
159e4a70e3eSLorenzo Bianconi 	.postdisable = hts221_buffer_postdisable,
160e4a70e3eSLorenzo Bianconi };
161e4a70e3eSLorenzo Bianconi 
hts221_buffer_handler_thread(int irq,void * p)162e4a70e3eSLorenzo Bianconi static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
163e4a70e3eSLorenzo Bianconi {
164e4a70e3eSLorenzo Bianconi 	struct iio_poll_func *pf = p;
165e4a70e3eSLorenzo Bianconi 	struct iio_dev *iio_dev = pf->indio_dev;
166e4a70e3eSLorenzo Bianconi 	struct hts221_hw *hw = iio_priv(iio_dev);
167e4a70e3eSLorenzo Bianconi 	struct iio_chan_spec const *ch;
168e4a70e3eSLorenzo Bianconi 	int err;
169e4a70e3eSLorenzo Bianconi 
170e4a70e3eSLorenzo Bianconi 	/* humidity data */
171e4a70e3eSLorenzo Bianconi 	ch = &iio_dev->channels[HTS221_SENSOR_H];
17262177922SLorenzo Bianconi 	err = regmap_bulk_read(hw->regmap, ch->address,
1735c49056aSJonathan Cameron 			       &hw->scan.channels[0],
1745c49056aSJonathan Cameron 			       sizeof(hw->scan.channels[0]));
175e4a70e3eSLorenzo Bianconi 	if (err < 0)
176e4a70e3eSLorenzo Bianconi 		goto out;
177e4a70e3eSLorenzo Bianconi 
178e4a70e3eSLorenzo Bianconi 	/* temperature data */
179e4a70e3eSLorenzo Bianconi 	ch = &iio_dev->channels[HTS221_SENSOR_T];
18062177922SLorenzo Bianconi 	err = regmap_bulk_read(hw->regmap, ch->address,
1815c49056aSJonathan Cameron 			       &hw->scan.channels[1],
1825c49056aSJonathan Cameron 			       sizeof(hw->scan.channels[1]));
183e4a70e3eSLorenzo Bianconi 	if (err < 0)
184e4a70e3eSLorenzo Bianconi 		goto out;
185e4a70e3eSLorenzo Bianconi 
1865c49056aSJonathan Cameron 	iio_push_to_buffers_with_timestamp(iio_dev, &hw->scan,
187e4a70e3eSLorenzo Bianconi 					   iio_get_time_ns(iio_dev));
188e4a70e3eSLorenzo Bianconi 
189e4a70e3eSLorenzo Bianconi out:
190e4a70e3eSLorenzo Bianconi 	iio_trigger_notify_done(hw->trig);
191e4a70e3eSLorenzo Bianconi 
192e4a70e3eSLorenzo Bianconi 	return IRQ_HANDLED;
193e4a70e3eSLorenzo Bianconi }
194e4a70e3eSLorenzo Bianconi 
hts221_allocate_buffers(struct iio_dev * iio_dev)1957d17577dSAlexandru Ardelean int hts221_allocate_buffers(struct iio_dev *iio_dev)
196e4a70e3eSLorenzo Bianconi {
1977d17577dSAlexandru Ardelean 	struct hts221_hw *hw = iio_priv(iio_dev);
1987d17577dSAlexandru Ardelean 	return devm_iio_triggered_buffer_setup(hw->dev, iio_dev,
199e4a70e3eSLorenzo Bianconi 					NULL, hts221_buffer_handler_thread,
200e4a70e3eSLorenzo Bianconi 					&hts221_buffer_ops);
201e4a70e3eSLorenzo Bianconi }
202e4a70e3eSLorenzo Bianconi 
203e4a70e3eSLorenzo Bianconi MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
204e4a70e3eSLorenzo Bianconi MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
205e4a70e3eSLorenzo Bianconi MODULE_LICENSE("GPL v2");
206