1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * STMicroelectronics hts221 sensor driver |
4 | * |
5 | * Copyright 2016 STMicroelectronics Inc. |
6 | * |
7 | * Lorenzo Bianconi <lorenzo.bianconi@st.com> |
8 | */ |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/device.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/irqreturn.h> |
14 | #include <linux/property.h> |
15 | #include <linux/regmap.h> |
16 | #include <linux/bitfield.h> |
17 | |
18 | #include <linux/iio/iio.h> |
19 | #include <linux/iio/trigger.h> |
20 | #include <linux/iio/events.h> |
21 | #include <linux/iio/trigger_consumer.h> |
22 | #include <linux/iio/triggered_buffer.h> |
23 | #include <linux/iio/buffer.h> |
24 | |
25 | #include <linux/platform_data/st_sensors_pdata.h> |
26 | |
27 | #include "hts221.h" |
28 | |
29 | #define HTS221_REG_DRDY_HL_ADDR 0x22 |
30 | #define HTS221_REG_DRDY_HL_MASK BIT(7) |
31 | #define HTS221_REG_DRDY_PP_OD_ADDR 0x22 |
32 | #define HTS221_REG_DRDY_PP_OD_MASK BIT(6) |
33 | #define HTS221_REG_DRDY_EN_ADDR 0x22 |
34 | #define HTS221_REG_DRDY_EN_MASK BIT(2) |
35 | #define HTS221_REG_STATUS_ADDR 0x27 |
36 | #define HTS221_RH_DRDY_MASK BIT(1) |
37 | #define HTS221_TEMP_DRDY_MASK BIT(0) |
38 | |
39 | static int hts221_trig_set_state(struct iio_trigger *trig, bool state) |
40 | { |
41 | struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig); |
42 | struct hts221_hw *hw = iio_priv(indio_dev: iio_dev); |
43 | |
44 | return regmap_update_bits(map: hw->regmap, HTS221_REG_DRDY_EN_ADDR, |
45 | HTS221_REG_DRDY_EN_MASK, |
46 | FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state)); |
47 | } |
48 | |
49 | static const struct iio_trigger_ops hts221_trigger_ops = { |
50 | .set_trigger_state = hts221_trig_set_state, |
51 | }; |
52 | |
53 | static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) |
54 | { |
55 | struct hts221_hw *hw = private; |
56 | int err, status; |
57 | |
58 | err = regmap_read(map: hw->regmap, HTS221_REG_STATUS_ADDR, val: &status); |
59 | if (err < 0) |
60 | return IRQ_HANDLED; |
61 | |
62 | /* |
63 | * H_DA bit (humidity data available) is routed to DRDY line. |
64 | * Humidity sample is computed after temperature one. |
65 | * Here we can assume data channels are both available if H_DA bit |
66 | * is set in status register |
67 | */ |
68 | if (!(status & HTS221_RH_DRDY_MASK)) |
69 | return IRQ_NONE; |
70 | |
71 | iio_trigger_poll_nested(trig: hw->trig); |
72 | |
73 | return IRQ_HANDLED; |
74 | } |
75 | |
76 | int hts221_allocate_trigger(struct iio_dev *iio_dev) |
77 | { |
78 | struct hts221_hw *hw = iio_priv(indio_dev: iio_dev); |
79 | struct st_sensors_platform_data *pdata = dev_get_platdata(dev: hw->dev); |
80 | bool irq_active_low = false, open_drain = false; |
81 | unsigned long irq_type; |
82 | int err; |
83 | |
84 | irq_type = irqd_get_trigger_type(d: irq_get_irq_data(irq: hw->irq)); |
85 | |
86 | switch (irq_type) { |
87 | case IRQF_TRIGGER_HIGH: |
88 | case IRQF_TRIGGER_RISING: |
89 | break; |
90 | case IRQF_TRIGGER_LOW: |
91 | case IRQF_TRIGGER_FALLING: |
92 | irq_active_low = true; |
93 | break; |
94 | default: |
95 | dev_info(hw->dev, |
96 | "mode %lx unsupported, using IRQF_TRIGGER_RISING\n" , |
97 | irq_type); |
98 | irq_type = IRQF_TRIGGER_RISING; |
99 | break; |
100 | } |
101 | |
102 | err = regmap_update_bits(map: hw->regmap, HTS221_REG_DRDY_HL_ADDR, |
103 | HTS221_REG_DRDY_HL_MASK, |
104 | FIELD_PREP(HTS221_REG_DRDY_HL_MASK, |
105 | irq_active_low)); |
106 | if (err < 0) |
107 | return err; |
108 | |
109 | if (device_property_read_bool(dev: hw->dev, propname: "drive-open-drain" ) || |
110 | (pdata && pdata->open_drain)) { |
111 | irq_type |= IRQF_SHARED; |
112 | open_drain = true; |
113 | } |
114 | |
115 | err = regmap_update_bits(map: hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR, |
116 | HTS221_REG_DRDY_PP_OD_MASK, |
117 | FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK, |
118 | open_drain)); |
119 | if (err < 0) |
120 | return err; |
121 | |
122 | err = devm_request_threaded_irq(dev: hw->dev, irq: hw->irq, NULL, |
123 | thread_fn: hts221_trigger_handler_thread, |
124 | irqflags: irq_type | IRQF_ONESHOT, |
125 | devname: hw->name, dev_id: hw); |
126 | if (err) { |
127 | dev_err(hw->dev, "failed to request trigger irq %d\n" , |
128 | hw->irq); |
129 | return err; |
130 | } |
131 | |
132 | hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger" , |
133 | iio_dev->name); |
134 | if (!hw->trig) |
135 | return -ENOMEM; |
136 | |
137 | iio_trigger_set_drvdata(trig: hw->trig, data: iio_dev); |
138 | hw->trig->ops = &hts221_trigger_ops; |
139 | |
140 | err = devm_iio_trigger_register(dev: hw->dev, trig_info: hw->trig); |
141 | |
142 | iio_dev->trig = iio_trigger_get(trig: hw->trig); |
143 | |
144 | return err; |
145 | } |
146 | |
147 | static int hts221_buffer_preenable(struct iio_dev *iio_dev) |
148 | { |
149 | return hts221_set_enable(hw: iio_priv(indio_dev: iio_dev), enable: true); |
150 | } |
151 | |
152 | static int hts221_buffer_postdisable(struct iio_dev *iio_dev) |
153 | { |
154 | return hts221_set_enable(hw: iio_priv(indio_dev: iio_dev), enable: false); |
155 | } |
156 | |
157 | static const struct iio_buffer_setup_ops hts221_buffer_ops = { |
158 | .preenable = hts221_buffer_preenable, |
159 | .postdisable = hts221_buffer_postdisable, |
160 | }; |
161 | |
162 | static irqreturn_t hts221_buffer_handler_thread(int irq, void *p) |
163 | { |
164 | struct iio_poll_func *pf = p; |
165 | struct iio_dev *iio_dev = pf->indio_dev; |
166 | struct hts221_hw *hw = iio_priv(indio_dev: iio_dev); |
167 | struct iio_chan_spec const *ch; |
168 | int err; |
169 | |
170 | /* humidity data */ |
171 | ch = &iio_dev->channels[HTS221_SENSOR_H]; |
172 | err = regmap_bulk_read(map: hw->regmap, reg: ch->address, |
173 | val: &hw->scan.channels[0], |
174 | val_count: sizeof(hw->scan.channels[0])); |
175 | if (err < 0) |
176 | goto out; |
177 | |
178 | /* temperature data */ |
179 | ch = &iio_dev->channels[HTS221_SENSOR_T]; |
180 | err = regmap_bulk_read(map: hw->regmap, reg: ch->address, |
181 | val: &hw->scan.channels[1], |
182 | val_count: sizeof(hw->scan.channels[1])); |
183 | if (err < 0) |
184 | goto out; |
185 | |
186 | iio_push_to_buffers_with_timestamp(indio_dev: iio_dev, data: &hw->scan, |
187 | timestamp: iio_get_time_ns(indio_dev: iio_dev)); |
188 | |
189 | out: |
190 | iio_trigger_notify_done(trig: hw->trig); |
191 | |
192 | return IRQ_HANDLED; |
193 | } |
194 | |
195 | int hts221_allocate_buffers(struct iio_dev *iio_dev) |
196 | { |
197 | struct hts221_hw *hw = iio_priv(indio_dev: iio_dev); |
198 | return devm_iio_triggered_buffer_setup(hw->dev, iio_dev, |
199 | NULL, hts221_buffer_handler_thread, |
200 | &hts221_buffer_ops); |
201 | } |
202 | |
203 | MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>" ); |
204 | MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver" ); |
205 | MODULE_LICENSE("GPL v2" ); |
206 | |