1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * as3935.c - Support for AS3935 Franklin lightning sensor |
4 | * |
5 | * Copyright (C) 2014, 2017-2018 |
6 | * Author: Matt Ranostay <matt.ranostay@konsulko.com> |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/mod_devicetable.h> |
11 | #include <linux/init.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/workqueue.h> |
15 | #include <linux/devm-helpers.h> |
16 | #include <linux/mutex.h> |
17 | #include <linux/err.h> |
18 | #include <linux/irq.h> |
19 | #include <linux/spi/spi.h> |
20 | #include <linux/iio/iio.h> |
21 | #include <linux/iio/sysfs.h> |
22 | #include <linux/iio/trigger.h> |
23 | #include <linux/iio/trigger_consumer.h> |
24 | #include <linux/iio/buffer.h> |
25 | #include <linux/iio/triggered_buffer.h> |
26 | |
27 | #define AS3935_AFE_GAIN 0x00 |
28 | #define AS3935_AFE_MASK 0x3F |
29 | #define AS3935_AFE_GAIN_MAX 0x1F |
30 | #define AS3935_AFE_PWR_BIT BIT(0) |
31 | |
32 | #define AS3935_NFLWDTH 0x01 |
33 | #define AS3935_NFLWDTH_MASK 0x7f |
34 | |
35 | #define AS3935_INT 0x03 |
36 | #define AS3935_INT_MASK 0x0f |
37 | #define AS3935_DISTURB_INT BIT(2) |
38 | #define AS3935_EVENT_INT BIT(3) |
39 | #define AS3935_NOISE_INT BIT(0) |
40 | |
41 | #define AS3935_DATA 0x07 |
42 | #define AS3935_DATA_MASK 0x3F |
43 | |
44 | #define AS3935_TUNE_CAP 0x08 |
45 | #define AS3935_DEFAULTS 0x3C |
46 | #define AS3935_CALIBRATE 0x3D |
47 | |
48 | #define AS3935_READ_DATA BIT(14) |
49 | #define AS3935_ADDRESS(x) ((x) << 8) |
50 | |
51 | #define MAX_PF_CAP 120 |
52 | #define TUNE_CAP_DIV 8 |
53 | |
54 | struct as3935_state { |
55 | struct spi_device *spi; |
56 | struct iio_trigger *trig; |
57 | struct mutex lock; |
58 | struct delayed_work work; |
59 | |
60 | unsigned long noise_tripped; |
61 | u32 tune_cap; |
62 | u32 nflwdth_reg; |
63 | /* Ensure timestamp is naturally aligned */ |
64 | struct { |
65 | u8 chan; |
66 | s64 timestamp __aligned(8); |
67 | } scan; |
68 | u8 buf[2] __aligned(IIO_DMA_MINALIGN); |
69 | }; |
70 | |
71 | static const struct iio_chan_spec as3935_channels[] = { |
72 | { |
73 | .type = IIO_PROXIMITY, |
74 | .info_mask_separate = |
75 | BIT(IIO_CHAN_INFO_RAW) | |
76 | BIT(IIO_CHAN_INFO_PROCESSED) | |
77 | BIT(IIO_CHAN_INFO_SCALE), |
78 | .scan_index = 0, |
79 | .scan_type = { |
80 | .sign = 'u', |
81 | .realbits = 6, |
82 | .storagebits = 8, |
83 | }, |
84 | }, |
85 | IIO_CHAN_SOFT_TIMESTAMP(1), |
86 | }; |
87 | |
88 | static int as3935_read(struct as3935_state *st, unsigned int reg, int *val) |
89 | { |
90 | u8 cmd; |
91 | int ret; |
92 | |
93 | cmd = (AS3935_READ_DATA | AS3935_ADDRESS(reg)) >> 8; |
94 | ret = spi_w8r8(spi: st->spi, cmd); |
95 | if (ret < 0) |
96 | return ret; |
97 | *val = ret; |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static int as3935_write(struct as3935_state *st, |
103 | unsigned int reg, |
104 | unsigned int val) |
105 | { |
106 | u8 *buf = st->buf; |
107 | |
108 | buf[0] = AS3935_ADDRESS(reg) >> 8; |
109 | buf[1] = val; |
110 | |
111 | return spi_write(spi: st->spi, buf, len: 2); |
112 | } |
113 | |
114 | static ssize_t as3935_sensor_sensitivity_show(struct device *dev, |
115 | struct device_attribute *attr, |
116 | char *buf) |
117 | { |
118 | struct as3935_state *st = iio_priv(indio_dev: dev_to_iio_dev(dev)); |
119 | int val, ret; |
120 | |
121 | ret = as3935_read(st, AS3935_AFE_GAIN, val: &val); |
122 | if (ret) |
123 | return ret; |
124 | val = (val & AS3935_AFE_MASK) >> 1; |
125 | |
126 | return sysfs_emit(buf, fmt: "%d\n" , val); |
127 | } |
128 | |
129 | static ssize_t as3935_sensor_sensitivity_store(struct device *dev, |
130 | struct device_attribute *attr, |
131 | const char *buf, size_t len) |
132 | { |
133 | struct as3935_state *st = iio_priv(indio_dev: dev_to_iio_dev(dev)); |
134 | unsigned long val; |
135 | int ret; |
136 | |
137 | ret = kstrtoul(s: buf, base: 10, res: &val); |
138 | if (ret) |
139 | return -EINVAL; |
140 | |
141 | if (val > AS3935_AFE_GAIN_MAX) |
142 | return -EINVAL; |
143 | |
144 | as3935_write(st, AS3935_AFE_GAIN, val: val << 1); |
145 | |
146 | return len; |
147 | } |
148 | |
149 | static ssize_t as3935_noise_level_tripped_show(struct device *dev, |
150 | struct device_attribute *attr, |
151 | char *buf) |
152 | { |
153 | struct as3935_state *st = iio_priv(indio_dev: dev_to_iio_dev(dev)); |
154 | int ret; |
155 | |
156 | mutex_lock(&st->lock); |
157 | ret = sysfs_emit(buf, fmt: "%d\n" , !time_after(jiffies, st->noise_tripped + HZ)); |
158 | mutex_unlock(lock: &st->lock); |
159 | |
160 | return ret; |
161 | } |
162 | |
163 | static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR, |
164 | as3935_sensor_sensitivity_show, as3935_sensor_sensitivity_store, 0); |
165 | |
166 | static IIO_DEVICE_ATTR(noise_level_tripped, S_IRUGO, |
167 | as3935_noise_level_tripped_show, NULL, 0); |
168 | |
169 | static struct attribute *as3935_attributes[] = { |
170 | &iio_dev_attr_sensor_sensitivity.dev_attr.attr, |
171 | &iio_dev_attr_noise_level_tripped.dev_attr.attr, |
172 | NULL, |
173 | }; |
174 | |
175 | static const struct attribute_group as3935_attribute_group = { |
176 | .attrs = as3935_attributes, |
177 | }; |
178 | |
179 | static int as3935_read_raw(struct iio_dev *indio_dev, |
180 | struct iio_chan_spec const *chan, |
181 | int *val, |
182 | int *val2, |
183 | long m) |
184 | { |
185 | struct as3935_state *st = iio_priv(indio_dev); |
186 | int ret; |
187 | |
188 | |
189 | switch (m) { |
190 | case IIO_CHAN_INFO_PROCESSED: |
191 | case IIO_CHAN_INFO_RAW: |
192 | *val2 = 0; |
193 | ret = as3935_read(st, AS3935_DATA, val); |
194 | if (ret) |
195 | return ret; |
196 | |
197 | /* storm out of range */ |
198 | if (*val == AS3935_DATA_MASK) |
199 | return -EINVAL; |
200 | |
201 | if (m == IIO_CHAN_INFO_RAW) |
202 | return IIO_VAL_INT; |
203 | |
204 | if (m == IIO_CHAN_INFO_PROCESSED) |
205 | *val *= 1000; |
206 | break; |
207 | case IIO_CHAN_INFO_SCALE: |
208 | *val = 1000; |
209 | break; |
210 | default: |
211 | return -EINVAL; |
212 | } |
213 | |
214 | return IIO_VAL_INT; |
215 | } |
216 | |
217 | static const struct iio_info as3935_info = { |
218 | .attrs = &as3935_attribute_group, |
219 | .read_raw = &as3935_read_raw, |
220 | }; |
221 | |
222 | static irqreturn_t as3935_trigger_handler(int irq, void *private) |
223 | { |
224 | struct iio_poll_func *pf = private; |
225 | struct iio_dev *indio_dev = pf->indio_dev; |
226 | struct as3935_state *st = iio_priv(indio_dev); |
227 | int val, ret; |
228 | |
229 | ret = as3935_read(st, AS3935_DATA, val: &val); |
230 | if (ret) |
231 | goto err_read; |
232 | |
233 | st->scan.chan = val & AS3935_DATA_MASK; |
234 | iio_push_to_buffers_with_timestamp(indio_dev, data: &st->scan, |
235 | timestamp: iio_get_time_ns(indio_dev)); |
236 | err_read: |
237 | iio_trigger_notify_done(trig: indio_dev->trig); |
238 | |
239 | return IRQ_HANDLED; |
240 | } |
241 | |
242 | static void as3935_event_work(struct work_struct *work) |
243 | { |
244 | struct as3935_state *st; |
245 | int val; |
246 | int ret; |
247 | |
248 | st = container_of(work, struct as3935_state, work.work); |
249 | |
250 | ret = as3935_read(st, AS3935_INT, val: &val); |
251 | if (ret) { |
252 | dev_warn(&st->spi->dev, "read error\n" ); |
253 | return; |
254 | } |
255 | |
256 | val &= AS3935_INT_MASK; |
257 | |
258 | switch (val) { |
259 | case AS3935_EVENT_INT: |
260 | iio_trigger_poll_nested(trig: st->trig); |
261 | break; |
262 | case AS3935_DISTURB_INT: |
263 | case AS3935_NOISE_INT: |
264 | mutex_lock(&st->lock); |
265 | st->noise_tripped = jiffies; |
266 | mutex_unlock(lock: &st->lock); |
267 | dev_warn(&st->spi->dev, "noise level is too high\n" ); |
268 | break; |
269 | } |
270 | } |
271 | |
272 | static irqreturn_t as3935_interrupt_handler(int irq, void *private) |
273 | { |
274 | struct iio_dev *indio_dev = private; |
275 | struct as3935_state *st = iio_priv(indio_dev); |
276 | |
277 | /* |
278 | * Delay work for >2 milliseconds after an interrupt to allow |
279 | * estimated distance to recalculated. |
280 | */ |
281 | |
282 | schedule_delayed_work(dwork: &st->work, delay: msecs_to_jiffies(m: 3)); |
283 | |
284 | return IRQ_HANDLED; |
285 | } |
286 | |
287 | static void calibrate_as3935(struct as3935_state *st) |
288 | { |
289 | as3935_write(st, AS3935_DEFAULTS, val: 0x96); |
290 | as3935_write(st, AS3935_CALIBRATE, val: 0x96); |
291 | as3935_write(st, AS3935_TUNE_CAP, |
292 | BIT(5) | (st->tune_cap / TUNE_CAP_DIV)); |
293 | |
294 | mdelay(2); |
295 | as3935_write(st, AS3935_TUNE_CAP, val: (st->tune_cap / TUNE_CAP_DIV)); |
296 | as3935_write(st, AS3935_NFLWDTH, val: st->nflwdth_reg); |
297 | } |
298 | |
299 | static int as3935_suspend(struct device *dev) |
300 | { |
301 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
302 | struct as3935_state *st = iio_priv(indio_dev); |
303 | int val, ret; |
304 | |
305 | mutex_lock(&st->lock); |
306 | ret = as3935_read(st, AS3935_AFE_GAIN, val: &val); |
307 | if (ret) |
308 | goto err_suspend; |
309 | val |= AS3935_AFE_PWR_BIT; |
310 | |
311 | ret = as3935_write(st, AS3935_AFE_GAIN, val); |
312 | |
313 | err_suspend: |
314 | mutex_unlock(lock: &st->lock); |
315 | |
316 | return ret; |
317 | } |
318 | |
319 | static int as3935_resume(struct device *dev) |
320 | { |
321 | struct iio_dev *indio_dev = dev_get_drvdata(dev); |
322 | struct as3935_state *st = iio_priv(indio_dev); |
323 | int val, ret; |
324 | |
325 | mutex_lock(&st->lock); |
326 | ret = as3935_read(st, AS3935_AFE_GAIN, val: &val); |
327 | if (ret) |
328 | goto err_resume; |
329 | val &= ~AS3935_AFE_PWR_BIT; |
330 | ret = as3935_write(st, AS3935_AFE_GAIN, val); |
331 | |
332 | calibrate_as3935(st); |
333 | |
334 | err_resume: |
335 | mutex_unlock(lock: &st->lock); |
336 | |
337 | return ret; |
338 | } |
339 | |
340 | static DEFINE_SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume); |
341 | |
342 | static int as3935_probe(struct spi_device *spi) |
343 | { |
344 | struct device *dev = &spi->dev; |
345 | struct iio_dev *indio_dev; |
346 | struct iio_trigger *trig; |
347 | struct as3935_state *st; |
348 | int ret; |
349 | |
350 | /* Be sure lightning event interrupt is specified */ |
351 | if (!spi->irq) { |
352 | dev_err(dev, "unable to get event interrupt\n" ); |
353 | return -EINVAL; |
354 | } |
355 | |
356 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*st)); |
357 | if (!indio_dev) |
358 | return -ENOMEM; |
359 | |
360 | st = iio_priv(indio_dev); |
361 | st->spi = spi; |
362 | |
363 | spi_set_drvdata(spi, data: indio_dev); |
364 | mutex_init(&st->lock); |
365 | |
366 | ret = device_property_read_u32(dev, |
367 | propname: "ams,tuning-capacitor-pf" , val: &st->tune_cap); |
368 | if (ret) { |
369 | st->tune_cap = 0; |
370 | dev_warn(dev, "no tuning-capacitor-pf set, defaulting to %d" , |
371 | st->tune_cap); |
372 | } |
373 | |
374 | if (st->tune_cap > MAX_PF_CAP) { |
375 | dev_err(dev, "wrong tuning-capacitor-pf setting of %d\n" , |
376 | st->tune_cap); |
377 | return -EINVAL; |
378 | } |
379 | |
380 | ret = device_property_read_u32(dev, |
381 | propname: "ams,nflwdth" , val: &st->nflwdth_reg); |
382 | if (!ret && st->nflwdth_reg > AS3935_NFLWDTH_MASK) { |
383 | dev_err(dev, "invalid nflwdth setting of %d\n" , |
384 | st->nflwdth_reg); |
385 | return -EINVAL; |
386 | } |
387 | |
388 | indio_dev->name = spi_get_device_id(sdev: spi)->name; |
389 | indio_dev->channels = as3935_channels; |
390 | indio_dev->num_channels = ARRAY_SIZE(as3935_channels); |
391 | indio_dev->modes = INDIO_DIRECT_MODE; |
392 | indio_dev->info = &as3935_info; |
393 | |
394 | trig = devm_iio_trigger_alloc(dev, "%s-dev%d" , |
395 | indio_dev->name, |
396 | iio_device_id(indio_dev)); |
397 | |
398 | if (!trig) |
399 | return -ENOMEM; |
400 | |
401 | st->trig = trig; |
402 | st->noise_tripped = jiffies - HZ; |
403 | iio_trigger_set_drvdata(trig, data: indio_dev); |
404 | |
405 | ret = devm_iio_trigger_register(dev, trig_info: trig); |
406 | if (ret) { |
407 | dev_err(dev, "failed to register trigger\n" ); |
408 | return ret; |
409 | } |
410 | |
411 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, |
412 | iio_pollfunc_store_time, |
413 | as3935_trigger_handler, NULL); |
414 | |
415 | if (ret) { |
416 | dev_err(dev, "cannot setup iio trigger\n" ); |
417 | return ret; |
418 | } |
419 | |
420 | calibrate_as3935(st); |
421 | |
422 | ret = devm_delayed_work_autocancel(dev, w: &st->work, worker: as3935_event_work); |
423 | if (ret) |
424 | return ret; |
425 | |
426 | ret = devm_request_irq(dev, irq: spi->irq, |
427 | handler: &as3935_interrupt_handler, |
428 | IRQF_TRIGGER_RISING, |
429 | devname: dev_name(dev), |
430 | dev_id: indio_dev); |
431 | |
432 | if (ret) { |
433 | dev_err(dev, "unable to request irq\n" ); |
434 | return ret; |
435 | } |
436 | |
437 | ret = devm_iio_device_register(dev, indio_dev); |
438 | if (ret < 0) { |
439 | dev_err(dev, "unable to register device\n" ); |
440 | return ret; |
441 | } |
442 | return 0; |
443 | } |
444 | |
445 | static const struct of_device_id as3935_of_match[] = { |
446 | { .compatible = "ams,as3935" , }, |
447 | { /* sentinel */ }, |
448 | }; |
449 | MODULE_DEVICE_TABLE(of, as3935_of_match); |
450 | |
451 | static const struct spi_device_id as3935_id[] = { |
452 | {"as3935" , 0}, |
453 | {}, |
454 | }; |
455 | MODULE_DEVICE_TABLE(spi, as3935_id); |
456 | |
457 | static struct spi_driver as3935_driver = { |
458 | .driver = { |
459 | .name = "as3935" , |
460 | .of_match_table = as3935_of_match, |
461 | .pm = pm_sleep_ptr(&as3935_pm_ops), |
462 | }, |
463 | .probe = as3935_probe, |
464 | .id_table = as3935_id, |
465 | }; |
466 | module_spi_driver(as3935_driver); |
467 | |
468 | MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>" ); |
469 | MODULE_DESCRIPTION("AS3935 lightning sensor" ); |
470 | MODULE_LICENSE("GPL" ); |
471 | |