diff options
Diffstat (limited to 'drivers/iio/accel/adxl372.c')
| -rw-r--r-- | drivers/iio/accel/adxl372.c | 320 |
1 files changed, 196 insertions, 124 deletions
diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 28a8793a53b6..e375d068a3f5 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1,12 +1,13 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer core driver + * ADXL371/ADXL372 3-Axis Digital Accelerometer core driver * * Copyright 2018 Analog Devices Inc. */ #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> @@ -180,6 +181,16 @@ enum adxl372_odr { ADXL372_ODR_1600HZ, ADXL372_ODR_3200HZ, ADXL372_ODR_6400HZ, + ADXL372_ODR_NUM +}; + +enum adxl371_odr { + ADXL371_ODR_320HZ, + ADXL371_ODR_640HZ, + ADXL371_ODR_1280HZ, + ADXL371_ODR_2560HZ, + ADXL371_ODR_5120HZ, + ADXL371_ODR_NUM }; enum adxl372_bandwidth { @@ -214,14 +225,67 @@ enum adxl372_fifo_mode { ADXL372_FIFO_OLD_SAVED }; -static const int adxl372_samp_freq_tbl[5] = { - 400, 800, 1600, 3200, 6400, +static const int adxl372_samp_freq_tbl[ADXL372_ODR_NUM] = { + [ADXL372_ODR_400HZ] = 400, + [ADXL372_ODR_800HZ] = 800, + [ADXL372_ODR_1600HZ] = 1600, + [ADXL372_ODR_3200HZ] = 3200, + [ADXL372_ODR_6400HZ] = 6400, +}; + +static const int adxl372_bw_freq_tbl[ADXL372_ODR_NUM] = { + [ADXL372_BW_200HZ] = 200, + [ADXL372_BW_400HZ] = 400, + [ADXL372_BW_800HZ] = 800, + [ADXL372_BW_1600HZ] = 1600, + [ADXL372_BW_3200HZ] = 3200, +}; + +static const int adxl371_samp_freq_tbl[ADXL371_ODR_NUM] = { + [ADXL371_ODR_320HZ] = 320, + [ADXL371_ODR_640HZ] = 640, + [ADXL371_ODR_1280HZ] = 1280, + [ADXL371_ODR_2560HZ] = 2560, + [ADXL371_ODR_5120HZ] = 5120, }; -static const int adxl372_bw_freq_tbl[5] = { - 200, 400, 800, 1600, 3200, +static const int adxl371_bw_freq_tbl[ADXL371_ODR_NUM] = { + [ADXL371_ODR_320HZ] = 160, + [ADXL371_ODR_640HZ] = 320, + [ADXL371_ODR_1280HZ] = 640, + [ADXL371_ODR_2560HZ] = 1280, + [ADXL371_ODR_5120HZ] = 2560, }; +const struct adxl372_chip_info adxl371_chip_info = { + .name = "adxl371", + .samp_freq_tbl = adxl371_samp_freq_tbl, + .bw_freq_tbl = adxl371_bw_freq_tbl, + .num_freqs = ARRAY_SIZE(adxl371_samp_freq_tbl), + .act_time_scale_us = 4125, + .act_time_scale_low_us = 8250, + .inact_time_scale_ms = 16, + .inact_time_scale_low_ms = 32, + .max_odr = ADXL371_ODR_5120HZ, + /* Silicon erratum (er001) causes FIFO data misalignment on ADXL371 */ + .fifo_supported = false, +}; +EXPORT_SYMBOL_NS_GPL(adxl371_chip_info, "IIO_ADXL372"); + +const struct adxl372_chip_info adxl372_chip_info = { + .name = "adxl372", + .samp_freq_tbl = adxl372_samp_freq_tbl, + .bw_freq_tbl = adxl372_bw_freq_tbl, + .num_freqs = ARRAY_SIZE(adxl372_samp_freq_tbl), + .act_time_scale_us = 3300, + .act_time_scale_low_us = 6600, + .inact_time_scale_ms = 13, + .inact_time_scale_low_ms = 26, + .max_odr = ADXL372_ODR_6400HZ, + .fifo_supported = true, +}; +EXPORT_SYMBOL_NS_GPL(adxl372_chip_info, "IIO_ADXL372"); + struct adxl372_axis_lookup { unsigned int bits; enum adxl372_fifo_format fifo_format; @@ -257,8 +321,12 @@ static const struct iio_event_spec adxl372_events[] = { .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = index, \ .scan_type = { \ @@ -279,6 +347,7 @@ static const struct iio_chan_spec adxl372_channels[] = { }; struct adxl372_state { + const struct adxl372_chip_info *chip_info; int irq; struct device *dev; struct regmap *regmap; @@ -336,18 +405,14 @@ static ssize_t adxl372_write_threshold_value(struct iio_dev *indio_dev, unsigned struct adxl372_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->threshold_m); + guard(mutex)(&st->threshold_m); + ret = regmap_write(st->regmap, addr, ADXL372_THRESH_VAL_H_SEL(threshold)); if (ret < 0) - goto unlock; - - ret = regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), - ADXL372_THRESH_VAL_L_SEL(threshold) << 5); - -unlock: - mutex_unlock(&st->threshold_m); + return ret; - return ret; + return regmap_update_bits(st->regmap, addr + 1, GENMASK(7, 5), + ADXL372_THRESH_VAL_L_SEL(threshold) << 5); } static int adxl372_read_axis(struct adxl372_state *st, u8 addr) @@ -471,13 +536,14 @@ static int adxl372_set_activity_time_ms(struct adxl372_state *st, int ret; /* - * 3.3 ms per code is the scale factor of the TIME_ACT register for - * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below. + * The scale factor of the TIME_ACT register depends on the ODR. + * A higher scale factor is used at the maximum ODR and a lower + * one at all other rates. */ - if (st->odr == ADXL372_ODR_6400HZ) - scale_factor = 3300; + if (st->odr == st->chip_info->max_odr) + scale_factor = st->chip_info->act_time_scale_us; else - scale_factor = 6600; + scale_factor = st->chip_info->act_time_scale_low_us; reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor); @@ -501,13 +567,14 @@ static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, int ret; /* - * 13 ms per code is the scale factor of the TIME_INACT register for - * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below. + * The scale factor of the TIME_INACT register depends on the ODR. + * A higher scale factor is used at the maximum ODR and a lower + * one at all other rates. */ - if (st->odr == ADXL372_ODR_6400HZ) - scale_factor = 13; + if (st->odr == st->chip_info->max_odr) + scale_factor = st->chip_info->inact_time_scale_ms; else - scale_factor = 26; + scale_factor = st->chip_info->inact_time_scale_low_ms; res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor); reg_val_h = (res >> 8) & 0xFF; @@ -717,7 +784,7 @@ static int adxl372_setup(struct adxl372_state *st) if (ret < 0) return ret; - ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ); + ret = adxl372_set_odr(st, st->chip_info->max_odr); if (ret < 0) return ret; @@ -777,10 +844,10 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, *val2 = ADXL372_USCALE; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - *val = adxl372_samp_freq_tbl[st->odr]; + *val = st->chip_info->samp_freq_tbl[st->odr]; return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - *val = adxl372_bw_freq_tbl[st->bw]; + *val = st->chip_info->bw_freq_tbl[st->bw]; return IIO_VAL_INT; } @@ -796,23 +863,17 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl, - ARRAY_SIZE(adxl372_samp_freq_tbl), - val); + odr_index = adxl372_find_closest_match(st->chip_info->samp_freq_tbl, + st->chip_info->num_freqs, + val); ret = adxl372_set_odr(st, odr_index); if (ret < 0) return ret; - /* - * The timer period depends on the ODR selected. - * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms - */ + /* Recalculate activity time as the timer period depends on ODR */ ret = adxl372_set_activity_time_ms(st, st->act_time_ms); if (ret < 0) return ret; - /* - * The timer period depends on the ODR selected. - * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms - */ + /* Recalculate inactivity time as the timer period depends on ODR */ ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms); if (ret < 0) return ret; @@ -825,9 +886,9 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, return ret; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl, - ARRAY_SIZE(adxl372_bw_freq_tbl), - val); + bw_index = adxl372_find_closest_match(st->chip_info->bw_freq_tbl, + st->chip_info->num_freqs, + val); return adxl372_set_bandwidth(st, bw_index); default: return -EINVAL; @@ -957,24 +1018,6 @@ static int adxl372_write_event_config(struct iio_dev *indio_dev, const struct ii return adxl372_set_interrupts(st, st->int1_bitmask, 0); } -static ssize_t adxl372_show_filter_freq_avail(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct adxl372_state *st = iio_priv(indio_dev); - int i; - size_t len = 0; - - for (i = 0; i <= st->odr; i++) - len += scnprintf(buf + len, PAGE_SIZE - len, - "%d ", adxl372_bw_freq_tbl[i]); - - buf[len - 1] = '\n'; - - return len; -} - static ssize_t adxl372_get_fifo_enabled(struct device *dev, struct device_attribute *attr, char *buf) @@ -1142,25 +1185,38 @@ static const struct iio_trigger_ops adxl372_peak_data_trigger_ops = { .set_trigger_state = adxl372_peak_dready_trig_set_state, }; -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); -static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, - 0444, adxl372_show_filter_freq_avail, NULL, 0); - -static struct attribute *adxl372_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr, - NULL, -}; +static int adxl372_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl372_state *st = iio_priv(indio_dev); -static const struct attribute_group adxl372_attrs_group = { - .attrs = adxl372_attributes, -}; + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->chip_info->samp_freq_tbl; + *type = IIO_VAL_INT; + *length = st->chip_info->num_freqs; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = st->chip_info->bw_freq_tbl; + *type = IIO_VAL_INT; + /* + * Bandwidth cannot exceed half the sampling frequency + * (Nyquist), so limit available values based on current ODR. + */ + *length = st->odr + 1; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} static const struct iio_info adxl372_info = { .validate_trigger = &adxl372_validate_trigger, - .attrs = &adxl372_attrs_group, .read_raw = adxl372_read_raw, .write_raw = adxl372_write_raw, + .read_avail = adxl372_read_avail, .read_event_config = adxl372_read_event_config, .write_event_config = adxl372_write_event_config, .read_event_value = adxl372_read_event_value, @@ -1175,8 +1231,57 @@ bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_NS_GPL(adxl372_readable_noinc_reg, "IIO_ADXL372"); +static int adxl372_buffer_setup(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + struct device *dev = st->dev; + int ret; + + ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, + adxl372_trigger_handler, + IIO_BUFFER_DIRECTION_IN, + &adxl372_buffer_ops, + adxl372_fifo_attributes); + if (ret) + return ret; + + if (!st->irq) + return 0; + + st->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->dready_trig) + return -ENOMEM; + + st->peak_datardy_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-peak", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->peak_datardy_trig) + return -ENOMEM; + + st->dready_trig->ops = &adxl372_trigger_ops; + st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; + iio_trigger_set_drvdata(st->dready_trig, indio_dev); + iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->dready_trig); + if (ret) + return ret; + + ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->dready_trig); + + return devm_request_irq(dev, st->irq, + iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + indio_dev->name, st->dready_trig); +} + int adxl372_probe(struct device *dev, struct regmap *regmap, - int irq, const char *name) + int irq, const struct adxl372_chip_info *chip_info) { struct iio_dev *indio_dev; struct adxl372_state *st; @@ -1192,64 +1297,30 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->dev = dev; st->regmap = regmap; st->irq = irq; + st->chip_info = chip_info; - mutex_init(&st->threshold_m); + ret = devm_mutex_init(dev, &st->threshold_m); + if (ret < 0) + return ret; indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); - indio_dev->available_scan_masks = adxl372_channel_masks; - indio_dev->name = name; + indio_dev->name = chip_info->name; indio_dev->info = &adxl372_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; - ret = adxl372_setup(st); - if (ret < 0) { - dev_err(dev, "ADXL372 setup failed\n"); - return ret; + if (chip_info->fifo_supported) { + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->available_scan_masks = adxl372_channel_masks; + } else { + indio_dev->modes = INDIO_DIRECT_MODE; } - ret = devm_iio_triggered_buffer_setup_ext(dev, - indio_dev, NULL, - adxl372_trigger_handler, - IIO_BUFFER_DIRECTION_IN, - &adxl372_buffer_ops, - adxl372_fifo_attributes); + ret = adxl372_setup(st); if (ret < 0) - return ret; - - if (st->irq) { - st->dready_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); - if (st->dready_trig == NULL) - return -ENOMEM; - - st->peak_datardy_trig = devm_iio_trigger_alloc(dev, - "%s-dev%d-peak", - indio_dev->name, - iio_device_id(indio_dev)); - if (!st->peak_datardy_trig) - return -ENOMEM; - - st->dready_trig->ops = &adxl372_trigger_ops; - st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; - iio_trigger_set_drvdata(st->dready_trig, indio_dev); - iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); - ret = devm_iio_trigger_register(dev, st->dready_trig); - if (ret < 0) - return ret; - - ret = devm_iio_trigger_register(dev, st->peak_datardy_trig); - if (ret < 0) - return ret; - - indio_dev->trig = iio_trigger_get(st->dready_trig); + return dev_err_probe(dev, ret, "ADXL372 setup failed\n"); - ret = devm_request_irq(dev, st->irq, - iio_trigger_generic_data_rdy_poll, - IRQF_TRIGGER_RISING | IRQF_NO_THREAD, - indio_dev->name, st->dready_trig); + if (chip_info->fifo_supported) { + ret = adxl372_buffer_setup(indio_dev); if (ret < 0) return ret; } @@ -1259,5 +1330,6 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, EXPORT_SYMBOL_NS_GPL(adxl372_probe, "IIO_ADXL372"); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); -MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADXL371/ADXL372 3-axis accelerometer driver"); MODULE_LICENSE("GPL"); |
