diff options
Diffstat (limited to 'drivers/iio')
132 files changed, 13784 insertions, 2464 deletions
diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 517e494ba555..bc6d634bd85c 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -43,7 +43,6 @@ #define ADXL345_REG_INT_ENABLE 0x2E #define ADXL345_REG_INT_MAP 0x2F #define ADXL345_REG_INT_SOURCE 0x30 -#define ADXL345_REG_INT_SOURCE_MSK 0xFF #define ADXL345_REG_DATA_FORMAT 0x31 #define ADXL345_REG_XYZ_BASE 0x32 #define ADXL345_REG_DATA_AXIS(index) \ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index d1b2d3985a40..375c27d16827 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -76,6 +76,26 @@ static const unsigned long adxl345_scan_masks[] = { 0 }; +/** + * adxl345_set_measure_en() - Enable and disable measuring. + * + * @st: The device data. + * @en: Enable measurements, else standby mode. + * + * For lowest power operation, standby mode can be used. In standby mode, + * current consumption is supposed to be reduced to 0.1uA (typical). In this + * mode no measurements are made. Placing the device into standby mode + * preserves the contents of FIFO. + * + * Return: Returns 0 if successful, or a negative error value. + */ +static int adxl345_set_measure_en(struct adxl345_state *st, bool en) +{ + unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY; + + return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val); +} + static int adxl345_set_interrupts(struct adxl345_state *st) { int ret; @@ -87,8 +107,7 @@ static int adxl345_set_interrupts(struct adxl345_state *st) * interrupts to the INT1 pin, whereas bits set to 1 send their respective * interrupts to the INT2 pin. The intio shall convert this accordingly. */ - int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, - st->intio ? st->int_map : ~st->int_map); + int_map = st->intio ? st->int_map : ~st->int_map; ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map); if (ret) @@ -182,6 +201,16 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int adxl345_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adxl345_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + return regmap_write(st->regmap, reg, writeval); +} + static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value) { struct adxl345_state *st = iio_priv(indio_dev); @@ -214,26 +243,6 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, } } -/** - * adxl345_set_measure_en() - Enable and disable measuring. - * - * @st: The device data. - * @en: Enable measurements, else standby mode. - * - * For lowest power operation, standby mode can be used. In standby mode, - * current consumption is supposed to be reduced to 0.1uA (typical). In this - * mode no measurements are made. Placing the device into standby mode - * preserves the contents of FIFO. - * - * Return: Returns 0 if successful, or a negative error value. - */ -static int adxl345_set_measure_en(struct adxl345_state *st, bool en) -{ - unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY; - - return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val); -} - static void adxl345_powerdown(void *ptr) { struct adxl345_state *st = ptr; @@ -394,18 +403,6 @@ static const struct iio_buffer_setup_ops adxl345_buffer_ops = { .predisable = adxl345_buffer_predisable, }; -static int adxl345_get_status(struct adxl345_state *st) -{ - int ret; - unsigned int regval; - - ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); - if (ret < 0) - return ret; - - return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval); -} - static int adxl345_fifo_push(struct iio_dev *indio_dev, int samples) { @@ -439,14 +436,10 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p) int int_stat; int samples; - int_stat = adxl345_get_status(st); - if (int_stat <= 0) + if (regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &int_stat)) return IRQ_NONE; - if (int_stat & ADXL345_INT_OVERRUN) - goto err; - - if (int_stat & ADXL345_INT_WATERMARK) { + if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) { samples = adxl345_get_samples(st); if (samples < 0) goto err; @@ -454,6 +447,10 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p) if (adxl345_fifo_push(indio_dev, samples) < 0) goto err; } + + if (FIELD_GET(ADXL345_INT_OVERRUN, int_stat)) + goto err; + return IRQ_HANDLED; err: @@ -467,6 +464,7 @@ static const struct iio_info adxl345_info = { .read_raw = adxl345_read_raw, .write_raw = adxl345_write_raw, .write_raw_get_fmt = adxl345_write_raw_get_fmt, + .debugfs_reg_access = &adxl345_reg_access, .hwfifo_set_watermark = adxl345_set_watermark, }; diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index a48ac0d7bd96..add4053e7a02 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -477,45 +477,42 @@ static int adxl367_set_fifo_watermark(struct adxl367_state *st, static int adxl367_set_range(struct iio_dev *indio_dev, enum adxl367_range range) { - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct adxl367_state *st = iio_priv(indio_dev); - int ret; + struct adxl367_state *st = iio_priv(indio_dev); + int ret; - guard(mutex)(&st->lock); + guard(mutex)(&st->lock); - ret = adxl367_set_measure_en(st, false); - if (ret) - return ret; + ret = adxl367_set_measure_en(st, false); + if (ret) + return ret; - ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, - ADXL367_FILTER_CTL_RANGE_MASK, - FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, - range)); - if (ret) - return ret; + ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, + ADXL367_FILTER_CTL_RANGE_MASK, + FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, + range)); + if (ret) + return ret; - adxl367_scale_act_thresholds(st, st->range, range); + adxl367_scale_act_thresholds(st, st->range, range); - /* Activity thresholds depend on range */ - ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, - st->act_threshold); - if (ret) - return ret; + /* Activity thresholds depend on range */ + ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, + st->act_threshold); + if (ret) + return ret; - ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, - st->inact_threshold); - if (ret) - return ret; + ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, + st->inact_threshold); + if (ret) + return ret; - ret = adxl367_set_measure_en(st, true); - if (ret) - return ret; + ret = adxl367_set_measure_en(st, true); + if (ret) + return ret; - st->range = range; + st->range = range; - return 0; - } - unreachable(); + return 0; } static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms) @@ -620,23 +617,20 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) { - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct adxl367_state *st = iio_priv(indio_dev); - int ret; + struct adxl367_state *st = iio_priv(indio_dev); + int ret; - guard(mutex)(&st->lock); + guard(mutex)(&st->lock); - ret = adxl367_set_measure_en(st, false); - if (ret) - return ret; + ret = adxl367_set_measure_en(st, false); + if (ret) + return ret; - ret = _adxl367_set_odr(st, odr); - if (ret) - return ret; + ret = _adxl367_set_odr(st, odr); + if (ret) + return ret; - return adxl367_set_measure_en(st, true); - } - unreachable(); + return adxl367_set_measure_en(st, true); } static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg, @@ -725,32 +719,29 @@ static int adxl367_read_sample(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct adxl367_state *st = iio_priv(indio_dev); - u16 sample; - int ret; + struct adxl367_state *st = iio_priv(indio_dev); + u16 sample; + int ret; - guard(mutex)(&st->lock); + guard(mutex)(&st->lock); - ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); - if (ret) - return ret; + ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); + if (ret) + return ret; - ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, - sizeof(st->sample_buf)); - if (ret) - return ret; + ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, + sizeof(st->sample_buf)); + if (ret) + return ret; - sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); - *val = sign_extend32(sample, chan->scan_type.realbits - 1); + sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); + *val = sign_extend32(sample, chan->scan_type.realbits - 1); - ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); - if (ret) - return ret; + ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); + if (ret) + return ret; - return IIO_VAL_INT; - } - unreachable(); + return IIO_VAL_INT; } static int adxl367_get_status(struct adxl367_state *st, u8 *status, @@ -852,10 +843,15 @@ static int adxl367_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long info) { struct adxl367_state *st = iio_priv(indio_dev); + int ret; switch (info) { case IIO_CHAN_INFO_RAW: - return adxl367_read_sample(indio_dev, chan, val); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = adxl367_read_sample(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ACCEL: { @@ -912,7 +908,12 @@ static int adxl367_write_raw(struct iio_dev *indio_dev, if (ret) return ret; - return adxl367_set_odr(indio_dev, odr); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = adxl367_set_odr(indio_dev, odr); + iio_device_release_direct(indio_dev); + return ret; } case IIO_CHAN_INFO_SCALE: { enum adxl367_range range; @@ -921,7 +922,12 @@ static int adxl367_write_raw(struct iio_dev *indio_dev, if (ret) return ret; - return adxl367_set_range(indio_dev, range); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = adxl367_set_range(indio_dev, range); + iio_device_release_direct(indio_dev); + return ret; } default: return -EINVAL; @@ -1069,13 +1075,15 @@ static int adxl367_read_event_config(struct iio_dev *indio_dev, } } -static int adxl367_write_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - bool state) +static int __adxl367_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) { + struct adxl367_state *st = iio_priv(indio_dev); enum adxl367_activity_type act; + int ret; switch (dir) { case IIO_EV_DIR_RISING: @@ -1088,28 +1096,38 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev, return -EINVAL; } - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct adxl367_state *st = iio_priv(indio_dev); - int ret; + guard(mutex)(&st->lock); + + ret = adxl367_set_measure_en(st, false); + if (ret) + return ret; - guard(mutex)(&st->lock); + ret = adxl367_set_act_interrupt_en(st, act, state); + if (ret) + return ret; - ret = adxl367_set_measure_en(st, false); - if (ret) - return ret; + ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED + : ADXL367_ACT_DISABLED); + if (ret) + return ret; - ret = adxl367_set_act_interrupt_en(st, act, state); - if (ret) - return ret; + return adxl367_set_measure_en(st, true); +} - ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED - : ADXL367_ACT_DISABLED); - if (ret) - return ret; +static int adxl367_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + int ret; - return adxl367_set_measure_en(st, true); - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __adxl367_write_event_config(indio_dev, chan, type, dir, state); + iio_device_release_direct(indio_dev); + return ret; } static ssize_t adxl367_get_fifo_enabled(struct device *dev, diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 8ba5fbe6e1f5..961145b50293 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -763,12 +763,11 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = adxl372_read_axis(st, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c index 90340f134722..0cf3c6815829 100644 --- a/drivers/iio/accel/adxl380.c +++ b/drivers/iio/accel/adxl380.c @@ -1175,12 +1175,11 @@ static int adxl380_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = adxl380_read_chn(st, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 128db14ba726..aa664a923f91 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -540,14 +540,13 @@ static int bma180_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&data->mutex); ret = bma180_get_data_reg(data, chan->scan_index); mutex_unlock(&data->mutex); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; if (chan->scan_type.sign == 's') { diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index ae806ed60271..23f5e1ce9cc4 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -190,7 +190,7 @@ const struct regmap_config bma400_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = BMA400_CMD_REG, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .writeable_reg = bma400_is_writable_reg, .volatile_reg = bma400_is_volatile_reg, }; diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index 9206fbdbf520..dea126f993c1 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -145,7 +145,7 @@ const struct regmap_config bmi088_regmap_conf = { .val_bits = 8, .max_register = 0x7E, .volatile_table = &bmi088_volatile_table, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, "IIO_BMI088"); @@ -313,12 +313,13 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) + if (!iio_device_claim_direct(indio_dev)) { + ret = -EBUSY; goto out_read_raw_pm_put; + } ret = bmi088_accel_get_axis(data, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (!ret) ret = IIO_VAL_INT; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 987212a7c038..48e4282964a0 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -460,22 +460,20 @@ static int fxls8962af_write_raw(struct iio_dev *indio_dev, if (val != 0) return -EINVAL; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = fxls8962af_set_full_scale(data, val2); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = fxls8962af_set_samp_freq(data, val, val2); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; default: return -EINVAL; @@ -683,14 +681,13 @@ fxls8962af_write_event_config(struct iio_dev *indio_dev, fxls8962af_active(data); ret = fxls8962af_power_on(data); } else { - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; /* Not in buffered mode so disable power */ ret = fxls8962af_power_off(data); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); } return ret; diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 5aeb3b951ac5..07dcf5f0599f 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -149,7 +149,7 @@ static const struct regmap_config kx022a_regmap_config = { .rd_noinc_table = &kx022a_nir_regs, .precious_table = &kx022a_precious_regs, .max_register = KX022A_MAX_REGISTER, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; /* Regmap configs kx132 */ @@ -260,7 +260,7 @@ static const struct regmap_config kx132_regmap_config = { .rd_noinc_table = &kx132_nir_regs, .precious_table = &kx132_precious_regs, .max_register = KX132_MAX_REGISTER, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; struct kx022a_data { @@ -510,26 +510,13 @@ static int kx022a_write_raw_get_fmt(struct iio_dev *idev, } } -static int kx022a_write_raw(struct iio_dev *idev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __kx022a_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct kx022a_data *data = iio_priv(idev); int ret, n; - /* - * We should not allow changing scale or frequency when FIFO is running - * as it will mess the timestamp/scale for samples existing in the - * buffer. If this turns out to be an issue we can later change logic - * to internally flush the fifo before reconfiguring so the samples in - * fifo keep matching the freq/scale settings. (Such setup could cause - * issues if users trust the watermark to be reached within known - * time-limit). - */ - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; - switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: n = ARRAY_SIZE(kx022a_accel_samp_freq_table); @@ -538,20 +525,19 @@ static int kx022a_write_raw(struct iio_dev *idev, if (val == kx022a_accel_samp_freq_table[n][0] && val2 == kx022a_accel_samp_freq_table[n][1]) break; - if (n < 0) { - ret = -EINVAL; - goto unlock_out; - } + if (n < 0) + return -EINVAL; + ret = kx022a_turn_off_lock(data); if (ret) - break; + return ret; ret = regmap_update_bits(data->regmap, data->chip_info->odcntl, KX022A_MASK_ODR, n); data->odr_ns = kx022a_odrs[n]; kx022a_turn_on_unlock(data); - break; + return ret; case IIO_CHAN_INFO_SCALE: n = data->chip_info->scale_table_size / 2; @@ -559,27 +545,44 @@ static int kx022a_write_raw(struct iio_dev *idev, if (val == data->chip_info->scale_table[n][0] && val2 == data->chip_info->scale_table[n][1]) break; - if (n < 0) { - ret = -EINVAL; - goto unlock_out; - } + if (n < 0) + return -EINVAL; ret = kx022a_turn_off_lock(data); if (ret) - break; + return ret; ret = regmap_update_bits(data->regmap, data->chip_info->cntl, KX022A_MASK_GSEL, n << KX022A_GSEL_SHIFT); kx022a_turn_on_unlock(data); - break; + return ret; default: - ret = -EINVAL; - break; + return -EINVAL; } +} -unlock_out: - iio_device_release_direct_mode(idev); +static int kx022a_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + /* + * We should not allow changing scale or frequency when FIFO is running + * as it will mess the timestamp/scale for samples existing in the + * buffer. If this turns out to be an issue we can later change logic + * to internally flush the fifo before reconfiguring so the samples in + * fifo keep matching the freq/scale settings. (Such setup could cause + * issues if users trust the watermark to be reached within known + * time-limit). + */ + if (!iio_device_claim_direct(idev)) + return -EBUSY; + + ret = __kx022a_write_raw(idev, chan, val, val2, mask); + + iio_device_release_direct(idev); return ret; } @@ -620,15 +623,14 @@ static int kx022a_read_raw(struct iio_dev *idev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; + if (!iio_device_claim_direct(idev)) + return -EBUSY; mutex_lock(&data->mutex); ret = kx022a_get_axis(data, chan, val); mutex_unlock(&data->mutex); - iio_device_release_direct_mode(idev); + iio_device_release_direct(idev); return ret; diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c index caa40a14a631..e2853090fa6e 100644 --- a/drivers/iio/accel/mc3230.c +++ b/drivers/iio/accel/mc3230.c @@ -22,20 +22,37 @@ #define MC3230_MODE_OPCON_STANDBY 0x03 #define MC3230_REG_CHIP_ID 0x18 -#define MC3230_CHIP_ID 0x01 - #define MC3230_REG_PRODUCT_CODE 0x3b -#define MC3230_PRODUCT_CODE 0x19 /* * The accelerometer has one measurement range: * * -1.5g - +1.5g (8-bit, signed) * - * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765 */ -static const int mc3230_nscale = 115411765; +struct mc3230_chip_info { + const char *name; + const u8 chip_id; + const u8 product_code; + const int scale; +}; + +static const struct mc3230_chip_info mc3230_chip_info = { + .name = "mc3230", + .chip_id = 0x01, + .product_code = 0x19, + /* (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765 */ + .scale = 115411765, +}; + +static const struct mc3230_chip_info mc3510c_chip_info = { + .name = "mc3510c", + .chip_id = 0x23, + .product_code = 0x10, + /* Was obtained empirically */ + .scale = 625000000, +}; #define MC3230_CHANNEL(reg, axis) { \ .type = IIO_ACCEL, \ @@ -44,18 +61,35 @@ static const int mc3230_nscale = 115411765; .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = mc3230_ext_info, \ +} + +struct mc3230_data { + const struct mc3230_chip_info *chip_info; + struct i2c_client *client; + struct iio_mount_matrix orientation; +}; + +static const struct iio_mount_matrix * +mc3230_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mc3230_data *data = iio_priv(indio_dev); + + return &data->orientation; } +static const struct iio_chan_spec_ext_info mc3230_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, mc3230_get_mount_matrix), + { } +}; + static const struct iio_chan_spec mc3230_channels[] = { MC3230_CHANNEL(MC3230_REG_XOUT, X), MC3230_CHANNEL(MC3230_REG_YOUT, Y), MC3230_CHANNEL(MC3230_REG_ZOUT, Z), }; -struct mc3230_data { - struct i2c_client *client; -}; - static int mc3230_set_opcon(struct mc3230_data *data, int opcon) { int ret; @@ -95,7 +129,7 @@ static int mc3230_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; - *val2 = mc3230_nscale; + *val2 = data->chip_info->scale; return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; @@ -111,15 +145,28 @@ static int mc3230_probe(struct i2c_client *client) int ret; struct iio_dev *indio_dev; struct mc3230_data *data; + const struct mc3230_chip_info *chip_info; + + chip_info = i2c_get_match_data(client); + if (chip_info == NULL) { + dev_err(&client->dev, "failed to get match data"); + return -ENODATA; + } /* First check chip-id and product-id */ ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID); - if (ret != MC3230_CHIP_ID) - return (ret < 0) ? ret : -ENODEV; + if (ret != chip_info->chip_id) { + dev_info(&client->dev, + "chip id check fail: 0x%x != 0x%x !\n", + ret, chip_info->chip_id); + } ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE); - if (ret != MC3230_PRODUCT_CODE) - return (ret < 0) ? ret : -ENODEV; + if (ret != chip_info->product_code) { + dev_info(&client->dev, + "product code check fail: 0x%x != 0x%x !\n", + ret, chip_info->product_code); + } indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) { @@ -128,11 +175,12 @@ static int mc3230_probe(struct i2c_client *client) } data = iio_priv(indio_dev); + data->chip_info = chip_info; data->client = client; i2c_set_clientdata(client, indio_dev); indio_dev->info = &mc3230_info; - indio_dev->name = "mc3230"; + indio_dev->name = chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = mc3230_channels; indio_dev->num_channels = ARRAY_SIZE(mc3230_channels); @@ -141,6 +189,10 @@ static int mc3230_probe(struct i2c_client *client) if (ret < 0) return ret; + ret = iio_read_mount_matrix(&client->dev, &data->orientation); + if (ret) + return ret; + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&client->dev, "device_register failed\n"); @@ -180,14 +232,23 @@ static int mc3230_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); static const struct i2c_device_id mc3230_i2c_id[] = { - { "mc3230" }, - {} + { "mc3230", (kernel_ulong_t)&mc3230_chip_info }, + { "mc3510c", (kernel_ulong_t)&mc3510c_chip_info }, + { } }; MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id); +static const struct of_device_id mc3230_of_match[] = { + { .compatible = "mcube,mc3230", &mc3230_chip_info }, + { .compatible = "mcube,mc3510c", &mc3510c_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, mc3230_of_match); + static struct i2c_driver mc3230_driver = { .driver = { .name = "mc3230", + .of_match_table = mc3230_of_match, .pm = pm_sleep_ptr(&mc3230_pm_ops), }, .probe = mc3230_probe, diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 962d289065ab..05f5482f366e 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -497,14 +497,13 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&data->lock); ret = mma8452_read(data, buffer); mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -707,55 +706,45 @@ static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg); } -static int mma8452_write_raw(struct iio_dev *indio_dev, +static int __mma8452_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct mma8452_data *data = iio_priv(indio_dev); - int i, ret; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + int i, j, ret; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: i = mma8452_get_samp_freq_index(data, val, val2); - if (i < 0) { - ret = i; - break; - } + if (i < 0) + return i; + data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK; data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT; data->sleep_val = mma8452_calculate_sleep(data); - ret = mma8452_change_config(data, MMA8452_CTRL_REG1, - data->ctrl_reg1); - break; + return mma8452_change_config(data, MMA8452_CTRL_REG1, + data->ctrl_reg1); + case IIO_CHAN_INFO_SCALE: i = mma8452_get_scale_index(data, val, val2); - if (i < 0) { - ret = i; - break; - } + if (i < 0) + return i; data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK; data->data_cfg |= i; - ret = mma8452_change_config(data, MMA8452_DATA_CFG, - data->data_cfg); - break; + return mma8452_change_config(data, MMA8452_DATA_CFG, + data->data_cfg); + case IIO_CHAN_INFO_CALIBBIAS: - if (val < -128 || val > 127) { - ret = -EINVAL; - break; - } + if (val < -128 || val > 127) + return -EINVAL; - ret = mma8452_change_config(data, - MMA8452_OFF_X + chan->scan_index, - val); - break; + return mma8452_change_config(data, + MMA8452_OFF_X + chan->scan_index, + val); case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: if (val == 0 && val2 == 0) { @@ -764,29 +753,38 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK; ret = mma8452_set_hp_filter_frequency(data, val, val2); if (ret < 0) - break; + return ret; } - ret = mma8452_change_config(data, MMA8452_DATA_CFG, + return mma8452_change_config(data, MMA8452_DATA_CFG, data->data_cfg); - break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - ret = mma8452_get_odr_index(data); + j = mma8452_get_odr_index(data); for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) { - if (mma8452_os_ratio[i][ret] == val) { - ret = mma8452_set_power_mode(data, i); - break; - } + if (mma8452_os_ratio[i][j] == val) + return mma8452_set_power_mode(data, i); } - break; + + return -EINVAL; + default: - ret = -EINVAL; - break; + return -EINVAL; } +} + +static int mma8452_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - iio_device_release_direct_mode(indio_dev); + ret = __mma8452_write_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); return ret; } diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index e7fb860f3233..d31c11fbbe68 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -332,7 +332,7 @@ static const struct regmap_config msa311_regmap_config = { .wr_table = &msa311_writeable_table, .rd_table = &msa311_readable_table, .volatile_table = &msa311_volatile_table, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; #define MSA311_GENMASK(field) ({ \ @@ -594,23 +594,24 @@ static int msa311_read_raw_data(struct iio_dev *indio_dev, __le16 axis; int err; - err = pm_runtime_resume_and_get(dev); - if (err) - return err; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - err = iio_device_claim_direct_mode(indio_dev); - if (err) + err = pm_runtime_resume_and_get(dev); + if (err) { + iio_device_release_direct(indio_dev); return err; + } mutex_lock(&msa311->lock); err = msa311_get_axis(msa311, chan, &axis); mutex_unlock(&msa311->lock); - iio_device_release_direct_mode(indio_dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); + iio_device_release_direct(indio_dev); + if (err) { dev_err(dev, "can't get axis %s (%pe)\n", chan->datasheet_name, ERR_PTR(err)); @@ -756,18 +757,19 @@ static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2) unsigned int odr; int err; - err = pm_runtime_resume_and_get(dev); - if (err) - return err; - /* * Sampling frequency changing is prohibited when buffer mode is * enabled, because sometimes MSA311 chip returns outliers during * frequency values growing up in the read operation moment. */ - err = iio_device_claim_direct_mode(indio_dev); - if (err) + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + err = pm_runtime_resume_and_get(dev); + if (err) { + iio_device_release_direct(indio_dev); return err; + } err = -EINVAL; for (odr = 0; odr < ARRAY_SIZE(msa311_odr_table); odr++) @@ -779,11 +781,11 @@ static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2) break; } - iio_device_release_direct_mode(indio_dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); + iio_device_release_direct(indio_dev); + if (err) dev_err(dev, "can't update frequency (%pe)\n", ERR_PTR(err)); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 849c90203071..6529df1a498c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -33,6 +33,20 @@ config AD4000 To compile this driver as a module, choose M here: the module will be called ad4000. +config AD4030 + tristate "Analog Devices AD4030 ADC Driver" + depends on SPI + depends on GPIOLIB + select REGMAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD4030 and AD4630 high speed + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4030. + config AD4130 tristate "Analog Device AD4130 ADC Driver" depends on SPI @@ -51,9 +65,11 @@ config AD4130 config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI - select REGMAP_SPI select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER + select REGMAP + select SPI_OFFLOAD help Say yes here to build support for Analog Devices AD4695 and similar analog to digital converters (ADC). @@ -61,6 +77,20 @@ config AD4695 To compile this driver as a module, choose M here: the module will be called ad4695. +config AD4851 + tristate "Analog Device AD4851 DAS Driver" + depends on SPI + depends on PWM + select REGMAP_SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD4851, AD4852, + AD4853, AD4854, AD4855, AD4856, AD4857, AD4858, AD4858I high speed + data acquisition system (DAS). + + To compile this driver as a module, choose M here: the module will be + called ad4851. + config AD7091R tristate @@ -112,6 +142,16 @@ config AD7173 To compile this driver as a module, choose M here: the module will be called ad7173. +config AD7191 + tristate "Analog Devices AD7191 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7191. + + To compile this driver as a module, choose M here: the + module will be called ad7191. + config AD7192 tristate "Analog Devices AD7192 and similar ADC driver" depends on SPI @@ -188,7 +228,9 @@ config AD7298 config AD7380 tristate "Analog Devices AD7380 ADC driver" depends on SPI_MASTER + select SPI_OFFLOAD select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGER select IIO_TRIGGERED_BUFFER help @@ -360,7 +402,9 @@ config AD7923 config AD7944 tristate "Analog Devices AD7944 and similar ADCs driver" depends on SPI + select SPI_OFFLOAD select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER help Say yes here to build support for Analog Devices @@ -1467,6 +1511,16 @@ config TI_ADS1119 This driver can also be built as a module. If so, the module will be called ti-ads1119. +config TI_ADS7138 + tristate "Texas Instruments ADS7128 and ADS7138 ADC driver" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS7128 and + ADS7138 8-channel A/D converters with 12-bit resolution. + + This driver can also be built as a module. If so, the module will be + called ti-ads7138. + config TI_ADS7924 tristate "Texas Instruments ADS7924 ADC" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ee19afba62b7..3e918c3eec69 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -7,13 +7,16 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD4000) += ad4000.o +obj-$(CONFIG_AD4030) += ad4030.o obj-$(CONFIG_AD4130) += ad4130.o obj-$(CONFIG_AD4695) += ad4695.o +obj-$(CONFIG_AD4851) += ad4851.o obj-$(CONFIG_AD7091R) += ad7091r-base.o obj-$(CONFIG_AD7091R5) += ad7091r5.o obj-$(CONFIG_AD7091R8) += ad7091r8.o obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7173) += ad7173.o +obj-$(CONFIG_AD7191) += ad7191.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7280) += ad7280a.o @@ -133,6 +136,7 @@ obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o +obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index 1d556a842a68..4fe8dee48da9 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -535,12 +535,16 @@ static int ad4000_read_raw(struct iio_dev *indio_dev, int *val2, long info) { struct ad4000_state *st = iio_priv(indio_dev); + int ret; switch (info) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return ad4000_single_conversion(indio_dev, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4000_single_conversion(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: *val = st->scale_tbl[st->span_comp][0]; *val2 = st->scale_tbl[st->span_comp][1]; @@ -585,36 +589,46 @@ static int ad4000_write_raw_get_fmt(struct iio_dev *indio_dev, } } -static int ad4000_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, - long mask) +static int __ad4000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val2) { struct ad4000_state *st = iio_priv(indio_dev); unsigned int reg_val; bool span_comp_en; int ret; - switch (mask) { - case IIO_CHAN_INFO_SCALE: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - guard(mutex)(&st->lock); + guard(mutex)(&st->lock); + + ret = ad4000_read_reg(st, ®_val); + if (ret < 0) + return ret; + + span_comp_en = val2 == st->scale_tbl[1][1]; + reg_val &= ~AD4000_CFG_SPAN_COMP; + reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en); - ret = ad4000_read_reg(st, ®_val); - if (ret < 0) - return ret; + ret = ad4000_write_reg(st, reg_val); + if (ret < 0) + return ret; - span_comp_en = val2 == st->scale_tbl[1][1]; - reg_val &= ~AD4000_CFG_SPAN_COMP; - reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en); + st->span_comp = span_comp_en; + return 0; +} - ret = ad4000_write_reg(st, reg_val); - if (ret < 0) - return ret; +static int ad4000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; - st->span_comp = span_comp_en; - return 0; - } - unreachable(); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __ad4000_write_raw(indio_dev, chan, val2); + iio_device_release_direct(indio_dev); + return ret; default: return -EINVAL; } diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c new file mode 100644 index 000000000000..9a020680885d --- /dev/null +++ b/drivers/iio/adc/ad4030.c @@ -0,0 +1,1230 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4030 and AD4630 ADC family driver. + * + * Copyright 2024 Analog Devices, Inc. + * Copyright 2024 BayLibre, SAS + * + * based on code from: + * Analog Devices, Inc. + * Sergiu Cuciurean <sergiu.cuciurean@analog.com> + * Nuno Sa <nuno.sa@analog.com> + * Marcelo Schmitt <marcelo.schmitt@analog.com> + * Liviu Adace <liviu.adace@analog.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#define AD4030_REG_INTERFACE_CONFIG_A 0x00 +#define AD4030_REG_INTERFACE_CONFIG_A_SW_RESET (BIT(0) | BIT(7)) +#define AD4030_REG_INTERFACE_CONFIG_B 0x01 +#define AD4030_REG_DEVICE_CONFIG 0x02 +#define AD4030_REG_CHIP_TYPE 0x03 +#define AD4030_REG_PRODUCT_ID_L 0x04 +#define AD4030_REG_PRODUCT_ID_H 0x05 +#define AD4030_REG_CHIP_GRADE 0x06 +#define AD4030_REG_CHIP_GRADE_AD4030_24_GRADE 0x10 +#define AD4030_REG_CHIP_GRADE_AD4630_16_GRADE 0x03 +#define AD4030_REG_CHIP_GRADE_AD4630_24_GRADE 0x00 +#define AD4030_REG_CHIP_GRADE_AD4632_16_GRADE 0x05 +#define AD4030_REG_CHIP_GRADE_AD4632_24_GRADE 0x02 +#define AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE GENMASK(7, 3) +#define AD4030_REG_SCRATCH_PAD 0x0A +#define AD4030_REG_SPI_REVISION 0x0B +#define AD4030_REG_VENDOR_L 0x0C +#define AD4030_REG_VENDOR_H 0x0D +#define AD4030_REG_STREAM_MODE 0x0E +#define AD4030_REG_INTERFACE_CONFIG_C 0x10 +#define AD4030_REG_INTERFACE_STATUS_A 0x11 +#define AD4030_REG_EXIT_CFG_MODE 0x14 +#define AD4030_REG_EXIT_CFG_MODE_EXIT_MSK BIT(0) +#define AD4030_REG_AVG 0x15 +#define AD4030_REG_AVG_MASK_AVG_SYNC BIT(7) +#define AD4030_REG_AVG_MASK_AVG_VAL GENMASK(4, 0) +#define AD4030_REG_OFFSET_X0_0 0x16 +#define AD4030_REG_OFFSET_X0_1 0x17 +#define AD4030_REG_OFFSET_X0_2 0x18 +#define AD4030_REG_OFFSET_X1_0 0x19 +#define AD4030_REG_OFFSET_X1_1 0x1A +#define AD4030_REG_OFFSET_X1_2 0x1B +#define AD4030_REG_OFFSET_BYTES_NB 3 +#define AD4030_REG_OFFSET_CHAN(ch) \ + (AD4030_REG_OFFSET_X0_2 + (AD4030_REG_OFFSET_BYTES_NB * (ch))) +#define AD4030_REG_GAIN_X0_LSB 0x1C +#define AD4030_REG_GAIN_X0_MSB 0x1D +#define AD4030_REG_GAIN_X1_LSB 0x1E +#define AD4030_REG_GAIN_X1_MSB 0x1F +#define AD4030_REG_GAIN_MAX_GAIN 1999970 +#define AD4030_REG_GAIN_BYTES_NB 2 +#define AD4030_REG_GAIN_CHAN(ch) \ + (AD4030_REG_GAIN_X0_MSB + (AD4030_REG_GAIN_BYTES_NB * (ch))) +#define AD4030_REG_MODES 0x20 +#define AD4030_REG_MODES_MASK_OUT_DATA_MODE GENMASK(2, 0) +#define AD4030_REG_MODES_MASK_LANE_MODE GENMASK(7, 6) +#define AD4030_REG_OSCILATOR 0x21 +#define AD4030_REG_IO 0x22 +#define AD4030_REG_IO_MASK_IO2X BIT(1) +#define AD4030_REG_PAT0 0x23 +#define AD4030_REG_PAT1 0x24 +#define AD4030_REG_PAT2 0x25 +#define AD4030_REG_PAT3 0x26 +#define AD4030_REG_DIG_DIAG 0x34 +#define AD4030_REG_DIG_ERR 0x35 + +/* Sequence starting with "1 0 1" to enable reg access */ +#define AD4030_REG_ACCESS 0xA0 + +#define AD4030_MAX_IIO_SAMPLE_SIZE_BUFFERED BITS_TO_BYTES(64) +#define AD4030_MAX_HARDWARE_CHANNEL_NB 2 +#define AD4030_MAX_IIO_CHANNEL_NB 5 +#define AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK 0b10 +#define AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK 0b1100 +#define AD4030_GAIN_MIDLE_POINT 0x8000 +/* + * This accounts for 1 sample per channel plus one s64 for the timestamp, + * aligned on a s64 boundary + */ +#define AD4030_MAXIMUM_RX_BUFFER_SIZE \ + (ALIGN(AD4030_MAX_IIO_SAMPLE_SIZE_BUFFERED * \ + AD4030_MAX_HARDWARE_CHANNEL_NB, \ + sizeof(s64)) + sizeof(s64)) + +#define AD4030_VREF_MIN_UV (4096 * MILLI) +#define AD4030_VREF_MAX_UV (5000 * MILLI) +#define AD4030_VIO_THRESHOLD_UV (1400 * MILLI) +#define AD4030_SPI_MAX_XFER_LEN 8 +#define AD4030_SPI_MAX_REG_XFER_SPEED (80 * MEGA) +#define AD4030_TCNVH_NS 10 +#define AD4030_TCNVL_NS 20 +#define AD4030_TCYC_NS 500 +#define AD4030_TCYC_ADJUSTED_NS (AD4030_TCYC_NS - AD4030_TCNVL_NS) +#define AD4030_TRESET_PW_NS 50 +#define AD4632_TCYC_NS 2000 +#define AD4632_TCYC_ADJUSTED_NS (AD4632_TCYC_NS - AD4030_TCNVL_NS) +#define AD4030_TRESET_COM_DELAY_MS 750 + +enum ad4030_out_mode { + AD4030_OUT_DATA_MD_DIFF, + AD4030_OUT_DATA_MD_16_DIFF_8_COM, + AD4030_OUT_DATA_MD_24_DIFF_8_COM, + AD4030_OUT_DATA_MD_30_AVERAGED_DIFF, + AD4030_OUT_DATA_MD_32_PATTERN, +}; + +enum { + AD4030_LANE_MD_1_PER_CH, + AD4030_LANE_MD_2_PER_CH, + AD4030_LANE_MD_4_PER_CH, + AD4030_LANE_MD_INTERLEAVED, +}; + +enum { + AD4030_SCAN_TYPE_NORMAL, + AD4030_SCAN_TYPE_AVG, +}; + +struct ad4030_chip_info { + const char *name; + const unsigned long *available_masks; + const struct iio_chan_spec channels[AD4030_MAX_IIO_CHANNEL_NB]; + u8 grade; + u8 precision_bits; + /* Number of hardware channels */ + int num_voltage_inputs; + unsigned int tcyc_ns; +}; + +struct ad4030_state { + struct spi_device *spi; + struct regmap *regmap; + const struct ad4030_chip_info *chip; + const struct iio_scan_type *current_scan_type; + struct gpio_desc *cnv_gpio; + int vref_uv; + int vio_uv; + int offset_avail[3]; + unsigned int avg_log2; + enum ad4030_out_mode mode; + + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 tx_data[AD4030_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN); + union { + u8 raw[AD4030_MAXIMUM_RX_BUFFER_SIZE]; + struct { + s32 diff; + u8 common; + } single; + struct { + s32 diff[2]; + u8 common[2]; + } dual; + } rx_data; +}; + +/* + * For a chip with 2 hardware channel this will be used to create 2 common-mode + * channels: + * - voltage4 + * - voltage5 + * As the common-mode channels are after the differential ones, we compute the + * channel number like this: + * - _idx is the scan_index (the order in the output buffer) + * - _ch is the hardware channel number this common-mode channel is related + * - _idx - _ch gives us the number of channel in the chip + * - _idx - _ch * 2 is the starting number of the common-mode channels, since + * for each differential channel there is a common-mode channel + * - _idx - _ch * 2 + _ch gives the channel number for this specific common-mode + * channel + */ +#define AD4030_CHAN_CMO(_idx, _ch) { \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_ch), \ + .channel = ((_idx) - (_ch)) * 2 + (_ch), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .storagebits = 8, \ + .realbits = 8, \ + .endianness = IIO_BE, \ + }, \ +} + +/* + * For a chip with 2 hardware channel this will be used to create 2 differential + * channels: + * - voltage0-voltage1 + * - voltage2-voltage3 + */ +#define AD4030_CHAN_DIFF(_idx, _scan_type) { \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_idx), \ + .channel = (_idx) * 2, \ + .channel2 = (_idx) * 2 + 1, \ + .scan_index = (_idx), \ + .differential = true, \ + .has_ext_scan_type = 1, \ + .ext_scan_type = _scan_type, \ + .num_ext_scan_type = ARRAY_SIZE(_scan_type), \ +} + +static const int ad4030_average_modes[] = { + 1, 2, 4, 8, 16, 32, 64, 128, + 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, + 65536, +}; + +static int ad4030_enter_config_mode(struct ad4030_state *st) +{ + st->tx_data[0] = AD4030_REG_ACCESS; + + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .bits_per_word = 8, + .len = 1, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4030_exit_config_mode(struct ad4030_state *st) +{ + st->tx_data[0] = 0; + st->tx_data[1] = AD4030_REG_EXIT_CFG_MODE; + st->tx_data[2] = AD4030_REG_EXIT_CFG_MODE_EXIT_MSK; + + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .bits_per_word = 8, + .len = 3, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4030_spi_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + int ret; + struct ad4030_state *st = context; + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .rx_buf = st->rx_data.raw, + .bits_per_word = 8, + .len = reg_size + val_size, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + if (xfer.len > sizeof(st->tx_data) || + xfer.len > sizeof(st->rx_data.raw)) + return -EINVAL; + + ret = ad4030_enter_config_mode(st); + if (ret) + return ret; + + memset(st->tx_data, 0, sizeof(st->tx_data)); + memcpy(st->tx_data, reg, reg_size); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + memcpy(val, &st->rx_data.raw[reg_size], val_size); + + return ad4030_exit_config_mode(st); +} + +static int ad4030_spi_write(void *context, const void *data, size_t count) +{ + int ret; + struct ad4030_state *st = context; + bool is_reset = count >= 3 && + ((u8 *)data)[0] == 0 && + ((u8 *)data)[1] == 0 && + ((u8 *)data)[2] == 0x81; + struct spi_transfer xfer = { + .tx_buf = st->tx_data, + .bits_per_word = 8, + .len = count, + .speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED, + }; + + if (count > sizeof(st->tx_data)) + return -EINVAL; + + ret = ad4030_enter_config_mode(st); + if (ret) + return ret; + + memcpy(st->tx_data, data, count); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + /* + * From datasheet: "After a [...] reset, no SPI commands or conversions + * can be started for 750us" + * After a reset we are in conversion mode, no need to exit config mode + */ + if (is_reset) { + fsleep(750); + return 0; + } + + return ad4030_exit_config_mode(st); +} + +static const struct regmap_bus ad4030_regmap_bus = { + .read = ad4030_spi_read, + .write = ad4030_spi_write, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_range ad4030_regmap_rd_range[] = { + regmap_reg_range(AD4030_REG_INTERFACE_CONFIG_A, AD4030_REG_CHIP_GRADE), + regmap_reg_range(AD4030_REG_SCRATCH_PAD, AD4030_REG_STREAM_MODE), + regmap_reg_range(AD4030_REG_INTERFACE_CONFIG_C, + AD4030_REG_INTERFACE_STATUS_A), + regmap_reg_range(AD4030_REG_EXIT_CFG_MODE, AD4030_REG_PAT3), + regmap_reg_range(AD4030_REG_DIG_DIAG, AD4030_REG_DIG_ERR), +}; + +static const struct regmap_range ad4030_regmap_wr_range[] = { + regmap_reg_range(AD4030_REG_CHIP_TYPE, AD4030_REG_CHIP_GRADE), + regmap_reg_range(AD4030_REG_SPI_REVISION, AD4030_REG_VENDOR_H), +}; + +static const struct regmap_access_table ad4030_regmap_rd_table = { + .yes_ranges = ad4030_regmap_rd_range, + .n_yes_ranges = ARRAY_SIZE(ad4030_regmap_rd_range), +}; + +static const struct regmap_access_table ad4030_regmap_wr_table = { + .no_ranges = ad4030_regmap_wr_range, + .n_no_ranges = ARRAY_SIZE(ad4030_regmap_wr_range), +}; + +static const struct regmap_config ad4030_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = 0x80, + .rd_table = &ad4030_regmap_rd_table, + .wr_table = &ad4030_regmap_wr_table, + .max_register = AD4030_REG_DIG_ERR, +}; + +static int ad4030_get_chan_scale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2) +{ + struct ad4030_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + + if (chan->differential) { + scan_type = iio_get_current_scan_type(indio_dev, + st->chip->channels); + *val = (st->vref_uv * 2) / MILLI; + *val2 = scan_type->realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + + *val = st->vref_uv / MILLI; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad4030_get_chan_calibscale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u16 gain; + int ret; + + ret = regmap_bulk_read(st->regmap, AD4030_REG_GAIN_CHAN(chan->address), + st->rx_data.raw, AD4030_REG_GAIN_BYTES_NB); + if (ret) + return ret; + + gain = get_unaligned_be16(st->rx_data.raw); + + /* From datasheet: multiplied output = input × gain word/0x8000 */ + *val = gain / AD4030_GAIN_MIDLE_POINT; + *val2 = mul_u64_u32_div(gain % AD4030_GAIN_MIDLE_POINT, NANO, + AD4030_GAIN_MIDLE_POINT); + + return IIO_VAL_INT_PLUS_NANO; +} + +/* Returns the offset where 1 LSB = (VREF/2^precision_bits - 1)/gain */ +static int ad4030_get_chan_calibbias(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_bulk_read(st->regmap, + AD4030_REG_OFFSET_CHAN(chan->address), + st->rx_data.raw, AD4030_REG_OFFSET_BYTES_NB); + if (ret) + return ret; + + switch (st->chip->precision_bits) { + case 16: + *val = sign_extend32(get_unaligned_be16(st->rx_data.raw), 15); + return IIO_VAL_INT; + + case 24: + *val = sign_extend32(get_unaligned_be24(st->rx_data.raw), 23); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ad4030_set_chan_calibscale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int gain_int, + int gain_frac) +{ + struct ad4030_state *st = iio_priv(indio_dev); + u64 gain; + + if (gain_int < 0 || gain_frac < 0) + return -EINVAL; + + gain = mul_u32_u32(gain_int, MICRO) + gain_frac; + + if (gain > AD4030_REG_GAIN_MAX_GAIN) + return -EINVAL; + + put_unaligned_be16(DIV_ROUND_CLOSEST_ULL(gain * AD4030_GAIN_MIDLE_POINT, + MICRO), + st->tx_data); + + return regmap_bulk_write(st->regmap, + AD4030_REG_GAIN_CHAN(chan->address), + st->tx_data, AD4030_REG_GAIN_BYTES_NB); +} + +static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int offset) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (offset < st->offset_avail[0] || offset > st->offset_avail[2]) + return -EINVAL; + + st->tx_data[2] = 0; + + switch (st->chip->precision_bits) { + case 16: + put_unaligned_be16(offset, st->tx_data); + break; + + case 24: + put_unaligned_be24(offset, st->tx_data); + break; + + default: + return -EINVAL; + } + + return regmap_bulk_write(st->regmap, + AD4030_REG_OFFSET_CHAN(chan->address), + st->tx_data, AD4030_REG_OFFSET_BYTES_NB); +} + +static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val) +{ + struct ad4030_state *st = iio_priv(dev); + unsigned int avg_log2 = ilog2(avg_val); + unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1; + int ret; + + if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx]) + return -EINVAL; + + ret = regmap_write(st->regmap, AD4030_REG_AVG, + AD4030_REG_AVG_MASK_AVG_SYNC | + FIELD_PREP(AD4030_REG_AVG_MASK_AVG_VAL, avg_log2)); + if (ret) + return ret; + + st->avg_log2 = avg_log2; + + return 0; +} + +static bool ad4030_is_common_byte_asked(struct ad4030_state *st, + unsigned int mask) +{ + return mask & (st->chip->num_voltage_inputs == 1 ? + AD4030_SINGLE_COMMON_BYTE_CHANNELS_MASK : + AD4030_DUAL_COMMON_BYTE_CHANNELS_MASK); +} + +static int ad4030_set_mode(struct iio_dev *indio_dev, unsigned long mask) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + if (st->avg_log2 > 0) { + st->mode = AD4030_OUT_DATA_MD_30_AVERAGED_DIFF; + } else if (ad4030_is_common_byte_asked(st, mask)) { + switch (st->chip->precision_bits) { + case 16: + st->mode = AD4030_OUT_DATA_MD_16_DIFF_8_COM; + break; + + case 24: + st->mode = AD4030_OUT_DATA_MD_24_DIFF_8_COM; + break; + + default: + return -EINVAL; + } + } else { + st->mode = AD4030_OUT_DATA_MD_DIFF; + } + + st->current_scan_type = iio_get_current_scan_type(indio_dev, + st->chip->channels); + if (IS_ERR(st->current_scan_type)) + return PTR_ERR(st->current_scan_type); + + return regmap_update_bits(st->regmap, AD4030_REG_MODES, + AD4030_REG_MODES_MASK_OUT_DATA_MODE, + st->mode); +} + +/* + * Descramble 2 32bits numbers out of a 64bits. The bits are interleaved: + * 1 bit for first number, 1 bit for the second, and so on... + */ +static void ad4030_extract_interleaved(u8 *src, u32 *ch0, u32 *ch1) +{ + u8 h0, h1, l0, l1; + u32 out0, out1; + u8 *out0_raw = (u8 *)&out0; + u8 *out1_raw = (u8 *)&out1; + + for (int i = 0; i < 4; i++) { + h0 = src[i * 2]; + l1 = src[i * 2 + 1]; + h1 = h0 << 1; + l0 = l1 >> 1; + + h0 &= 0xAA; + l0 &= 0x55; + h1 &= 0xAA; + l1 &= 0x55; + + h0 = (h0 | h0 << 001) & 0xCC; + h1 = (h1 | h1 << 001) & 0xCC; + l0 = (l0 | l0 >> 001) & 0x33; + l1 = (l1 | l1 >> 001) & 0x33; + h0 = (h0 | h0 << 002) & 0xF0; + h1 = (h1 | h1 << 002) & 0xF0; + l0 = (l0 | l0 >> 002) & 0x0F; + l1 = (l1 | l1 >> 002) & 0x0F; + + out0_raw[i] = h0 | l0; + out1_raw[i] = h1 | l1; + } + + *ch0 = out0; + *ch1 = out1; +} + +static int ad4030_conversion(struct iio_dev *indio_dev) +{ + struct ad4030_state *st = iio_priv(indio_dev); + unsigned char diff_realbytes = + BITS_TO_BYTES(st->current_scan_type->realbits); + unsigned char diff_storagebytes = + BITS_TO_BYTES(st->current_scan_type->storagebits); + unsigned int bytes_to_read; + unsigned long cnv_nb = BIT(st->avg_log2); + unsigned int i; + int ret; + + /* Number of bytes for one differential channel */ + bytes_to_read = diff_realbytes; + /* Add one byte if we are using a differential + common byte mode */ + bytes_to_read += (st->mode == AD4030_OUT_DATA_MD_24_DIFF_8_COM || + st->mode == AD4030_OUT_DATA_MD_16_DIFF_8_COM) ? 1 : 0; + /* Mulitiply by the number of hardware channels */ + bytes_to_read *= st->chip->num_voltage_inputs; + + for (i = 0; i < cnv_nb; i++) { + gpiod_set_value_cansleep(st->cnv_gpio, 1); + ndelay(AD4030_TCNVH_NS); + gpiod_set_value_cansleep(st->cnv_gpio, 0); + ndelay(st->chip->tcyc_ns); + } + + ret = spi_read(st->spi, st->rx_data.raw, bytes_to_read); + if (ret) + return ret; + + if (st->chip->num_voltage_inputs == 2) + ad4030_extract_interleaved(st->rx_data.raw, + &st->rx_data.dual.diff[0], + &st->rx_data.dual.diff[1]); + + if (st->mode != AD4030_OUT_DATA_MD_16_DIFF_8_COM && + st->mode != AD4030_OUT_DATA_MD_24_DIFF_8_COM) + return 0; + + if (st->chip->num_voltage_inputs == 1) { + st->rx_data.single.common = st->rx_data.raw[diff_realbytes]; + return 0; + } + + for (i = 0; i < st->chip->num_voltage_inputs; i++) + st->rx_data.dual.common[i] = + st->rx_data.raw[diff_storagebytes * i + diff_realbytes]; + + return 0; +} + +static int ad4030_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4030_set_mode(indio_dev, BIT(chan->scan_index)); + if (ret) + return ret; + + st->current_scan_type = iio_get_current_scan_type(indio_dev, + st->chip->channels); + if (IS_ERR(st->current_scan_type)) + return PTR_ERR(st->current_scan_type); + + ret = ad4030_conversion(indio_dev); + if (ret) + return ret; + + if (chan->differential) + if (st->chip->num_voltage_inputs == 1) + *val = st->rx_data.single.diff; + else + *val = st->rx_data.dual.diff[chan->address]; + else + if (st->chip->num_voltage_inputs == 1) + *val = st->rx_data.single.common; + else + *val = st->rx_data.dual.common[chan->address]; + + return IIO_VAL_INT; +} + +static irqreturn_t ad4030_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + ret = ad4030_conversion(indio_dev); + if (ret) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_data.raw, + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const int ad4030_gain_avail[3][2] = { + { 0, 0 }, + { 0, 30518 }, + { 1, 999969482 }, +}; + +static int ad4030_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, + int *length, long mask) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + *vals = st->offset_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + + case IIO_CHAN_INFO_CALIBSCALE: + *vals = (void *)ad4030_gain_avail; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_RANGE; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4030_average_modes; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(ad4030_average_modes); + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static int ad4030_read_raw_dispatch(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad4030_single_conversion(indio_dev, chan, val); + + case IIO_CHAN_INFO_CALIBSCALE: + return ad4030_get_chan_calibscale(indio_dev, chan, val, val2); + + case IIO_CHAN_INFO_CALIBBIAS: + return ad4030_get_chan_calibbias(indio_dev, chan, val); + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = BIT(st->avg_log2); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ad4030_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long info) +{ + int ret; + + if (info == IIO_CHAN_INFO_SCALE) + return ad4030_get_chan_scale(indio_dev, chan, val, val2); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4030_read_raw_dispatch(indio_dev, chan, val, val2, info); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4030_write_raw_dispatch(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + switch (info) { + case IIO_CHAN_INFO_CALIBSCALE: + return ad4030_set_chan_calibscale(indio_dev, chan, val, val2); + + case IIO_CHAN_INFO_CALIBBIAS: + if (val2 != 0) + return -EINVAL; + return ad4030_set_chan_calibbias(indio_dev, chan, val); + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4030_set_avg_frame_len(indio_dev, val); + + default: + return -EINVAL; + } +} + +static int ad4030_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4030_write_raw_dispatch(indio_dev, chan, val, val2, info); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4030_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + const struct ad4030_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) + ret = regmap_read(st->regmap, reg, readval); + else + ret = regmap_write(st->regmap, reg, writeval); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4030_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->differential) + return sprintf(label, "differential%lu\n", chan->address); + return sprintf(label, "common-mode%lu\n", chan->address); +} + +static int ad4030_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + return st->avg_log2 ? AD4030_SCAN_TYPE_AVG : AD4030_SCAN_TYPE_NORMAL; +} + +static const struct iio_info ad4030_iio_info = { + .read_avail = ad4030_read_avail, + .read_raw = ad4030_read_raw, + .write_raw = ad4030_write_raw, + .debugfs_reg_access = ad4030_reg_access, + .read_label = ad4030_read_label, + .get_current_scan_type = ad4030_get_current_scan_type, +}; + +static int ad4030_buffer_preenable(struct iio_dev *indio_dev) +{ + return ad4030_set_mode(indio_dev, *indio_dev->active_scan_mask); +} + +static bool ad4030_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4030_state *st = iio_priv(indio_dev); + + /* Asking for both common channels and averaging */ + if (st->avg_log2 && ad4030_is_common_byte_asked(st, *scan_mask)) + return false; + + return true; +} + +static const struct iio_buffer_setup_ops ad4030_buffer_setup_ops = { + .preenable = ad4030_buffer_preenable, + .validate_scan_mask = ad4030_validate_scan_mask, +}; + +static int ad4030_regulators_get(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + static const char * const ids[] = { "vdd-5v", "vdd-1v8" }; + int ret; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ids), ids); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + st->vio_uv = devm_regulator_get_enable_read_voltage(dev, "vio"); + if (st->vio_uv < 0) + return dev_err_probe(dev, st->vio_uv, + "Failed to enable and read vio voltage\n"); + + st->vref_uv = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (st->vref_uv < 0) { + if (st->vref_uv != -ENODEV) + return dev_err_probe(dev, st->vref_uv, + "Failed to read ref voltage\n"); + + /* if not using optional REF, the REFIN must be used */ + st->vref_uv = devm_regulator_get_enable_read_voltage(dev, + "refin"); + if (st->vref_uv < 0) + return dev_err_probe(dev, st->vref_uv, + "Failed to read refin voltage\n"); + } + + return 0; +} + +static int ad4030_reset(struct ad4030_state *st) +{ + struct device *dev = &st->spi->dev; + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset)) + return dev_err_probe(dev, PTR_ERR(reset), + "Failed to get reset GPIO\n"); + + if (reset) { + ndelay(50); + gpiod_set_value_cansleep(reset, 0); + return 0; + } + + return regmap_write(st->regmap, AD4030_REG_INTERFACE_CONFIG_A, + AD4030_REG_INTERFACE_CONFIG_A_SW_RESET); +} + +static int ad4030_detect_chip_info(const struct ad4030_state *st) +{ + unsigned int grade; + int ret; + + ret = regmap_read(st->regmap, AD4030_REG_CHIP_GRADE, &grade); + if (ret) + return ret; + + grade = FIELD_GET(AD4030_REG_CHIP_GRADE_MASK_CHIP_GRADE, grade); + if (grade != st->chip->grade) + dev_warn(&st->spi->dev, "Unknown grade(0x%x) for %s\n", grade, + st->chip->name); + + return 0; +} + +static int ad4030_config(struct ad4030_state *st) +{ + int ret; + u8 reg_modes; + + st->offset_avail[0] = (int)BIT(st->chip->precision_bits - 1) * -1; + st->offset_avail[1] = 1; + st->offset_avail[2] = BIT(st->chip->precision_bits - 1) - 1; + + if (st->chip->num_voltage_inputs > 1) + reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE, + AD4030_LANE_MD_INTERLEAVED); + else + reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE, + AD4030_LANE_MD_1_PER_CH); + + ret = regmap_write(st->regmap, AD4030_REG_MODES, reg_modes); + if (ret) + return ret; + + if (st->vio_uv < AD4030_VIO_THRESHOLD_UV) + return regmap_write(st->regmap, AD4030_REG_IO, + AD4030_REG_IO_MASK_IO2X); + + return 0; +} + +static int ad4030_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4030_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->regmap = devm_regmap_init(dev, &ad4030_regmap_bus, st, + &ad4030_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + st->chip = spi_get_device_match_data(spi); + if (!st->chip) + return -EINVAL; + + ret = ad4030_regulators_get(st); + if (ret) + return ret; + + /* + * From datasheet: "Perform a reset no sooner than 3ms after the power + * supplies are valid and stable" + */ + fsleep(3000); + + ret = ad4030_reset(st); + if (ret) + return ret; + + ret = ad4030_detect_chip_info(st); + if (ret) + return ret; + + ret = ad4030_config(st); + if (ret) + return ret; + + st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "Failed to get cnv gpio\n"); + + /* + * One hardware channel is split in two software channels when using + * common byte mode. Add one more channel for the timestamp. + */ + indio_dev->num_channels = 2 * st->chip->num_voltage_inputs + 1; + indio_dev->name = st->chip->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad4030_iio_info; + indio_dev->channels = st->chip->channels; + indio_dev->available_scan_masks = st->chip->available_masks; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4030_trigger_handler, + &ad4030_buffer_setup_ops); + if (ret) + return dev_err_probe(dev, ret, + "Failed to setup triggered buffer\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const unsigned long ad4030_channel_masks[] = { + /* Differential only */ + BIT(0), + /* Differential and common-mode voltage */ + GENMASK(1, 0), + 0, +}; + +static const unsigned long ad4630_channel_masks[] = { + /* Differential only */ + BIT(1) | BIT(0), + /* Differential with common byte */ + GENMASK(3, 0), + 0, +}; + +static const struct iio_scan_type ad4030_24_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 32, + .realbits = 24, + .shift = 8, + .endianness = IIO_BE, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .storagebits = 32, + .realbits = 30, + .shift = 2, + .endianness = IIO_BE, + }, +}; + +static const struct iio_scan_type ad4030_16_scan_types[] = { + [AD4030_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 32, + .realbits = 16, + .shift = 16, + .endianness = IIO_BE, + }, + [AD4030_SCAN_TYPE_AVG] = { + .sign = 's', + .storagebits = 32, + .realbits = 30, + .shift = 2, + .endianness = IIO_BE, + } +}; + +static const struct ad4030_chip_info ad4030_24_chip_info = { + .name = "ad4030-24", + .available_masks = ad4030_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_CMO(1, 0), + IIO_CHAN_SOFT_TIMESTAMP(2), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4030_24_GRADE, + .precision_bits = 24, + .num_voltage_inputs = 1, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, +}; + +static const struct ad4030_chip_info ad4630_16_chip_info = { + .name = "ad4630-16", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_DIFF(1, ad4030_16_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4630_16_GRADE, + .precision_bits = 16, + .num_voltage_inputs = 2, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, +}; + +static const struct ad4030_chip_info ad4630_24_chip_info = { + .name = "ad4630-24", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_DIFF(1, ad4030_24_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4630_24_GRADE, + .precision_bits = 24, + .num_voltage_inputs = 2, + .tcyc_ns = AD4030_TCYC_ADJUSTED_NS, +}; + +static const struct ad4030_chip_info ad4632_16_chip_info = { + .name = "ad4632-16", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_16_scan_types), + AD4030_CHAN_DIFF(1, ad4030_16_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4632_16_GRADE, + .precision_bits = 16, + .num_voltage_inputs = 2, + .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, +}; + +static const struct ad4030_chip_info ad4632_24_chip_info = { + .name = "ad4632-24", + .available_masks = ad4630_channel_masks, + .channels = { + AD4030_CHAN_DIFF(0, ad4030_24_scan_types), + AD4030_CHAN_DIFF(1, ad4030_24_scan_types), + AD4030_CHAN_CMO(2, 0), + AD4030_CHAN_CMO(3, 1), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .grade = AD4030_REG_CHIP_GRADE_AD4632_24_GRADE, + .precision_bits = 24, + .num_voltage_inputs = 2, + .tcyc_ns = AD4632_TCYC_ADJUSTED_NS, +}; + +static const struct spi_device_id ad4030_id_table[] = { + { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info }, + { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info }, + { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info }, + { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info }, + { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4030_id_table); + +static const struct of_device_id ad4030_of_match[] = { + { .compatible = "adi,ad4030-24", .data = &ad4030_24_chip_info }, + { .compatible = "adi,ad4630-16", .data = &ad4630_16_chip_info }, + { .compatible = "adi,ad4630-24", .data = &ad4630_24_chip_info }, + { .compatible = "adi,ad4632-16", .data = &ad4632_16_chip_info }, + { .compatible = "adi,ad4632-24", .data = &ad4632_24_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4030_of_match); + +static struct spi_driver ad4030_driver = { + .driver = { + .name = "ad4030", + .of_match_table = ad4030_of_match, + }, + .probe = ad4030_probe, + .id_table = ad4030_id_table, +}; +module_spi_driver(ad4030_driver); + +MODULE_AUTHOR("Esteban Blanc <eblanc@baylibre.com>"); +MODULE_DESCRIPTION("Analog Devices AD4630 ADC family driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index de32cc9d18c5..0f4c9cd6c102 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -6,6 +6,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> @@ -203,7 +204,7 @@ enum ad4130_mode { AD4130_MODE_IDLE = 0b0100, }; -enum ad4130_filter_mode { +enum ad4130_filter_type { AD4130_FILTER_SINC4, AD4130_FILTER_SINC4_SINC1, AD4130_FILTER_SINC3, @@ -223,6 +224,10 @@ enum ad4130_pin_function { AD4130_PIN_FN_VBIAS = BIT(3), }; +/* + * If you make adaptations in this struct, you most likely also have to adapt + * ad4130_setup_info_eq(), too. + */ struct ad4130_setup_info { unsigned int iout0_val; unsigned int iout1_val; @@ -230,7 +235,7 @@ struct ad4130_setup_info { unsigned int pga; unsigned int fs; u32 ref_sel; - enum ad4130_filter_mode filter_mode; + enum ad4130_filter_type filter_type; bool ref_bufp; bool ref_bufm; }; @@ -251,7 +256,7 @@ struct ad4130_chan_info { }; struct ad4130_filter_config { - enum ad4130_filter_mode filter_mode; + enum ad4130_filter_type filter_type; unsigned int odr_div; unsigned int fs_max; enum iio_available_type samp_freq_avail_type; @@ -337,9 +342,9 @@ static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = { [AD4130_BURNOUT_4000NA] = 4000, }; -#define AD4130_VARIABLE_ODR_CONFIG(_filter_mode, _odr_div, _fs_max) \ +#define AD4130_VARIABLE_ODR_CONFIG(_filter_type, _odr_div, _fs_max) \ { \ - .filter_mode = (_filter_mode), \ + .filter_type = (_filter_type), \ .odr_div = (_odr_div), \ .fs_max = (_fs_max), \ .samp_freq_avail_type = IIO_AVAIL_RANGE, \ @@ -350,9 +355,9 @@ static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = { }, \ } -#define AD4130_FIXED_ODR_CONFIG(_filter_mode, _odr_div) \ +#define AD4130_FIXED_ODR_CONFIG(_filter_type, _odr_div) \ { \ - .filter_mode = (_filter_mode), \ + .filter_type = (_filter_type), \ .odr_div = (_odr_div), \ .fs_max = AD4130_FILTER_SELECT_MIN, \ .samp_freq_avail_type = IIO_AVAIL_LIST, \ @@ -374,7 +379,7 @@ static const struct ad4130_filter_config ad4130_filter_configs[] = { AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF4, 148), }; -static const char * const ad4130_filter_modes_str[] = { +static const char * const ad4130_filter_types_str[] = { [AD4130_FILTER_SINC4] = "sinc4", [AD4130_FILTER_SINC4_SINC1] = "sinc4+sinc1", [AD4130_FILTER_SINC3] = "sinc3", @@ -591,6 +596,40 @@ static irqreturn_t ad4130_irq_handler(int irq, void *private) return IRQ_HANDLED; } +static bool ad4130_setup_info_eq(struct ad4130_setup_info *a, + struct ad4130_setup_info *b) +{ + /* + * This is just to make sure that the comparison is adapted after + * struct ad4130_setup_info was changed. + */ + static_assert(sizeof(*a) == + sizeof(struct { + unsigned int iout0_val; + unsigned int iout1_val; + unsigned int burnout; + unsigned int pga; + unsigned int fs; + u32 ref_sel; + enum ad4130_filter_type filter_type; + bool ref_bufp; + bool ref_bufm; + })); + + if (a->iout0_val != b->iout0_val || + a->iout1_val != b->iout1_val || + a->burnout != b->burnout || + a->pga != b->pga || + a->fs != b->fs || + a->ref_sel != b->ref_sel || + a->filter_type != b->filter_type || + a->ref_bufp != b->ref_bufp || + a->ref_bufm != b->ref_bufm) + return false; + + return true; +} + static int ad4130_find_slot(struct ad4130_state *st, struct ad4130_setup_info *target_setup_info, unsigned int *slot, bool *overwrite) @@ -604,8 +643,7 @@ static int ad4130_find_slot(struct ad4130_state *st, struct ad4130_slot_info *slot_info = &st->slots_info[i]; /* Immediately accept a matching setup info. */ - if (!memcmp(target_setup_info, &slot_info->setup, - sizeof(*target_setup_info))) { + if (ad4130_setup_info_eq(target_setup_info, &slot_info->setup)) { *slot = i; return 0; } @@ -691,7 +729,7 @@ static int ad4130_write_slot_setup(struct ad4130_state *st, if (ret) return ret; - val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_mode) | + val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_type) | FIELD_PREP(AD4130_FILTER_SELECT_MASK, setup_info->fs); ret = regmap_write(st->regmap, AD4130_FILTER_X_REG(slot), val); @@ -835,11 +873,11 @@ static int ad4130_set_channel_enable(struct ad4130_state *st, * (used in ad4130_fs_to_freq) */ -static void ad4130_freq_to_fs(enum ad4130_filter_mode filter_mode, +static void ad4130_freq_to_fs(enum ad4130_filter_type filter_type, int val, int val2, unsigned int *fs) { const struct ad4130_filter_config *filter_config = - &ad4130_filter_configs[filter_mode]; + &ad4130_filter_configs[filter_type]; u64 dividend, divisor; int temp; @@ -858,11 +896,11 @@ static void ad4130_freq_to_fs(enum ad4130_filter_mode filter_mode, *fs = temp; } -static void ad4130_fs_to_freq(enum ad4130_filter_mode filter_mode, +static void ad4130_fs_to_freq(enum ad4130_filter_type filter_type, unsigned int fs, int *val, int *val2) { const struct ad4130_filter_config *filter_config = - &ad4130_filter_configs[filter_mode]; + &ad4130_filter_configs[filter_type]; unsigned int dividend, divisor; u64 temp; @@ -874,7 +912,7 @@ static void ad4130_fs_to_freq(enum ad4130_filter_mode filter_mode, *val = div_u64_rem(temp, NANO, val2); } -static int ad4130_set_filter_mode(struct iio_dev *indio_dev, +static int ad4130_set_filter_type(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int val) { @@ -882,17 +920,17 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev, unsigned int channel = chan->scan_index; struct ad4130_chan_info *chan_info = &st->chans_info[channel]; struct ad4130_setup_info *setup_info = &chan_info->setup; - enum ad4130_filter_mode old_filter_mode; + enum ad4130_filter_type old_filter_type; int freq_val, freq_val2; unsigned int old_fs; int ret = 0; guard(mutex)(&st->lock); - if (setup_info->filter_mode == val) + if (setup_info->filter_type == val) return 0; old_fs = setup_info->fs; - old_filter_mode = setup_info->filter_mode; + old_filter_type = setup_info->filter_type; /* * When switching between filter modes, try to match the ODR as @@ -900,48 +938,55 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev, * using the old filter mode, then convert it back into FS using * the new filter mode. */ - ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs, + ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs, &freq_val, &freq_val2); ad4130_freq_to_fs(val, freq_val, freq_val2, &setup_info->fs); - setup_info->filter_mode = val; + setup_info->filter_type = val; ret = ad4130_write_channel_setup(st, channel, false); if (ret) { setup_info->fs = old_fs; - setup_info->filter_mode = old_filter_mode; + setup_info->filter_type = old_filter_type; return ret; } return 0; } -static int ad4130_get_filter_mode(struct iio_dev *indio_dev, +static int ad4130_get_filter_type(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { struct ad4130_state *st = iio_priv(indio_dev); unsigned int channel = chan->scan_index; struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup; - enum ad4130_filter_mode filter_mode; + enum ad4130_filter_type filter_type; guard(mutex)(&st->lock); - filter_mode = setup_info->filter_mode; + filter_type = setup_info->filter_type; - return filter_mode; + return filter_type; } -static const struct iio_enum ad4130_filter_mode_enum = { - .items = ad4130_filter_modes_str, - .num_items = ARRAY_SIZE(ad4130_filter_modes_str), - .set = ad4130_set_filter_mode, - .get = ad4130_get_filter_mode, +static const struct iio_enum ad4130_filter_type_enum = { + .items = ad4130_filter_types_str, + .num_items = ARRAY_SIZE(ad4130_filter_types_str), + .set = ad4130_set_filter_type, + .get = ad4130_get_filter_type, }; -static const struct iio_chan_spec_ext_info ad4130_filter_mode_ext_info[] = { - IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_mode_enum), +static const struct iio_chan_spec_ext_info ad4130_ext_info[] = { + /* + * `filter_type` is the standardized IIO ABI for digital filtering. + * `filter_mode` is just kept for backwards compatibility. + */ + IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_type_enum), IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_TYPE, - &ad4130_filter_mode_enum), + &ad4130_filter_type_enum), + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4130_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4130_filter_type_enum), { } }; @@ -955,7 +1000,7 @@ static const struct iio_chan_spec ad4130_channel_template = { BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), - .ext_info = ad4130_filter_mode_ext_info, + .ext_info = ad4130_ext_info, .scan_type = { .sign = 'u', .endianness = IIO_BE, @@ -1005,7 +1050,7 @@ static int ad4130_set_channel_freq(struct ad4130_state *st, guard(mutex)(&st->lock); old_fs = setup_info->fs; - ad4130_freq_to_fs(setup_info->filter_mode, val, val2, &fs); + ad4130_freq_to_fs(setup_info->filter_type, val, val2, &fs); if (fs == setup_info->fs) return 0; @@ -1060,13 +1105,11 @@ static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, static int ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel, int *val) { - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct ad4130_state *st = iio_priv(indio_dev); + struct ad4130_state *st = iio_priv(indio_dev); - guard(mutex)(&st->lock); - return _ad4130_read_sample(indio_dev, channel, val); - } - unreachable(); + guard(mutex)(&st->lock); + + return _ad4130_read_sample(indio_dev, channel, val); } static int ad4130_read_raw(struct iio_dev *indio_dev, @@ -1076,10 +1119,16 @@ static int ad4130_read_raw(struct iio_dev *indio_dev, struct ad4130_state *st = iio_priv(indio_dev); unsigned int channel = chan->scan_index; struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup; + int ret; switch (info) { case IIO_CHAN_INFO_RAW: - return ad4130_read_sample(indio_dev, channel, val); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4130_read_sample(indio_dev, channel, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: { guard(mutex)(&st->lock); *val = st->scale_tbls[setup_info->ref_sel][setup_info->pga][0]; @@ -1093,7 +1142,7 @@ static int ad4130_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: { guard(mutex)(&st->lock); - ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs, + ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs, val, val2); return IIO_VAL_INT_PLUS_NANO; @@ -1123,7 +1172,7 @@ static int ad4130_read_avail(struct iio_dev *indio_dev, return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SAMP_FREQ: scoped_guard(mutex, &st->lock) { - filter_config = &ad4130_filter_configs[setup_info->filter_mode]; + filter_config = &ad4130_filter_configs[setup_info->filter_type]; } *vals = (int *)filter_config->samp_freq_avail; diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c index b79d135a5471..8222c8ab2940 100644 --- a/drivers/iio/adc/ad4695.c +++ b/drivers/iio/adc/ad4695.c @@ -19,14 +19,19 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/gpio/consumer.h> +#include <linux/iio/buffer-dmaengine.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> #include <linux/minmax.h> +#include <linux/mutex.h> #include <linux/property.h> +#include <linux/pwm.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/spi/offload/consumer.h> +#include <linux/spi/offload/provider.h> #include <linux/spi/spi.h> #include <linux/units.h> @@ -66,12 +71,15 @@ #define AD4695_REG_STD_SEQ_CONFIG 0x0024 #define AD4695_REG_GPIO_CTRL 0x0026 #define AD4695_REG_GP_MODE 0x0027 +#define AD4695_REG_GP_MODE_BUSY_GP_SEL BIT(5) +#define AD4695_REG_GP_MODE_BUSY_GP_EN BIT(1) #define AD4695_REG_TEMP_CTRL 0x0029 #define AD4695_REG_TEMP_CTRL_TEMP_EN BIT(0) #define AD4695_REG_CONFIG_IN(n) (0x0030 | (n)) #define AD4695_REG_CONFIG_IN_MODE BIT(6) #define AD4695_REG_CONFIG_IN_PAIR GENMASK(5, 4) #define AD4695_REG_CONFIG_IN_AINHIGHZ_EN BIT(3) +#define AD4695_REG_CONFIG_IN_OSR_SET GENMASK(1, 0) #define AD4695_REG_UPPER_IN(n) (0x0040 | (2 * (n))) #define AD4695_REG_LOWER_IN(n) (0x0060 | (2 * (n))) #define AD4695_REG_HYST_IN(n) (0x0080 | (2 * (n))) @@ -92,6 +100,8 @@ #define AD4695_T_REFBUF_MS 100 #define AD4695_T_REGCONFIG_NS 20 #define AD4695_T_SCK_CNV_DELAY_NS 80 +#define AD4695_T_CNVL_NS 80 +#define AD4695_T_CNVH_NS 10 #define AD4695_REG_ACCESS_SCLK_HZ (10 * MEGA) /* Max number of voltage input channels. */ @@ -118,17 +128,27 @@ struct ad4695_channel_config { bool bipolar; enum ad4695_in_pair pin_pairing; unsigned int common_mode_mv; + unsigned int oversampling_ratio; }; struct ad4695_state { struct spi_device *spi; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; struct regmap *regmap; struct regmap *regmap16; struct gpio_desc *reset_gpio; + /* currently PWM CNV only supported with SPI offload use */ + struct pwm_device *cnv_pwm; + /* protects against concurrent use of cnv_pwm */ + struct mutex cnv_pwm_lock; + /* offload also requires separate gpio to manually control CNV */ + struct gpio_desc *cnv_gpio; /* voltages channels plus temperature and timestamp */ struct iio_chan_spec iio_chan[AD4695_MAX_CHANNELS + 2]; struct ad4695_channel_config channels_cfg[AD4695_MAX_CHANNELS]; const struct ad4695_chip_info *chip_info; + int sample_freq_range[3]; /* Reference voltage. */ unsigned int vref_mv; /* Common mode input pin voltage. */ @@ -148,6 +168,8 @@ struct ad4695_state { /* Commands to send for single conversion. */ u16 cnv_cmd; u8 cnv_cmd2; + /* Buffer for storing data from regmap bus reads/writes */ + u8 regmap_bus_data[4]; }; static const struct regmap_range ad4695_regmap_rd_ranges[] = { @@ -192,7 +214,6 @@ static const struct regmap_config ad4695_regmap_config = { .max_register = AD4695_REG_AS_SLOT(127), .rd_table = &ad4695_regmap_rd_table, .wr_table = &ad4695_regmap_wr_table, - .can_multi_write = true, }; static const struct regmap_range ad4695_regmap16_rd_ranges[] = { @@ -224,7 +245,126 @@ static const struct regmap_config ad4695_regmap16_config = { .max_register = AD4695_REG_GAIN_IN(15), .rd_table = &ad4695_regmap16_rd_table, .wr_table = &ad4695_regmap16_wr_table, - .can_multi_write = true, +}; + +static int ad4695_regmap_bus_reg_write(void *context, const void *data, + size_t count) +{ + struct ad4695_state *st = context; + struct spi_transfer xfer = { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .len = count, + .tx_buf = st->regmap_bus_data, + }; + + if (count > ARRAY_SIZE(st->regmap_bus_data)) + return -EINVAL; + + memcpy(st->regmap_bus_data, data, count); + + return spi_sync_transfer(st->spi, &xfer, 1); +} + +static int ad4695_regmap_bus_reg_read(void *context, const void *reg, + size_t reg_size, void *val, + size_t val_size) +{ + struct ad4695_state *st = context; + struct spi_transfer xfers[] = { + { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .len = reg_size, + .tx_buf = &st->regmap_bus_data[0], + }, { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .len = val_size, + .rx_buf = &st->regmap_bus_data[2], + }, + }; + int ret; + + if (reg_size > 2) + return -EINVAL; + + if (val_size > 2) + return -EINVAL; + + memcpy(&st->regmap_bus_data[0], reg, reg_size); + + ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) + return ret; + + memcpy(val, &st->regmap_bus_data[2], val_size); + + return 0; +} + +static const struct regmap_bus ad4695_regmap_bus = { + .write = ad4695_regmap_bus_reg_write, + .read = ad4695_regmap_bus_reg_read, + .read_flag_mask = 0x80, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +enum { + AD4695_SCAN_TYPE_OSR_1, + AD4695_SCAN_TYPE_OSR_4, + AD4695_SCAN_TYPE_OSR_16, + AD4695_SCAN_TYPE_OSR_64, +}; + +static const struct iio_scan_type ad4695_scan_type_offload_u[] = { + [AD4695_SCAN_TYPE_OSR_1] = { + .sign = 'u', + .realbits = 16, + .shift = 3, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_4] = { + .sign = 'u', + .realbits = 17, + .shift = 2, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_16] = { + .sign = 'u', + .realbits = 18, + .shift = 1, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_64] = { + .sign = 'u', + .realbits = 19, + .storagebits = 32, + }, +}; + +static const struct iio_scan_type ad4695_scan_type_offload_s[] = { + [AD4695_SCAN_TYPE_OSR_1] = { + .sign = 's', + .realbits = 16, + .shift = 3, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_4] = { + .sign = 's', + .realbits = 17, + .shift = 2, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_16] = { + .sign = 's', + .realbits = 18, + .shift = 1, + .storagebits = 32, + }, + [AD4695_SCAN_TYPE_OSR_64] = { + .sign = 's', + .realbits = 19, + .storagebits = 32, + }, }; static const struct iio_chan_spec ad4695_channel_template = { @@ -264,6 +404,10 @@ static const char * const ad4695_power_supplies[] = { "avdd", "vio" }; +static const int ad4695_oversampling_ratios[] = { + 1, 4, 16, 64, +}; + static const struct ad4695_chip_info ad4695_chip_info = { .name = "ad4695", .max_sample_rate = 500 * KILO, @@ -292,6 +436,13 @@ static const struct ad4695_chip_info ad4698_chip_info = { .num_voltage_inputs = 8, }; +static void ad4695_cnv_manual_trigger(struct ad4695_state *st) +{ + gpiod_set_value_cansleep(st->cnv_gpio, 1); + ndelay(10); + gpiod_set_value_cansleep(st->cnv_gpio, 0); +} + /** * ad4695_set_single_cycle_mode - Set the device in single cycle mode * @st: The AD4695 state @@ -364,11 +515,31 @@ static int ad4695_enter_advanced_sequencer_mode(struct ad4695_state *st, u32 n) */ static int ad4695_exit_conversion_mode(struct ad4695_state *st) { - struct spi_transfer xfer = { - .tx_buf = &st->cnv_cmd2, - .len = 1, - .delay.value = AD4695_T_REGCONFIG_NS, - .delay.unit = SPI_DELAY_UNIT_NSECS, + /* + * An extra transfer is needed to trigger a conversion here so + * that we can be 100% sure the command will be processed by the + * ADC, rather than relying on it to be in the correct state + * when this function is called (this chip has a quirk where the + * command only works when reading a conversion, and if the + * previous conversion was already read then it won't work). The + * actual conversion command is then run at the slower + * AD4695_REG_ACCESS_SCLK_HZ speed to guarantee this works. + */ + struct spi_transfer xfers[] = { + { + .delay.value = AD4695_T_CNVL_NS, + .delay.unit = SPI_DELAY_UNIT_NSECS, + .cs_change = 1, + .cs_change_delay.value = AD4695_T_CNVH_NS, + .cs_change_delay.unit = SPI_DELAY_UNIT_NSECS, + }, + { + .speed_hz = AD4695_REG_ACCESS_SCLK_HZ, + .tx_buf = &st->cnv_cmd2, + .len = 1, + .delay.value = AD4695_T_REGCONFIG_NS, + .delay.unit = SPI_DELAY_UNIT_NSECS, + }, }; /* @@ -377,7 +548,18 @@ static int ad4695_exit_conversion_mode(struct ad4695_state *st) */ st->cnv_cmd2 = AD4695_CMD_EXIT_CNV_MODE << 3; - return spi_sync_transfer(st->spi, &xfer, 1); + if (st->cnv_gpio) { + ad4695_cnv_manual_trigger(st); + + /* + * In this case, CNV is not connected to CS, so we don't need + * the extra CS toggle to trigger the conversion and toggling + * CS would have no effect. + */ + return spi_sync_transfer(st->spi, &xfers[1], 1); + } + + return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); } static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv) @@ -402,6 +584,29 @@ static int ad4695_set_ref_voltage(struct ad4695_state *st, int vref_mv) FIELD_PREP(AD4695_REG_REF_CTRL_VREF_SET, val)); } +/** + * ad4695_osr_to_regval - convert ratio to OSR register value + * @ratio: ratio to check + * + * Check if ratio is present in the list of available ratios and return + * the corresponding value that needs to be written to the register to + * select that ratio. + * + * Returns: register value (0 to 3) or -EINVAL if there is not an exact + * match + */ +static int ad4695_osr_to_regval(int ratio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ad4695_oversampling_ratios); i++) { + if (ratio == ad4695_oversampling_ratios[i]) + return i; + } + + return -EINVAL; +} + static int ad4695_write_chn_cfg(struct ad4695_state *st, struct ad4695_channel_config *cfg) { @@ -604,6 +809,161 @@ out: return IRQ_HANDLED; } +static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + struct spi_transfer *xfer = &st->buf_read_xfer[0]; + struct pwm_state state; + u8 temp_chan_bit = st->chip_info->num_voltage_inputs; + u8 num_slots = 0; + u8 temp_en = 0; + unsigned int bit; + int ret; + + iio_for_each_active_channel(indio_dev, bit) { + if (bit == temp_chan_bit) { + temp_en = 1; + continue; + } + + ret = regmap_write(st->regmap, AD4695_REG_AS_SLOT(num_slots), + FIELD_PREP(AD4695_REG_AS_SLOT_INX, bit)); + if (ret) + return ret; + + num_slots++; + } + + /* + * For non-offload, we could discard data to work around this + * restriction, but with offload, that is not possible. + */ + if (num_slots < 2) { + dev_err(&st->spi->dev, + "At least two voltage channels must be enabled.\n"); + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, AD4695_REG_TEMP_CTRL, + AD4695_REG_TEMP_CTRL_TEMP_EN, + FIELD_PREP(AD4695_REG_TEMP_CTRL_TEMP_EN, + temp_en)); + if (ret) + return ret; + + /* Each BUSY event means just one sample for one channel is ready. */ + memset(xfer, 0, sizeof(*xfer)); + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + /* Using 19 bits per word to allow for possible oversampling */ + xfer->bits_per_word = 19; + xfer->len = 4; + + spi_message_init_with_transfers(&st->buf_read_msg, xfer, 1); + st->buf_read_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->buf_read_msg); + if (ret) + return ret; + + /* + * NB: technically, this is part the SPI offload trigger enable, but it + * doesn't work to call it from the offload trigger enable callback + * because it requires accessing the SPI bus. Calling it from the + * trigger enable callback could cause a deadlock. + */ + ret = regmap_set_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_EN); + if (ret) + goto err_unoptimize_message; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); + if (ret) + goto err_disable_busy_output; + + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + if (ret) + goto err_offload_trigger_disable; + + mutex_lock(&st->cnv_pwm_lock); + pwm_get_state(st->cnv_pwm, &state); + /* + * PWM subsystem generally rounds down, so requesting 2x minimum high + * time ensures that we meet the minimum high time in any case. + */ + state.duty_cycle = AD4695_T_CNVH_NS * 2; + ret = pwm_apply_might_sleep(st->cnv_pwm, &state); + mutex_unlock(&st->cnv_pwm_lock); + if (ret) + goto err_offload_exit_conversion_mode; + + return 0; + +err_offload_exit_conversion_mode: + /* + * We have to unwind in a different order to avoid triggering offload. + * ad4695_exit_conversion_mode() triggers a conversion, so it has to be + * done after spi_offload_trigger_disable(). + */ + spi_offload_trigger_disable(st->offload, st->offload_trigger); + ad4695_exit_conversion_mode(st); + goto err_disable_busy_output; + +err_offload_trigger_disable: + spi_offload_trigger_disable(st->offload, st->offload_trigger); + +err_disable_busy_output: + regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_EN); + +err_unoptimize_message: + spi_unoptimize_message(&st->buf_read_msg); + + return ret; +} + +static int ad4695_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4695_state *st = iio_priv(indio_dev); + struct pwm_state state; + int ret; + + scoped_guard(mutex, &st->cnv_pwm_lock) { + pwm_get_state(st->cnv_pwm, &state); + state.duty_cycle = 0; + ret = pwm_apply_might_sleep(st->cnv_pwm, &state); + if (ret) + return ret; + } + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + /* + * ad4695_exit_conversion_mode() triggers a conversion, so it has to be + * done after spi_offload_trigger_disable(). + */ + ret = ad4695_exit_conversion_mode(st); + if (ret) + return ret; + + ret = regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_EN); + if (ret) + return ret; + + spi_unoptimize_message(&st->buf_read_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4695_offload_buffer_setup_ops = { + .postenable = ad4695_offload_buffer_postenable, + .predisable = ad4695_offload_buffer_predisable, +}; + /** * ad4695_read_one_sample - Read a single sample using single-cycle mode * @st: The AD4695 state @@ -636,6 +996,13 @@ static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address) return ret; /* + * If CNV is connected to CS, the previous function will have triggered + * the conversion, otherwise, we do it manually. + */ + if (st->cnv_gpio) + ad4695_cnv_manual_trigger(st); + + /* * Setting the first channel to the temperature channel isn't supported * in single-cycle mode, so we have to do an extra conversion to read * the temperature. @@ -646,6 +1013,13 @@ static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address) ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); if (ret) return ret; + + /* + * If CNV is connected to CS, the previous function will have + * triggered the conversion, otherwise, we do it manually. + */ + if (st->cnv_gpio) + ad4695_cnv_manual_trigger(st); } /* Then read the result and exit conversion mode. */ @@ -655,36 +1029,58 @@ static int ad4695_read_one_sample(struct ad4695_state *st, unsigned int address) return spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); } +static int __ad4695_read_info_raw(struct ad4695_state *st, + struct iio_chan_spec const *chan, + int *val) +{ + u8 realbits = chan->scan_type.realbits; + int ret; + + ret = ad4695_read_one_sample(st, chan->address); + if (ret) + return ret; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(st->raw_data, realbits - 1); + else + *val = st->raw_data; + + return IIO_VAL_INT; +} + static int ad4695_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct ad4695_state *st = iio_priv(indio_dev); - struct ad4695_channel_config *cfg = &st->channels_cfg[chan->scan_index]; - u8 realbits = chan->scan_type.realbits; + const struct iio_scan_type *scan_type; + struct ad4695_channel_config *cfg; unsigned int reg_val; int ret, tmp; + u8 realbits; + + if (chan->type == IIO_VOLTAGE) + cfg = &st->channels_cfg[chan->scan_index]; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + realbits = scan_type->realbits; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = ad4695_read_one_sample(st, chan->address); - if (ret) - return ret; - - if (chan->scan_type.sign == 's') - *val = sign_extend32(st->raw_data, realbits - 1); - else - *val = st->raw_data; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - return IIO_VAL_INT; - } - unreachable(); + ret = __ad4695_read_info_raw(st, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_VOLTAGE: *val = st->vref_mv; - *val2 = chan->scan_type.realbits; + *val2 = realbits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_TEMP: /* T_scale (°C) = raw * V_REF (mV) / (-1.8 mV/°C * 2^16) */ @@ -717,111 +1113,245 @@ static int ad4695_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_CALIBSCALE: switch (chan->type) { case IIO_VOLTAGE: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = regmap_read(st->regmap16, - AD4695_REG_GAIN_IN(chan->scan_index), - ®_val); - if (ret) - return ret; - - *val = reg_val; - *val2 = 15; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = regmap_read(st->regmap16, + AD4695_REG_GAIN_IN(chan->scan_index), + ®_val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val = reg_val; + *val2 = 15; - return IIO_VAL_FRACTIONAL_LOG2; - } - unreachable(); + return IIO_VAL_FRACTIONAL_LOG2; default: return -EINVAL; } case IIO_CHAN_INFO_CALIBBIAS: - switch (chan->type) { - case IIO_VOLTAGE: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = regmap_read(st->regmap16, - AD4695_REG_OFFSET_IN(chan->scan_index), - ®_val); - if (ret) - return ret; + switch (chan->type) + case IIO_VOLTAGE: { + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = regmap_read(st->regmap16, + AD4695_REG_OFFSET_IN(chan->scan_index), + ®_val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; - tmp = sign_extend32(reg_val, 15); + tmp = sign_extend32(reg_val, 15); + switch (cfg->oversampling_ratio) { + case 1: *val = tmp / 4; *val2 = abs(tmp) % 4 * MICRO / 4; + break; + case 4: + *val = tmp / 2; + *val2 = abs(tmp) % 2 * MICRO / 2; + break; + case 16: + *val = tmp; + *val2 = 0; + break; + case 64: + *val = tmp * 2; + *val2 = 0; + break; + default: + return -EINVAL; + } - if (tmp < 0 && *val2) { - *val *= -1; - *val2 *= -1; - } - - return IIO_VAL_INT_PLUS_MICRO; + if (tmp < 0 && *val2) { + *val *= -1; + *val2 *= -1; } - unreachable(); + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_VOLTAGE: + *val = cfg->oversampling_ratio; + return IIO_VAL_INT; default: return -EINVAL; } + case IIO_CHAN_INFO_SAMP_FREQ: { + struct pwm_state state; + unsigned int osr = 1; + + if (chan->type == IIO_VOLTAGE) + osr = cfg->oversampling_ratio; + + ret = pwm_get_state_hw(st->cnv_pwm, &state); + if (ret) + return ret; + + /* + * The effective sampling frequency for a channel is the input + * frequency divided by the channel's OSR value. + */ + *val = DIV_ROUND_UP_ULL(NSEC_PER_SEC, state.period * osr); + + return IIO_VAL_INT; + } default: return -EINVAL; } } -static int ad4695_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int ad4695_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ad4695_set_osr_val(struct ad4695_state *st, + struct iio_chan_spec const *chan, + int val) +{ + int osr = ad4695_osr_to_regval(val); + + if (osr < 0) + return osr; + + switch (chan->type) { + case IIO_VOLTAGE: + st->channels_cfg[chan->scan_index].oversampling_ratio = val; + return regmap_update_bits(st->regmap, + AD4695_REG_CONFIG_IN(chan->scan_index), + AD4695_REG_CONFIG_IN_OSR_SET, + FIELD_PREP(AD4695_REG_CONFIG_IN_OSR_SET, osr)); + default: + return -EINVAL; + } +} + +static unsigned int ad4695_get_calibbias(int val, int val2, int osr) +{ + int val_calc, scale; + + switch (osr) { + case 4: + scale = 4; + break; + case 16: + scale = 2; + break; + case 64: + scale = 1; + break; + default: + scale = 8; + break; + } + + val = clamp_t(int, val, S32_MIN / 8, S32_MAX / 8); + + /* val2 range is (-MICRO, MICRO) if val == 0, otherwise [0, MICRO) */ + if (val < 0) + val_calc = val * scale - val2 * scale / MICRO; + else if (val2 < 0) + /* if val2 < 0 then val == 0 */ + val_calc = val2 * scale / (int)MICRO; + else + val_calc = val * scale + val2 * scale / MICRO; + + val_calc /= 2; + + return clamp_t(int, val_calc, S16_MIN, S16_MAX); +} + +static int __ad4695_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct ad4695_state *st = iio_priv(indio_dev); unsigned int reg_val; + unsigned int osr = 1; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - switch (chan->type) { - case IIO_VOLTAGE: - if (val < 0 || val2 < 0) - reg_val = 0; - else if (val > 1) - reg_val = U16_MAX; - else - reg_val = (val * (1 << 16) + - mul_u64_u32_div(val2, 1 << 16, - MICRO)) / 2; - - return regmap_write(st->regmap16, - AD4695_REG_GAIN_IN(chan->scan_index), - reg_val); - default: - return -EINVAL; - } - case IIO_CHAN_INFO_CALIBBIAS: - switch (chan->type) { - case IIO_VOLTAGE: - if (val2 >= 0 && val > S16_MAX / 4) - reg_val = S16_MAX; - else if ((val2 < 0 ? -val : val) < S16_MIN / 4) - reg_val = S16_MIN; - else if (val2 < 0) - reg_val = clamp_t(int, - -(val * 4 + -val2 * 4 / MICRO), - S16_MIN, S16_MAX); - else if (val < 0) - reg_val = clamp_t(int, - val * 4 - val2 * 4 / MICRO, - S16_MIN, S16_MAX); - else - reg_val = clamp_t(int, - val * 4 + val2 * 4 / MICRO, - S16_MIN, S16_MAX); - - return regmap_write(st->regmap16, - AD4695_REG_OFFSET_IN(chan->scan_index), - reg_val); - default: - return -EINVAL; - } + if (chan->type == IIO_VOLTAGE) + osr = st->channels_cfg[chan->scan_index].oversampling_ratio; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (val < 0 || val2 < 0) + reg_val = 0; + else if (val > 1) + reg_val = U16_MAX; + else + reg_val = (val * (1 << 16) + + mul_u64_u32_div(val2, 1 << 16, + MICRO)) / 2; + + return regmap_write(st->regmap16, + AD4695_REG_GAIN_IN(chan->scan_index), + reg_val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_VOLTAGE: + reg_val = ad4695_get_calibbias(val, val2, osr); + return regmap_write(st->regmap16, + AD4695_REG_OFFSET_IN(chan->scan_index), + reg_val); default: return -EINVAL; } + case IIO_CHAN_INFO_SAMP_FREQ: { + struct pwm_state state; + /* + * Limit the maximum acceptable sample rate according to + * the channel's oversampling ratio. + */ + u64 max_osr_rate = DIV_ROUND_UP_ULL(st->chip_info->max_sample_rate, + osr); + + if (val <= 0 || val > max_osr_rate) + return -EINVAL; + + guard(mutex)(&st->cnv_pwm_lock); + pwm_get_state(st->cnv_pwm, &state); + /* + * The required sample frequency for a given OSR is the + * input frequency multiplied by it. + */ + state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, val * osr); + return pwm_apply_might_sleep(st->cnv_pwm, &state); + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4695_set_osr_val(st, chan, val); + default: + return -EINVAL; } - unreachable(); +} + +static int ad4695_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __ad4695_write_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + + return ret; } static int ad4695_read_avail(struct iio_dev *indio_dev, @@ -829,17 +1359,43 @@ static int ad4695_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + int ret; static const int ad4695_calibscale_available[6] = { /* Range of 0 (inclusive) to 2 (exclusive) */ 0, 15, 1, 15, U16_MAX, 15 }; - static const int ad4695_calibbias_available[6] = { + static const int ad4695_calibbias_available[4][6] = { /* * Datasheet says FSR/8 which translates to signed/4. The step - * depends on oversampling ratio which is always 1 for now. + * depends on oversampling ratio, so we need four different + * ranges to select from. */ - S16_MIN / 4, 0, 0, MICRO / 4, S16_MAX / 4, S16_MAX % 4 * MICRO / 4 + { + S16_MIN / 4, 0, + 0, MICRO / 4, + S16_MAX / 4, S16_MAX % 4 * MICRO / 4 + }, + { + S16_MIN / 2, 0, + 0, MICRO / 2, + S16_MAX / 2, S16_MAX % 2 * MICRO / 2, + }, + { + S16_MIN, 0, + 1, 0, + S16_MAX, 0, + }, + { + S16_MIN * 2, 0, + 2, 0, + S16_MAX * 2, 0, + }, }; + struct ad4695_state *st = iio_priv(indio_dev); + unsigned int osr = 1; + + if (chan->type == IIO_VOLTAGE) + osr = st->channels_cfg[chan->scan_index].oversampling_ratio; switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: @@ -854,12 +1410,36 @@ static int ad4695_read_avail(struct iio_dev *indio_dev, case IIO_CHAN_INFO_CALIBBIAS: switch (chan->type) { case IIO_VOLTAGE: - *vals = ad4695_calibbias_available; + ret = ad4695_osr_to_regval(osr); + if (ret < 0) + return ret; + /* + * Select the appropriate calibbias array based on the + * OSR value in the register. + */ + *vals = ad4695_calibbias_available[ret]; *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_RANGE; default: return -EINVAL; } + case IIO_CHAN_INFO_SAMP_FREQ: + /* Max sample rate for the channel depends on OSR */ + st->sample_freq_range[2] = + DIV_ROUND_UP_ULL(st->chip_info->max_sample_rate, osr); + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_VOLTAGE: + *vals = ad4695_oversampling_ratios; + *length = ARRAY_SIZE(ad4695_oversampling_ratios); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -871,31 +1451,64 @@ static int ad4695_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int *readval) { struct ad4695_state *st = iio_priv(indio_dev); - - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - if (readval) { - if (regmap_check_range_table(st->regmap, reg, - &ad4695_regmap_rd_table)) - return regmap_read(st->regmap, reg, readval); - if (regmap_check_range_table(st->regmap16, reg, - &ad4695_regmap16_rd_table)) - return regmap_read(st->regmap16, reg, readval); - } else { - if (regmap_check_range_table(st->regmap, reg, - &ad4695_regmap_wr_table)) - return regmap_write(st->regmap, reg, writeval); - if (regmap_check_range_table(st->regmap16, reg, - &ad4695_regmap16_wr_table)) - return regmap_write(st->regmap16, reg, writeval); - } + int ret = -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) { + if (regmap_check_range_table(st->regmap, reg, + &ad4695_regmap_rd_table)) + ret = regmap_read(st->regmap, reg, readval); + if (regmap_check_range_table(st->regmap16, reg, + &ad4695_regmap16_rd_table)) + ret = regmap_read(st->regmap16, reg, readval); + } else { + if (regmap_check_range_table(st->regmap, reg, + &ad4695_regmap_wr_table)) + ret = regmap_write(st->regmap, reg, writeval); + if (regmap_check_range_table(st->regmap16, reg, + &ad4695_regmap16_wr_table)) + ret = regmap_write(st->regmap16, reg, writeval); } + iio_device_release_direct(indio_dev); - return -EINVAL; + return ret; +} + +static int ad4695_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4695_state *st = iio_priv(indio_dev); + unsigned int osr = st->channels_cfg[chan->scan_index].oversampling_ratio; + + switch (osr) { + case 1: + return AD4695_SCAN_TYPE_OSR_1; + case 4: + return AD4695_SCAN_TYPE_OSR_4; + case 16: + return AD4695_SCAN_TYPE_OSR_16; + case 64: + return AD4695_SCAN_TYPE_OSR_64; + default: + return -EINVAL; + } } static const struct iio_info ad4695_info = { .read_raw = &ad4695_read_raw, + .write_raw_get_fmt = &ad4695_write_raw_get_fmt, + .write_raw = &ad4695_write_raw, + .read_avail = &ad4695_read_avail, + .debugfs_reg_access = &ad4695_debugfs_reg_access, +}; + +static const struct iio_info ad4695_offload_info = { + .read_raw = &ad4695_read_raw, + .write_raw_get_fmt = &ad4695_write_raw_get_fmt, .write_raw = &ad4695_write_raw, + .get_current_scan_type = &ad4695_get_current_scan_type, .read_avail = &ad4695_read_avail, .debugfs_reg_access = &ad4695_debugfs_reg_access, }; @@ -915,6 +1528,9 @@ static int ad4695_parse_channel_cfg(struct ad4695_state *st) chan_cfg->highz_en = true; chan_cfg->channel = i; + /* This is the default OSR after reset */ + chan_cfg->oversampling_ratio = 1; + *iio_chan = ad4695_channel_template; iio_chan->channel = i; iio_chan->scan_index = i; @@ -1008,26 +1624,188 @@ static int ad4695_parse_channel_cfg(struct ad4695_state *st) return 0; } +static bool ad4695_offload_trigger_match(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* + * Requires 2 args: + * args[0] is the trigger event. + * args[1] is the GPIO pin number. + */ + if (nargs != 2 || args[0] != AD4695_TRIGGER_EVENT_BUSY) + return false; + + return true; +} + +static int ad4695_offload_trigger_request(struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + struct ad4695_state *st = spi_offload_trigger_get_priv(trigger); + + /* Should already be validated by match, but just in case. */ + if (nargs != 2) + return -EINVAL; + + /* DT tells us if BUSY event uses GP0 or GP3. */ + if (args[1] == AD4695_TRIGGER_PIN_GP3) + return regmap_set_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_SEL); + + return regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, + AD4695_REG_GP_MODE_BUSY_GP_SEL); +} + +static int +ad4695_offload_trigger_validate(struct spi_offload_trigger *trigger, + struct spi_offload_trigger_config *config) +{ + if (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +/* + * NB: There are no enable/disable callbacks here due to requiring a SPI + * message to enable or disable the BUSY output on the ADC. + */ +static const struct spi_offload_trigger_ops ad4695_offload_trigger_ops = { + .match = ad4695_offload_trigger_match, + .request = ad4695_offload_trigger_request, + .validate = ad4695_offload_trigger_validate, +}; + +static void ad4695_pwm_disable(void *pwm) +{ + pwm_disable(pwm); +} + +static int ad4695_probe_spi_offload(struct iio_dev *indio_dev, + struct ad4695_state *st) +{ + struct device *dev = &st->spi->dev; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad4695_offload_trigger_ops, + .priv = st, + }; + struct pwm_state pwm_state; + struct dma_chan *rx_dma; + int ret, i; + + indio_dev->info = &ad4695_offload_info; + indio_dev->num_channels = st->chip_info->num_voltage_inputs + 1; + indio_dev->setup_ops = &ad4695_offload_buffer_setup_ops; + + if (!st->cnv_gpio) + return dev_err_probe(dev, -ENODEV, + "CNV GPIO is required for SPI offload\n"); + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "failed to register offload trigger\n"); + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + ret = devm_mutex_init(dev, &st->cnv_pwm_lock); + if (ret) + return ret; + + st->cnv_pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv_pwm)) + return dev_err_probe(dev, PTR_ERR(st->cnv_pwm), + "failed to get CNV PWM\n"); + + pwm_init_state(st->cnv_pwm, &pwm_state); + + /* If firmware didn't provide default rate, use 10kHz (arbitrary). */ + if (pwm_state.period == 0) + pwm_state.period = 100 * MILLI; + + pwm_state.enabled = true; + + ret = pwm_apply_might_sleep(st->cnv_pwm, &pwm_state); + if (ret) + return dev_err_probe(dev, ret, "failed to apply CNV PWM\n"); + + ret = devm_add_action_or_reset(dev, ad4695_pwm_disable, st->cnv_pwm); + if (ret) + return ret; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + for (i = 0; i < indio_dev->num_channels; i++) { + struct iio_chan_spec *chan = &st->iio_chan[i]; + struct ad4695_channel_config *cfg; + + /* + * NB: When using offload support, all channels need to have the + * same bits_per_word because they all use the same SPI message + * for reading one sample. In order to prevent breaking + * userspace in the future when oversampling support is added, + * all channels are set read 19 bits with a shift of 3 to mask + * out the extra bits even though we currently only support 16 + * bit samples (oversampling ratio == 1). + */ + chan->scan_type.shift = 3; + chan->scan_type.storagebits = 32; + /* add sample frequency for PWM CNV trigger */ + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SAMP_FREQ); + chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_SAMP_FREQ); + + /* Add the oversampling properties only for voltage channels */ + if (chan->type != IIO_VOLTAGE) + continue; + + cfg = &st->channels_cfg[i]; + + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->info_mask_separate_available |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->has_ext_scan_type = 1; + if (cfg->bipolar) { + chan->ext_scan_type = ad4695_scan_type_offload_s; + chan->num_ext_scan_type = + ARRAY_SIZE(ad4695_scan_type_offload_s); + } else { + chan->ext_scan_type = ad4695_scan_type_offload_u; + chan->num_ext_scan_type = + ARRAY_SIZE(ad4695_scan_type_offload_u); + } + } + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); +} + +static const struct spi_offload_config ad4695_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + static int ad4695_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct ad4695_state *st; struct iio_dev *indio_dev; - struct gpio_desc *cnv_gpio; bool use_internal_ldo_supply; bool use_internal_ref_buffer; int ret; - cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW); - if (IS_ERR(cnv_gpio)) - return dev_err_probe(dev, PTR_ERR(cnv_gpio), - "Failed to get CNV GPIO\n"); - - /* Driver currently requires CNV pin to be connected to SPI CS */ - if (cnv_gpio) - return dev_err_probe(dev, -ENODEV, - "CNV GPIO is not supported\n"); - indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; @@ -1039,19 +1817,27 @@ static int ad4695_probe(struct spi_device *spi) if (!st->chip_info) return -EINVAL; - /* Registers cannot be read at the max allowable speed */ - spi->max_speed_hz = AD4695_REG_ACCESS_SCLK_HZ; + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = st->chip_info->max_sample_rate; /* max */ - st->regmap = devm_regmap_init_spi(spi, &ad4695_regmap_config); + st->regmap = devm_regmap_init(dev, &ad4695_regmap_bus, st, + &ad4695_regmap_config); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n"); - st->regmap16 = devm_regmap_init_spi(spi, &ad4695_regmap16_config); + st->regmap16 = devm_regmap_init(dev, &ad4695_regmap_bus, st, + &ad4695_regmap16_config); if (IS_ERR(st->regmap16)) return dev_err_probe(dev, PTR_ERR(st->regmap16), "Failed to initialize regmap16\n"); + st->cnv_gpio = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(st->cnv_gpio)) + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), + "Failed to get CNV GPIO\n"); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4695_power_supplies), ad4695_power_supplies); @@ -1179,12 +1965,31 @@ static int ad4695_probe(struct spi_device *spi) indio_dev->channels = st->iio_chan; indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - ad4695_trigger_handler, - &ad4695_buffer_setup_ops); - if (ret) - return ret; + st->offload = devm_spi_offload_get(dev, spi, &ad4695_spi_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + + /* If no SPI offload, fall back to low speed usage. */ + if (ret == -ENODEV) { + /* Driver currently requires CNV pin to be connected to SPI CS */ + if (st->cnv_gpio) + return dev_err_probe(dev, -EINVAL, + "CNV GPIO is not supported\n"); + + indio_dev->num_channels = st->chip_info->num_voltage_inputs + 2; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad4695_trigger_handler, + &ad4695_buffer_setup_ops); + if (ret) + return ret; + } else { + ret = ad4695_probe_spi_offload(indio_dev, st); + if (ret) + return ret; + } return devm_iio_device_register(dev, indio_dev); } @@ -1221,3 +2026,4 @@ MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>"); MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>"); MODULE_DESCRIPTION("Analog Devices AD4695 ADC driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c new file mode 100644 index 000000000000..98ebc853db79 --- /dev/null +++ b/drivers/iio/adc/ad4851.c @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4851 DAS driver + * + * Copyright 2024 Analog Devices Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pwm.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +#include <linux/iio/backend.h> +#include <linux/iio/iio.h> + +#define AD4851_REG_INTERFACE_CONFIG_A 0x00 +#define AD4851_REG_INTERFACE_CONFIG_B 0x01 +#define AD4851_REG_PRODUCT_ID_L 0x04 +#define AD4851_REG_PRODUCT_ID_H 0x05 +#define AD4851_REG_DEVICE_CTRL 0x25 +#define AD4851_REG_PACKET 0x26 +#define AD4851_REG_OVERSAMPLE 0x27 + +#define AD4851_REG_CH_CONFIG_BASE 0x2A +#define AD4851_REG_CHX_SOFTSPAN(ch) ((0x12 * (ch)) + AD4851_REG_CH_CONFIG_BASE) +#define AD4851_REG_CHX_OFFSET(ch) (AD4851_REG_CHX_SOFTSPAN(ch) + 0x01) +#define AD4851_REG_CHX_OFFSET_LSB(ch) AD4851_REG_CHX_OFFSET(ch) +#define AD4851_REG_CHX_OFFSET_MID(ch) (AD4851_REG_CHX_OFFSET_LSB(ch) + 0x01) +#define AD4851_REG_CHX_OFFSET_MSB(ch) (AD4851_REG_CHX_OFFSET_MID(ch) + 0x01) +#define AD4851_REG_CHX_GAIN(ch) (AD4851_REG_CHX_OFFSET(ch) + 0x03) +#define AD4851_REG_CHX_GAIN_LSB(ch) AD4851_REG_CHX_GAIN(ch) +#define AD4851_REG_CHX_GAIN_MSB(ch) (AD4851_REG_CHX_GAIN(ch) + 0x01) +#define AD4851_REG_CHX_PHASE(ch) (AD4851_REG_CHX_GAIN(ch) + 0x02) +#define AD4851_REG_CHX_PHASE_LSB(ch) AD4851_REG_CHX_PHASE(ch) +#define AD4851_REG_CHX_PHASE_MSB(ch) (AD4851_REG_CHX_PHASE_LSB(ch) + 0x01) + +#define AD4851_REG_TESTPAT_0(c) (0x38 + (c) * 0x12) +#define AD4851_REG_TESTPAT_1(c) (0x39 + (c) * 0x12) +#define AD4851_REG_TESTPAT_2(c) (0x3A + (c) * 0x12) +#define AD4851_REG_TESTPAT_3(c) (0x3B + (c) * 0x12) + +#define AD4851_SW_RESET (BIT(7) | BIT(0)) +#define AD4851_SDO_ENABLE BIT(4) +#define AD4851_SINGLE_INSTRUCTION BIT(7) +#define AD4851_REFBUF BIT(2) +#define AD4851_REFSEL BIT(1) +#define AD4851_ECHO_CLOCK_MODE BIT(0) + +#define AD4851_PACKET_FORMAT_0 0 +#define AD4851_PACKET_FORMAT_1 1 +#define AD4851_PACKET_FORMAT_MASK GENMASK(1, 0) + +#define AD4851_OS_EN_MSK BIT(7) +#define AD4851_OS_RATIO_MSK GENMASK(3, 0) + +#define AD4851_TEST_PAT BIT(2) + +#define AD4858_PACKET_SIZE_20 0 +#define AD4858_PACKET_SIZE_24 1 +#define AD4858_PACKET_SIZE_32 2 + +#define AD4857_PACKET_SIZE_16 0 +#define AD4857_PACKET_SIZE_24 1 + +#define AD4851_TESTPAT_0_DEFAULT 0x2A +#define AD4851_TESTPAT_1_DEFAULT 0x3C +#define AD4851_TESTPAT_2_DEFAULT 0xCE +#define AD4851_TESTPAT_3_DEFAULT(c) (0x0A + (0x10 * (c))) + +#define AD4851_SOFTSPAN_0V_2V5 0 +#define AD4851_SOFTSPAN_N2V5_2V5 1 +#define AD4851_SOFTSPAN_0V_5V 2 +#define AD4851_SOFTSPAN_N5V_5V 3 +#define AD4851_SOFTSPAN_0V_6V25 4 +#define AD4851_SOFTSPAN_N6V25_6V25 5 +#define AD4851_SOFTSPAN_0V_10V 6 +#define AD4851_SOFTSPAN_N10V_10V 7 +#define AD4851_SOFTSPAN_0V_12V5 8 +#define AD4851_SOFTSPAN_N12V5_12V5 9 +#define AD4851_SOFTSPAN_0V_20V 10 +#define AD4851_SOFTSPAN_N20V_20V 11 +#define AD4851_SOFTSPAN_0V_25V 12 +#define AD4851_SOFTSPAN_N25V_25V 13 +#define AD4851_SOFTSPAN_0V_40V 14 +#define AD4851_SOFTSPAN_N40V_40V 15 + +#define AD4851_MAX_LANES 8 +#define AD4851_MAX_IODELAY 32 + +#define AD4851_T_CNVH_NS 40 +#define AD4851_T_CNVH_NS_MARGIN 10 + +#define AD4841_MAX_SCALE_AVAIL 8 + +#define AD4851_MAX_CH_NR 8 +#define AD4851_CH_START 0 + +struct ad4851_scale { + unsigned int scale_val; + u8 reg_val; +}; + +static const struct ad4851_scale ad4851_scale_table_unipolar[] = { + { 2500, 0x0 }, + { 5000, 0x2 }, + { 6250, 0x4 }, + { 10000, 0x6 }, + { 12500, 0x8 }, + { 20000, 0xA }, + { 25000, 0xC }, + { 40000, 0xE }, +}; + +static const struct ad4851_scale ad4851_scale_table_bipolar[] = { + { 5000, 0x1 }, + { 10000, 0x3 }, + { 12500, 0x5 }, + { 20000, 0x7 }, + { 25000, 0x9 }, + { 40000, 0xB }, + { 50000, 0xD }, + { 80000, 0xF }, +}; + +static const unsigned int ad4851_scale_avail_unipolar[] = { + 2500, + 5000, + 6250, + 10000, + 12500, + 20000, + 25000, + 40000, +}; + +static const unsigned int ad4851_scale_avail_bipolar[] = { + 5000, + 10000, + 12500, + 20000, + 25000, + 40000, + 50000, + 80000, +}; + +struct ad4851_chip_info { + const char *name; + unsigned int product_id; + int num_scales; + unsigned long max_sample_rate_hz; + unsigned int resolution; + unsigned int max_channels; + int (*parse_channels)(struct iio_dev *indio_dev); +}; + +enum { + AD4851_SCAN_TYPE_NORMAL, + AD4851_SCAN_TYPE_RESOLUTION_BOOST, +}; + +struct ad4851_state { + struct spi_device *spi; + struct pwm_device *cnv; + struct iio_backend *back; + /* + * Synchronize access to members the of driver state, and ensure + * atomicity of consecutive regmap operations. + */ + struct mutex lock; + struct regmap *regmap; + const struct ad4851_chip_info *info; + struct gpio_desc *pd_gpio; + bool resolution_boost_enabled; + unsigned long cnv_trigger_rate_hz; + unsigned int osr; + bool vrefbuf_en; + bool vrefio_en; + bool bipolar_ch[AD4851_MAX_CH_NR]; + unsigned int scales_unipolar[AD4841_MAX_SCALE_AVAIL][2]; + unsigned int scales_bipolar[AD4841_MAX_SCALE_AVAIL][2]; +}; + +static int ad4851_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4851_set_sampling_freq(struct ad4851_state *st, unsigned int freq) +{ + struct pwm_state cnv_state = { + .duty_cycle = AD4851_T_CNVH_NS + AD4851_T_CNVH_NS_MARGIN, + .enabled = true, + }; + int ret; + + freq = clamp(freq, 1, st->info->max_sample_rate_hz); + + cnv_state.period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, freq); + + ret = pwm_apply_might_sleep(st->cnv, &cnv_state); + if (ret) + return ret; + + st->cnv_trigger_rate_hz = freq; + + return 0; +} + +static const int ad4851_oversampling_ratios[] = { + 1, 2, 4, 8, 16, 32, 64, 128, + 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, + 65536, +}; + +static int ad4851_osr_to_regval(unsigned int ratio) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(ad4851_oversampling_ratios); i++) + if (ratio == ad4851_oversampling_ratios[i]) + return i - 1; + + return -EINVAL; +} + +static int __ad4851_get_scale(struct iio_dev *indio_dev, int scale_tbl, + unsigned int *val, unsigned int *val2) +{ + const struct iio_scan_type *scan_type; + unsigned int tmp; + + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + tmp = ((u64)scale_tbl * MICRO) >> scan_type->realbits; + *val = tmp / MICRO; + *val2 = tmp % MICRO; + + return 0; +} + +static int ad4851_scale_fill(struct iio_dev *indio_dev) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int i, val1, val2; + int ret; + + for (i = 0; i < ARRAY_SIZE(ad4851_scale_avail_unipolar); i++) { + ret = __ad4851_get_scale(indio_dev, + ad4851_scale_avail_unipolar[i], + &val1, &val2); + if (ret) + return ret; + + st->scales_unipolar[i][0] = val1; + st->scales_unipolar[i][1] = val2; + } + + for (i = 0; i < ARRAY_SIZE(ad4851_scale_avail_bipolar); i++) { + ret = __ad4851_get_scale(indio_dev, + ad4851_scale_avail_bipolar[i], + &val1, &val2); + if (ret) + return ret; + + st->scales_bipolar[i][0] = val1; + st->scales_bipolar[i][1] = val2; + } + + return 0; +} + +static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int osr) +{ + struct ad4851_state *st = iio_priv(indio_dev); + int val, ret; + + guard(mutex)(&st->lock); + + if (osr == 1) { + ret = regmap_clear_bits(st->regmap, AD4851_REG_OVERSAMPLE, + AD4851_OS_EN_MSK); + if (ret) + return ret; + } else { + val = ad4851_osr_to_regval(osr); + if (val < 0) + return -EINVAL; + + ret = regmap_update_bits(st->regmap, AD4851_REG_OVERSAMPLE, + AD4851_OS_EN_MSK | + AD4851_OS_RATIO_MSK, + FIELD_PREP(AD4851_OS_EN_MSK, 1) | + FIELD_PREP(AD4851_OS_RATIO_MSK, val)); + if (ret) + return ret; + } + + ret = iio_backend_oversampling_ratio_set(st->back, osr); + if (ret) + return ret; + + switch (st->info->resolution) { + case 20: + switch (osr) { + case 0: + return -EINVAL; + case 1: + val = 20; + break; + default: + val = 24; + break; + } + break; + case 16: + val = 16; + break; + default: + return -EINVAL; + } + + ret = iio_backend_data_size_set(st->back, val); + if (ret) + return ret; + + if (osr == 1 || st->info->resolution == 16) { + ret = regmap_clear_bits(st->regmap, AD4851_REG_PACKET, + AD4851_PACKET_FORMAT_MASK); + if (ret) + return ret; + + st->resolution_boost_enabled = false; + } else { + ret = regmap_update_bits(st->regmap, AD4851_REG_PACKET, + AD4851_PACKET_FORMAT_MASK, + FIELD_PREP(AD4851_PACKET_FORMAT_MASK, 1)); + if (ret) + return ret; + + st->resolution_boost_enabled = true; + } + + if (st->osr != osr) { + ret = ad4851_scale_fill(indio_dev); + if (ret) + return ret; + + st->osr = osr; + } + + return 0; +} + +static int ad4851_get_oversampling_ratio(struct ad4851_state *st, unsigned int *val) +{ + unsigned int osr; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD4851_REG_OVERSAMPLE, &osr); + if (ret) + return ret; + + if (!FIELD_GET(AD4851_OS_EN_MSK, osr)) + *val = 1; + else + *val = ad4851_oversampling_ratios[FIELD_GET(AD4851_OS_RATIO_MSK, osr) + 1]; + + st->osr = *val; + + return IIO_VAL_INT; +} + +static void ad4851_pwm_disable(void *data) +{ + pwm_disable(data); +} + +static int ad4851_setup(struct ad4851_state *st) +{ + unsigned int product_id; + int ret; + + if (st->pd_gpio) { + /* To initiate a global reset, bring the PD pin high twice */ + gpiod_set_value(st->pd_gpio, 1); + fsleep(1); + gpiod_set_value(st->pd_gpio, 0); + fsleep(1); + gpiod_set_value(st->pd_gpio, 1); + fsleep(1); + gpiod_set_value(st->pd_gpio, 0); + fsleep(1000); + } else { + ret = regmap_set_bits(st->regmap, AD4851_REG_INTERFACE_CONFIG_A, + AD4851_SW_RESET); + if (ret) + return ret; + } + + if (st->vrefbuf_en) { + ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL, + AD4851_REFBUF); + if (ret) + return ret; + } + + if (st->vrefio_en) { + ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL, + AD4851_REFSEL); + if (ret) + return ret; + } + + ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_B, + AD4851_SINGLE_INSTRUCTION); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A, + AD4851_SDO_ENABLE); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4851_REG_PRODUCT_ID_L, &product_id); + if (ret) + return ret; + + if (product_id != st->info->product_id) + dev_info(&st->spi->dev, "Unknown product ID: 0x%02X\n", + product_id); + + ret = regmap_set_bits(st->regmap, AD4851_REG_DEVICE_CTRL, + AD4851_ECHO_CLOCK_MODE); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_PACKET, 0); +} + +/* + * Find the longest consecutive sequence of false values from field + * and return starting index. + */ +static int ad4851_find_opt(const unsigned long *field, unsigned int start, + unsigned int nbits, unsigned int *val) +{ + unsigned int bit = start, end, start_cnt, cnt = 0; + + for_each_clear_bitrange_from(bit, end, field, start + nbits) { + if (end - bit > cnt) { + cnt = end - bit; + start_cnt = bit - start; + } + } + + if (!cnt) + return -ENOENT; + + *val = start_cnt; + + return cnt; +} + +static int ad4851_calibrate(struct iio_dev *indio_dev) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int opt_delay, num_lanes, delay, i, s; + enum iio_backend_interface_type interface_type; + DECLARE_BITMAP(pn_status, AD4851_MAX_LANES * AD4851_MAX_IODELAY); + bool status; + int c, ret; + + ret = iio_backend_interface_type_get(st->back, &interface_type); + if (ret) + return ret; + + switch (interface_type) { + case IIO_BACKEND_INTERFACE_SERIAL_CMOS: + num_lanes = indio_dev->num_channels; + break; + case IIO_BACKEND_INTERFACE_SERIAL_LVDS: + num_lanes = 1; + break; + default: + return -EINVAL; + } + + if (st->info->resolution == 16) { + ret = iio_backend_data_size_set(st->back, 24); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_PACKET, + AD4851_TEST_PAT | AD4857_PACKET_SIZE_24); + if (ret) + return ret; + } else { + ret = iio_backend_data_size_set(st->back, 32); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_PACKET, + AD4851_TEST_PAT | AD4858_PACKET_SIZE_32); + if (ret) + return ret; + } + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_0(i), + AD4851_TESTPAT_0_DEFAULT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_1(i), + AD4851_TESTPAT_1_DEFAULT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_2(i), + AD4851_TESTPAT_2_DEFAULT); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_3(i), + AD4851_TESTPAT_3_DEFAULT(i)); + if (ret) + return ret; + + ret = iio_backend_chan_enable(st->back, + indio_dev->channels[i].channel); + if (ret) + return ret; + } + + for (i = 0; i < num_lanes; i++) { + for (delay = 0; delay < AD4851_MAX_IODELAY; delay++) { + ret = iio_backend_iodelay_set(st->back, i, delay); + if (ret) + return ret; + + ret = iio_backend_chan_status(st->back, i, &status); + if (ret) + return ret; + + __assign_bit(i * AD4851_MAX_IODELAY + delay, pn_status, + status); + } + } + + for (i = 0; i < num_lanes; i++) { + c = ad4851_find_opt(pn_status, i * AD4851_MAX_IODELAY, + AD4851_MAX_IODELAY, &s); + if (c < 0) + return c; + + opt_delay = s + c / 2; + ret = iio_backend_iodelay_set(st->back, i, opt_delay); + if (ret) + return ret; + } + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = iio_backend_chan_disable(st->back, i); + if (ret) + return ret; + } + + ret = iio_backend_data_size_set(st->back, 20); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_PACKET, 0); +} + +static int ad4851_get_calibscale(struct ad4851_state *st, int ch, int *val, int *val2) +{ + unsigned int reg_val; + int gain; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), ®_val); + if (ret) + return ret; + + gain = reg_val << 8; + + ret = regmap_read(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), ®_val); + if (ret) + return ret; + + gain |= reg_val; + + *val = gain; + *val2 = 15; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int ad4851_set_calibscale(struct ad4851_state *st, int ch, int val, + int val2) +{ + u64 gain; + u8 buf[2]; + int ret; + + if (val < 0 || val2 < 0) + return -EINVAL; + + gain = val * MICRO + val2; + gain = DIV_U64_ROUND_CLOSEST(gain * 32768, MICRO); + + put_unaligned_be16(gain, buf); + + guard(mutex)(&st->lock); + + ret = regmap_write(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch), buf[0]); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch), buf[1]); +} + +static int ad4851_get_calibbias(struct ad4851_state *st, int ch, int *val) +{ + unsigned int lsb, mid, msb; + int ret; + + guard(mutex)(&st->lock); + /* + * After testing, the bulk_write operations doesn't work as expected + * here since the cs needs to be raised after each byte transaction. + */ + ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), &msb); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), &mid); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), &lsb); + if (ret) + return ret; + + if (st->info->resolution == 16) { + *val = msb << 8; + *val |= mid; + *val = sign_extend32(*val, 15); + } else { + *val = msb << 12; + *val |= mid << 4; + *val |= lsb >> 4; + *val = sign_extend32(*val, 19); + } + + return IIO_VAL_INT; +} + +static int ad4851_set_calibbias(struct ad4851_state *st, int ch, int val) +{ + u8 buf[3]; + int ret; + + if (val < 0) + return -EINVAL; + + if (st->info->resolution == 16) + put_unaligned_be16(val, buf); + else + put_unaligned_be24(val << 4, buf); + + guard(mutex)(&st->lock); + /* + * After testing, the bulk_write operations doesn't work as expected + * here since the cs needs to be raised after each byte transaction. + */ + ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_LSB(ch), buf[2]); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MID(ch), buf[1]); + if (ret) + return ret; + + return regmap_write(st->regmap, AD4851_REG_CHX_OFFSET_MSB(ch), buf[0]); +} + +static int ad4851_set_scale(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int val, int val2) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int scale_val[2]; + unsigned int i; + const struct ad4851_scale *scale_table; + size_t table_size; + int ret; + + if (st->bipolar_ch[chan->channel]) { + scale_table = ad4851_scale_table_bipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_bipolar); + } else { + scale_table = ad4851_scale_table_unipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_unipolar); + } + + for (i = 0; i < table_size; i++) { + ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val, + &scale_val[0], &scale_val[1]); + if (ret) + return ret; + + if (scale_val[0] != val || scale_val[1] != val2) + continue; + + return regmap_write(st->regmap, + AD4851_REG_CHX_SOFTSPAN(chan->channel), + scale_table[i].reg_val); + } + + return -EINVAL; +} + +static int ad4851_get_scale(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2) +{ + struct ad4851_state *st = iio_priv(indio_dev); + const struct ad4851_scale *scale_table; + size_t table_size; + u32 softspan_val; + int i, ret; + + if (st->bipolar_ch[chan->channel]) { + scale_table = ad4851_scale_table_bipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_bipolar); + } else { + scale_table = ad4851_scale_table_unipolar; + table_size = ARRAY_SIZE(ad4851_scale_table_unipolar); + } + + ret = regmap_read(st->regmap, AD4851_REG_CHX_SOFTSPAN(chan->channel), + &softspan_val); + if (ret) + return ret; + + for (i = 0; i < table_size; i++) { + if (softspan_val == scale_table[i].reg_val) + break; + } + + if (i == table_size) + return -EIO; + + ret = __ad4851_get_scale(indio_dev, scale_table[i].scale_val, val, + val2); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad4851_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->cnv_trigger_rate_hz; + *val2 = st->osr; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_CALIBSCALE: + return ad4851_get_calibscale(st, chan->channel, val, val2); + case IIO_CHAN_INFO_SCALE: + return ad4851_get_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4851_get_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4851_get_oversampling_ratio(st, val); + default: + return -EINVAL; + } +} + +static int ad4851_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 0 || val2 < 0) + return -EINVAL; + return ad4851_set_sampling_freq(st, val * st->osr + val2 * st->osr / MICRO); + case IIO_CHAN_INFO_SCALE: + return ad4851_set_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4851_set_calibscale(st, chan->channel, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4851_set_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4851_set_oversampling_ratio(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad4851_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4851_state *st = iio_priv(indio_dev); + unsigned int c; + int ret; + + for (c = 0; c < indio_dev->num_channels; c++) { + if (test_bit(c, scan_mask)) + ret = iio_backend_chan_enable(st->back, c); + else + ret = iio_backend_chan_disable(st->back, c); + if (ret) + return ret; + } + + return 0; +} + +static int ad4851_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (st->bipolar_ch[chan->channel]) { + *vals = (const int *)st->scales_bipolar; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(ad4851_scale_avail_bipolar) * 2; + } else { + *vals = (const int *)st->scales_unipolar; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(ad4851_scale_avail_unipolar) * 2; + } + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad4851_oversampling_ratios; + *length = ARRAY_SIZE(ad4851_oversampling_ratios); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_scan_type ad4851_scan_type_20_u[] = { + [AD4851_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 20, + .storagebits = 32, + }, + [AD4851_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + }, +}; + +static const struct iio_scan_type ad4851_scan_type_20_b[] = { + [AD4851_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 20, + .storagebits = 32, + }, + [AD4851_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + }, +}; + +static int ad4851_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4851_state *st = iio_priv(indio_dev); + + return st->resolution_boost_enabled ? AD4851_SCAN_TYPE_RESOLUTION_BOOST + : AD4851_SCAN_TYPE_NORMAL; +} + +#define AD4851_IIO_CHANNEL \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .indexed = 1 + +/* + * In case of AD4858_IIO_CHANNEL the scan_type is handled dynamically during the + * parse_channels function. + */ +#define AD4858_IIO_CHANNEL \ +{ \ + AD4851_IIO_CHANNEL \ +} + +#define AD4857_IIO_CHANNEL \ +{ \ + AD4851_IIO_CHANNEL, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + }, \ +} + +static int ad4851_parse_channels_common(struct iio_dev *indio_dev, + struct iio_chan_spec **chans, + const struct iio_chan_spec ad4851_chan) +{ + struct ad4851_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct iio_chan_spec *channels, *chan_start; + unsigned int num_channels, reg; + unsigned int index = 0; + int ret; + + num_channels = device_get_child_node_count(dev); + if (num_channels > AD4851_MAX_CH_NR) + return dev_err_probe(dev, -EINVAL, "Too many channels: %u\n", + num_channels); + + channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + chan_start = channels; + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Missing channel number\n"); + if (reg >= AD4851_MAX_CH_NR) + return dev_err_probe(dev, -EINVAL, + "Invalid channel number\n"); + *channels = ad4851_chan; + channels->scan_index = index++; + channels->channel = reg; + + if (fwnode_property_present(child, "diff-channels")) { + channels->channel2 = reg + st->info->max_channels; + channels->differential = 1; + } + + st->bipolar_ch[reg] = fwnode_property_read_bool(child, "bipolar"); + + if (st->bipolar_ch[reg]) { + channels->scan_type.sign = 's'; + } else { + ret = regmap_write(st->regmap, AD4851_REG_CHX_SOFTSPAN(reg), + AD4851_SOFTSPAN_0V_40V); + if (ret) + return ret; + } + + channels++; + } + + *chans = chan_start; + + return num_channels; +} + +static int ad4857_parse_channels(struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ad4851_channels; + const struct iio_chan_spec ad4851_chan = AD4857_IIO_CHANNEL; + int ret; + + ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels, + ad4851_chan); + if (ret < 0) + return ret; + + indio_dev->channels = ad4851_channels; + indio_dev->num_channels = ret; + + return 0; +} + +static int ad4858_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4851_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct iio_chan_spec *ad4851_channels; + const struct iio_chan_spec ad4851_chan = AD4858_IIO_CHANNEL; + int ret; + + ret = ad4851_parse_channels_common(indio_dev, &ad4851_channels, + ad4851_chan); + if (ret < 0) + return ret; + + device_for_each_child_node_scoped(dev, child) { + ad4851_channels->has_ext_scan_type = 1; + if (fwnode_property_read_bool(child, "bipolar")) { + ad4851_channels->ext_scan_type = ad4851_scan_type_20_b; + ad4851_channels->num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_b); + } else { + ad4851_channels->ext_scan_type = ad4851_scan_type_20_u; + ad4851_channels->num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_u); + } + ad4851_channels++; + } + + indio_dev->channels = ad4851_channels; + indio_dev->num_channels = ret; + + return 0; +} + +/* + * parse_channels() function handles the rest of the channel related attributes + * that are usually are stored in the chip info structure. + */ +static const struct ad4851_chip_info ad4851_info = { + .name = "ad4851", + .product_id = 0x67, + .max_sample_rate_hz = 250 * KILO, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4852_info = { + .name = "ad4852", + .product_id = 0x66, + .max_sample_rate_hz = 250 * KILO, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4853_info = { + .name = "ad4853", + .product_id = 0x65, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4854_info = { + .name = "ad4854", + .product_id = 0x64, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4855_info = { + .name = "ad4855", + .product_id = 0x63, + .max_sample_rate_hz = 250 * KILO, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4856_info = { + .name = "ad4856", + .product_id = 0x62, + .max_sample_rate_hz = 250 * KILO, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4857_info = { + .name = "ad4857", + .product_id = 0x61, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 16, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4857_parse_channels, +}; + +static const struct ad4851_chip_info ad4858_info = { + .name = "ad4858", + .product_id = 0x60, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct ad4851_chip_info ad4858i_info = { + .name = "ad4858i", + .product_id = 0x6F, + .max_sample_rate_hz = 1 * MEGA, + .resolution = 20, + .max_channels = AD4851_MAX_CH_NR, + .parse_channels = ad4858_parse_channels, +}; + +static const struct iio_info ad4851_iio_info = { + .debugfs_reg_access = ad4851_reg_access, + .read_raw = ad4851_read_raw, + .write_raw = ad4851_write_raw, + .update_scan_mode = ad4851_update_scan_mode, + .get_current_scan_type = ad4851_get_current_scan_type, + .read_avail = ad4851_read_avail, +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), +}; + +static const char * const ad4851_power_supplies[] = { + "vcc", "vdd", "vee", "vio", +}; + +static int ad4851_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct device *dev = &spi->dev; + struct ad4851_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4851_power_supplies), + ad4851_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + ret = devm_regulator_get_enable_optional(dev, "vddh"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vddh voltage\n"); + + ret = devm_regulator_get_enable_optional(dev, "vddl"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vddl voltage\n"); + + ret = devm_regulator_get_enable_optional(dev, "vrefbuf"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vrefbuf voltage\n"); + + st->vrefbuf_en = ret != -ENODEV; + + ret = devm_regulator_get_enable_optional(dev, "vrefio"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable vrefio voltage\n"); + + st->vrefio_en = ret != -ENODEV; + + st->pd_gpio = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_LOW); + if (IS_ERR(st->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(st->pd_gpio), + "Error on requesting pd GPIO\n"); + + st->cnv = devm_pwm_get(dev, NULL); + if (IS_ERR(st->cnv)) + return dev_err_probe(dev, PTR_ERR(st->cnv), + "Error on requesting pwm\n"); + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + st->regmap = devm_regmap_init_spi(spi, ®map_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = ad4851_set_sampling_freq(st, HZ_PER_MHZ); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&st->spi->dev, ad4851_pwm_disable, + st->cnv); + if (ret) + return ret; + + ret = ad4851_setup(st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->info = &ad4851_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = st->info->parse_channels(indio_dev); + if (ret) + return ret; + + ret = ad4851_scale_fill(indio_dev); + if (ret) + return ret; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + ret = ad4851_calibrate(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad4851_of_match[] = { + { .compatible = "adi,ad4851", .data = &ad4851_info, }, + { .compatible = "adi,ad4852", .data = &ad4852_info, }, + { .compatible = "adi,ad4853", .data = &ad4853_info, }, + { .compatible = "adi,ad4854", .data = &ad4854_info, }, + { .compatible = "adi,ad4855", .data = &ad4855_info, }, + { .compatible = "adi,ad4856", .data = &ad4856_info, }, + { .compatible = "adi,ad4857", .data = &ad4857_info, }, + { .compatible = "adi,ad4858", .data = &ad4858_info, }, + { .compatible = "adi,ad4858i", .data = &ad4858i_info, }, + { } +}; + +static const struct spi_device_id ad4851_spi_id[] = { + { "ad4851", (kernel_ulong_t)&ad4851_info }, + { "ad4852", (kernel_ulong_t)&ad4852_info }, + { "ad4853", (kernel_ulong_t)&ad4853_info }, + { "ad4854", (kernel_ulong_t)&ad4854_info }, + { "ad4855", (kernel_ulong_t)&ad4855_info }, + { "ad4856", (kernel_ulong_t)&ad4856_info }, + { "ad4857", (kernel_ulong_t)&ad4857_info }, + { "ad4858", (kernel_ulong_t)&ad4858_info }, + { "ad4858i", (kernel_ulong_t)&ad4858i_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4851_spi_id); + +static struct spi_driver ad4851_driver = { + .probe = ad4851_probe, + .driver = { + .name = "ad4851", + .of_match_table = ad4851_of_match, + }, + .id_table = ad4851_spi_id, +}; +module_spi_driver(ad4851_driver); + +MODULE_AUTHOR("Sergiu Cuciurean <sergiu.cuciurean@analog.com>"); +MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>"); +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD4851 DAS driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c index 606486c4dfe8..931ff71b2888 100644 --- a/drivers/iio/adc/ad7091r-base.c +++ b/drivers/iio/adc/ad7091r-base.c @@ -7,6 +7,7 @@ #include <linux/bitops.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/interrupt.h> diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 6ae27cdd3250..3ea81a98e455 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -53,6 +53,11 @@ #define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2) #define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x) +#define AD7124_MODE_CAL_INT_ZERO 0x5 /* Internal Zero-Scale Calibration */ +#define AD7124_MODE_CAL_INT_FULL 0x6 /* Internal Full-Scale Calibration */ +#define AD7124_MODE_CAL_SYS_ZERO 0x7 /* System Zero-Scale Calibration */ +#define AD7124_MODE_CAL_SYS_FULL 0x8 /* System Full-Scale Calibration */ + /* AD7124 ID */ #define AD7124_DEVICE_ID_MSK GENMASK(7, 4) #define AD7124_DEVICE_ID_GET(x) FIELD_GET(AD7124_DEVICE_ID_MSK, x) @@ -151,7 +156,11 @@ struct ad7124_chip_info { struct ad7124_channel_config { bool live; unsigned int cfg_slot; - /* Following fields are used to compare equality. */ + /* + * Following fields are used to compare for equality. If you + * make adaptations in it, you most likely also have to adapt + * ad7124_find_similar_live_cfg(), too. + */ struct_group(config_props, enum ad7124_ref_sel refsel; bool bipolar; @@ -162,6 +171,8 @@ struct ad7124_channel_config { unsigned int odr; unsigned int odr_sel_bits; unsigned int filter_type; + unsigned int calibration_offset; + unsigned int calibration_gain; ); }; @@ -170,6 +181,7 @@ struct ad7124_channel { struct ad7124_channel_config cfg; unsigned int ain; unsigned int slot; + u8 syscalib_mode; }; struct ad7124_state { @@ -182,24 +194,13 @@ struct ad7124_state { unsigned int num_channels; struct mutex cfgs_lock; /* lock for configs access */ unsigned long cfg_slots_status; /* bitmap with slot status (1 means it is used) */ - DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS); -}; -static const struct iio_chan_spec ad7124_channel_template = { - .type = IIO_VOLTAGE, - .indexed = 1, - .differential = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE) | - BIT(IIO_CHAN_INFO_OFFSET) | - BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), - .scan_type = { - .sign = 'u', - .realbits = 24, - .storagebits = 32, - .endianness = IIO_BE, - }, + /* + * Stores the power-on reset value for the GAIN(x) registers which are + * needed for measurements at gain 1 (i.e. CONFIG(x).PGA == 0) + */ + unsigned int gain_default; + DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS); }; static struct ad7124_chip_info ad7124_chip_info_tbl[] = { @@ -338,15 +339,42 @@ static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_ struct ad7124_channel_config *cfg) { struct ad7124_channel_config *cfg_aux; - ptrdiff_t cmp_size; int i; - cmp_size = sizeof_field(struct ad7124_channel_config, config_props); + /* + * This is just to make sure that the comparison is adapted after + * struct ad7124_channel_config was changed. + */ + static_assert(sizeof_field(struct ad7124_channel_config, config_props) == + sizeof(struct { + enum ad7124_ref_sel refsel; + bool bipolar; + bool buf_positive; + bool buf_negative; + unsigned int vref_mv; + unsigned int pga_bits; + unsigned int odr; + unsigned int odr_sel_bits; + unsigned int filter_type; + unsigned int calibration_offset; + unsigned int calibration_gain; + })); + for (i = 0; i < st->num_channels; i++) { cfg_aux = &st->channels[i].cfg; if (cfg_aux->live && - !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size)) + cfg->refsel == cfg_aux->refsel && + cfg->bipolar == cfg_aux->bipolar && + cfg->buf_positive == cfg_aux->buf_positive && + cfg->buf_negative == cfg_aux->buf_negative && + cfg->vref_mv == cfg_aux->vref_mv && + cfg->pga_bits == cfg_aux->pga_bits && + cfg->odr == cfg_aux->odr && + cfg->odr_sel_bits == cfg_aux->odr_sel_bits && + cfg->filter_type == cfg_aux->filter_type && + cfg->calibration_offset == cfg_aux->calibration_offset && + cfg->calibration_gain == cfg_aux->calibration_gain) return cfg_aux; } @@ -402,6 +430,14 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co cfg->cfg_slot = cfg_slot; + ret = ad_sd_write_reg(&st->sd, AD7124_OFFSET(cfg->cfg_slot), 3, cfg->calibration_offset); + if (ret) + return ret; + + ret = ad_sd_write_reg(&st->sd, AD7124_GAIN(cfg->cfg_slot), 3, cfg->calibration_gain); + if (ret) + return ret; + tmp = (cfg->buf_positive << 1) + cfg->buf_negative; val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) | AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits); @@ -540,14 +576,21 @@ static int ad7124_append_status(struct ad_sigma_delta *sd, bool append) return 0; } -static int ad7124_disable_all(struct ad_sigma_delta *sd) +static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan) { struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + + /* The relevant thing here is that AD7124_CHANNEL_EN_MSK is cleared. */ + return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan), 2, 0); +} + +static int ad7124_disable_all(struct ad_sigma_delta *sd) +{ int ret; int i; - for (i = 0; i < st->num_channels; i++) { - ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_EN_MSK, 0, 2); + for (i = 0; i < 16; i++) { + ret = ad7124_disable_one(sd, i); if (ret < 0) return ret; } @@ -555,13 +598,6 @@ static int ad7124_disable_all(struct ad_sigma_delta *sd) return 0; } -static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan) -{ - struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); - - return ad7124_spi_write_mask(st, AD7124_CHANNEL(chan), AD7124_CHANNEL_EN_MSK, 0, 2); -} - static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { .set_channel = ad7124_set_channel, .append_status = ad7124_append_status, @@ -808,13 +844,22 @@ static int ad7124_soft_reset(struct ad7124_state *st) return dev_err_probe(dev, ret, "Error reading status register\n"); if (!(readval & AD7124_STATUS_POR_FLAG_MSK)) - return 0; + break; /* The AD7124 requires typically 2ms to power up and settle */ usleep_range(100, 2000); } while (--timeout); - return dev_err_probe(dev, -EIO, "Soft reset failed\n"); + if (readval & AD7124_STATUS_POR_FLAG_MSK) + return dev_err_probe(dev, -EIO, "Soft reset failed\n"); + + ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, &st->gain_default); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading gain register\n"); + + dev_dbg(dev, "Reset value of GAIN register is 0x%x\n", st->gain_default); + + return 0; } static int ad7124_check_chip_id(struct ad7124_state *st) @@ -842,6 +887,140 @@ static int ad7124_check_chip_id(struct ad7124_state *st) return 0; } +enum { + AD7124_SYSCALIB_ZERO_SCALE, + AD7124_SYSCALIB_FULL_SCALE, +}; + +static int ad7124_syscalib_locked(struct ad7124_state *st, const struct iio_chan_spec *chan) +{ + struct device *dev = &st->sd.spi->dev; + struct ad7124_channel *ch = &st->channels[chan->channel]; + int ret; + + if (ch->syscalib_mode == AD7124_SYSCALIB_ZERO_SCALE) { + ch->cfg.calibration_offset = 0x800000; + + ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_SYS_ZERO, + chan->address); + if (ret < 0) + return ret; + + ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(ch->cfg.cfg_slot), 3, + &ch->cfg.calibration_offset); + if (ret < 0) + return ret; + + dev_dbg(dev, "offset for channel %d after zero-scale calibration: 0x%x\n", + chan->channel, ch->cfg.calibration_offset); + } else { + ch->cfg.calibration_gain = st->gain_default; + + ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_SYS_FULL, + chan->address); + if (ret < 0) + return ret; + + ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(ch->cfg.cfg_slot), 3, + &ch->cfg.calibration_gain); + if (ret < 0) + return ret; + + dev_dbg(dev, "gain for channel %d after full-scale calibration: 0x%x\n", + chan->channel, ch->cfg.calibration_gain); + } + + return 0; +} + +static ssize_t ad7124_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7124_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret; + + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; + + if (!sys_calib) + return len; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7124_syscalib_locked(st, chan); + + iio_device_release_direct(indio_dev); + + return ret ?: len; +} + +static const char * const ad7124_syscalib_modes[] = { + [AD7124_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7124_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7124_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7124_state *st = iio_priv(indio_dev); + + st->channels[chan->channel].syscalib_mode = mode; + + return 0; +} + +static int ad7124_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7124_state *st = iio_priv(indio_dev); + + return st->channels[chan->channel].syscalib_mode; +} + +static const struct iio_enum ad7124_syscalib_mode_enum = { + .items = ad7124_syscalib_modes, + .num_items = ARRAY_SIZE(ad7124_syscalib_modes), + .set = ad7124_set_syscalib_mode, + .get = ad7124_get_syscalib_mode +}; + +static const struct iio_chan_spec_ext_info ad7124_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7124_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7124_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7124_syscalib_mode_enum), + { } +}; + +static const struct iio_chan_spec ad7124_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .ext_info = ad7124_calibsys_ext_info, +}; + /* * Input specifiers 8 - 15 are explicitly reserved for ad7124-4 * while they are fine for ad7124-8. Values above 31 don't fit @@ -881,12 +1060,12 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, /* Add one for temperature */ st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS); - chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels, + chan = devm_kcalloc(dev, st->num_channels, sizeof(*chan), GFP_KERNEL); if (!chan) return -ENOMEM; - channels = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*channels), + channels = devm_kcalloc(dev, st->num_channels, sizeof(*channels), GFP_KERNEL); if (!channels) return -ENOMEM; @@ -1016,11 +1195,10 @@ static int ad7124_setup(struct ad7124_state *st) * set all channels to this default value. */ ad7124_set_channel_odr(st, i, 10); - - /* Disable all channels to prevent unintended conversions. */ - ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, 0); } + ad7124_disable_all(&st->sd); + ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); if (ret < 0) return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n"); @@ -1028,6 +1206,91 @@ static int ad7124_setup(struct ad7124_state *st) return ret; } +static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev) +{ + struct device *dev = &st->sd.spi->dev; + int ret, i; + + for (i = 0; i < st->num_channels; i++) { + + if (indio_dev->channels[i].type != IIO_VOLTAGE) + continue; + + /* + * For calibration the OFFSET register should hold its reset default + * value. For the GAIN register there is no such requirement but + * for gain 1 it should hold the reset default value, too. So to + * simplify matters use the reset default value for both. + */ + st->channels[i].cfg.calibration_offset = 0x800000; + st->channels[i].cfg.calibration_gain = st->gain_default; + + /* + * Full-scale calibration isn't supported at gain 1, so skip in + * that case. Note that untypically full-scale calibration has + * to happen before zero-scale calibration. This only applies to + * the internal calibration. For system calibration it's as + * usual: first zero-scale then full-scale calibration. + */ + if (st->channels[i].cfg.pga_bits > 0) { + ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_FULL, i); + if (ret < 0) + return ret; + + /* + * read out the resulting value of GAIN + * after full-scale calibration because the next + * ad_sd_calibrate() call overwrites this via + * ad_sigma_delta_set_channel() -> ad7124_set_channel() + * ... -> ad7124_enable_channel(). + */ + ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(st->channels[i].cfg.cfg_slot), 3, + &st->channels[i].cfg.calibration_gain); + if (ret < 0) + return ret; + } + + ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_ZERO, i); + if (ret < 0) + return ret; + + ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(st->channels[i].cfg.cfg_slot), 3, + &st->channels[i].cfg.calibration_offset); + if (ret < 0) + return ret; + + dev_dbg(dev, "offset and gain for channel %d = 0x%x + 0x%x\n", i, + st->channels[i].cfg.calibration_offset, + st->channels[i].cfg.calibration_gain); + } + + return 0; +} + +static int ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev) +{ + int ret; + unsigned int adc_control = st->adc_control; + + /* + * Calibration isn't supported at full power, so speed down a bit. + * Setting .adc_control is enough here because the control register is + * written as part of ad_sd_calibrate() -> ad_sigma_delta_set_mode(). + * The resulting calibration is then also valid for high-speed, so just + * restore adc_control afterwards. + */ + if (FIELD_GET(AD7124_ADC_CTRL_PWR_MSK, adc_control) >= AD7124_FULL_POWER) { + st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK; + st->adc_control |= AD7124_ADC_CTRL_PWR(AD7124_MID_POWER); + } + + ret = __ad7124_calibrate_all(st, indio_dev); + + st->adc_control = adc_control; + + return ret; +} + static void ad7124_reg_disable(void *r) { regulator_disable(r); @@ -1106,6 +1369,10 @@ static int ad7124_probe(struct spi_device *spi) if (ret < 0) return dev_err_probe(dev, ret, "Failed to setup triggers\n"); + ret = ad7124_calibrate_all(st, indio_dev); + if (ret) + return ret; + ret = devm_iio_device_register(&spi->dev, indio_dev); if (ret < 0) return dev_err_probe(dev, ret, "Failed to register iio device\n"); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 6c4ed10ae580..69de5886474c 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -35,6 +35,7 @@ #include <linux/units.h> #include <linux/iio/buffer.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> @@ -102,6 +103,7 @@ #define AD7173_GPIO_PDSW BIT(14) #define AD7173_GPIO_OP_EN2_3 BIT(13) +#define AD4111_GPIO_GP_OW_EN BIT(12) #define AD7173_GPIO_MUX_IO BIT(12) #define AD7173_GPIO_SYNC_EN BIT(11) #define AD7173_GPIO_ERR_EN BIT(10) @@ -149,6 +151,7 @@ #define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) #define AD7173_MAX_CONFIGS 8 +#define AD4111_OW_DET_THRSH_MV 300 #define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */ #define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */ @@ -171,6 +174,7 @@ struct ad7173_device_info { unsigned int clock; unsigned int id; char *name; + const struct ad_sigma_delta_info *sd_info; bool has_current_inputs; bool has_vincom_input; bool has_temp; @@ -181,15 +185,23 @@ struct ad7173_device_info { bool has_int_ref; bool has_ref2; bool has_internal_fs_calibration; + bool has_openwire_det; bool higher_gpio_bits; u8 num_gpios; }; struct ad7173_channel_config { + /* Openwire detection threshold */ + unsigned int openwire_thrsh_raw; + int openwire_comp_chan; u8 cfg_slot; bool live; - /* Following fields are used to compare equality. */ + /* + * Following fields are used to compare equality. If you + * make adaptations in it, you most likely also have to adapt + * ad7173_find_live_config(), too. + */ struct_group(config_props, bool bipolar; bool input_buf; @@ -202,11 +214,11 @@ struct ad7173_channel { unsigned int ain; struct ad7173_channel_config cfg; u8 syscalib_mode; + bool openwire_det_en; }; struct ad7173_state { struct ad_sigma_delta sd; - struct ad_sigma_delta_info sigma_delta_info; const struct ad7173_device_info *info; struct ad7173_channel *channels; struct regulator_bulk_data regulators[3]; @@ -265,228 +277,6 @@ static unsigned int ad4111_current_channel_config[] = { 0x18B, /* 12:IIN3+ 11:IIN3− */ }; -static const struct ad7173_device_info ad4111_device_info = { - .name = "ad4111", - .id = AD4111_ID, - .num_voltage_in_div = 8, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 8, - .num_gpios = 2, - .higher_gpio_bits = true, - .has_temp = true, - .has_vincom_input = true, - .has_input_buf = true, - .has_current_inputs = true, - .has_int_ref = true, - .has_internal_fs_calibration = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad4112_device_info = { - .name = "ad4112", - .id = AD4112_ID, - .num_voltage_in_div = 8, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 8, - .num_gpios = 2, - .higher_gpio_bits = true, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_current_inputs = true, - .has_int_ref = true, - .has_internal_fs_calibration = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad4113_device_info = { - .name = "ad4113", - .id = AD4113_ID, - .num_voltage_in_div = 8, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 8, - .num_gpios = 2, - .data_reg_only_16bit = true, - .higher_gpio_bits = true, - .has_vincom_input = true, - .has_input_buf = true, - .has_int_ref = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad4114_device_info = { - .name = "ad4114", - .id = AD4114_ID, - .num_voltage_in_div = 16, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 16, - .num_gpios = 4, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_internal_fs_calibration = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad4115_device_info = { - .name = "ad4115", - .id = AD4115_ID, - .num_voltage_in_div = 16, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 16, - .num_gpios = 4, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_internal_fs_calibration = true, - .clock = 8 * HZ_PER_MHZ, - .sinc5_data_rates = ad4115_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad4116_device_info = { - .name = "ad4116", - .id = AD4116_ID, - .num_voltage_in_div = 11, - .num_channels = 16, - .num_configs = 8, - .num_voltage_in = 16, - .num_gpios = 4, - .has_vincom_input = true, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_internal_fs_calibration = true, - .clock = 4 * HZ_PER_MHZ, - .sinc5_data_rates = ad4116_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7172_2_device_info = { - .name = "ad7172-2", - .id = AD7172_2_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_pow_supply_monitoring = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7172_4_device_info = { - .name = "ad7172-4", - .id = AD7172_4_ID, - .num_voltage_in = 9, - .num_channels = 8, - .num_configs = 8, - .num_gpios = 4, - .has_input_buf = true, - .has_ref2 = true, - .has_pow_supply_monitoring = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7173_8_device_info = { - .name = "ad7173-8", - .id = AD7173_ID, - .num_voltage_in = 17, - .num_channels = 16, - .num_configs = 8, - .num_gpios = 4, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_ref2 = true, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7175_2_device_info = { - .name = "ad7175-2", - .id = AD7175_2_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_pow_supply_monitoring = true, - .clock = 16 * HZ_PER_MHZ, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7175_8_device_info = { - .name = "ad7175-8", - .id = AD7175_8_ID, - .num_voltage_in = 17, - .num_channels = 16, - .num_configs = 8, - .num_gpios = 4, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_ref2 = true, - .has_pow_supply_monitoring = true, - .clock = 16 * HZ_PER_MHZ, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7176_2_device_info = { - .name = "ad7176-2", - .id = AD7176_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_int_ref = true, - .clock = 16 * HZ_PER_MHZ, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), -}; - -static const struct ad7173_device_info ad7177_2_device_info = { - .name = "ad7177-2", - .id = AD7177_ID, - .num_voltage_in = 5, - .num_channels = 4, - .num_configs = 4, - .num_gpios = 2, - .has_temp = true, - .has_input_buf = true, - .has_int_ref = true, - .has_pow_supply_monitoring = true, - .clock = 16 * HZ_PER_MHZ, - .odr_start_value = AD7177_ODR_START_VALUE, - .sinc5_data_rates = ad7175_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), -}; - static const char *const ad7173_ref_sel_str[] = { [AD7173_SETUP_REF_SEL_EXT_REF] = "vref", [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2", @@ -559,6 +349,9 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, if (ret) return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + mode = st->channels[chan->channel].syscalib_mode; if (sys_calib) { if (mode == AD7173_SYSCALIB_ZERO_SCALE) @@ -569,6 +362,8 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, chan->address); } + iio_device_release_direct(indio_dev); + return ret ? : len; } @@ -616,6 +411,76 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d return 0; } +/* + * Associative array of channel pairs for open wire detection + * The array is indexed by ain and gives the associated channel pair + * to perform the open wire detection with + * the channel pair [0] is for non differential and pair [1] + * is for differential inputs + */ +static int openwire_ain_to_channel_pair[][2][2] = { +/* AIN Single Differential */ + [0] = { { 0, 15 }, { 1, 2 } }, + [1] = { { 1, 2 }, { 2, 1 } }, + [2] = { { 3, 4 }, { 5, 6 } }, + [3] = { { 5, 6 }, { 6, 5 } }, + [4] = { { 7, 8 }, { 9, 10 } }, + [5] = { { 9, 10 }, { 10, 9 } }, + [6] = { { 11, 12 }, { 13, 14 } }, + [7] = { { 13, 14 }, { 14, 13 } }, +}; + +/* + * Openwire detection on ad4111 works by running the same input measurement + * on two different channels and compare if the difference between the two + * measurements exceeds a certain value (typical 300mV) + */ +static int ad4111_openwire_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + struct ad7173_channel_config *cfg = &adchan->cfg; + int ret, val1, val2; + + ret = regmap_set_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, + AD4111_GPIO_GP_OW_EN); + if (ret) + return ret; + + adchan->cfg.openwire_comp_chan = + openwire_ain_to_channel_pair[chan->channel][chan->differential][0]; + + ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val1); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Error running ad_sigma_delta single conversion: %d", ret); + goto out; + } + + adchan->cfg.openwire_comp_chan = + openwire_ain_to_channel_pair[chan->channel][chan->differential][1]; + + ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val2); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Error running ad_sigma_delta single conversion: %d", ret); + goto out; + } + + if (abs(val1 - val2) > cfg->openwire_thrsh_raw) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, chan->address, + IIO_EV_TYPE_FAULT, IIO_EV_DIR_FAULT_OPENWIRE), + iio_get_time_ns(indio_dev)); + +out: + adchan->cfg.openwire_comp_chan = -1; + regmap_clear_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, + AD4111_GPIO_GP_OW_EN); + return ret; +} + static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base, unsigned int offset, unsigned int *reg, unsigned int *mask) @@ -712,15 +577,28 @@ static struct ad7173_channel_config * ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg) { struct ad7173_channel_config *cfg_aux; - ptrdiff_t cmp_size; int i; - cmp_size = sizeof_field(struct ad7173_channel_config, config_props); + /* + * This is just to make sure that the comparison is adapted after + * struct ad7173_channel_config was changed. + */ + static_assert(sizeof_field(struct ad7173_channel_config, config_props) == + sizeof(struct { + bool bipolar; + bool input_buf; + u8 odr; + u8 ref_sel; + })); + for (i = 0; i < st->num_channels; i++) { cfg_aux = &st->channels[i].cfg; if (cfg_aux->live && - !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size)) + cfg->bipolar == cfg_aux->bipolar && + cfg->input_buf == cfg_aux->input_buf && + cfg->odr == cfg_aux->odr && + cfg->ref_sel == cfg_aux->ref_sel) return cfg_aux; } return NULL; @@ -813,6 +691,9 @@ static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel) FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) | st->channels[channel].ain; + if (st->channels[channel].cfg.openwire_comp_chan >= 0) + channel = st->channels[channel].cfg.openwire_comp_chan; + return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val); } @@ -861,21 +742,280 @@ static int ad7173_disable_all(struct ad_sigma_delta *sd) static int ad7173_disable_one(struct ad_sigma_delta *sd, unsigned int chan) { + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + + if (st->channels[chan].cfg.openwire_comp_chan >= 0) + chan = st->channels[chan].cfg.openwire_comp_chan; + return ad_sd_write_reg(sd, AD7173_REG_CH(chan), 2, 0); } -static const struct ad_sigma_delta_info ad7173_sigma_delta_info = { +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 4, +}; + +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = { .set_channel = ad7173_set_channel, .append_status = ad7173_append_status, .disable_all = ad7173_disable_all, .disable_one = ad7173_disable_one, .set_mode = ad7173_set_mode, .has_registers = true, + .has_named_irqs = true, .addr_shift = 0, .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .data_reg = AD7173_REG_DATA, .num_resetclks = 64, + .num_slots = 8, +}; + +static const struct ad7173_device_info ad4111_device_info = { + .name = "ad4111", + .id = AD4111_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .higher_gpio_bits = true, + .has_temp = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_current_inputs = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .has_openwire_det = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4112_device_info = { + .name = "ad4112", + .id = AD4112_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_current_inputs = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4113_device_info = { + .name = "ad4113", + .id = AD4113_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in_div = 8, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 8, + .num_gpios = 2, + .data_reg_only_16bit = true, + .higher_gpio_bits = true, + .has_vincom_input = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4114_device_info = { + .name = "ad4114", + .id = AD4114_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in_div = 16, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4115_device_info = { + .name = "ad4115", + .id = AD4115_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in_div = 16, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 8 * HZ_PER_MHZ, + .sinc5_data_rates = ad4115_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad4116_device_info = { + .name = "ad4116", + .id = AD4116_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in_div = 11, + .num_channels = 16, + .num_configs = 8, + .num_voltage_in = 16, + .num_gpios = 4, + .has_vincom_input = true, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_internal_fs_calibration = true, + .clock = 4 * HZ_PER_MHZ, + .sinc5_data_rates = ad4116_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7172_2_device_info = { + .name = "ad7172-2", + .id = AD7172_2_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7172_4_device_info = { + .name = "ad7172-4", + .id = AD7172_4_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 9, + .num_channels = 8, + .num_configs = 8, + .num_gpios = 4, + .has_input_buf = true, + .has_ref2 = true, + .has_pow_supply_monitoring = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7173_8_device_info = { + .name = "ad7173-8", + .id = AD7173_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7175_2_device_info = { + .name = "ad7175-2", + .id = AD7175_2_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7175_8_device_info = { + .name = "ad7175-8", + .id = AD7175_8_ID, + .sd_info = &ad7173_sigma_delta_info_8_slots, + .num_voltage_in = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7176_2_device_info = { + .name = "ad7176-2", + .id = AD7176_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_int_ref = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), +}; + +static const struct ad7173_device_info ad7177_2_device_info = { + .name = "ad7177-2", + .id = AD7177_ID, + .sd_info = &ad7173_sigma_delta_info_4_slots, + .num_voltage_in = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_pow_supply_monitoring = true, + .clock = 16 * HZ_PER_MHZ, + .odr_start_value = AD7177_ODR_START_VALUE, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), }; static int ad7173_setup(struct iio_dev *indio_dev) @@ -969,6 +1109,12 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; + if (ch->openwire_det_en) { + ret = ad4111_openwire_event(indio_dev, chan); + if (ret < 0) + return ret; + } + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -1033,11 +1179,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, struct ad7173_state *st = iio_priv(indio_dev); struct ad7173_channel_config *cfg; unsigned int freq, i; - int ret; + int ret = 0; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; switch (info) { /* @@ -1071,7 +1216,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, break; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -1113,12 +1258,57 @@ static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg, return ad_sd_write_reg(&st->sd, reg, reg_size, writeval); } +static int ad7173_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + + switch (type) { + case IIO_EV_TYPE_FAULT: + adchan->openwire_det_en = state; + return 0; + default: + return -EINVAL; + } +} + +static int ad7173_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *adchan = &st->channels[chan->address]; + + switch (type) { + case IIO_EV_TYPE_FAULT: + return adchan->openwire_det_en; + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad4111_events[] = { + { + .type = IIO_EV_TYPE_FAULT, + .dir = IIO_EV_DIR_FAULT_OPENWIRE, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), + }, +}; + static const struct iio_info ad7173_info = { .read_raw = &ad7173_read_raw, .write_raw = &ad7173_write_raw, .debugfs_reg_access = &ad7173_debug_reg_access, .validate_trigger = ad_sd_validate_trigger, .update_scan_mode = ad7173_update_scan_mode, + .write_event_config = ad7173_write_event_config, + .read_event_config = ad7173_read_event_config, }; static const struct iio_scan_type ad4113_scan_type = { @@ -1322,6 +1512,37 @@ static int ad7173_validate_reference(struct ad7173_state *st, int ref_sel) return 0; } +static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st, + bool differential, + unsigned int ain0, + unsigned int ain1) +{ + /* + * If the channel is configured as differential, + * the ad4111 requires specific ains to be used together + */ + if (differential) + return (ain0 % 2) ? (ain0 - 1) == ain1 : (ain0 + 1) == ain1; + + return ain1 == AD4111_VINCOM_INPUT; +} + +static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st, + struct iio_chan_spec *chan, + struct ad7173_channel *chan_st_priv, + unsigned int thrsh_mv) { + unsigned int thrsh_raw; + + thrsh_raw = + BIT(chan->scan_type.realbits - !!(chan_st_priv->cfg.bipolar)) + * thrsh_mv + / ad7173_get_ref_voltage_milli(st, chan_st_priv->cfg.ref_sel); + if (chan->channel < st->info->num_voltage_in_div) + thrsh_raw /= AD4111_DIVIDER_RATIO; + + return thrsh_raw; +} + static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) { struct ad7173_channel *chans_st_arr, *chan_st_priv; @@ -1369,6 +1590,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.bipolar = false; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + chan_st_priv->cfg.openwire_comp_chan = -1; st->adc_mode |= AD7173_ADC_MODE_REF_EN; if (st->info->data_reg_only_16bit) chan_arr[chan_index].scan_type = ad4113_scan_type; @@ -1435,6 +1657,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->channel = ain[0]; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.odr = 0; + chan_st_priv->cfg.openwire_comp_chan = -1; chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); if (chan_st_priv->cfg.bipolar) @@ -1449,6 +1672,14 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan->channel2 = ain[1]; chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]); + if (st->info->has_openwire_det && + ad7173_validate_openwire_ain_inputs(st, chan->differential, ain[0], ain[1])) { + chan->event_spec = ad4111_events; + chan->num_event_specs = ARRAY_SIZE(ad4111_events); + chan_st_priv->cfg.openwire_thrsh_raw = + ad7173_calc_openwire_thrsh_raw(st, chan, chan_st_priv, + AD4111_OW_DET_THRSH_MV); + } } if (st->info->data_reg_only_16bit) @@ -1515,12 +1746,6 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) return ret; } - ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy"); - if (ret < 0) - return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n"); - - st->sigma_delta_info.irq_line = ret; - return ad7173_fw_parse_channel_config(indio_dev); } @@ -1552,9 +1777,7 @@ static int ad7173_probe(struct spi_device *spi) spi->mode = SPI_MODE_3; spi_setup(spi); - st->sigma_delta_info = ad7173_sigma_delta_info; - st->sigma_delta_info.num_slots = st->info->num_configs; - ret = ad_sd_init(&st->sd, indio_dev, spi, &st->sigma_delta_info); + ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info); if (ret) return ret; diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c new file mode 100644 index 000000000000..d9cd903ffdd2 --- /dev/null +++ b/drivers/iio/adc/ad7191.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7191 ADC driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/adc/ad_sigma_delta.h> +#include <linux/iio/iio.h> + +#define ad_sigma_delta_to_ad7191(sigmad) \ + container_of((sigmad), struct ad7191_state, sd) + +#define AD7191_TEMP_CODES_PER_DEGREE 2815 + +#define AD7191_CHAN_MASK BIT(0) +#define AD7191_TEMP_MASK BIT(1) + +enum ad7191_channel { + AD7191_CH_AIN1_AIN2, + AD7191_CH_AIN3_AIN4, + AD7191_CH_TEMP, +}; + +/* + * NOTE: + * The AD7191 features a dual-use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use SPI bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt-capable GPIO. + * + * The SPI controller's chip select must be connected to the PDOWN pin + * of the ADC. When CS (PDOWN) is high, it powers down the device and + * resets the internal circuitry. + */ + +struct ad7191_state { + struct ad_sigma_delta sd; + struct mutex lock; /* Protect device state */ + + struct gpio_descs *odr_gpios; + struct gpio_descs *pga_gpios; + struct gpio_desc *temp_gpio; + struct gpio_desc *chan_gpio; + + u16 int_vref_mv; + const u32 (*scale_avail)[2]; + size_t scale_avail_size; + u32 scale_index; + const u32 *samp_freq_avail; + size_t samp_freq_avail_size; + u32 samp_freq_index; + + struct clk *mclk; +}; + +static int ad7191_set_channel(struct ad_sigma_delta *sd, unsigned int address) +{ + struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd); + u8 temp_gpio_val, chan_gpio_val; + + if (!FIELD_FIT(AD7191_CHAN_MASK | AD7191_TEMP_MASK, address)) + return -EINVAL; + + chan_gpio_val = FIELD_GET(AD7191_CHAN_MASK, address); + temp_gpio_val = FIELD_GET(AD7191_TEMP_MASK, address); + + gpiod_set_value(st->chan_gpio, chan_gpio_val); + gpiod_set_value(st->temp_gpio, temp_gpio_val); + + return 0; +} + +static int ad7191_set_cs(struct ad_sigma_delta *sigma_delta, int assert) +{ + struct spi_transfer t = { + .len = 0, + .cs_change = assert, + }; + struct spi_message m; + + spi_message_init_with_transfers(&m, &t, 1); + + return spi_sync_locked(sigma_delta->spi, &m); +} + +static int ad7191_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd); + + switch (mode) { + case AD_SD_MODE_CONTINUOUS: + case AD_SD_MODE_SINGLE: + return ad7191_set_cs(&st->sd, 1); + case AD_SD_MODE_IDLE: + return ad7191_set_cs(&st->sd, 0); + default: + return -EINVAL; + } +} + +static const struct ad_sigma_delta_info ad7191_sigma_delta_info = { + .set_channel = ad7191_set_channel, + .set_mode = ad7191_set_mode, + .has_registers = false, +}; + +static int ad7191_init_regulators(struct iio_dev *indio_dev) +{ + struct ad7191_state *st = iio_priv(indio_dev); + struct device *dev = &st->sd.spi->dev; + int ret; + + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable specified AVdd supply\n"); + + ret = devm_regulator_get_enable(dev, "dvdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable specified DVdd supply\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get Vref voltage\n"); + + st->int_vref_mv = ret / 1000; + + return 0; +} + +static int ad7191_config_setup(struct iio_dev *indio_dev) +{ + struct ad7191_state *st = iio_priv(indio_dev); + struct device *dev = &st->sd.spi->dev; + /* Sampling frequencies in Hz, see Table 5 */ + static const u32 samp_freq[4] = { 120, 60, 50, 10 }; + /* Gain options, see Table 7 */ + const u32 gain[4] = { 1, 8, 64, 128 }; + static u32 scale_buffer[4][2]; + int odr_value, odr_index = 0, pga_value, pga_index = 0, i, ret; + u64 scale_uv; + + st->samp_freq_index = 0; + st->scale_index = 0; + + ret = device_property_read_u32(dev, "adi,odr-value", &odr_value); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "Failed to get odr value.\n"); + + if (ret == -EINVAL) { + st->odr_gpios = devm_gpiod_get_array(dev, "odr", GPIOD_OUT_LOW); + if (IS_ERR(st->odr_gpios)) + return dev_err_probe(dev, PTR_ERR(st->odr_gpios), + "Failed to get odr gpios.\n"); + + if (st->odr_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, "Expected 2 odr gpio pins.\n"); + + st->samp_freq_avail = samp_freq; + st->samp_freq_avail_size = ARRAY_SIZE(samp_freq); + } else { + for (i = 0; i < ARRAY_SIZE(samp_freq); i++) { + if (odr_value != samp_freq[i]) + continue; + odr_index = i; + break; + } + + st->samp_freq_avail = &samp_freq[odr_index]; + st->samp_freq_avail_size = 1; + + st->odr_gpios = NULL; + } + + mutex_lock(&st->lock); + + for (i = 0; i < ARRAY_SIZE(scale_buffer); i++) { + scale_uv = ((u64)st->int_vref_mv * NANO) >> + (indio_dev->channels[0].scan_type.realbits - 1); + do_div(scale_uv, gain[i]); + scale_buffer[i][1] = do_div(scale_uv, NANO); + scale_buffer[i][0] = scale_uv; + } + + mutex_unlock(&st->lock); + + ret = device_property_read_u32(dev, "adi,pga-value", &pga_value); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "Failed to get pga value.\n"); + + if (ret == -EINVAL) { + st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW); + if (IS_ERR(st->pga_gpios)) + return dev_err_probe(dev, PTR_ERR(st->pga_gpios), + "Failed to get pga gpios.\n"); + + if (st->pga_gpios->ndescs != 2) + return dev_err_probe(dev, -EINVAL, "Expected 2 pga gpio pins.\n"); + + st->scale_avail = scale_buffer; + st->scale_avail_size = ARRAY_SIZE(scale_buffer); + } else { + for (i = 0; i < ARRAY_SIZE(gain); i++) { + if (pga_value != gain[i]) + continue; + pga_index = i; + break; + } + + st->scale_avail = &scale_buffer[pga_index]; + st->scale_avail_size = 1; + + st->pga_gpios = NULL; + } + + st->temp_gpio = devm_gpiod_get(dev, "temp", GPIOD_OUT_LOW); + if (IS_ERR(st->temp_gpio)) + return dev_err_probe(dev, PTR_ERR(st->temp_gpio), + "Failed to get temp gpio.\n"); + + st->chan_gpio = devm_gpiod_get(dev, "chan", GPIOD_OUT_LOW); + if (IS_ERR(st->chan_gpio)) + return dev_err_probe(dev, PTR_ERR(st->chan_gpio), + "Failed to get chan gpio.\n"); + + return 0; +} + +static int ad7191_clock_setup(struct ad7191_state *st) +{ + struct device *dev = &st->sd.spi->dev; + + st->mclk = devm_clk_get_optional_enabled(dev, "mclk"); + if (IS_ERR(st->mclk)) + return dev_err_probe(dev, PTR_ERR(st->mclk), + "Failed to get mclk.\n"); + + return 0; +} + +static int ad7191_setup(struct iio_dev *indio_dev) +{ + struct ad7191_state *st = iio_priv(indio_dev); + int ret; + + ret = ad7191_init_regulators(indio_dev); + if (ret) + return ret; + + ret = ad7191_config_setup(indio_dev); + if (ret) + return ret; + + return ad7191_clock_setup(st); +} + +static int ad7191_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long m) +{ + struct ad7191_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: { + guard(mutex)(&st->lock); + *val = st->scale_avail[st->scale_index][0]; + *val2 = st->scale_avail[st->scale_index][1]; + return IIO_VAL_INT_PLUS_NANO; + } + case IIO_TEMP: + *val = 0; + *val2 = NANO / AD7191_TEMP_CODES_PER_DEGREE; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << (chan->scan_type.realbits - 1)); + switch (chan->type) { + case IIO_VOLTAGE: + return IIO_VAL_INT; + case IIO_TEMP: + *val -= 273 * AD7191_TEMP_CODES_PER_DEGREE; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->samp_freq_avail[st->samp_freq_index]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7191_set_gain(struct ad7191_state *st, int gain_index) +{ + DECLARE_BITMAP(bitmap, 2) = { }; + + st->scale_index = gain_index; + + bitmap_write(bitmap, gain_index, 0, 2); + + return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap); +} + +static int ad7191_set_samp_freq(struct ad7191_state *st, int samp_freq_index) +{ + DECLARE_BITMAP(bitmap, 2) = {}; + + st->samp_freq_index = samp_freq_index; + + bitmap_write(bitmap, samp_freq_index, 0, 2); + + return gpiod_multi_set_value_cansleep(st->odr_gpios, bitmap); +} + +static int __ad7191_write_raw(struct ad7191_state *st, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: { + if (!st->pga_gpios) + return -EPERM; + guard(mutex)(&st->lock); + for (i = 0; i < st->scale_avail_size; i++) { + if (val2 == st->scale_avail[i][1]) + return ad7191_set_gain(st, i); + } + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: { + if (!st->odr_gpios) + return -EPERM; + guard(mutex)(&st->lock); + for (i = 0; i < st->samp_freq_avail_size; i++) { + if (val == st->samp_freq_avail[i]) + return ad7191_set_samp_freq(st, i); + } + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad7191_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct ad7191_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7191_write_raw(st, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7191_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7191_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, const int **vals, + int *type, int *length, long mask) +{ + struct ad7191_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + *length = st->scale_avail_size * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)st->samp_freq_avail; + *type = IIO_VAL_INT; + *length = st->samp_freq_avail_size; + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static const struct iio_info ad7191_info = { + .read_raw = ad7191_read_raw, + .write_raw = ad7191_write_raw, + .write_raw_get_fmt = ad7191_write_raw_get_fmt, + .read_avail = ad7191_read_avail, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_chan_spec ad7191_channels[] = { + { + .type = IIO_TEMP, + .address = AD7191_CH_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_VOLTAGE, + .differential = 1, + .indexed = 1, + .channel = 1, + .channel2 = 2, + .address = AD7191_CH_AIN1_AIN2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_VOLTAGE, + .differential = 1, + .indexed = 1, + .channel = 3, + .channel2 = 4, + .address = AD7191_CH_AIN3_AIN4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static int ad7191_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7191_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = "ad7191"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7191_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7191_channels); + indio_dev->info = &ad7191_info; + + ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7191_sigma_delta_info); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7191_setup(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7191_of_match[] = { + { .compatible = "adi,ad7191", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7191_of_match); + +static const struct spi_device_id ad7191_id_table[] = { + { "ad7191" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7191_id_table); + +static struct spi_driver ad7191_driver = { + .driver = { + .name = "ad7191", + .of_match_table = ad7191_of_match, + }, + .probe = ad7191_probe, + .id_table = ad7191_id_table, +}; +module_spi_driver(ad7191_driver); + +MODULE_AUTHOR("Alisa-Dariana Roman <alisa.roman@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7191 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA"); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index cfaf8f7e0a07..530e1d307860 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -7,6 +7,7 @@ #include <linux/interrupt.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/device.h> @@ -256,6 +257,9 @@ static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev, if (ret) return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + temp = st->syscalib_mode[chan->channel]; if (sys_calib) { if (temp == AD7192_SYSCALIB_ZERO_SCALE) @@ -266,6 +270,8 @@ static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev, chan->address); } + iio_device_release_direct(indio_dev); + return ret ? ret : len; } @@ -693,9 +699,8 @@ static ssize_t ad7192_set(struct device *dev, if (ret < 0) return ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; switch ((u32)this_attr->address) { case AD7192_REG_GPOCON: @@ -718,7 +723,7 @@ static ssize_t ad7192_set(struct device *dev, ret = -EINVAL; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret ? ret : len; } @@ -945,82 +950,83 @@ static int ad7192_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int ad7192_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) +static int __ad7192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) { struct ad7192_state *st = iio_priv(indio_dev); - int ret, i, div; + int i, div; unsigned int tmp; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - mutex_lock(&st->lock); + guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_SCALE: - ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) - if (val2 == st->scale_avail[i][1]) { - ret = 0; - tmp = st->conf; - st->conf &= ~AD7192_CONF_GAIN_MASK; - st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i); - if (tmp == st->conf) - break; - ad_sd_write_reg(&st->sd, AD7192_REG_CONF, - 3, st->conf); - ad7192_calibrate_all(st); - break; - } - break; - case IIO_CHAN_INFO_SAMP_FREQ: - if (!val) { - ret = -EINVAL; - break; + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + if (val2 != st->scale_avail[i][1]) + continue; + + tmp = st->conf; + st->conf &= ~AD7192_CONF_GAIN_MASK; + st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i); + if (tmp == st->conf) + return 0; + ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); + ad7192_calibrate_all(st); + return 0; } + return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) + return -EINVAL; div = st->fclk / (val * ad7192_get_f_order(st) * 1024); - if (div < 1 || div > 1023) { - ret = -EINVAL; - break; - } + if (div < 1 || div > 1023) + return -EINVAL; st->mode &= ~AD7192_MODE_RATE_MASK; st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div); ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); ad7192_update_filter_freq_avail(st); - break; + return 0; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000); - break; + return ad7192_set_3db_filter_freq(st, val, val2 / 1000); case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) - if (val == st->oversampling_ratio_avail[i]) { - ret = 0; - tmp = st->mode; - st->mode &= ~AD7192_MODE_AVG_MASK; - st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i); - if (tmp == st->mode) - break; - ad_sd_write_reg(&st->sd, AD7192_REG_MODE, - 3, st->mode); - break; - } - ad7192_update_filter_freq_avail(st); - break; + for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) { + if (val != st->oversampling_ratio_avail[i]) + continue; + + tmp = st->mode; + st->mode &= ~AD7192_MODE_AVG_MASK; + st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i); + if (tmp == st->mode) + return 0; + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + ad7192_update_filter_freq_avail(st); + return 0; + } + return -EINVAL; default: - ret = -EINVAL; + return -EINVAL; } +} + +static int ad7192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - mutex_unlock(&st->lock); + ret = __ad7192_write_raw(indio_dev, chan, val, val2, mask); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 858c8be2ff1a..18559757f908 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -153,11 +153,10 @@ static int ad7266_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ad7266_read_single(st, val, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index b35bd4d9ef81..28b88092b4aa 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -232,16 +232,15 @@ static int ad7298_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; if (chan->address == AD7298_CH_TEMP) ret = ad7298_scan_temp(st, val); else ret = ad7298_scan_direct(st, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index 4f32cb22f140..4fcb49fdf566 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -15,6 +15,10 @@ * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf * adaq4370-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4370-4.pdf * adaq4380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4380-4.pdf + * adaq4381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4381-4.pdf + * + * HDL ad738x_fmc: https://analogdevicesinc.github.io/hdl/projects/ad738x_fmc/index.html + * */ #include <linux/align.h> @@ -29,11 +33,14 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/spi/offload/consumer.h> #include <linux/spi/spi.h> #include <linux/units.h> #include <linux/util_macros.h> #include <linux/iio/buffer.h> +#include <linux/iio/buffer-dmaengine.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> @@ -91,6 +98,12 @@ #define AD7380_NUM_SDO_LINES 1 #define AD7380_DEFAULT_GAIN_MILLI 1000 +/* + * Using SPI offload, storagebits is always 32, so can't be used to compute struct + * spi_transfer.len. Using realbits instead. + */ +#define AD7380_SPI_BYTES(scan_type) ((scan_type)->realbits > 16 ? 4 : 2) + struct ad7380_timing_specs { const unsigned int t_csh_ns; /* CS minimum high time */ }; @@ -98,6 +111,7 @@ struct ad7380_timing_specs { struct ad7380_chip_info { const char *name; const struct iio_chan_spec *channels; + const struct iio_chan_spec *offload_channels; unsigned int num_channels; unsigned int num_simult_channels; bool has_hardware_gain; @@ -110,6 +124,25 @@ struct ad7380_chip_info { unsigned int num_vcm_supplies; const unsigned long *available_scan_masks; const struct ad7380_timing_specs *timing_specs; + u32 max_conversion_rate_hz; +}; + +static const struct iio_event_spec ad7380_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_dir = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE), + }, }; enum { @@ -197,6 +230,91 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = { }, }; +/* + * Defining here scan types for offload mode, since with current available HDL + * only a value of 32 for storagebits is supported. + */ + +/* Extended scan types for 12-bit unsigned chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_12_u_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 12, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 14, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit signed chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_14_s_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 14, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 14-bit unsigned chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_14_u_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 14, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit signed_chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_16_s_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 's', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + +/* Extended scan types for 16-bit unsigned chips, offload support. */ +static const struct iio_scan_type ad7380_scan_type_16_u_offload[] = { + [AD7380_SCAN_TYPE_NORMAL] = { + .sign = 'u', + .realbits = 16, + .storagebits = 32, + .endianness = IIO_CPU, + }, + [AD7380_SCAN_TYPE_RESOLUTION_BOOST] = { + .sign = 'u', + .realbits = 18, + .storagebits = 32, + .endianness = IIO_CPU, + }, +}; + #define _AD7380_CHANNEL(index, bits, diff, sign, gain) { \ .type = IIO_VOLTAGE, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ @@ -214,50 +332,127 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = { .has_ext_scan_type = 1, \ .ext_scan_type = ad7380_scan_type_##bits##_##sign, \ .num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \ + .event_spec = ad7380_events, \ + .num_event_specs = ARRAY_SIZE(ad7380_events), \ +} + +#define _AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, gain) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + ((gain) ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \ + ((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \ + .info_mask_shared_by_type = ((gain) ? 0 : BIT(IIO_CHAN_INFO_SCALE)) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .indexed = 1, \ + .differential = (diff), \ + .channel = (diff) ? (2 * (index)) : (index), \ + .channel2 = (diff) ? (2 * (index) + 1) : 0, \ + .scan_index = (index), \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad7380_scan_type_##bits##_##sign##_offload, \ + .num_ext_scan_type = \ + ARRAY_SIZE(ad7380_scan_type_##bits##_##sign##_offload), \ + .event_spec = ad7380_events, \ + .num_event_specs = ARRAY_SIZE(ad7380_events), \ } +/* + * Notes on the offload channels: + * - There is no soft timestamp since everything is done in hardware. + * - There is a sampling frequency attribute added. This controls the SPI + * offload trigger. + * - The storagebits value depends on the SPI offload provider. Currently there + * is only one supported provider, namely the ADI PULSAR ADC HDL project, + * which always uses 32-bit words for data values, even for <= 16-bit ADCs. + * So the value is just hardcoded to 32 for now. + */ + #define AD7380_CHANNEL(index, bits, diff, sign) \ _AD7380_CHANNEL(index, bits, diff, sign, false) #define ADAQ4380_CHANNEL(index, bits, diff, sign) \ _AD7380_CHANNEL(index, bits, diff, sign, true) -#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \ +#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + AD7380_CHANNEL(2, bits, diff, sign), \ + AD7380_CHANNEL(3, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + ADAQ4380_CHANNEL(0, bits, diff, sign), \ + ADAQ4380_CHANNEL(1, bits, diff, sign), \ + ADAQ4380_CHANNEL(2, bits, diff, sign), \ + ADAQ4380_CHANNEL(3, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_CHANNEL(0, bits, diff, sign), \ + AD7380_CHANNEL(1, bits, diff, sign), \ + AD7380_CHANNEL(2, bits, diff, sign), \ + AD7380_CHANNEL(3, bits, diff, sign), \ + AD7380_CHANNEL(4, bits, diff, sign), \ + AD7380_CHANNEL(5, bits, diff, sign), \ + AD7380_CHANNEL(6, bits, diff, sign), \ + AD7380_CHANNEL(7, bits, diff, sign), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +#define AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign) \ +_AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, false) + +#define ADAQ4380_OFFLOAD_CHANNEL(index, bits, diff, sign) \ +_AD7380_OFFLOAD_CHANNEL(index, bits, diff, sign, true) + +#define DEFINE_AD7380_2_OFFLOAD_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ - AD7380_CHANNEL(0, bits, diff, sign), \ - AD7380_CHANNEL(1, bits, diff, sign), \ - IIO_CHAN_SOFT_TIMESTAMP(2), \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ } -#define DEFINE_AD7380_4_CHANNEL(name, bits, diff, sign) \ +#define DEFINE_AD7380_4_OFFLOAD_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ - AD7380_CHANNEL(0, bits, diff, sign), \ - AD7380_CHANNEL(1, bits, diff, sign), \ - AD7380_CHANNEL(2, bits, diff, sign), \ - AD7380_CHANNEL(3, bits, diff, sign), \ - IIO_CHAN_SOFT_TIMESTAMP(4), \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \ } -#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \ -static const struct iio_chan_spec name[] = { \ - ADAQ4380_CHANNEL(0, bits, diff, sign), \ - ADAQ4380_CHANNEL(1, bits, diff, sign), \ - ADAQ4380_CHANNEL(2, bits, diff, sign), \ - ADAQ4380_CHANNEL(3, bits, diff, sign), \ - IIO_CHAN_SOFT_TIMESTAMP(4), \ +#define DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(name, bits, diff, sign) \ +static const struct iio_chan_spec name[] = { \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \ } -#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \ +#define DEFINE_AD7380_8_OFFLOAD_CHANNEL(name, bits, diff, sign) \ static const struct iio_chan_spec name[] = { \ - AD7380_CHANNEL(0, bits, diff, sign), \ - AD7380_CHANNEL(1, bits, diff, sign), \ - AD7380_CHANNEL(2, bits, diff, sign), \ - AD7380_CHANNEL(3, bits, diff, sign), \ - AD7380_CHANNEL(4, bits, diff, sign), \ - AD7380_CHANNEL(5, bits, diff, sign), \ - AD7380_CHANNEL(6, bits, diff, sign), \ - AD7380_CHANNEL(7, bits, diff, sign), \ - IIO_CHAN_SOFT_TIMESTAMP(8), \ + AD7380_OFFLOAD_CHANNEL(0, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(1, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(2, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(3, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(4, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(5, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(6, bits, diff, sign), \ + AD7380_OFFLOAD_CHANNEL(7, bits, diff, sign), \ } /* fully differential */ @@ -266,6 +461,7 @@ DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s); DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s); DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s); DEFINE_ADAQ4380_4_CHANNEL(adaq4380_4_channels, 16, 1, s); +DEFINE_ADAQ4380_4_CHANNEL(adaq4381_4_channels, 14, 1, s); /* pseudo differential */ DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s); DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s); @@ -280,6 +476,28 @@ DEFINE_AD7380_8_CHANNEL(ad7386_4_channels, 16, 0, u); DEFINE_AD7380_8_CHANNEL(ad7387_4_channels, 14, 0, u); DEFINE_AD7380_8_CHANNEL(ad7388_4_channels, 12, 0, u); +/* offload channels */ +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7380_offload_channels, 16, 1, s); +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7381_offload_channels, 14, 1, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7380_4_offload_channels, 16, 1, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7381_4_offload_channels, 14, 1, s); +DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(adaq4380_4_offload_channels, 16, 1, s); +DEFINE_ADAQ4380_4_OFFLOAD_CHANNEL(adaq4381_4_offload_channels, 14, 1, s); + +/* pseudo differential */ +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7383_offload_channels, 16, 0, s); +DEFINE_AD7380_2_OFFLOAD_CHANNEL(ad7384_offload_channels, 14, 0, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7383_4_offload_channels, 16, 0, s); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7384_4_offload_channels, 14, 0, s); + +/* Single ended */ +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7386_offload_channels, 16, 0, u); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7387_offload_channels, 14, 0, u); +DEFINE_AD7380_4_OFFLOAD_CHANNEL(ad7388_offload_channels, 12, 0, u); +DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7386_4_offload_channels, 16, 0, u); +DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7387_4_offload_channels, 14, 0, u); +DEFINE_AD7380_8_OFFLOAD_CHANNEL(ad7388_4_offload_channels, 12, 0, u); + static const char * const ad7380_supplies[] = { "vcc", "vlogic", }; @@ -386,28 +604,33 @@ static const int ad7380_gains[] = { static const struct ad7380_chip_info ad7380_chip_info = { .name = "ad7380", .channels = ad7380_channels, + .offload_channels = ad7380_offload_channels, .num_channels = ARRAY_SIZE(ad7380_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, .num_supplies = ARRAY_SIZE(ad7380_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7381_chip_info = { .name = "ad7381", .channels = ad7381_channels, + .offload_channels = ad7381_offload_channels, .num_channels = ARRAY_SIZE(ad7381_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, .num_supplies = ARRAY_SIZE(ad7380_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7383_chip_info = { .name = "ad7383", .channels = ad7383_channels, + .offload_channels = ad7383_offload_channels, .num_channels = ARRAY_SIZE(ad7383_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, @@ -416,11 +639,13 @@ static const struct ad7380_chip_info ad7383_chip_info = { .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7384_chip_info = { .name = "ad7384", .channels = ad7384_channels, + .offload_channels = ad7384_offload_channels, .num_channels = ARRAY_SIZE(ad7384_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, @@ -429,11 +654,13 @@ static const struct ad7380_chip_info ad7384_chip_info = { .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), .available_scan_masks = ad7380_2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7386_chip_info = { .name = "ad7386", .channels = ad7386_channels, + .offload_channels = ad7386_offload_channels, .num_channels = ARRAY_SIZE(ad7386_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, @@ -441,11 +668,13 @@ static const struct ad7380_chip_info ad7386_chip_info = { .has_mux = true, .available_scan_masks = ad7380_2x2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7387_chip_info = { .name = "ad7387", .channels = ad7387_channels, + .offload_channels = ad7387_offload_channels, .num_channels = ARRAY_SIZE(ad7387_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, @@ -453,11 +682,13 @@ static const struct ad7380_chip_info ad7387_chip_info = { .has_mux = true, .available_scan_masks = ad7380_2x2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7388_chip_info = { .name = "ad7388", .channels = ad7388_channels, + .offload_channels = ad7388_offload_channels, .num_channels = ARRAY_SIZE(ad7388_channels), .num_simult_channels = 2, .supplies = ad7380_supplies, @@ -465,11 +696,13 @@ static const struct ad7380_chip_info ad7388_chip_info = { .has_mux = true, .available_scan_masks = ad7380_2x2_channel_scan_masks, .timing_specs = &ad7380_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7380_4_chip_info = { .name = "ad7380-4", .channels = ad7380_4_channels, + .offload_channels = ad7380_4_offload_channels, .num_channels = ARRAY_SIZE(ad7380_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, @@ -477,22 +710,26 @@ static const struct ad7380_chip_info ad7380_4_chip_info = { .external_ref_only = true, .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7381_4_chip_info = { .name = "ad7381-4", .channels = ad7381_4_channels, + .offload_channels = ad7381_4_offload_channels, .num_channels = ARRAY_SIZE(ad7381_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, .num_supplies = ARRAY_SIZE(ad7380_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7383_4_chip_info = { .name = "ad7383-4", .channels = ad7383_4_channels, + .offload_channels = ad7383_4_offload_channels, .num_channels = ARRAY_SIZE(ad7383_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, @@ -501,11 +738,13 @@ static const struct ad7380_chip_info ad7383_4_chip_info = { .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7384_4_chip_info = { .name = "ad7384-4", .channels = ad7384_4_channels, + .offload_channels = ad7384_4_offload_channels, .num_channels = ARRAY_SIZE(ad7384_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, @@ -514,11 +753,13 @@ static const struct ad7380_chip_info ad7384_4_chip_info = { .num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies), .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7386_4_chip_info = { .name = "ad7386-4", .channels = ad7386_4_channels, + .offload_channels = ad7386_4_offload_channels, .num_channels = ARRAY_SIZE(ad7386_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, @@ -526,11 +767,13 @@ static const struct ad7380_chip_info ad7386_4_chip_info = { .has_mux = true, .available_scan_masks = ad7380_2x4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7387_4_chip_info = { .name = "ad7387-4", .channels = ad7387_4_channels, + .offload_channels = ad7387_4_offload_channels, .num_channels = ARRAY_SIZE(ad7387_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, @@ -538,11 +781,13 @@ static const struct ad7380_chip_info ad7387_4_chip_info = { .has_mux = true, .available_scan_masks = ad7380_2x4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info ad7388_4_chip_info = { .name = "ad7388-4", .channels = ad7388_4_channels, + .offload_channels = ad7388_4_offload_channels, .num_channels = ARRAY_SIZE(ad7388_4_channels), .num_simult_channels = 4, .supplies = ad7380_supplies, @@ -550,11 +795,13 @@ static const struct ad7380_chip_info ad7388_4_chip_info = { .has_mux = true, .available_scan_masks = ad7380_2x4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, }; static const struct ad7380_chip_info adaq4370_4_chip_info = { .name = "adaq4370-4", .channels = adaq4380_4_channels, + .offload_channels = adaq4380_4_offload_channels, .num_channels = ARRAY_SIZE(adaq4380_4_channels), .num_simult_channels = 4, .supplies = adaq4380_supplies, @@ -563,11 +810,13 @@ static const struct ad7380_chip_info adaq4370_4_chip_info = { .has_hardware_gain = true, .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 2 * MEGA, }; static const struct ad7380_chip_info adaq4380_4_chip_info = { .name = "adaq4380-4", .channels = adaq4380_4_channels, + .offload_channels = adaq4380_4_offload_channels, .num_channels = ARRAY_SIZE(adaq4380_4_channels), .num_simult_channels = 4, .supplies = adaq4380_supplies, @@ -576,13 +825,32 @@ static const struct ad7380_chip_info adaq4380_4_chip_info = { .has_hardware_gain = true, .available_scan_masks = ad7380_4_channel_scan_masks, .timing_specs = &ad7380_4_timing, + .max_conversion_rate_hz = 4 * MEGA, +}; + +static const struct ad7380_chip_info adaq4381_4_chip_info = { + .name = "adaq4381-4", + .channels = adaq4381_4_channels, + .offload_channels = adaq4381_4_offload_channels, + .num_channels = ARRAY_SIZE(adaq4381_4_channels), + .num_simult_channels = 4, + .supplies = adaq4380_supplies, + .num_supplies = ARRAY_SIZE(adaq4380_supplies), + .adaq_internal_ref_only = true, + .has_hardware_gain = true, + .available_scan_masks = ad7380_4_channel_scan_masks, + .timing_specs = &ad7380_4_timing, +}; + +static const struct spi_offload_config ad7380_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, }; struct ad7380_state { const struct ad7380_chip_info *chip_info; struct spi_device *spi; struct regmap *regmap; - unsigned int oversampling_ratio; bool resolution_boost_enabled; unsigned int ch; bool seq; @@ -594,6 +862,13 @@ struct ad7380_state { struct spi_message normal_msg; struct spi_transfer seq_xfer[4]; struct spi_message seq_msg; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + unsigned long offload_trigger_hz; + + int sample_freq_range[3]; /* * DMA (thus cache coherency maintenance) requires the transfer buffers * to live in their own cache lines. @@ -663,6 +938,20 @@ static int ad7380_regmap_reg_read(void *context, unsigned int reg, return 0; } +static const struct reg_default ad7380_reg_defaults[] = { + { AD7380_REG_ADDR_ALERT_LOW_TH, 0x800 }, + { AD7380_REG_ADDR_ALERT_HIGH_TH, 0x7FF }, +}; + +static const struct regmap_range ad7380_volatile_reg_ranges[] = { + regmap_reg_range(AD7380_REG_ADDR_CONFIG2, AD7380_REG_ADDR_ALERT), +}; + +static const struct regmap_access_table ad7380_volatile_regs = { + .yes_ranges = ad7380_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7380_volatile_reg_ranges), +}; + static const struct regmap_config ad7380_regmap_config = { .reg_bits = 3, .val_bits = 12, @@ -670,20 +959,59 @@ static const struct regmap_config ad7380_regmap_config = { .reg_write = ad7380_regmap_reg_write, .max_register = AD7380_REG_ADDR_ALERT_HIGH_TH, .can_sleep = true, + .reg_defaults = ad7380_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ad7380_reg_defaults), + .volatile_table = &ad7380_volatile_regs, + .cache_type = REGCACHE_MAPLE, }; static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg, u32 writeval, u32 *readval) { - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct ad7380_state *st = iio_priv(indio_dev); + struct ad7380_state *st = iio_priv(indio_dev); + int ret; - if (readval) - return regmap_read(st->regmap, reg, readval); - else - return regmap_write(st->regmap, reg, writeval); - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) + ret = regmap_read(st->regmap, reg, readval); + else + ret = regmap_write(st->regmap, reg, writeval); + + iio_device_release_direct(indio_dev); + + return ret; +} + +/** + * ad7380_regval_to_osr - convert OSR register value to ratio + * @regval: register value to check + * + * Returns: the ratio corresponding to the OSR register. If regval is not in + * bound, return 1 (oversampling disabled) + * + */ +static int ad7380_regval_to_osr(unsigned int regval) +{ + if (regval >= ARRAY_SIZE(ad7380_oversampling_ratios)) + return 1; + + return ad7380_oversampling_ratios[regval]; +} + +static int ad7380_get_osr(struct ad7380_state *st, int *val) +{ + u32 tmp; + int ret; + + ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp); + if (ret) + return ret; + + *val = ad7380_regval_to_osr(FIELD_GET(AD7380_CONFIG1_OSR, tmp)); + + return 0; } /* @@ -701,11 +1029,15 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) .unit = SPI_DELAY_UNIT_NSECS, } }; - int ret; + int oversampling_ratio, ret; if (st->ch == ch) return 0; + ret = ad7380_get_osr(st, &oversampling_ratio); + if (ret) + return ret; + ret = regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG1, AD7380_CONFIG1_CH, @@ -716,9 +1048,9 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) st->ch = ch; - if (st->oversampling_ratio > 1) + if (oversampling_ratio > 1) xfer.delay.value = T_CONVERT_0_NS + - T_CONVERT_X_NS * (st->oversampling_ratio - 1) * + T_CONVERT_X_NS * (oversampling_ratio - 1) * st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; return spi_sync_transfer(st->spi, &xfer, 1); @@ -729,20 +1061,25 @@ static int ad7380_set_ch(struct ad7380_state *st, unsigned int ch) * @st: device instance specific state * @scan_type: current scan type */ -static void ad7380_update_xfers(struct ad7380_state *st, +static int ad7380_update_xfers(struct ad7380_state *st, const struct iio_scan_type *scan_type) { struct spi_transfer *xfer = st->seq ? st->seq_xfer : st->normal_xfer; unsigned int t_convert = T_CONVERT_NS; + int oversampling_ratio, ret; /* * In the case of oversampling, conversion time is higher than in normal * mode. Technically T_CONVERT_X_NS is lower for some chips, but we use * the maximum value for simplicity for now. */ - if (st->oversampling_ratio > 1) + ret = ad7380_get_osr(st, &oversampling_ratio); + if (ret) + return ret; + + if (oversampling_ratio > 1) t_convert = T_CONVERT_0_NS + T_CONVERT_X_NS * - (st->oversampling_ratio - 1) * + (oversampling_ratio - 1) * st->chip_info->num_simult_channels / AD7380_NUM_SDO_LINES; if (st->seq) { @@ -751,11 +1088,11 @@ static void ad7380_update_xfers(struct ad7380_state *st, xfer[2].bits_per_word = xfer[3].bits_per_word = scan_type->realbits; xfer[2].len = xfer[3].len = - BITS_TO_BYTES(scan_type->storagebits) * + AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; xfer[3].rx_buf = xfer[2].rx_buf + xfer[2].len; /* Additional delay required here when oversampling is enabled */ - if (st->oversampling_ratio > 1) + if (oversampling_ratio > 1) xfer[2].delay.value = t_convert; else xfer[2].delay.value = 0; @@ -764,16 +1101,145 @@ static void ad7380_update_xfers(struct ad7380_state *st, xfer[0].delay.value = t_convert; xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS; xfer[1].bits_per_word = scan_type->realbits; - xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) * + xfer[1].len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; } + + return 0; } +static int ad7380_set_sample_freq(struct ad7380_state *st, int val) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = val, + }, + }; + int ret; + + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + st->offload_trigger_hz = config.periodic.frequency_hz; + + return 0; +} + +static int ad7380_init_offload_msg(struct ad7380_state *st, + struct iio_dev *indio_dev) +{ + struct spi_transfer *xfer = &st->offload_xfer; + struct device *dev = &st->spi->dev; + const struct iio_scan_type *scan_type; + int oversampling_ratio; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, + &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + if (st->chip_info->has_mux) { + int index; + + ret = iio_active_scan_mask_index(indio_dev); + if (ret < 0) + return ret; + + index = ret; + if (index == AD7380_SCAN_MASK_SEQ) { + ret = regmap_set_bits(st->regmap, AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ); + if (ret) + return ret; + + st->seq = true; + } else { + ret = ad7380_set_ch(st, index); + if (ret) + return ret; + } + } + + ret = ad7380_get_osr(st, &oversampling_ratio); + if (ret) + return ret; + + xfer->bits_per_word = scan_type->realbits; + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; + + spi_message_init_with_transfers(&st->offload_msg, xfer, 1); + st->offload_msg.offload = st->offload; + + ret = spi_optimize_message(st->spi, &st->offload_msg); + if (ret) { + dev_err(dev, "failed to prepare offload msg, err: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ad7380_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7380_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = st->offload_trigger_hz, + }, + }; + int ret; + + ret = ad7380_init_offload_msg(st, indio_dev); + if (ret) + return ret; + + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config); + if (ret) + spi_unoptimize_message(&st->offload_msg); + + return ret; +} + +static int ad7380_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + if (st->seq) { + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_SEQ, + FIELD_PREP(AD7380_CONFIG1_SEQ, 0)); + if (ret) + return ret; + + st->seq = false; + } + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + spi_unoptimize_message(&st->offload_msg); + + return 0; +} + +static const struct iio_buffer_setup_ops ad7380_offload_buffer_setup_ops = { + .postenable = ad7380_offload_buffer_postenable, + .predisable = ad7380_offload_buffer_predisable, +}; + static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev) { struct ad7380_state *st = iio_priv(indio_dev); const struct iio_scan_type *scan_type; struct spi_message *msg = &st->normal_msg; + int ret; /* * Currently, we always read all channels at the same time. The scan_type @@ -785,7 +1251,6 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev) if (st->chip_info->has_mux) { unsigned int index; - int ret; /* * Depending on the requested scan_mask and current state, @@ -816,7 +1281,9 @@ static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev) } - ad7380_update_xfers(st, scan_type); + ret = ad7380_update_xfers(st, scan_type); + if (ret) + return ret; return spi_optimize_message(st->spi, msg); } @@ -889,13 +1356,15 @@ static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index, return ret; } - ad7380_update_xfers(st, scan_type); + ret = ad7380_update_xfers(st, scan_type); + if (ret) + return ret; ret = spi_sync(st->spi, &st->normal_msg); if (ret < 0) return ret; - if (scan_type->storagebits > 16) { + if (scan_type->realbits > 16) { if (scan_type->sign == 's') *val = sign_extend32(*(u32 *)(st->scan_data + 4 * index), scan_type->realbits - 1); @@ -920,6 +1389,7 @@ static int ad7380_read_raw(struct iio_dev *indio_dev, { struct ad7380_state *st = iio_priv(indio_dev); const struct iio_scan_type *scan_type; + int ret; scan_type = iio_get_current_scan_type(indio_dev, chan); @@ -928,11 +1398,15 @@ static int ad7380_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - return ad7380_read_direct(st, chan->scan_index, - scan_type, val); - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_read_direct(st, chan->scan_index, + scan_type, val); + + iio_device_release_direct(indio_dev); + + return ret; case IIO_CHAN_INFO_SCALE: /* * According to the datasheet, the LSB size is: @@ -961,8 +1435,19 @@ static int ad7380_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - *val = st->oversampling_ratio; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7380_get_osr(st, val); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->offload_trigger_hz; return IIO_VAL_INT; default: return -EINVAL; @@ -974,6 +1459,8 @@ static int ad7380_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + struct ad7380_state *st = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *vals = ad7380_oversampling_ratios; @@ -981,6 +1468,10 @@ static int ad7380_read_avail(struct iio_dev *indio_dev, *type = IIO_VAL_INT; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; default: return -EINVAL; } @@ -1008,47 +1499,61 @@ static int ad7380_osr_to_regval(int ratio) return -EINVAL; } +static int ad7380_set_oversampling_ratio(struct ad7380_state *st, int val) +{ + int ret, osr, boost; + + osr = ad7380_osr_to_regval(val); + if (osr < 0) + return osr; + + /* always enable resolution boost when oversampling is enabled */ + boost = osr > 0 ? 1 : 0; + + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_OSR | AD7380_CONFIG1_RES, + FIELD_PREP(AD7380_CONFIG1_OSR, osr) | + FIELD_PREP(AD7380_CONFIG1_RES, boost)); + + if (ret) + return ret; + + st->resolution_boost_enabled = boost; + + /* + * Perform a soft reset. This will flush the oversampling + * block and FIFO but will maintain the content of the + * configurable registers. + */ + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG2, + AD7380_CONFIG2_RESET, + FIELD_PREP(AD7380_CONFIG2_RESET, + AD7380_CONFIG2_RESET_SOFT)); + return ret; +} static int ad7380_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct ad7380_state *st = iio_priv(indio_dev); - int ret, osr, boost; + int ret; switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1) + return -EINVAL; + return ad7380_set_sample_freq(st, val); case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - osr = ad7380_osr_to_regval(val); - if (osr < 0) - return osr; - - /* always enable resolution boost when oversampling is enabled */ - boost = osr > 0 ? 1 : 0; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = regmap_update_bits(st->regmap, - AD7380_REG_ADDR_CONFIG1, - AD7380_CONFIG1_OSR | AD7380_CONFIG1_RES, - FIELD_PREP(AD7380_CONFIG1_OSR, osr) | - FIELD_PREP(AD7380_CONFIG1_RES, boost)); + ret = ad7380_set_oversampling_ratio(st, val); - if (ret) - return ret; + iio_device_release_direct(indio_dev); - st->oversampling_ratio = val; - st->resolution_boost_enabled = boost; - - /* - * Perform a soft reset. This will flush the oversampling - * block and FIFO but will maintain the content of the - * configurable registers. - */ - return regmap_update_bits(st->regmap, - AD7380_REG_ADDR_CONFIG2, - AD7380_CONFIG2_RESET, - FIELD_PREP(AD7380_CONFIG2_RESET, - AD7380_CONFIG2_RESET_SOFT)); - } - unreachable(); + return ret; default: return -EINVAL; } @@ -1063,12 +1568,179 @@ static int ad7380_get_current_scan_type(const struct iio_dev *indio_dev, : AD7380_SCAN_TYPE_NORMAL; } +static int ad7380_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int tmp, ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7380_REG_ADDR_CONFIG1, &tmp); + + iio_device_release_direct(indio_dev); + + if (ret) + return ret; + + return FIELD_GET(AD7380_CONFIG1_ALERTEN, tmp); +} + +static int ad7380_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_update_bits(st->regmap, + AD7380_REG_ADDR_CONFIG1, + AD7380_CONFIG1_ALERTEN, + FIELD_PREP(AD7380_CONFIG1_ALERTEN, state)); + + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7380_get_alert_th(struct ad7380_state *st, + enum iio_event_direction dir, + int *val) +{ + int ret, tmp; + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(st->regmap, + AD7380_REG_ADDR_ALERT_HIGH_TH, + &tmp); + if (ret) + return ret; + + *val = FIELD_GET(AD7380_ALERT_HIGH_TH, tmp); + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = regmap_read(st->regmap, + AD7380_REG_ADDR_ALERT_LOW_TH, + &tmp); + if (ret) + return ret; + + *val = FIELD_GET(AD7380_ALERT_LOW_TH, tmp); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7380_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad7380_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_get_alert_th(st, dir, val); + + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int ad7380_set_alert_th(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + int val) +{ + struct ad7380_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + u16 th; + + /* + * According to the datasheet, + * AD7380_REG_ADDR_ALERT_HIGH_TH[11:0] are the 12 MSB of the + * 16-bits internal alert high register. LSB are set to 0xf. + * AD7380_REG_ADDR_ALERT_LOW_TH[11:0] are the 12 MSB of the + * 16 bits internal alert low register. LSB are set to 0x0. + * + * When alert is enabled the conversion from the adc is compared + * immediately to the alert high/low thresholds, before any + * oversampling. This means that the thresholds are the same for + * normal mode and oversampling mode. + */ + + /* Extract the 12 MSB of val */ + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + th = val >> (scan_type->realbits - 12); + + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(st->regmap, + AD7380_REG_ADDR_ALERT_HIGH_TH, + th); + case IIO_EV_DIR_FALLING: + return regmap_write(st->regmap, + AD7380_REG_ADDR_ALERT_LOW_TH, + th); + default: + return -EINVAL; + } +} + +static int ad7380_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_set_alert_th(indio_dev, chan, dir, val); + + iio_device_release_direct(indio_dev); + return ret; + default: + return -EINVAL; + } +} + static const struct iio_info ad7380_info = { .read_raw = &ad7380_read_raw, .read_avail = &ad7380_read_avail, .write_raw = &ad7380_write_raw, .get_current_scan_type = &ad7380_get_current_scan_type, .debugfs_reg_access = &ad7380_debugfs_reg_access, + .read_event_config = &ad7380_read_event_config, + .write_event_config = &ad7380_write_event_config, + .read_event_value = &ad7380_read_event_value, + .write_event_value = &ad7380_write_event_value, }; static int ad7380_init(struct ad7380_state *st, bool external_ref_en) @@ -1092,7 +1764,6 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en) } /* This is the default value after reset. */ - st->oversampling_ratio = 1; st->ch = 0; st->seq = false; @@ -1103,6 +1774,53 @@ static int ad7380_init(struct ad7380_state *st, bool external_ref_en) AD7380_NUM_SDO_LINES)); } +static int ad7380_probe_spi_offload(struct iio_dev *indio_dev, + struct ad7380_state *st) +{ + struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; + struct dma_chan *rx_dma; + int sample_rate, ret; + + indio_dev->setup_ops = &ad7380_offload_buffer_setup_ops; + indio_dev->channels = st->chip_info->offload_channels; + /* Just removing the timestamp channel. */ + indio_dev->num_channels--; + + st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload, + SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + sample_rate = st->chip_info->max_conversion_rate_hz * + AD7380_NUM_SDO_LINES / st->chip_info->num_simult_channels; + + st->sample_freq_range[0] = 1; /* min */ + st->sample_freq_range[1] = 1; /* step */ + st->sample_freq_range[2] = sample_rate; /* max */ + + /* + * Starting with a quite low frequency, to allow oversampling x32, + * user is then reponsible to adjust the frequency for the specific case. + */ + ret = ad7380_set_sample_freq(st, sample_rate / 32); + if (ret) + return ret; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "cannot setup dma buffer\n"); + + return 0; +} + static int ad7380_probe(struct spi_device *spi) { struct device *dev = &spi->dev; @@ -1274,12 +1992,24 @@ static int ad7380_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->available_scan_masks = st->chip_info->available_scan_masks; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - ad7380_trigger_handler, - &ad7380_buffer_setup_ops); - if (ret) - return ret; + st->offload = devm_spi_offload_get(dev, spi, &ad7380_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get offload\n"); + + /* If no SPI offload, fall back to low speed usage. */ + if (ret == -ENODEV) { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad7380_trigger_handler, + &ad7380_buffer_setup_ops); + if (ret) + return ret; + } else { + ret = ad7380_probe_spi_offload(indio_dev, st); + if (ret) + return ret; + } ret = ad7380_init(st, external_ref_en); if (ret) @@ -1305,6 +2035,7 @@ static const struct of_device_id ad7380_of_match_table[] = { { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info }, { .compatible = "adi,adaq4370-4", .data = &adaq4370_4_chip_info }, { .compatible = "adi,adaq4380-4", .data = &adaq4380_4_chip_info }, + { .compatible = "adi,adaq4381-4", .data = &adaq4381_4_chip_info }, { } }; @@ -1325,6 +2056,7 @@ static const struct spi_device_id ad7380_id_table[] = { { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info }, { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info }, { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info }, + { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad7380_id_table); @@ -1342,3 +2074,4 @@ module_spi_driver(ad7380_driver); MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD738x ADC driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index aeb8e383fe71..37b0515cf4fc 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -138,11 +138,10 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ad7476_scan_direct(st); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index d39354afd539..1a314fddd7eb 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -5,6 +5,7 @@ * Copyright 2011 Analog Devices Inc. */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> @@ -85,6 +86,10 @@ static const unsigned int ad7606_oversampling_avail[7] = { 1, 2, 4, 8, 16, 32, 64, }; +static const unsigned int ad7606b_oversampling_avail[9] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, +}; + static const unsigned int ad7616_oversampling_avail[8] = { 1, 2, 4, 8, 16, 32, 64, 128, }; @@ -187,6 +192,8 @@ static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); +static int ad7616_sw_mode_setup(struct iio_dev *indio_dev); +static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev); const struct ad7606_chip_info ad7605_4_info = { .channels = ad7605_channels, @@ -239,6 +246,7 @@ const struct ad7606_chip_info ad7606b_info = { .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .sw_setup_cb = ad7606b_sw_mode_setup, }; EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606"); @@ -250,6 +258,7 @@ const struct ad7606_chip_info ad7606c_16_info = { .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7606c_16bit_chan_scale_setup, + .sw_setup_cb = ad7606b_sw_mode_setup, }; EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606"); @@ -294,6 +303,7 @@ const struct ad7606_chip_info ad7606c_18_info = { .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7606c_18bit_chan_scale_setup, + .sw_setup_cb = ad7606b_sw_mode_setup, }; EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606"); @@ -307,6 +317,7 @@ const struct ad7606_chip_info ad7616_info = { .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail), .os_req_reset = true, .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .sw_setup_cb = ad7616_sw_mode_setup, }; EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606"); @@ -752,13 +763,13 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = ad7606_scan_direct(indio_dev, chan->address, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_scan_direct(indio_dev, chan->address, val); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: if (st->sw_mode_en) ch = chan->address; @@ -818,8 +829,7 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val) values[0] = val & GENMASK(2, 0); - gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc, - st->gpio_os->info, values); + gpiod_multi_set_value_cansleep(st->gpio_os, values); /* AD7616 requires a reset to update value */ if (st->chip_info->os_req_reset) @@ -852,7 +862,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, } val = (val * MICRO) + val2; i = find_closest(val, scale_avail_uv, cs->num_scales); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = st->write_scale(indio_dev, ch, i + cs->reg_offset); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; cs->range = i; @@ -863,7 +877,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, return -EINVAL; i = find_closest(val, st->oversampling_avail, st->num_os_ratios); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = st->write_os(indio_dev, i); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; st->oversampling = st->oversampling_avail[i]; @@ -1138,16 +1156,117 @@ static const struct iio_trigger_ops ad7606_trigger_ops = { .validate_device = iio_trigger_validate_own_device, }; -static int ad7606_sw_mode_setup(struct iio_dev *indio_dev) +static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr, + unsigned long mask, unsigned int val) +{ + int readval; + + readval = st->bops->reg_read(st, addr); + if (readval < 0) + return readval; + + readval &= ~mask; + readval |= val; + + return st->bops->reg_write(st, addr, readval); +} + +static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) { struct ad7606_state *st = iio_priv(indio_dev); + unsigned int ch_addr, mode, ch_index; - st->sw_mode_en = st->bops->sw_mode_config && - device_property_present(st->dev, "adi,sw-mode"); - if (!st->sw_mode_en) - return 0; + /* + * Ad7616 has 16 channels divided in group A and group B. + * The range of channels from A are stored in registers with address 4 + * while channels from B are stored in register with address 6. + * The last bit from channels determines if it is from group A or B + * because the order of channels in iio is 0A, 0B, 1A, 1B... + */ + ch_index = ch >> 1; + + ch_addr = AD7616_RANGE_CH_ADDR(ch_index); + + if ((ch & 0x1) == 0) /* channel A */ + ch_addr += AD7616_RANGE_CH_A_ADDR_OFF; + else /* channel B */ + ch_addr += AD7616_RANGE_CH_B_ADDR_OFF; + + /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */ + mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11)); + + return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index), + mode); +} + +static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER, + AD7616_OS_MASK, val << 2); +} + +static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch), + AD7606_RANGE_CH_MSK(ch), + AD7606_RANGE_CH_MODE(ch, val)); +} + +static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + return st->bops->reg_write(st, AD7606_OS_MODE, val); +} + +static int ad7616_sw_mode_setup(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + int ret; + + /* + * Scale can be configured individually for each channel + * in software mode. + */ + + st->write_scale = ad7616_write_scale_sw; + st->write_os = &ad7616_write_os_sw; + + ret = st->bops->sw_mode_config(indio_dev); + if (ret) + return ret; - indio_dev->info = &ad7606_info_sw_mode; + /* Activate Burst mode and SEQEN MODE */ + return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER, + AD7616_BURST_MODE | AD7616_SEQEN_MODE, + AD7616_BURST_MODE | AD7616_SEQEN_MODE); +} + +static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev) +{ + struct ad7606_state *st = iio_priv(indio_dev); + DECLARE_BITMAP(os, 3); + + bitmap_fill(os, 3); + /* + * Software mode is enabled when all three oversampling + * pins are set to high. If oversampling gpios are defined + * in the device tree, then they need to be set to high, + * otherwise, they must be hardwired to VDD + */ + if (st->gpio_os) + gpiod_multi_set_value_cansleep(st->gpio_os, os); + + /* OS of 128 and 256 are available only in software mode */ + st->oversampling_avail = ad7606b_oversampling_avail; + st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail); + + st->write_scale = ad7606_write_scale_sw; + st->write_os = &ad7606_write_os_sw; return st->bops->sw_mode_config(indio_dev); } @@ -1246,17 +1365,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return -ERESTARTSYS; } - st->write_scale = ad7606_write_scale_hw; - st->write_os = ad7606_write_os_hw; - - ret = ad7606_sw_mode_setup(indio_dev); - if (ret) - return ret; - - ret = ad7606_chan_scales_setup(indio_dev); - if (ret) - return ret; - /* If convst pin is not defined, setup PWM. */ if (!st->gpio_convst) { st->cnvst_pwm = devm_pwm_get(dev, NULL); @@ -1334,6 +1442,20 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return ret; } + st->write_scale = ad7606_write_scale_hw; + st->write_os = ad7606_write_os_hw; + + st->sw_mode_en = st->chip_info->sw_setup_cb && + device_property_present(st->dev, "adi,sw-mode"); + if (st->sw_mode_en) { + indio_dev->info = &ad7606_info_sw_mode; + st->chip_info->sw_setup_cb(indio_dev); + } + + ret = ad7606_chan_scales_setup(indio_dev); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606"); diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 8778ffe515b3..71a30525eaab 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -10,37 +10,49 @@ #define AD760X_MAX_CHANNELS 16 -#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, bits) { \ +#define AD7616_CONFIGURATION_REGISTER 0x02 +#define AD7616_OS_MASK GENMASK(4, 2) +#define AD7616_BURST_MODE BIT(6) +#define AD7616_SEQEN_MODE BIT(5) +#define AD7616_RANGE_CH_A_ADDR_OFF 0x04 +#define AD7616_RANGE_CH_B_ADDR_OFF 0x06 +/* + * Range of channels from a group are stored in 2 registers. + * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register. + * For channels from second group(8-15) the order is the same, only with + * an offset of 2 for register address. + */ +#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2) +/* The range of the channel is stored in 2 bits */ +#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2)) +#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2)) + +#define AD7606_CONFIGURATION_REGISTER 0x02 +#define AD7606_SINGLE_DOUT 0x00 + +/* + * Range for AD7606B channels are stored in registers starting with address 0x3. + * Each register stores range for 2 channels(4 bits per channel). + */ +#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1))) +#define AD7606_RANGE_CH_MODE(ch, mode) \ + ((GENMASK(3, 0) & (mode)) << (4 * ((ch) & 0x1))) +#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1)) +#define AD7606_OS_MODE 0x08 + +#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, \ + mask_sep_avail, mask_all_avail, bits) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = num, \ .address = num, \ .info_mask_separate = mask_sep, \ + .info_mask_separate_available = \ + mask_sep_avail, \ .info_mask_shared_by_type = mask_type, \ .info_mask_shared_by_all = mask_all, \ - .scan_index = num, \ - .scan_type = { \ - .sign = 's', \ - .realbits = (bits), \ - .storagebits = (bits) > 16 ? 32 : 16, \ - .endianness = IIO_CPU, \ - }, \ -} - -#define AD7606_SW_CHANNEL(num, bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = num, \ - .address = num, \ - .info_mask_separate = \ - BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_separate_available = \ - BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_all = \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_all_available = \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + mask_all_avail, \ .scan_index = num, \ .scan_type = { \ .sign = 's', \ @@ -50,14 +62,30 @@ }, \ } +#define AD7606_SW_CHANNEL(num, bits) \ + AD760X_CHANNEL(num, \ + /* mask separate */ \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + /* mask type */ \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + /* mask all */ \ + 0, \ + /* mask separate available */ \ + BIT(IIO_CHAN_INFO_SCALE), \ + /* mask all available */ \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + bits) + #define AD7605_CHANNEL(num) \ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \ - BIT(IIO_CHAN_INFO_SCALE), 0, 16) + BIT(IIO_CHAN_INFO_SCALE), 0, 0, 0, 16) #define AD7606_CHANNEL(num, bits) \ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \ BIT(IIO_CHAN_INFO_SCALE), \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), bits) + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + 0, 0, bits) #define AD7616_CHANNEL(num) AD7606_SW_CHANNEL(num, 16) @@ -65,12 +93,29 @@ AD760X_CHANNEL(num, 0, \ BIT(IIO_CHAN_INFO_SCALE), \ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 16) + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + 0, 0, 16) + +#define AD7606_BI_SW_CHANNEL(num) \ + AD760X_CHANNEL(num, \ + /* mask separate */ \ + BIT(IIO_CHAN_INFO_SCALE), \ + /* mask type */ \ + 0, \ + /* mask all */ \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + /* mask separate available */ \ + BIT(IIO_CHAN_INFO_SCALE), \ + /* mask all available */ \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + 16) struct ad7606_state; typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev, struct iio_chan_spec *chan, int ch); +typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev); /** * struct ad7606_chip_info - chip specific information @@ -80,6 +125,7 @@ typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev, * @num_channels: number of channels * @num_adc_channels the number of channels the ADC actually inputs. * @scale_setup_cb: callback to setup the scales for each channel + * @sw_setup_cb: callback to setup the software mode if available. * @oversampling_avail pointer to the array which stores the available * oversampling ratios. * @oversampling_num number of elements stored in oversampling_avail array @@ -94,6 +140,7 @@ struct ad7606_chip_info { unsigned int num_adc_channels; unsigned int num_channels; ad7606_scale_setup_cb_t scale_setup_cb; + ad7606_sw_setup_cb_t sw_setup_cb; const unsigned int *oversampling_avail; unsigned int oversampling_num; bool os_req_reset; @@ -206,10 +253,6 @@ struct ad7606_bus_ops { int (*reg_write)(struct ad7606_state *st, unsigned int addr, unsigned int val); - int (*write_mask)(struct ad7606_state *st, - unsigned int addr, - unsigned long mask, - unsigned int val); int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask); u16 (*rd_wr_cmd)(int addr, char isWriteOp); }; diff --git a/drivers/iio/adc/ad7606_bus_iface.h b/drivers/iio/adc/ad7606_bus_iface.h new file mode 100644 index 000000000000..f2c979a9b7f3 --- /dev/null +++ b/drivers/iio/adc/ad7606_bus_iface.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2010-2024 Analog Devices Inc. + * Copyright (c) 2025 Baylibre, SAS + */ +#ifndef __LINUX_PLATFORM_DATA_AD7606_H__ +#define __LINUX_PLATFORM_DATA_AD7606_H__ + +struct iio_backend; + +struct ad7606_platform_data { + int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val); + int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val); +}; + +#endif /* __LINUX_PLATFORM_DATA_AD7606_H__ */ diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c index 64733b607aa8..335fb481bfde 100644 --- a/drivers/iio/adc/ad7606_par.c +++ b/drivers/iio/adc/ad7606_par.c @@ -19,6 +19,7 @@ #include <linux/iio/iio.h> #include "ad7606.h" +#include "ad7606_bus_iface.h" static const struct iio_chan_spec ad7606b_bi_channels[] = { AD7606_BI_CHANNEL(0), @@ -31,7 +32,19 @@ static const struct iio_chan_spec ad7606b_bi_channels[] = { AD7606_BI_CHANNEL(7), }; -static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) +static const struct iio_chan_spec ad7606b_bi_sw_channels[] = { + AD7606_BI_SW_CHANNEL(0), + AD7606_BI_SW_CHANNEL(1), + AD7606_BI_SW_CHANNEL(2), + AD7606_BI_SW_CHANNEL(3), + AD7606_BI_SW_CHANNEL(4), + AD7606_BI_SW_CHANNEL(5), + AD7606_BI_SW_CHANNEL(6), + AD7606_BI_SW_CHANNEL(7), +}; + +static int ad7606_par_bus_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) { struct ad7606_state *st = iio_priv(indio_dev); unsigned int c, ret; @@ -48,7 +61,8 @@ static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned return 0; } -static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio_dev) +static int ad7606_par_bus_setup_iio_backend(struct device *dev, + struct iio_dev *indio_dev) { struct ad7606_state *st = iio_priv(indio_dev); unsigned int ret, c; @@ -86,9 +100,39 @@ static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio return 0; } +static int ad7606_par_bus_reg_read(struct ad7606_state *st, unsigned int addr) +{ + struct ad7606_platform_data *pdata = st->dev->platform_data; + int val, ret; + + ret = pdata->bus_reg_read(st->back, addr, &val); + if (ret) + return ret; + + return val; +} + +static int ad7606_par_bus_reg_write(struct ad7606_state *st, unsigned int addr, + unsigned int val) +{ + struct ad7606_platform_data *pdata = st->dev->platform_data; + + return pdata->bus_reg_write(st->back, addr, val); +} + +static int ad7606_par_bus_sw_mode_config(struct iio_dev *indio_dev) +{ + indio_dev->channels = ad7606b_bi_sw_channels; + + return 0; +} + static const struct ad7606_bus_ops ad7606_bi_bops = { - .iio_backend_config = ad7606_bi_setup_iio_backend, - .update_scan_mode = ad7606_bi_update_scan_mode, + .iio_backend_config = ad7606_par_bus_setup_iio_backend, + .update_scan_mode = ad7606_par_bus_update_scan_mode, + .reg_read = ad7606_par_bus_reg_read, + .reg_write = ad7606_par_bus_reg_write, + .sw_mode_config = ad7606_par_bus_sw_mode_config, }; static int ad7606_par16_read_block(struct device *dev, diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index e2c147525706..885bf0b68e77 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -15,36 +15,6 @@ #define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */ -#define AD7616_CONFIGURATION_REGISTER 0x02 -#define AD7616_OS_MASK GENMASK(4, 2) -#define AD7616_BURST_MODE BIT(6) -#define AD7616_SEQEN_MODE BIT(5) -#define AD7616_RANGE_CH_A_ADDR_OFF 0x04 -#define AD7616_RANGE_CH_B_ADDR_OFF 0x06 -/* - * Range of channels from a group are stored in 2 registers. - * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register. - * For channels from second group(8-15) the order is the same, only with - * an offset of 2 for register address. - */ -#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2) -/* The range of the channel is stored in 2 bits */ -#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2)) -#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2)) - -#define AD7606_CONFIGURATION_REGISTER 0x02 -#define AD7606_SINGLE_DOUT 0x00 - -/* - * Range for AD7606B channels are stored in registers starting with address 0x3. - * Each register stores range for 2 channels(4 bits per channel). - */ -#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1))) -#define AD7606_RANGE_CH_MODE(ch, mode) \ - ((GENMASK(3, 0) & mode) << (4 * ((ch) & 0x1))) -#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1)) -#define AD7606_OS_MODE 0x08 - static const struct iio_chan_spec ad7616_sw_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(16), AD7616_CHANNEL(0), @@ -89,10 +59,6 @@ static const struct iio_chan_spec ad7606c_18_sw_channels[] = { AD7606_SW_CHANNEL(7, 18), }; -static const unsigned int ad7606B_oversampling_avail[9] = { - 1, 2, 4, 8, 16, 32, 64, 128, 256 -}; - static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp) { /* @@ -194,118 +160,20 @@ static int ad7606_spi_reg_write(struct ad7606_state *st, return spi_write(spi, &st->d16[0], sizeof(st->d16[0])); } -static int ad7606_spi_write_mask(struct ad7606_state *st, - unsigned int addr, - unsigned long mask, - unsigned int val) -{ - int readval; - - readval = st->bops->reg_read(st, addr); - if (readval < 0) - return readval; - - readval &= ~mask; - readval |= val; - - return st->bops->reg_write(st, addr, readval); -} - -static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) -{ - struct ad7606_state *st = iio_priv(indio_dev); - unsigned int ch_addr, mode, ch_index; - - - /* - * Ad7616 has 16 channels divided in group A and group B. - * The range of channels from A are stored in registers with address 4 - * while channels from B are stored in register with address 6. - * The last bit from channels determines if it is from group A or B - * because the order of channels in iio is 0A, 0B, 1A, 1B... - */ - ch_index = ch >> 1; - - ch_addr = AD7616_RANGE_CH_ADDR(ch_index); - - if ((ch & 0x1) == 0) /* channel A */ - ch_addr += AD7616_RANGE_CH_A_ADDR_OFF; - else /* channel B */ - ch_addr += AD7616_RANGE_CH_B_ADDR_OFF; - - /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */ - mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11)); - return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index), - mode); -} - -static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val) -{ - struct ad7606_state *st = iio_priv(indio_dev); - - return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER, - AD7616_OS_MASK, val << 2); -} - -static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) -{ - struct ad7606_state *st = iio_priv(indio_dev); - - return ad7606_spi_write_mask(st, - AD7606_RANGE_CH_ADDR(ch), - AD7606_RANGE_CH_MSK(ch), - AD7606_RANGE_CH_MODE(ch, val)); -} - -static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val) -{ - struct ad7606_state *st = iio_priv(indio_dev); - - return ad7606_spi_reg_write(st, AD7606_OS_MODE, val); -} - static int ad7616_sw_mode_config(struct iio_dev *indio_dev) { - struct ad7606_state *st = iio_priv(indio_dev); - /* * Scale can be configured individually for each channel * in software mode. */ indio_dev->channels = ad7616_sw_channels; - st->write_scale = ad7616_write_scale_sw; - st->write_os = &ad7616_write_os_sw; - - /* Activate Burst mode and SEQEN MODE */ - return st->bops->write_mask(st, - AD7616_CONFIGURATION_REGISTER, - AD7616_BURST_MODE | AD7616_SEQEN_MODE, - AD7616_BURST_MODE | AD7616_SEQEN_MODE); + return 0; } static int ad7606B_sw_mode_config(struct iio_dev *indio_dev) { struct ad7606_state *st = iio_priv(indio_dev); - DECLARE_BITMAP(os, 3); - - bitmap_fill(os, 3); - /* - * Software mode is enabled when all three oversampling - * pins are set to high. If oversampling gpios are defined - * in the device tree, then they need to be set to high, - * otherwise, they must be hardwired to VDD - */ - if (st->gpio_os) { - gpiod_set_array_value(st->gpio_os->ndescs, - st->gpio_os->desc, st->gpio_os->info, os); - } - /* OS of 128 and 256 are available only in software mode */ - st->oversampling_avail = ad7606B_oversampling_avail; - st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail); - - st->write_scale = ad7606_write_scale_sw; - st->write_os = &ad7606_write_os_sw; /* Configure device spi to output on a single channel */ st->bops->reg_write(st, @@ -350,7 +218,6 @@ static const struct ad7606_bus_ops ad7616_spi_bops = { .read_block = ad7606_spi_read_block, .reg_read = ad7606_spi_reg_read, .reg_write = ad7606_spi_reg_write, - .write_mask = ad7606_spi_write_mask, .rd_wr_cmd = ad7616_spi_rd_wr_cmd, .sw_mode_config = ad7616_sw_mode_config, }; @@ -359,7 +226,6 @@ static const struct ad7606_bus_ops ad7606b_spi_bops = { .read_block = ad7606_spi_read_block, .reg_read = ad7606_spi_reg_read, .reg_write = ad7606_spi_reg_write, - .write_mask = ad7606_spi_write_mask, .rd_wr_cmd = ad7606B_spi_rd_wr_cmd, .sw_mode_config = ad7606B_sw_mode_config, }; @@ -368,7 +234,6 @@ static const struct ad7606_bus_ops ad7606c_18_spi_bops = { .read_block = ad7606_spi_read_block18to32, .reg_read = ad7606_spi_reg_read, .reg_write = ad7606_spi_reg_write, - .write_mask = ad7606_spi_write_mask, .rd_wr_cmd = ad7606B_spi_rd_wr_cmd, .sw_mode_config = ad7606c_18_sw_mode_config, }; diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c index afa9bf4ddf3c..0466c0c7eae4 100644 --- a/drivers/iio/adc/ad7625.c +++ b/drivers/iio/adc/ad7625.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// SPDX-License-Identifier: (GPL-2.0-only) /* * Analog Devices Inc. AD7625 ADC driver * @@ -248,12 +248,15 @@ static int ad7625_write_raw(struct iio_dev *indio_dev, int val, int val2, long info) { struct ad7625_state *st = iio_priv(indio_dev); + int ret; switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return ad7625_set_sampling_freq(st, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7625_set_sampling_freq(st, val); + iio_device_release_direct(indio_dev); + return ret; default: return -EINVAL; } @@ -680,5 +683,5 @@ module_platform_driver(ad7625_driver); MODULE_AUTHOR("Trevor Gamblin <tgamblin@baylibre.com>"); MODULE_DESCRIPTION("Analog Devices AD7625 ADC"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 113703fb7245..5a863005aca6 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -142,7 +142,7 @@ static const struct iio_chan_spec ad7768_channels[] = { .channel = 0, .scan_index = 0, .scan_type = { - .sign = 'u', + .sign = 's', .realbits = 24, .storagebits = 32, .shift = 8, @@ -154,7 +154,6 @@ static const struct iio_chan_spec ad7768_channels[] = { struct ad7768_state { struct spi_device *spi; struct regulator *vref; - struct mutex lock; struct clk *mclk; unsigned int mclk_freq; unsigned int samp_freq; @@ -256,18 +255,20 @@ static int ad7768_reg_access(struct iio_dev *indio_dev, struct ad7768_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->lock); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + if (readval) { ret = ad7768_spi_reg_read(st, reg, 1); if (ret < 0) - goto err_unlock; + goto err_release; *readval = ret; ret = 0; } else { ret = ad7768_spi_reg_write(st, reg, writeval); } -err_unlock: - mutex_unlock(&st->lock); +err_release: + iio_device_release_direct(indio_dev); return ret; } @@ -365,17 +366,15 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ad7768_scan_direct(indio_dev); - if (ret >= 0) - *val = ret; - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; + *val = sign_extend32(ret, chan->scan_type.realbits - 1); return IIO_VAL_INT; @@ -471,18 +470,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p) struct ad7768_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&st->lock); - ret = spi_read(st->spi, &st->data.scan.chan, 3); if (ret < 0) - goto err_unlock; + goto out; iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan, iio_get_time_ns(indio_dev)); -err_unlock: +out: iio_trigger_notify_done(indio_dev->trig); - mutex_unlock(&st->lock); return IRQ_HANDLED; } @@ -574,6 +570,21 @@ static int ad7768_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); + /* + * Datasheet recommends SDI line to be kept high when data is not being + * clocked out of the controller and the spi clock is free running, + * to prevent accidental reset. + * Since many controllers do not support the SPI_MOSI_IDLE_HIGH flag + * yet, only request the MOSI idle state to enable if the controller + * supports it. + */ + if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) { + spi->mode |= SPI_MOSI_IDLE_HIGH; + ret = spi_setup(spi); + if (ret < 0) + return ret; + } + st->spi = spi; st->vref = devm_regulator_get(&spi->dev, "vref"); @@ -596,8 +607,6 @@ static int ad7768_probe(struct spi_device *spi) st->mclk_freq = clk_get_rate(st->mclk); - mutex_init(&st->lock); - indio_dev->channels = ad7768_channels; indio_dev->num_channels = ARRAY_SIZE(ad7768_channels); indio_dev->name = spi_get_device_id(spi)->name; diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 2537dab69a35..a5d87faa5e12 100644 --- a/drivers/iio/adc/ad7779.c +++ b/drivers/iio/adc/ad7779.c @@ -467,59 +467,82 @@ static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val) calibbias[2]); } +static int __ad7779_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + ret = ad7779_get_calibscale(st, chan->channel); + if (ret < 0) + return ret; + *val = ret; + *val2 = GAIN_REL; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_CALIBBIAS: + ret = ad7779_get_calibbias(st, chan->channel); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sampling_freq; + if (*val < 0) + return -EINVAL; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + static int ad7779_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct ad7779_state *st = iio_priv(indio_dev); int ret; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - ret = ad7779_get_calibscale(st, chan->channel); - if (ret < 0) - return ret; - *val = ret; - *val2 = GAIN_REL; - return IIO_VAL_FRACTIONAL; - case IIO_CHAN_INFO_CALIBBIAS: - ret = ad7779_get_calibbias(st, chan->channel); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SAMP_FREQ: - *val = st->sampling_freq; - if (*val < 0) - return -EINVAL; - return IIO_VAL_INT; - default: - return -EINVAL; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7779_read_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + return ret; +} + +static int __ad7779_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, + long mask) +{ + struct ad7779_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + return ad7779_set_calibscale(st, chan->channel, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad7779_set_calibbias(st, chan->channel, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7779_set_sampling_frequency(st, val); + default: + return -EINVAL; } - unreachable(); } static int ad7779_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct ad7779_state *st = iio_priv(indio_dev); + int ret; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - return ad7779_set_calibscale(st, chan->channel, val2); - case IIO_CHAN_INFO_CALIBBIAS: - return ad7779_set_calibbias(st, chan->channel, val); - case IIO_CHAN_INFO_SAMP_FREQ: - return ad7779_set_sampling_frequency(st, val); - default: - return -EINVAL; - } - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7779_write_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + return ret; } static int ad7779_buffer_preenable(struct iio_dev *indio_dev) diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index 76118fe22db8..597c2686ffa4 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -310,15 +310,11 @@ static int ad7791_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int ad7791_write_raw(struct iio_dev *indio_dev, +static int __ad7791_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct ad7791_state *st = iio_priv(indio_dev); - int ret, i; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + int i; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: @@ -328,22 +324,31 @@ static int ad7791_write_raw(struct iio_dev *indio_dev, break; } - if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) { - ret = -EINVAL; - break; - } + if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) + return -EINVAL; st->filter &= ~AD7791_FILTER_RATE_MASK; st->filter |= i; ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter), st->filter); - break; + return 0; default: - ret = -EINVAL; + return -EINVAL; } +} + +static int ad7791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7791_write_raw(indio_dev, chan, val, val2, mask); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 1b50d9643a63..ccf18ce48e34 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -462,64 +462,68 @@ static int ad7793_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int ad7793_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) +static int __ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct ad7793_state *st = iio_priv(indio_dev); - int ret, i; + int i; unsigned int tmp; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - switch (mask) { case IIO_CHAN_INFO_SCALE: - ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) - if (val2 == st->scale_avail[i][1]) { - ret = 0; - tmp = st->conf; - st->conf &= ~AD7793_CONF_GAIN(-1); - st->conf |= AD7793_CONF_GAIN(i); - - if (tmp == st->conf) - break; - - ad_sd_write_reg(&st->sd, AD7793_REG_CONF, - sizeof(st->conf), st->conf); - ad7793_calibrate_all(st); - break; - } - break; - case IIO_CHAN_INFO_SAMP_FREQ: - if (!val) { - ret = -EINVAL; - break; + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + if (val2 != st->scale_avail[i][1]) + continue; + + tmp = st->conf; + st->conf &= ~AD7793_CONF_GAIN(-1); + st->conf |= AD7793_CONF_GAIN(i); + + if (tmp == st->conf) + return 0; + + ad_sd_write_reg(&st->sd, AD7793_REG_CONF, + sizeof(st->conf), st->conf); + ad7793_calibrate_all(st); + + return 0; } + return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) + return -EINVAL; for (i = 0; i < 16; i++) if (val == st->chip_info->sample_freq_avail[i]) break; - if (i == 16) { - ret = -EINVAL; - break; - } + if (i == 16) + return -EINVAL; st->mode &= ~AD7793_MODE_RATE(-1); st->mode |= AD7793_MODE_RATE(i); ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), st->mode); - break; + return 0; default: - ret = -EINVAL; + return -EINVAL; } +} + +static int ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7793_write_raw(indio_dev, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); - iio_device_release_direct_mode(indio_dev); return ret; } diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 69add1dc4b53..87ff95643794 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -152,11 +152,10 @@ static int ad7887_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ad7887_scan_direct(st, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index acc44cb34f82..87945efb940b 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -260,11 +260,10 @@ static int ad7923_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ad7923_scan_direct(st, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c index 0ec9cda10f5f..2f949fe55873 100644 --- a/drivers/iio/adc/ad7944.c +++ b/drivers/iio/adc/ad7944.c @@ -16,11 +16,14 @@ #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> +#include <linux/spi/offload/consumer.h> #include <linux/spi/spi.h> #include <linux/string_helpers.h> +#include <linux/units.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/buffer-dmaengine.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> @@ -54,6 +57,12 @@ struct ad7944_adc { enum ad7944_spi_mode spi_mode; struct spi_transfer xfers[3]; struct spi_message msg; + struct spi_transfer offload_xfers[2]; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + unsigned long offload_trigger_hz; + int sample_freq_range[3]; void *chain_mode_buf; /* Chip-specific timing specifications. */ const struct ad7944_timing_spec *timing_spec; @@ -81,6 +90,8 @@ struct ad7944_adc { /* quite time before CNV rising edge */ #define AD7944_T_QUIET_NS 20 +/* minimum CNV high time to trigger conversion */ +#define AD7944_T_CNVH_NS 10 static const struct ad7944_timing_spec ad7944_timing_spec = { .conv_ns = 420, @@ -95,20 +106,27 @@ static const struct ad7944_timing_spec ad7986_timing_spec = { struct ad7944_chip_info { const char *name; const struct ad7944_timing_spec *timing_spec; + u32 max_sample_rate_hz; const struct iio_chan_spec channels[2]; + const struct iio_chan_spec offload_channels[1]; }; +/* get number of bytes for SPI xfer */ +#define AD7944_SPI_BYTES(scan_type) ((scan_type).realbits > 16 ? 4 : 2) + /* * AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip * @_name: The name of the chip * @_ts: The timing specification for the chip + * @_max: The maximum sample rate in Hz * @_bits: The number of bits in the conversion result * @_diff: Whether the chip is true differential or not */ -#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \ +#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _max, _bits, _diff) \ static const struct ad7944_chip_info _name##_chip_info = { \ .name = #_name, \ .timing_spec = &_ts##_timing_spec, \ + .max_sample_rate_hz = _max, \ .channels = { \ { \ .type = IIO_VOLTAGE, \ @@ -126,13 +144,43 @@ static const struct ad7944_chip_info _name##_chip_info = { \ }, \ IIO_CHAN_SOFT_TIMESTAMP(1), \ }, \ + .offload_channels = { \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _diff, \ + .channel = 0, \ + .channel2 = _diff ? 1 : 0, \ + .scan_index = 0, \ + .scan_type.sign = _diff ? 's' : 'u', \ + .scan_type.realbits = _bits, \ + .scan_type.storagebits = 32, \ + .scan_type.endianness = IIO_CPU, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + }, \ + }, \ } +/* + * Notes on the offload channels: + * - There is no soft timestamp since everything is done in hardware. + * - There is a sampling frequency attribute added. This controls the SPI + * offload trigger. + * - The storagebits value depends on the SPI offload provider. Currently there + * is only one supported provider, namely the ADI PULSAR ADC HDL project, + * which always uses 32-bit words for data values, even for <= 16-bit ADCs. + * So the value is just hardcoded to 32 for now. + */ + /* pseudo-differential with ground sense */ -AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0); -AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0); +AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 2.5 * MEGA, 14, 0); +AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 2.5 * MEGA, 16, 0); /* fully differential */ -AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1); +AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 2 * MEGA, 18, 1); static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc, const struct iio_chan_spec *chan) @@ -164,7 +212,7 @@ static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc * /* Then we can read the data during the acquisition phase */ xfers[2].rx_buf = &adc->sample.raw; - xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits); + xfers[2].len = AD7944_SPI_BYTES(chan->scan_type); xfers[2].bits_per_word = chan->scan_type.realbits; spi_message_init_with_transfers(&adc->msg, xfers, 3); @@ -193,7 +241,7 @@ static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; xfers[1].rx_buf = &adc->sample.raw; - xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); + xfers[1].len = AD7944_SPI_BYTES(chan->scan_type); xfers[1].bits_per_word = chan->scan_type.realbits; spi_message_init_with_transfers(&adc->msg, xfers, 2); @@ -228,7 +276,7 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; xfers[1].rx_buf = adc->chain_mode_buf; - xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits) * n_chain_dev; + xfers[1].len = AD7944_SPI_BYTES(chan->scan_type) * n_chain_dev; xfers[1].bits_per_word = chan->scan_type.realbits; spi_message_init_with_transfers(&adc->msg, xfers, 2); @@ -236,6 +284,48 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc return devm_spi_optimize_message(dev, adc->spi, &adc->msg); } +/* + * Unlike ad7944_3wire_cs_mode_init_msg(), this creates a message that reads + * during the conversion phase instead of the acquisition phase when reading + * a sample from the ADC. This is needed to be able to read at the maximum + * sample rate. It requires the SPI controller to have offload support and a + * high enough SCLK rate to read the sample during the conversion phase. + */ +static int ad7944_3wire_cs_mode_init_offload_msg(struct device *dev, + struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + struct spi_transfer *xfers = adc->offload_xfers; + int ret; + + /* + * CS is tied to CNV and we need a low to high transition to start the + * conversion, so place CNV low for t_QUIET to prepare for this. + */ + xfers[0].delay.value = AD7944_T_QUIET_NS; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + /* CNV has to be high for a minimum time to trigger conversion. */ + xfers[0].cs_change = 1; + xfers[0].cs_change_delay.value = AD7944_T_CNVH_NS; + xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + /* Then we can read the previous sample during the conversion phase */ + xfers[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfers[1].len = AD7944_SPI_BYTES(chan->scan_type); + xfers[1].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->offload_msg, xfers, + ARRAY_SIZE(adc->offload_xfers)); + + adc->offload_msg.offload = adc->offload; + + ret = devm_spi_optimize_message(dev, adc->spi, &adc->offload_msg); + if (ret) + return dev_err_probe(dev, ret, "failed to prepare offload msg\n"); + + return 0; +} + /** * ad7944_convert_and_acquire - Perform a single conversion and acquisition * @adc: The ADC device structure @@ -274,12 +364,12 @@ static int ad7944_single_conversion(struct ad7944_adc *adc, return ret; if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { - if (chan->scan_type.storagebits > 16) + if (chan->scan_type.realbits > 16) *val = ((u32 *)adc->chain_mode_buf)[chan->scan_index]; else *val = ((u16 *)adc->chain_mode_buf)[chan->scan_index]; } else { - if (chan->scan_type.storagebits > 16) + if (chan->scan_type.realbits > 16) *val = adc->sample.raw.u32; else *val = adc->sample.raw.u16; @@ -291,6 +381,23 @@ static int ad7944_single_conversion(struct ad7944_adc *adc, return IIO_VAL_INT; } +static int ad7944_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = adc->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + static int ad7944_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long info) @@ -300,12 +407,11 @@ static int ad7944_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ad7944_single_conversion(adc, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -323,13 +429,104 @@ static int ad7944_read_raw(struct iio_dev *indio_dev, return -EINVAL; } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adc->offload_trigger_hz; + return IIO_VAL_INT; + default: return -EINVAL; } } +static int ad7944_set_sample_freq(struct ad7944_adc *adc, int val) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = val, + }, + }; + int ret; + + ret = spi_offload_trigger_validate(adc->offload_trigger, &config); + if (ret) + return ret; + + adc->offload_trigger_hz = config.periodic.frequency_hz; + + return 0; +} + +static int ad7944_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long info) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1 || val > adc->sample_freq_range[2]) + return -EINVAL; + + return ad7944_set_sample_freq(adc, val); + default: + return -EINVAL; + } +} + +static int ad7944_write_raw_get_fmt(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + static const struct iio_info ad7944_iio_info = { + .read_avail = &ad7944_read_avail, .read_raw = &ad7944_read_raw, + .write_raw = &ad7944_write_raw, + .write_raw_get_fmt = &ad7944_write_raw_get_fmt, +}; + +static int ad7944_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = adc->offload_trigger_hz, + }, + }; + int ret; + + gpiod_set_value_cansleep(adc->turbo, 1); + + ret = spi_offload_trigger_enable(adc->offload, adc->offload_trigger, + &config); + if (ret) + gpiod_set_value_cansleep(adc->turbo, 0); + + return ret; +} + +static int ad7944_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + + spi_offload_trigger_disable(adc->offload, adc->offload_trigger); + gpiod_set_value_cansleep(adc->turbo, 0); + + return 0; +} + +static const struct iio_buffer_setup_ops ad7944_offload_buffer_setup_ops = { + .postenable = &ad7944_offload_buffer_postenable, + .predisable = &ad7944_offload_buffer_predisable, }; static irqreturn_t ad7944_trigger_handler(int irq, void *p) @@ -409,8 +606,7 @@ static int ad7944_chain_mode_alloc(struct device *dev, /* 1 word for each voltage channel + aligned u64 for timestamp */ chain_mode_buf_size = ALIGN(n_chain_dev * - BITS_TO_BYTES(chan[0].scan_type.storagebits), sizeof(u64)) - + sizeof(u64); + AD7944_SPI_BYTES(chan[0].scan_type), sizeof(u64)) + sizeof(u64); buf = devm_kzalloc(dev, chain_mode_buf_size, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -444,6 +640,11 @@ static const char * const ad7944_power_supplies[] = { "avdd", "dvdd", "bvdd", "vio" }; +static const struct spi_offload_config ad7944_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + static int ad7944_probe(struct spi_device *spi) { const struct ad7944_chip_info *chip_info; @@ -469,6 +670,10 @@ static int ad7944_probe(struct spi_device *spi) adc->timing_spec = chip_info->timing_spec; + adc->sample_freq_range[0] = 1; /* min */ + adc->sample_freq_range[1] = 1; /* step */ + adc->sample_freq_range[2] = chip_info->max_sample_rate_hz; /* max */ + ret = device_property_match_property_string(dev, "adi,spi-mode", ad7944_spi_modes, ARRAY_SIZE(ad7944_spi_modes)); @@ -588,20 +793,74 @@ static int ad7944_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad7944_iio_info; - if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { - indio_dev->available_scan_masks = chain_scan_masks; - indio_dev->channels = chain_chan; - indio_dev->num_channels = n_chain_dev + 1; + adc->offload = devm_spi_offload_get(dev, spi, &ad7944_offload_config); + ret = PTR_ERR_OR_ZERO(adc->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get offload\n"); + + /* Fall back to low speed usage when no SPI offload available. */ + if (ret == -ENODEV) { + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { + indio_dev->available_scan_masks = chain_scan_masks; + indio_dev->channels = chain_chan; + indio_dev->num_channels = n_chain_dev + 1; + } else { + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = ARRAY_SIZE(chip_info->channels); + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad7944_trigger_handler, + NULL); + if (ret) + return ret; } else { - indio_dev->channels = chip_info->channels; - indio_dev->num_channels = ARRAY_SIZE(chip_info->channels); - } + struct dma_chan *rx_dma; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - ad7944_trigger_handler, NULL); - if (ret) - return ret; + if (adc->spi_mode != AD7944_SPI_MODE_SINGLE) + return dev_err_probe(dev, -EINVAL, + "offload only supported in single mode\n"); + + indio_dev->setup_ops = &ad7944_offload_buffer_setup_ops; + indio_dev->channels = chip_info->offload_channels; + indio_dev->num_channels = ARRAY_SIZE(chip_info->offload_channels); + + adc->offload_trigger = devm_spi_offload_trigger_get(dev, + adc->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(adc->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(adc->offload_trigger), + "failed to get offload trigger\n"); + + ret = ad7944_set_sample_freq(adc, 2 * MEGA); + if (ret) + return dev_err_probe(dev, ret, + "failed to init sample rate\n"); + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + adc->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + /* + * REVISIT: ideally, we would confirm that the offload RX DMA + * buffer layout is the same as what is hard-coded in + * offload_channels. Right now, the only supported offload + * is the pulsar_adc project which always uses 32-bit word + * size for data values, regardless of the SPI bits per word. + */ + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, + indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return ret; + + ret = ad7944_3wire_cs_mode_init_offload_msg(dev, adc, + &chip_info->offload_channels[0]); + if (ret) + return ret; + } return devm_iio_device_register(dev, indio_dev); } @@ -636,3 +895,4 @@ module_spi_driver(ad7944_driver); MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>"); MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index aa44b4e2542b..993f4651b73a 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -291,13 +291,12 @@ static int ad799x_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&st->lock); ret = ad799x_scan_direct(st, chan->scan_index); mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -411,9 +410,8 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, struct ad799x_state *st = iio_priv(indio_dev); int ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&st->lock); @@ -429,7 +427,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, ret = ad799x_write_config(st, st->config); mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index f30119b42ba0..f7a9f46ea0dc 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -813,6 +813,18 @@ static int ad9467_read_raw(struct iio_dev *indio_dev, } } +static int __ad9467_update_clock(struct ad9467_state *st, long r_clk) +{ + int ret; + + ret = clk_set_rate(st->clk, r_clk); + if (ret) + return ret; + + guard(mutex)(&st->lock); + return ad9467_calibrate(st); +} + static int ad9467_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -842,14 +854,11 @@ static int ad9467_write_raw(struct iio_dev *indio_dev, if (sample_rate == r_clk) return 0; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = clk_set_rate(st->clk, r_clk); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - guard(mutex)(&st->lock); - ret = ad9467_calibrate(st); - } + ret = __ad9467_update_clock(st, r_clk); + iio_device_release_direct(indio_dev); return ret; default: return -EINVAL; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index d5d81581ab34..6c37f8e21120 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -339,6 +339,7 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, out: sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, channel); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); @@ -386,11 +387,12 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, unsigned int data_reg; int ret = 0; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - ad_sigma_delta_set_channel(sigma_delta, chan->address); + ret = ad_sigma_delta_set_channel(sigma_delta, chan->address); + if (ret) + goto out_release; spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; @@ -431,7 +433,8 @@ out_unlock: sigma_delta->keep_cs_asserted = false; sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); - iio_device_release_direct_mode(indio_dev); +out_release: + iio_device_release_direct(indio_dev); if (ret) return ret; @@ -801,10 +804,15 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, spin_lock_init(&sigma_delta->irq_lock); - if (info->irq_line) - sigma_delta->irq_line = info->irq_line; - else + if (info->has_named_irqs) { + sigma_delta->irq_line = fwnode_irq_get_byname(dev_fwnode(&spi->dev), + "rdy"); + if (sigma_delta->irq_line < 0) + return dev_err_probe(&spi->dev, sigma_delta->irq_line, + "Interrupt 'rdy' is required\n"); + } else { sigma_delta->irq_line = spi->irq; + } sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN); if (IS_ERR(sigma_delta->rdy_gpiod)) diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index c7357601f0f8..cf942c043457 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -12,9 +12,9 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/delay.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/regmap.h> @@ -27,6 +27,7 @@ #include <linux/iio/buffer.h> #include <linux/iio/iio.h> +#include "ad7606_bus_iface.h" /* * Register definitions: * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map @@ -39,9 +40,19 @@ #define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1) #define ADI_AXI_REG_RSTN_RSTN BIT(0) +#define ADI_AXI_ADC_REG_CONFIG 0x000c +#define ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N BIT(7) + #define ADI_AXI_ADC_REG_CTRL 0x0044 #define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) +#define ADI_AXI_ADC_REG_CNTRL_3 0x004c +#define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2) +#define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0) +#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0 +#define AXI_AD485X_PACKET_FORMAT_24BIT 0x1 +#define AXI_AD485X_PACKET_FORMAT_32BIT 0x2 + #define ADI_AXI_ADC_REG_DRP_STATUS 0x0074 #define ADI_AXI_ADC_DRP_LOCKED BIT(17) @@ -73,6 +84,12 @@ #define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4) #define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0) +#define ADI_AXI_REG_CONFIG_WR 0x0080 +#define ADI_AXI_REG_CONFIG_RD 0x0084 +#define ADI_AXI_REG_CONFIG_CTRL 0x008c +#define ADI_AXI_REG_CONFIG_CTRL_READ 0x03 +#define ADI_AXI_REG_CONFIG_CTRL_WRITE 0x01 + #define ADI_AXI_ADC_MAX_IO_NUM_LANES 15 #define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \ @@ -80,7 +97,20 @@ ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ ADI_AXI_REG_CHAN_CTRL_ENABLE) +#define ADI_AXI_REG_READ_BIT 0x8000 +#define ADI_AXI_REG_ADDRESS_MASK 0xff00 +#define ADI_AXI_REG_VALUE_MASK 0x00ff + +struct axi_adc_info { + unsigned int version; + const struct iio_backend_info *backend_info; + bool has_child_nodes; + const void *pdata; + unsigned int pdata_sz; +}; + struct adi_axi_adc_state { + const struct axi_adc_info *info; struct regmap *regmap; struct device *dev; /* lock to protect multiple accesses to the device registers */ @@ -290,6 +320,88 @@ static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan) ADI_AXI_REG_CHAN_CTRL_ENABLE); } +static int axi_adc_interface_type_get(struct iio_backend *back, + enum iio_backend_interface_type *type) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CONFIG, &val); + if (ret) + return ret; + + if (val & ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N) + *type = IIO_BACKEND_INTERFACE_SERIAL_CMOS; + else + *type = IIO_BACKEND_INTERFACE_SERIAL_LVDS; + + return 0; +} + +static int axi_adc_ad485x_data_size_set(struct iio_backend *back, + unsigned int size) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + unsigned int val; + + switch (size) { + /* + * There are two different variants of the AXI AXI_AD485X IP block, a + * 16-bit and a 20-bit variant. + * The 0x0 value (AXI_AD485X_PACKET_FORMAT_20BIT) is corresponding also + * to the 16-bit variant of the IP block. + */ + case 16: + case 20: + val = AXI_AD485X_PACKET_FORMAT_20BIT; + break; + case 24: + val = AXI_AD485X_PACKET_FORMAT_24BIT; + break; + /* + * The 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT) corresponds only to the + * 20-bit variant of the IP block. Setting this value properly is + * ensured by the upper layers of the drivers calling the axi-adc + * functions. + * Also, for 16-bit IP block, the 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT) + * value is handled as maximum size available which is 24-bit for this + * configuration. + */ + case 32: + val = AXI_AD485X_PACKET_FORMAT_32BIT; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK, + FIELD_PREP(AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK, val)); +} + +static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back, + unsigned int ratio) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + /* The current state of the function enables or disables the + * oversampling in REG_CNTRL_3 register. A ratio equal to 1 implies no + * oversampling, while a value greater than 1 implies oversampling being + * enabled. + */ + switch (ratio) { + case 0: + return -EINVAL; + case 1: + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD485X_CNTRL_3_OS_EN_MSK); + default: + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD485X_CNTRL_3_OS_EN_MSK); + } +} + static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, struct iio_dev *indio_dev) { @@ -302,10 +414,79 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name); } +static int axi_adc_raw_write(struct iio_backend *back, u32 val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_WR, val); + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, + ADI_AXI_REG_CONFIG_CTRL_WRITE); + fsleep(100); + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00); + fsleep(100); + + return 0; +} + +static int axi_adc_raw_read(struct iio_backend *back, u32 *val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, + ADI_AXI_REG_CONFIG_CTRL_READ); + fsleep(100); + regmap_read(st->regmap, ADI_AXI_REG_CONFIG_RD, val); + regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00); + fsleep(100); + + return 0; +} + +static int ad7606_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + int addr; + + guard(mutex)(&st->lock); + + /* + * The address is written on the highest weight byte, and the MSB set + * at 1 indicates a read operation. + */ + addr = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) | ADI_AXI_REG_READ_BIT; + axi_adc_raw_write(back, addr); + axi_adc_raw_read(back, val); + + /* Write 0x0 on the bus to get back to ADC mode */ + axi_adc_raw_write(back, 0); + + return 0; +} + +static int ad7606_bus_reg_write(struct iio_backend *back, u32 reg, u32 val) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 buf; + + guard(mutex)(&st->lock); + + /* Write any register to switch to register mode */ + axi_adc_raw_write(back, 0xaf00); + + buf = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) | + FIELD_PREP(ADI_AXI_REG_VALUE_MASK, val); + axi_adc_raw_write(back, buf); + + /* Write 0x0 on the bus to get back to ADC mode */ + axi_adc_raw_write(back, 0); + + return 0; +} + static void axi_adc_free_buffer(struct iio_backend *back, struct iio_buffer *buffer) { - iio_dmaengine_buffer_free(buffer); + iio_dmaengine_buffer_teardown(buffer); } static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg, @@ -325,6 +506,36 @@ static const struct regmap_config axi_adc_regmap_config = { .reg_stride = 4, }; +static void axi_adc_child_remove(void *data) +{ + platform_device_unregister(data); +} + +static int axi_adc_create_platform_device(struct adi_axi_adc_state *st, + struct fwnode_handle *child) +{ + struct platform_device_info pi = { + .parent = st->dev, + .name = fwnode_get_name(child), + .id = PLATFORM_DEVID_AUTO, + .fwnode = child, + .data = st->info->pdata, + .size_data = st->info->pdata_sz, + }; + struct platform_device *pdev; + int ret; + + pdev = platform_device_register_full(&pi); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + ret = devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev); + if (ret) + return ret; + + return 0; +} + static const struct iio_backend_ops adi_axi_adc_ops = { .enable = axi_adc_enable, .disable = axi_adc_disable, @@ -337,6 +548,7 @@ static const struct iio_backend_ops adi_axi_adc_ops = { .iodelay_set = axi_adc_iodelays_set, .test_pattern_set = axi_adc_test_pattern_set, .chan_status = axi_adc_chan_status, + .interface_type_get = axi_adc_interface_type_get, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), }; @@ -346,9 +558,32 @@ static const struct iio_backend_info adi_axi_adc_generic = { .ops = &adi_axi_adc_ops, }; +static const struct iio_backend_ops adi_ad485x_ops = { + .enable = axi_adc_enable, + .disable = axi_adc_disable, + .data_format_set = axi_adc_data_format_set, + .chan_enable = axi_adc_chan_enable, + .chan_disable = axi_adc_chan_disable, + .request_buffer = axi_adc_request_buffer, + .free_buffer = axi_adc_free_buffer, + .data_sample_trigger = axi_adc_data_sample_trigger, + .iodelay_set = axi_adc_iodelays_set, + .chan_status = axi_adc_chan_status, + .interface_type_get = axi_adc_interface_type_get, + .data_size_set = axi_adc_ad485x_data_size_set, + .oversampling_ratio_set = axi_adc_ad485x_oversampling_ratio_set, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), + .debugfs_print_chan_status = + iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), +}; + +static const struct iio_backend_info axi_ad485x = { + .name = "axi-ad485x", + .ops = &adi_ad485x_ops, +}; + static int adi_axi_adc_probe(struct platform_device *pdev) { - const unsigned int *expected_ver; struct adi_axi_adc_state *st; void __iomem *base; unsigned int ver; @@ -370,8 +605,8 @@ static int adi_axi_adc_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap), "failed to init register map\n"); - expected_ver = device_get_match_data(&pdev->dev); - if (!expected_ver) + st->info = device_get_match_data(&pdev->dev); + if (!st->info) return -ENODEV; clk = devm_clk_get_enabled(&pdev->dev, NULL); @@ -391,23 +626,46 @@ static int adi_axi_adc_probe(struct platform_device *pdev) if (ret) return ret; - if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) { + if (ADI_AXI_PCORE_VER_MAJOR(ver) != + ADI_AXI_PCORE_VER_MAJOR(st->info->version)) { dev_err(&pdev->dev, "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", - ADI_AXI_PCORE_VER_MAJOR(*expected_ver), - ADI_AXI_PCORE_VER_MINOR(*expected_ver), - ADI_AXI_PCORE_VER_PATCH(*expected_ver), + ADI_AXI_PCORE_VER_MAJOR(st->info->version), + ADI_AXI_PCORE_VER_MINOR(st->info->version), + ADI_AXI_PCORE_VER_PATCH(st->info->version), ADI_AXI_PCORE_VER_MAJOR(ver), ADI_AXI_PCORE_VER_MINOR(ver), ADI_AXI_PCORE_VER_PATCH(ver)); return -ENODEV; } - ret = devm_iio_backend_register(&pdev->dev, &adi_axi_adc_generic, st); + ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st); if (ret) return dev_err_probe(&pdev->dev, ret, "failed to register iio backend\n"); + device_for_each_child_node_scoped(&pdev->dev, child) { + int val; + + if (!st->info->has_child_nodes) + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid fdt axi-dac compatible."); + + /* Processing only reg 0 node */ + ret = fwnode_property_read_u32(child, "reg", &val); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "invalid reg property."); + if (val != 0) + return dev_err_probe(&pdev->dev, -EINVAL, + "invalid node address."); + + ret = axi_adc_create_platform_device(st, child); + if (ret) + return dev_err_probe(&pdev->dev, -EINVAL, + "cannot create device."); + } + dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n", ADI_AXI_PCORE_VER_MAJOR(ver), ADI_AXI_PCORE_VER_MINOR(ver), @@ -416,11 +674,34 @@ static int adi_axi_adc_probe(struct platform_device *pdev) return 0; } -static unsigned int adi_axi_adc_10_0_a_info = ADI_AXI_PCORE_VER(10, 0, 'a'); +static const struct axi_adc_info adc_generic = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &adi_axi_adc_generic, +}; + +static const struct axi_adc_info adi_axi_ad485x = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &axi_ad485x, +}; + +static const struct ad7606_platform_data ad7606_pdata = { + .bus_reg_read = ad7606_bus_reg_read, + .bus_reg_write = ad7606_bus_reg_write, +}; + +static const struct axi_adc_info adc_ad7606 = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &adi_axi_adc_generic, + .pdata = &ad7606_pdata, + .pdata_sz = sizeof(ad7606_pdata), + .has_child_nodes = true, +}; /* Match table for of_platform binding */ static const struct of_device_id adi_axi_adc_of_match[] = { - { .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info }, + { .compatible = "adi,axi-adc-10.0.a", .data = &adc_generic }, + { .compatible = "adi,axi-ad485x", .data = &adi_axi_ad485x }, + { .compatible = "adi,axi-ad7606x", .data = &adc_ad7606 }, { /* end of list */ } }; MODULE_DEVICE_TABLE(of, adi_axi_adc_of_match); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index c3a1dea2aa82..414610afcb2c 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -9,6 +9,7 @@ */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-mapping.h> @@ -1826,19 +1827,10 @@ static int at91_adc_read_info_locked(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct at91_adc_state *st = iio_priv(indio_dev); - int ret; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - mutex_lock(&st->lock); - ret = at91_adc_read_info_raw(indio_dev, chan, val); - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + guard(mutex)(&st->lock); - return ret; + return at91_adc_read_info_raw(indio_dev, chan, val); } static void at91_adc_temp_sensor_configure(struct at91_adc_state *st, @@ -1883,14 +1875,11 @@ static int at91_adc_read_temp(struct iio_dev *indio_dev, u32 tmp; int ret, vbg, vtemp; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = pm_runtime_resume_and_get(st->dev); if (ret < 0) - goto unlock; + return ret; at91_adc_temp_sensor_configure(st, true); @@ -1912,9 +1901,6 @@ restore_config: at91_adc_temp_sensor_configure(st, false); pm_runtime_mark_last_busy(st->dev); pm_runtime_put_autosuspend(st->dev); -unlock: - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -1936,10 +1922,16 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct at91_adc_state *st = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - return at91_adc_read_info_locked(indio_dev, chan, val); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = at91_adc_read_info_locked(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: *val = st->vref_uv / 1000; @@ -1951,7 +1943,13 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: if (chan->type != IIO_TEMP) return -EINVAL; - return at91_adc_read_temp(indio_dev, chan, val); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = at91_adc_read_temp(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + + return ret; case IIO_CHAN_INFO_SAMP_FREQ: *val = at91_adc_get_sample_freq(st); @@ -1979,28 +1977,26 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, if (val == st->oversampling_ratio) return 0; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&st->lock); /* update ratio */ ret = at91_adc_config_emr(st, val, 0); mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: if (val < st->soc_info.min_sample_rate || val > st->soc_info.max_sample_rate) return -EINVAL; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&st->lock); at91_adc_setup_samp_freq(indio_dev, val, st->soc_info.startup_time, 0); mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return 0; default: return -EINVAL; diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 221a5fdc1eaa..a1e48a756a7b 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -314,15 +314,14 @@ static int dln2_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret < 0) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&dln2->mutex); ret = dln2_adc_read(dln2, chan->channel); mutex_unlock(&dln2->mutex); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index f5ba4a1b5a7d..7e736e77d8bb 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -336,10 +336,6 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, int ret; struct max1027_state *st = iio_priv(indio_dev); - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - /* Configure conversion register with the requested chan */ st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | MAX1027_NOSCAN; @@ -349,7 +345,7 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, if (ret < 0) { dev_err(&indio_dev->dev, "Failed to configure conversion register\n"); - goto release; + return ret; } /* @@ -359,14 +355,10 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, */ ret = max1027_wait_eoc(indio_dev); if (ret) - goto release; + return ret; /* Read result */ ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2); - -release: - iio_device_release_direct_mode(indio_dev); - if (ret < 0) return ret; @@ -382,37 +374,32 @@ static int max1027_read_raw(struct iio_dev *indio_dev, int ret = 0; struct max1027_state *st = iio_priv(indio_dev); - mutex_lock(&st->lock); + guard(mutex)(&st->lock); switch (mask) { case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = max1027_read_single_value(indio_dev, chan, val); - break; + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_TEMP: *val = 1; *val2 = 8; - ret = IIO_VAL_FRACTIONAL; - break; + return IIO_VAL_FRACTIONAL; case IIO_VOLTAGE: *val = 2500; *val2 = chan->scan_type.realbits; - ret = IIO_VAL_FRACTIONAL_LOG2; - break; + return IIO_VAL_FRACTIONAL_LOG2; default: - ret = -EINVAL; - break; + return -EINVAL; } - break; default: - ret = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&st->lock); - - return ret; } static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index 76abafd47404..437d9f24b5a1 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -471,9 +471,8 @@ static int max11410_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&state->lock); @@ -481,7 +480,7 @@ static int max11410_read_raw(struct iio_dev *indio_dev, mutex_unlock(&state->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; @@ -507,12 +506,37 @@ static int max11410_read_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int __max11410_write_samp_freq(struct max11410_state *st, + int val, int val2) +{ + int ret, i, reg_val, filter; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, MAX11410_REG_FILTER, ®_val); + if (ret) + return ret; + + filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val); + + for (i = 0; i < max11410_sampling_len[filter]; ++i) { + if (val == max11410_sampling_rates[filter][i][0] && + val2 == max11410_sampling_rates[filter][i][1]) + break; + } + if (i == max11410_sampling_len[filter]) + return -EINVAL; + + return regmap_write_bits(st->regmap, MAX11410_REG_FILTER, + MAX11410_FILTER_RATE_MASK, i); +} + static int max11410_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct max11410_state *st = iio_priv(indio_dev); - int i, ret, reg_val, filter, gain; + int ret, gain; u32 *scale_avail; switch (mask) { @@ -525,9 +549,8 @@ static int max11410_write_raw(struct iio_dev *indio_dev, if (val != 0 || val2 == 0) return -EINVAL; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; /* Convert from INT_PLUS_MICRO to FRACTIONAL_LOG2 */ val2 = val2 * DIV_ROUND_CLOSEST(BIT(24), 1000000); @@ -536,38 +559,15 @@ static int max11410_write_raw(struct iio_dev *indio_dev, st->channels[chan->address].gain = clamp_val(gain, 0, 7); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return 0; case IIO_CHAN_INFO_SAMP_FREQ: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - mutex_lock(&st->lock); - - ret = regmap_read(st->regmap, MAX11410_REG_FILTER, ®_val); - if (ret) - goto out; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val); - - for (i = 0; i < max11410_sampling_len[filter]; ++i) { - if (val == max11410_sampling_rates[filter][i][0] && - val2 == max11410_sampling_rates[filter][i][1]) - break; - } - if (i == max11410_sampling_len[filter]) { - ret = -EINVAL; - goto out; - } - - ret = regmap_write_bits(st->regmap, MAX11410_REG_FILTER, - MAX11410_FILTER_RATE_MASK, i); - -out: - mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + ret = __max11410_write_samp_freq(st, val, val2); + iio_device_release_direct(indio_dev); return ret; default: diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index e8d731bc34e0..35717ec082ce 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -364,55 +364,52 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, int *val, long m) { - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - s32 data; - u8 rxbuf[2]; - struct max1363_state *st = iio_priv(indio_dev); - struct i2c_client *client = st->client; - - guard(mutex)(&st->lock); - - /* - * If monitor mode is enabled, the method for reading a single - * channel will have to be rather different and has not yet - * been implemented. - * - * Also, cannot read directly if buffered capture enabled. - */ - if (st->monitor_on) - return -EBUSY; + s32 data; + u8 rxbuf[2]; + struct max1363_state *st = iio_priv(indio_dev); + struct i2c_client *client = st->client; - /* Check to see if current scan mode is correct */ - if (st->current_mode != &max1363_mode_table[chan->address]) { - int ret; + guard(mutex)(&st->lock); - /* Update scan mode if needed */ - st->current_mode = &max1363_mode_table[chan->address]; - ret = max1363_set_scan_mode(st); - if (ret < 0) - return ret; - } - if (st->chip_info->bits != 8) { - /* Get reading */ - data = st->recv(client, rxbuf, 2); - if (data < 0) - return data; - - data = get_unaligned_be16(rxbuf) & - ((1 << st->chip_info->bits) - 1); - } else { - /* Get reading */ - data = st->recv(client, rxbuf, 1); - if (data < 0) - return data; - - data = rxbuf[0]; - } - *val = data; + /* + * If monitor mode is enabled, the method for reading a single + * channel will have to be rather different and has not yet + * been implemented. + * + * Also, cannot read directly if buffered capture enabled. + */ + if (st->monitor_on) + return -EBUSY; + + /* Check to see if current scan mode is correct */ + if (st->current_mode != &max1363_mode_table[chan->address]) { + int ret; + + /* Update scan mode if needed */ + st->current_mode = &max1363_mode_table[chan->address]; + ret = max1363_set_scan_mode(st); + if (ret < 0) + return ret; + } + if (st->chip_info->bits != 8) { + /* Get reading */ + data = st->recv(client, rxbuf, 2); + if (data < 0) + return data; + + data = get_unaligned_be16(rxbuf) & + ((1 << st->chip_info->bits) - 1); + } else { + /* Get reading */ + data = st->recv(client, rxbuf, 1); + if (data < 0) + return data; - return 0; + data = rxbuf[0]; } - unreachable(); + *val = data; + + return 0; } static int max1363_read_raw(struct iio_dev *indio_dev, @@ -426,7 +423,11 @@ static int max1363_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = max1363_read_single_chan(indio_dev, chan, val, m); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; return IIO_VAL_INT; @@ -947,46 +948,58 @@ error_ret: return ret; } -static int max1363_write_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, enum iio_event_type type, +static int __max1363_write_event_config(struct max1363_state *st, + const struct iio_chan_spec *chan, enum iio_event_direction dir, bool state) { - struct max1363_state *st = iio_priv(indio_dev); - - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - int number = chan->channel; - u16 unifiedmask; - int ret; + int number = chan->channel; + u16 unifiedmask; + int ret; - guard(mutex)(&st->lock); + guard(mutex)(&st->lock); - unifiedmask = st->mask_low | st->mask_high; - if (dir == IIO_EV_DIR_FALLING) { + unifiedmask = st->mask_low | st->mask_high; + if (dir == IIO_EV_DIR_FALLING) { - if (state == 0) - st->mask_low &= ~(1 << number); - else { - ret = __max1363_check_event_mask((1 << number), - unifiedmask); - if (ret) - return ret; - st->mask_low |= (1 << number); - } - } else { - if (state == 0) - st->mask_high &= ~(1 << number); - else { - ret = __max1363_check_event_mask((1 << number), - unifiedmask); - if (ret) - return ret; - st->mask_high |= (1 << number); - } + if (state == 0) + st->mask_low &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + return ret; + st->mask_low |= (1 << number); + } + } else { + if (state == 0) + st->mask_high &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + return ret; + st->mask_high |= (1 << number); } } - max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); return 0; + +} +static int max1363_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __max1363_write_event_config(st, chan, dir, state); + iio_device_release_direct(indio_dev); + max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); + + return ret; } /* diff --git a/drivers/iio/adc/max34408.c b/drivers/iio/adc/max34408.c index 971e6e5dee9b..4f45fd22a90c 100644 --- a/drivers/iio/adc/max34408.c +++ b/drivers/iio/adc/max34408.c @@ -8,6 +8,7 @@ */ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/module.h> diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index 63f518215156..beb5511c4504 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -7,6 +7,7 @@ #include <linux/unaligned.h> #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/i2c.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index a29e54754c8f..9a099df79518 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Rockchip Successive Approximation Register (SAR) A/D Converter - * Copyright (C) 2014 ROCKCHIP, Inc. + * Copyright (C) 2014 Rockchip Electronics Co., Ltd. */ #include <linux/bitfield.h> @@ -275,6 +275,40 @@ static const struct rockchip_saradc_data rk3399_saradc_data = { .power_down = rockchip_saradc_power_down_v1, }; +static const struct iio_chan_spec rockchip_rk3528_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), +}; + +static const struct rockchip_saradc_data rk3528_saradc_data = { + .channels = rockchip_rk3528_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3528_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + +static const struct iio_chan_spec rockchip_rk3562_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), + SARADC_CHANNEL(6, "adc6", 10), + SARADC_CHANNEL(7, "adc7", 10), +}; + +static const struct rockchip_saradc_data rk3562_saradc_data = { + .channels = rockchip_rk3562_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3562_saradc_iio_channels), + .clk_rate = 1000000, + .start = rockchip_saradc_start_v2, + .read = rockchip_saradc_read_v2, +}; + static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = { SARADC_CHANNEL(0, "adc0", 10), SARADC_CHANNEL(1, "adc1", 10), @@ -325,6 +359,12 @@ static const struct of_device_id rockchip_saradc_match[] = { .compatible = "rockchip,rk3399-saradc", .data = &rk3399_saradc_data, }, { + .compatible = "rockchip,rk3528-saradc", + .data = &rk3528_saradc_data, + }, { + .compatible = "rockchip,rk3562-saradc", + .data = &rk3562_saradc_data, + }, { .compatible = "rockchip,rk3568-saradc", .data = &rk3568_saradc_data, }, { diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index 337bc8b31b2c..54239df61d86 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -514,26 +514,37 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev, } } -static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, - int val2, long mask) +static int __rtq6056_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + long mask) { struct rtq6056_priv *priv = iio_priv(indio_dev); const struct richtek_dev_data *devdata = priv->devdata; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: - if (devdata->fixed_samp_freq) - return -EINVAL; - return rtq6056_adc_set_samp_freq(priv, chan, val); - case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - return devdata->set_average(priv, val); - default: + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (devdata->fixed_samp_freq) return -EINVAL; - } + return rtq6056_adc_set_samp_freq(priv, chan, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return devdata->set_average(priv, val); + default: + return -EINVAL; } - unreachable(); +} + +static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __rtq6056_adc_write_raw(indio_dev, chan, val, mask); + iio_device_release_direct(indio_dev); + return ret; } static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = { @@ -590,9 +601,8 @@ static ssize_t shunt_resistor_store(struct device *dev, struct rtq6056_priv *priv = iio_priv(indio_dev); int val, val_fract, ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); if (ret) @@ -601,7 +611,7 @@ static ssize_t shunt_resistor_store(struct device *dev, ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract); out_store: - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret ?: len; } diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 2201ee9987ae..0914148d1a22 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -615,8 +615,7 @@ static int stm32_adc_core_switches_probe(struct device *dev, } /* Booster can be used to supply analog switches (optional) */ - if (priv->cfg->has_syscfg & HAS_VBOOSTER && - of_property_read_bool(np, "booster-supply")) { + if (priv->cfg->has_syscfg & HAS_VBOOSTER) { priv->booster = devm_regulator_get_optional(dev, "booster"); if (IS_ERR(priv->booster)) { ret = PTR_ERR(priv->booster); @@ -628,8 +627,7 @@ static int stm32_adc_core_switches_probe(struct device *dev, } /* Vdd can be used to supply analog switches (optional) */ - if (priv->cfg->has_syscfg & HAS_ANASWVDD && - of_property_read_bool(np, "vdd-supply")) { + if (priv->cfg->has_syscfg & HAS_ANASWVDD) { priv->vdd = devm_regulator_get_optional(dev, "vdd"); if (IS_ERR(priv->vdd)) { ret = PTR_ERR(priv->vdd); diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 9d3b23efcc06..5dbf5f136768 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1471,9 +1471,8 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; if (chan->type == IIO_VOLTAGE) ret = stm32_adc_single_conv(indio_dev, chan, val); else @@ -1482,7 +1481,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, if (mask == IIO_CHAN_INFO_PROCESSED) *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val; - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index fe11b0d8eab3..726ddafc9f6d 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1275,9 +1275,8 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = stm32_dfsdm_compute_all_osrs(indio_dev, val); if (!ret) { @@ -1287,25 +1286,56 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, adc->oversamp = val; adc->sample_freq = spi_freq / val; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: if (!val) return -EINVAL; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } return -EINVAL; } +static int __stm32_dfsdm_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret = 0; + + if (adc->hwc) + ret = iio_hw_consumer_enable(adc->hwc); + if (adc->backend) + ret = iio_backend_enable(adc->backend[chan->scan_index]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "%s: IIO enable failed (channel %d)\n", + __func__, chan->channel); + return ret; + } + ret = stm32_dfsdm_single_conv(indio_dev, chan, val); + if (adc->hwc) + iio_hw_consumer_disable(adc->hwc); + if (adc->backend) + iio_backend_disable(adc->backend[chan->scan_index]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "%s: Conversion failed (channel %d)\n", + __func__, chan->channel); + return ret; + } + + return 0; +} + static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -1323,33 +1353,13 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __stm32_dfsdm_read_info_raw(indio_dev, chan, val); + iio_device_release_direct(indio_dev); if (ret) return ret; - if (adc->hwc) - ret = iio_hw_consumer_enable(adc->hwc); - if (adc->backend) - ret = iio_backend_enable(adc->backend[idx]); - if (ret < 0) { - dev_err(&indio_dev->dev, - "%s: IIO enable failed (channel %d)\n", - __func__, chan->channel); - iio_device_release_direct_mode(indio_dev); - return ret; - } - ret = stm32_dfsdm_single_conv(indio_dev, chan, val); - if (adc->hwc) - iio_hw_consumer_disable(adc->hwc); - if (adc->backend) - iio_backend_disable(adc->backend[idx]); - if (ret < 0) { - dev_err(&indio_dev->dev, - "%s: Conversion failed (channel %d)\n", - __func__, chan->channel); - iio_device_release_direct_mode(indio_dev); - return ret; - } - iio_device_release_direct_mode(indio_dev); return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index da16876c32ae..9c845ee01697 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -96,19 +96,18 @@ static int adc084s021_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret < 0) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = regulator_enable(adc->reg); if (ret) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } adc->tx_buf[0] = channel->channel << 3; ret = adc084s021_adc_conversion(adc, &be_val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); regulator_disable(adc->reg); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index 9758ac801310..7d615e2bbf39 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -181,13 +181,12 @@ static int adc108s102_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = adc108s102_scan_direct(st, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 474e733fb8e0..28aa6b80160c 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -137,13 +137,13 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = ti_adc_read_measurement(data, chan, val); - if (ret) - return ret; - return IIO_VAL_INT; - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ti_adc_read_measurement(data, chan, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = regulator_get_voltage(data->ref); if (ret < 0) diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index de019b3faa48..f120e7e21cff 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -336,19 +336,24 @@ static int ads1119_read_raw(struct iio_dev *indio_dev, { struct ads1119_state *st = iio_priv(indio_dev); unsigned int index = chan->address; + int ret; if (index >= st->num_channels_cfg) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return ads1119_single_conversion(st, chan, val, false); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ads1119_single_conversion(st, chan, val, false); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_OFFSET: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return ads1119_single_conversion(st, chan, val, true); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ads1119_single_conversion(st, chan, val, true); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: *val = st->vref_uV / 1000; *val /= st->channels_cfg[index].gain; diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index f452f57f11c9..77c299bb4ebc 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -184,7 +184,7 @@ static int ads124s_reset(struct iio_dev *indio_dev) if (priv->reset_gpio) { gpiod_set_value_cansleep(priv->reset_gpio, 0); - udelay(200); + fsleep(200); gpiod_set_value_cansleep(priv->reset_gpio, 1); } else { return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET); diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c index 03f762415fa5..ae30b47e4514 100644 --- a/drivers/iio/adc/ti-ads1298.c +++ b/drivers/iio/adc/ti-ads1298.c @@ -319,13 +319,12 @@ static int ads1298_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ads1298_read_one(priv, chan->scan_index); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 91a79ebc4bde..c6096b64664e 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -505,12 +505,11 @@ static int ads131e08_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ads131e08_read_direct(indio_dev, channel, value); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; @@ -551,12 +550,11 @@ static int ads131e08_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ads131e08_set_data_rate(st, value); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; default: diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c new file mode 100644 index 000000000000..ee5c1b8e3a8e --- /dev/null +++ b/drivers/iio/adc/ti-ads7138.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADS7138 - Texas Instruments Analog-to-Digital Converter + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/unaligned.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +/* + * Always assume 16 bits resolution as HW registers are aligned like that and + * with enabled oversampling/averaging it actually corresponds to 16 bits. + */ +#define ADS7138_RES_BITS 16 + +/* ADS7138 operation codes */ +#define ADS7138_OPCODE_SINGLE_WRITE 0x08 +#define ADS7138_OPCODE_SET_BIT 0x18 +#define ADS7138_OPCODE_CLEAR_BIT 0x20 +#define ADS7138_OPCODE_BLOCK_WRITE 0x28 +#define ADS7138_OPCODE_BLOCK_READ 0x30 + +/* ADS7138 registers */ +#define ADS7138_REG_GENERAL_CFG 0x01 +#define ADS7138_REG_OSR_CFG 0x03 +#define ADS7138_REG_OPMODE_CFG 0x04 +#define ADS7138_REG_SEQUENCE_CFG 0x10 +#define ADS7138_REG_AUTO_SEQ_CH_SEL 0x12 +#define ADS7138_REG_ALERT_CH_SEL 0x14 +#define ADS7138_REG_EVENT_FLAG 0x18 +#define ADS7138_REG_EVENT_HIGH_FLAG 0x1A +#define ADS7138_REG_EVENT_LOW_FLAG 0x1C +#define ADS7138_REG_HIGH_TH_HYS_CH(x) ((x) * 4 + 0x20) +#define ADS7138_REG_LOW_TH_CNT_CH(x) ((x) * 4 + 0x22) +#define ADS7138_REG_MAX_LSB_CH(x) ((x) * 2 + 0x60) +#define ADS7138_REG_MIN_LSB_CH(x) ((x) * 2 + 0x80) +#define ADS7138_REG_RECENT_LSB_CH(x) ((x) * 2 + 0xA0) + +#define ADS7138_GENERAL_CFG_RST BIT(0) +#define ADS7138_GENERAL_CFG_DWC_EN BIT(4) +#define ADS7138_GENERAL_CFG_STATS_EN BIT(5) +#define ADS7138_OSR_CFG_MASK GENMASK(2, 0) +#define ADS7138_OPMODE_CFG_CONV_MODE BIT(5) +#define ADS7138_OPMODE_CFG_FREQ_MASK GENMASK(4, 0) +#define ADS7138_SEQUENCE_CFG_SEQ_MODE BIT(0) +#define ADS7138_SEQUENCE_CFG_SEQ_START BIT(4) +#define ADS7138_THRESHOLD_LSB_MASK GENMASK(7, 4) + +enum ads7138_modes { + ADS7138_MODE_MANUAL, + ADS7138_MODE_AUTO, +}; + +struct ads7138_chip_data { + const char *name; + const int channel_num; +}; + +struct ads7138_data { + /* Protects RMW access to the I2C interface */ + struct mutex lock; + struct i2c_client *client; + struct regulator *vref_regu; + const struct ads7138_chip_data *chip_data; +}; + +/* + * 2D array of available sampling frequencies and the corresponding register + * values. Structured like this to be easily usable in read_avail function. + */ +static const int ads7138_samp_freqs_bits[2][26] = { + { + 163, 244, 326, 488, 651, 977, 1302, 1953, + 2604, 3906, 5208, 7813, 10417, 15625, 20833, 31250, + 41667, 62500, 83333, 125000, 166667, 250000, 333333, 500000, + 666667, 1000000 + }, { + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + /* Here is a hole, due to duplicate frequencies */ + 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, + 0x01, 0x00 + } +}; + +static const int ads7138_oversampling_ratios[] = { + 1, 2, 4, 8, 16, 32, 64, 128 +}; + +static int ads7138_i2c_write_block(const struct i2c_client *client, u8 reg, + u8 *values, u8 length) +{ + int ret; + int len = length + 2; /* "+ 2" for OPCODE and reg */ + + u8 *buf __free(kfree) = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = ADS7138_OPCODE_BLOCK_WRITE; + buf[1] = reg; + memcpy(&buf[2], values, length); + + ret = i2c_master_send(client, buf, len); + if (ret < 0) + return ret; + if (ret != len) + return -EIO; + + return 0; +} + +static int ads7138_i2c_write_with_opcode(const struct i2c_client *client, + u8 reg, u8 regval, u8 opcode) +{ + u8 buf[3] = { opcode, reg, regval }; + int ret; + + ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(buf)) + return -EIO; + + return 0; +} + +static int ads7138_i2c_write(const struct i2c_client *client, u8 reg, u8 value) +{ + return ads7138_i2c_write_with_opcode(client, reg, value, + ADS7138_OPCODE_SINGLE_WRITE); +} + +static int ads7138_i2c_set_bit(const struct i2c_client *client, u8 reg, u8 bits) +{ + return ads7138_i2c_write_with_opcode(client, reg, bits, + ADS7138_OPCODE_SET_BIT); +} + +static int ads7138_i2c_clear_bit(const struct i2c_client *client, u8 reg, u8 bits) +{ + return ads7138_i2c_write_with_opcode(client, reg, bits, + ADS7138_OPCODE_CLEAR_BIT); +} + +static int ads7138_i2c_read_block(const struct i2c_client *client, u8 reg, + u8 *out_values, u8 length) +{ + u8 buf[2] = { ADS7138_OPCODE_BLOCK_READ, reg }; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .len = ARRAY_SIZE(buf), + .buf = buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = out_values, + }, + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + return 0; +} + +static int ads7138_i2c_read(const struct i2c_client *client, u8 reg) +{ + u8 value; + int ret; + + ret = ads7138_i2c_read_block(client, reg, &value, sizeof(value)); + if (ret) + return ret; + return value; +} + +static int ads7138_freq_to_bits(int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[0]); i++) + if (freq == ads7138_samp_freqs_bits[0][i]) + return ads7138_samp_freqs_bits[1][i]; + + return -EINVAL; +} + +static int ads7138_bits_to_freq(int bits) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[1]); i++) + if (bits == ads7138_samp_freqs_bits[1][i]) + return ads7138_samp_freqs_bits[0][i]; + + return -EINVAL; +} + +static int ads7138_osr_to_bits(int osr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads7138_oversampling_ratios); i++) + if (osr == ads7138_oversampling_ratios[i]) + return i; + + return -EINVAL; +} + +static int ads7138_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct ads7138_data *data = iio_priv(indio_dev); + int ret, vref, bits; + u8 values[2]; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ads7138_i2c_read_block(data->client, + ADS7138_REG_RECENT_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = get_unaligned_le16(values); + return IIO_VAL_INT; + case IIO_CHAN_INFO_PEAK: + ret = ads7138_i2c_read_block(data->client, + ADS7138_REG_MAX_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = get_unaligned_le16(values); + return IIO_VAL_INT; + case IIO_CHAN_INFO_TROUGH: + ret = ads7138_i2c_read_block(data->client, + ADS7138_REG_MIN_LSB_CH(chan->channel), + values, ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = get_unaligned_le16(values); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG); + if (ret < 0) + return ret; + + bits = FIELD_GET(ADS7138_OPMODE_CFG_FREQ_MASK, ret); + *val = ads7138_bits_to_freq(bits); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref = regulator_get_voltage(data->vref_regu); + if (vref < 0) + return vref; + *val = vref / 1000; + *val2 = ADS7138_RES_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = ads7138_i2c_read(data->client, ADS7138_REG_OSR_CFG); + if (ret < 0) + return ret; + + bits = FIELD_GET(ADS7138_OSR_CFG_MASK, ret); + *val = ads7138_oversampling_ratios[bits]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads7138_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads7138_data *data = iio_priv(indio_dev); + int bits, ret; + u8 value; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + bits = ads7138_freq_to_bits(val); + if (bits < 0) + return bits; + + guard(mutex)(&data->lock); + ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG); + if (ret < 0) + return ret; + + value = ret & ~ADS7138_OPMODE_CFG_FREQ_MASK; + value |= FIELD_PREP(ADS7138_OPMODE_CFG_FREQ_MASK, bits); + return ads7138_i2c_write(data->client, ADS7138_REG_OPMODE_CFG, + value); + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + bits = ads7138_osr_to_bits(val); + if (bits < 0) + return bits; + + return ads7138_i2c_write(data->client, ADS7138_REG_OSR_CFG, + bits); + default: + return -EINVAL; + } +} + +static int ads7138_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ads7138_data *data = iio_priv(indio_dev); + u8 reg, values[2]; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + reg = (dir == IIO_EV_DIR_RISING) ? + ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) : + ADS7138_REG_LOW_TH_CNT_CH(chan->channel); + ret = ads7138_i2c_read_block(data->client, reg, values, + ARRAY_SIZE(values)); + if (ret) + return ret; + + *val = ((values[1] << 4) | (values[0] >> 4)); + return IIO_VAL_INT; + case IIO_EV_INFO_HYSTERESIS: + ret = ads7138_i2c_read(data->client, + ADS7138_REG_HIGH_TH_HYS_CH(chan->channel)); + if (ret < 0) + return ret; + + *val = ret & ~ADS7138_THRESHOLD_LSB_MASK; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ads7138_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ads7138_data *data = iio_priv(indio_dev); + u8 reg, values[2]; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: { + if (val >= BIT(12) || val < 0) + return -EINVAL; + + reg = (dir == IIO_EV_DIR_RISING) ? + ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) : + ADS7138_REG_LOW_TH_CNT_CH(chan->channel); + + guard(mutex)(&data->lock); + ret = ads7138_i2c_read(data->client, reg); + if (ret < 0) + return ret; + + values[0] = ret & ~ADS7138_THRESHOLD_LSB_MASK; + values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, val); + values[1] = (val >> 4); + return ads7138_i2c_write_block(data->client, reg, values, + ARRAY_SIZE(values)); + } + case IIO_EV_INFO_HYSTERESIS: { + if (val >= BIT(4) || val < 0) + return -EINVAL; + + reg = ADS7138_REG_HIGH_TH_HYS_CH(chan->channel); + + guard(mutex)(&data->lock); + ret = ads7138_i2c_read(data->client, reg); + if (ret < 0) + return ret; + + values[0] = val & ~ADS7138_THRESHOLD_LSB_MASK; + values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, ret >> 4); + return ads7138_i2c_write(data->client, reg, values[0]); + } + default: + return -EINVAL; + } +} + +static int ads7138_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ads7138_data *data = iio_priv(indio_dev); + int ret; + + if (dir != IIO_EV_DIR_EITHER) + return -EINVAL; + + ret = ads7138_i2c_read(data->client, ADS7138_REG_ALERT_CH_SEL); + if (ret < 0) + return ret; + + return (ret & BIT(chan->channel)) ? 1 : 0; +} + +static int ads7138_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct ads7138_data *data = iio_priv(indio_dev); + + if (dir != IIO_EV_DIR_EITHER) + return -EINVAL; + + if (state) + return ads7138_i2c_set_bit(data->client, + ADS7138_REG_ALERT_CH_SEL, + BIT(chan->channel)); + else + return ads7138_i2c_clear_bit(data->client, + ADS7138_REG_ALERT_CH_SEL, + BIT(chan->channel)); +} + +static int ads7138_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = ads7138_samp_freqs_bits[0]; + *length = ARRAY_SIZE(ads7138_samp_freqs_bits[0]); + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ads7138_oversampling_ratios; + *length = ARRAY_SIZE(ads7138_oversampling_ratios); + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info ti_ads7138_info = { + .read_raw = &ads7138_read_raw, + .read_avail = &ads7138_read_avail, + .write_raw = &ads7138_write_raw, + .read_event_value = &ads7138_read_event, + .write_event_value = &ads7138_write_event, + .read_event_config = &ads7138_read_event_config, + .write_event_config = &ads7138_write_event_config, +}; + +static const struct iio_event_spec ads7138_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define ADS7138_V_CHAN(_chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_PEAK) | \ + BIT(IIO_CHAN_INFO_TROUGH), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = "AIN"#_chan, \ + .event_spec = ads7138_events, \ + .num_event_specs = ARRAY_SIZE(ads7138_events), \ +} + +static const struct iio_chan_spec ads7138_channels[] = { + ADS7138_V_CHAN(0), + ADS7138_V_CHAN(1), + ADS7138_V_CHAN(2), + ADS7138_V_CHAN(3), + ADS7138_V_CHAN(4), + ADS7138_V_CHAN(5), + ADS7138_V_CHAN(6), + ADS7138_V_CHAN(7), +}; + +static irqreturn_t ads7138_event_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct ads7138_data *data = iio_priv(indio_dev); + struct device *dev = &data->client->dev; + u8 i, events_high, events_low; + u64 code; + int ret; + + /* Check if interrupt was trigger by us */ + ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_FLAG); + if (ret <= 0) + return IRQ_NONE; + + ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_HIGH_FLAG); + if (ret < 0) { + dev_warn(dev, "Failed to read event high flags: %d\n", ret); + return IRQ_HANDLED; + } + events_high = ret; + + ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_LOW_FLAG); + if (ret < 0) { + dev_warn(dev, "Failed to read event low flags: %d\n", ret); + return IRQ_HANDLED; + } + events_low = ret; + + for (i = 0; i < data->chip_data->channel_num; i++) { + if (events_high & BIT(i)) { + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(indio_dev, code, + iio_get_time_ns(indio_dev)); + } + if (events_low & BIT(i)) { + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(indio_dev, code, + iio_get_time_ns(indio_dev)); + } + } + + /* Try to clear all interrupt flags */ + ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_HIGH_FLAG, 0xFF); + if (ret) + dev_warn(dev, "Failed to clear event high flags: %d\n", ret); + + ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_LOW_FLAG, 0xFF); + if (ret) + dev_warn(dev, "Failed to clear event low flags: %d\n", ret); + + return IRQ_HANDLED; +} + +static int ads7138_set_conv_mode(struct ads7138_data *data, + enum ads7138_modes mode) +{ + if (mode == ADS7138_MODE_AUTO) + return ads7138_i2c_set_bit(data->client, ADS7138_REG_OPMODE_CFG, + ADS7138_OPMODE_CFG_CONV_MODE); + return ads7138_i2c_clear_bit(data->client, ADS7138_REG_OPMODE_CFG, + ADS7138_OPMODE_CFG_CONV_MODE); +} + +static int ads7138_init_hw(struct ads7138_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + + data->vref_regu = devm_regulator_get(dev, "avdd"); + if (IS_ERR(data->vref_regu)) + return dev_err_probe(dev, PTR_ERR(data->vref_regu), + "Failed to get avdd regulator\n"); + + ret = regulator_get_voltage(data->vref_regu); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get avdd voltage\n"); + + /* Reset the chip to get a defined starting configuration */ + ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_RST); + if (ret) + return ret; + + ret = ads7138_set_conv_mode(data, ADS7138_MODE_AUTO); + if (ret) + return ret; + + /* Enable statistics and digital window comparator */ + ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG, + ADS7138_GENERAL_CFG_STATS_EN | + ADS7138_GENERAL_CFG_DWC_EN); + if (ret) + return ret; + + /* Enable all channels for auto sequencing */ + ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_AUTO_SEQ_CH_SEL, 0xFF); + if (ret) + return ret; + + /* Set auto sequence mode and start sequencing */ + return ads7138_i2c_set_bit(data->client, ADS7138_REG_SEQUENCE_CFG, + ADS7138_SEQUENCE_CFG_SEQ_START | + ADS7138_SEQUENCE_CFG_SEQ_MODE); +} + +static int ads7138_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct ads7138_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + data->chip_data = i2c_get_match_data(client); + if (!data->chip_data) + return -ENODEV; + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + indio_dev->name = data->chip_data->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads7138_channels; + indio_dev->num_channels = ARRAY_SIZE(ads7138_channels); + indio_dev->info = &ti_ads7138_info; + + i2c_set_clientdata(client, indio_dev); + + if (client->irq > 0) { + ret = devm_request_threaded_irq(dev, client->irq, + NULL, ads7138_event_handler, + IRQF_TRIGGER_LOW | + IRQF_ONESHOT | IRQF_SHARED, + client->name, indio_dev); + if (ret) + return ret; + } + + ret = ads7138_init_hw(data); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize device\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); + + return 0; +} + +static int ads7138_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ads7138_data *data = iio_priv(indio_dev); + + return ads7138_set_conv_mode(data, ADS7138_MODE_MANUAL); +} + +static int ads7138_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ads7138_data *data = iio_priv(indio_dev); + + return ads7138_set_conv_mode(data, ADS7138_MODE_AUTO); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(ads7138_pm_ops, + ads7138_runtime_suspend, + ads7138_runtime_resume, + NULL); + +static const struct ads7138_chip_data ads7128_data = { + .name = "ads7128", + .channel_num = 8, +}; + +static const struct ads7138_chip_data ads7138_data = { + .name = "ads7138", + .channel_num = 8, +}; + +static const struct of_device_id ads7138_of_match[] = { + { .compatible = "ti,ads7128", .data = &ads7128_data }, + { .compatible = "ti,ads7138", .data = &ads7138_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ads7138_of_match); + +static const struct i2c_device_id ads7138_device_ids[] = { + { "ads7128", (kernel_ulong_t)&ads7128_data }, + { "ads7138", (kernel_ulong_t)&ads7138_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ads7138_device_ids); + +static struct i2c_driver ads7138_driver = { + .driver = { + .name = "ads7138", + .of_match_table = ads7138_of_match, + .pm = pm_ptr(&ads7138_pm_ops), + }, + .id_table = ads7138_device_ids, + .probe = ads7138_probe, +}; +module_i2c_driver(ads7138_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tobias Sperling <tobias.sperling@softing.com>"); +MODULE_DESCRIPTION("Driver for TI ADS7138 ADCs"); diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c index 66b54c0d75aa..b1f745f75dbe 100644 --- a/drivers/iio/adc/ti-ads7924.c +++ b/drivers/iio/adc/ti-ads7924.c @@ -251,11 +251,8 @@ static const struct iio_info ads7924_info = { .read_raw = ads7924_read_raw, }; -static int ads7924_get_channels_config(struct i2c_client *client, - struct iio_dev *indio_dev) +static int ads7924_get_channels_config(struct device *dev) { - struct ads7924_data *priv = iio_priv(indio_dev); - struct device *dev = priv->dev; struct fwnode_handle *node; int num_channels = 0; @@ -380,7 +377,7 @@ static int ads7924_probe(struct i2c_client *client) indio_dev->num_channels = ARRAY_SIZE(ads7924_channels); indio_dev->info = &ads7924_info; - ret = ads7924_get_channels_config(client, indio_dev); + ret = ads7924_get_channels_config(dev); if (ret < 0) return dev_err_probe(dev, ret, "failed to get channels configuration\n"); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c index 08de997584fd..5a138be983ed 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -131,11 +131,10 @@ static int tlc4541_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = spi_sync(st->spi, &st->scan_single_msg); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = be16_to_cpu(st->rx_buf[0]); diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index 7dde5713973f..49560059f4b7 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -812,9 +812,7 @@ static int tsc2046_adc_probe(struct spi_device *spi) spin_lock_init(&priv->state_lock); priv->state = TSC2046_STATE_SHUTDOWN; - hrtimer_init(&priv->trig_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_SOFT); - priv->trig_timer.function = tsc2046_adc_timer; + hrtimer_setup(&priv->trig_timer, tsc2046_adc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); ret = devm_iio_trigger_register(dev, trig); if (ret) { diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index cfbfcaefec0f..e1f8740ae688 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1245,8 +1245,8 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, unsigned int *conf, int irq) channel_templates = xadc_us_channels; max_channels = ARRAY_SIZE(xadc_us_channels); } - channels = devm_kmemdup(dev, channel_templates, - sizeof(channels[0]) * max_channels, GFP_KERNEL); + channels = devm_kmemdup_array(dev, channel_templates, max_channels, + sizeof(*channel_templates), GFP_KERNEL); if (!channels) return -ENOMEM; diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index daea2bde7acf..f14d12b03da6 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -826,6 +826,8 @@ static int _ad74413r_get_single_adc_result(struct ad74413r_state *st, unsigned int uval; int ret; + guard(mutex)(&st->lock); + reinit_completion(&st->adc_data_completion); ret = ad74413r_set_adc_channel_enable(st, channel, true); @@ -865,12 +867,14 @@ static int ad74413r_get_single_adc_result(struct iio_dev *indio_dev, unsigned int channel, int *val) { struct ad74413r_state *st = iio_priv(indio_dev); + int ret; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - guard(mutex)(&st->lock); - return _ad74413r_get_single_adc_result(st, channel, val); - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = _ad74413r_get_single_adc_result(st, channel, val); + iio_device_release_direct(indio_dev); + return ret; } static void ad74413r_adc_to_resistance_result(int adc_result, int *val) diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index 2ee4c0d70281..d9a359e1388a 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -161,8 +161,7 @@ static int hmc425a_write(struct iio_dev *indio_dev, u32 value) values[0] = value; - gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc, - NULL, values); + gpiod_multi_set_value_cansleep(st->gpios, values); return 0; } diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 7ea784304ffb..ee294a775e8a 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -624,7 +624,7 @@ out_unlock: /** * iio_dma_buffer_read() - DMA buffer read callback - * @buffer: Buffer to read form + * @buffer: Buffer to read from * @n: Number of bytes to read * @user_buffer: Userspace buffer to copy the data to * @@ -640,7 +640,7 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_read, "IIO_DMA_BUFFER"); /** * iio_dma_buffer_write() - DMA buffer write callback - * @buffer: Buffer to read form + * @buffer: Buffer to write to * @n: Number of bytes to read * @user_buffer: Userspace buffer to copy the data from * diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 614e1c4189a9..e9d9a7d39fe1 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -206,39 +206,29 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { /** * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine - * @dev: DMA channel consumer device - * @channel: DMA channel name, typically "rx". + * @chan: DMA channel. * * This allocates a new IIO buffer which internally uses the DMAengine framework - * to perform its transfers. The parent device will be used to request the DMA - * channel. + * to perform its transfers. * * Once done using the buffer iio_dmaengine_buffer_free() should be used to * release it. */ -static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, - const char *channel) +static struct iio_buffer *iio_dmaengine_buffer_alloc(struct dma_chan *chan) { struct dmaengine_buffer *dmaengine_buffer; unsigned int width, src_width, dest_width; struct dma_slave_caps caps; - struct dma_chan *chan; int ret; + ret = dma_get_slave_caps(chan, &caps); + if (ret < 0) + return ERR_PTR(ret); + dmaengine_buffer = kzalloc(sizeof(*dmaengine_buffer), GFP_KERNEL); if (!dmaengine_buffer) return ERR_PTR(-ENOMEM); - chan = dma_request_chan(dev, channel); - if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - goto err_free; - } - - ret = dma_get_slave_caps(chan, &caps); - if (ret < 0) - goto err_release; - /* Needs to be aligned to the maximum of the minimums */ if (caps.src_addr_widths) src_width = __ffs(caps.src_addr_widths); @@ -262,12 +252,6 @@ static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops; return &dmaengine_buffer->queue.buffer; - -err_release: - dma_release_channel(chan); -err_free: - kfree(dmaengine_buffer); - return ERR_PTR(ret); } /** @@ -276,17 +260,57 @@ err_free: * * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc(). */ -void iio_dmaengine_buffer_free(struct iio_buffer *buffer) +static void iio_dmaengine_buffer_free(struct iio_buffer *buffer) { struct dmaengine_buffer *dmaengine_buffer = iio_buffer_to_dmaengine_buffer(buffer); iio_dma_buffer_exit(&dmaengine_buffer->queue); - dma_release_channel(dmaengine_buffer->chan); - iio_buffer_put(buffer); } -EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, "IIO_DMAENGINE_BUFFER"); + +/** + * iio_dmaengine_buffer_teardown() - Releases DMA channel and frees buffer + * @buffer: Buffer to free + * + * Releases the DMA channel and frees the buffer previously setup with + * iio_dmaengine_buffer_setup_ext(). + */ +void iio_dmaengine_buffer_teardown(struct iio_buffer *buffer) +{ + struct dmaengine_buffer *dmaengine_buffer = + iio_buffer_to_dmaengine_buffer(buffer); + struct dma_chan *chan = dmaengine_buffer->chan; + + iio_dmaengine_buffer_free(buffer); + dma_release_channel(chan); +} +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_teardown, "IIO_DMAENGINE_BUFFER"); + +static struct iio_buffer +*__iio_dmaengine_buffer_setup_ext(struct iio_dev *indio_dev, + struct dma_chan *chan, + enum iio_buffer_direction dir) +{ + struct iio_buffer *buffer; + int ret; + + buffer = iio_dmaengine_buffer_alloc(chan); + if (IS_ERR(buffer)) + return ERR_CAST(buffer); + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + + buffer->direction = dir; + + ret = iio_device_attach_buffer(indio_dev, buffer); + if (ret) { + iio_dmaengine_buffer_free(buffer); + return ERR_PTR(ret); + } + + return buffer; +} /** * iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device @@ -300,7 +324,7 @@ EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, "IIO_DMAENGINE_BUFFER"); * It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the * IIO device. * - * Once done using the buffer iio_dmaengine_buffer_free() should be used to + * Once done using the buffer iio_dmaengine_buffer_teardown() should be used to * release it. */ struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, @@ -308,30 +332,24 @@ struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, const char *channel, enum iio_buffer_direction dir) { + struct dma_chan *chan; struct iio_buffer *buffer; - int ret; - - buffer = iio_dmaengine_buffer_alloc(dev, channel); - if (IS_ERR(buffer)) - return ERR_CAST(buffer); - - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - buffer->direction = dir; + chan = dma_request_chan(dev, channel); + if (IS_ERR(chan)) + return ERR_CAST(chan); - ret = iio_device_attach_buffer(indio_dev, buffer); - if (ret) { - iio_dmaengine_buffer_free(buffer); - return ERR_PTR(ret); - } + buffer = __iio_dmaengine_buffer_setup_ext(indio_dev, chan, dir); + if (IS_ERR(buffer)) + dma_release_channel(chan); return buffer; } EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup_ext, "IIO_DMAENGINE_BUFFER"); -static void __devm_iio_dmaengine_buffer_free(void *buffer) +static void devm_iio_dmaengine_buffer_teardown(void *buffer) { - iio_dmaengine_buffer_free(buffer); + iio_dmaengine_buffer_teardown(buffer); } /** @@ -357,11 +375,49 @@ int devm_iio_dmaengine_buffer_setup_ext(struct device *dev, if (IS_ERR(buffer)) return PTR_ERR(buffer); - return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, + return devm_add_action_or_reset(dev, devm_iio_dmaengine_buffer_teardown, buffer); } EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, "IIO_DMAENGINE_BUFFER"); +static void devm_iio_dmaengine_buffer_free(void *buffer) +{ + iio_dmaengine_buffer_free(buffer); +} + +/** + * devm_iio_dmaengine_buffer_setup_with_handle() - Setup a DMA buffer for an + * IIO device + * @dev: Device for devm ownership + * @indio_dev: IIO device to which to attach this buffer. + * @chan: DMA channel + * @dir: Direction of buffer (in or out) + * + * This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc() + * and attaches it to an IIO device with iio_device_attach_buffer(). + * It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the + * IIO device. + * + * This is the same as devm_iio_dmaengine_buffer_setup_ext() except that the + * caller manages requesting and releasing the DMA channel handle. + */ +int devm_iio_dmaengine_buffer_setup_with_handle(struct device *dev, + struct iio_dev *indio_dev, + struct dma_chan *chan, + enum iio_buffer_direction dir) +{ + struct iio_buffer *buffer; + + buffer = __iio_dmaengine_buffer_setup_ext(indio_dev, chan, dir); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + return devm_add_action_or_reset(dev, devm_iio_dmaengine_buffer_free, + buffer); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_with_handle, + "IIO_DMAENGINE_BUFFER"); + MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index 48d5ad2075b6..152f81ff57e3 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -100,25 +100,35 @@ static const struct iio_chan_spec ens160_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; +static int __ens160_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct ens160_data *data = iio_priv(indio_dev); + int ret; + + guard(mutex)(&data->mutex); + ret = regmap_bulk_read(data->regmap, chan->address, + &data->buf, sizeof(data->buf)); + if (ret) + return ret; + *val = le16_to_cpu(data->buf); + return IIO_VAL_INT; +} + static int ens160_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct ens160_data *data = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - guard(mutex)(&data->mutex); - ret = regmap_bulk_read(data->regmap, chan->address, - &data->buf, sizeof(data->buf)); - if (ret) - return ret; - *val = le16_to_cpu(data->buf); - return IIO_VAL_INT; - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __ens160_read_raw(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: switch (chan->channel2) { case IIO_MOD_CO2: diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index d613c54cb28d..3fed6b63710f 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -5,6 +5,7 @@ * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> */ #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/device.h> @@ -198,112 +199,103 @@ static int scd30_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const int *val, int *val2, long mask) { struct scd30_state *state = iio_priv(indio_dev); - int ret = -EINVAL; + int ret; u16 tmp; - mutex_lock(&state->lock); + guard(mutex)(&state->lock); switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: if (chan->output) { *val = state->pressure_comp; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; } - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - break; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = scd30_read(state); if (ret) { - iio_device_release_direct_mode(indio_dev); - break; + iio_device_release_direct(indio_dev); + return ret; } *val = state->meas[chan->address]; - iio_device_release_direct_mode(indio_dev); - ret = IIO_VAL_INT; - break; + iio_device_release_direct(indio_dev); + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; *val2 = 1; - ret = IIO_VAL_INT_PLUS_MICRO; - break; + return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: ret = scd30_command_read(state, CMD_MEAS_INTERVAL, &tmp); if (ret) - break; + return ret; *val = 0; *val2 = 1000000000 / tmp; - ret = IIO_VAL_INT_PLUS_NANO; - break; + return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_CALIBBIAS: ret = scd30_command_read(state, CMD_TEMP_OFFSET, &tmp); if (ret) - break; + return ret; *val = tmp; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; + default: + return -EINVAL; } - mutex_unlock(&state->lock); - - return ret; } static int scd30_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct scd30_state *state = iio_priv(indio_dev); - int ret = -EINVAL; + int ret; - mutex_lock(&state->lock); + guard(mutex)(&state->lock); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: if (val) - break; + return -EINVAL; val = 1000000000 / val2; if (val < SCD30_MEAS_INTERVAL_MIN_S || val > SCD30_MEAS_INTERVAL_MAX_S) - break; + return -EINVAL; ret = scd30_command_write(state, CMD_MEAS_INTERVAL, val); if (ret) - break; + return ret; state->meas_interval = val; - break; + return 0; case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_PRESSURE: if (val < SCD30_PRESSURE_COMP_MIN_MBAR || val > SCD30_PRESSURE_COMP_MAX_MBAR) - break; + return -EINVAL; ret = scd30_command_write(state, CMD_START_MEAS, val); if (ret) - break; + return ret; state->pressure_comp = val; - break; + return 0; default: - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_CALIBBIAS: if (val < 0 || val > SCD30_TEMP_OFFSET_MAX) - break; + return -EINVAL; /* * Manufacturer does not explicitly specify min/max sensible * values hence check is omitted for simplicity. */ - ret = scd30_command_write(state, CMD_TEMP_OFFSET / 10, val); + return scd30_command_write(state, CMD_TEMP_OFFSET / 10, val); + default: + return -EINVAL; } - mutex_unlock(&state->lock); - - return ret; } static int scd30_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile index e0a33ab66d21..c358fa0328ab 100644 --- a/drivers/iio/common/cros_ec_sensors/Makefile +++ b/drivers/iio/common/cros_ec_sensors/Makefile @@ -3,6 +3,7 @@ # Makefile for sensors seen through the ChromeOS EC sensor hub. # -obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o +cros-ec-sensors-core-objs += cros_ec_sensors_core.o cros_ec_sensors_trace.o +obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros-ec-sensors-core.o obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o obj-$(CONFIG_IIO_CROS_EC_SENSORS_LID_ANGLE) += cros_ec_lid_angle.o diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index 9fc71a73caa1..7751d6f69b12 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -23,6 +23,8 @@ #include <linux/platform_data/cros_ec_sensorhub.h> #include <linux/platform_device.h> +#include "cros_ec_sensors_trace.h" + /* * Hard coded to the first device to support sensor fifo. The EC has a 2048 * byte fifo and will trigger an interrupt when fifo is 2/3 full. @@ -413,6 +415,7 @@ EXPORT_SYMBOL_GPL(cros_ec_sensors_core_register); int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state, u16 opt_length) { + struct ec_response_motion_sense *resp = (struct ec_response_motion_sense *)state->msg->data; int ret; if (opt_length) @@ -423,12 +426,12 @@ int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state, memcpy(state->msg->data, &state->param, sizeof(state->param)); ret = cros_ec_cmd_xfer_status(state->ec, state->msg); + trace_cros_ec_motion_host_cmd(&state->param, resp, ret); if (ret < 0) return ret; - if (ret && - state->resp != (struct ec_response_motion_sense *)state->msg->data) - memcpy(state->resp, state->msg->data, ret); + if (ret && state->resp != resp) + memcpy(state->resp, resp, ret); return 0; } diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.c new file mode 100644 index 000000000000..c4db949fa775 --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +// Trace events for the ChromeOS Embedded Controller +// +// Copyright 2025 Google LLC. + +#define TRACE_SYMBOL(a) {a, #a} + +// Generate the list using the following script: +// sed -n 's/^.*\(MOTIONSENSE_CMD.*\) = .*,$/\tTRACE_SYMBOL(\1), \\/p' include/linux/platform_data/cros_ec_commands.h +#define MOTIONSENSE_CMDS \ + TRACE_SYMBOL(MOTIONSENSE_CMD_DUMP), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_INFO), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_EC_RATE), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_SENSOR_ODR), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_SENSOR_RANGE), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_KB_WAKE_ANGLE), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_DATA), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_FIFO_INFO), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_FIFO_FLUSH), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_FIFO_READ), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_PERFORM_CALIB), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_SENSOR_OFFSET), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_LIST_ACTIVITIES), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_SET_ACTIVITY), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_LID_ANGLE), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_FIFO_INT_ENABLE), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_SPOOF), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE), \ + TRACE_SYMBOL(MOTIONSENSE_CMD_SENSOR_SCALE) + +#define CREATE_TRACE_POINTS +#include "cros_ec_sensors_trace.h" diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.h new file mode 100644 index 000000000000..8956f2e8ad08 --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_trace.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Trace events for the ChromeOS Embedded Controller + * + * Copyright 2025 Google LLC. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cros_ec + +#if !defined(_CROS_EC_SENSORS_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _CROS_EC_SENSORS_TRACE_H_ + +#include <linux/bits.h> +#include <linux/types.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> + +#include <linux/tracepoint.h> + +TRACE_EVENT(cros_ec_motion_host_cmd, + TP_PROTO(struct ec_params_motion_sense *param, + struct ec_response_motion_sense *resp, + int retval), + TP_ARGS(param, resp, retval), + TP_STRUCT__entry(__field(uint8_t, cmd) + __field(uint8_t, sensor_id) + __field(uint32_t, data) + __field(int, retval) + __field(int32_t, ret) + ), + TP_fast_assign(__entry->cmd = param->cmd; + __entry->sensor_id = param->sensor_odr.sensor_num; + __entry->data = param->sensor_odr.data; + __entry->retval = retval; + __entry->ret = retval > 0 ? resp->sensor_odr.ret : -1; + ), + TP_printk("%s, id: %d, data: %u, result: %u, return: %d", + __print_symbolic(__entry->cmd, MOTIONSENSE_CMDS), + __entry->sensor_id, + __entry->data, + __entry->retval, + __entry->ret) +); + +#endif /* _CROS_EC_SENSORS_TRACE_H_ */ + +/* this part must be outside header guard */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/iio/common/cros_ec_sensors + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE cros_ec_sensors_trace + +#include <trace/define_trace.h> diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index 65f8a2b13cfd..22ea10eb48ae 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -190,7 +190,7 @@ static void ssp_enable_wdt_timer(struct ssp_data *data) static void ssp_disable_wdt_timer(struct ssp_data *data) { - del_timer_sync(&data->wdt_timer); + timer_delete_sync(&data->wdt_timer); cancel_work_sync(&data->work_wdt); } @@ -589,7 +589,7 @@ static void ssp_remove(struct spi_device *spi) free_irq(data->spi->irq, data); - del_timer_sync(&data->wdt_timer); + timer_delete_sync(&data->wdt_timer); cancel_work_sync(&data->work_wdt); mutex_destroy(&data->comm_lock); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 5690a37267d8..4811ea973125 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -296,6 +296,9 @@ config AD5770R config AD5791 tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver" depends on SPI + select SPI_OFFLOAD + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE help Say yes here to build support for Analog Devices AD5760, AD5780, AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c index 03e0864f5084..b8807e54fa05 100644 --- a/drivers/iio/dac/ad3552r-common.c +++ b/drivers/iio/dac/ad3552r-common.c @@ -11,23 +11,21 @@ #include "ad3552r.h" -const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2] = { +static const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2] = { [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 }, [AD3552R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 }, [AD3552R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 }, [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 }, [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = { -10000, 10000 } }; -EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, "IIO_AD3552R"); -const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = { +static const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = { [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = { 0, 2500 }, [AD3542R_CH_OUTPUT_RANGE_0__5V] = { 0, 5000 }, [AD3542R_CH_OUTPUT_RANGE_0__10V] = { 0, 10000 }, [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = { -5000, 5000 }, [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 } }; -EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, "IIO_AD3552R"); /* Gain * AD3552R_GAIN_SCALE */ static const s32 gains_scaling_table[] = { @@ -37,6 +35,50 @@ static const s32 gains_scaling_table[] = { [AD3552R_CH_GAIN_SCALING_0_125] = 125 }; +const struct ad3552r_model_data ad3541r_model_data = { + .model_name = "ad3541r", + .chip_id = AD3541R_ID, + .num_hw_channels = 1, + .ranges_table = ad3542r_ch_ranges, + .num_ranges = ARRAY_SIZE(ad3542r_ch_ranges), + .requires_output_range = true, + .num_spi_data_lanes = 2, +}; +EXPORT_SYMBOL_NS_GPL(ad3541r_model_data, "IIO_AD3552R"); + +const struct ad3552r_model_data ad3542r_model_data = { + .model_name = "ad3542r", + .chip_id = AD3542R_ID, + .num_hw_channels = 2, + .ranges_table = ad3542r_ch_ranges, + .num_ranges = ARRAY_SIZE(ad3542r_ch_ranges), + .requires_output_range = true, + .num_spi_data_lanes = 2, +}; +EXPORT_SYMBOL_NS_GPL(ad3542r_model_data, "IIO_AD3552R"); + +const struct ad3552r_model_data ad3551r_model_data = { + .model_name = "ad3551r", + .chip_id = AD3551R_ID, + .num_hw_channels = 1, + .ranges_table = ad3552r_ch_ranges, + .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges), + .requires_output_range = false, + .num_spi_data_lanes = 4, +}; +EXPORT_SYMBOL_NS_GPL(ad3551r_model_data, "IIO_AD3552R"); + +const struct ad3552r_model_data ad3552r_model_data = { + .model_name = "ad3552r", + .chip_id = AD3552R_ID, + .num_hw_channels = 2, + .ranges_table = ad3552r_ch_ranges, + .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges), + .requires_output_range = false, + .num_spi_data_lanes = 4, +}; +EXPORT_SYMBOL_NS_GPL(ad3552r_model_data, "IIO_AD3552R"); + u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs) { return FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1) | diff --git a/drivers/iio/dac/ad3552r-hs.c b/drivers/iio/dac/ad3552r-hs.c index 8974df625670..cd8dabb60c55 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -19,6 +19,31 @@ #include "ad3552r.h" #include "ad3552r-hs.h" +/* + * Important notes for register map access: + * ======================================== + * + * Register address space is divided in 2 regions, primary (config) and + * secondary (DAC). Primary region can only be accessed in simple SPI mode, + * with exception for ad355x models where setting QSPI pin high allows QSPI + * access to both the regions. + * + * Due to the fact that ad3541/2r do not implement QSPI, for proper device + * detection, HDL keeps "QSPI" pin level low at boot (see ad3552r manual, rev B + * table 7, pin 31, digital input). For this reason, actually the working mode + * between SPI, DSPI and QSPI must be set via software, configuring the target + * DAC appropriately, together with the backend API to configure the bus mode + * accordingly. + * + * Also, important to note that none of the three modes allow to read in DDR. + * + * In non-buffering operations, mode is set to simple SPI SDR for all primary + * and secondary region r/w accesses, to avoid to switch the mode each time DAC + * register is accessed (raw accesses, r/w), and to be able to dump registers + * content (possible as non DDR only). + * In buffering mode, driver sets best possible mode, D/QSPI and DDR. + */ + struct ad3552r_hs_state { const struct ad3552r_model_data *model_data; struct gpio_desc *reset_gpio; @@ -27,16 +52,26 @@ struct ad3552r_hs_state { bool single_channel; struct ad3552r_ch_data ch_data[AD3552R_MAX_CH]; struct ad3552r_hs_platform_data *data; + /* INTERFACE_CONFIG_D register cache, in DDR we cannot read values. */ + u32 config_d; }; -static int ad3552r_qspi_update_reg_bits(struct ad3552r_hs_state *st, - u32 reg, u32 mask, u32 val, - size_t xfer_size) +static int ad3552r_hs_reg_read(struct ad3552r_hs_state *st, u32 reg, u32 *val, + size_t xfer_size) +{ + /* No chip in the family supports DDR read. Informing of this. */ + WARN_ON_ONCE(st->config_d & AD3552R_MASK_SPI_CONFIG_DDR); + + return st->data->bus_reg_read(st->back, reg, val, xfer_size); +} + +static int ad3552r_hs_update_reg_bits(struct ad3552r_hs_state *st, u32 reg, + u32 mask, u32 val, size_t xfer_size) { u32 rval; int ret; - ret = st->data->bus_reg_read(st->back, reg, &rval, xfer_size); + ret = ad3552r_hs_reg_read(st, reg, &rval, xfer_size); if (ret) return ret; @@ -56,16 +91,20 @@ static int ad3552r_hs_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: /* - * Using 4 lanes (QSPI), then using 2 as DDR mode is - * considered always on (considering buffering mode always). + * Using a "num_spi_data_lanes" variable since ad3541/2 have + * only DSPI interface, while ad355x is QSPI. Then using 2 as + * DDR mode is considered always on (considering buffering + * mode always). */ *val = DIV_ROUND_CLOSEST(st->data->bus_sample_data_clock_hz * - 4 * 2, chan->scan_type.realbits); + st->model_data->num_spi_data_lanes * 2, + chan->scan_type.realbits); return IIO_VAL_INT; case IIO_CHAN_INFO_RAW: - ret = st->data->bus_reg_read(st->back, + /* For RAW accesses, stay always in simple-spi. */ + ret = ad3552r_hs_reg_read(st, AD3552R_REG_ADDR_CH_DAC_16B(chan->channel), val, 2); if (ret) @@ -90,20 +129,60 @@ static int ad3552r_hs_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct ad3552r_hs_state *st = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - return st->data->bus_reg_write(st->back, - AD3552R_REG_ADDR_CH_DAC_16B(chan->channel), - val, 2); - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + /* For RAW accesses, stay always in simple-spi. */ + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_CH_DAC_16B(chan->channel), + val, 2); + + iio_device_release_direct(indio_dev); + return ret; default: return -EINVAL; } } +static int ad3552r_hs_set_bus_io_mode_hs(struct ad3552r_hs_state *st) +{ + int bus_mode; + + if (st->model_data->num_spi_data_lanes == 4) + bus_mode = AD3552R_IO_MODE_QSPI; + else + bus_mode = AD3552R_IO_MODE_DSPI; + + return st->data->bus_set_io_mode(st->back, bus_mode); +} + +static int ad3552r_hs_set_target_io_mode_hs(struct ad3552r_hs_state *st) +{ + u32 mode_target; + + /* + * Best access for secondary reg area, QSPI where possible, + * else as DSPI. + */ + if (st->model_data->num_spi_data_lanes == 4) + mode_target = AD3552R_QUAD_SPI; + else + mode_target = AD3552R_DUAL_SPI; + + /* + * Better to not use update here, since generally it is already + * set as DDR mode, and it's not possible to read in DDR mode. + */ + return st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_TRANSFER_REGISTER, + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE, + mode_target) | + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1); +} + static int ad3552r_hs_buffer_postenable(struct iio_dev *indio_dev) { struct ad3552r_hs_state *st = iio_priv(indio_dev); @@ -132,48 +211,111 @@ static int ad3552r_hs_buffer_postenable(struct iio_dev *indio_dev) return -EINVAL; } - ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_STREAM_MODE, - loop_len, 1); + /* + * With ad3541/2r support, QSPI pin is held low at reset from HDL, + * streaming start sequence must respect strictly the order below. + */ + + /* Primary region access, set streaming mode (now in SPI + SDR). */ + ret = ad3552r_hs_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST, 0, 1); if (ret) return ret; - /* Inform DAC chip to switch into DDR mode */ - ret = ad3552r_qspi_update_reg_bits(st, - AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SPI_CONFIG_DDR, - AD3552R_MASK_SPI_CONFIG_DDR, 1); + /* + * Set target loop len, keeping the value: streaming writes at address + * 0x2c or 0x2a, in descending loop (2 or 4 bytes), keeping loop len + * value so that it's not cleared hereafter when _CS is deasserted. + */ + ret = ad3552r_hs_update_reg_bits(st, AD3552R_REG_ADDR_TRANSFER_REGISTER, + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, + 1); if (ret) - return ret; + goto exit_err_streaming; + + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_STREAM_MODE, + loop_len, 1); + if (ret) + goto exit_err_streaming; + + st->config_d |= AD3552R_MASK_SPI_CONFIG_DDR; + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + st->config_d, 1); + if (ret) + goto exit_err_streaming; - /* Inform DAC IP to go for DDR mode from now on */ ret = iio_backend_ddr_enable(st->back); - if (ret) { - dev_err(st->dev, "could not set DDR mode, not streaming"); - goto exit_err; - } + if (ret) + goto exit_err_ddr_mode_target; + + /* + * From here onward mode is DDR, so reading any register is not possible + * anymore, including calling "ad3552r_hs_update_reg_bits" function. + */ + /* Set target to best high speed mode (D or QSPI). */ + ret = ad3552r_hs_set_target_io_mode_hs(st); + if (ret) + goto exit_err_ddr_mode; + + /* Set bus to best high speed mode (D or QSPI). */ + ret = ad3552r_hs_set_bus_io_mode_hs(st); + if (ret) + goto exit_err_bus_mode_target; + + /* + * Backend setup must be done now only, or related register values will + * be disrupted by previous bus accesses. + */ ret = iio_backend_data_transfer_addr(st->back, val); if (ret) - goto exit_err; + goto exit_err_bus_mode_target; ret = iio_backend_data_format_set(st->back, 0, &fmt); if (ret) - goto exit_err; + goto exit_err_bus_mode_target; ret = iio_backend_data_stream_enable(st->back); if (ret) - goto exit_err; + goto exit_err_bus_mode_target; return 0; -exit_err: - ad3552r_qspi_update_reg_bits(st, - AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SPI_CONFIG_DDR, - 0, 1); +exit_err_bus_mode_target: + /* Back to simple SPI, not using update to avoid read. */ + st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_TRANSFER_REGISTER, + FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE, + AD3552R_SPI) | + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1); + + /* + * Back bus to simple SPI, this must be executed together with above + * target mode unwind, and can be done only after it. + */ + st->data->bus_set_io_mode(st->back, AD3552R_IO_MODE_SPI); +exit_err_ddr_mode: iio_backend_ddr_disable(st->back); +exit_err_ddr_mode_target: + /* + * Back to SDR. In DDR we cannot read, whatever the mode is, so not + * using update. + */ + st->config_d &= ~AD3552R_MASK_SPI_CONFIG_DDR; + st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + st->config_d, 1); + +exit_err_streaming: + /* Back to single instruction mode, disabling loop. */ + st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST | + AD3552R_MASK_SHORT_INSTRUCTION, 1); + return ret; } @@ -186,11 +328,22 @@ static int ad3552r_hs_buffer_predisable(struct iio_dev *indio_dev) if (ret) return ret; - /* Inform DAC to set in SDR mode */ - ret = ad3552r_qspi_update_reg_bits(st, - AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SPI_CONFIG_DDR, - 0, 1); + /* + * Set us to simple SPI, even if still in ddr, so to be able to write + * in primary region. + */ + ret = st->data->bus_set_io_mode(st->back, AD3552R_IO_MODE_SPI); + if (ret) + return ret; + + /* + * Back to SDR (in DDR we cannot read, whatever the mode is, so not + * using update). + */ + st->config_d &= ~AD3552R_MASK_SPI_CONFIG_DDR; + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + st->config_d, 1); if (ret) return ret; @@ -198,6 +351,24 @@ static int ad3552r_hs_buffer_predisable(struct iio_dev *indio_dev) if (ret) return ret; + /* + * Back to simple SPI for secondary region too now, so to be able to + * dump/read registers there too if needed. + */ + ret = ad3552r_hs_update_reg_bits(st, AD3552R_REG_ADDR_TRANSFER_REGISTER, + AD3552R_MASK_MULTI_IO_MODE, + AD3552R_SPI, 1); + if (ret) + return ret; + + /* Back to single instruction mode, disabling loop. */ + ret = ad3552r_hs_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST, + AD3552R_MASK_SINGLE_INST, 1); + if (ret) + return ret; + return 0; } @@ -211,10 +382,10 @@ static inline int ad3552r_hs_set_output_range(struct ad3552r_hs_state *st, else val = FIELD_PREP(AD3552R_MASK_CH1_RANGE, mode); - return ad3552r_qspi_update_reg_bits(st, - AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, - AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch), - val, 1); + return ad3552r_hs_update_reg_bits(st, + AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch), + val, 1); } static int ad3552r_hs_reset(struct ad3552r_hs_state *st) @@ -230,10 +401,10 @@ static int ad3552r_hs_reset(struct ad3552r_hs_state *st) fsleep(10); gpiod_set_value_cansleep(st->reset_gpio, 0); } else { - ret = ad3552r_qspi_update_reg_bits(st, - AD3552R_REG_ADDR_INTERFACE_CONFIG_A, - AD3552R_MASK_SOFTWARE_RESET, - AD3552R_MASK_SOFTWARE_RESET, 1); + ret = ad3552r_hs_update_reg_bits(st, + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_SOFTWARE_RESET, + AD3552R_MASK_SOFTWARE_RESET, 1); if (ret) return ret; } @@ -304,30 +475,49 @@ static int ad3552r_hs_setup(struct ad3552r_hs_state *st) if (ret) return ret; + /* HDL starts with DDR enabled, disabling it. */ ret = iio_backend_ddr_disable(st->back); if (ret) return ret; + ret = st->data->bus_reg_write(st->back, + AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST | + AD3552R_MASK_SHORT_INSTRUCTION, 1); + if (ret) + return ret; + ret = ad3552r_hs_scratch_pad_test(st); if (ret) return ret; - ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_L, - &val, 1); + /* + * Caching config_d, needed to restore it after streaming, + * and also, to detect possible DDR read, that's not allowed. + */ + ret = st->data->bus_reg_read(st->back, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + &st->config_d, 1); + if (ret) + return ret; + + ret = ad3552r_hs_reg_read(st, AD3552R_REG_ADDR_PRODUCT_ID_L, &val, 1); if (ret) return ret; id = val; - ret = st->data->bus_reg_read(st->back, AD3552R_REG_ADDR_PRODUCT_ID_H, - &val, 1); + ret = ad3552r_hs_reg_read(st, AD3552R_REG_ADDR_PRODUCT_ID_H, &val, 1); if (ret) return ret; id |= val << 8; if (id != st->model_data->chip_id) - dev_info(st->dev, "Chip ID error. Expected 0x%x, Read 0x%x\n", - AD3552R_ID, id); + dev_warn(st->dev, + "chip ID mismatch, detected 0x%x but expected 0x%x\n", + id, st->model_data->chip_id); + + dev_dbg(st->dev, "chip id %s detected", st->model_data->model_name); /* Clear reset error flag, see ad3552r manual, rev B table 38. */ ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_ERR_STATUS, @@ -341,14 +531,6 @@ static int ad3552r_hs_setup(struct ad3552r_hs_state *st) if (ret) return ret; - ret = st->data->bus_reg_write(st->back, - AD3552R_REG_ADDR_TRANSFER_REGISTER, - FIELD_PREP(AD3552R_MASK_MULTI_IO_MODE, - AD3552R_QUAD_SPI) | - AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, 1); - if (ret) - return ret; - ret = iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL); if (ret) return ret; @@ -363,19 +545,21 @@ static int ad3552r_hs_setup(struct ad3552r_hs_state *st) val = ret; - ret = ad3552r_qspi_update_reg_bits(st, - AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, - AD3552R_MASK_REFERENCE_VOLTAGE_SEL, - val, 1); + ret = ad3552r_hs_update_reg_bits(st, + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, + AD3552R_MASK_REFERENCE_VOLTAGE_SEL, + val, 1); if (ret) return ret; ret = ad3552r_get_drive_strength(st->dev, &val); if (!ret) { - ret = ad3552r_qspi_update_reg_bits(st, + st->config_d |= + FIELD_PREP(AD3552R_MASK_SDO_DRIVE_STRENGTH, val); + + ret = st->data->bus_reg_write(st->back, AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SDO_DRIVE_STRENGTH, - val, 1); + st->config_d, 1); if (ret) return ret; } @@ -504,15 +688,10 @@ static int ad3552r_hs_probe(struct platform_device *pdev) return devm_iio_device_register(&pdev->dev, indio_dev); } -static const struct ad3552r_model_data ad3552r_model_data = { - .model_name = "ad3552r", - .chip_id = AD3552R_ID, - .num_hw_channels = 2, - .ranges_table = ad3552r_ch_ranges, - .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges), -}; - static const struct of_device_id ad3552r_hs_of_id[] = { + { .compatible = "adi,ad3541r", .data = &ad3541r_model_data }, + { .compatible = "adi,ad3542r", .data = &ad3542r_model_data }, + { .compatible = "adi,ad3551r", .data = &ad3551r_model_data }, { .compatible = "adi,ad3552r", .data = &ad3552r_model_data }, { } }; diff --git a/drivers/iio/dac/ad3552r-hs.h b/drivers/iio/dac/ad3552r-hs.h index 724261d38dea..4a9e35234124 100644 --- a/drivers/iio/dac/ad3552r-hs.h +++ b/drivers/iio/dac/ad3552r-hs.h @@ -8,11 +8,19 @@ struct iio_backend; +enum ad3552r_io_mode { + AD3552R_IO_MODE_SPI, + AD3552R_IO_MODE_DSPI, + AD3552R_IO_MODE_QSPI, +}; + struct ad3552r_hs_platform_data { int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val, size_t data_size); int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val, size_t data_size); + int (*bus_set_io_mode)(struct iio_backend *back, + enum ad3552r_io_mode mode); u32 bus_sample_data_clock_hz; }; diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index 7944f5c1d264..a44b163f3183 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -655,42 +655,6 @@ static int ad3552r_probe(struct spi_device *spi) return devm_iio_device_register(&spi->dev, indio_dev); } -static const struct ad3552r_model_data ad3541r_model_data = { - .model_name = "ad3541r", - .chip_id = AD3541R_ID, - .num_hw_channels = 1, - .ranges_table = ad3542r_ch_ranges, - .num_ranges = ARRAY_SIZE(ad3542r_ch_ranges), - .requires_output_range = true, -}; - -static const struct ad3552r_model_data ad3542r_model_data = { - .model_name = "ad3542r", - .chip_id = AD3542R_ID, - .num_hw_channels = 2, - .ranges_table = ad3542r_ch_ranges, - .num_ranges = ARRAY_SIZE(ad3542r_ch_ranges), - .requires_output_range = true, -}; - -static const struct ad3552r_model_data ad3551r_model_data = { - .model_name = "ad3551r", - .chip_id = AD3551R_ID, - .num_hw_channels = 1, - .ranges_table = ad3552r_ch_ranges, - .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges), - .requires_output_range = false, -}; - -static const struct ad3552r_model_data ad3552r_model_data = { - .model_name = "ad3552r", - .chip_id = AD3552R_ID, - .num_hw_channels = 2, - .ranges_table = ad3552r_ch_ranges, - .num_ranges = ARRAY_SIZE(ad3552r_ch_ranges), - .requires_output_range = false, -}; - static const struct spi_device_id ad3552r_id[] = { { .name = "ad3541r", diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h index 4b5581039ae9..768fa264d39e 100644 --- a/drivers/iio/dac/ad3552r.h +++ b/drivers/iio/dac/ad3552r.h @@ -132,10 +132,14 @@ #define AD3552R_MAX_RANGES 5 #define AD3542R_MAX_RANGES 5 +#define AD3552R_SPI 0 +#define AD3552R_DUAL_SPI 1 #define AD3552R_QUAD_SPI 2 -extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2]; -extern const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2]; +extern const struct ad3552r_model_data ad3541r_model_data; +extern const struct ad3552r_model_data ad3542r_model_data; +extern const struct ad3552r_model_data ad3551r_model_data; +extern const struct ad3552r_model_data ad3552r_model_data; enum ad3552r_id { AD3541R_ID = 0x400b, @@ -151,6 +155,7 @@ struct ad3552r_model_data { const s32 (*ranges_table)[2]; int num_ranges; bool requires_output_range; + int num_spi_data_lanes; }; struct ad3552r_ch_data { diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 57374f78f6b8..07848be3f8d5 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -6,21 +6,24 @@ * Copyright 2011 Analog Devices Inc. */ -#include <linux/interrupt.h> -#include <linux/fs.h> -#include <linux/device.h> +#include <linux/bitops.h> #include <linux/delay.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/spi/offload/consumer.h> +#include <linux/spi/spi.h> #include <linux/sysfs.h> -#include <linux/regulator/consumer.h> -#include <linux/module.h> -#include <linux/bitops.h> +#include <linux/units.h> +#include <linux/iio/buffer-dmaengine.h> +#include <linux/iio/dac/ad5791.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/iio/dac/ad5791.h> #define AD5791_DAC_MASK GENMASK(19, 0) @@ -64,11 +67,13 @@ * struct ad5791_chip_info - chip specific information * @name: name of the dac chip * @channel: channel specification + * @channel_offload: channel specification for offload * @get_lin_comp: function pointer to the device specific function */ struct ad5791_chip_info { const char *name; const struct iio_chan_spec channel; + const struct iio_chan_spec channel_offload; int (*get_lin_comp)(unsigned int span); }; @@ -81,6 +86,11 @@ struct ad5791_chip_info { * @gpio_clear: clear gpio * @gpio_ldac: load dac gpio * @chip_info: chip model specific constants + * @offload_msg: spi message used for offload + * @offload_xfer: spi transfer used for offload + * @offload: offload device + * @offload_trigger: offload trigger + * @offload_trigger_hz: offload sample rate * @vref_mv: actual reference voltage used * @vref_neg_mv: voltage of the negative supply * @ctrl: control register cache @@ -96,6 +106,11 @@ struct ad5791_state { struct gpio_desc *gpio_clear; struct gpio_desc *gpio_ldac; const struct ad5791_chip_info *chip_info; + struct spi_message offload_msg; + struct spi_transfer offload_xfer; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + unsigned int offload_trigger_hz; unsigned short vref_mv; unsigned int vref_neg_mv; unsigned ctrl; @@ -232,6 +247,25 @@ static int ad5780_get_lin_comp(unsigned int span) return AD5780_LINCOMP_10_20; } +static int ad5791_set_sample_freq(struct ad5791_state *st, int val) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = val, + }, + }; + int ret; + + ret = spi_offload_trigger_validate(st->offload_trigger, &config); + if (ret) + return ret; + + st->offload_trigger_hz = config.periodic.frequency_hz; + + return 0; +} + static int ad5791_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -259,6 +293,9 @@ static int ad5791_read_raw(struct iio_dev *indio_dev, do_div(val64, st->vref_mv); *val = -val64; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->offload_trigger_hz; + return IIO_VAL_INT; default: return -EINVAL; } @@ -294,7 +331,25 @@ static const struct ad5791_chip_info _name##_chip_info = { \ .scan_type = { \ .sign = 'u', \ .realbits = (bits), \ - .storagebits = 24, \ + .storagebits = 32, \ + .shift = (_shift), \ + }, \ + .ext_info = ad5791_ext_info, \ + }, \ + .channel_offload = { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .address = AD5791_ADDR_DAC0, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 32, \ .shift = (_shift), \ }, \ .ext_info = ad5791_ext_info, \ @@ -322,14 +377,106 @@ static int ad5791_write_raw(struct iio_dev *indio_dev, return ad5791_spi_write(st, chan->address, val); + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1) + return -EINVAL; + return ad5791_set_sample_freq(st, val); default: return -EINVAL; } } +static int ad5791_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int ad5791_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad5791_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = st->offload_trigger_hz, + }, + }; + + if (st->pwr_down) + return -EINVAL; + + return spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); +} + +static int ad5791_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad5791_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + return 0; +} + +static const struct iio_buffer_setup_ops ad5791_buffer_setup_ops = { + .preenable = &ad5791_buffer_preenable, + .postdisable = &ad5791_buffer_postdisable, +}; + +static int ad5791_offload_setup(struct iio_dev *indio_dev) +{ + struct ad5791_state *st = iio_priv(indio_dev); + struct spi_device *spi = st->spi; + struct dma_chan *tx_dma; + int ret; + + st->offload_trigger = devm_spi_offload_trigger_get(&spi->dev, + st->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(&spi->dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + + ret = ad5791_set_sample_freq(st, 1 * MEGA); + if (ret) + return dev_err_probe(&spi->dev, ret, + "failed to init sample rate\n"); + + tx_dma = devm_spi_offload_tx_stream_request_dma_chan(&spi->dev, + st->offload); + if (IS_ERR(tx_dma)) + return dev_err_probe(&spi->dev, PTR_ERR(tx_dma), + "failed to get offload TX DMA\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(&spi->dev, + indio_dev, tx_dma, IIO_BUFFER_DIRECTION_OUT); + if (ret) + return ret; + + st->offload_xfer.len = 4; + st->offload_xfer.bits_per_word = 24; + st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_TX_STREAM; + + spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1); + st->offload_msg.offload = st->offload; + + return devm_spi_optimize_message(&spi->dev, st->spi, &st->offload_msg); +} + static const struct iio_info ad5791_info = { .read_raw = &ad5791_read_raw, .write_raw = &ad5791_write_raw, + .write_raw_get_fmt = &ad5791_write_raw_get_fmt, +}; + +static const struct spi_offload_config ad5791_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_TX_STREAM_DMA, }; static int ad5791_probe(struct spi_device *spi) @@ -416,6 +563,21 @@ static int ad5791_probe(struct spi_device *spi) indio_dev->channels = &st->chip_info->channel; indio_dev->num_channels = 1; indio_dev->name = st->chip_info->name; + + st->offload = devm_spi_offload_get(&spi->dev, spi, &ad5791_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, "failed to get offload\n"); + + if (ret != -ENODEV) { + indio_dev->channels = &st->chip_info->channel_offload; + indio_dev->setup_ops = &ad5791_buffer_setup_ops; + ret = ad5791_offload_setup(indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "fail to setup offload\n"); + } + return devm_iio_device_register(&spi->dev, indio_dev); } @@ -452,3 +614,4 @@ module_spi_driver(ad5791_driver); MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c index 535ee3105af6..6e45686902dd 100644 --- a/drivers/iio/dac/ad8460.c +++ b/drivers/iio/dac/ad8460.c @@ -264,9 +264,12 @@ static ssize_t ad8460_write_toggle_en(struct iio_dev *indio_dev, uintptr_t priva if (ret) return ret; - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return ad8460_enable_apg_mode(state, toggle_en); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad8460_enable_apg_mode(state, toggle_en); + iio_device_release_direct(indio_dev); + return ret; } static ssize_t ad8460_read_powerdown(struct iio_dev *indio_dev, uintptr_t private, @@ -421,14 +424,17 @@ static int ad8460_write_raw(struct iio_dev *indio_dev, long mask) { struct ad8460_state *state = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_VOLTAGE: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return ad8460_set_sample(state, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad8460_set_sample(state, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CURRENT: return regmap_write(state->regmap, AD8460_CTRL_REG(0x04), FIELD_PREP(AD8460_QUIESCENT_CURRENT_MSK, val)); diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index b143f7ed6847..892d770aec69 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -64,7 +64,7 @@ #define AXI_DAC_UI_STATUS_IF_BUSY BIT(4) #define AXI_DAC_CUSTOM_CTRL_REG 0x008C #define AXI_DAC_CUSTOM_CTRL_ADDRESS GENMASK(31, 24) -#define AXI_DAC_CUSTOM_CTRL_SYNCED_TRANSFER BIT(2) +#define AXI_DAC_CUSTOM_CTRL_MULTI_IO_MODE GENMASK(3, 2) #define AXI_DAC_CUSTOM_CTRL_STREAM BIT(1) #define AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA BIT(0) @@ -168,7 +168,7 @@ static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back, static void axi_dac_free_buffer(struct iio_backend *back, struct iio_buffer *buffer) { - iio_dmaengine_buffer_free(buffer); + iio_dmaengine_buffer_teardown(buffer); } enum { @@ -585,6 +585,14 @@ static int axi_dac_ddr_disable(struct iio_backend *back) static int axi_dac_data_stream_enable(struct iio_backend *back) { struct axi_dac_state *st = iio_backend_get_priv(back); + int ret, val; + + ret = regmap_read_poll_timeout(st->regmap, + AXI_DAC_UI_STATUS_REG, val, + FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, val) == 0, + 10, 100 * KILO); + if (ret) + return ret; return regmap_set_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, AXI_DAC_CUSTOM_CTRL_STREAM_ENABLE); @@ -714,6 +722,28 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val, return regmap_read(st->regmap, AXI_DAC_CUSTOM_RD_REG, val); } +static int axi_dac_bus_set_io_mode(struct iio_backend *back, + enum ad3552r_io_mode mode) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + int ival, ret; + + if (mode > AD3552R_IO_MODE_QSPI) + return -EINVAL; + + guard(mutex)(&st->lock); + + ret = regmap_update_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, + AXI_DAC_CUSTOM_CTRL_MULTI_IO_MODE, + FIELD_PREP(AXI_DAC_CUSTOM_CTRL_MULTI_IO_MODE, mode)); + if (ret) + return ret; + + return regmap_read_poll_timeout(st->regmap, AXI_DAC_UI_STATUS_REG, ival, + FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0, 10, + 100 * KILO); +} + static void axi_dac_child_remove(void *data) { platform_device_unregister(data); @@ -725,6 +755,7 @@ static int axi_dac_create_platform_device(struct axi_dac_state *st, struct ad3552r_hs_platform_data pdata = { .bus_reg_read = axi_dac_bus_reg_read, .bus_reg_write = axi_dac_bus_reg_write, + .bus_set_io_mode = axi_dac_bus_set_io_mode, .bus_sample_data_clock_hz = st->dac_clk_rate, }; struct platform_device_info pi = { diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c index 09efacaf8f78..8575d4a08963 100644 --- a/drivers/iio/dummy/iio_simple_dummy.c +++ b/drivers/iio/dummy/iio_simple_dummy.c @@ -267,6 +267,65 @@ static const struct iio_chan_spec iio_dummy_channels[] = { }, }; +static int __iio_dummy_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) { + /* Set integer part to cached value */ + *val = st->dac_val; + return IIO_VAL_INT; + } else if (chan->differential) { + if (chan->channel == 1) + *val = st->differential_adc_val[0]; + else + *val = st->differential_adc_val[1]; + return IIO_VAL_INT; + } else { + *val = st->single_ended_adc_val; + return IIO_VAL_INT; + } + + case IIO_ACCEL: + *val = st->accel_val; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int __iio_dummy_read_processed(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + switch (chan->type) { + case IIO_STEPS: + *val = st->steps; + return IIO_VAL_INT; + case IIO_ACTIVITY: + switch (chan->channel2) { + case IIO_MOD_RUNNING: + *val = st->activity_running; + return IIO_VAL_INT; + case IIO_MOD_WALKING: + *val = st->activity_walking; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + /** * iio_dummy_read_raw() - data read function. * @indio_dev: the struct iio_dev associated with this device instance @@ -283,59 +342,21 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev, long mask) { struct iio_dummy_state *st = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: /* magic value - channel value read */ - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - guard(mutex)(&st->lock); - switch (chan->type) { - case IIO_VOLTAGE: - if (chan->output) { - /* Set integer part to cached value */ - *val = st->dac_val; - return IIO_VAL_INT; - } else if (chan->differential) { - if (chan->channel == 1) - *val = st->differential_adc_val[0]; - else - *val = st->differential_adc_val[1]; - return IIO_VAL_INT; - } else { - *val = st->single_ended_adc_val; - return IIO_VAL_INT; - } - - case IIO_ACCEL: - *val = st->accel_val; - return IIO_VAL_INT; - default: - return -EINVAL; - } - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __iio_dummy_read_raw(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_PROCESSED: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - guard(mutex)(&st->lock); - switch (chan->type) { - case IIO_STEPS: - *val = st->steps; - return IIO_VAL_INT; - case IIO_ACTIVITY: - switch (chan->channel2) { - case IIO_MOD_RUNNING: - *val = st->activity_running; - return IIO_VAL_INT; - case IIO_MOD_WALKING: - *val = st->activity_walking; - return IIO_VAL_INT; - default: - return -EINVAL; - } - default: - return -EINVAL; - } - } - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __iio_dummy_read_processed(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_OFFSET: /* only single ended adc -> 7 */ *val = 7; diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index d752507e0c98..9a84e81787b1 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -42,6 +42,12 @@ #define ADF4371_MOD2WORD_MSK GENMASK(5, 0) #define ADF4371_MOD2WORD(x) FIELD_PREP(ADF4371_MOD2WORD_MSK, x) +/* ADF4371_REG22 */ +#define ADF4371_REFIN_MODE_MASK BIT(6) +#define ADF4371_REFIN_MODE(x) FIELD_PREP(ADF4371_REFIN_MODE_MASK, x) +#define ADF4371_REF_DOUB_MASK BIT(5) +#define ADF4371_REF_DOUB(x) FIELD_PREP(ADF4371_REF_DOUB_MASK, x)\ + /* ADF4371_REG24 */ #define ADF4371_RF_DIV_SEL_MSK GENMASK(6, 4) #define ADF4371_RF_DIV_SEL(x) FIELD_PREP(ADF4371_RF_DIV_SEL_MSK, x) @@ -70,6 +76,10 @@ #define ADF4371_MAX_FREQ_PFD 250000000UL /* Hz */ #define ADF4371_MAX_FREQ_REFIN 600000000UL /* Hz */ +#define ADF4371_MAX_FREQ_REFIN_SE 500000000UL /* Hz */ + +#define ADF4371_MIN_CLKIN_DOUB_FREQ 10000000ULL /* Hz */ +#define ADF4371_MAX_CLKIN_DOUB_FREQ 125000000ULL /* Hz */ /* MOD1 is a 24-bit primary modulus with fixed value of 2^25 */ #define ADF4371_MODULUS1 33554432ULL @@ -176,6 +186,7 @@ struct adf4371_state { unsigned int mod2; unsigned int rf_div_sel; unsigned int ref_div_factor; + bool ref_diff_en; u8 buf[10] __aligned(IIO_DMA_MINALIGN); }; @@ -477,7 +488,7 @@ static const struct iio_info adf4371_info = { static int adf4371_setup(struct adf4371_state *st) { unsigned int synth_timeout = 2, timeout = 1, vco_alc_timeout = 1; - unsigned int vco_band_div, tmp; + unsigned int vco_band_div, tmp, ref_doubler_en = 0; int ret; /* Perform a software reset */ @@ -505,6 +516,23 @@ static int adf4371_setup(struct adf4371_state *st) ADF4371_ADDR_ASC(1) | ADF4371_ADDR_ASC_R(1)); if (ret < 0) return ret; + + if ((st->ref_diff_en && st->clkin_freq > ADF4371_MAX_FREQ_REFIN) || + (!st->ref_diff_en && st->clkin_freq > ADF4371_MAX_FREQ_REFIN_SE)) + return -EINVAL; + + if (st->clkin_freq < ADF4371_MAX_CLKIN_DOUB_FREQ && + st->clkin_freq > ADF4371_MIN_CLKIN_DOUB_FREQ) + ref_doubler_en = 1; + + ret = regmap_update_bits(st->regmap, ADF4371_REG(0x22), + ADF4371_REF_DOUB_MASK | + ADF4371_REFIN_MODE_MASK, + ADF4371_REF_DOUB(ref_doubler_en) | + ADF4371_REFIN_MODE(st->ref_diff_en)); + if (ret < 0) + return ret; + /* * Calculate and maximize PFD frequency * fPFD = REFIN × ((1 + D)/(R × (1 + T))) @@ -514,7 +542,8 @@ static int adf4371_setup(struct adf4371_state *st) */ do { st->ref_div_factor++; - st->fpfd = st->clkin_freq / st->ref_div_factor; + st->fpfd = st->clkin_freq * (1 + ref_doubler_en) / + st->ref_div_factor; } while (st->fpfd > ADF4371_MAX_FREQ_PFD); /* Calculate Timeouts */ @@ -574,10 +603,16 @@ static int adf4371_probe(struct spi_device *spi) indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; + st->ref_diff_en = false; + clkin = devm_clk_get_enabled(&spi->dev, "clkin"); - if (IS_ERR(clkin)) - return dev_err_probe(&spi->dev, PTR_ERR(clkin), - "Failed to get clkin\n"); + if (IS_ERR(clkin)) { + clkin = devm_clk_get_enabled(&spi->dev, "clkin-diff"); + if (IS_ERR(clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(clkin), + "Failed to get clkin/clkin-diff\n"); + st->ref_diff_en = true; + } st->clkin_freq = clk_get_rate(clkin); diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c index 9c5d7e8ee99c..e6caab49f98a 100644 --- a/drivers/iio/gyro/bmg160_i2c.c +++ b/drivers/iio/gyro/bmg160_i2c.c @@ -58,6 +58,7 @@ MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id); static const struct of_device_id bmg160_of_match[] = { { .compatible = "bosch,bmg160" }, { .compatible = "bosch,bmi055_gyro" }, + { .compatible = "bosch,bmi088_gyro" }, { } }; diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c index fc2e453527b9..ac04b3b1b554 100644 --- a/drivers/iio/gyro/bmg160_spi.c +++ b/drivers/iio/gyro/bmg160_spi.c @@ -41,9 +41,19 @@ static const struct spi_device_id bmg160_spi_id[] = { MODULE_DEVICE_TABLE(spi, bmg160_spi_id); +static const struct of_device_id bmg160_of_match[] = { + { .compatible = "bosch,bmg160" }, + { .compatible = "bosch,bmi055_gyro" }, + { .compatible = "bosch,bmi088_gyro" }, + { } +}; + +MODULE_DEVICE_TABLE(of, bmg160_of_match); + static struct spi_driver bmg160_spi_driver = { .driver = { .name = "bmg160_spi", + .of_match_table = bmg160_of_match, .pm = &bmg160_pm_ops, }, .probe = bmg160_spi_probe, diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index c97e25448772..48c59d09eea7 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/printk.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/sysfs.h> #include <linux/io.h> #include <linux/mod_devicetable.h> @@ -99,7 +100,7 @@ static void dht11_edges_print(struct dht11 *dht11) for (i = 1; i < dht11->num_edges; ++i) { dev_dbg(dht11->dev, "%d: %lld ns %s\n", i, dht11->edges[i].ts - dht11->edges[i - 1].ts, - dht11->edges[i - 1].value ? "high" : "low"); + str_high_low(dht11->edges[i - 1].value)); } } #endif /* CONFIG_DYNAMIC_DEBUG */ diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index ca0efecb5b5c..15612f0f189b 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -52,6 +52,19 @@ config ADIS16480 Say yes here to build support for Analog Devices ADIS16375, ADIS16480, ADIS16485, ADIS16488 inertial sensors. +config ADIS16550 + tristate "Analog Devices ADIS16550 and similar IMU driver" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + select CRC32 + help + Say yes here to build support for Analog Devices ADIS16550 inertial + sensor containing triaxis gyroscope and triaxis accelerometer. + + To compile this driver as a module, choose M here: the module will be + called adis16550. + source "drivers/iio/imu/bmi160/Kconfig" source "drivers/iio/imu/bmi270/Kconfig" source "drivers/iio/imu/bmi323/Kconfig" diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 04c77c2c4df8..e901aea498d3 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_ADIS16400) += adis16400.o obj-$(CONFIG_ADIS16460) += adis16460.o obj-$(CONFIG_ADIS16475) += adis16475.o obj-$(CONFIG_ADIS16480) += adis16480.o +obj-$(CONFIG_ADIS16550) += adis16550.o adis_lib-y += adis.o adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_trigger.o diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 494171844812..0ea072a4c966 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -223,13 +223,13 @@ int __adis_update_bits_base(struct adis *adis, unsigned int reg, const u32 mask, int ret; u32 __val; - ret = __adis_read_reg(adis, reg, &__val, size); + ret = adis->ops->read(adis, reg, &__val, size); if (ret) return ret; __val = (__val & ~mask) | (val & mask); - return __adis_write_reg(adis, reg, __val, size); + return adis->ops->write(adis, reg, __val, size); } EXPORT_SYMBOL_NS_GPL(__adis_update_bits_base, "IIO_ADISLIB"); @@ -304,11 +304,20 @@ EXPORT_SYMBOL_NS(__adis_enable_irq, "IIO_ADISLIB"); */ int __adis_check_status(struct adis *adis) { - u16 status; + unsigned int status; + int diag_stat_bits; + u16 status_16 = 0; int ret; int i; - ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, &status); + if (adis->data->diag_stat_size) { + ret = adis->ops->read(adis, adis->data->diag_stat_reg, &status, + adis->data->diag_stat_size); + } else { + ret = __adis_read_reg_16(adis, adis->data->diag_stat_reg, + &status_16); + status = status_16; + } if (ret) return ret; @@ -317,7 +326,10 @@ int __adis_check_status(struct adis *adis) if (status == 0) return 0; - for (i = 0; i < 16; ++i) { + diag_stat_bits = BITS_PER_BYTE * (adis->data->diag_stat_size ? + adis->data->diag_stat_size : 2); + + for (i = 0; i < diag_stat_bits; ++i) { if (status & BIT(i)) { dev_err(&adis->spi->dev, "%s.\n", adis->data->status_error_msgs[i]); @@ -468,7 +480,7 @@ int adis_single_conversion(struct iio_dev *indio_dev, guard(mutex)(&adis->state_lock); - ret = __adis_read_reg(adis, chan->address, &uval, + ret = adis->ops->read(adis, chan->address, &uval, chan->scan_type.storagebits / 8); if (ret) return ret; @@ -488,6 +500,12 @@ int adis_single_conversion(struct iio_dev *indio_dev, } EXPORT_SYMBOL_NS_GPL(adis_single_conversion, "IIO_ADISLIB"); +static const struct adis_ops adis_default_ops = { + .read = __adis_read_reg, + .write = __adis_write_reg, + .reset = __adis_reset, +}; + /** * adis_init() - Initialize adis device structure * @adis: The adis device @@ -517,6 +535,11 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, adis->spi = spi; adis->data = data; + if (!adis->ops->write && !adis->ops->read && !adis->ops->reset) + adis->ops = &adis_default_ops; + else if (!adis->ops->write || !adis->ops->read || !adis->ops->reset) + return -EINVAL; + iio_device_set_drvdata(indio_dev, adis); if (data->has_paging) { diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c new file mode 100644 index 000000000000..b14ea8937c7f --- /dev/null +++ b/drivers/iio/imu/adis16550.c @@ -0,0 +1,1147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADIS16550 IMU driver + * + * Copyright 2024 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/crc32.h> +#include <linux/debugfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/imu/adis.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/lcm.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/swab.h> +#include <linux/unaligned.h> + +#define ADIS16550_REG_BURST_GYRO_ACCEL 0x0a +#define ADIS16550_REG_BURST_DELTA_ANG_VEL 0x0b +#define ADIS16550_BURST_DATA_GYRO_ACCEL_MASK GENMASK(6, 1) +#define ADIS16550_BURST_DATA_DELTA_ANG_VEL_MASK GENMASK(12, 7) + +#define ADIS16550_REG_STATUS 0x0e +#define ADIS16550_REG_TEMP 0x10 +#define ADIS16550_REG_X_GYRO 0x12 +#define ADIS16550_REG_Y_GYRO 0x14 +#define ADIS16550_REG_Z_GYRO 0x16 +#define ADIS16550_REG_X_ACCEL 0x18 +#define ADIS16550_REG_Y_ACCEL 0x1a +#define ADIS16550_REG_Z_ACCEL 0x1c +#define ADIS16550_REG_X_DELTANG_L 0x1E +#define ADIS16550_REG_Y_DELTANG_L 0x20 +#define ADIS16550_REG_Z_DELTANG_L 0x22 +#define ADIS16550_REG_X_DELTVEL_L 0x24 +#define ADIS16550_REG_Y_DELTVEL_L 0x26 +#define ADIS16550_REG_Z_DELTVEL_L 0x28 +#define ADIS16550_REG_X_GYRO_SCALE 0x30 +#define ADIS16550_REG_Y_GYRO_SCALE 0x32 +#define ADIS16550_REG_Z_GYRO_SCALE 0x34 +#define ADIS16550_REG_X_ACCEL_SCALE 0x36 +#define ADIS16550_REG_Y_ACCEL_SCALE 0x38 +#define ADIS16550_REG_Z_ACCEL_SCALE 0x3a +#define ADIS16550_REG_X_GYRO_BIAS 0x40 +#define ADIS16550_REG_Y_GYRO_BIAS 0x42 +#define ADIS16550_REG_Z_GYRO_BIAS 0x44 +#define ADIS16550_REG_X_ACCEL_BIAS 0x46 +#define ADIS16550_REG_Y_ACCEL_BIAS 0x48 +#define ADIS16550_REG_Z_ACCEL_BIAS 0x4a +#define ADIS16550_REG_COMMAND 0x50 +#define ADIS16550_REG_CONFIG 0x52 +#define ADIS16550_GYRO_FIR_EN_MASK BIT(3) +#define ADIS16550_ACCL_FIR_EN_MASK BIT(2) +#define ADIS16550_SYNC_MASK \ + (ADIS16550_SYNC_EN_MASK | ADIS16550_SYNC_MODE_MASK) +#define ADIS16550_SYNC_MODE_MASK BIT(1) +#define ADIS16550_SYNC_EN_MASK BIT(0) +/* max of 4000 SPS in scale sync */ +#define ADIS16550_SYNC_SCALE_MAX_RATE (4000 * 1000) +#define ADIS16550_REG_DEC_RATE 0x54 +#define ADIS16550_REG_SYNC_SCALE 0x56 +#define ADIS16550_REG_SERIAL_NUM 0x76 +#define ADIS16550_REG_FW_REV 0x7A +#define ADIS16550_REG_FW_DATE 0x7C +#define ADIS16550_REG_PROD_ID 0x7E +#define ADIS16550_REG_FLASH_CNT 0x72 +/* SPI protocol*/ +#define ADIS16550_SPI_DATA_MASK GENMASK(31, 16) +#define ADIS16550_SPI_REG_MASK GENMASK(14, 8) +#define ADIS16550_SPI_R_W_MASK BIT(7) +#define ADIS16550_SPI_CRC_MASK GENMASK(3, 0) +#define ADIS16550_SPI_SV_MASK GENMASK(7, 6) +/* burst read */ +#define ADIS16550_BURST_N_ELEM 12 +#define ADIS16550_BURST_DATA_LEN (ADIS16550_BURST_N_ELEM * 4) +#define ADIS16550_MAX_SCAN_DATA 12 + +struct adis16550_sync { + u16 sync_mode; + u16 min_rate; + u16 max_rate; +}; + +struct adis16550_chip_info { + const struct iio_chan_spec *channels; + const struct adis16550_sync *sync_mode; + char *name; + u32 num_channels; + u32 gyro_max_val; + u32 gyro_max_scale; + u32 accel_max_val; + u32 accel_max_scale; + u32 temp_scale; + u32 deltang_max_val; + u32 deltvel_max_val; + u32 int_clk; + u16 max_dec; + u16 num_sync; +}; + +struct adis16550 { + const struct adis16550_chip_info *info; + struct adis adis; + unsigned long clk_freq_hz; + u32 sync_mode; + struct spi_transfer xfer[2]; + u8 buffer[ADIS16550_BURST_DATA_LEN + sizeof(u32)] __aligned(IIO_DMA_MINALIGN); + __be32 din[2]; + __be32 dout[2]; +}; + +enum { + ADIS16550_SV_INIT, + ADIS16550_SV_OK, + ADIS16550_SV_NOK, + ADIS16550_SV_SPI_ERROR, +}; + +/* + * This is a simplified implementation of lib/crc4.c. It could not be used + * directly since the polynomial used is different from the one used by the + * 16550 which is 0b10001 + */ +static u8 spi_crc4(const u32 val) +{ + int i; + const int bits = 28; + u8 crc = 0xa; + /* ignore 4lsb */ + const u32 __val = val >> 4; + + /* Calculate crc4 over four-bit nibbles, starting at the MSbit */ + for (i = bits - 4; i >= 0; i -= 4) + crc = crc ^ ((__val >> i) & 0xf); + + return crc; +} + +static int adis16550_spi_validate(const struct adis *adis, __be32 dout, + u16 *data) +{ + u32 __dout; + u8 crc, crc_rcv, sv; + + __dout = be32_to_cpu(dout); + + /* validate received message */ + crc_rcv = FIELD_GET(ADIS16550_SPI_CRC_MASK, __dout); + crc = spi_crc4(__dout); + if (crc_rcv != crc) { + dev_err(&adis->spi->dev, + "Invalid crc, rcv: 0x%02x, calc: 0x%02x!\n", + crc_rcv, crc); + return -EIO; + } + sv = FIELD_GET(ADIS16550_SPI_SV_MASK, __dout); + if (sv >= ADIS16550_SV_NOK) { + dev_err(&adis->spi->dev, + "State vector error detected: %02X", sv); + return -EIO; + } + *data = FIELD_GET(ADIS16550_SPI_DATA_MASK, __dout); + + return 0; +} + +static void adis16550_spi_msg_prepare(const u32 reg, const bool write, + const u16 data, __be32 *din) +{ + u8 crc; + u32 __din; + + __din = FIELD_PREP(ADIS16550_SPI_REG_MASK, reg); + + if (write) { + __din |= FIELD_PREP(ADIS16550_SPI_R_W_MASK, 1); + __din |= FIELD_PREP(ADIS16550_SPI_DATA_MASK, data); + } + + crc = spi_crc4(__din); + __din |= FIELD_PREP(ADIS16550_SPI_CRC_MASK, crc); + + *din = cpu_to_be32(__din); +} + +static int adis16550_spi_xfer(const struct adis *adis, u32 reg, u32 len, + u32 *readval, u32 writeval) +{ + int ret; + u16 data = 0; + struct spi_message msg; + bool wr = readval ? false : true; + struct spi_device *spi = adis->spi; + struct adis16550 *st = container_of(adis, struct adis16550, adis); + struct spi_transfer xfers[] = { + { + .tx_buf = &st->din[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &st->din[1], + .len = 4, + .cs_change = 1, + .rx_buf = st->dout, + }, { + .tx_buf = &st->din[1], + .rx_buf = &st->dout[1], + .len = 4, + }, + }; + + spi_message_init(&msg); + + switch (len) { + case 4: + adis16550_spi_msg_prepare(reg + 1, wr, writeval >> 16, + &st->din[0]); + spi_message_add_tail(&xfers[0], &msg); + fallthrough; + case 2: + adis16550_spi_msg_prepare(reg, wr, writeval, &st->din[1]); + spi_message_add_tail(&xfers[1], &msg); + spi_message_add_tail(&xfers[2], &msg); + break; + default: + return -EINVAL; + } + + ret = spi_sync(spi, &msg); + if (ret) { + dev_err(&spi->dev, "Spi failure %d\n", ret); + return ret; + } + /* + * When writing a register, the device will reply with a readback on the + * transfer so that we can validate if our data was actually written.. + */ + switch (len) { + case 4: + ret = adis16550_spi_validate(adis, st->dout[0], &data); + if (ret) + return ret; + + if (readval) { + *readval = data << 16; + } else if ((writeval >> 16) != data && reg != ADIS16550_REG_COMMAND) { + dev_err(&spi->dev, + "Data not written: wr: 0x%04X, rcv: 0x%04X\n", + writeval >> 16, data); + return -EIO; + } + + fallthrough; + case 2: + ret = adis16550_spi_validate(adis, st->dout[1], &data); + if (ret) + return ret; + + if (readval) { + *readval = (*readval & GENMASK(31, 16)) | data; + } else if ((writeval & GENMASK(15, 0)) != data && reg != ADIS16550_REG_COMMAND) { + dev_err(&spi->dev, + "Data not written: wr: 0x%04X, rcv: 0x%04X\n", + (u16)writeval, data); + return -EIO; + } + } + + return 0; +} + +static int adis16550_spi_read(struct adis *adis, const u32 reg, + u32 *value, const u32 len) +{ + return adis16550_spi_xfer(adis, reg, len, value, 0); +} + +static int adis16550_spi_write(struct adis *adis, const u32 reg, + const u32 value, const u32 len) +{ + return adis16550_spi_xfer(adis, reg, len, NULL, value); +} + +static ssize_t adis16550_show_firmware_revision(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct adis16550 *st = file->private_data; + char buf[7]; + size_t len; + u16 rev; + int ret; + + ret = adis_read_reg_16(&st->adis, ADIS16550_REG_FW_REV, &rev); + if (ret) + return ret; + + len = scnprintf(buf, sizeof(buf), "%x.%x\n", rev >> 8, rev & 0xff); + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations adis16550_firmware_revision_fops = { + .open = simple_open, + .read = adis16550_show_firmware_revision, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static ssize_t adis16550_show_firmware_date(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct adis16550 *st = file->private_data; + char buf[12]; + size_t len; + u32 date; + int ret; + + ret = adis_read_reg_32(&st->adis, ADIS16550_REG_FW_DATE, &date); + if (ret) + return ret; + + len = scnprintf(buf, sizeof(buf), "%.2x-%.2x-%.4x\n", date & 0xff, + (date >> 8) & 0xff, date >> 16); + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations adis16550_firmware_date_fops = { + .open = simple_open, + .read = adis16550_show_firmware_date, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static int adis16550_show_serial_number(void *arg, u64 *val) +{ + struct adis16550 *st = arg; + u32 serial; + int ret; + + ret = adis_read_reg_32(&st->adis, ADIS16550_REG_SERIAL_NUM, &serial); + if (ret) + return ret; + + *val = serial; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(adis16550_serial_number_fops, + adis16550_show_serial_number, NULL, "0x%.8llx\n"); + +static int adis16550_show_product_id(void *arg, u64 *val) +{ + struct adis16550 *st = arg; + u16 prod_id; + int ret; + + ret = adis_read_reg_16(&st->adis, ADIS16550_REG_PROD_ID, &prod_id); + if (ret) + return ret; + + *val = prod_id; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(adis16550_product_id_fops, + adis16550_show_product_id, NULL, "%llu\n"); + +static int adis16550_show_flash_count(void *arg, u64 *val) +{ + struct adis16550 *st = arg; + u16 flash_count; + int ret; + + ret = adis_read_reg_16(&st->adis, ADIS16550_REG_FLASH_CNT, &flash_count); + if (ret) + return ret; + + *val = flash_count; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(adis16550_flash_count_fops, + adis16550_show_flash_count, NULL, "%lld\n"); + +static void adis16550_debugfs_init(struct iio_dev *indio_dev) +{ + struct adis16550 *st = iio_priv(indio_dev); + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + + debugfs_create_file_unsafe("serial_number", 0400, d, st, + &adis16550_serial_number_fops); + debugfs_create_file_unsafe("product_id", 0400, d, st, + &adis16550_product_id_fops); + debugfs_create_file("firmware_revision", 0400, d, st, + &adis16550_firmware_revision_fops); + debugfs_create_file("firmware_date", 0400, d, st, + &adis16550_firmware_date_fops); + debugfs_create_file_unsafe("flash_count", 0400, d, st, + &adis16550_flash_count_fops); +} + +enum { + ADIS16550_SYNC_MODE_DIRECT, + ADIS16550_SYNC_MODE_SCALED, +}; + +static int adis16550_get_freq(struct adis16550 *st, u32 *freq) +{ + int ret; + u16 dec = 0; + u32 sample_rate = st->clk_freq_hz; + + adis_dev_auto_lock(&st->adis); + + if (st->sync_mode == ADIS16550_SYNC_MODE_SCALED) { + u16 sync_scale; + + ret = __adis_read_reg_16(&st->adis, ADIS16550_REG_SYNC_SCALE, &sync_scale); + if (ret) + return ret; + + sample_rate = st->clk_freq_hz * sync_scale; + } + + ret = __adis_read_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, &dec); + if (ret) + return -EINVAL; + *freq = DIV_ROUND_CLOSEST(sample_rate, dec + 1); + + return 0; +} + +static int adis16550_set_freq_hz(struct adis16550 *st, u32 freq_hz) +{ + u16 dec; + int ret; + u32 sample_rate = st->clk_freq_hz; + /* + * The optimal sample rate for the supported IMUs is between + * int_clk - 1000 and int_clk + 500. + */ + u32 max_sample_rate = st->info->int_clk * 1000 + 500000; + u32 min_sample_rate = st->info->int_clk * 1000 - 1000000; + + if (!freq_hz) + return -EINVAL; + + adis_dev_auto_lock(&st->adis); + + if (st->sync_mode == ADIS16550_SYNC_MODE_SCALED) { + unsigned long scaled_rate = lcm(st->clk_freq_hz, freq_hz); + int sync_scale; + + if (scaled_rate > max_sample_rate) + scaled_rate = max_sample_rate / st->clk_freq_hz * st->clk_freq_hz; + else + scaled_rate = max_sample_rate / scaled_rate * scaled_rate; + + if (scaled_rate < min_sample_rate) + scaled_rate = roundup(min_sample_rate, st->clk_freq_hz); + + sync_scale = scaled_rate / st->clk_freq_hz; + ret = __adis_write_reg_16(&st->adis, ADIS16550_REG_SYNC_SCALE, + sync_scale); + if (ret) + return ret; + + sample_rate = scaled_rate; + } + + dec = DIV_ROUND_CLOSEST(sample_rate, freq_hz); + + if (dec) + dec--; + + dec = min(dec, st->info->max_dec); + + return __adis_write_reg_16(&st->adis, ADIS16550_REG_DEC_RATE, dec); +} + +static int adis16550_get_accl_filter_freq(struct adis16550 *st, int *freq_hz) +{ + int ret; + u16 config = 0; + + ret = adis_read_reg_16(&st->adis, ADIS16550_REG_CONFIG, &config); + if (ret) + return -EINVAL; + + if (FIELD_GET(ADIS16550_ACCL_FIR_EN_MASK, config)) + *freq_hz = 100; + else + *freq_hz = 0; + + return 0; +} + +static int adis16550_set_accl_filter_freq(struct adis16550 *st, int freq_hz) +{ + u8 en = freq_hz ? 1 : 0; + u16 val = FIELD_PREP(ADIS16550_ACCL_FIR_EN_MASK, en); + + return __adis_update_bits(&st->adis, ADIS16550_REG_CONFIG, + ADIS16550_ACCL_FIR_EN_MASK, val); +} + +static int adis16550_get_gyro_filter_freq(struct adis16550 *st, int *freq_hz) +{ + int ret; + u16 config = 0; + + ret = adis_read_reg_16(&st->adis, ADIS16550_REG_CONFIG, &config); + if (ret) + return -EINVAL; + + if (FIELD_GET(ADIS16550_GYRO_FIR_EN_MASK, config)) + *freq_hz = 100; + else + *freq_hz = 0; + + return 0; +} + +static int adis16550_set_gyro_filter_freq(struct adis16550 *st, int freq_hz) +{ + u8 en = freq_hz ? 1 : 0; + u16 val = FIELD_PREP(ADIS16550_GYRO_FIR_EN_MASK, en); + + return __adis_update_bits(&st->adis, ADIS16550_REG_CONFIG, + ADIS16550_GYRO_FIR_EN_MASK, val); +} + +enum { + ADIS16550_SCAN_TEMP, + ADIS16550_SCAN_GYRO_X, + ADIS16550_SCAN_GYRO_Y, + ADIS16550_SCAN_GYRO_Z, + ADIS16550_SCAN_ACCEL_X, + ADIS16550_SCAN_ACCEL_Y, + ADIS16550_SCAN_ACCEL_Z, + ADIS16550_SCAN_DELTANG_X, + ADIS16550_SCAN_DELTANG_Y, + ADIS16550_SCAN_DELTANG_Z, + ADIS16550_SCAN_DELTVEL_X, + ADIS16550_SCAN_DELTVEL_Y, + ADIS16550_SCAN_DELTVEL_Z, +}; + +static const u32 adis16550_calib_bias[] = { + [ADIS16550_SCAN_GYRO_X] = ADIS16550_REG_X_GYRO_BIAS, + [ADIS16550_SCAN_GYRO_Y] = ADIS16550_REG_Y_GYRO_BIAS, + [ADIS16550_SCAN_GYRO_Z] = ADIS16550_REG_Z_GYRO_BIAS, + [ADIS16550_SCAN_ACCEL_X] = ADIS16550_REG_X_ACCEL_BIAS, + [ADIS16550_SCAN_ACCEL_Y] = ADIS16550_REG_Y_ACCEL_BIAS, + [ADIS16550_SCAN_ACCEL_Z] = ADIS16550_REG_Z_ACCEL_BIAS, + +}; + +static const u32 adis16550_calib_scale[] = { + [ADIS16550_SCAN_GYRO_X] = ADIS16550_REG_X_GYRO_SCALE, + [ADIS16550_SCAN_GYRO_Y] = ADIS16550_REG_Y_GYRO_SCALE, + [ADIS16550_SCAN_GYRO_Z] = ADIS16550_REG_Z_GYRO_SCALE, + [ADIS16550_SCAN_ACCEL_X] = ADIS16550_REG_X_ACCEL_SCALE, + [ADIS16550_SCAN_ACCEL_Y] = ADIS16550_REG_Y_ACCEL_SCALE, + [ADIS16550_SCAN_ACCEL_Z] = ADIS16550_REG_Z_ACCEL_SCALE, +}; + +static int adis16550_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct adis16550 *st = iio_priv(indio_dev); + const int idx = chan->scan_index; + u16 scale; + int ret; + u32 tmp; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, 0, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: + *val = st->info->gyro_max_val; + *val2 = st->info->gyro_max_scale; + return IIO_VAL_FRACTIONAL; + case IIO_ACCEL: + *val = st->info->accel_max_val; + *val2 = st->info->accel_max_scale; + return IIO_VAL_FRACTIONAL; + case IIO_TEMP: + *val = st->info->temp_scale; + return IIO_VAL_INT; + case IIO_DELTA_ANGL: + *val = st->info->deltang_max_val; + *val2 = 31; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_DELTA_VELOCITY: + *val = st->info->deltvel_max_val; + *val2 = 31; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* temperature centered at 25°C */ + *val = DIV_ROUND_CLOSEST(25000, st->info->temp_scale); + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = adis_read_reg_32(&st->adis, + adis16550_calib_bias[idx], val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + ret = adis_read_reg_16(&st->adis, + adis16550_calib_scale[idx], &scale); + if (ret) + return ret; + + *val = scale; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = adis16550_get_freq(st, &tmp); + if (ret) + return ret; + + *val = tmp / 1000; + *val2 = (tmp % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + switch (chan->type) { + case IIO_ANGL_VEL: + ret = adis16550_get_accl_filter_freq(st, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_ACCEL: + ret = adis16550_get_gyro_filter_freq(st, val); + if (ret) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adis16550_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long info) +{ + struct adis16550 *st = iio_priv(indio_dev); + const int idx = chan->scan_index; + u32 tmp; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + tmp = val * 1000 + val2 / 1000; + return adis16550_set_freq_hz(st, tmp); + case IIO_CHAN_INFO_CALIBBIAS: + return adis_write_reg_32(&st->adis, adis16550_calib_bias[idx], + val); + case IIO_CHAN_INFO_CALIBSCALE: + return adis_write_reg_16(&st->adis, adis16550_calib_scale[idx], + val); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + switch (chan->type) { + case IIO_ANGL_VEL: + return adis16550_set_accl_filter_freq(st, val); + case IIO_ACCEL: + return adis16550_set_gyro_filter_freq(st, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +#define ADIS16550_MOD_CHAN(_type, _mod, _address, _si) \ + { \ + .type = (_type), \ + .modified = 1, \ + .channel2 = (_mod), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .address = (_address), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADIS16550_GYRO_CHANNEL(_mod) \ + ADIS16550_MOD_CHAN(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \ + ADIS16550_REG_ ## _mod ## _GYRO, ADIS16550_SCAN_GYRO_ ## _mod) + +#define ADIS16550_ACCEL_CHANNEL(_mod) \ + ADIS16550_MOD_CHAN(IIO_ACCEL, IIO_MOD_ ## _mod, \ + ADIS16550_REG_ ## _mod ## _ACCEL, ADIS16550_SCAN_ACCEL_ ## _mod) + +#define ADIS16550_TEMP_CHANNEL() { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .address = ADIS16550_REG_TEMP, \ + .scan_index = ADIS16550_SCAN_TEMP, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADIS16550_MOD_CHAN_DELTA(_type, _mod, _address, _si) { \ + .type = (_type), \ + .modified = 1, \ + .channel2 = (_mod), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .address = (_address), \ + .scan_index = _si, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADIS16550_DELTANG_CHAN(_mod) \ + ADIS16550_MOD_CHAN_DELTA(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \ + ADIS16550_REG_ ## _mod ## _DELTANG_L, ADIS16550_SCAN_DELTANG_ ## _mod) + +#define ADIS16550_DELTVEL_CHAN(_mod) \ + ADIS16550_MOD_CHAN_DELTA(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \ + ADIS16550_REG_ ## _mod ## _DELTVEL_L, ADIS16550_SCAN_DELTVEL_ ## _mod) + +#define ADIS16550_DELTANG_CHAN_NO_SCAN(_mod) \ + ADIS16550_MOD_CHAN_DELTA(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \ + ADIS16550_REG_ ## _mod ## _DELTANG_L, -1) + +#define ADIS16550_DELTVEL_CHAN_NO_SCAN(_mod) \ + ADIS16550_MOD_CHAN_DELTA(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \ + ADIS16550_REG_ ## _mod ## _DELTVEL_L, -1) + +static const struct iio_chan_spec adis16550_channels[] = { + ADIS16550_TEMP_CHANNEL(), + ADIS16550_GYRO_CHANNEL(X), + ADIS16550_GYRO_CHANNEL(Y), + ADIS16550_GYRO_CHANNEL(Z), + ADIS16550_ACCEL_CHANNEL(X), + ADIS16550_ACCEL_CHANNEL(Y), + ADIS16550_ACCEL_CHANNEL(Z), + ADIS16550_DELTANG_CHAN(X), + ADIS16550_DELTANG_CHAN(Y), + ADIS16550_DELTANG_CHAN(Z), + ADIS16550_DELTVEL_CHAN(X), + ADIS16550_DELTVEL_CHAN(Y), + ADIS16550_DELTVEL_CHAN(Z), + IIO_CHAN_SOFT_TIMESTAMP(13), +}; + +static const struct adis16550_sync adis16550_sync_modes[] = { + { ADIS16550_SYNC_MODE_DIRECT, 3000, 4500 }, + { ADIS16550_SYNC_MODE_SCALED, 1, 128 }, +}; + +static const struct adis16550_chip_info adis16550_chip_info = { + .num_channels = ARRAY_SIZE(adis16550_channels), + .channels = adis16550_channels, + .name = "adis16550", + .gyro_max_val = 1, + .gyro_max_scale = IIO_RAD_TO_DEGREE(80 << 16), + .accel_max_val = 1, + .accel_max_scale = IIO_M_S_2_TO_G(102400000), + .temp_scale = 4, + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 125, + .int_clk = 4000, + .max_dec = 4095, + .sync_mode = adis16550_sync_modes, + .num_sync = ARRAY_SIZE(adis16550_sync_modes), +}; + +static u32 adis16550_validate_crc(__be32 *buffer, const u8 n_elem) +{ + int i; + u32 crc_calc; + u32 crc_buf[ADIS16550_BURST_N_ELEM - 2]; + u32 crc = be32_to_cpu(buffer[ADIS16550_BURST_N_ELEM - 1]); + /* + * The crc calculation of the data is done in little endian. Hence, we + * always swap the 32bit elements making sure that the data LSB is + * always on address 0... + */ + for (i = 0; i < n_elem; i++) + crc_buf[i] = be32_to_cpu(buffer[i]); + + crc_calc = crc32(~0, crc_buf, n_elem * 4); + crc_calc ^= ~0; + + return (crc_calc == crc); +} + +static irqreturn_t adis16550_trigger_handler(int irq, void *p) +{ + int ret; + u16 dummy; + bool valid; + struct iio_poll_func *pf = p; + __be32 data[ADIS16550_MAX_SCAN_DATA]; + struct iio_dev *indio_dev = pf->indio_dev; + struct adis16550 *st = iio_priv(indio_dev); + struct adis *adis = iio_device_get_drvdata(indio_dev); + __be32 *buffer = (__be32 *)st->buffer; + + ret = spi_sync(adis->spi, &adis->msg); + if (ret) + goto done; + /* + * Validate the header. The header is a normal spi reply with state + * vector and crc4. + */ + ret = adis16550_spi_validate(&st->adis, buffer[0], &dummy); + if (ret) + goto done; + + /* the header is not included in the crc */ + valid = adis16550_validate_crc(buffer, ADIS16550_BURST_N_ELEM - 2); + if (!valid) { + dev_err(&adis->spi->dev, "Burst Invalid crc!\n"); + goto done; + } + + /* copy the temperature together with sensor data */ + memcpy(data, &buffer[3], + (ADIS16550_SCAN_ACCEL_Z - ADIS16550_SCAN_GYRO_X + 2) * + sizeof(__be32)); + iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp); +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static const unsigned long adis16550_channel_masks[] = { + ADIS16550_BURST_DATA_GYRO_ACCEL_MASK | BIT(ADIS16550_SCAN_TEMP), + ADIS16550_BURST_DATA_DELTA_ANG_VEL_MASK | BIT(ADIS16550_SCAN_TEMP), + 0 +}; + +static int adis16550_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + u16 burst_length = ADIS16550_BURST_DATA_LEN; + struct adis16550 *st = iio_priv(indio_dev); + u8 burst_cmd; + u8 *tx; + + memset(st->buffer, 0, burst_length + sizeof(u32)); + + if (*scan_mask & ADIS16550_BURST_DATA_GYRO_ACCEL_MASK) + burst_cmd = ADIS16550_REG_BURST_GYRO_ACCEL; + else + burst_cmd = ADIS16550_REG_BURST_DELTA_ANG_VEL; + + tx = st->buffer + burst_length; + tx[0] = 0x00; + tx[1] = 0x00; + tx[2] = burst_cmd; + /* crc4 is 0 on burst command */ + tx[3] = spi_crc4(get_unaligned_le32(tx)); + + return 0; +} + +static int adis16550_reset(struct adis *adis) +{ + return __adis_write_reg_16(adis, ADIS16550_REG_COMMAND, BIT(15)); +} + +static int adis16550_config_sync(struct adis16550 *st) +{ + struct device *dev = &st->adis.spi->dev; + const struct adis16550_sync *sync_mode_data; + struct clk *clk; + int ret, i; + u16 mode; + + clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + if (!clk) { + st->clk_freq_hz = st->info->int_clk * 1000; + return 0; + } + + st->clk_freq_hz = clk_get_rate(clk); + + for (i = 0; i < st->info->num_sync; i++) { + if (st->clk_freq_hz >= st->info->sync_mode[i].min_rate && + st->clk_freq_hz <= st->info->sync_mode[i].max_rate) { + sync_mode_data = &st->info->sync_mode[i]; + break; + } + } + + if (i == st->info->num_sync) + return dev_err_probe(dev, -EINVAL, "Clk rate: %lu not in a valid range", + st->clk_freq_hz); + + if (sync_mode_data->sync_mode == ADIS16550_SYNC_MODE_SCALED) { + u16 sync_scale; + /* + * In sps scaled sync we must scale the input clock to a range + * of [3000 4500]. + */ + + sync_scale = DIV_ROUND_CLOSEST(st->info->int_clk, st->clk_freq_hz); + + if (3000 > sync_scale || 4500 < sync_scale) + return dev_err_probe(dev, -EINVAL, + "Invalid value:%u for sync_scale", + sync_scale); + + ret = adis_write_reg_16(&st->adis, ADIS16550_REG_SYNC_SCALE, + sync_scale); + if (ret) + return ret; + + st->clk_freq_hz = st->info->int_clk; + } + + st->clk_freq_hz *= 1000; + + mode = FIELD_PREP(ADIS16550_SYNC_MODE_MASK, sync_mode_data->sync_mode) | + FIELD_PREP(ADIS16550_SYNC_EN_MASK, true); + + return __adis_update_bits(&st->adis, ADIS16550_REG_CONFIG, + ADIS16550_SYNC_MASK, mode); +} + +static const struct iio_info adis16550_info = { + .read_raw = &adis16550_read_raw, + .write_raw = &adis16550_write_raw, + .update_scan_mode = adis16550_update_scan_mode, + .debugfs_reg_access = adis_debugfs_reg_access, +}; + +enum { + ADIS16550_STATUS_CRC_CODE, + ADIS16550_STATUS_CRC_CONFIG, + ADIS16550_STATUS_FLASH_UPDATE, + ADIS16550_STATUS_INERIAL, + ADIS16550_STATUS_SENSOR, + ADIS16550_STATUS_TEMPERATURE, + ADIS16550_STATUS_SPI, + ADIS16550_STATUS_PROCESSING, + ADIS16550_STATUS_POWER, + ADIS16550_STATUS_BOOT, + ADIS16550_STATUS_WATCHDOG = 15, + ADIS16550_STATUS_REGULATOR = 28, + ADIS16550_STATUS_SENSOR_SUPPLY, + ADIS16550_STATUS_CPU_SUPPLY, + ADIS16550_STATUS_5V_SUPPLY, +}; + +static const char * const adis16550_status_error_msgs[] = { + [ADIS16550_STATUS_CRC_CODE] = "Code CRC Error", + [ADIS16550_STATUS_CRC_CONFIG] = "Configuration/Calibration CRC Error", + [ADIS16550_STATUS_FLASH_UPDATE] = "Flash Update Error", + [ADIS16550_STATUS_INERIAL] = "Overrange for Inertial Signals", + [ADIS16550_STATUS_SENSOR] = "Sensor failure", + [ADIS16550_STATUS_TEMPERATURE] = "Temperature Error", + [ADIS16550_STATUS_SPI] = "SPI Communication Error", + [ADIS16550_STATUS_PROCESSING] = "Processing Overrun Error", + [ADIS16550_STATUS_POWER] = "Power Supply Failure", + [ADIS16550_STATUS_BOOT] = "Boot Memory Failure", + [ADIS16550_STATUS_WATCHDOG] = "Watchdog timer flag", + [ADIS16550_STATUS_REGULATOR] = "Internal Regulator Error", + [ADIS16550_STATUS_SENSOR_SUPPLY] = "Internal Sensor Supply Error.", + [ADIS16550_STATUS_CPU_SUPPLY] = "Internal Processor Supply Error.", + [ADIS16550_STATUS_5V_SUPPLY] = "External 5V Supply Error", +}; + +static const struct adis_timeout adis16550_timeouts = { + .reset_ms = 1000, + .sw_reset_ms = 1000, + .self_test_ms = 1000, +}; + +static const struct adis_data adis16550_data = { + .diag_stat_reg = ADIS16550_REG_STATUS, + .diag_stat_size = 4, + .prod_id_reg = ADIS16550_REG_PROD_ID, + .prod_id = 16550, + .self_test_mask = BIT(1), + .self_test_reg = ADIS16550_REG_COMMAND, + .cs_change_delay = 5, + .unmasked_drdy = true, + .status_error_msgs = adis16550_status_error_msgs, + .status_error_mask = BIT(ADIS16550_STATUS_CRC_CODE) | + BIT(ADIS16550_STATUS_CRC_CONFIG) | + BIT(ADIS16550_STATUS_FLASH_UPDATE) | + BIT(ADIS16550_STATUS_INERIAL) | + BIT(ADIS16550_STATUS_SENSOR) | + BIT(ADIS16550_STATUS_TEMPERATURE) | + BIT(ADIS16550_STATUS_SPI) | + BIT(ADIS16550_STATUS_PROCESSING) | + BIT(ADIS16550_STATUS_POWER) | + BIT(ADIS16550_STATUS_BOOT) | + BIT(ADIS16550_STATUS_WATCHDOG) | + BIT(ADIS16550_STATUS_REGULATOR) | + BIT(ADIS16550_STATUS_SENSOR_SUPPLY) | + BIT(ADIS16550_STATUS_CPU_SUPPLY) | + BIT(ADIS16550_STATUS_5V_SUPPLY), + .timeouts = &adis16550_timeouts, +}; + +static const struct adis_ops adis16550_ops = { + .write = adis16550_spi_write, + .read = adis16550_spi_read, + .reset = adis16550_reset, +}; + +static int adis16550_probe(struct spi_device *spi) +{ + u16 burst_length = ADIS16550_BURST_DATA_LEN; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct adis16550 *st; + struct adis *adis; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -EINVAL; + adis = &st->adis; + indio_dev->name = st->info->name; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + indio_dev->available_scan_masks = adis16550_channel_masks; + indio_dev->info = &adis16550_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + st->adis.ops = &adis16550_ops; + st->xfer[0].tx_buf = st->buffer + burst_length; + st->xfer[0].len = 4; + st->xfer[0].cs_change = 1; + st->xfer[0].delay.value = 8; + st->xfer[0].delay.unit = SPI_DELAY_UNIT_USECS; + st->xfer[1].rx_buf = st->buffer; + st->xfer[1].len = burst_length; + + spi_message_init_with_transfers(&adis->msg, st->xfer, 2); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to get vdd regulator\n"); + + ret = adis_init(&st->adis, indio_dev, spi, &adis16550_data); + if (ret) + return ret; + + ret = __adis_initial_startup(&st->adis); + if (ret) + return ret; + + ret = adis16550_config_sync(st); + if (ret) + return ret; + + ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, + adis16550_trigger_handler); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + adis16550_debugfs_init(indio_dev); + + return 0; +} + +static const struct spi_device_id adis16550_id[] = { + { "adis16550", (kernel_ulong_t)&adis16550_chip_info}, + { } +}; +MODULE_DEVICE_TABLE(spi, adis16550_id); + +static const struct of_device_id adis16550_of_match[] = { + { .compatible = "adi,adis16550", .data = &adis16550_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adis16550_of_match); + +static struct spi_driver adis16550_driver = { + .driver = { + .name = "adis16550", + .of_match_table = adis16550_of_match, + }, + .probe = adis16550_probe, + .id_table = adis16550_id, +}; +module_spi_driver(adis16550_driver); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_AUTHOR("Ramona Gradinariu <ramona.gradinariu@analog.com>"); +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>"); +MODULE_AUTHOR("Robert Budai <robert.budai@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16550 IMU driver"); +MODULE_IMPORT_NS("IIO_ADISLIB"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bmi270/bmi270.h b/drivers/iio/imu/bmi270/bmi270.h index fdfad5784cc5..d94525f6aee8 100644 --- a/drivers/iio/imu/bmi270/bmi270.h +++ b/drivers/iio/imu/bmi270/bmi270.h @@ -6,22 +6,6 @@ #include <linux/regmap.h> #include <linux/iio/iio.h> -struct device; -struct bmi270_data { - struct device *dev; - struct regmap *regmap; - const struct bmi270_chip_info *chip_info; - - /* - * Where IIO_DMA_MINALIGN may be larger than 8 bytes, align to - * that to ensure a DMA safe buffer. - */ - struct { - __le16 channels[6]; - aligned_s64 timestamp; - } data __aligned(IIO_DMA_MINALIGN); -}; - struct bmi270_chip_info { const char *name; int chip_id; @@ -32,6 +16,7 @@ extern const struct regmap_config bmi270_regmap_config; extern const struct bmi270_chip_info bmi260_chip_info; extern const struct bmi270_chip_info bmi270_chip_info; +struct device; int bmi270_core_probe(struct device *dev, struct regmap *regmap, const struct bmi270_chip_info *chip_info); diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c index 7fec52e0b486..a86be5af5ccb 100644 --- a/drivers/iio/imu/bmi270/bmi270_core.c +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -4,10 +4,13 @@ #include <linux/firmware.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/regmap.h> +#include <linux/units.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> @@ -25,13 +28,17 @@ #define BMI270_ACCEL_X_REG 0x0c #define BMI270_ANG_VEL_X_REG 0x12 +#define BMI270_INT_STATUS_1_REG 0x1d +#define BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK GENMASK(7, 6) + #define BMI270_INTERNAL_STATUS_REG 0x21 #define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) #define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 - #define BMI270_INTERNAL_STATUS_AXES_REMAP_ERR_MSK BIT(5) #define BMI270_INTERNAL_STATUS_ODR_50HZ_ERR_MSK BIT(6) +#define BMI270_TEMPERATURE_0_REG 0x22 + #define BMI270_ACC_CONF_REG 0x40 #define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) #define BMI270_ACC_CONF_ODR_100HZ 0x08 @@ -53,6 +60,20 @@ #define BMI270_GYR_CONF_RANGE_REG 0x43 #define BMI270_GYR_CONF_RANGE_MSK GENMASK(2, 0) +#define BMI270_INT1_IO_CTRL_REG 0x53 +#define BMI270_INT2_IO_CTRL_REG 0x54 +#define BMI270_INT_IO_CTRL_LVL_MSK BIT(1) +#define BMI270_INT_IO_CTRL_OD_MSK BIT(2) +#define BMI270_INT_IO_CTRL_OP_MSK BIT(3) +#define BMI270_INT_IO_LVL_OD_OP_MSK GENMASK(3, 1) + +#define BMI270_INT_LATCH_REG 0x55 +#define BMI270_INT_LATCH_REG_MSK BIT(0) + +#define BMI270_INT_MAP_DATA_REG 0x58 +#define BMI270_INT_MAP_DATA_DRDY_INT1_MSK BIT(2) +#define BMI270_INT_MAP_DATA_DRDY_INT2_MSK BIT(6) + #define BMI270_INIT_CTRL_REG 0x59 #define BMI270_INIT_CTRL_LOAD_DONE_MSK BIT(0) @@ -69,9 +90,38 @@ #define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) #define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) +/* See datasheet section 4.6.14, Temperature Sensor */ +#define BMI270_TEMP_OFFSET 11776 +#define BMI270_TEMP_SCALE 1953125 + #define BMI260_INIT_DATA_FILE "bmi260-init-data.fw" #define BMI270_INIT_DATA_FILE "bmi270-init-data.fw" +enum bmi270_irq_pin { + BMI270_IRQ_DISABLED, + BMI270_IRQ_INT1, + BMI270_IRQ_INT2, +}; + +struct bmi270_data { + struct device *dev; + struct regmap *regmap; + const struct bmi270_chip_info *chip_info; + enum bmi270_irq_pin irq_pin; + struct iio_trigger *trig; + /* Protect device's private data from concurrent access */ + struct mutex mutex; + + /* + * Where IIO_DMA_MINALIGN may be larger than 8 bytes, align to + * that to ensure a DMA safe buffer. + */ + struct { + __le16 channels[6]; + aligned_s64 timestamp; + } buffer __aligned(IIO_DMA_MINALIGN); +}; + enum bmi270_scan { BMI270_SCAN_ACCEL_X, BMI270_SCAN_ACCEL_Y, @@ -109,6 +159,7 @@ EXPORT_SYMBOL_NS_GPL(bmi270_chip_info, "IIO_BMI270"); enum bmi270_sensor_type { BMI270_ACCEL = 0, BMI270_GYRO, + BMI270_TEMP, }; struct bmi270_scale { @@ -136,6 +187,10 @@ static const struct bmi270_scale bmi270_gyro_scale[] = { { 0, 66 }, }; +static const struct bmi270_scale bmi270_temp_scale[] = { + { BMI270_TEMP_SCALE / MICRO, BMI270_TEMP_SCALE % MICRO }, +}; + struct bmi270_scale_item { const struct bmi270_scale *tbl; int num; @@ -150,6 +205,10 @@ static const struct bmi270_scale_item bmi270_scale_table[] = { .tbl = bmi270_gyro_scale, .num = ARRAY_SIZE(bmi270_gyro_scale), }, + [BMI270_TEMP] = { + .tbl = bmi270_temp_scale, + .num = ARRAY_SIZE(bmi270_temp_scale), + }, }; static const struct bmi270_odr bmi270_accel_odr[] = { @@ -244,6 +303,8 @@ static int bmi270_set_scale(struct bmi270_data *data, int chan_type, int uscale) return -EINVAL; } + guard(mutex)(&data->mutex); + for (i = 0; i < bmi270_scale_item.num; i++) { if (bmi270_scale_item.tbl[i].uscale != uscale) continue; @@ -254,17 +315,18 @@ static int bmi270_set_scale(struct bmi270_data *data, int chan_type, int uscale) return -EINVAL; } -static int bmi270_get_scale(struct bmi270_data *bmi270_device, int chan_type, +static int bmi270_get_scale(struct bmi270_data *data, int chan_type, int *scale, int *uscale) { int ret; unsigned int val; struct bmi270_scale_item bmi270_scale_item; + guard(mutex)(&data->mutex); + switch (chan_type) { case IIO_ACCEL: - ret = regmap_read(bmi270_device->regmap, - BMI270_ACC_CONF_RANGE_REG, &val); + ret = regmap_read(data->regmap, BMI270_ACC_CONF_RANGE_REG, &val); if (ret) return ret; @@ -272,14 +334,17 @@ static int bmi270_get_scale(struct bmi270_data *bmi270_device, int chan_type, bmi270_scale_item = bmi270_scale_table[BMI270_ACCEL]; break; case IIO_ANGL_VEL: - ret = regmap_read(bmi270_device->regmap, - BMI270_GYR_CONF_RANGE_REG, &val); + ret = regmap_read(data->regmap, BMI270_GYR_CONF_RANGE_REG, &val); if (ret) return ret; val = FIELD_GET(BMI270_GYR_CONF_RANGE_MSK, val); bmi270_scale_item = bmi270_scale_table[BMI270_GYRO]; break; + case IIO_TEMP: + val = 0; + bmi270_scale_item = bmi270_scale_table[BMI270_TEMP]; + break; default: return -EINVAL; } @@ -287,6 +352,7 @@ static int bmi270_get_scale(struct bmi270_data *bmi270_device, int chan_type, if (val >= bmi270_scale_item.num) return -EINVAL; + *scale = bmi270_scale_item.tbl[val].scale; *uscale = bmi270_scale_item.tbl[val].uscale; return 0; } @@ -313,6 +379,8 @@ static int bmi270_set_odr(struct bmi270_data *data, int chan_type, int odr, return -EINVAL; } + guard(mutex)(&data->mutex); + for (i = 0; i < bmi270_odr_item.num; i++) { if (bmi270_odr_item.tbl[i].odr != odr || bmi270_odr_item.tbl[i].uodr != uodr) @@ -331,6 +399,8 @@ static int bmi270_get_odr(struct bmi270_data *data, int chan_type, int *odr, int i, val, ret; struct bmi270_odr_item bmi270_odr_item; + guard(mutex)(&data->mutex); + switch (chan_type) { case IIO_ACCEL: ret = regmap_read(data->regmap, BMI270_ACC_CONF_REG, &val); @@ -364,29 +434,85 @@ static int bmi270_get_odr(struct bmi270_data *data, int chan_type, int *odr, return -EINVAL; } +static irqreturn_t bmi270_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmi270_data *data = iio_priv(indio_dev); + unsigned int status; + int ret; + + scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, BMI270_INT_STATUS_1_REG, + &status); + if (ret) + return IRQ_NONE; + } + + if (FIELD_GET(BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK, status)) + iio_trigger_poll_nested(data->trig); + + return IRQ_HANDLED; +} + +static int bmi270_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct bmi270_data *data = iio_trigger_get_drvdata(trig); + unsigned int field_value = 0; + unsigned int mask; + + guard(mutex)(&data->mutex); + + switch (data->irq_pin) { + case BMI270_IRQ_INT1: + mask = BMI270_INT_MAP_DATA_DRDY_INT1_MSK; + set_mask_bits(&field_value, BMI270_INT_MAP_DATA_DRDY_INT1_MSK, + FIELD_PREP(BMI270_INT_MAP_DATA_DRDY_INT1_MSK, + state)); + break; + case BMI270_IRQ_INT2: + mask = BMI270_INT_MAP_DATA_DRDY_INT2_MSK; + set_mask_bits(&field_value, BMI270_INT_MAP_DATA_DRDY_INT2_MSK, + FIELD_PREP(BMI270_INT_MAP_DATA_DRDY_INT2_MSK, + state)); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(data->regmap, BMI270_INT_MAP_DATA_REG, mask, + field_value); +} + +static const struct iio_trigger_ops bmi270_trigger_ops = { + .set_trigger_state = &bmi270_data_rdy_trigger_set_state, +}; + static irqreturn_t bmi270_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - struct bmi270_data *bmi270_device = iio_priv(indio_dev); + struct bmi270_data *data = iio_priv(indio_dev); int ret; - ret = regmap_bulk_read(bmi270_device->regmap, BMI270_ACCEL_X_REG, - &bmi270_device->data.channels, - sizeof(bmi270_device->data.channels)); + guard(mutex)(&data->mutex); + + ret = regmap_bulk_read(data->regmap, BMI270_ACCEL_X_REG, + &data->buffer.channels, + sizeof(data->buffer.channels)); if (ret) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, &bmi270_device->data, + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, pf->timestamp); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } -static int bmi270_get_data(struct bmi270_data *bmi270_device, - int chan_type, int axis, int *val) +static int bmi270_get_data(struct bmi270_data *data, int chan_type, int axis, + int *val) { __le16 sample; int reg; @@ -399,17 +525,22 @@ static int bmi270_get_data(struct bmi270_data *bmi270_device, case IIO_ANGL_VEL: reg = BMI270_ANG_VEL_X_REG + (axis - IIO_MOD_X) * 2; break; + case IIO_TEMP: + reg = BMI270_TEMPERATURE_0_REG; + break; default: return -EINVAL; } - ret = regmap_bulk_read(bmi270_device->regmap, reg, &sample, sizeof(sample)); + guard(mutex)(&data->mutex); + + ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample)); if (ret) return ret; *val = sign_extend32(le16_to_cpu(sample), 15); - return 0; + return IIO_VAL_INT; } static int bmi270_read_raw(struct iio_dev *indio_dev, @@ -417,21 +548,28 @@ static int bmi270_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { int ret; - struct bmi270_data *bmi270_device = iio_priv(indio_dev); + struct bmi270_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_RAW: - ret = bmi270_get_data(bmi270_device, chan->type, chan->channel2, val); - if (ret) - return ret; - - return IIO_VAL_INT; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi270_get_data(data, chan->type, chan->channel2, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: - *val = 0; - ret = bmi270_get_scale(bmi270_device, chan->type, val2); + ret = bmi270_get_scale(data, chan->type, val, val2); return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = BMI270_TEMP_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } case IIO_CHAN_INFO_SAMP_FREQ: - ret = bmi270_get_odr(bmi270_device, chan->type, val, val2); + ret = bmi270_get_odr(data, chan->type, val, val2); return ret ? ret : IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; @@ -443,12 +581,21 @@ static int bmi270_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct bmi270_data *data = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_SCALE: - return bmi270_set_scale(data, chan->type, val2); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi270_set_scale(data, chan->type, val2); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SAMP_FREQ: - return bmi270_set_odr(data, chan->type, val, val2); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi270_set_odr(data, chan->type, val, val2); + iio_device_release_direct(indio_dev); + return ret; default: return -EINVAL; } @@ -544,15 +691,132 @@ static const struct iio_chan_spec bmi270_channels[] = { BMI270_ANG_VEL_CHANNEL(X), BMI270_ANG_VEL_CHANNEL(Y), BMI270_ANG_VEL_CHANNEL(Z), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = -1, /* No buffer support */ + }, IIO_CHAN_SOFT_TIMESTAMP(BMI270_SCAN_TIMESTAMP), }; -static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) +static int bmi270_int_pin_config(struct bmi270_data *data, + enum bmi270_irq_pin irq_pin, + bool active_high, bool open_drain, bool latch) +{ + unsigned int reg, field_value; + int ret; + + ret = regmap_update_bits(data->regmap, BMI270_INT_LATCH_REG, + BMI270_INT_LATCH_REG_MSK, + FIELD_PREP(BMI270_INT_LATCH_REG_MSK, latch)); + if (ret) + return ret; + + switch (irq_pin) { + case BMI270_IRQ_INT1: + reg = BMI270_INT1_IO_CTRL_REG; + break; + case BMI270_IRQ_INT2: + reg = BMI270_INT2_IO_CTRL_REG; + break; + default: + return -EINVAL; + } + + field_value = FIELD_PREP(BMI270_INT_IO_CTRL_LVL_MSK, active_high) | + FIELD_PREP(BMI270_INT_IO_CTRL_OD_MSK, open_drain) | + FIELD_PREP(BMI270_INT_IO_CTRL_OP_MSK, 1); + return regmap_update_bits(data->regmap, reg, + BMI270_INT_IO_LVL_OD_OP_MSK, field_value); +} + +static int bmi270_trigger_probe(struct bmi270_data *data, + struct iio_dev *indio_dev) +{ + bool open_drain, active_high, latch; + struct fwnode_handle *fwnode; + enum bmi270_irq_pin irq_pin; + int ret, irq, irq_type; + + fwnode = dev_fwnode(data->dev); + if (!fwnode) + return -ENODEV; + + irq = fwnode_irq_get_byname(fwnode, "INT1"); + if (irq > 0) { + irq_pin = BMI270_IRQ_INT1; + } else { + irq = fwnode_irq_get_byname(fwnode, "INT2"); + if (irq < 0) + return 0; + + irq_pin = BMI270_IRQ_INT2; + } + + irq_type = irq_get_trigger_type(irq); + switch (irq_type) { + case IRQF_TRIGGER_RISING: + latch = false; + active_high = true; + break; + case IRQF_TRIGGER_HIGH: + latch = true; + active_high = true; + break; + case IRQF_TRIGGER_FALLING: + latch = false; + active_high = false; + break; + case IRQF_TRIGGER_LOW: + latch = true; + active_high = false; + break; + default: + return dev_err_probe(data->dev, -EINVAL, + "Invalid interrupt type 0x%x specified\n", + irq_type); + } + + open_drain = fwnode_property_read_bool(fwnode, "drive-open-drain"); + + ret = bmi270_int_pin_config(data, irq_pin, active_high, open_drain, + latch); + if (ret) + return dev_err_probe(data->dev, ret, + "Failed to configure irq line\n"); + + data->trig = devm_iio_trigger_alloc(data->dev, "%s-trig-%d", + indio_dev->name, irq_pin); + if (!data->trig) + return -ENOMEM; + + data->trig->ops = &bmi270_trigger_ops; + iio_trigger_set_drvdata(data->trig, data); + + ret = devm_request_threaded_irq(data->dev, irq, NULL, + bmi270_irq_thread_handler, + IRQF_ONESHOT, "bmi270-int", indio_dev); + if (ret) + return dev_err_probe(data->dev, ret, "Failed to request IRQ\n"); + + ret = devm_iio_trigger_register(data->dev, data->trig); + if (ret) + return dev_err_probe(data->dev, ret, + "Trigger registration failed\n"); + + data->irq_pin = irq_pin; + + return 0; +} + +static int bmi270_validate_chip_id(struct bmi270_data *data) { int chip_id; int ret; - struct device *dev = bmi270_device->dev; - struct regmap *regmap = bmi270_device->regmap; + struct device *dev = data->dev; + struct regmap *regmap = data->regmap; ret = regmap_read(regmap, BMI270_CHIP_ID_REG, &chip_id); if (ret) @@ -566,24 +830,24 @@ static int bmi270_validate_chip_id(struct bmi270_data *bmi270_device) if (chip_id == BMI160_CHIP_ID_VAL) return -ENODEV; - if (chip_id != bmi270_device->chip_info->chip_id) + if (chip_id != data->chip_info->chip_id) dev_info(dev, "Unexpected chip id 0x%x", chip_id); if (chip_id == bmi260_chip_info.chip_id) - bmi270_device->chip_info = &bmi260_chip_info; + data->chip_info = &bmi260_chip_info; else if (chip_id == bmi270_chip_info.chip_id) - bmi270_device->chip_info = &bmi270_chip_info; + data->chip_info = &bmi270_chip_info; return 0; } -static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) +static int bmi270_write_calibration_data(struct bmi270_data *data) { int ret; int status = 0; const struct firmware *init_data; - struct device *dev = bmi270_device->dev; - struct regmap *regmap = bmi270_device->regmap; + struct device *dev = data->dev; + struct regmap *regmap = data->regmap; ret = regmap_clear_bits(regmap, BMI270_PWR_CONF_REG, BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); @@ -604,8 +868,7 @@ static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) return dev_err_probe(dev, ret, "Failed to prepare device to load init data"); - ret = request_firmware(&init_data, - bmi270_device->chip_info->fw_name, dev); + ret = request_firmware(&init_data, data->chip_info->fw_name, dev); if (ret) return dev_err_probe(dev, ret, "Failed to load init data file"); @@ -637,16 +900,17 @@ static int bmi270_write_calibration_data(struct bmi270_data *bmi270_device) return 0; } -static int bmi270_configure_imu(struct bmi270_data *bmi270_device) +static int bmi270_configure_imu(struct bmi270_data *data) { int ret; - struct device *dev = bmi270_device->dev; - struct regmap *regmap = bmi270_device->regmap; + struct device *dev = data->dev; + struct regmap *regmap = data->regmap; ret = regmap_set_bits(regmap, BMI270_PWR_CTRL_REG, BMI270_PWR_CTRL_AUX_EN_MSK | BMI270_PWR_CTRL_GYR_EN_MSK | - BMI270_PWR_CTRL_ACCEL_EN_MSK); + BMI270_PWR_CTRL_ACCEL_EN_MSK | + BMI270_PWR_CTRL_TEMP_EN_MSK); if (ret) return dev_err_probe(dev, ret, "Failed to enable accelerometer and gyroscope"); @@ -677,38 +941,40 @@ static int bmi270_configure_imu(struct bmi270_data *bmi270_device) return 0; } -static int bmi270_chip_init(struct bmi270_data *bmi270_device) +static int bmi270_chip_init(struct bmi270_data *data) { int ret; - ret = bmi270_validate_chip_id(bmi270_device); + ret = bmi270_validate_chip_id(data); if (ret) return ret; - ret = bmi270_write_calibration_data(bmi270_device); + ret = bmi270_write_calibration_data(data); if (ret) return ret; - return bmi270_configure_imu(bmi270_device); + return bmi270_configure_imu(data); } int bmi270_core_probe(struct device *dev, struct regmap *regmap, const struct bmi270_chip_info *chip_info) { int ret; - struct bmi270_data *bmi270_device; + struct bmi270_data *data; struct iio_dev *indio_dev; - indio_dev = devm_iio_device_alloc(dev, sizeof(*bmi270_device)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; - bmi270_device = iio_priv(indio_dev); - bmi270_device->dev = dev; - bmi270_device->regmap = regmap; - bmi270_device->chip_info = chip_info; + data = iio_priv(indio_dev); + data->dev = dev; + data->regmap = regmap; + data->chip_info = chip_info; + data->irq_pin = BMI270_IRQ_DISABLED; + mutex_init(&data->mutex); - ret = bmi270_chip_init(bmi270_device); + ret = bmi270_chip_init(data); if (ret) return ret; @@ -719,6 +985,10 @@ int bmi270_core_probe(struct device *dev, struct regmap *regmap, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &bmi270_info; + ret = bmi270_trigger_probe(data, indio_dev); + if (ret) + return ret; + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, bmi270_trigger_handler, NULL); diff --git a/drivers/iio/imu/bmi323/bmi323_core.c b/drivers/iio/imu/bmi323/bmi323_core.c index 7f386c5e58b4..fc54d464a3ae 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -1702,26 +1702,30 @@ static int bmi323_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct bmi323_data *data = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return bmi323_set_odr(data, - bmi323_iio_to_sensor(chan->type), - val, val2); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi323_set_odr(data, bmi323_iio_to_sensor(chan->type), + val, val2); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SCALE: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return bmi323_set_scale(data, - bmi323_iio_to_sensor(chan->type), - val, val2); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi323_set_scale(data, bmi323_iio_to_sensor(chan->type), + val, val2); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return bmi323_set_average(data, - bmi323_iio_to_sensor(chan->type), - val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi323_set_average(data, bmi323_iio_to_sensor(chan->type), + val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_ENABLE: return bmi323_enable_steps(data, val); case IIO_CHAN_INFO_PROCESSED: { @@ -1747,6 +1751,7 @@ static int bmi323_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct bmi323_data *data = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_PROCESSED: @@ -1755,10 +1760,11 @@ static int bmi323_read_raw(struct iio_dev *indio_dev, switch (chan->type) { case IIO_ACCEL: case IIO_ANGL_VEL: - iio_device_claim_direct_scoped(return -EBUSY, - indio_dev) - return bmi323_read_axis(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = bmi323_read_axis(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_TEMP: return bmi323_get_temp_data(data, val); default: diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c index 0732cfa258c4..8cc071463249 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -7,7 +7,7 @@ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> */ -#include <linux/device.h> +#include <linux/device/devres.h> #include <linux/err.h> #include <linux/gfp_types.h> #include <linux/i2c.h> diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 43ec57c1e604..806e55f75f65 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -7,7 +7,7 @@ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> */ -#include <linux/device.h> +#include <linux/device/devres.h> #include <linux/err.h> #include <linux/gfp_types.h> #include <linux/module.h> diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 363281272035..a43c8d1bb3d0 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -155,10 +155,12 @@ static ssize_t iio_backend_debugfs_write_reg(struct file *file, ssize_t rc; int ret; - rc = simple_write_to_buffer(buf, sizeof(buf), ppos, userbuf, count); + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count); if (rc < 0) return rc; + buf[count] = '\0'; + ret = sscanf(buf, "%i %i", &back->cached_reg_addr, &val); switch (ret) { @@ -637,6 +639,66 @@ ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private, EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, "IIO_BACKEND"); /** + * iio_backend_interface_type_get - get the interface type used. + * @back: Backend device + * @type: Interface type + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_interface_type_get(struct iio_backend *back, + enum iio_backend_interface_type *type) +{ + int ret; + + ret = iio_backend_op_call(back, interface_type_get, type); + if (ret) + return ret; + + if (*type >= IIO_BACKEND_INTERFACE_MAX) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_interface_type_get, "IIO_BACKEND"); + +/** + * iio_backend_data_size_set - set the data width/size in the data bus. + * @back: Backend device + * @size: Size in bits + * + * Some frontend devices can dynamically control the word/data size on the + * interface/data bus. Hence, the backend device needs to be aware of it so + * data can be correctly transferred. + * + * Return: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_size_set(struct iio_backend *back, unsigned int size) +{ + if (!size) + return -EINVAL; + + return iio_backend_op_call(back, data_size_set, size); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_size_set, "IIO_BACKEND"); + +/** + * iio_backend_oversampling_ratio_set - set the oversampling ratio + * @back: Backend device + * @ratio: The oversampling ratio - value 1 corresponds to no oversampling. + * + * Return: + * 0 on success, negative error number on failure. + */ +int iio_backend_oversampling_ratio_set(struct iio_backend *back, + unsigned int ratio) +{ + return iio_backend_op_call(back, oversampling_ratio_set, ratio); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_oversampling_ratio_set, "IIO_BACKEND"); + +/** * iio_backend_extend_chan_spec - Extend an IIO channel * @back: Backend device * @chan: IIO channel diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index a2117ad1337d..b9f4113ae5fc 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -410,11 +410,12 @@ static ssize_t iio_debugfs_write_reg(struct file *file, char buf[80]; int ret; - count = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, userbuf, count)) - return -EFAULT; + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, + count); + if (ret < 0) + return ret; - buf[count] = 0; + buf[count] = '\0'; ret = sscanf(buf, "%i %i", ®, &val); diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index db06501b0e61..06295cfc2da8 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -232,6 +232,7 @@ static const char * const iio_ev_type_text[] = { [IIO_EV_TYPE_CHANGE] = "change", [IIO_EV_TYPE_MAG_REFERENCED] = "mag_referenced", [IIO_EV_TYPE_GESTURE] = "gesture", + [IIO_EV_TYPE_FAULT] = "fault", }; static const char * const iio_ev_dir_text[] = { @@ -240,6 +241,7 @@ static const char * const iio_ev_dir_text[] = { [IIO_EV_DIR_FALLING] = "falling", [IIO_EV_DIR_SINGLETAP] = "singletap", [IIO_EV_DIR_DOUBLETAP] = "doubletap", + [IIO_EV_DIR_FAULT_OPENWIRE] = "openwire", }; static const char * const iio_ev_info_text[] = { diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index d70ebe3bf774..f35c36fd4a55 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -160,16 +160,123 @@ static void iio_gts_purge_avail_scale_table(struct iio_gts *gts) gts->num_avail_all_scales = 0; } +static int scale_eq(int *sc1, int *sc2) +{ + return sc1[0] == sc2[0] && sc1[1] == sc2[1]; +} + +static int scale_smaller(int *sc1, int *sc2) +{ + if (sc1[0] != sc2[0]) + return sc1[0] < sc2[0]; + + /* If integer parts are equal, fixp parts */ + return sc1[1] < sc2[1]; +} + +/* + * Do a single table listing all the unique scales that any combination of + * supported gains and times can provide. + */ +static int do_combined_scaletable(struct iio_gts *gts, + size_t all_scales_tbl_bytes) +{ + int t_idx, i, new_idx; + int **scales = gts->per_time_avail_scale_tables; + int *all_scales = kcalloc(gts->num_itime, all_scales_tbl_bytes, + GFP_KERNEL); + + if (!all_scales) + return -ENOMEM; + /* + * Create table containing all of the supported scales by looping + * through all of the per-time scales and copying the unique scales + * into one sorted table. + * + * We assume all the gains for same integration time were unique. + * It is likely the first time table had greatest time multiplier as + * the times are in the order of preference and greater times are + * usually preferred. Hence we start from the last table which is likely + * to have the smallest total gains. + */ + t_idx = gts->num_itime - 1; + memcpy(all_scales, scales[t_idx], all_scales_tbl_bytes); + new_idx = gts->num_hwgain * 2; + + while (t_idx-- > 0) { + for (i = 0; i < gts->num_hwgain ; i++) { + int *candidate = &scales[t_idx][i * 2]; + int chk; + + if (scale_smaller(candidate, &all_scales[new_idx - 2])) { + all_scales[new_idx] = candidate[0]; + all_scales[new_idx + 1] = candidate[1]; + new_idx += 2; + + continue; + } + for (chk = 0; chk < new_idx; chk += 2) + if (!scale_smaller(candidate, &all_scales[chk])) + break; + + if (scale_eq(candidate, &all_scales[chk])) + continue; + + memmove(&all_scales[chk + 2], &all_scales[chk], + (new_idx - chk) * sizeof(int)); + all_scales[chk] = candidate[0]; + all_scales[chk + 1] = candidate[1]; + new_idx += 2; + } + } + + gts->num_avail_all_scales = new_idx / 2; + gts->avail_all_scales_table = all_scales; + + return 0; +} + +static void iio_gts_free_int_table_array(int **arr, int num_tables) +{ + int i; + + for (i = 0; i < num_tables; i++) + kfree(arr[i]); + + kfree(arr); +} + +static int iio_gts_alloc_int_table_array(int ***arr, int num_tables, int num_table_items) +{ + int i, **tmp; + + tmp = kcalloc(num_tables, sizeof(**arr), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + for (i = 0; i < num_tables; i++) { + tmp[i] = kcalloc(num_table_items, sizeof(int), GFP_KERNEL); + if (!tmp[i]) + goto err_free; + } + + *arr = tmp; + + return 0; +err_free: + iio_gts_free_int_table_array(tmp, i); + + return -ENOMEM; +} + static int iio_gts_gain_cmp(const void *a, const void *b) { return *(int *)a - *(int *)b; } -static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) +static int fill_and_sort_scaletables(struct iio_gts *gts, int **gains, int **scales) { - int i, j, new_idx, time_idx, ret = 0; - int *all_gains; - size_t gain_bytes; + int i, j, ret; for (i = 0; i < gts->num_itime; i++) { /* @@ -189,71 +296,69 @@ static int gain_to_scaletables(struct iio_gts *gts, int **gains, int **scales) } } - gain_bytes = array_size(gts->num_hwgain, sizeof(int)); - all_gains = kcalloc(gts->num_itime, gain_bytes, GFP_KERNEL); - if (!all_gains) - return -ENOMEM; + return 0; +} + +static void compute_per_time_gains(struct iio_gts *gts, int **gains) +{ + int i, j; + + for (i = 0; i < gts->num_itime; i++) { + for (j = 0; j < gts->num_hwgain; j++) + gains[i][j] = gts->hwgain_table[j].gain * + gts->itime_table[i].mul; + } +} + +static int compute_per_time_tables(struct iio_gts *gts, int **scales) +{ + int **per_time_gains; + int ret; /* - * We assume all the gains for same integration time were unique. - * It is likely the first time table had greatest time multiplier as - * the times are in the order of preference and greater times are - * usually preferred. Hence we start from the last table which is likely - * to have the smallest total gains. + * Create a temporary array of the 'total gains' for each integration + * time. */ - time_idx = gts->num_itime - 1; - memcpy(all_gains, gains[time_idx], gain_bytes); - new_idx = gts->num_hwgain; + ret = iio_gts_alloc_int_table_array(&per_time_gains, gts->num_itime, + gts->num_hwgain); + if (ret) + return ret; - while (time_idx-- > 0) { - for (j = 0; j < gts->num_hwgain; j++) { - int candidate = gains[time_idx][j]; - int chk; + compute_per_time_gains(gts, per_time_gains); - if (candidate > all_gains[new_idx - 1]) { - all_gains[new_idx] = candidate; - new_idx++; + /* Convert the gains to scales and populate the scale tables */ + ret = fill_and_sort_scaletables(gts, per_time_gains, scales); - continue; - } - for (chk = 0; chk < new_idx; chk++) - if (candidate <= all_gains[chk]) - break; + iio_gts_free_int_table_array(per_time_gains, gts->num_itime); - if (candidate == all_gains[chk]) - continue; + return ret; +} - memmove(&all_gains[chk + 1], &all_gains[chk], - (new_idx - chk) * sizeof(int)); - all_gains[chk] = candidate; - new_idx++; - } - } +/* + * Create a table of supported scales for each supported integration time. + * This can be used as available_scales by drivers which don't allow scale + * setting to change the integration time to display correct set of scales + * depending on the used integration time. + */ +static int **create_per_time_scales(struct iio_gts *gts) +{ + int **per_time_scales, ret; - gts->avail_all_scales_table = kcalloc(new_idx, 2 * sizeof(int), - GFP_KERNEL); - if (!gts->avail_all_scales_table) { - ret = -ENOMEM; - goto free_out; - } - gts->num_avail_all_scales = new_idx; + ret = iio_gts_alloc_int_table_array(&per_time_scales, gts->num_itime, + gts->num_hwgain * 2); + if (ret) + return ERR_PTR(ret); - for (i = 0; i < gts->num_avail_all_scales; i++) { - ret = iio_gts_total_gain_to_scale(gts, all_gains[i], - >s->avail_all_scales_table[i * 2], - >s->avail_all_scales_table[i * 2 + 1]); + ret = compute_per_time_tables(gts, per_time_scales); + if (ret) + goto err_out; - if (ret) { - kfree(gts->avail_all_scales_table); - gts->num_avail_all_scales = 0; - goto free_out; - } - } + return per_time_scales; -free_out: - kfree(all_gains); +err_out: + iio_gts_free_int_table_array(per_time_scales, gts->num_itime); - return ret; + return ERR_PTR(ret); } /** @@ -275,55 +380,26 @@ free_out: */ static int iio_gts_build_avail_scale_table(struct iio_gts *gts) { - int **per_time_gains, **per_time_scales, i, j, ret = -ENOMEM; + int ret, all_scales_tbl_bytes; + int **per_time_scales; - per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL); - if (!per_time_gains) - return ret; - - per_time_scales = kcalloc(gts->num_itime, sizeof(*per_time_scales), GFP_KERNEL); - if (!per_time_scales) - goto free_gains; - - for (i = 0; i < gts->num_itime; i++) { - per_time_scales[i] = kcalloc(gts->num_hwgain, 2 * sizeof(int), - GFP_KERNEL); - if (!per_time_scales[i]) - goto err_free_out; - - per_time_gains[i] = kcalloc(gts->num_hwgain, sizeof(int), - GFP_KERNEL); - if (!per_time_gains[i]) { - kfree(per_time_scales[i]); - goto err_free_out; - } - - for (j = 0; j < gts->num_hwgain; j++) - per_time_gains[i][j] = gts->hwgain_table[j].gain * - gts->itime_table[i].mul; - } + if (unlikely(check_mul_overflow(gts->num_hwgain, 2 * sizeof(int), + &all_scales_tbl_bytes))) + return -EOVERFLOW; - ret = gain_to_scaletables(gts, per_time_gains, per_time_scales); - if (ret) - goto err_free_out; + per_time_scales = create_per_time_scales(gts); + if (IS_ERR(per_time_scales)) + return PTR_ERR(per_time_scales); - for (i = 0; i < gts->num_itime; i++) - kfree(per_time_gains[i]); - kfree(per_time_gains); gts->per_time_avail_scale_tables = per_time_scales; - return 0; - -err_free_out: - for (i--; i >= 0; i--) { - kfree(per_time_scales[i]); - kfree(per_time_gains[i]); + ret = do_combined_scaletable(gts, all_scales_tbl_bytes); + if (ret) { + iio_gts_free_int_table_array(per_time_scales, gts->num_itime); + return ret; } - kfree(per_time_scales); -free_gains: - kfree(per_time_gains); - return ret; + return 0; } static void iio_gts_us_to_int_micro(int *time_us, int *int_micro_times, @@ -950,7 +1026,15 @@ int iio_gts_find_gain_time_sel_for_scale(struct iio_gts *gts, int scale_int, } EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_time_sel_for_scale, "IIO_GTS_HELPER"); -static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) +/** + * iio_gts_get_total_gain - Fetch total gain for given HW-gain and time + * @gts: Gain time scale descriptor + * @gain: HW-gain for which the total gain is searched for + * @time: Integration time for which the total gain is searched for + * + * Return: total gain on success and -EINVAL on error. + */ +int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) { const struct iio_itime_sel_mul *itime; @@ -966,6 +1050,7 @@ static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) return gain * itime->mul; } +EXPORT_SYMBOL_NS_GPL(iio_gts_get_total_gain, "IIO_GTS_HELPER"); static int iio_gts_get_scale_linear(struct iio_gts *gts, int gain, int time, u64 *scale) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index e34e551eef3e..4a7d983c9cd4 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -43,6 +43,16 @@ config ADUX1020 To compile this driver as a module, choose M here: the module will be called adux1020. +config AL3000A + tristate "AL3000a ambient light sensor" + depends on I2C + help + Say Y here if you want to build a driver for the Dyna Image AL3000a + ambient light sensor. + + To compile this driver as a module, choose M here: the + module will be called al3000a. + config AL3010 tristate "AL3010 ambient light sensor" depends on I2C @@ -63,6 +73,17 @@ config AL3320A To compile this driver as a module, choose M here: the module will be called al3320a. +config APDS9160 + tristate "APDS9160 combined als and proximity sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here if you want to build support for a Broadcom APDS9160 + combined ambient light and proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called apds9160. + config APDS9300 tristate "APDS9300 ambient light sensor" depends on I2C @@ -683,6 +704,7 @@ config VEML6030 select REGMAP_I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select IIO_GTS_HELPER depends on I2C help Say Y here if you want to build a driver for the Vishay VEML6030 diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 11a4041b918a..8229ebe6edc4 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -7,8 +7,10 @@ obj-$(CONFIG_ACPI_ALS) += acpi-als.o obj-$(CONFIG_ADJD_S311) += adjd_s311.o obj-$(CONFIG_ADUX1020) += adux1020.o +obj-$(CONFIG_AL3000A) += al3000a.o obj-$(CONFIG_AL3010) += al3010.o obj-$(CONFIG_AL3320A) += al3320a.o +obj-$(CONFIG_APDS9160) += apds9160.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_APDS9306) += apds9306.o obj-$(CONFIG_APDS9960) += apds9960.o diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 593d614b1689..9240983a6cc4 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -118,7 +118,6 @@ static const struct regmap_config adux1020_regmap_config = { .reg_bits = 8, .val_bits = 16, .max_register = 0x6F, - .cache_type = REGCACHE_NONE, }; static const struct reg_sequence adux1020_def_conf[] = { diff --git a/drivers/iio/light/al3000a.c b/drivers/iio/light/al3000a.c new file mode 100644 index 000000000000..e2fbb1270040 --- /dev/null +++ b/drivers/iio/light/al3000a.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> + +#define AL3000A_REG_SYSTEM 0x00 +#define AL3000A_REG_DATA 0x05 + +#define AL3000A_CONFIG_ENABLE 0x00 +#define AL3000A_CONFIG_DISABLE 0x0b +#define AL3000A_CONFIG_RESET 0x0f +#define AL3000A_GAIN_MASK GENMASK(5, 0) + +/* + * These are pre-calculated lux values based on possible output of sensor + * (range 0x00 - 0x3F) + */ +static const u32 lux_table[] = { + 1, 1, 1, 2, 2, 2, 3, 4, /* 0 - 7 */ + 4, 5, 6, 7, 9, 11, 13, 16, /* 8 - 15 */ + 19, 22, 27, 32, 39, 46, 56, 67, /* 16 - 23 */ + 80, 96, 116, 139, 167, 200, 240, 289, /* 24 - 31 */ + 347, 416, 499, 600, 720, 864, 1037, 1245, /* 32 - 39 */ + 1495, 1795, 2155, 2587, 3105, 3728, 4475, 5373, /* 40 - 47 */ + 6450, 7743, 9296, 11160, 13397, 16084, 19309, 23180, /* 48 - 55 */ + 27828, 33408, 40107, 48148, 57803, 69393, 83306, 100000 /* 56 - 63 */ +}; + +static const struct regmap_config al3000a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AL3000A_REG_DATA, +}; + +struct al3000a_data { + struct regmap *regmap; + struct regulator *vdd_supply; +}; + +static const struct iio_chan_spec al3000a_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +static int al3000a_set_pwr_on(struct al3000a_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = regulator_enable(data->vdd_supply); + if (ret) { + dev_err(dev, "failed to enable vdd power supply\n"); + return ret; + } + + return regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_ENABLE); +} + +static void al3000a_set_pwr_off(void *_data) +{ + struct al3000a_data *data = _data; + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_DISABLE); + if (ret) + dev_err(dev, "failed to write system register\n"); + + ret = regulator_disable(data->vdd_supply); + if (ret) + dev_err(dev, "failed to disable vdd power supply\n"); +} + +static int al3000a_init(struct al3000a_data *data) +{ + int ret; + + ret = al3000a_set_pwr_on(data); + if (ret) + return ret; + + ret = regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_RESET); + if (ret) + return ret; + + return regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_ENABLE); +} + +static int al3000a_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct al3000a_data *data = iio_priv(indio_dev); + int ret, gain; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = regmap_read(data->regmap, AL3000A_REG_DATA, &gain); + if (ret) + return ret; + + *val = lux_table[gain & AL3000A_GAIN_MASK]; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info al3000a_info = { + .read_raw = al3000a_read_raw, +}; + +static int al3000a_probe(struct i2c_client *client) +{ + struct al3000a_data *data; + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + data->regmap = devm_regmap_init_i2c(client, &al3000a_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "cannot allocate regmap\n"); + + data->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd_supply)) + return dev_err_probe(dev, PTR_ERR(data->vdd_supply), + "failed to get vdd regulator\n"); + + indio_dev->info = &al3000a_info; + indio_dev->name = "al3000a"; + indio_dev->channels = al3000a_channels; + indio_dev->num_channels = ARRAY_SIZE(al3000a_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = al3000a_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to init ALS\n"); + + ret = devm_add_action_or_reset(dev, al3000a_set_pwr_off, data); + if (ret) + return dev_err_probe(dev, ret, "failed to add action\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static int al3000a_suspend(struct device *dev) +{ + struct al3000a_data *data = iio_priv(dev_get_drvdata(dev)); + + al3000a_set_pwr_off(data); + return 0; +} + +static int al3000a_resume(struct device *dev) +{ + struct al3000a_data *data = iio_priv(dev_get_drvdata(dev)); + + return al3000a_set_pwr_on(data); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(al3000a_pm_ops, al3000a_suspend, al3000a_resume); + +static const struct i2c_device_id al3000a_id[] = { + { "al3000a" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, al3000a_id); + +static const struct of_device_id al3000a_of_match[] = { + { .compatible = "dynaimage,al3000a" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, al3000a_of_match); + +static struct i2c_driver al3000a_driver = { + .driver = { + .name = "al3000a", + .of_match_table = al3000a_of_match, + .pm = pm_sleep_ptr(&al3000a_pm_ops), + }, + .probe = al3000a_probe, + .id_table = al3000a_id, +}; +module_i2c_driver(al3000a_driver); + +MODULE_AUTHOR("Svyatolsav Ryhel <clamor95@gmail.com>"); +MODULE_DESCRIPTION("al3000a Ambient Light Sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c new file mode 100644 index 000000000000..d3f415930ec9 --- /dev/null +++ b/drivers/iio/light/apds9160.c @@ -0,0 +1,1594 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * APDS9160 sensor driver. + * Chip is combined proximity and ambient light sensor. + * Author: 2024 Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com> + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> +#include <linux/iio/events.h> +#include <linux/iio/sysfs.h> + +#include <linux/unaligned.h> + +#define APDS9160_REGMAP_NAME "apds9160_regmap" + +/* Main control register */ +#define APDS9160_REG_CTRL 0x00 +#define APDS9160_CTRL_SWRESET BIT(4) /* 1: Activate reset */ +#define APDS9160_CTRL_MODE_RGB BIT(2) /* 0: ALS & IR, 1: RGB & IR */ +#define APDS9160_CTRL_EN_ALS BIT(1) /* 1: ALS active */ +#define APDS9160_CTLR_EN_PS BIT(0) /* 1: PS active */ + +/* Status register */ +#define APDS9160_SR_LS_INT BIT(4) +#define APDS9160_SR_LS_NEW_DATA BIT(3) +#define APDS9160_SR_PS_INT BIT(1) +#define APDS9160_SR_PS_NEW_DATA BIT(0) + +/* Interrupt configuration registers */ +#define APDS9160_REG_INT_CFG 0x19 +#define APDS9160_REG_INT_PST 0x1A +#define APDS9160_INT_CFG_EN_LS BIT(2) /* LS int enable */ +#define APDS9160_INT_CFG_EN_PS BIT(0) /* PS int enable */ + +/* Proximity registers */ +#define APDS9160_REG_PS_LED 0x01 +#define APDS9160_REG_PS_PULSES 0x02 +#define APDS9160_REG_PS_MEAS_RATE 0x03 +#define APDS9160_REG_PS_THRES_HI_LSB 0x1B +#define APDS9160_REG_PS_THRES_HI_MSB 0x1C +#define APDS9160_REG_PS_THRES_LO_LSB 0x1D +#define APDS9160_REG_PS_THRES_LO_MSB 0x1E +#define APDS9160_REG_PS_DATA_LSB 0x08 +#define APDS9160_REG_PS_DATA_MSB 0x09 +#define APDS9160_REG_PS_CAN_LEVEL_DIG_LSB 0x1F +#define APDS9160_REG_PS_CAN_LEVEL_DIG_MSB 0x20 +#define APDS9160_REG_PS_CAN_LEVEL_ANA_DUR 0x21 +#define APDS9160_REG_PS_CAN_LEVEL_ANA_CURRENT 0x22 + +/* Light sensor registers */ +#define APDS9160_REG_LS_MEAS_RATE 0x04 +#define APDS9160_REG_LS_GAIN 0x05 +#define APDS9160_REG_LS_DATA_CLEAR_LSB 0x0A +#define APDS9160_REG_LS_DATA_CLEAR 0x0B +#define APDS9160_REG_LS_DATA_CLEAR_MSB 0x0C +#define APDS9160_REG_LS_DATA_ALS_LSB 0x0D +#define APDS9160_REG_LS_DATA_ALS 0x0E +#define APDS9160_REG_LS_DATA_ALS_MSB 0x0F +#define APDS9160_REG_LS_THRES_UP_LSB 0x24 +#define APDS9160_REG_LS_THRES_UP 0x25 +#define APDS9160_REG_LS_THRES_UP_MSB 0x26 +#define APDS9160_REG_LS_THRES_LO_LSB 0x27 +#define APDS9160_REG_LS_THRES_LO 0x28 +#define APDS9160_REG_LS_THRES_LO_MSB 0x29 +#define APDS9160_REG_LS_THRES_VAR 0x2A + +/* Part identification number register */ +#define APDS9160_REG_ID 0x06 + +/* Status register */ +#define APDS9160_REG_SR 0x07 +#define APDS9160_SR_DATA_ALS BIT(3) +#define APDS9160_SR_DATA_PS BIT(0) + +/* Supported ID:s */ +#define APDS9160_PART_ID_0 0x03 + +#define APDS9160_PS_THRES_MAX 0x7FF +#define APDS9160_LS_THRES_MAX 0xFFFFF +#define APDS9160_CMD_LS_RESOLUTION_25MS 0x04 +#define APDS9160_CMD_LS_RESOLUTION_50MS 0x03 +#define APDS9160_CMD_LS_RESOLUTION_100MS 0x02 +#define APDS9160_CMD_LS_RESOLUTION_200MS 0x01 +#define APDS9160_PS_DATA_MASK 0x7FF + +#define APDS9160_DEFAULT_LS_GAIN 3 +#define APDS9160_DEFAULT_LS_RATE 100 +#define APDS9160_DEFAULT_PS_RATE 100 +#define APDS9160_DEFAULT_PS_CANCELLATION_LEVEL 0 +#define APDS9160_DEFAULT_PS_ANALOG_CANCELLATION 0 +#define APDS9160_DEFAULT_PS_GAIN 1 +#define APDS9160_DEFAULT_PS_CURRENT 100 +#define APDS9160_DEFAULT_PS_RESOLUTION_11BITS 0x03 + +static const struct reg_default apds9160_reg_defaults[] = { + { APDS9160_REG_CTRL, 0x00 }, /* Sensors disabled by default */ + { APDS9160_REG_PS_LED, 0x33 }, /* 60 kHz frequency, 100 mA */ + { APDS9160_REG_PS_PULSES, 0x08 }, /* 8 pulses */ + { APDS9160_REG_PS_MEAS_RATE, 0x05 }, /* 100ms */ + { APDS9160_REG_LS_MEAS_RATE, 0x22 }, /* 100ms */ + { APDS9160_REG_LS_GAIN, 0x01 }, /* 3x */ + { APDS9160_REG_INT_CFG, 0x10 }, /* Interrupts disabled */ + { APDS9160_REG_INT_PST, 0x00 }, + { APDS9160_REG_PS_THRES_HI_LSB, 0xFF }, + { APDS9160_REG_PS_THRES_HI_MSB, 0x07 }, + { APDS9160_REG_PS_THRES_LO_LSB, 0x00 }, + { APDS9160_REG_PS_THRES_LO_MSB, 0x00 }, + { APDS9160_REG_PS_CAN_LEVEL_DIG_LSB, 0x00 }, + { APDS9160_REG_PS_CAN_LEVEL_DIG_MSB, 0x00 }, + { APDS9160_REG_PS_CAN_LEVEL_ANA_DUR, 0x00 }, + { APDS9160_REG_PS_CAN_LEVEL_ANA_CURRENT, 0x00 }, + { APDS9160_REG_LS_THRES_UP_LSB, 0xFF }, + { APDS9160_REG_LS_THRES_UP, 0xFF }, + { APDS9160_REG_LS_THRES_UP_MSB, 0x0F }, + { APDS9160_REG_LS_THRES_LO_LSB, 0x00 }, + { APDS9160_REG_LS_THRES_LO, 0x00 }, + { APDS9160_REG_LS_THRES_LO_MSB, 0x00 }, + { APDS9160_REG_LS_THRES_VAR, 0x00 }, +}; + +static const struct regmap_range apds9160_readable_ranges[] = { + regmap_reg_range(APDS9160_REG_CTRL, APDS9160_REG_LS_THRES_VAR), +}; + +static const struct regmap_access_table apds9160_readable_table = { + .yes_ranges = apds9160_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9160_readable_ranges), +}; + +static const struct regmap_range apds9160_writeable_ranges[] = { + regmap_reg_range(APDS9160_REG_CTRL, APDS9160_REG_LS_GAIN), + regmap_reg_range(APDS9160_REG_INT_CFG, APDS9160_REG_LS_THRES_VAR), +}; + +static const struct regmap_access_table apds9160_writeable_table = { + .yes_ranges = apds9160_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9160_writeable_ranges), +}; + +static const struct regmap_range apds9160_volatile_ranges[] = { + regmap_reg_range(APDS9160_REG_SR, APDS9160_REG_LS_DATA_ALS_MSB), +}; + +static const struct regmap_access_table apds9160_volatile_table = { + .yes_ranges = apds9160_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9160_volatile_ranges), +}; + +static const struct regmap_config apds9160_regmap_config = { + .name = APDS9160_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + + .rd_table = &apds9160_readable_table, + .wr_table = &apds9160_writeable_table, + .volatile_table = &apds9160_volatile_table, + + .reg_defaults = apds9160_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(apds9160_reg_defaults), + .max_register = 37, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct iio_event_spec apds9160_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec apds9160_channels[] = { + { + /* Proximity sensor channel */ + .type = IIO_PROXIMITY, + .address = APDS9160_REG_PS_DATA_LSB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .event_spec = apds9160_event_spec, + .num_event_specs = ARRAY_SIZE(apds9160_event_spec), + }, + { + /* Proximity sensor led current */ + .type = IIO_CURRENT, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + }, + { + /* Illuminance */ + .type = IIO_LIGHT, + .address = APDS9160_REG_LS_DATA_ALS_LSB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .event_spec = apds9160_event_spec, + .num_event_specs = ARRAY_SIZE(apds9160_event_spec), + }, + { + /* Clear channel */ + .type = IIO_INTENSITY, + .address = APDS9160_REG_LS_DATA_CLEAR_LSB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .channel2 = IIO_MOD_LIGHT_CLEAR, + .modified = 1, + }, +}; + +static const struct iio_chan_spec apds9160_channels_without_events[] = { + { + /* Proximity sensor channel */ + .type = IIO_PROXIMITY, + .address = APDS9160_REG_PS_DATA_LSB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + /* Proximity sensor led current */ + .type = IIO_CURRENT, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + }, + { + /* Illuminance */ + .type = IIO_LIGHT, + .address = APDS9160_REG_LS_DATA_ALS_LSB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + /* Clear channel */ + .type = IIO_INTENSITY, + .address = APDS9160_REG_LS_DATA_CLEAR_LSB, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .channel2 = IIO_MOD_LIGHT_CLEAR, + .modified = 1, + }, +}; + +static const int apds9160_als_rate_avail[] = { + 25, 50, 100, 200 +}; + +static const int apds9160_als_rate_map[][2] = { + { 25, 0x00 }, + { 50, 0x01 }, + { 100, 0x02 }, + { 200, 0x03 }, +}; + +static const int apds9160_als_gain_map[][2] = { + { 1, 0x00 }, + { 3, 0x01 }, + { 6, 0x02 }, + { 18, 0x03 }, + { 54, 0x04 }, +}; + +static const int apds9160_ps_gain_avail[] = { + 1, 2, 4, 8 +}; + +static const int apds9160_ps_gain_map[][2] = { + { 1, 0x00 }, + { 2, 0x01 }, + { 4, 0x02 }, + { 8, 0x03 }, +}; + +static const int apds9160_ps_rate_avail[] = { + 25, 50, 100, 200, 400 +}; + +static const int apds9160_ps_rate_map[][2] = { + { 25, 0x03 }, + { 50, 0x04 }, + { 100, 0x05 }, + { 200, 0x06 }, + { 400, 0x07 }, +}; + +static const int apds9160_ps_led_current_avail[] = { + 10, 25, 50, 100, 150, 175, 200 +}; + +static const int apds9160_ps_led_current_map[][2] = { + { 10, 0x00 }, + { 25, 0x01 }, + { 50, 0x02 }, + { 100, 0x03 }, + { 150, 0x04 }, + { 175, 0x05 }, + { 200, 0x06 }, +}; + +/** + * struct apds9160_scale - apds9160 scale mapping definition + * + * @itime: Integration time in ms + * @gain: Gain multiplier + * @scale1: lux/count resolution + * @scale2: micro lux/count + */ +struct apds9160_scale { + int itime; + int gain; + int scale1; + int scale2; +}; + +/* Scale mapping extracted from datasheet */ +static const struct apds9160_scale apds9160_als_scale_map[] = { + { + .itime = 25, + .gain = 1, + .scale1 = 3, + .scale2 = 272000, + }, + { + .itime = 25, + .gain = 3, + .scale1 = 1, + .scale2 = 77000, + }, + { + .itime = 25, + .gain = 6, + .scale1 = 0, + .scale2 = 525000, + }, + { + .itime = 25, + .gain = 18, + .scale1 = 0, + .scale2 = 169000, + }, + { + .itime = 25, + .gain = 54, + .scale1 = 0, + .scale2 = 49000, + }, + { + .itime = 50, + .gain = 1, + .scale1 = 1, + .scale2 = 639000, + }, + { + .itime = 50, + .gain = 3, + .scale1 = 0, + .scale2 = 538000, + }, + { + .itime = 50, + .gain = 6, + .scale1 = 0, + .scale2 = 263000, + }, + { + .itime = 50, + .gain = 18, + .scale1 = 0, + .scale2 = 84000, + }, + { + .itime = 50, + .gain = 54, + .scale1 = 0, + .scale2 = 25000, + }, + { + .itime = 100, + .gain = 1, + .scale1 = 0, + .scale2 = 819000, + }, + { + .itime = 100, + .gain = 3, + .scale1 = 0, + .scale2 = 269000, + }, + { + .itime = 100, + .gain = 6, + .scale1 = 0, + .scale2 = 131000, + }, + { + .itime = 100, + .gain = 18, + .scale1 = 0, + .scale2 = 42000, + }, + { + .itime = 100, + .gain = 54, + .scale1 = 0, + .scale2 = 12000, + }, + { + .itime = 200, + .gain = 1, + .scale1 = 0, + .scale2 = 409000, + }, + { + .itime = 200, + .gain = 3, + .scale1 = 0, + .scale2 = 135000, + }, + { + .itime = 200, + .gain = 6, + .scale1 = 0, + .scale2 = 66000, + }, + { + .itime = 200, + .gain = 18, + .scale1 = 0, + .scale2 = 21000, + }, + { + .itime = 200, + .gain = 54, + .scale1 = 0, + .scale2 = 6000, + }, +}; + +static const int apds9160_25ms_avail[][2] = { + { 3, 272000 }, + { 1, 77000 }, + { 0, 525000 }, + { 0, 169000 }, + { 0, 49000 }, +}; + +static const int apds9160_50ms_avail[][2] = { + { 1, 639000 }, + { 0, 538000 }, + { 0, 263000 }, + { 0, 84000 }, + { 0, 25000 }, +}; + +static const int apds9160_100ms_avail[][2] = { + { 0, 819000 }, + { 0, 269000 }, + { 0, 131000 }, + { 0, 42000 }, + { 0, 12000 }, +}; + +static const int apds9160_200ms_avail[][2] = { + { 0, 409000 }, + { 0, 135000 }, + { 0, 66000 }, + { 0, 21000 }, + { 0, 6000 }, +}; + +static const struct reg_field apds9160_reg_field_ls_en = + REG_FIELD(APDS9160_REG_CTRL, 1, 1); + +static const struct reg_field apds9160_reg_field_ps_en = + REG_FIELD(APDS9160_REG_CTRL, 0, 0); + +static const struct reg_field apds9160_reg_field_int_ps = + REG_FIELD(APDS9160_REG_INT_CFG, 0, 0); + +static const struct reg_field apds9160_reg_field_int_als = + REG_FIELD(APDS9160_REG_INT_CFG, 2, 2); + +static const struct reg_field apds9160_reg_field_ps_overflow = + REG_FIELD(APDS9160_REG_PS_DATA_MSB, 3, 3); + +static const struct reg_field apds9160_reg_field_als_rate = + REG_FIELD(APDS9160_REG_LS_MEAS_RATE, 0, 2); + +static const struct reg_field apds9160_reg_field_als_gain = + REG_FIELD(APDS9160_REG_LS_GAIN, 0, 2); + +static const struct reg_field apds9160_reg_field_ps_rate = + REG_FIELD(APDS9160_REG_PS_MEAS_RATE, 0, 2); + +static const struct reg_field apds9160_reg_field_als_res = + REG_FIELD(APDS9160_REG_LS_MEAS_RATE, 4, 6); + +static const struct reg_field apds9160_reg_field_ps_current = + REG_FIELD(APDS9160_REG_PS_LED, 0, 2); + +static const struct reg_field apds9160_reg_field_ps_gain = + REG_FIELD(APDS9160_REG_PS_MEAS_RATE, 6, 7); + +static const struct reg_field apds9160_reg_field_ps_resolution = + REG_FIELD(APDS9160_REG_PS_MEAS_RATE, 3, 4); + +struct apds9160_chip { + struct i2c_client *client; + struct regmap *regmap; + + struct regmap_field *reg_enable_ps; + struct regmap_field *reg_enable_als; + struct regmap_field *reg_int_ps; + struct regmap_field *reg_int_als; + struct regmap_field *reg_ps_overflow; + struct regmap_field *reg_als_rate; + struct regmap_field *reg_als_resolution; + struct regmap_field *reg_ps_rate; + struct regmap_field *reg_als_gain; + struct regmap_field *reg_ps_current; + struct regmap_field *reg_ps_gain; + struct regmap_field *reg_ps_resolution; + + struct mutex lock; /* protects state and config data */ + + /* State data */ + int als_int; + int ps_int; + + /* Configuration values */ + int als_itime; + int als_hwgain; + int als_scale1; + int als_scale2; + int ps_rate; + int ps_cancellation_level; + int ps_current; + int ps_gain; +}; + +static int apds9160_set_ps_rate(struct apds9160_chip *data, int val) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9160_ps_rate_map); idx++) { + int ret; + + if (apds9160_ps_rate_map[idx][0] != val) + continue; + + ret = regmap_field_write(data->reg_ps_rate, + apds9160_ps_rate_map[idx][1]); + if (ret) + return ret; + data->ps_rate = val; + + return ret; + } + + return -EINVAL; +} + +static int apds9160_set_ps_gain(struct apds9160_chip *data, int val) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9160_ps_gain_map); idx++) { + int ret; + + if (apds9160_ps_gain_map[idx][0] != val) + continue; + + ret = regmap_field_write(data->reg_ps_gain, + apds9160_ps_gain_map[idx][1]); + if (ret) + return ret; + data->ps_gain = val; + + return ret; + } + + return -EINVAL; +} + +/* + * The PS intelligent cancellation level register allows + * for an on-chip substraction of the ADC count caused by + * unwanted reflected light from PS ADC output. + */ +static int apds9160_set_ps_cancellation_level(struct apds9160_chip *data, + int val) +{ + int ret; + __le16 buf; + + if (val < 0 || val > 0xFFFF) + return -EINVAL; + + buf = cpu_to_le16(val); + ret = regmap_bulk_write(data->regmap, APDS9160_REG_PS_CAN_LEVEL_DIG_LSB, + &buf, 2); + if (ret) + return ret; + + data->ps_cancellation_level = val; + + return ret; +} + +/* + * This parameter determines the cancellation pulse duration + * in each of the PWM pulse. The cancellation is applied during the + * integration phase of the PS measurement. + * Duration is programmed in half clock cycles + * A duration value of 0 or 1 will not generate any cancellation pulse + */ +static int apds9160_set_ps_analog_cancellation(struct apds9160_chip *data, + int val) +{ + if (val < 0 || val > 63) + return -EINVAL; + + return regmap_write(data->regmap, APDS9160_REG_PS_CAN_LEVEL_ANA_DUR, + val); +} + +/* + * This parameter works in conjunction with the cancellation pulse duration + * The value determines the current used for crosstalk cancellation + * Coarse value is in steps of 60 nA + * Fine value is in steps of 2.4 nA + */ +static int apds9160_set_ps_cancellation_current(struct apds9160_chip *data, + int coarse_val, + int fine_val) +{ + int val; + + if (coarse_val < 0 || coarse_val > 4) + return -EINVAL; + + if (fine_val < 0 || fine_val > 15) + return -EINVAL; + + /* Coarse value at B4:B5 and fine value at B0:B3 */ + val = (coarse_val << 4) | fine_val; + + return regmap_write(data->regmap, APDS9160_REG_PS_CAN_LEVEL_ANA_CURRENT, + val); +} + +static int apds9160_ps_init_analog_cancellation(struct device *dev, + struct apds9160_chip *data) +{ + int ret, duration, picoamp, idx, coarse, fine; + + ret = device_property_read_u32(dev, + "ps-cancellation-duration", &duration); + if (ret || duration == 0) { + /* Don't fail since this is not required */ + return 0; + } + + ret = device_property_read_u32(dev, + "ps-cancellation-current-picoamp", &picoamp); + if (ret) + return ret; + + if (picoamp < 60000 || picoamp > 276000 || picoamp % 2400 != 0) + return dev_err_probe(dev, -EINVAL, + "Invalid cancellation current\n"); + + /* Compute required coarse and fine value from requested current */ + fine = 0; + coarse = 0; + for (idx = 60000; idx < picoamp; idx += 2400) { + if (fine == 15) { + fine = 0; + coarse++; + idx += 21600; + } else { + fine++; + } + } + + if (picoamp != idx) + dev_warn(dev, + "Invalid cancellation current %i, rounding to %i\n", + picoamp, idx); + + ret = apds9160_set_ps_analog_cancellation(data, duration); + if (ret) + return ret; + + return apds9160_set_ps_cancellation_current(data, coarse, fine); +} + +static int apds9160_set_ps_current(struct apds9160_chip *data, int val) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9160_ps_led_current_map); idx++) { + int ret; + + if (apds9160_ps_led_current_map[idx][0] != val) + continue; + + ret = regmap_field_write( + data->reg_ps_current, + apds9160_ps_led_current_map[idx][1]); + if (ret) + return ret; + data->ps_current = val; + + return ret; + } + + return -EINVAL; +} + +static int apds9160_set_als_gain(struct apds9160_chip *data, int gain) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9160_als_gain_map); idx++) { + int ret; + + if (gain != apds9160_als_gain_map[idx][0]) + continue; + + ret = regmap_field_write(data->reg_als_gain, + apds9160_als_gain_map[idx][1]); + if (ret) + return ret; + data->als_hwgain = gain; + + return ret; + } + + return -EINVAL; +} + +static int apds9160_set_als_scale(struct apds9160_chip *data, int val, int val2) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9160_als_scale_map); idx++) { + if (apds9160_als_scale_map[idx].itime == data->als_itime && + apds9160_als_scale_map[idx].scale1 == val && + apds9160_als_scale_map[idx].scale2 == val2) { + int ret = apds9160_set_als_gain(data, + apds9160_als_scale_map[idx].gain); + if (ret) + return ret; + data->als_scale1 = val; + data->als_scale2 = val2; + + return ret; + } + } + + return -EINVAL; +} + +static int apds9160_set_als_resolution(struct apds9160_chip *data, int val) +{ + switch (val) { + case 25: + return regmap_field_write(data->reg_als_resolution, + APDS9160_CMD_LS_RESOLUTION_25MS); + case 50: + return regmap_field_write(data->reg_als_resolution, + APDS9160_CMD_LS_RESOLUTION_50MS); + case 200: + return regmap_field_write(data->reg_als_resolution, + APDS9160_CMD_LS_RESOLUTION_200MS); + default: + return regmap_field_write(data->reg_als_resolution, + APDS9160_CMD_LS_RESOLUTION_100MS); + } +} + +static int apds9160_set_als_rate(struct apds9160_chip *data, int val) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(apds9160_als_rate_map); idx++) { + if (apds9160_als_rate_map[idx][0] != val) + continue; + + return regmap_field_write(data->reg_als_rate, + apds9160_als_rate_map[idx][1]); + } + + return -EINVAL; +} + +/* + * Setting the integration time ajusts resolution, rate, scale and gain + */ +static int apds9160_set_als_int_time(struct apds9160_chip *data, int val) +{ + int ret; + int idx; + + ret = apds9160_set_als_rate(data, val); + if (ret) + return ret; + + /* Match resolution register with rate */ + ret = apds9160_set_als_resolution(data, val); + if (ret) + return ret; + + data->als_itime = val; + + /* Set the scale minimum gain */ + for (idx = 0; idx < ARRAY_SIZE(apds9160_als_scale_map); idx++) { + if (data->als_itime != apds9160_als_scale_map[idx].itime) + continue; + + return apds9160_set_als_scale(data, + apds9160_als_scale_map[idx].scale1, + apds9160_als_scale_map[idx].scale2); + } + + return -EINVAL; +} + +static int apds9160_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct apds9160_chip *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + switch (chan->type) { + case IIO_LIGHT: + *length = ARRAY_SIZE(apds9160_als_rate_avail); + *vals = (const int *)apds9160_als_rate_avail; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + case IIO_PROXIMITY: + *length = ARRAY_SIZE(apds9160_ps_rate_avail); + *vals = (const int *)apds9160_ps_rate_avail; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_PROXIMITY: + *length = ARRAY_SIZE(apds9160_ps_gain_avail); + *vals = (const int *)apds9160_ps_gain_avail; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + case IIO_LIGHT: + /* The available scales changes depending on itime */ + switch (data->als_itime) { + case 25: + *length = ARRAY_SIZE(apds9160_25ms_avail) * 2; + *vals = (const int *)apds9160_25ms_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + case 50: + *length = ARRAY_SIZE(apds9160_50ms_avail) * 2; + *vals = (const int *)apds9160_50ms_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + case 100: + *length = ARRAY_SIZE(apds9160_100ms_avail) * 2; + *vals = (const int *)apds9160_100ms_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + case 200: + *length = ARRAY_SIZE(apds9160_200ms_avail) * 2; + *vals = (const int *)apds9160_200ms_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_CURRENT: + *length = ARRAY_SIZE(apds9160_ps_led_current_avail); + *vals = (const int *)apds9160_ps_led_current_avail; + *type = IIO_VAL_INT; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static int apds9160_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + return IIO_VAL_INT; + case IIO_CHAN_INFO_HARDWAREGAIN: + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int apds9160_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct apds9160_chip *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_PROXIMITY: { + __le16 buf; + + ret = regmap_bulk_read(data->regmap, chan->address, + &buf, 2); + if (ret) + return ret; + *val = le16_to_cpu(buf); + /* Remove overflow bits from result */ + *val = FIELD_GET(APDS9160_PS_DATA_MASK, *val); + + return IIO_VAL_INT; + } + case IIO_LIGHT: + case IIO_INTENSITY: { + u8 buf[3]; + + ret = regmap_bulk_read(data->regmap, chan->address, + &buf, 3); + if (ret) + return ret; + *val = get_unaligned_le24(buf); + + return IIO_VAL_INT; + } + case IIO_CURRENT: + *val = data->ps_current; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->type) { + case IIO_LIGHT: + *val = data->als_hwgain; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_INT_TIME: + switch (chan->type) { + case IIO_PROXIMITY: + *val = data->ps_rate; + + return IIO_VAL_INT; + case IIO_LIGHT: + *val = data->als_itime; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + *val = data->ps_cancellation_level; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_PROXIMITY: + *val = data->ps_gain; + + return IIO_VAL_INT; + case IIO_LIGHT: + *val = data->als_scale1; + *val2 = data->als_scale2; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +}; + +static int apds9160_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct apds9160_chip *data = iio_priv(indio_dev); + + guard(mutex)(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val2 != 0) + return -EINVAL; + switch (chan->type) { + case IIO_PROXIMITY: + return apds9160_set_ps_rate(data, val); + case IIO_LIGHT: + return apds9160_set_als_int_time(data, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_PROXIMITY: + return apds9160_set_ps_gain(data, val); + case IIO_LIGHT: + return apds9160_set_als_scale(data, val, val2); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + if (val2 != 0) + return -EINVAL; + switch (chan->type) { + case IIO_PROXIMITY: + return apds9160_set_ps_cancellation_level(data, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + if (val2 != 0) + return -EINVAL; + switch (chan->type) { + case IIO_CURRENT: + return apds9160_set_ps_current(data, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static inline int apds9160_get_thres_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, u8 *reg) +{ + switch (dir) { + case IIO_EV_DIR_RISING: + switch (chan->type) { + case IIO_PROXIMITY: + *reg = APDS9160_REG_PS_THRES_HI_LSB; + break; + case IIO_LIGHT: + *reg = APDS9160_REG_LS_THRES_UP_LSB; + break; + default: + return -EINVAL; + } break; + case IIO_EV_DIR_FALLING: + switch (chan->type) { + case IIO_PROXIMITY: + *reg = APDS9160_REG_PS_THRES_LO_LSB; + break; + case IIO_LIGHT: + *reg = APDS9160_REG_LS_THRES_LO_LSB; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int apds9160_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + u8 reg; + int ret; + struct apds9160_chip *data = iio_priv(indio_dev); + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + ret = apds9160_get_thres_reg(chan, dir, ®); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_PROXIMITY: { + __le16 buf; + + ret = regmap_bulk_read(data->regmap, reg, &buf, 2); + if (ret < 0) + return ret; + *val = le16_to_cpu(buf); + return IIO_VAL_INT; + } + case IIO_LIGHT: { + u8 buf[3]; + + ret = regmap_bulk_read(data->regmap, reg, &buf, 3); + if (ret < 0) + return ret; + *val = get_unaligned_le24(buf); + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + +static int apds9160_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + u8 reg; + int ret = 0; + struct apds9160_chip *data = iio_priv(indio_dev); + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + ret = apds9160_get_thres_reg(chan, dir, ®); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_PROXIMITY: { + __le16 buf; + + if (val < 0 || val > APDS9160_PS_THRES_MAX) + return -EINVAL; + + buf = cpu_to_le16(val); + return regmap_bulk_write(data->regmap, reg, &buf, 2); + } + case IIO_LIGHT: { + u8 buf[3]; + + if (val < 0 || val > APDS9160_LS_THRES_MAX) + return -EINVAL; + + put_unaligned_le24(val, buf); + return regmap_bulk_write(data->regmap, reg, &buf, 3); + } + default: + return -EINVAL; + } +} + +static int apds9160_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct apds9160_chip *data = iio_priv(indio_dev); + + switch (chan->type) { + case IIO_PROXIMITY: + return data->ps_int; + case IIO_LIGHT: + return data->als_int; + default: + return -EINVAL; + } +} + +static int apds9160_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct apds9160_chip *data = iio_priv(indio_dev); + int ret; + + switch (chan->type) { + case IIO_PROXIMITY: + ret = regmap_field_write(data->reg_int_ps, state); + if (ret) + return ret; + data->ps_int = state; + + return 0; + case IIO_LIGHT: + ret = regmap_field_write(data->reg_int_als, state); + if (ret) + return ret; + data->als_int = state; + + return 0; + default: + return -EINVAL; + } +} + +static irqreturn_t apds9160_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct apds9160_chip *data = iio_priv(indio_dev); + int ret, status; + + /* Reading status register clears the interrupt flag */ + ret = regmap_read(data->regmap, APDS9160_REG_SR, &status); + if (ret < 0) { + dev_err_ratelimited(&data->client->dev, + "irq status reg read failed\n"); + return IRQ_HANDLED; + } + + if ((status & APDS9160_SR_LS_INT) && + (status & APDS9160_SR_LS_NEW_DATA) && data->als_int) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } + + if ((status & APDS9160_SR_PS_INT) && + (status & APDS9160_SR_PS_NEW_DATA) && data->ps_int) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } + + return IRQ_HANDLED; +} + +static int apds9160_detect(struct apds9160_chip *chip) +{ + struct i2c_client *client = chip->client; + int ret; + u32 val; + + ret = regmap_read(chip->regmap, APDS9160_REG_ID, &val); + if (ret < 0) { + dev_err(&client->dev, "ID read failed\n"); + return ret; + } + + if (val != APDS9160_PART_ID_0) + dev_info(&client->dev, "Unknown part id %u\n", val); + + return 0; +} + +static void apds9160_disable(void *chip) +{ + struct apds9160_chip *data = chip; + int ret; + + ret = regmap_field_write(data->reg_enable_als, 0); + if (ret) + return; + + regmap_field_write(data->reg_enable_ps, 0); +} + +static int apds9160_chip_init(struct apds9160_chip *chip) +{ + int ret; + + /* Write default values to interrupt register */ + ret = regmap_field_write(chip->reg_int_ps, 0); + chip->ps_int = 0; + if (ret) + return ret; + + ret = regmap_field_write(chip->reg_int_als, 0); + chip->als_int = 0; + if (ret) + return ret; + + /* Write default values to control register */ + ret = regmap_field_write(chip->reg_enable_als, 1); + if (ret) + return ret; + + ret = regmap_field_write(chip->reg_enable_ps, 1); + if (ret) + return ret; + + /* Write other default values */ + ret = regmap_field_write(chip->reg_ps_resolution, + APDS9160_DEFAULT_PS_RESOLUTION_11BITS); + if (ret) + return ret; + + /* Write default values to configuration registers */ + ret = apds9160_set_ps_current(chip, APDS9160_DEFAULT_PS_CURRENT); + if (ret) + return ret; + + ret = apds9160_set_ps_rate(chip, APDS9160_DEFAULT_PS_RATE); + if (ret) + return ret; + + ret = apds9160_set_als_int_time(chip, APDS9160_DEFAULT_LS_RATE); + if (ret) + return ret; + + ret = apds9160_set_als_scale(chip, + apds9160_100ms_avail[0][0], + apds9160_100ms_avail[0][1]); + if (ret) + return ret; + + ret = apds9160_set_ps_gain(chip, APDS9160_DEFAULT_PS_GAIN); + if (ret) + return ret; + + ret = apds9160_set_ps_analog_cancellation( + chip, APDS9160_DEFAULT_PS_ANALOG_CANCELLATION); + if (ret) + return ret; + + ret = apds9160_set_ps_cancellation_level( + chip, APDS9160_DEFAULT_PS_CANCELLATION_LEVEL); + if (ret) + return ret; + + return devm_add_action_or_reset(&chip->client->dev, apds9160_disable, + chip); +} + +static int apds9160_regfield_init(struct apds9160_chip *data) +{ + struct device *dev = &data->client->dev; + struct regmap *regmap = data->regmap; + struct regmap_field *tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_int_als); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_int_als = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_int_ps); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_int_ps = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ls_en); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_enable_als = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ps_en); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_enable_ps = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, + apds9160_reg_field_ps_overflow); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_ps_overflow = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_als_rate); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_als_rate = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_als_res); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_als_resolution = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ps_rate); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_ps_rate = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_als_gain); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_als_gain = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, + apds9160_reg_field_ps_current); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_ps_current = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9160_reg_field_ps_gain); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_ps_gain = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, + apds9160_reg_field_ps_resolution); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + data->reg_ps_resolution = tmp; + + return 0; +} + +static const struct iio_info apds9160_info = { + .read_avail = apds9160_read_avail, + .read_raw = apds9160_read_raw, + .write_raw = apds9160_write_raw, + .write_raw_get_fmt = apds9160_write_raw_get_fmt, + .read_event_value = apds9160_read_event, + .write_event_value = apds9160_write_event, + .read_event_config = apds9160_read_event_config, + .write_event_config = apds9160_write_event_config, +}; + +static const struct iio_info apds9160_info_no_events = { + .read_avail = apds9160_read_avail, + .read_raw = apds9160_read_raw, + .write_raw = apds9160_write_raw, + .write_raw_get_fmt = apds9160_write_raw_get_fmt, +}; + +static int apds9160_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct apds9160_chip *chip; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable vdd supply\n"); + + indio_dev->name = "apds9160"; + indio_dev->modes = INDIO_DIRECT_MODE; + + chip = iio_priv(indio_dev); + chip->client = client; + chip->regmap = devm_regmap_init_i2c(client, &apds9160_regmap_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "regmap initialization failed.\n"); + + chip->client = client; + mutex_init(&chip->lock); + + ret = apds9160_detect(chip); + if (ret < 0) + return dev_err_probe(dev, ret, "apds9160 not found\n"); + + ret = apds9160_regfield_init(chip); + if (ret) + return ret; + + ret = apds9160_chip_init(chip); + if (ret) + return ret; + + ret = apds9160_ps_init_analog_cancellation(dev, chip); + if (ret) + return ret; + + if (client->irq > 0) { + indio_dev->info = &apds9160_info; + indio_dev->channels = apds9160_channels; + indio_dev->num_channels = ARRAY_SIZE(apds9160_channels); + ret = devm_request_threaded_irq(dev, client->irq, NULL, + apds9160_irq_handler, + IRQF_ONESHOT, "apds9160_event", + indio_dev); + if (ret) { + return dev_err_probe(dev, ret, + "request irq (%d) failed\n", + client->irq); + } + } else { + indio_dev->info = &apds9160_info_no_events; + indio_dev->channels = apds9160_channels_without_events; + indio_dev->num_channels = + ARRAY_SIZE(apds9160_channels_without_events); + } + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "failed iio device registration\n"); + + return ret; +} + +static const struct of_device_id apds9160_of_match[] = { + { .compatible = "brcm,apds9160" }, + { } +}; +MODULE_DEVICE_TABLE(of, apds9160_of_match); + +static const struct i2c_device_id apds9160_id[] = { + { "apds9160", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, apds9160_id); + +static struct i2c_driver apds9160_driver = { + .driver = { + .name = "apds9160", + .of_match_table = apds9160_of_match, + }, + .probe = apds9160_probe, + .id_table = apds9160_id, +}; +module_i2c_driver(apds9160_driver); + +MODULE_DESCRIPTION("APDS9160 combined ALS and proximity sensor"); +MODULE_AUTHOR("Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 3b4056be54a0..56ab5fe90ff9 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -426,16 +426,16 @@ static int bh1745_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = regmap_bulk_read(data->regmap, chan->address, - &value, 2); - if (ret) - return ret; - *val = value; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - return IIO_VAL_INT; - } - unreachable(); + ret = regmap_bulk_read(data->regmap, chan->address, &value, 2); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + *val = value; + + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: { guard(mutex)(&data->lock); diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index aeae0566ec12..bb90f738312a 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -492,7 +492,7 @@ static int cm32181_probe(struct i2c_client *client) ret = devm_iio_device_register(dev, indio_dev); if (ret) { - dev_err(dev, "%s: regist device failed\n", __func__); + dev_err(dev, "%s: register device failed\n", __func__); return ret; } diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index ae3fc3299eec..446dd54d5037 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -683,7 +683,7 @@ static int cm36651_probe(struct i2c_client *client) ret = iio_device_register(indio_dev); if (ret) { - dev_err(&client->dev, "%s: regist device failed\n", __func__); + dev_err(&client->dev, "%s: register device failed\n", __func__); goto error_free_irq; } diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 9b71825eea9b..473a9c3e32a3 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -24,10 +24,12 @@ #include <linux/regmap.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> +#include <linux/units.h> #include <linux/regulator/consumer.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/events.h> +#include <linux/iio/iio-gts-helper.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> @@ -59,22 +61,36 @@ #define VEML6035_INT_CHAN BIT(3) #define VEML6035_CHAN_EN BIT(2) +/* Regfields */ +#define VEML6030_GAIN_RF REG_FIELD(VEML6030_REG_ALS_CONF, 11, 12) +#define VEML6030_IT_RF REG_FIELD(VEML6030_REG_ALS_CONF, 6, 9) + +#define VEML6035_GAIN_RF REG_FIELD(VEML6030_REG_ALS_CONF, 10, 12) + +/* Maximum scales x 10000 to work with integers */ +#define VEML6030_MAX_SCALE 21504 +#define VEML6035_MAX_SCALE 4096 + enum veml6030_scan { VEML6030_SCAN_ALS, VEML6030_SCAN_WH, VEML6030_SCAN_TIMESTAMP, }; +struct veml6030_rf { + struct regmap_field *it; + struct regmap_field *gain; +}; + struct veml603x_chip { const char *name; - const int(*scale_vals)[][2]; - const int num_scale_vals; const struct iio_chan_spec *channels; const int num_channels; + const struct reg_field gain_rf; + const struct reg_field it_rf; + const int max_scale; int (*hw_init)(struct iio_dev *indio_dev, struct device *dev); int (*set_info)(struct iio_dev *indio_dev); - int (*set_als_gain)(struct iio_dev *indio_dev, int val, int val2); - int (*get_als_gain)(struct iio_dev *indio_dev, int *val, int *val2); }; /* @@ -91,40 +107,56 @@ struct veml603x_chip { struct veml6030_data { struct i2c_client *client; struct regmap *regmap; - int cur_resolution; - int cur_gain; - int cur_integration_time; + struct veml6030_rf rf; const struct veml603x_chip *chip; + struct iio_gts gts; + }; -static const int veml6030_it_times[][2] = { - { 0, 25000 }, - { 0, 50000 }, - { 0, 100000 }, - { 0, 200000 }, - { 0, 400000 }, - { 0, 800000 }, +#define VEML6030_SEL_IT_25MS 0x0C +#define VEML6030_SEL_IT_50MS 0x08 +#define VEML6030_SEL_IT_100MS 0x00 +#define VEML6030_SEL_IT_200MS 0x01 +#define VEML6030_SEL_IT_400MS 0x02 +#define VEML6030_SEL_IT_800MS 0x03 +static const struct iio_itime_sel_mul veml6030_it_sel[] = { + GAIN_SCALE_ITIME_US(25000, VEML6030_SEL_IT_25MS, 1), + GAIN_SCALE_ITIME_US(50000, VEML6030_SEL_IT_50MS, 2), + GAIN_SCALE_ITIME_US(100000, VEML6030_SEL_IT_100MS, 4), + GAIN_SCALE_ITIME_US(200000, VEML6030_SEL_IT_200MS, 8), + GAIN_SCALE_ITIME_US(400000, VEML6030_SEL_IT_400MS, 16), + GAIN_SCALE_ITIME_US(800000, VEML6030_SEL_IT_800MS, 32), }; -/* - * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is - * ALS gain x (1/4), 0.5 is ALS gain x (1/2), 1.0 is ALS gain x 1, - * 2.0 is ALS gain x2, and 4.0 is ALS gain x 4. +/* Gains are multiplied by 8 to work with integers. The values in the + * iio-gts tables don't need corrections because the maximum value of + * the scale refers to GAIN = x1, and the rest of the values are + * obtained from the resulting linear function. */ -static const int veml6030_scale_vals[][2] = { - { 0, 125000 }, - { 0, 250000 }, - { 1, 0 }, - { 2, 0 }, +#define VEML6030_SEL_MILLI_GAIN_X125 2 +#define VEML6030_SEL_MILLI_GAIN_X250 3 +#define VEML6030_SEL_MILLI_GAIN_X1000 0 +#define VEML6030_SEL_MILLI_GAIN_X2000 1 +static const struct iio_gain_sel_pair veml6030_gain_sel[] = { + GAIN_SCALE_GAIN(1, VEML6030_SEL_MILLI_GAIN_X125), + GAIN_SCALE_GAIN(2, VEML6030_SEL_MILLI_GAIN_X250), + GAIN_SCALE_GAIN(8, VEML6030_SEL_MILLI_GAIN_X1000), + GAIN_SCALE_GAIN(16, VEML6030_SEL_MILLI_GAIN_X2000), }; -static const int veml6035_scale_vals[][2] = { - { 0, 125000 }, - { 0, 250000 }, - { 0, 500000 }, - { 1, 0 }, - { 2, 0 }, - { 4, 0 }, +#define VEML6035_SEL_MILLI_GAIN_X125 4 +#define VEML6035_SEL_MILLI_GAIN_X250 5 +#define VEML6035_SEL_MILLI_GAIN_X500 7 +#define VEML6035_SEL_MILLI_GAIN_X1000 0 +#define VEML6035_SEL_MILLI_GAIN_X2000 1 +#define VEML6035_SEL_MILLI_GAIN_X4000 3 +static const struct iio_gain_sel_pair veml6035_gain_sel[] = { + GAIN_SCALE_GAIN(1, VEML6035_SEL_MILLI_GAIN_X125), + GAIN_SCALE_GAIN(2, VEML6035_SEL_MILLI_GAIN_X250), + GAIN_SCALE_GAIN(4, VEML6035_SEL_MILLI_GAIN_X500), + GAIN_SCALE_GAIN(8, VEML6035_SEL_MILLI_GAIN_X1000), + GAIN_SCALE_GAIN(16, VEML6035_SEL_MILLI_GAIN_X2000), + GAIN_SCALE_GAIN(32, VEML6035_SEL_MILLI_GAIN_X4000), }; /* @@ -319,113 +351,112 @@ static const struct iio_chan_spec veml7700_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(VEML6030_SCAN_TIMESTAMP), }; +static const struct regmap_range veml6030_readable_ranges[] = { + regmap_reg_range(VEML6030_REG_ALS_CONF, VEML6030_REG_ALS_INT), +}; + +static const struct regmap_access_table veml6030_readable_table = { + .yes_ranges = veml6030_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6030_readable_ranges), +}; + +static const struct regmap_range veml6030_writable_ranges[] = { + regmap_reg_range(VEML6030_REG_ALS_CONF, VEML6030_REG_ALS_PSM), +}; + +static const struct regmap_access_table veml6030_writable_table = { + .yes_ranges = veml6030_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6030_writable_ranges), +}; + +static const struct regmap_range veml6030_volatile_ranges[] = { + regmap_reg_range(VEML6030_REG_ALS_DATA, VEML6030_REG_WH_DATA), +}; + +static const struct regmap_access_table veml6030_volatile_table = { + .yes_ranges = veml6030_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(veml6030_volatile_ranges), +}; + static const struct regmap_config veml6030_regmap_config = { .name = "veml6030_regmap", .reg_bits = 8, .val_bits = 16, .max_register = VEML6030_REG_ALS_INT, .val_format_endian = REGMAP_ENDIAN_LITTLE, + .rd_table = &veml6030_readable_table, + .wr_table = &veml6030_writable_table, + .volatile_table = &veml6030_volatile_table, + .cache_type = REGCACHE_RBTREE, }; -static int veml6030_get_intgrn_tm(struct iio_dev *indio_dev, - int *val, int *val2) +static int veml6030_get_it(struct veml6030_data *data, int *val, int *val2) { - int ret, reg; - struct veml6030_data *data = iio_priv(indio_dev); + int ret, it_idx; - ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); - if (ret) { - dev_err(&data->client->dev, - "can't read als conf register %d\n", ret); + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) return ret; - } - switch ((reg >> 6) & 0xF) { - case 0: - *val2 = 100000; - break; - case 1: - *val2 = 200000; - break; - case 2: - *val2 = 400000; - break; - case 3: - *val2 = 800000; - break; - case 8: - *val2 = 50000; - break; - case 12: - *val2 = 25000; - break; - default: - return -EINVAL; - } + ret = iio_gts_find_int_time_by_sel(&data->gts, it_idx); + if (ret < 0) + return ret; + *val2 = ret; *val = 0; + return IIO_VAL_INT_PLUS_MICRO; } -static int veml6030_set_intgrn_tm(struct iio_dev *indio_dev, - int val, int val2) +static int veml6030_set_it(struct iio_dev *indio_dev, int val, int val2) { - int ret, new_int_time, int_idx; struct veml6030_data *data = iio_priv(indio_dev); + int ret, gain_idx, it_idx, new_gain, prev_gain, prev_it; + bool in_range; - if (val) + if (val || !iio_gts_valid_time(&data->gts, val2)) return -EINVAL; - switch (val2) { - case 25000: - new_int_time = 0x300; - int_idx = 5; - break; - case 50000: - new_int_time = 0x200; - int_idx = 4; - break; - case 100000: - new_int_time = 0x00; - int_idx = 3; - break; - case 200000: - new_int_time = 0x40; - int_idx = 2; - break; - case 400000: - new_int_time = 0x80; - int_idx = 1; - break; - case 800000: - new_int_time = 0xC0; - int_idx = 0; - break; - default: - return -EINVAL; - } + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; - ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, - VEML6030_ALS_IT, new_int_time); - if (ret) { - dev_err(&data->client->dev, - "can't update als integration time %d\n", ret); + ret = regmap_field_read(data->rf.gain, &gain_idx); + if (ret) return ret; - } - /* - * Cache current integration time and update resolution. For every - * increase in integration time to next level, resolution is halved - * and vice-versa. - */ - if (data->cur_integration_time < int_idx) - data->cur_resolution <<= int_idx - data->cur_integration_time; - else if (data->cur_integration_time > int_idx) - data->cur_resolution >>= data->cur_integration_time - int_idx; + prev_it = iio_gts_find_int_time_by_sel(&data->gts, it_idx); + if (prev_it < 0) + return prev_it; + + if (prev_it == val2) + return 0; - data->cur_integration_time = int_idx; + prev_gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx); + if (prev_gain < 0) + return prev_gain; - return ret; + ret = iio_gts_find_new_gain_by_gain_time_min(&data->gts, prev_gain, prev_it, + val2, &new_gain, &in_range); + if (ret) + return ret; + + if (!in_range) + dev_dbg(&data->client->dev, "Optimal gain out of range\n"); + + ret = iio_gts_find_sel_by_int_time(&data->gts, val2); + if (ret < 0) + return ret; + + ret = regmap_field_write(data->rf.it, ret); + if (ret) + return ret; + + ret = iio_gts_find_sel_by_gain(&data->gts, new_gain); + if (ret < 0) + return ret; + + return regmap_field_write(data->rf.gain, ret); } static int veml6030_read_persistence(struct iio_dev *indio_dev, @@ -434,7 +465,7 @@ static int veml6030_read_persistence(struct iio_dev *indio_dev, int ret, reg, period, x, y; struct veml6030_data *data = iio_priv(indio_dev); - ret = veml6030_get_intgrn_tm(indio_dev, &x, &y); + ret = veml6030_get_it(data, &x, &y); if (ret < 0) return ret; @@ -459,7 +490,7 @@ static int veml6030_write_persistence(struct iio_dev *indio_dev, int ret, period, x, y; struct veml6030_data *data = iio_priv(indio_dev); - ret = veml6030_get_intgrn_tm(indio_dev, &x, &y); + ret = veml6030_get_it(data, &x, &y); if (ret < 0) return ret; @@ -488,177 +519,29 @@ static int veml6030_write_persistence(struct iio_dev *indio_dev, return ret; } -/* - * Cache currently set gain & update resolution. For every - * increase in the gain to next level, resolution is halved - * and vice-versa. - */ -static void veml6030_update_gain_res(struct veml6030_data *data, int gain_idx) -{ - if (data->cur_gain < gain_idx) - data->cur_resolution <<= gain_idx - data->cur_gain; - else if (data->cur_gain > gain_idx) - data->cur_resolution >>= data->cur_gain - gain_idx; - - data->cur_gain = gain_idx; -} - -static int veml6030_set_als_gain(struct iio_dev *indio_dev, - int val, int val2) +static int veml6030_set_scale(struct iio_dev *indio_dev, int val, int val2) { - int ret, new_gain, gain_idx; + int ret, gain_sel, it_idx, it_sel; struct veml6030_data *data = iio_priv(indio_dev); - if (val == 0 && val2 == 125000) { - new_gain = 0x1000; /* 0x02 << 11 */ - gain_idx = 3; - } else if (val == 0 && val2 == 250000) { - new_gain = 0x1800; - gain_idx = 2; - } else if (val == 1 && val2 == 0) { - new_gain = 0x00; - gain_idx = 1; - } else if (val == 2 && val2 == 0) { - new_gain = 0x800; - gain_idx = 0; - } else { - return -EINVAL; - } - - ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, - VEML6030_ALS_GAIN, new_gain); - if (ret) { - dev_err(&data->client->dev, - "can't set als gain %d\n", ret); + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) return ret; - } - - veml6030_update_gain_res(data, gain_idx); - - return 0; -} - -static int veml6035_set_als_gain(struct iio_dev *indio_dev, int val, int val2) -{ - int ret, new_gain, gain_idx; - struct veml6030_data *data = iio_priv(indio_dev); - if (val == 0 && val2 == 125000) { - new_gain = VEML6035_SENS; - gain_idx = 5; - } else if (val == 0 && val2 == 250000) { - new_gain = VEML6035_SENS | VEML6035_GAIN; - gain_idx = 4; - } else if (val == 0 && val2 == 500000) { - new_gain = VEML6035_SENS | VEML6035_GAIN | - VEML6035_DG; - gain_idx = 3; - } else if (val == 1 && val2 == 0) { - new_gain = 0x0000; - gain_idx = 2; - } else if (val == 2 && val2 == 0) { - new_gain = VEML6035_GAIN; - gain_idx = 1; - } else if (val == 4 && val2 == 0) { - new_gain = VEML6035_GAIN | VEML6035_DG; - gain_idx = 0; - } else { - return -EINVAL; - } - - ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, - VEML6035_GAIN_M, new_gain); - if (ret) { - dev_err(&data->client->dev, "can't set als gain %d\n", ret); + ret = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2, + &gain_sel, &it_sel); + if (ret) return ret; - } - - veml6030_update_gain_res(data, gain_idx); - return 0; -} - -static int veml6030_get_als_gain(struct iio_dev *indio_dev, - int *val, int *val2) -{ - int ret, reg; - struct veml6030_data *data = iio_priv(indio_dev); - - ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); - if (ret) { - dev_err(&data->client->dev, - "can't read als conf register %d\n", ret); + ret = regmap_field_write(data->rf.it, it_sel); + if (ret) return ret; - } - switch ((reg >> 11) & 0x03) { - case 0: - *val = 1; - *val2 = 0; - break; - case 1: - *val = 2; - *val2 = 0; - break; - case 2: - *val = 0; - *val2 = 125000; - break; - case 3: - *val = 0; - *val2 = 250000; - break; - default: - return -EINVAL; - } - - return IIO_VAL_INT_PLUS_MICRO; -} - -static int veml6035_get_als_gain(struct iio_dev *indio_dev, int *val, int *val2) -{ - int ret, reg; - struct veml6030_data *data = iio_priv(indio_dev); - - ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, ®); - if (ret) { - dev_err(&data->client->dev, - "can't read als conf register %d\n", ret); + ret = regmap_field_write(data->rf.gain, gain_sel); + if (ret) return ret; - } - switch (FIELD_GET(VEML6035_GAIN_M, reg)) { - case 0: - *val = 1; - *val2 = 0; - break; - case 1: - case 2: - *val = 2; - *val2 = 0; - break; - case 3: - *val = 4; - *val2 = 0; - break; - case 4: - *val = 0; - *val2 = 125000; - break; - case 5: - case 6: - *val = 0; - *val2 = 250000; - break; - case 7: - *val = 0; - *val2 = 500000; - break; - default: - return -EINVAL; - } - - return IIO_VAL_INT_PLUS_MICRO; + return 0; } static int veml6030_read_thresh(struct iio_dev *indio_dev, @@ -705,6 +588,71 @@ static int veml6030_write_thresh(struct iio_dev *indio_dev, return ret; } +static int veml6030_get_total_gain(struct veml6030_data *data) +{ + int gain, it, reg, ret; + + ret = regmap_field_read(data->rf.gain, ®); + if (ret) + return ret; + + gain = iio_gts_find_gain_by_sel(&data->gts, reg); + if (gain < 0) + return gain; + + ret = regmap_field_read(data->rf.it, ®); + if (ret) + return ret; + + it = iio_gts_find_int_time_by_sel(&data->gts, reg); + if (it < 0) + return it; + + return iio_gts_get_total_gain(&data->gts, gain, it); +} + +static int veml6030_get_scale(struct veml6030_data *data, int *val, int *val2) +{ + int gain, it, reg, ret; + + ret = regmap_field_read(data->rf.gain, ®); + if (ret) + return ret; + + gain = iio_gts_find_gain_by_sel(&data->gts, reg); + if (gain < 0) + return gain; + + ret = regmap_field_read(data->rf.it, ®); + if (ret) + return ret; + + it = iio_gts_find_int_time_by_sel(&data->gts, reg); + if (it < 0) + return it; + + ret = iio_gts_get_scale(&data->gts, gain, it, val, val2); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_NANO; +} + +static int veml6030_process_als(struct veml6030_data *data, int raw, + int *val, int *val2) +{ + int total_gain; + + total_gain = veml6030_get_total_gain(data); + if (total_gain < 0) + return total_gain; + + *val = raw * data->chip->max_scale / total_gain / 10000; + *val2 = raw * data->chip->max_scale / total_gain % 10000 * 100; + + return IIO_VAL_INT_PLUS_MICRO; +} + /* * Provide both raw as well as light reading in lux. * light (in lux) = resolution * raw reading @@ -728,11 +676,9 @@ static int veml6030_read_raw(struct iio_dev *indio_dev, dev_err(dev, "can't read als data %d\n", ret); return ret; } - if (mask == IIO_CHAN_INFO_PROCESSED) { - *val = (reg * data->cur_resolution) / 10000; - *val2 = (reg * data->cur_resolution) % 10000 * 100; - return IIO_VAL_INT_PLUS_MICRO; - } + if (mask == IIO_CHAN_INFO_PROCESSED) + return veml6030_process_als(data, reg, val, val2); + *val = reg; return IIO_VAL_INT; case IIO_INTENSITY: @@ -747,9 +693,9 @@ static int veml6030_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_INT_TIME: - return veml6030_get_intgrn_tm(indio_dev, val, val2); + return veml6030_get_it(data, val, val2); case IIO_CHAN_INFO_SCALE: - return data->chip->get_als_gain(indio_dev, val, val2); + return veml6030_get_scale(data, val, val2); default: return -EINVAL; } @@ -764,15 +710,9 @@ static int veml6030_read_avail(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: - *vals = (int *)&veml6030_it_times; - *length = 2 * ARRAY_SIZE(veml6030_it_times); - *type = IIO_VAL_INT_PLUS_MICRO; - return IIO_AVAIL_LIST; + return iio_gts_avail_times(&data->gts, vals, type, length); case IIO_CHAN_INFO_SCALE: - *vals = (int *)*data->chip->scale_vals; - *length = 2 * data->chip->num_scale_vals; - *type = IIO_VAL_INT_PLUS_MICRO; - return IIO_AVAIL_LIST; + return iio_gts_all_avail_scales(&data->gts, vals, type, length); } return -EINVAL; @@ -782,13 +722,25 @@ static int veml6030_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct veml6030_data *data = iio_priv(indio_dev); - switch (mask) { case IIO_CHAN_INFO_INT_TIME: - return veml6030_set_intgrn_tm(indio_dev, val, val2); + return veml6030_set_it(indio_dev, val, val2); case IIO_CHAN_INFO_SCALE: - return data->chip->set_als_gain(indio_dev, val, val2); + return veml6030_set_scale(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static int veml6030_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -886,6 +838,7 @@ static const struct iio_info veml6030_info = { .read_raw = veml6030_read_raw, .read_avail = veml6030_read_avail, .write_raw = veml6030_write_raw, + .write_raw_get_fmt = veml6030_write_raw_get_fmt, .read_event_value = veml6030_read_event_val, .write_event_value = veml6030_write_event_val, .read_event_config = veml6030_read_interrupt_config, @@ -897,6 +850,7 @@ static const struct iio_info veml6030_info_no_irq = { .read_raw = veml6030_read_raw, .read_avail = veml6030_read_avail, .write_raw = veml6030_write_raw, + .write_raw_get_fmt = veml6030_write_raw_get_fmt, }; static irqreturn_t veml6030_event_handler(int irq, void *private) @@ -990,6 +944,27 @@ static int veml7700_set_info(struct iio_dev *indio_dev) return 0; } +static int veml6030_regfield_init(struct iio_dev *indio_dev) +{ + struct veml6030_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + struct device *dev = &data->client->dev; + struct regmap_field *rm_field; + struct veml6030_rf *rf = &data->rf; + + rm_field = devm_regmap_field_alloc(dev, regmap, data->chip->it_rf); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->it = rm_field; + + rm_field = devm_regmap_field_alloc(dev, regmap, data->chip->gain_rf); + if (IS_ERR(rm_field)) + return PTR_ERR(rm_field); + rf->gain = rm_field; + + return 0; +} + /* * Set ALS gain to 1/8, integration time to 100 ms, PSM to mode 2, * persistence to 1 x integration time and the threshold @@ -1001,6 +976,13 @@ static int veml6030_hw_init(struct iio_dev *indio_dev, struct device *dev) int ret, val; struct veml6030_data *data = iio_priv(indio_dev); + ret = devm_iio_init_iio_gts(dev, 2, 150400000, + veml6030_gain_sel, ARRAY_SIZE(veml6030_gain_sel), + veml6030_it_sel, ARRAY_SIZE(veml6030_it_sel), + &data->gts); + if (ret) + return dev_err_probe(dev, ret, "failed to init iio gts\n"); + ret = veml6030_als_shut_down(data); if (ret) return dev_err_probe(dev, ret, "can't shutdown als\n"); @@ -1036,11 +1018,6 @@ static int veml6030_hw_init(struct iio_dev *indio_dev, struct device *dev) return dev_err_probe(dev, ret, "can't clear als interrupt status\n"); - /* Cache currently active measurement parameters */ - data->cur_gain = 3; - data->cur_resolution = 5376; - data->cur_integration_time = 3; - return ret; } @@ -1056,6 +1033,13 @@ static int veml6035_hw_init(struct iio_dev *indio_dev, struct device *dev) int ret, val; struct veml6030_data *data = iio_priv(indio_dev); + ret = devm_iio_init_iio_gts(dev, 0, 409600000, + veml6035_gain_sel, ARRAY_SIZE(veml6035_gain_sel), + veml6030_it_sel, ARRAY_SIZE(veml6030_it_sel), + &data->gts); + if (ret) + return dev_err_probe(dev, ret, "failed to init iio gts\n"); + ret = veml6030_als_shut_down(data); if (ret) return dev_err_probe(dev, ret, "can't shutdown als\n"); @@ -1092,11 +1076,6 @@ static int veml6035_hw_init(struct iio_dev *indio_dev, struct device *dev) return dev_err_probe(dev, ret, "can't clear als interrupt status\n"); - /* Cache currently active measurement parameters */ - data->cur_gain = 5; - data->cur_resolution = 1024; - data->cur_integration_time = 3; - return 0; } @@ -1143,6 +1122,11 @@ static int veml6030_probe(struct i2c_client *client) if (ret < 0) return ret; + ret = veml6030_regfield_init(indio_dev); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to init regfields\n"); + ret = data->chip->hw_init(indio_dev, &client->dev); if (ret < 0) return ret; @@ -1187,38 +1171,35 @@ static DEFINE_RUNTIME_DEV_PM_OPS(veml6030_pm_ops, veml6030_runtime_suspend, static const struct veml603x_chip veml6030_chip = { .name = "veml6030", - .scale_vals = &veml6030_scale_vals, - .num_scale_vals = ARRAY_SIZE(veml6030_scale_vals), .channels = veml6030_channels, .num_channels = ARRAY_SIZE(veml6030_channels), + .gain_rf = VEML6030_GAIN_RF, + .it_rf = VEML6030_IT_RF, + .max_scale = VEML6030_MAX_SCALE, .hw_init = veml6030_hw_init, .set_info = veml6030_set_info, - .set_als_gain = veml6030_set_als_gain, - .get_als_gain = veml6030_get_als_gain, }; static const struct veml603x_chip veml6035_chip = { .name = "veml6035", - .scale_vals = &veml6035_scale_vals, - .num_scale_vals = ARRAY_SIZE(veml6035_scale_vals), .channels = veml6030_channels, .num_channels = ARRAY_SIZE(veml6030_channels), + .gain_rf = VEML6035_GAIN_RF, + .it_rf = VEML6030_IT_RF, + .max_scale = VEML6035_MAX_SCALE, .hw_init = veml6035_hw_init, .set_info = veml6030_set_info, - .set_als_gain = veml6035_set_als_gain, - .get_als_gain = veml6035_get_als_gain, }; static const struct veml603x_chip veml7700_chip = { .name = "veml7700", - .scale_vals = &veml6030_scale_vals, - .num_scale_vals = ARRAY_SIZE(veml6030_scale_vals), .channels = veml7700_channels, .num_channels = ARRAY_SIZE(veml7700_channels), + .gain_rf = VEML6030_GAIN_RF, + .it_rf = VEML6030_IT_RF, + .max_scale = VEML6030_MAX_SCALE, .hw_init = veml6030_hw_init, .set_info = veml7700_set_info, - .set_als_gain = veml6030_set_als_gain, - .get_als_gain = veml6030_get_als_gain, }; static const struct of_device_id veml6030_of_match[] = { @@ -1260,3 +1241,4 @@ module_i2c_driver(veml6030_driver); MODULE_AUTHOR("Rishi Gupta <gupt21@gmail.com>"); MODULE_DESCRIPTION("VEML6030 Ambient Light Sensor"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_GTS_HELPER"); diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c index 05d4c0e9015d..859891e8f115 100644 --- a/drivers/iio/light/veml6075.c +++ b/drivers/iio/light/veml6075.c @@ -195,13 +195,17 @@ static int veml6075_read_uv_direct(struct veml6075_data *data, int chan, static int veml6075_read_int_time_index(struct veml6075_data *data) { - int ret, conf; + int ret, conf, int_index; ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf); if (ret < 0) return ret; - return FIELD_GET(VEML6075_CONF_IT, conf); + int_index = FIELD_GET(VEML6075_CONF_IT, conf); + if (int_index >= ARRAY_SIZE(veml6075_it_ms)) + return -EINVAL; + + return int_index; } static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val) diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 7177cd1d67cb..3debf1320ad1 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -235,6 +235,17 @@ config SENSORS_RM3100_SPI To compile this driver as a module, choose M here: the module will be called rm3100-spi. +config SI7210 + tristate "SI7210 Hall effect sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support for the SI7210 Hall effect sensor. + + This driver can also be compiled as a module. + To compile this driver as a module, choose M here: the module + will be called si7210. + config TI_TMAG5273 tristate "TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor" depends on I2C diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 3e4c2ecd9adf..9297723a97d8 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -31,6 +31,8 @@ obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o +obj-$(CONFIG_SI7210) += si7210.o + obj-$(CONFIG_TI_TMAG5273) += tmag5273.o obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c index a70bf8a3c73b..c1fc339e85b4 100644 --- a/drivers/iio/magnetometer/af8133j.c +++ b/drivers/iio/magnetometer/af8133j.c @@ -383,7 +383,6 @@ static const struct regmap_config af8133j_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = AF8133J_REG_SWR, - .cache_type = REGCACHE_NONE, }; static void af8133j_power_down_action(void *ptr) diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 08975c60e325..7bc341c69697 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -535,8 +535,8 @@ static int ak8974_detect(struct ak8974 *ak8974) fab_data2, sizeof(fab_data2)); for (i = 0; i < 3; ++i) { - static const char axis[3] = "XYZ"; - static const char pgaxis[6] = "ZYZXYX"; + static const char axis[] = "XYZ"; + static const char pgaxis[] = "ZYZXYX"; unsigned offz = le16_to_cpu(fab_data2[i]) & 0x7F; unsigned fine = le16_to_cpu(fab_data1[i]); unsigned sens = le16_to_cpu(fab_data1[i + 3]); diff --git a/drivers/iio/magnetometer/si7210.c b/drivers/iio/magnetometer/si7210.c new file mode 100644 index 000000000000..27e3feba7a0f --- /dev/null +++ b/drivers/iio/magnetometer/si7210.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Silicon Labs Si7210 Hall Effect sensor driver + * + * Copyright (c) 2024 Antoni Pokusinski <apokusinski01@gmail.com> + * + * Datasheet: + * https://www.silabs.com/documents/public/data-sheets/si7210-datasheet.pdf + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/math64.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> +#include <asm/byteorder.h> + +/* Registers offsets and masks */ +#define SI7210_REG_DSPSIGM 0xC1 +#define SI7210_REG_DSPSIGL 0xC2 + +#define SI7210_MASK_DSPSIGSEL GENMASK(2, 0) +#define SI7210_REG_DSPSIGSEL 0xC3 + +#define SI7210_MASK_STOP BIT(1) +#define SI7210_MASK_ONEBURST BIT(2) +#define SI7210_REG_POWER_CTRL 0xC4 + +#define SI7210_MASK_ARAUTOINC BIT(0) +#define SI7210_REG_ARAUTOINC 0xC5 + +#define SI7210_REG_A0 0xCA +#define SI7210_REG_A1 0xCB +#define SI7210_REG_A2 0xCC +#define SI7210_REG_A3 0xCE +#define SI7210_REG_A4 0xCF +#define SI7210_REG_A5 0xD0 + +#define SI7210_REG_OTP_ADDR 0xE1 +#define SI7210_REG_OTP_DATA 0xE2 + +#define SI7210_MASK_OTP_READ_EN BIT(1) +#define SI7210_REG_OTP_CTRL 0xE3 + +/* OTP data registers offsets */ +#define SI7210_OTPREG_TMP_OFF 0x1D +#define SI7210_OTPREG_TMP_GAIN 0x1E + +#define SI7210_OTPREG_A0_20 0x21 +#define SI7210_OTPREG_A1_20 0x22 +#define SI7210_OTPREG_A2_20 0x23 +#define SI7210_OTPREG_A3_20 0x24 +#define SI7210_OTPREG_A4_20 0x25 +#define SI7210_OTPREG_A5_20 0x26 + +#define SI7210_OTPREG_A0_200 0x27 +#define SI7210_OTPREG_A1_200 0x28 +#define SI7210_OTPREG_A2_200 0x29 +#define SI7210_OTPREG_A3_200 0x2A +#define SI7210_OTPREG_A4_200 0x2B +#define SI7210_OTPREG_A5_200 0x2C + +#define A_REGS_COUNT 6 + +static const unsigned int a20_otp_regs[A_REGS_COUNT] = { + SI7210_OTPREG_A0_20, SI7210_OTPREG_A1_20, SI7210_OTPREG_A2_20, + SI7210_OTPREG_A3_20, SI7210_OTPREG_A4_20, SI7210_OTPREG_A5_20, +}; + +static const unsigned int a200_otp_regs[A_REGS_COUNT] = { + SI7210_OTPREG_A0_200, SI7210_OTPREG_A1_200, SI7210_OTPREG_A2_200, + SI7210_OTPREG_A3_200, SI7210_OTPREG_A4_200, SI7210_OTPREG_A5_200, +}; + +static const struct regmap_range si7210_read_reg_ranges[] = { + regmap_reg_range(SI7210_REG_DSPSIGM, SI7210_REG_ARAUTOINC), + regmap_reg_range(SI7210_REG_A0, SI7210_REG_A2), + regmap_reg_range(SI7210_REG_A3, SI7210_REG_A5), + regmap_reg_range(SI7210_REG_OTP_ADDR, SI7210_REG_OTP_CTRL), +}; + +static const struct regmap_access_table si7210_readable_regs = { + .yes_ranges = si7210_read_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(si7210_read_reg_ranges), +}; + +static const struct regmap_range si7210_write_reg_ranges[] = { + regmap_reg_range(SI7210_REG_DSPSIGSEL, SI7210_REG_ARAUTOINC), + regmap_reg_range(SI7210_REG_A0, SI7210_REG_A2), + regmap_reg_range(SI7210_REG_A3, SI7210_REG_A5), + regmap_reg_range(SI7210_REG_OTP_ADDR, SI7210_REG_OTP_CTRL), +}; + +static const struct regmap_access_table si7210_writeable_regs = { + .yes_ranges = si7210_write_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(si7210_write_reg_ranges), +}; + +static const struct regmap_range si7210_volatile_reg_ranges[] = { + regmap_reg_range(SI7210_REG_DSPSIGM, SI7210_REG_DSPSIGL), + regmap_reg_range(SI7210_REG_POWER_CTRL, SI7210_REG_POWER_CTRL), +}; + +static const struct regmap_access_table si7210_volatile_regs = { + .yes_ranges = si7210_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(si7210_volatile_reg_ranges), +}; + +static const struct regmap_config si7210_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SI7210_REG_OTP_CTRL, + + .rd_table = &si7210_readable_regs, + .wr_table = &si7210_writeable_regs, + .volatile_table = &si7210_volatile_regs, +}; + +struct si7210_data { + struct regmap *regmap; + struct i2c_client *client; + struct regulator *vdd; + struct mutex fetch_lock; /* lock for a single measurement fetch */ + s8 temp_offset; + s8 temp_gain; + s8 scale_20_a[A_REGS_COUNT]; + s8 scale_200_a[A_REGS_COUNT]; + u8 curr_scale; +}; + +static const struct iio_chan_spec si7210_channels[] = { + { + .type = IIO_MAGN, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), + }, { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +static int si7210_fetch_measurement(struct si7210_data *data, + struct iio_chan_spec const *chan, + u16 *buf) +{ + u8 dspsigsel = chan->type == IIO_MAGN ? 0 : 1; + int ret; + __be16 result; + + guard(mutex)(&data->fetch_lock); + + ret = regmap_update_bits(data->regmap, SI7210_REG_DSPSIGSEL, + SI7210_MASK_DSPSIGSEL, dspsigsel); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, SI7210_REG_POWER_CTRL, + SI7210_MASK_ONEBURST | SI7210_MASK_STOP, + SI7210_MASK_ONEBURST & ~SI7210_MASK_STOP); + if (ret) + return ret; + + /* + * Read the contents of the + * registers containing the result: DSPSIGM, DSPSIGL + */ + ret = regmap_bulk_read(data->regmap, SI7210_REG_DSPSIGM, + &result, sizeof(result)); + if (ret) + return ret; + + *buf = be16_to_cpu(result); + + return 0; +} + +static int si7210_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct si7210_data *data = iio_priv(indio_dev); + long long temp; + u16 dspsig; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = si7210_fetch_measurement(data, chan, &dspsig); + if (ret) + return ret; + + *val = dspsig & GENMASK(14, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + if (data->curr_scale == 20) + *val2 = 12500; + else /* data->curr_scale == 200 */ + *val2 = 125000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = -16384; + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: + ret = si7210_fetch_measurement(data, chan, &dspsig); + if (ret) + return ret; + + /* temp = 32 * Dspsigm[6:0] + (Dspsigl[7:0] >> 3) */ + temp = FIELD_GET(GENMASK(14, 3), dspsig); + temp = div_s64(-383 * temp * temp, 100) + 160940 * temp - 279800000; + temp *= (1 + (data->temp_gain / 2048)); + temp += (int)(MICRO / 16) * data->temp_offset; + + ret = regulator_get_voltage(data->vdd); + if (ret < 0) + return ret; + + /* temp -= 0.222 * VDD */ + temp -= 222 * div_s64(ret, MILLI); + + *val = div_s64(temp, MILLI); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int si7210_set_scale(struct si7210_data *data, unsigned int scale) +{ + s8 *a_otp_values; + int ret; + + if (scale == 20) + a_otp_values = data->scale_20_a; + else if (scale == 200) + a_otp_values = data->scale_200_a; + else + return -EINVAL; + + guard(mutex)(&data->fetch_lock); + + /* Write the registers 0xCA - 0xCC */ + ret = regmap_bulk_write(data->regmap, SI7210_REG_A0, a_otp_values, 3); + if (ret) + return ret; + + /* Write the registers 0xCE - 0xD0 */ + ret = regmap_bulk_write(data->regmap, SI7210_REG_A3, &a_otp_values[3], 3); + if (ret) + return ret; + + data->curr_scale = scale; + + return 0; +} + +static int si7210_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct si7210_data *data = iio_priv(indio_dev); + unsigned int scale; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val == 0 && val2 == 12500) + scale = 20; + else if (val == 0 && val2 == 125000) + scale = 200; + else + return -EINVAL; + + return si7210_set_scale(data, scale); + default: + return -EINVAL; + } +} + +static int si7210_read_otpreg_val(struct si7210_data *data, unsigned int otpreg, u8 *val) +{ + int ret; + unsigned int otpdata; + + ret = regmap_write(data->regmap, SI7210_REG_OTP_ADDR, otpreg); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, SI7210_REG_OTP_CTRL, + SI7210_MASK_OTP_READ_EN, SI7210_MASK_OTP_READ_EN); + if (ret) + return ret; + + ret = regmap_read(data->regmap, SI7210_REG_OTP_DATA, &otpdata); + if (ret) + return ret; + + *val = otpdata; + + return 0; +} + +/* + * According to the datasheet, the primary method to wake up a + * device is to send an empty write. However this is not feasible + * using the current API so we use the other method i.e. read a single + * byte. The device should respond with 0xFF. + */ +static int si7210_device_wake(struct si7210_data *data) +{ + int ret; + + ret = i2c_smbus_read_byte(data->client); + if (ret < 0) + return ret; + + if (ret != 0xFF) + return -EIO; + + return 0; +} + +static int si7210_device_init(struct si7210_data *data) +{ + int ret; + unsigned int i; + + ret = si7210_device_wake(data); + if (ret) + return ret; + + fsleep(1000); + + ret = si7210_read_otpreg_val(data, SI7210_OTPREG_TMP_GAIN, &data->temp_gain); + if (ret) + return ret; + + ret = si7210_read_otpreg_val(data, SI7210_OTPREG_TMP_OFF, &data->temp_offset); + if (ret) + return ret; + + for (i = 0; i < A_REGS_COUNT; i++) { + ret = si7210_read_otpreg_val(data, a20_otp_regs[i], &data->scale_20_a[i]); + if (ret) + return ret; + } + + for (i = 0; i < A_REGS_COUNT; i++) { + ret = si7210_read_otpreg_val(data, a200_otp_regs[i], &data->scale_200_a[i]); + if (ret) + return ret; + } + + ret = regmap_update_bits(data->regmap, SI7210_REG_ARAUTOINC, + SI7210_MASK_ARAUTOINC, SI7210_MASK_ARAUTOINC); + if (ret) + return ret; + + return si7210_set_scale(data, 20); +} + +static const struct iio_info si7210_info = { + .read_raw = si7210_read_raw, + .write_raw = si7210_write_raw, +}; + +static int si7210_probe(struct i2c_client *client) +{ + struct si7210_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + ret = devm_mutex_init(&client->dev, &data->fetch_lock); + if (ret) + return ret; + + data->regmap = devm_regmap_init_i2c(client, &si7210_regmap_conf); + if (IS_ERR(data->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(data->regmap), + "failed to register regmap\n"); + + data->vdd = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd)) + return dev_err_probe(&client->dev, PTR_ERR(data->vdd), + "failed to get VDD regulator\n"); + + ret = regulator_enable(data->vdd); + if (ret) + return ret; + + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &si7210_info; + indio_dev->channels = si7210_channels; + indio_dev->num_channels = ARRAY_SIZE(si7210_channels); + + ret = si7210_device_init(data); + if (ret) + return dev_err_probe(&client->dev, ret, + "device initialization failed\n"); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id si7210_id[] = { + { "si7210" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si7210_id); + +static const struct of_device_id si7210_dt_ids[] = { + { .compatible = "silabs,si7210" }, + { } +}; +MODULE_DEVICE_TABLE(of, si7210_dt_ids); + +static struct i2c_driver si7210_driver = { + .driver = { + .name = "si7210", + .of_match_table = si7210_dt_ids, + }, + .probe = si7210_probe, + .id_table = si7210_id, +}; +module_i2c_driver(si7210_driver); + +MODULE_AUTHOR("Antoni Pokusinski <apokusinski01@gmail.com>"); +MODULE_DESCRIPTION("Silicon Labs Si7210 Hall Effect sensor I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c index 49a239ebdabf..a6034bf05d97 100644 --- a/drivers/iio/pressure/zpa2326_i2c.c +++ b/drivers/iio/pressure/zpa2326_i2c.c @@ -25,7 +25,6 @@ static const struct regmap_config zpa2326_regmap_i2c_config = { .precious_reg = zpa2326_isreg_precious, .max_register = ZPA2326_TEMP_OUT_H_REG, .read_flag_mask = BIT(7), - .cache_type = REGCACHE_NONE, }; static unsigned int zpa2326_i2c_hwid(const struct i2c_client *client) diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c index 317270fa1c43..c678f5b96266 100644 --- a/drivers/iio/pressure/zpa2326_spi.c +++ b/drivers/iio/pressure/zpa2326_spi.c @@ -26,7 +26,6 @@ static const struct regmap_config zpa2326_regmap_spi_config = { .precious_reg = zpa2326_isreg_precious, .max_register = ZPA2326_TEMP_OUT_H_REG, .read_flag_mask = BIT(7) | BIT(6), - .cache_type = REGCACHE_NONE, }; static int zpa2326_probe_spi(struct spi_device *spi) diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index b09d15230111..b0ffd3574013 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -10,6 +10,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> +#include <linux/string_choices.h> #include <linux/iio/buffer.h> #include <linux/iio/events.h> @@ -783,7 +784,7 @@ static int irsd200_set_trigger_state(struct iio_trigger *trig, bool state) ret = regmap_field_write(data->regfields[IRS_REGF_INTR_DATA], state); if (ret) { dev_err(data->dev, "Could not %s data interrupt source (%d)\n", - state ? "enable" : "disable", ret); + str_enable_disable(state), ret); } return ret; diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 0d7f0518d4fb..b60707eba39d 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -337,19 +337,26 @@ static int sx9310_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct sx_common_data *data = iio_priv(indio_dev); + int ret; if (chan->type != IIO_PROXIMITY) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return sx_common_read_proximity(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sx_common_read_proximity(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_HARDWAREGAIN: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return sx9310_read_gain(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sx9310_read_gain(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SAMP_FREQ: return sx9310_read_samp_freq(data, val, val2); default: diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index f7819dd2775c..73d972416c01 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -429,16 +429,23 @@ static int sx9324_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct sx_common_data *data = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return sx_common_read_proximity(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sx_common_read_proximity(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_HARDWAREGAIN: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return sx9324_read_gain(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sx9324_read_gain(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SAMP_FREQ: return sx9324_read_samp_freq(data, val, val2); default: diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c index a6ff16e33c1e..4448988d4e7e 100644 --- a/drivers/iio/proximity/sx9360.c +++ b/drivers/iio/proximity/sx9360.c @@ -321,16 +321,23 @@ static int sx9360_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct sx_common_data *data = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return sx_common_read_proximity(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sx_common_read_proximity(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_HARDWAREGAIN: - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) - return sx9360_read_gain(data, chan, val); - unreachable(); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = sx9360_read_gain(data, chan, val); + iio_device_release_direct(indio_dev); + return ret; case IIO_CHAN_INFO_SAMP_FREQ: return sx9360_read_samp_freq(data, val, val2); default: diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index b681129a99b6..ab860cedecd1 100644 --- a/drivers/iio/resolver/ad2s1210.c +++ b/drivers/iio/resolver/ad2s1210.c @@ -46,6 +46,7 @@ */ #include <linux/bitfield.h> +#include <linux/bitmap.h> #include <linux/bits.h> #include <linux/cleanup.h> #include <linux/clk.h> @@ -175,15 +176,14 @@ struct ad2s1210_state { static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode) { struct gpio_descs *gpios = st->mode_gpios; - DECLARE_BITMAP(bitmap, 2); + DECLARE_BITMAP(bitmap, 2) = { }; if (!gpios) return mode == st->fixed_mode ? 0 : -EOPNOTSUPP; - bitmap[0] = mode; + bitmap_write(bitmap, mode, 0, 2); - return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info, - bitmap); + return gpiod_multi_set_value_cansleep(gpios, bitmap); } /* @@ -1427,7 +1427,7 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) struct device *dev = &st->sdev->dev; struct gpio_descs *resolution_gpios; struct gpio_desc *reset_gpio; - DECLARE_BITMAP(bitmap, 2); + DECLARE_BITMAP(bitmap, 2) = { }; int ret; /* should not be sampling on startup */ @@ -1471,12 +1471,9 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st) return dev_err_probe(dev, -EINVAL, "requires exactly 2 resolution-gpios\n"); - bitmap[0] = st->resolution; + bitmap_write(bitmap, st->resolution, 0, 2); - ret = gpiod_set_array_value(resolution_gpios->ndescs, - resolution_gpios->desc, - resolution_gpios->info, - bitmap); + ret = gpiod_multi_set_value_cansleep(resolution_gpios, bitmap); if (ret < 0) return dev_err_probe(dev, ret, "failed to set resolution gpios\n"); diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 1998047a1f24..b5c94b7492f5 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -85,19 +85,25 @@ static int tmp006_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: if (channel->type == IIO_VOLTAGE) { /* LSB is 156.25 nV */ - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = tmp006_read_measurement(data, TMP006_VOBJECT); - if (ret < 0) - return ret; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = tmp006_read_measurement(data, TMP006_VOBJECT); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 15); } else if (channel->type == IIO_TEMP) { /* LSB is 0.03125 degrees Celsius */ - iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - ret = tmp006_read_measurement(data, TMP006_TAMBIENT); - if (ret < 0) - return ret; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = tmp006_read_measurement(data, TMP006_TAMBIENT); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 15) >> TMP006_TAMBIENT_SHIFT; } else { break; @@ -142,9 +148,8 @@ static int tmp006_write_raw(struct iio_dev *indio_dev, for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++) if ((val == tmp006_freqs[i][0]) && (val2 == tmp006_freqs[i][1])) { - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; data->config &= ~TMP006_CONFIG_CR_MASK; data->config |= i << TMP006_CONFIG_CR_SHIFT; @@ -153,7 +158,7 @@ static int tmp006_write_raw(struct iio_dev *indio_dev, TMP006_CONFIG, data->config); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } return -EINVAL; diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index 716c795d08fb..82c72baccb62 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -145,8 +145,8 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops; trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups; - hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); - trig_info->timer.function = iio_hrtimer_trig_handler; + hrtimer_setup(&trig_info->timer, iio_hrtimer_trig_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_HARD); trig_info->sampling_frequency[0] = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency[0]; |