1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ADXL313 3-Axis Digital Accelerometer |
4 | * |
5 | * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> |
6 | * |
7 | * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf |
8 | */ |
9 | |
10 | #include <linux/bitfield.h> |
11 | #include <linux/module.h> |
12 | #include <linux/regmap.h> |
13 | |
14 | #include "adxl313.h" |
15 | |
16 | static const struct regmap_range adxl312_readable_reg_range[] = { |
17 | regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0), |
18 | regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), |
19 | regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), |
20 | regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), |
21 | }; |
22 | |
23 | static const struct regmap_range adxl313_readable_reg_range[] = { |
24 | regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID), |
25 | regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), |
26 | regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), |
27 | regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), |
28 | regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), |
29 | }; |
30 | |
31 | const struct regmap_access_table adxl312_readable_regs_table = { |
32 | .yes_ranges = adxl312_readable_reg_range, |
33 | .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range), |
34 | }; |
35 | EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL313); |
36 | |
37 | const struct regmap_access_table adxl313_readable_regs_table = { |
38 | .yes_ranges = adxl313_readable_reg_range, |
39 | .n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range), |
40 | }; |
41 | EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313); |
42 | |
43 | const struct regmap_access_table adxl314_readable_regs_table = { |
44 | .yes_ranges = adxl312_readable_reg_range, |
45 | .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range), |
46 | }; |
47 | EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313); |
48 | |
49 | static int adxl312_check_id(struct device *dev, |
50 | struct adxl313_data *data) |
51 | { |
52 | unsigned int regval; |
53 | int ret; |
54 | |
55 | ret = regmap_read(map: data->regmap, ADXL313_REG_DEVID0, val: ®val); |
56 | if (ret) |
57 | return ret; |
58 | |
59 | if (regval != ADXL313_DEVID0_ADXL312_314) |
60 | dev_warn(dev, "Invalid manufacturer ID: %#02x\n" , regval); |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static int adxl313_check_id(struct device *dev, |
66 | struct adxl313_data *data) |
67 | { |
68 | unsigned int regval; |
69 | int ret; |
70 | |
71 | ret = regmap_read(map: data->regmap, ADXL313_REG_DEVID0, val: ®val); |
72 | if (ret) |
73 | return ret; |
74 | |
75 | if (regval != ADXL313_DEVID0) |
76 | dev_warn(dev, "Invalid manufacturer ID: 0x%02x\n" , regval); |
77 | |
78 | /* Check DEVID1 and PARTID */ |
79 | if (regval == ADXL313_DEVID0) { |
80 | ret = regmap_read(map: data->regmap, ADXL313_REG_DEVID1, val: ®val); |
81 | if (ret) |
82 | return ret; |
83 | |
84 | if (regval != ADXL313_DEVID1) |
85 | dev_warn(dev, "Invalid mems ID: 0x%02x\n" , regval); |
86 | |
87 | ret = regmap_read(map: data->regmap, ADXL313_REG_PARTID, val: ®val); |
88 | if (ret) |
89 | return ret; |
90 | |
91 | if (regval != ADXL313_PARTID) |
92 | dev_warn(dev, "Invalid device ID: 0x%02x\n" , regval); |
93 | } |
94 | |
95 | return 0; |
96 | } |
97 | |
98 | const struct adxl313_chip_info adxl31x_chip_info[] = { |
99 | [ADXL312] = { |
100 | .name = "adxl312" , |
101 | .type = ADXL312, |
102 | .scale_factor = 28425072, |
103 | .variable_range = true, |
104 | .soft_reset = false, |
105 | .check_id = &adxl312_check_id, |
106 | }, |
107 | [ADXL313] = { |
108 | .name = "adxl313" , |
109 | .type = ADXL313, |
110 | .scale_factor = 9576806, |
111 | .variable_range = true, |
112 | .soft_reset = true, |
113 | .check_id = &adxl313_check_id, |
114 | }, |
115 | [ADXL314] = { |
116 | .name = "adxl314" , |
117 | .type = ADXL314, |
118 | .scale_factor = 478858719, |
119 | .variable_range = false, |
120 | .soft_reset = false, |
121 | .check_id = &adxl312_check_id, |
122 | }, |
123 | }; |
124 | EXPORT_SYMBOL_NS_GPL(adxl31x_chip_info, IIO_ADXL313); |
125 | |
126 | static const struct regmap_range adxl312_writable_reg_range[] = { |
127 | regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), |
128 | regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), |
129 | regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP), |
130 | regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT), |
131 | regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), |
132 | }; |
133 | |
134 | static const struct regmap_range adxl313_writable_reg_range[] = { |
135 | regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), |
136 | regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), |
137 | regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), |
138 | regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP), |
139 | regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT), |
140 | regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), |
141 | }; |
142 | |
143 | const struct regmap_access_table adxl312_writable_regs_table = { |
144 | .yes_ranges = adxl312_writable_reg_range, |
145 | .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range), |
146 | }; |
147 | EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL313); |
148 | |
149 | const struct regmap_access_table adxl313_writable_regs_table = { |
150 | .yes_ranges = adxl313_writable_reg_range, |
151 | .n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range), |
152 | }; |
153 | EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313); |
154 | |
155 | const struct regmap_access_table adxl314_writable_regs_table = { |
156 | .yes_ranges = adxl312_writable_reg_range, |
157 | .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range), |
158 | }; |
159 | EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL313); |
160 | |
161 | static const int adxl313_odr_freqs[][2] = { |
162 | [0] = { 6, 250000 }, |
163 | [1] = { 12, 500000 }, |
164 | [2] = { 25, 0 }, |
165 | [3] = { 50, 0 }, |
166 | [4] = { 100, 0 }, |
167 | [5] = { 200, 0 }, |
168 | [6] = { 400, 0 }, |
169 | [7] = { 800, 0 }, |
170 | [8] = { 1600, 0 }, |
171 | [9] = { 3200, 0 }, |
172 | }; |
173 | |
174 | #define ADXL313_ACCEL_CHANNEL(index, axis) { \ |
175 | .type = IIO_ACCEL, \ |
176 | .address = index, \ |
177 | .modified = 1, \ |
178 | .channel2 = IIO_MOD_##axis, \ |
179 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
180 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ |
181 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ |
182 | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ |
183 | .info_mask_shared_by_type_available = \ |
184 | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ |
185 | .scan_type = { \ |
186 | .realbits = 13, \ |
187 | }, \ |
188 | } |
189 | |
190 | static const struct iio_chan_spec adxl313_channels[] = { |
191 | ADXL313_ACCEL_CHANNEL(0, X), |
192 | ADXL313_ACCEL_CHANNEL(1, Y), |
193 | ADXL313_ACCEL_CHANNEL(2, Z), |
194 | }; |
195 | |
196 | static int adxl313_set_odr(struct adxl313_data *data, |
197 | unsigned int freq1, unsigned int freq2) |
198 | { |
199 | unsigned int i; |
200 | |
201 | for (i = 0; i < ARRAY_SIZE(adxl313_odr_freqs); i++) { |
202 | if (adxl313_odr_freqs[i][0] == freq1 && |
203 | adxl313_odr_freqs[i][1] == freq2) |
204 | break; |
205 | } |
206 | |
207 | if (i == ARRAY_SIZE(adxl313_odr_freqs)) |
208 | return -EINVAL; |
209 | |
210 | return regmap_update_bits(map: data->regmap, ADXL313_REG_BW_RATE, |
211 | ADXL313_RATE_MSK, |
212 | FIELD_PREP(ADXL313_RATE_MSK, ADXL313_RATE_BASE + i)); |
213 | } |
214 | |
215 | static int adxl313_read_axis(struct adxl313_data *data, |
216 | struct iio_chan_spec const *chan) |
217 | { |
218 | int ret; |
219 | |
220 | mutex_lock(&data->lock); |
221 | |
222 | ret = regmap_bulk_read(map: data->regmap, |
223 | ADXL313_REG_DATA_AXIS(chan->address), |
224 | val: &data->transf_buf, val_count: sizeof(data->transf_buf)); |
225 | if (ret) |
226 | goto unlock_ret; |
227 | |
228 | ret = le16_to_cpu(data->transf_buf); |
229 | |
230 | unlock_ret: |
231 | mutex_unlock(lock: &data->lock); |
232 | return ret; |
233 | } |
234 | |
235 | static int adxl313_read_freq_avail(struct iio_dev *indio_dev, |
236 | struct iio_chan_spec const *chan, |
237 | const int **vals, int *type, int *length, |
238 | long mask) |
239 | { |
240 | switch (mask) { |
241 | case IIO_CHAN_INFO_SAMP_FREQ: |
242 | *vals = (const int *)adxl313_odr_freqs; |
243 | *length = ARRAY_SIZE(adxl313_odr_freqs) * 2; |
244 | *type = IIO_VAL_INT_PLUS_MICRO; |
245 | return IIO_AVAIL_LIST; |
246 | default: |
247 | return -EINVAL; |
248 | } |
249 | } |
250 | |
251 | static int adxl313_read_raw(struct iio_dev *indio_dev, |
252 | struct iio_chan_spec const *chan, |
253 | int *val, int *val2, long mask) |
254 | { |
255 | struct adxl313_data *data = iio_priv(indio_dev); |
256 | unsigned int regval; |
257 | int ret; |
258 | |
259 | switch (mask) { |
260 | case IIO_CHAN_INFO_RAW: |
261 | ret = adxl313_read_axis(data, chan); |
262 | if (ret < 0) |
263 | return ret; |
264 | |
265 | *val = sign_extend32(value: ret, index: chan->scan_type.realbits - 1); |
266 | return IIO_VAL_INT; |
267 | case IIO_CHAN_INFO_SCALE: |
268 | *val = 0; |
269 | |
270 | *val2 = data->chip_info->scale_factor; |
271 | |
272 | return IIO_VAL_INT_PLUS_NANO; |
273 | case IIO_CHAN_INFO_CALIBBIAS: |
274 | ret = regmap_read(map: data->regmap, |
275 | ADXL313_REG_OFS_AXIS(chan->address), val: ®val); |
276 | if (ret) |
277 | return ret; |
278 | |
279 | /* |
280 | * 8-bit resolution at minimum range, that is 4x accel data scale |
281 | * factor at full resolution |
282 | */ |
283 | *val = sign_extend32(value: regval, index: 7) * 4; |
284 | return IIO_VAL_INT; |
285 | case IIO_CHAN_INFO_SAMP_FREQ: |
286 | ret = regmap_read(map: data->regmap, ADXL313_REG_BW_RATE, val: ®val); |
287 | if (ret) |
288 | return ret; |
289 | |
290 | ret = FIELD_GET(ADXL313_RATE_MSK, regval) - ADXL313_RATE_BASE; |
291 | *val = adxl313_odr_freqs[ret][0]; |
292 | *val2 = adxl313_odr_freqs[ret][1]; |
293 | return IIO_VAL_INT_PLUS_MICRO; |
294 | default: |
295 | return -EINVAL; |
296 | } |
297 | } |
298 | |
299 | static int adxl313_write_raw(struct iio_dev *indio_dev, |
300 | struct iio_chan_spec const *chan, |
301 | int val, int val2, long mask) |
302 | { |
303 | struct adxl313_data *data = iio_priv(indio_dev); |
304 | |
305 | switch (mask) { |
306 | case IIO_CHAN_INFO_CALIBBIAS: |
307 | /* |
308 | * 8-bit resolution at minimum range, that is 4x accel data scale |
309 | * factor at full resolution |
310 | */ |
311 | if (clamp_val(val, -128 * 4, 127 * 4) != val) |
312 | return -EINVAL; |
313 | |
314 | return regmap_write(map: data->regmap, |
315 | ADXL313_REG_OFS_AXIS(chan->address), |
316 | val: val / 4); |
317 | case IIO_CHAN_INFO_SAMP_FREQ: |
318 | return adxl313_set_odr(data, freq1: val, freq2: val2); |
319 | default: |
320 | return -EINVAL; |
321 | } |
322 | } |
323 | |
324 | static const struct iio_info adxl313_info = { |
325 | .read_raw = adxl313_read_raw, |
326 | .write_raw = adxl313_write_raw, |
327 | .read_avail = adxl313_read_freq_avail, |
328 | }; |
329 | |
330 | static int adxl313_setup(struct device *dev, struct adxl313_data *data, |
331 | int (*setup)(struct device *, struct regmap *)) |
332 | { |
333 | int ret; |
334 | |
335 | /* |
336 | * If sw reset available, ensures the device is in a consistent |
337 | * state after start up |
338 | */ |
339 | if (data->chip_info->soft_reset) { |
340 | ret = regmap_write(map: data->regmap, ADXL313_REG_SOFT_RESET, |
341 | ADXL313_SOFT_RESET); |
342 | if (ret) |
343 | return ret; |
344 | } |
345 | |
346 | if (setup) { |
347 | ret = setup(dev, data->regmap); |
348 | if (ret) |
349 | return ret; |
350 | } |
351 | |
352 | ret = data->chip_info->check_id(dev, data); |
353 | if (ret) |
354 | return ret; |
355 | |
356 | /* Sets the range to maximum, full resolution, if applicable */ |
357 | if (data->chip_info->variable_range) { |
358 | ret = regmap_update_bits(map: data->regmap, ADXL313_REG_DATA_FORMAT, |
359 | ADXL313_RANGE_MSK, |
360 | FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_MAX)); |
361 | if (ret) |
362 | return ret; |
363 | |
364 | /* Enables full resolution */ |
365 | ret = regmap_update_bits(map: data->regmap, ADXL313_REG_DATA_FORMAT, |
366 | ADXL313_FULL_RES, ADXL313_FULL_RES); |
367 | if (ret) |
368 | return ret; |
369 | } |
370 | |
371 | /* Enables measurement mode */ |
372 | return regmap_update_bits(map: data->regmap, ADXL313_REG_POWER_CTL, |
373 | ADXL313_POWER_CTL_MSK, |
374 | ADXL313_MEASUREMENT_MODE); |
375 | } |
376 | |
377 | /** |
378 | * adxl313_core_probe() - probe and setup for adxl313 accelerometer |
379 | * @dev: Driver model representation of the device |
380 | * @regmap: Register map of the device |
381 | * @chip_info: Structure containing device specific data |
382 | * @setup: Setup routine to be executed right before the standard device |
383 | * setup, can also be set to NULL if not required |
384 | * |
385 | * Return: 0 on success, negative errno on error cases |
386 | */ |
387 | int adxl313_core_probe(struct device *dev, |
388 | struct regmap *regmap, |
389 | const struct adxl313_chip_info *chip_info, |
390 | int (*setup)(struct device *, struct regmap *)) |
391 | { |
392 | struct adxl313_data *data; |
393 | struct iio_dev *indio_dev; |
394 | int ret; |
395 | |
396 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*data)); |
397 | if (!indio_dev) |
398 | return -ENOMEM; |
399 | |
400 | data = iio_priv(indio_dev); |
401 | data->regmap = regmap; |
402 | data->chip_info = chip_info; |
403 | |
404 | mutex_init(&data->lock); |
405 | |
406 | indio_dev->name = chip_info->name; |
407 | indio_dev->info = &adxl313_info; |
408 | indio_dev->modes = INDIO_DIRECT_MODE; |
409 | indio_dev->channels = adxl313_channels; |
410 | indio_dev->num_channels = ARRAY_SIZE(adxl313_channels); |
411 | |
412 | ret = adxl313_setup(dev, data, setup); |
413 | if (ret) { |
414 | dev_err(dev, "ADXL313 setup failed\n" ); |
415 | return ret; |
416 | } |
417 | |
418 | return devm_iio_device_register(dev, indio_dev); |
419 | } |
420 | EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, IIO_ADXL313); |
421 | |
422 | MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>" ); |
423 | MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver" ); |
424 | MODULE_LICENSE("GPL v2" ); |
425 | |