xref: /linux/drivers/iio/humidity/hts221_buffer.c (revision f3f0ae16f0ce2a11067b958c7cf2a48c32ea6d60)
1e4a70e3eSLorenzo Bianconi /*
2e4a70e3eSLorenzo Bianconi  * STMicroelectronics hts221 sensor driver
3e4a70e3eSLorenzo Bianconi  *
4e4a70e3eSLorenzo Bianconi  * Copyright 2016 STMicroelectronics Inc.
5e4a70e3eSLorenzo Bianconi  *
6e4a70e3eSLorenzo Bianconi  * Lorenzo Bianconi <lorenzo.bianconi@st.com>
7e4a70e3eSLorenzo Bianconi  *
8e4a70e3eSLorenzo Bianconi  * Licensed under the GPL-2.
9e4a70e3eSLorenzo Bianconi  */
10e4a70e3eSLorenzo Bianconi #include <linux/kernel.h>
11e4a70e3eSLorenzo Bianconi #include <linux/module.h>
12e4a70e3eSLorenzo Bianconi #include <linux/device.h>
13e4a70e3eSLorenzo Bianconi #include <linux/interrupt.h>
14e4a70e3eSLorenzo Bianconi #include <linux/irqreturn.h>
15e4a70e3eSLorenzo Bianconi 
16e4a70e3eSLorenzo Bianconi #include <linux/iio/iio.h>
17e4a70e3eSLorenzo Bianconi #include <linux/iio/trigger.h>
18e4a70e3eSLorenzo Bianconi #include <linux/iio/events.h>
19e4a70e3eSLorenzo Bianconi #include <linux/iio/trigger_consumer.h>
20e4a70e3eSLorenzo Bianconi #include <linux/iio/triggered_buffer.h>
21e4a70e3eSLorenzo Bianconi #include <linux/iio/buffer.h>
22e4a70e3eSLorenzo Bianconi 
239251f7aaSLorenzo Bianconi #include <linux/platform_data/st_sensors_pdata.h>
249251f7aaSLorenzo Bianconi 
25e4a70e3eSLorenzo Bianconi #include "hts221.h"
26e4a70e3eSLorenzo Bianconi 
27e7b4b45eSLorenzo Bianconi #define HTS221_REG_DRDY_HL_ADDR		0x22
28e7b4b45eSLorenzo Bianconi #define HTS221_REG_DRDY_HL_MASK		BIT(7)
299251f7aaSLorenzo Bianconi #define HTS221_REG_DRDY_PP_OD_ADDR	0x22
309251f7aaSLorenzo Bianconi #define HTS221_REG_DRDY_PP_OD_MASK	BIT(6)
31*f3f0ae16SLorenzo Bianconi #define HTS221_REG_DRDY_EN_ADDR		0x22
32*f3f0ae16SLorenzo Bianconi #define HTS221_REG_DRDY_EN_MASK		BIT(2)
33e4a70e3eSLorenzo Bianconi #define HTS221_REG_STATUS_ADDR		0x27
34e4a70e3eSLorenzo Bianconi #define HTS221_RH_DRDY_MASK		BIT(1)
35e4a70e3eSLorenzo Bianconi #define HTS221_TEMP_DRDY_MASK		BIT(0)
36e4a70e3eSLorenzo Bianconi 
37e4a70e3eSLorenzo Bianconi static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
38e4a70e3eSLorenzo Bianconi {
39e4a70e3eSLorenzo Bianconi 	struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
40e4a70e3eSLorenzo Bianconi 	struct hts221_hw *hw = iio_priv(iio_dev);
41*f3f0ae16SLorenzo Bianconi 	int err;
42e4a70e3eSLorenzo Bianconi 
43*f3f0ae16SLorenzo Bianconi 	err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR,
44*f3f0ae16SLorenzo Bianconi 				     HTS221_REG_DRDY_EN_MASK, state);
45*f3f0ae16SLorenzo Bianconi 
46*f3f0ae16SLorenzo Bianconi 	return err < 0 ? err : 0;
47e4a70e3eSLorenzo Bianconi }
48e4a70e3eSLorenzo Bianconi 
49e4a70e3eSLorenzo Bianconi static const struct iio_trigger_ops hts221_trigger_ops = {
50e4a70e3eSLorenzo Bianconi 	.owner = THIS_MODULE,
51e4a70e3eSLorenzo Bianconi 	.set_trigger_state = hts221_trig_set_state,
52e4a70e3eSLorenzo Bianconi };
53e4a70e3eSLorenzo Bianconi 
54e4a70e3eSLorenzo Bianconi static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
55e4a70e3eSLorenzo Bianconi {
567ae6df68Ssimran singhal 	struct hts221_hw *hw = private;
57e4a70e3eSLorenzo Bianconi 	u8 status;
58e4a70e3eSLorenzo Bianconi 	int err;
59e4a70e3eSLorenzo Bianconi 
60e4a70e3eSLorenzo Bianconi 	err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
61e4a70e3eSLorenzo Bianconi 			   &status);
62e4a70e3eSLorenzo Bianconi 	if (err < 0)
63e4a70e3eSLorenzo Bianconi 		return IRQ_HANDLED;
64e4a70e3eSLorenzo Bianconi 
65e4a70e3eSLorenzo Bianconi 	/*
66e4a70e3eSLorenzo Bianconi 	 * H_DA bit (humidity data available) is routed to DRDY line.
67e4a70e3eSLorenzo Bianconi 	 * Humidity sample is computed after temperature one.
68e4a70e3eSLorenzo Bianconi 	 * Here we can assume data channels are both available if H_DA bit
69e4a70e3eSLorenzo Bianconi 	 * is set in status register
70e4a70e3eSLorenzo Bianconi 	 */
71e4a70e3eSLorenzo Bianconi 	if (!(status & HTS221_RH_DRDY_MASK))
72e4a70e3eSLorenzo Bianconi 		return IRQ_NONE;
73e4a70e3eSLorenzo Bianconi 
74e4a70e3eSLorenzo Bianconi 	iio_trigger_poll_chained(hw->trig);
75e4a70e3eSLorenzo Bianconi 
76e4a70e3eSLorenzo Bianconi 	return IRQ_HANDLED;
77e4a70e3eSLorenzo Bianconi }
78e4a70e3eSLorenzo Bianconi 
79e4a70e3eSLorenzo Bianconi int hts221_allocate_trigger(struct hts221_hw *hw)
80e4a70e3eSLorenzo Bianconi {
81e4a70e3eSLorenzo Bianconi 	struct iio_dev *iio_dev = iio_priv_to_dev(hw);
829251f7aaSLorenzo Bianconi 	bool irq_active_low = false, open_drain = false;
839251f7aaSLorenzo Bianconi 	struct device_node *np = hw->dev->of_node;
849251f7aaSLorenzo Bianconi 	struct st_sensors_platform_data *pdata;
85e4a70e3eSLorenzo Bianconi 	unsigned long irq_type;
86e4a70e3eSLorenzo Bianconi 	int err;
87e4a70e3eSLorenzo Bianconi 
88e4a70e3eSLorenzo Bianconi 	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
89e4a70e3eSLorenzo Bianconi 
90e4a70e3eSLorenzo Bianconi 	switch (irq_type) {
91e4a70e3eSLorenzo Bianconi 	case IRQF_TRIGGER_HIGH:
92e4a70e3eSLorenzo Bianconi 	case IRQF_TRIGGER_RISING:
93e4a70e3eSLorenzo Bianconi 		break;
94e7b4b45eSLorenzo Bianconi 	case IRQF_TRIGGER_LOW:
95e7b4b45eSLorenzo Bianconi 	case IRQF_TRIGGER_FALLING:
96e7b4b45eSLorenzo Bianconi 		irq_active_low = true;
97e7b4b45eSLorenzo Bianconi 		break;
98e4a70e3eSLorenzo Bianconi 	default:
99e4a70e3eSLorenzo Bianconi 		dev_info(hw->dev,
100e4a70e3eSLorenzo Bianconi 			 "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
101e4a70e3eSLorenzo Bianconi 			 irq_type);
102e4a70e3eSLorenzo Bianconi 		irq_type = IRQF_TRIGGER_RISING;
103e4a70e3eSLorenzo Bianconi 		break;
104e4a70e3eSLorenzo Bianconi 	}
105e4a70e3eSLorenzo Bianconi 
106e7b4b45eSLorenzo Bianconi 	err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR,
107e7b4b45eSLorenzo Bianconi 				     HTS221_REG_DRDY_HL_MASK, irq_active_low);
108e7b4b45eSLorenzo Bianconi 	if (err < 0)
109e7b4b45eSLorenzo Bianconi 		return err;
1109251f7aaSLorenzo Bianconi 
1119251f7aaSLorenzo Bianconi 	pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
1129251f7aaSLorenzo Bianconi 	if ((np && of_property_read_bool(np, "drive-open-drain")) ||
1139251f7aaSLorenzo Bianconi 	    (pdata && pdata->open_drain)) {
1149251f7aaSLorenzo Bianconi 		irq_type |= IRQF_SHARED;
1159251f7aaSLorenzo Bianconi 		open_drain = true;
1169251f7aaSLorenzo Bianconi 	}
1179251f7aaSLorenzo Bianconi 
1189251f7aaSLorenzo Bianconi 	err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR,
1199251f7aaSLorenzo Bianconi 				     HTS221_REG_DRDY_PP_OD_MASK,
1209251f7aaSLorenzo Bianconi 				     open_drain);
1219251f7aaSLorenzo Bianconi 	if (err < 0)
1229251f7aaSLorenzo Bianconi 		return err;
1239251f7aaSLorenzo Bianconi 
124e4a70e3eSLorenzo Bianconi 	err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
125e4a70e3eSLorenzo Bianconi 					hts221_trigger_handler_thread,
126e4a70e3eSLorenzo Bianconi 					irq_type | IRQF_ONESHOT,
127e4a70e3eSLorenzo Bianconi 					hw->name, hw);
128e4a70e3eSLorenzo Bianconi 	if (err) {
129e4a70e3eSLorenzo Bianconi 		dev_err(hw->dev, "failed to request trigger irq %d\n",
130e4a70e3eSLorenzo Bianconi 			hw->irq);
131e4a70e3eSLorenzo Bianconi 		return err;
132e4a70e3eSLorenzo Bianconi 	}
133e4a70e3eSLorenzo Bianconi 
134e4a70e3eSLorenzo Bianconi 	hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
135e4a70e3eSLorenzo Bianconi 					  iio_dev->name);
136e4a70e3eSLorenzo Bianconi 	if (!hw->trig)
137e4a70e3eSLorenzo Bianconi 		return -ENOMEM;
138e4a70e3eSLorenzo Bianconi 
139e4a70e3eSLorenzo Bianconi 	iio_trigger_set_drvdata(hw->trig, iio_dev);
140e4a70e3eSLorenzo Bianconi 	hw->trig->ops = &hts221_trigger_ops;
141e4a70e3eSLorenzo Bianconi 	hw->trig->dev.parent = hw->dev;
142e4a70e3eSLorenzo Bianconi 	iio_dev->trig = iio_trigger_get(hw->trig);
143e4a70e3eSLorenzo Bianconi 
144e4a70e3eSLorenzo Bianconi 	return devm_iio_trigger_register(hw->dev, hw->trig);
145e4a70e3eSLorenzo Bianconi }
146e4a70e3eSLorenzo Bianconi 
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 
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 	.postenable = iio_triggered_buffer_postenable,
160e4a70e3eSLorenzo Bianconi 	.predisable = iio_triggered_buffer_predisable,
161e4a70e3eSLorenzo Bianconi 	.postdisable = hts221_buffer_postdisable,
162e4a70e3eSLorenzo Bianconi };
163e4a70e3eSLorenzo Bianconi 
164e4a70e3eSLorenzo Bianconi static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
165e4a70e3eSLorenzo Bianconi {
166e4a70e3eSLorenzo Bianconi 	u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
167e4a70e3eSLorenzo Bianconi 	struct iio_poll_func *pf = p;
168e4a70e3eSLorenzo Bianconi 	struct iio_dev *iio_dev = pf->indio_dev;
169e4a70e3eSLorenzo Bianconi 	struct hts221_hw *hw = iio_priv(iio_dev);
170e4a70e3eSLorenzo Bianconi 	struct iio_chan_spec const *ch;
171e4a70e3eSLorenzo Bianconi 	int err;
172e4a70e3eSLorenzo Bianconi 
173e4a70e3eSLorenzo Bianconi 	/* humidity data */
174e4a70e3eSLorenzo Bianconi 	ch = &iio_dev->channels[HTS221_SENSOR_H];
175e4a70e3eSLorenzo Bianconi 	err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
176e4a70e3eSLorenzo Bianconi 			   buffer);
177e4a70e3eSLorenzo Bianconi 	if (err < 0)
178e4a70e3eSLorenzo Bianconi 		goto out;
179e4a70e3eSLorenzo Bianconi 
180e4a70e3eSLorenzo Bianconi 	/* temperature data */
181e4a70e3eSLorenzo Bianconi 	ch = &iio_dev->channels[HTS221_SENSOR_T];
182e4a70e3eSLorenzo Bianconi 	err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
183e4a70e3eSLorenzo Bianconi 			   buffer + HTS221_DATA_SIZE);
184e4a70e3eSLorenzo Bianconi 	if (err < 0)
185e4a70e3eSLorenzo Bianconi 		goto out;
186e4a70e3eSLorenzo Bianconi 
187e4a70e3eSLorenzo Bianconi 	iio_push_to_buffers_with_timestamp(iio_dev, buffer,
188e4a70e3eSLorenzo Bianconi 					   iio_get_time_ns(iio_dev));
189e4a70e3eSLorenzo Bianconi 
190e4a70e3eSLorenzo Bianconi out:
191e4a70e3eSLorenzo Bianconi 	iio_trigger_notify_done(hw->trig);
192e4a70e3eSLorenzo Bianconi 
193e4a70e3eSLorenzo Bianconi 	return IRQ_HANDLED;
194e4a70e3eSLorenzo Bianconi }
195e4a70e3eSLorenzo Bianconi 
196e4a70e3eSLorenzo Bianconi int hts221_allocate_buffers(struct hts221_hw *hw)
197e4a70e3eSLorenzo Bianconi {
198e4a70e3eSLorenzo Bianconi 	return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
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