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