diff options
Diffstat (limited to 'drivers/iio/accel/adxl380.c')
| -rw-r--r-- | drivers/iio/accel/adxl380.c | 138 |
1 files changed, 94 insertions, 44 deletions
diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index aef5109c1ddd..8fab2fdbe147 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -232,25 +232,46 @@ bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_NS_GPL(adxl380_readable_noinc_reg, "IIO_ADXL380"); +static int adxl380_act_inact_enabled(struct adxl380_state *st, bool *enabled) +{ + unsigned int act_inact_ctl; + int ret; + + if (!st->chip_info->has_low_power) { + *enabled = false; + return 0; + } + + ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl); + if (ret) + return ret; + + *enabled = FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) || + FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl); + + return 0; +} + static int adxl380_set_measure_en(struct adxl380_state *st, bool en) { int ret; - unsigned int act_inact_ctl; u8 op_mode = ADXL380_OP_MODE_STANDBY; if (en) { - ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl); + bool act_inact_enabled; + + ret = adxl380_act_inact_enabled(st, &act_inact_enabled); if (ret) return ret; /* * Activity/Inactivity detection available only in VLP/ULP - * mode and for devices that support low power modes. Otherwise - * go straight to measure mode (same bits as ADXL380_OP_MODE_HP). + * mode and for devices that support low power modes. */ - if (st->chip_info->has_low_power && - (FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) || - FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl))) + if (act_inact_enabled) + st->odr = ADXL380_ODR_VLP; + + if (st->odr == ADXL380_ODR_VLP) op_mode = ADXL380_OP_MODE_VLP; else op_mode = ADXL380_OP_MODE_HP; @@ -417,17 +438,7 @@ static int adxl380_read_chn(struct adxl380_state *st, u8 addr) static int adxl380_get_odr(struct adxl380_state *st, int *odr) { - int ret; - unsigned int trig_cfg, odr_idx; - - ret = regmap_read(st->regmap, ADXL380_TRIG_CFG_REG, &trig_cfg); - if (ret) - return ret; - - odr_idx = (FIELD_GET(ADXL380_TRIG_CFG_SINC_RATE_MSK, trig_cfg) << 1) | - (FIELD_GET(ADXL380_TRIG_CFG_DEC_2X_MSK, trig_cfg) & 1); - - *odr = st->chip_info->samp_freq_tbl[odr_idx]; + *odr = st->chip_info->samp_freq_tbl[st->odr]; return 0; } @@ -488,18 +499,24 @@ static int adxl380_set_odr(struct adxl380_state *st, u8 odr) if (ret) return ret; - ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG, - ADXL380_TRIG_CFG_DEC_2X_MSK, - FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, odr & 1)); - if (ret) - return ret; + if (odr >= ADXL380_ODR_DSM) { + u8 mul = odr - ADXL380_ODR_DSM; + u8 field; - ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG, - ADXL380_TRIG_CFG_SINC_RATE_MSK, - FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, odr >> 1)); - if (ret) - return ret; + field = FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, mul & 1); + ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG, + ADXL380_TRIG_CFG_DEC_2X_MSK, field); + if (ret) + return ret; + field = FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, mul >> 1); + ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG, + ADXL380_TRIG_CFG_SINC_RATE_MSK, field); + if (ret) + return ret; + } + + st->odr = odr; ret = adxl380_set_measure_en(st, true); if (ret) return ret; @@ -949,14 +966,13 @@ static irqreturn_t adxl380_irq_handler(int irq, void *p) if (ret) return IRQ_HANDLED; - for (i = 0; i < fifo_entries; i += st->fifo_set_size) { - ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, - &st->fifo_buf[i], - 2 * st->fifo_set_size); - if (ret) - return IRQ_HANDLED; + fifo_entries = rounddown(fifo_entries, st->fifo_set_size); + ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, &st->fifo_buf, + sizeof(*st->fifo_buf) * fifo_entries); + if (ret) + return IRQ_HANDLED; + for (i = 0; i < fifo_entries; i += st->fifo_set_size) iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); - } return IRQ_HANDLED; } @@ -1138,6 +1154,32 @@ static const struct iio_buffer_setup_ops adxl380_buffer_ops = { .predisable = adxl380_buffer_predisable, }; +static int adxl380_samp_freq_avail(struct adxl380_state *st, const int **vals, + int *length) +{ + bool act_inact_enabled; + int ret; + + if (!st->chip_info->has_low_power) { + *vals = st->chip_info->samp_freq_tbl + ADXL380_ODR_DSM; + *length = ADXL380_ODR_MAX - ADXL380_ODR_DSM; + return 0; + } + + ret = adxl380_act_inact_enabled(st, &act_inact_enabled); + if (ret) + return 0; + + /* + * Motion detection is only functional in low-power mode, and this + * affects the available sampling frequencies. + */ + *vals = st->chip_info->samp_freq_tbl; + *length = act_inact_enabled ? ADXL380_ODR_DSM : ADXL380_ODR_MAX; + + return 0; +} + static int adxl380_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -1218,6 +1260,7 @@ static int adxl380_read_avail(struct iio_dev *indio_dev, long mask) { struct adxl380_state *st = iio_priv(indio_dev); + int ret; if (chan->type != IIO_ACCEL) return -EINVAL; @@ -1229,9 +1272,11 @@ static int adxl380_read_avail(struct iio_dev *indio_dev, *length = ARRAY_SIZE(st->chip_info->scale_tbl) * 2; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SAMP_FREQ: - *vals = (const int *)st->chip_info->samp_freq_tbl; + ret = adxl380_samp_freq_avail(st, vals, length); + if (ret) + return ret; + *type = IIO_VAL_INT; - *length = ARRAY_SIZE(st->chip_info->samp_freq_tbl); return IIO_AVAIL_LIST; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: *vals = (const int *)st->lpf_tbl; @@ -1254,12 +1299,16 @@ static int adxl380_write_raw(struct iio_dev *indio_dev, int val, int val2, long info) { struct adxl380_state *st = iio_priv(indio_dev); - int odr_index, lpf_index, hpf_index, range_index; + const int *freq_vals; + int odr_index, lpf_index, hpf_index, range_index, freq_count, ret; switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - odr_index = adxl380_find_match_1d_tbl(st->chip_info->samp_freq_tbl, - ARRAY_SIZE(st->chip_info->samp_freq_tbl), + ret = adxl380_samp_freq_avail(st, &freq_vals, &freq_count); + if (ret) + return ret; + + odr_index = adxl380_find_match_1d_tbl(freq_vals, freq_count, val); return adxl380_set_odr(st, odr_index); case IIO_CHAN_INFO_CALIBBIAS: @@ -1621,7 +1670,7 @@ const struct adxl380_chip_info adxl318_chip_info = { [ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 }, [ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 }, }, - .samp_freq_tbl = { 8000, 16000, 32000 }, + .samp_freq_tbl = { 0, 8000, 16000, 32000 }, /* * The datasheet defines an intercept of 550 LSB at 25 degC * and a sensitivity of 10.2 LSB/C. @@ -1639,7 +1688,7 @@ const struct adxl380_chip_info adxl319_chip_info = { [ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 }, [ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 }, }, - .samp_freq_tbl = { 16000, 32000, 64000 }, + .samp_freq_tbl = { 0, 16000, 32000, 64000 }, /* * The datasheet defines an intercept of 550 LSB at 25 degC * and a sensitivity of 10.2 LSB/C. @@ -1657,7 +1706,7 @@ const struct adxl380_chip_info adxl380_chip_info = { [ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 }, [ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 }, }, - .samp_freq_tbl = { 8000, 16000, 32000 }, + .samp_freq_tbl = { 1000, 8000, 16000, 32000 }, /* * The datasheet defines an intercept of 470 LSB at 25 degC * and a sensitivity of 10.2 LSB/C. @@ -1677,7 +1726,7 @@ const struct adxl380_chip_info adxl382_chip_info = { [ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 }, [ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 }, }, - .samp_freq_tbl = { 16000, 32000, 64000 }, + .samp_freq_tbl = { 1000, 16000, 32000, 64000 }, /* * The datasheet defines an intercept of 570 LSB at 25 degC * and a sensitivity of 10.2 LSB/C. @@ -1916,6 +1965,7 @@ int adxl380_probe(struct device *dev, struct regmap *regmap, st->dev = dev; st->regmap = regmap; st->chip_info = chip_info; + st->odr = ADXL380_ODR_DSM; mutex_init(&st->lock); |
