1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * STMicroelectronics uvis25 sensor driver |
4 | * |
5 | * Copyright 2017 STMicroelectronics Inc. |
6 | * |
7 | * Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/device.h> |
13 | #include <linux/iio/sysfs.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/pm.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/irqreturn.h> |
18 | #include <linux/iio/trigger.h> |
19 | #include <linux/iio/trigger_consumer.h> |
20 | #include <linux/iio/triggered_buffer.h> |
21 | #include <linux/iio/buffer.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | #include "st_uvis25.h" |
25 | |
26 | #define ST_UVIS25_REG_WHOAMI_ADDR 0x0f |
27 | #define ST_UVIS25_REG_WHOAMI_VAL 0xca |
28 | #define ST_UVIS25_REG_CTRL1_ADDR 0x20 |
29 | #define ST_UVIS25_REG_ODR_MASK BIT(0) |
30 | #define ST_UVIS25_REG_BDU_MASK BIT(1) |
31 | #define ST_UVIS25_REG_CTRL2_ADDR 0x21 |
32 | #define ST_UVIS25_REG_BOOT_MASK BIT(7) |
33 | #define ST_UVIS25_REG_CTRL3_ADDR 0x22 |
34 | #define ST_UVIS25_REG_HL_MASK BIT(7) |
35 | #define ST_UVIS25_REG_STATUS_ADDR 0x27 |
36 | #define ST_UVIS25_REG_UV_DA_MASK BIT(0) |
37 | #define ST_UVIS25_REG_OUT_ADDR 0x28 |
38 | |
39 | static const struct iio_chan_spec st_uvis25_channels[] = { |
40 | { |
41 | .type = IIO_UVINDEX, |
42 | .address = ST_UVIS25_REG_OUT_ADDR, |
43 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), |
44 | .scan_index = 0, |
45 | .scan_type = { |
46 | .sign = 'u', |
47 | .realbits = 8, |
48 | .storagebits = 8, |
49 | }, |
50 | }, |
51 | IIO_CHAN_SOFT_TIMESTAMP(1), |
52 | }; |
53 | |
54 | static int st_uvis25_check_whoami(struct st_uvis25_hw *hw) |
55 | { |
56 | int err, data; |
57 | |
58 | err = regmap_read(map: hw->regmap, ST_UVIS25_REG_WHOAMI_ADDR, val: &data); |
59 | if (err < 0) { |
60 | dev_err(regmap_get_device(hw->regmap), |
61 | "failed to read whoami register\n" ); |
62 | return err; |
63 | } |
64 | |
65 | if (data != ST_UVIS25_REG_WHOAMI_VAL) { |
66 | dev_err(regmap_get_device(hw->regmap), |
67 | "wrong whoami {%02x vs %02x}\n" , |
68 | data, ST_UVIS25_REG_WHOAMI_VAL); |
69 | return -ENODEV; |
70 | } |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | static int st_uvis25_set_enable(struct st_uvis25_hw *hw, bool enable) |
76 | { |
77 | int err; |
78 | |
79 | err = regmap_update_bits(map: hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, |
80 | ST_UVIS25_REG_ODR_MASK, val: enable); |
81 | if (err < 0) |
82 | return err; |
83 | |
84 | hw->enabled = enable; |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | static int st_uvis25_read_oneshot(struct st_uvis25_hw *hw, u8 addr, int *val) |
90 | { |
91 | int err; |
92 | |
93 | err = st_uvis25_set_enable(hw, enable: true); |
94 | if (err < 0) |
95 | return err; |
96 | |
97 | msleep(msecs: 1500); |
98 | |
99 | /* |
100 | * in order to avoid possible race conditions with interrupt |
101 | * generation, disable the sensor first and then poll output |
102 | * register. That sequence guarantees the interrupt will be reset |
103 | * when irq line is unmasked |
104 | */ |
105 | err = st_uvis25_set_enable(hw, enable: false); |
106 | if (err < 0) |
107 | return err; |
108 | |
109 | err = regmap_read(map: hw->regmap, reg: addr, val); |
110 | |
111 | return err < 0 ? err : IIO_VAL_INT; |
112 | } |
113 | |
114 | static int st_uvis25_read_raw(struct iio_dev *iio_dev, |
115 | struct iio_chan_spec const *ch, |
116 | int *val, int *val2, long mask) |
117 | { |
118 | int ret; |
119 | |
120 | ret = iio_device_claim_direct_mode(indio_dev: iio_dev); |
121 | if (ret) |
122 | return ret; |
123 | |
124 | switch (mask) { |
125 | case IIO_CHAN_INFO_PROCESSED: { |
126 | struct st_uvis25_hw *hw = iio_priv(indio_dev: iio_dev); |
127 | |
128 | /* |
129 | * mask irq line during oneshot read since the sensor |
130 | * does not export the capability to disable data-ready line |
131 | * in the register map and it is enabled by default. |
132 | * If the line is unmasked during read_raw() it will be set |
133 | * active and never reset since the trigger is disabled |
134 | */ |
135 | if (hw->irq > 0) |
136 | disable_irq(irq: hw->irq); |
137 | ret = st_uvis25_read_oneshot(hw, addr: ch->address, val); |
138 | if (hw->irq > 0) |
139 | enable_irq(irq: hw->irq); |
140 | break; |
141 | } |
142 | default: |
143 | ret = -EINVAL; |
144 | break; |
145 | } |
146 | |
147 | iio_device_release_direct_mode(indio_dev: iio_dev); |
148 | |
149 | return ret; |
150 | } |
151 | |
152 | static irqreturn_t st_uvis25_trigger_handler_thread(int irq, void *private) |
153 | { |
154 | struct st_uvis25_hw *hw = private; |
155 | int err, status; |
156 | |
157 | err = regmap_read(map: hw->regmap, ST_UVIS25_REG_STATUS_ADDR, val: &status); |
158 | if (err < 0) |
159 | return IRQ_HANDLED; |
160 | |
161 | if (!(status & ST_UVIS25_REG_UV_DA_MASK)) |
162 | return IRQ_NONE; |
163 | |
164 | iio_trigger_poll_nested(trig: hw->trig); |
165 | |
166 | return IRQ_HANDLED; |
167 | } |
168 | |
169 | static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) |
170 | { |
171 | struct st_uvis25_hw *hw = iio_priv(indio_dev: iio_dev); |
172 | struct device *dev = regmap_get_device(map: hw->regmap); |
173 | bool irq_active_low = false; |
174 | unsigned long irq_type; |
175 | int err; |
176 | |
177 | irq_type = irqd_get_trigger_type(d: irq_get_irq_data(irq: hw->irq)); |
178 | |
179 | switch (irq_type) { |
180 | case IRQF_TRIGGER_HIGH: |
181 | case IRQF_TRIGGER_RISING: |
182 | break; |
183 | case IRQF_TRIGGER_LOW: |
184 | case IRQF_TRIGGER_FALLING: |
185 | irq_active_low = true; |
186 | break; |
187 | default: |
188 | dev_info(dev, "mode %lx unsupported\n" , irq_type); |
189 | return -EINVAL; |
190 | } |
191 | |
192 | err = regmap_update_bits(map: hw->regmap, ST_UVIS25_REG_CTRL3_ADDR, |
193 | ST_UVIS25_REG_HL_MASK, val: irq_active_low); |
194 | if (err < 0) |
195 | return err; |
196 | |
197 | err = devm_request_threaded_irq(dev, irq: hw->irq, NULL, |
198 | thread_fn: st_uvis25_trigger_handler_thread, |
199 | irqflags: irq_type | IRQF_ONESHOT, |
200 | devname: iio_dev->name, dev_id: hw); |
201 | if (err) { |
202 | dev_err(dev, "failed to request trigger irq %d\n" , |
203 | hw->irq); |
204 | return err; |
205 | } |
206 | |
207 | hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger" , |
208 | iio_dev->name); |
209 | if (!hw->trig) |
210 | return -ENOMEM; |
211 | |
212 | iio_trigger_set_drvdata(trig: hw->trig, data: iio_dev); |
213 | |
214 | return devm_iio_trigger_register(dev, trig_info: hw->trig); |
215 | } |
216 | |
217 | static int st_uvis25_buffer_preenable(struct iio_dev *iio_dev) |
218 | { |
219 | return st_uvis25_set_enable(hw: iio_priv(indio_dev: iio_dev), enable: true); |
220 | } |
221 | |
222 | static int st_uvis25_buffer_postdisable(struct iio_dev *iio_dev) |
223 | { |
224 | return st_uvis25_set_enable(hw: iio_priv(indio_dev: iio_dev), enable: false); |
225 | } |
226 | |
227 | static const struct iio_buffer_setup_ops st_uvis25_buffer_ops = { |
228 | .preenable = st_uvis25_buffer_preenable, |
229 | .postdisable = st_uvis25_buffer_postdisable, |
230 | }; |
231 | |
232 | static irqreturn_t st_uvis25_buffer_handler_thread(int irq, void *p) |
233 | { |
234 | struct iio_poll_func *pf = p; |
235 | struct iio_dev *iio_dev = pf->indio_dev; |
236 | struct st_uvis25_hw *hw = iio_priv(indio_dev: iio_dev); |
237 | unsigned int val; |
238 | int err; |
239 | |
240 | err = regmap_read(map: hw->regmap, ST_UVIS25_REG_OUT_ADDR, val: &val); |
241 | if (err < 0) |
242 | goto out; |
243 | |
244 | hw->scan.chan = val; |
245 | |
246 | iio_push_to_buffers_with_timestamp(indio_dev: iio_dev, data: &hw->scan, |
247 | timestamp: iio_get_time_ns(indio_dev: iio_dev)); |
248 | |
249 | out: |
250 | iio_trigger_notify_done(trig: hw->trig); |
251 | |
252 | return IRQ_HANDLED; |
253 | } |
254 | |
255 | static int st_uvis25_allocate_buffer(struct iio_dev *iio_dev) |
256 | { |
257 | struct st_uvis25_hw *hw = iio_priv(indio_dev: iio_dev); |
258 | |
259 | return devm_iio_triggered_buffer_setup(regmap_get_device(hw->regmap), |
260 | iio_dev, NULL, |
261 | st_uvis25_buffer_handler_thread, |
262 | &st_uvis25_buffer_ops); |
263 | } |
264 | |
265 | static const struct iio_info st_uvis25_info = { |
266 | .read_raw = st_uvis25_read_raw, |
267 | }; |
268 | |
269 | static int st_uvis25_init_sensor(struct st_uvis25_hw *hw) |
270 | { |
271 | int err; |
272 | |
273 | err = regmap_update_bits(map: hw->regmap, ST_UVIS25_REG_CTRL2_ADDR, |
274 | ST_UVIS25_REG_BOOT_MASK, val: 1); |
275 | if (err < 0) |
276 | return err; |
277 | |
278 | msleep(msecs: 2000); |
279 | |
280 | return regmap_update_bits(map: hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, |
281 | ST_UVIS25_REG_BDU_MASK, val: 1); |
282 | } |
283 | |
284 | int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap) |
285 | { |
286 | struct st_uvis25_hw *hw; |
287 | struct iio_dev *iio_dev; |
288 | int err; |
289 | |
290 | iio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*hw)); |
291 | if (!iio_dev) |
292 | return -ENOMEM; |
293 | |
294 | dev_set_drvdata(dev, data: (void *)iio_dev); |
295 | |
296 | hw = iio_priv(indio_dev: iio_dev); |
297 | hw->irq = irq; |
298 | hw->regmap = regmap; |
299 | |
300 | err = st_uvis25_check_whoami(hw); |
301 | if (err < 0) |
302 | return err; |
303 | |
304 | iio_dev->modes = INDIO_DIRECT_MODE; |
305 | iio_dev->channels = st_uvis25_channels; |
306 | iio_dev->num_channels = ARRAY_SIZE(st_uvis25_channels); |
307 | iio_dev->name = ST_UVIS25_DEV_NAME; |
308 | iio_dev->info = &st_uvis25_info; |
309 | |
310 | err = st_uvis25_init_sensor(hw); |
311 | if (err < 0) |
312 | return err; |
313 | |
314 | if (hw->irq > 0) { |
315 | err = st_uvis25_allocate_buffer(iio_dev); |
316 | if (err < 0) |
317 | return err; |
318 | |
319 | err = st_uvis25_allocate_trigger(iio_dev); |
320 | if (err) |
321 | return err; |
322 | } |
323 | |
324 | return devm_iio_device_register(dev, iio_dev); |
325 | } |
326 | EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25); |
327 | |
328 | static int st_uvis25_suspend(struct device *dev) |
329 | { |
330 | struct iio_dev *iio_dev = dev_get_drvdata(dev); |
331 | struct st_uvis25_hw *hw = iio_priv(indio_dev: iio_dev); |
332 | |
333 | return regmap_update_bits(map: hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, |
334 | ST_UVIS25_REG_ODR_MASK, val: 0); |
335 | } |
336 | |
337 | static int st_uvis25_resume(struct device *dev) |
338 | { |
339 | struct iio_dev *iio_dev = dev_get_drvdata(dev); |
340 | struct st_uvis25_hw *hw = iio_priv(indio_dev: iio_dev); |
341 | |
342 | if (hw->enabled) |
343 | return regmap_update_bits(map: hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, |
344 | ST_UVIS25_REG_ODR_MASK, val: 1); |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | EXPORT_NS_SIMPLE_DEV_PM_OPS(st_uvis25_pm_ops, st_uvis25_suspend, st_uvis25_resume, IIO_UVIS25); |
350 | |
351 | MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>" ); |
352 | MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver" ); |
353 | MODULE_LICENSE("GPL v2" ); |
354 | |