1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ADXL355 3-Axis Digital Accelerometer IIO core driver |
4 | * |
5 | * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> |
6 | * |
7 | * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf |
8 | */ |
9 | |
10 | #include <linux/bits.h> |
11 | #include <linux/bitfield.h> |
12 | #include <linux/iio/buffer.h> |
13 | #include <linux/iio/iio.h> |
14 | #include <linux/iio/trigger.h> |
15 | #include <linux/iio/triggered_buffer.h> |
16 | #include <linux/iio/trigger_consumer.h> |
17 | #include <linux/limits.h> |
18 | #include <linux/math64.h> |
19 | #include <linux/module.h> |
20 | #include <linux/mod_devicetable.h> |
21 | #include <linux/property.h> |
22 | #include <linux/regmap.h> |
23 | #include <linux/units.h> |
24 | |
25 | #include <asm/unaligned.h> |
26 | |
27 | #include "adxl355.h" |
28 | |
29 | /* ADXL355 Register Definitions */ |
30 | #define ADXL355_DEVID_AD_REG 0x00 |
31 | #define ADXL355_DEVID_MST_REG 0x01 |
32 | #define ADXL355_PARTID_REG 0x02 |
33 | #define ADXL355_STATUS_REG 0x04 |
34 | #define ADXL355_FIFO_ENTRIES_REG 0x05 |
35 | #define ADXL355_TEMP2_REG 0x06 |
36 | #define ADXL355_XDATA3_REG 0x08 |
37 | #define ADXL355_YDATA3_REG 0x0B |
38 | #define ADXL355_ZDATA3_REG 0x0E |
39 | #define ADXL355_FIFO_DATA_REG 0x11 |
40 | #define ADXL355_OFFSET_X_H_REG 0x1E |
41 | #define ADXL355_OFFSET_Y_H_REG 0x20 |
42 | #define ADXL355_OFFSET_Z_H_REG 0x22 |
43 | #define ADXL355_ACT_EN_REG 0x24 |
44 | #define ADXL355_ACT_THRESH_H_REG 0x25 |
45 | #define ADXL355_ACT_THRESH_L_REG 0x26 |
46 | #define ADXL355_ACT_COUNT_REG 0x27 |
47 | #define ADXL355_FILTER_REG 0x28 |
48 | #define ADXL355_FILTER_ODR_MSK GENMASK(3, 0) |
49 | #define ADXL355_FILTER_HPF_MSK GENMASK(6, 4) |
50 | #define ADXL355_FIFO_SAMPLES_REG 0x29 |
51 | #define ADXL355_INT_MAP_REG 0x2A |
52 | #define ADXL355_SYNC_REG 0x2B |
53 | #define ADXL355_RANGE_REG 0x2C |
54 | #define ADXL355_POWER_CTL_REG 0x2D |
55 | #define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0) |
56 | #define ADXL355_POWER_CTL_DRDY_MSK BIT(2) |
57 | #define ADXL355_SELF_TEST_REG 0x2E |
58 | #define ADXL355_RESET_REG 0x2F |
59 | |
60 | #define ADXL355_DEVID_AD_VAL 0xAD |
61 | #define ADXL355_DEVID_MST_VAL 0x1D |
62 | #define ADXL355_PARTID_VAL 0xED |
63 | #define ADXL359_PARTID_VAL 0xE9 |
64 | #define ADXL355_RESET_CODE 0x52 |
65 | |
66 | static const struct regmap_range adxl355_read_reg_range[] = { |
67 | regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG), |
68 | regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG), |
69 | }; |
70 | |
71 | const struct regmap_access_table adxl355_readable_regs_tbl = { |
72 | .yes_ranges = adxl355_read_reg_range, |
73 | .n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range), |
74 | }; |
75 | EXPORT_SYMBOL_NS_GPL(adxl355_readable_regs_tbl, IIO_ADXL355); |
76 | |
77 | static const struct regmap_range adxl355_write_reg_range[] = { |
78 | regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG), |
79 | }; |
80 | |
81 | const struct regmap_access_table adxl355_writeable_regs_tbl = { |
82 | .yes_ranges = adxl355_write_reg_range, |
83 | .n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range), |
84 | }; |
85 | EXPORT_SYMBOL_NS_GPL(adxl355_writeable_regs_tbl, IIO_ADXL355); |
86 | |
87 | const struct adxl355_chip_info adxl35x_chip_info[] = { |
88 | [ADXL355] = { |
89 | .name = "adxl355" , |
90 | .part_id = ADXL355_PARTID_VAL, |
91 | /* |
92 | * At +/- 2g with 20-bit resolution, scale is given in datasheet |
93 | * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2. |
94 | */ |
95 | .accel_scale = { |
96 | .integer = 0, |
97 | .decimal = 38245, |
98 | }, |
99 | /* |
100 | * The datasheet defines an intercept of 1885 LSB at 25 degC |
101 | * and a slope of -9.05 LSB/C. The following formula can be used |
102 | * to find the temperature: |
103 | * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow |
104 | * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE. |
105 | * Hence using some rearranging we get the scale as -110.497238 |
106 | * and offset as -2111.25. |
107 | */ |
108 | .temp_offset = { |
109 | .integer = -2111, |
110 | .decimal = 250000, |
111 | }, |
112 | }, |
113 | [ADXL359] = { |
114 | .name = "adxl359" , |
115 | .part_id = ADXL359_PARTID_VAL, |
116 | /* |
117 | * At +/- 10g with 20-bit resolution, scale is given in datasheet |
118 | * as 19.5ug/LSB = 0.0000195 * 9.80665 = 0.0.00019122967 m/s^2. |
119 | */ |
120 | .accel_scale = { |
121 | .integer = 0, |
122 | .decimal = 191229, |
123 | }, |
124 | /* |
125 | * The datasheet defines an intercept of 1852 LSB at 25 degC |
126 | * and a slope of -9.05 LSB/C. The following formula can be used |
127 | * to find the temperature: |
128 | * Temp = ((RAW - 1852)/(-9.05)) + 25 but this doesn't follow |
129 | * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE. |
130 | * Hence using some rearranging we get the scale as -110.497238 |
131 | * and offset as -2079.25. |
132 | */ |
133 | .temp_offset = { |
134 | .integer = -2079, |
135 | .decimal = 250000, |
136 | }, |
137 | }, |
138 | }; |
139 | EXPORT_SYMBOL_NS_GPL(adxl35x_chip_info, IIO_ADXL355); |
140 | |
141 | enum adxl355_op_mode { |
142 | ADXL355_MEASUREMENT, |
143 | ADXL355_STANDBY, |
144 | ADXL355_TEMP_OFF, |
145 | }; |
146 | |
147 | enum adxl355_odr { |
148 | ADXL355_ODR_4000HZ, |
149 | ADXL355_ODR_2000HZ, |
150 | ADXL355_ODR_1000HZ, |
151 | ADXL355_ODR_500HZ, |
152 | ADXL355_ODR_250HZ, |
153 | ADXL355_ODR_125HZ, |
154 | ADXL355_ODR_62_5HZ, |
155 | ADXL355_ODR_31_25HZ, |
156 | ADXL355_ODR_15_625HZ, |
157 | ADXL355_ODR_7_813HZ, |
158 | ADXL355_ODR_3_906HZ, |
159 | }; |
160 | |
161 | enum adxl355_hpf_3db { |
162 | ADXL355_HPF_OFF, |
163 | ADXL355_HPF_24_7, |
164 | ADXL355_HPF_6_2084, |
165 | ADXL355_HPF_1_5545, |
166 | ADXL355_HPF_0_3862, |
167 | ADXL355_HPF_0_0954, |
168 | ADXL355_HPF_0_0238, |
169 | }; |
170 | |
171 | static const int adxl355_odr_table[][2] = { |
172 | [0] = {4000, 0}, |
173 | [1] = {2000, 0}, |
174 | [2] = {1000, 0}, |
175 | [3] = {500, 0}, |
176 | [4] = {250, 0}, |
177 | [5] = {125, 0}, |
178 | [6] = {62, 500000}, |
179 | [7] = {31, 250000}, |
180 | [8] = {15, 625000}, |
181 | [9] = {7, 813000}, |
182 | [10] = {3, 906000}, |
183 | }; |
184 | |
185 | static const int adxl355_hpf_3db_multipliers[] = { |
186 | 0, |
187 | 247000, |
188 | 62084, |
189 | 15545, |
190 | 3862, |
191 | 954, |
192 | 238, |
193 | }; |
194 | |
195 | enum adxl355_chans { |
196 | chan_x, chan_y, chan_z, |
197 | }; |
198 | |
199 | struct adxl355_chan_info { |
200 | u8 data_reg; |
201 | u8 offset_reg; |
202 | }; |
203 | |
204 | static const struct adxl355_chan_info adxl355_chans[] = { |
205 | [chan_x] = { |
206 | .data_reg = ADXL355_XDATA3_REG, |
207 | .offset_reg = ADXL355_OFFSET_X_H_REG |
208 | }, |
209 | [chan_y] = { |
210 | .data_reg = ADXL355_YDATA3_REG, |
211 | .offset_reg = ADXL355_OFFSET_Y_H_REG |
212 | }, |
213 | [chan_z] = { |
214 | .data_reg = ADXL355_ZDATA3_REG, |
215 | .offset_reg = ADXL355_OFFSET_Z_H_REG |
216 | }, |
217 | }; |
218 | |
219 | struct adxl355_data { |
220 | const struct adxl355_chip_info *chip_info; |
221 | struct regmap *regmap; |
222 | struct device *dev; |
223 | struct mutex lock; /* lock to protect op_mode */ |
224 | enum adxl355_op_mode op_mode; |
225 | enum adxl355_odr odr; |
226 | enum adxl355_hpf_3db hpf_3db; |
227 | int calibbias[3]; |
228 | int adxl355_hpf_3db_table[7][2]; |
229 | struct iio_trigger *dready_trig; |
230 | union { |
231 | u8 transf_buf[3]; |
232 | struct { |
233 | u8 buf[14]; |
234 | s64 ts; |
235 | } buffer; |
236 | } __aligned(IIO_DMA_MINALIGN); |
237 | }; |
238 | |
239 | static int adxl355_set_op_mode(struct adxl355_data *data, |
240 | enum adxl355_op_mode op_mode) |
241 | { |
242 | int ret; |
243 | |
244 | if (data->op_mode == op_mode) |
245 | return 0; |
246 | |
247 | ret = regmap_update_bits(map: data->regmap, ADXL355_POWER_CTL_REG, |
248 | ADXL355_POWER_CTL_MODE_MSK, val: op_mode); |
249 | if (ret) |
250 | return ret; |
251 | |
252 | data->op_mode = op_mode; |
253 | |
254 | return ret; |
255 | } |
256 | |
257 | static int adxl355_data_rdy_trigger_set_state(struct iio_trigger *trig, |
258 | bool state) |
259 | { |
260 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); |
261 | struct adxl355_data *data = iio_priv(indio_dev); |
262 | int ret; |
263 | |
264 | mutex_lock(&data->lock); |
265 | ret = regmap_update_bits(map: data->regmap, ADXL355_POWER_CTL_REG, |
266 | ADXL355_POWER_CTL_DRDY_MSK, |
267 | FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, |
268 | state ? 0 : 1)); |
269 | mutex_unlock(lock: &data->lock); |
270 | |
271 | return ret; |
272 | } |
273 | |
274 | static void adxl355_fill_3db_frequency_table(struct adxl355_data *data) |
275 | { |
276 | u32 multiplier; |
277 | u64 div, rem; |
278 | u64 odr; |
279 | int i; |
280 | |
281 | odr = mul_u64_u32_shr(a: adxl355_odr_table[data->odr][0], MEGA, shift: 0) + |
282 | adxl355_odr_table[data->odr][1]; |
283 | |
284 | for (i = 0; i < ARRAY_SIZE(adxl355_hpf_3db_multipliers); i++) { |
285 | multiplier = adxl355_hpf_3db_multipliers[i]; |
286 | div = div64_u64_rem(dividend: mul_u64_u32_shr(a: odr, mul: multiplier, shift: 0), |
287 | TERA * 100, remainder: &rem); |
288 | |
289 | data->adxl355_hpf_3db_table[i][0] = div; |
290 | data->adxl355_hpf_3db_table[i][1] = div_u64(dividend: rem, MEGA * 100); |
291 | } |
292 | } |
293 | |
294 | static int adxl355_setup(struct adxl355_data *data) |
295 | { |
296 | unsigned int regval; |
297 | int ret; |
298 | |
299 | ret = regmap_read(map: data->regmap, ADXL355_DEVID_AD_REG, val: ®val); |
300 | if (ret) |
301 | return ret; |
302 | |
303 | if (regval != ADXL355_DEVID_AD_VAL) { |
304 | dev_err(data->dev, "Invalid ADI ID 0x%02x\n" , regval); |
305 | return -ENODEV; |
306 | } |
307 | |
308 | ret = regmap_read(map: data->regmap, ADXL355_DEVID_MST_REG, val: ®val); |
309 | if (ret) |
310 | return ret; |
311 | |
312 | if (regval != ADXL355_DEVID_MST_VAL) { |
313 | dev_err(data->dev, "Invalid MEMS ID 0x%02x\n" , regval); |
314 | return -ENODEV; |
315 | } |
316 | |
317 | ret = regmap_read(map: data->regmap, ADXL355_PARTID_REG, val: ®val); |
318 | if (ret) |
319 | return ret; |
320 | |
321 | if (regval != ADXL355_PARTID_VAL) |
322 | dev_warn(data->dev, "Invalid DEV ID 0x%02x\n" , regval); |
323 | |
324 | /* |
325 | * Perform a software reset to make sure the device is in a consistent |
326 | * state after start-up. |
327 | */ |
328 | ret = regmap_write(map: data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE); |
329 | if (ret) |
330 | return ret; |
331 | |
332 | ret = regmap_update_bits(map: data->regmap, ADXL355_POWER_CTL_REG, |
333 | ADXL355_POWER_CTL_DRDY_MSK, |
334 | FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1)); |
335 | if (ret) |
336 | return ret; |
337 | |
338 | adxl355_fill_3db_frequency_table(data); |
339 | |
340 | return adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
341 | } |
342 | |
343 | static int adxl355_get_temp_data(struct adxl355_data *data, u8 addr) |
344 | { |
345 | return regmap_bulk_read(map: data->regmap, reg: addr, val: data->transf_buf, val_count: 2); |
346 | } |
347 | |
348 | static int adxl355_read_axis(struct adxl355_data *data, u8 addr) |
349 | { |
350 | int ret; |
351 | |
352 | ret = regmap_bulk_read(map: data->regmap, reg: addr, val: data->transf_buf, |
353 | ARRAY_SIZE(data->transf_buf)); |
354 | if (ret) |
355 | return ret; |
356 | |
357 | return get_unaligned_be24(p: data->transf_buf); |
358 | } |
359 | |
360 | static int adxl355_find_match(const int (*freq_tbl)[2], const int n, |
361 | const int val, const int val2) |
362 | { |
363 | int i; |
364 | |
365 | for (i = 0; i < n; i++) { |
366 | if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) |
367 | return i; |
368 | } |
369 | |
370 | return -EINVAL; |
371 | } |
372 | |
373 | static int adxl355_set_odr(struct adxl355_data *data, |
374 | enum adxl355_odr odr) |
375 | { |
376 | int ret; |
377 | |
378 | mutex_lock(&data->lock); |
379 | |
380 | if (data->odr == odr) { |
381 | mutex_unlock(lock: &data->lock); |
382 | return 0; |
383 | } |
384 | |
385 | ret = adxl355_set_op_mode(data, op_mode: ADXL355_STANDBY); |
386 | if (ret) |
387 | goto err_unlock; |
388 | |
389 | ret = regmap_update_bits(map: data->regmap, ADXL355_FILTER_REG, |
390 | ADXL355_FILTER_ODR_MSK, |
391 | FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr)); |
392 | if (ret) |
393 | goto err_set_opmode; |
394 | |
395 | data->odr = odr; |
396 | adxl355_fill_3db_frequency_table(data); |
397 | |
398 | ret = adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
399 | if (ret) |
400 | goto err_set_opmode; |
401 | |
402 | mutex_unlock(lock: &data->lock); |
403 | return 0; |
404 | |
405 | err_set_opmode: |
406 | adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
407 | err_unlock: |
408 | mutex_unlock(lock: &data->lock); |
409 | return ret; |
410 | } |
411 | |
412 | static int adxl355_set_hpf_3db(struct adxl355_data *data, |
413 | enum adxl355_hpf_3db hpf) |
414 | { |
415 | int ret; |
416 | |
417 | mutex_lock(&data->lock); |
418 | |
419 | if (data->hpf_3db == hpf) { |
420 | mutex_unlock(lock: &data->lock); |
421 | return 0; |
422 | } |
423 | |
424 | ret = adxl355_set_op_mode(data, op_mode: ADXL355_STANDBY); |
425 | if (ret) |
426 | goto err_unlock; |
427 | |
428 | ret = regmap_update_bits(map: data->regmap, ADXL355_FILTER_REG, |
429 | ADXL355_FILTER_HPF_MSK, |
430 | FIELD_PREP(ADXL355_FILTER_HPF_MSK, hpf)); |
431 | if (ret) |
432 | goto err_set_opmode; |
433 | |
434 | data->hpf_3db = hpf; |
435 | |
436 | ret = adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
437 | if (ret) |
438 | goto err_set_opmode; |
439 | |
440 | mutex_unlock(lock: &data->lock); |
441 | return 0; |
442 | |
443 | err_set_opmode: |
444 | adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
445 | err_unlock: |
446 | mutex_unlock(lock: &data->lock); |
447 | return ret; |
448 | } |
449 | |
450 | static int adxl355_set_calibbias(struct adxl355_data *data, |
451 | enum adxl355_chans chan, int calibbias) |
452 | { |
453 | int ret; |
454 | |
455 | mutex_lock(&data->lock); |
456 | |
457 | ret = adxl355_set_op_mode(data, op_mode: ADXL355_STANDBY); |
458 | if (ret) |
459 | goto err_unlock; |
460 | |
461 | put_unaligned_be16(val: calibbias, p: data->transf_buf); |
462 | ret = regmap_bulk_write(map: data->regmap, |
463 | reg: adxl355_chans[chan].offset_reg, |
464 | val: data->transf_buf, val_count: 2); |
465 | if (ret) |
466 | goto err_set_opmode; |
467 | |
468 | data->calibbias[chan] = calibbias; |
469 | |
470 | ret = adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
471 | if (ret) |
472 | goto err_set_opmode; |
473 | |
474 | mutex_unlock(lock: &data->lock); |
475 | return 0; |
476 | |
477 | err_set_opmode: |
478 | adxl355_set_op_mode(data, op_mode: ADXL355_MEASUREMENT); |
479 | err_unlock: |
480 | mutex_unlock(lock: &data->lock); |
481 | return ret; |
482 | } |
483 | |
484 | static int adxl355_read_raw(struct iio_dev *indio_dev, |
485 | struct iio_chan_spec const *chan, |
486 | int *val, int *val2, long mask) |
487 | { |
488 | struct adxl355_data *data = iio_priv(indio_dev); |
489 | int ret; |
490 | |
491 | switch (mask) { |
492 | case IIO_CHAN_INFO_RAW: |
493 | switch (chan->type) { |
494 | case IIO_TEMP: |
495 | ret = adxl355_get_temp_data(data, addr: chan->address); |
496 | if (ret < 0) |
497 | return ret; |
498 | *val = get_unaligned_be16(p: data->transf_buf); |
499 | |
500 | return IIO_VAL_INT; |
501 | case IIO_ACCEL: |
502 | ret = adxl355_read_axis(data, addr: adxl355_chans[ |
503 | chan->address].data_reg); |
504 | if (ret < 0) |
505 | return ret; |
506 | *val = sign_extend32(value: ret >> chan->scan_type.shift, |
507 | index: chan->scan_type.realbits - 1); |
508 | return IIO_VAL_INT; |
509 | default: |
510 | return -EINVAL; |
511 | } |
512 | |
513 | case IIO_CHAN_INFO_SCALE: |
514 | switch (chan->type) { |
515 | case IIO_TEMP: |
516 | /* |
517 | * Temperature scale is -110.497238. |
518 | * See the detailed explanation in adxl35x_chip_info |
519 | * definition above. |
520 | */ |
521 | *val = -110; |
522 | *val2 = 497238; |
523 | return IIO_VAL_INT_PLUS_MICRO; |
524 | case IIO_ACCEL: |
525 | *val = data->chip_info->accel_scale.integer; |
526 | *val2 = data->chip_info->accel_scale.decimal; |
527 | return IIO_VAL_INT_PLUS_NANO; |
528 | default: |
529 | return -EINVAL; |
530 | } |
531 | case IIO_CHAN_INFO_OFFSET: |
532 | *val = data->chip_info->temp_offset.integer; |
533 | *val2 = data->chip_info->temp_offset.decimal; |
534 | return IIO_VAL_INT_PLUS_MICRO; |
535 | case IIO_CHAN_INFO_CALIBBIAS: |
536 | *val = sign_extend32(value: data->calibbias[chan->address], index: 15); |
537 | return IIO_VAL_INT; |
538 | case IIO_CHAN_INFO_SAMP_FREQ: |
539 | *val = adxl355_odr_table[data->odr][0]; |
540 | *val2 = adxl355_odr_table[data->odr][1]; |
541 | return IIO_VAL_INT_PLUS_MICRO; |
542 | case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
543 | *val = data->adxl355_hpf_3db_table[data->hpf_3db][0]; |
544 | *val2 = data->adxl355_hpf_3db_table[data->hpf_3db][1]; |
545 | return IIO_VAL_INT_PLUS_MICRO; |
546 | default: |
547 | return -EINVAL; |
548 | } |
549 | } |
550 | |
551 | static int adxl355_write_raw(struct iio_dev *indio_dev, |
552 | struct iio_chan_spec const *chan, |
553 | int val, int val2, long mask) |
554 | { |
555 | struct adxl355_data *data = iio_priv(indio_dev); |
556 | int odr_idx, hpf_idx, calibbias; |
557 | |
558 | switch (mask) { |
559 | case IIO_CHAN_INFO_SAMP_FREQ: |
560 | odr_idx = adxl355_find_match(freq_tbl: adxl355_odr_table, |
561 | ARRAY_SIZE(adxl355_odr_table), |
562 | val, val2); |
563 | if (odr_idx < 0) |
564 | return odr_idx; |
565 | |
566 | return adxl355_set_odr(data, odr: odr_idx); |
567 | case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
568 | hpf_idx = adxl355_find_match(freq_tbl: data->adxl355_hpf_3db_table, |
569 | ARRAY_SIZE(data->adxl355_hpf_3db_table), |
570 | val, val2); |
571 | if (hpf_idx < 0) |
572 | return hpf_idx; |
573 | |
574 | return adxl355_set_hpf_3db(data, hpf: hpf_idx); |
575 | case IIO_CHAN_INFO_CALIBBIAS: |
576 | calibbias = clamp_t(int, val, S16_MIN, S16_MAX); |
577 | |
578 | return adxl355_set_calibbias(data, chan: chan->address, calibbias); |
579 | default: |
580 | return -EINVAL; |
581 | } |
582 | } |
583 | |
584 | static int adxl355_read_avail(struct iio_dev *indio_dev, |
585 | struct iio_chan_spec const *chan, |
586 | const int **vals, int *type, int *length, |
587 | long mask) |
588 | { |
589 | struct adxl355_data *data = iio_priv(indio_dev); |
590 | |
591 | switch (mask) { |
592 | case IIO_CHAN_INFO_SAMP_FREQ: |
593 | *vals = (const int *)adxl355_odr_table; |
594 | *type = IIO_VAL_INT_PLUS_MICRO; |
595 | /* Values are stored in a 2D matrix */ |
596 | *length = ARRAY_SIZE(adxl355_odr_table) * 2; |
597 | |
598 | return IIO_AVAIL_LIST; |
599 | case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: |
600 | *vals = (const int *)data->adxl355_hpf_3db_table; |
601 | *type = IIO_VAL_INT_PLUS_MICRO; |
602 | /* Values are stored in a 2D matrix */ |
603 | *length = ARRAY_SIZE(data->adxl355_hpf_3db_table) * 2; |
604 | |
605 | return IIO_AVAIL_LIST; |
606 | default: |
607 | return -EINVAL; |
608 | } |
609 | } |
610 | |
611 | static const unsigned long adxl355_avail_scan_masks[] = { |
612 | GENMASK(3, 0), |
613 | 0 |
614 | }; |
615 | |
616 | static const struct iio_info adxl355_info = { |
617 | .read_raw = adxl355_read_raw, |
618 | .write_raw = adxl355_write_raw, |
619 | .read_avail = &adxl355_read_avail, |
620 | }; |
621 | |
622 | static const struct iio_trigger_ops adxl355_trigger_ops = { |
623 | .set_trigger_state = &adxl355_data_rdy_trigger_set_state, |
624 | .validate_device = &iio_trigger_validate_own_device, |
625 | }; |
626 | |
627 | static irqreturn_t adxl355_trigger_handler(int irq, void *p) |
628 | { |
629 | struct iio_poll_func *pf = p; |
630 | struct iio_dev *indio_dev = pf->indio_dev; |
631 | struct adxl355_data *data = iio_priv(indio_dev); |
632 | int ret; |
633 | |
634 | mutex_lock(&data->lock); |
635 | |
636 | /* |
637 | * data->buffer is used both for triggered buffer support |
638 | * and read/write_raw(), hence, it has to be zeroed here before usage. |
639 | */ |
640 | data->buffer.buf[0] = 0; |
641 | |
642 | /* |
643 | * The acceleration data is 24 bits and big endian. It has to be saved |
644 | * in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer. |
645 | * The buf array is 14 bytes as it includes 3x4=12 bytes for |
646 | * accelaration data of x, y, and z axis. It also includes 2 bytes for |
647 | * temperature data. |
648 | */ |
649 | ret = regmap_bulk_read(map: data->regmap, ADXL355_XDATA3_REG, |
650 | val: &data->buffer.buf[1], val_count: 3); |
651 | if (ret) |
652 | goto out_unlock_notify; |
653 | |
654 | ret = regmap_bulk_read(map: data->regmap, ADXL355_YDATA3_REG, |
655 | val: &data->buffer.buf[5], val_count: 3); |
656 | if (ret) |
657 | goto out_unlock_notify; |
658 | |
659 | ret = regmap_bulk_read(map: data->regmap, ADXL355_ZDATA3_REG, |
660 | val: &data->buffer.buf[9], val_count: 3); |
661 | if (ret) |
662 | goto out_unlock_notify; |
663 | |
664 | ret = regmap_bulk_read(map: data->regmap, ADXL355_TEMP2_REG, |
665 | val: &data->buffer.buf[12], val_count: 2); |
666 | if (ret) |
667 | goto out_unlock_notify; |
668 | |
669 | iio_push_to_buffers_with_timestamp(indio_dev, data: &data->buffer, |
670 | timestamp: pf->timestamp); |
671 | |
672 | out_unlock_notify: |
673 | mutex_unlock(lock: &data->lock); |
674 | iio_trigger_notify_done(trig: indio_dev->trig); |
675 | |
676 | return IRQ_HANDLED; |
677 | } |
678 | |
679 | #define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \ |
680 | .type = IIO_ACCEL, \ |
681 | .address = reg, \ |
682 | .modified = 1, \ |
683 | .channel2 = IIO_MOD_##axis, \ |
684 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
685 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ |
686 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ |
687 | BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ |
688 | BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ |
689 | .info_mask_shared_by_type_available = \ |
690 | BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ |
691 | BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ |
692 | .scan_index = index, \ |
693 | .scan_type = { \ |
694 | .sign = 's', \ |
695 | .realbits = 20, \ |
696 | .storagebits = 32, \ |
697 | .shift = 4, \ |
698 | .endianness = IIO_BE, \ |
699 | } \ |
700 | } |
701 | |
702 | static const struct iio_chan_spec adxl355_channels[] = { |
703 | ADXL355_ACCEL_CHANNEL(0, chan_x, X), |
704 | ADXL355_ACCEL_CHANNEL(1, chan_y, Y), |
705 | ADXL355_ACCEL_CHANNEL(2, chan_z, Z), |
706 | { |
707 | .type = IIO_TEMP, |
708 | .address = ADXL355_TEMP2_REG, |
709 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
710 | BIT(IIO_CHAN_INFO_SCALE) | |
711 | BIT(IIO_CHAN_INFO_OFFSET), |
712 | .scan_index = 3, |
713 | .scan_type = { |
714 | .sign = 's', |
715 | .realbits = 12, |
716 | .storagebits = 16, |
717 | .endianness = IIO_BE, |
718 | }, |
719 | }, |
720 | IIO_CHAN_SOFT_TIMESTAMP(4), |
721 | }; |
722 | |
723 | static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) |
724 | { |
725 | struct adxl355_data *data = iio_priv(indio_dev); |
726 | int ret; |
727 | |
728 | data->dready_trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d" , |
729 | indio_dev->name, |
730 | iio_device_id(indio_dev)); |
731 | if (!data->dready_trig) |
732 | return -ENOMEM; |
733 | |
734 | data->dready_trig->ops = &adxl355_trigger_ops; |
735 | iio_trigger_set_drvdata(trig: data->dready_trig, data: indio_dev); |
736 | |
737 | ret = devm_request_irq(dev: data->dev, irq, |
738 | handler: &iio_trigger_generic_data_rdy_poll, |
739 | IRQF_ONESHOT, devname: "adxl355_irq" , dev_id: data->dready_trig); |
740 | if (ret) |
741 | return dev_err_probe(dev: data->dev, err: ret, fmt: "request irq %d failed\n" , |
742 | irq); |
743 | |
744 | ret = devm_iio_trigger_register(dev: data->dev, trig_info: data->dready_trig); |
745 | if (ret) { |
746 | dev_err(data->dev, "iio trigger register failed\n" ); |
747 | return ret; |
748 | } |
749 | |
750 | indio_dev->trig = iio_trigger_get(trig: data->dready_trig); |
751 | |
752 | return 0; |
753 | } |
754 | |
755 | int adxl355_core_probe(struct device *dev, struct regmap *regmap, |
756 | const struct adxl355_chip_info *chip_info) |
757 | { |
758 | struct adxl355_data *data; |
759 | struct iio_dev *indio_dev; |
760 | int ret; |
761 | int irq; |
762 | |
763 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*data)); |
764 | if (!indio_dev) |
765 | return -ENOMEM; |
766 | |
767 | data = iio_priv(indio_dev); |
768 | data->regmap = regmap; |
769 | data->dev = dev; |
770 | data->op_mode = ADXL355_STANDBY; |
771 | data->chip_info = chip_info; |
772 | mutex_init(&data->lock); |
773 | |
774 | indio_dev->name = chip_info->name; |
775 | indio_dev->info = &adxl355_info; |
776 | indio_dev->modes = INDIO_DIRECT_MODE; |
777 | indio_dev->channels = adxl355_channels; |
778 | indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); |
779 | indio_dev->available_scan_masks = adxl355_avail_scan_masks; |
780 | |
781 | ret = adxl355_setup(data); |
782 | if (ret) { |
783 | dev_err(dev, "ADXL355 setup failed\n" ); |
784 | return ret; |
785 | } |
786 | |
787 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, |
788 | &iio_pollfunc_store_time, |
789 | &adxl355_trigger_handler, NULL); |
790 | if (ret) { |
791 | dev_err(dev, "iio triggered buffer setup failed\n" ); |
792 | return ret; |
793 | } |
794 | |
795 | irq = fwnode_irq_get_byname(dev_fwnode(dev), name: "DRDY" ); |
796 | if (irq > 0) { |
797 | ret = adxl355_probe_trigger(indio_dev, irq); |
798 | if (ret) |
799 | return ret; |
800 | } |
801 | |
802 | return devm_iio_device_register(dev, indio_dev); |
803 | } |
804 | EXPORT_SYMBOL_NS_GPL(adxl355_core_probe, IIO_ADXL355); |
805 | |
806 | MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>" ); |
807 | MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver" ); |
808 | MODULE_LICENSE("GPL v2" ); |
809 | |