diff options
author | Martin Kepplinger <martink@posteo.de> | 2016-04-25 08:08:25 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2016-05-04 06:46:39 -0400 |
commit | ed859fc17d67f4c0ade6f5a58365e621f88de3cf (patch) | |
tree | fe48dab9c9e9d23e26e719606fe0a8f30ef04971 | |
parent | 6994aea7842d824a8cc38c950ddcab3f8a75c278 (diff) |
iio: mma8452: add support for oversampling ratio
This adds the following sysfs files according to the iio ABI:
-rw-r--r-- 4096 in_accel_oversampling_ratio
-r--r--r-- 4096 in_accel_oversampling_ratio_available
Internally, the device knows about 4 different power modes that differ
in oversampling ratio (and power consumption). We just show the user
what oversampling ratio(s) is/are available, depending on the current
frequency.
The referenced table in the datasheets makes it easier to understand.
Signed-off-by: Martin Kepplinger <martink@posteo.de>
Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/accel/mma8452.c | 185 |
1 files changed, 160 insertions, 25 deletions
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index e225d3c53bd5..458c82715427 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c | |||
@@ -76,6 +76,8 @@ | |||
76 | #define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */ | 76 | #define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */ |
77 | #define MMA8452_CTRL_REG2 0x2b | 77 | #define MMA8452_CTRL_REG2 0x2b |
78 | #define MMA8452_CTRL_REG2_RST BIT(6) | 78 | #define MMA8452_CTRL_REG2_RST BIT(6) |
79 | #define MMA8452_CTRL_REG2_MODS_SHIFT 3 | ||
80 | #define MMA8452_CTRL_REG2_MODS_MASK 0x1b | ||
79 | #define MMA8452_CTRL_REG4 0x2d | 81 | #define MMA8452_CTRL_REG4 0x2d |
80 | #define MMA8452_CTRL_REG5 0x2e | 82 | #define MMA8452_CTRL_REG5 0x2e |
81 | #define MMA8452_OFF_X 0x2f | 83 | #define MMA8452_OFF_X 0x2f |
@@ -257,20 +259,17 @@ static const int mma8452_samp_freq[8][2] = { | |||
257 | {6, 250000}, {1, 560000} | 259 | {6, 250000}, {1, 560000} |
258 | }; | 260 | }; |
259 | 261 | ||
260 | /* Datasheet table 35 (step time vs sample frequency) */ | 262 | /* Datasheet table: step time "Relationship with the ODR" (sample frequency) */ |
261 | static const int mma8452_transient_time_step_us[8] = { | 263 | static const int mma8452_transient_time_step_us[4][8] = { |
262 | 1250, | 264 | { 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */ |
263 | 2500, | 265 | { 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */ |
264 | 5000, | 266 | { 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/ |
265 | 10000, | 267 | { 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */ |
266 | 20000, | ||
267 | 20000, | ||
268 | 20000, | ||
269 | 20000 | ||
270 | }; | 268 | }; |
271 | 269 | ||
272 | /* Datasheet table 18 (normal mode) */ | 270 | /* Datasheet table "High-Pass Filter Cutoff Options" */ |
273 | static const int mma8452_hp_filter_cutoff[8][4][2] = { | 271 | static const int mma8452_hp_filter_cutoff[4][8][4][2] = { |
272 | { /* normal */ | ||
274 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ | 273 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ |
275 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ | 274 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ |
276 | { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ | 275 | { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ |
@@ -279,8 +278,61 @@ static const int mma8452_hp_filter_cutoff[8][4][2] = { | |||
279 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ | 278 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ |
280 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ | 279 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ |
281 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ | 280 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ |
281 | }, | ||
282 | { /* low noise low power */ | ||
283 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
284 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
285 | { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, | ||
286 | { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, | ||
287 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, | ||
288 | { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, | ||
289 | { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, | ||
290 | { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} } | ||
291 | }, | ||
292 | { /* high resolution */ | ||
293 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
294 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
295 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
296 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
297 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
298 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
299 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
300 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} } | ||
301 | }, | ||
302 | { /* low power */ | ||
303 | { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, | ||
304 | { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, | ||
305 | { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, | ||
306 | { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, | ||
307 | { {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} }, | ||
308 | { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, | ||
309 | { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, | ||
310 | { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} } | ||
311 | } | ||
282 | }; | 312 | }; |
283 | 313 | ||
314 | /* Datasheet table "MODS Oversampling modes averaging values at each ODR" */ | ||
315 | static const u16 mma8452_os_ratio[4][8] = { | ||
316 | /* 800 Hz, 400 Hz, ... , 1.56 Hz */ | ||
317 | { 2, 4, 4, 4, 4, 16, 32, 128 }, /* normal */ | ||
318 | { 2, 4, 4, 4, 4, 4, 8, 32 }, /* low power low noise */ | ||
319 | { 2, 4, 8, 16, 32, 128, 256, 1024 }, /* high resolution */ | ||
320 | { 2, 2, 2, 2, 2, 2, 4, 16 } /* low power */ | ||
321 | }; | ||
322 | |||
323 | static int mma8452_get_power_mode(struct mma8452_data *data) | ||
324 | { | ||
325 | int reg; | ||
326 | |||
327 | reg = i2c_smbus_read_byte_data(data->client, | ||
328 | MMA8452_CTRL_REG2); | ||
329 | if (reg < 0) | ||
330 | return reg; | ||
331 | |||
332 | return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >> | ||
333 | MMA8452_CTRL_REG2_MODS_SHIFT); | ||
334 | } | ||
335 | |||
284 | static ssize_t mma8452_show_samp_freq_avail(struct device *dev, | 336 | static ssize_t mma8452_show_samp_freq_avail(struct device *dev, |
285 | struct device_attribute *attr, | 337 | struct device_attribute *attr, |
286 | char *buf) | 338 | char *buf) |
@@ -306,10 +358,39 @@ static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, | |||
306 | { | 358 | { |
307 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 359 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
308 | struct mma8452_data *data = iio_priv(indio_dev); | 360 | struct mma8452_data *data = iio_priv(indio_dev); |
361 | int i, j; | ||
362 | |||
363 | i = mma8452_get_odr_index(data); | ||
364 | j = mma8452_get_power_mode(data); | ||
365 | if (j < 0) | ||
366 | return j; | ||
367 | |||
368 | return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i], | ||
369 | ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0])); | ||
370 | } | ||
371 | |||
372 | static ssize_t mma8452_show_os_ratio_avail(struct device *dev, | ||
373 | struct device_attribute *attr, | ||
374 | char *buf) | ||
375 | { | ||
376 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); | ||
377 | struct mma8452_data *data = iio_priv(indio_dev); | ||
309 | int i = mma8452_get_odr_index(data); | 378 | int i = mma8452_get_odr_index(data); |
379 | int j; | ||
380 | u16 val = 0; | ||
381 | size_t len = 0; | ||
382 | |||
383 | for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) { | ||
384 | if (val == mma8452_os_ratio[j][i]) | ||
385 | continue; | ||
386 | |||
387 | val = mma8452_os_ratio[j][i]; | ||
388 | |||
389 | len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val); | ||
390 | } | ||
391 | buf[len - 1] = '\n'; | ||
310 | 392 | ||
311 | return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i], | 393 | return len; |
312 | ARRAY_SIZE(mma8452_hp_filter_cutoff[0])); | ||
313 | } | 394 | } |
314 | 395 | ||
315 | static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); | 396 | static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); |
@@ -317,6 +398,8 @@ static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, | |||
317 | mma8452_show_scale_avail, NULL, 0); | 398 | mma8452_show_scale_avail, NULL, 0); |
318 | static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, | 399 | static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, |
319 | S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); | 400 | S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); |
401 | static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, S_IRUGO, | ||
402 | mma8452_show_os_ratio_avail, NULL, 0); | ||
320 | 403 | ||
321 | static int mma8452_get_samp_freq_index(struct mma8452_data *data, | 404 | static int mma8452_get_samp_freq_index(struct mma8452_data *data, |
322 | int val, int val2) | 405 | int val, int val2) |
@@ -335,24 +418,33 @@ static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2) | |||
335 | static int mma8452_get_hp_filter_index(struct mma8452_data *data, | 418 | static int mma8452_get_hp_filter_index(struct mma8452_data *data, |
336 | int val, int val2) | 419 | int val, int val2) |
337 | { | 420 | { |
338 | int i = mma8452_get_odr_index(data); | 421 | int i, j; |
422 | |||
423 | i = mma8452_get_odr_index(data); | ||
424 | j = mma8452_get_power_mode(data); | ||
425 | if (j < 0) | ||
426 | return j; | ||
339 | 427 | ||
340 | return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], | 428 | return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i], |
341 | ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2); | 429 | ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2); |
342 | } | 430 | } |
343 | 431 | ||
344 | static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) | 432 | static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) |
345 | { | 433 | { |
346 | int i, ret; | 434 | int j, i, ret; |
347 | 435 | ||
348 | ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); | 436 | ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); |
349 | if (ret < 0) | 437 | if (ret < 0) |
350 | return ret; | 438 | return ret; |
351 | 439 | ||
352 | i = mma8452_get_odr_index(data); | 440 | i = mma8452_get_odr_index(data); |
441 | j = mma8452_get_power_mode(data); | ||
442 | if (j < 0) | ||
443 | return j; | ||
444 | |||
353 | ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; | 445 | ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; |
354 | *hz = mma8452_hp_filter_cutoff[i][ret][0]; | 446 | *hz = mma8452_hp_filter_cutoff[j][i][ret][0]; |
355 | *uHz = mma8452_hp_filter_cutoff[i][ret][1]; | 447 | *uHz = mma8452_hp_filter_cutoff[j][i][ret][1]; |
356 | 448 | ||
357 | return 0; | 449 | return 0; |
358 | } | 450 | } |
@@ -414,6 +506,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, | |||
414 | } | 506 | } |
415 | 507 | ||
416 | return IIO_VAL_INT_PLUS_MICRO; | 508 | return IIO_VAL_INT_PLUS_MICRO; |
509 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: | ||
510 | ret = mma8452_get_power_mode(data); | ||
511 | if (ret < 0) | ||
512 | return ret; | ||
513 | |||
514 | i = mma8452_get_odr_index(data); | ||
515 | |||
516 | *val = mma8452_os_ratio[ret][i]; | ||
517 | return IIO_VAL_INT; | ||
417 | } | 518 | } |
418 | 519 | ||
419 | return -EINVAL; | 520 | return -EINVAL; |
@@ -480,6 +581,21 @@ fail: | |||
480 | return ret; | 581 | return ret; |
481 | } | 582 | } |
482 | 583 | ||
584 | static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode) | ||
585 | { | ||
586 | int reg; | ||
587 | |||
588 | reg = i2c_smbus_read_byte_data(data->client, | ||
589 | MMA8452_CTRL_REG2); | ||
590 | if (reg < 0) | ||
591 | return reg; | ||
592 | |||
593 | reg &= ~MMA8452_CTRL_REG2_MODS_MASK; | ||
594 | reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT; | ||
595 | |||
596 | return mma8452_change_config(data, MMA8452_CTRL_REG2, reg); | ||
597 | } | ||
598 | |||
483 | /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */ | 599 | /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */ |
484 | static int mma8452_freefall_mode_enabled(struct mma8452_data *data) | 600 | static int mma8452_freefall_mode_enabled(struct mma8452_data *data) |
485 | { | 601 | { |
@@ -597,6 +713,14 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, | |||
597 | return mma8452_change_config(data, MMA8452_DATA_CFG, | 713 | return mma8452_change_config(data, MMA8452_DATA_CFG, |
598 | data->data_cfg); | 714 | data->data_cfg); |
599 | 715 | ||
716 | case IIO_CHAN_INFO_OVERSAMPLING_RATIO: | ||
717 | ret = mma8452_get_odr_index(data); | ||
718 | |||
719 | for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) { | ||
720 | if (mma8452_os_ratio[i][ret] == val) | ||
721 | return mma8452_set_power_mode(data, i); | ||
722 | } | ||
723 | |||
600 | default: | 724 | default: |
601 | return -EINVAL; | 725 | return -EINVAL; |
602 | } | 726 | } |
@@ -610,7 +734,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, | |||
610 | int *val, int *val2) | 734 | int *val, int *val2) |
611 | { | 735 | { |
612 | struct mma8452_data *data = iio_priv(indio_dev); | 736 | struct mma8452_data *data = iio_priv(indio_dev); |
613 | int ret, us; | 737 | int ret, us, power_mode; |
614 | 738 | ||
615 | switch (info) { | 739 | switch (info) { |
616 | case IIO_EV_INFO_VALUE: | 740 | case IIO_EV_INFO_VALUE: |
@@ -629,7 +753,11 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, | |||
629 | if (ret < 0) | 753 | if (ret < 0) |
630 | return ret; | 754 | return ret; |
631 | 755 | ||
632 | us = ret * mma8452_transient_time_step_us[ | 756 | power_mode = mma8452_get_power_mode(data); |
757 | if (power_mode < 0) | ||
758 | return power_mode; | ||
759 | |||
760 | us = ret * mma8452_transient_time_step_us[power_mode][ | ||
633 | mma8452_get_odr_index(data)]; | 761 | mma8452_get_odr_index(data)]; |
634 | *val = us / USEC_PER_SEC; | 762 | *val = us / USEC_PER_SEC; |
635 | *val2 = us % USEC_PER_SEC; | 763 | *val2 = us % USEC_PER_SEC; |
@@ -677,8 +805,12 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, | |||
677 | val); | 805 | val); |
678 | 806 | ||
679 | case IIO_EV_INFO_PERIOD: | 807 | case IIO_EV_INFO_PERIOD: |
808 | ret = mma8452_get_power_mode(data); | ||
809 | if (ret < 0) | ||
810 | return ret; | ||
811 | |||
680 | steps = (val * USEC_PER_SEC + val2) / | 812 | steps = (val * USEC_PER_SEC + val2) / |
681 | mma8452_transient_time_step_us[ | 813 | mma8452_transient_time_step_us[ret][ |
682 | mma8452_get_odr_index(data)]; | 814 | mma8452_get_odr_index(data)]; |
683 | 815 | ||
684 | if (steps < 0 || steps > 0xff) | 816 | if (steps < 0 || steps > 0xff) |
@@ -978,7 +1110,8 @@ static struct attribute_group mma8452_event_attribute_group = { | |||
978 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ | 1110 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ |
979 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ | 1111 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ |
980 | BIT(IIO_CHAN_INFO_SCALE) | \ | 1112 | BIT(IIO_CHAN_INFO_SCALE) | \ |
981 | BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ | 1113 | BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \ |
1114 | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ | ||
982 | .scan_index = idx, \ | 1115 | .scan_index = idx, \ |
983 | .scan_type = { \ | 1116 | .scan_type = { \ |
984 | .sign = 's', \ | 1117 | .sign = 's', \ |
@@ -998,7 +1131,8 @@ static struct attribute_group mma8452_event_attribute_group = { | |||
998 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | 1131 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ |
999 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ | 1132 | BIT(IIO_CHAN_INFO_CALIBBIAS), \ |
1000 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ | 1133 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ |
1001 | BIT(IIO_CHAN_INFO_SCALE), \ | 1134 | BIT(IIO_CHAN_INFO_SCALE) | \ |
1135 | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ | ||
1002 | .scan_index = idx, \ | 1136 | .scan_index = idx, \ |
1003 | .scan_type = { \ | 1137 | .scan_type = { \ |
1004 | .sign = 's', \ | 1138 | .sign = 's', \ |
@@ -1171,6 +1305,7 @@ static struct attribute *mma8452_attributes[] = { | |||
1171 | &iio_dev_attr_sampling_frequency_available.dev_attr.attr, | 1305 | &iio_dev_attr_sampling_frequency_available.dev_attr.attr, |
1172 | &iio_dev_attr_in_accel_scale_available.dev_attr.attr, | 1306 | &iio_dev_attr_in_accel_scale_available.dev_attr.attr, |
1173 | &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, | 1307 | &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, |
1308 | &iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr, | ||
1174 | NULL | 1309 | NULL |
1175 | }; | 1310 | }; |
1176 | 1311 | ||