diff options
Diffstat (limited to 'drivers/iio')
503 files changed, 25433 insertions, 8351 deletions
diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 8601b9a8b8e7..5127e58eebc7 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -211,9 +211,9 @@ static const struct iio_chan_spec adis16201_channels[] = { BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, - BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 12), ADIS_INCLI_CHAN(Y, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, - BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 12), IIO_CHAN_SOFT_TIMESTAMP(7) }; diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 3d5c8719db3d..7d482dd595fa 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -9,37 +9,92 @@ #define _ADXL345_H_ #define ADXL345_REG_DEVID 0x00 +#define ADXL345_REG_THRESH_TAP 0x1D #define ADXL345_REG_OFSX 0x1E #define ADXL345_REG_OFSY 0x1F #define ADXL345_REG_OFSZ 0x20 #define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) + +/* Tap duration */ +#define ADXL345_REG_DUR 0x21 +/* Tap latency */ +#define ADXL345_REG_LATENT 0x22 +/* Tap window */ +#define ADXL345_REG_WINDOW 0x23 +/* Activity threshold */ +#define ADXL345_REG_THRESH_ACT 0x24 +/* Inactivity threshold */ +#define ADXL345_REG_THRESH_INACT 0x25 +/* Inactivity time */ +#define ADXL345_REG_TIME_INACT 0x26 +/* Axis enable control for activity and inactivity detection */ +#define ADXL345_REG_ACT_INACT_CTRL 0x27 +/* Free-fall threshold */ +#define ADXL345_REG_THRESH_FF 0x28 +/* Free-fall time */ +#define ADXL345_REG_TIME_FF 0x29 +/* Axis control for single tap or double tap */ +#define ADXL345_REG_TAP_AXIS 0x2A +/* Source of single tap or double tap */ +#define ADXL345_REG_ACT_TAP_STATUS 0x2B +/* Data rate and power mode control */ #define ADXL345_REG_BW_RATE 0x2C #define ADXL345_REG_POWER_CTL 0x2D +#define ADXL345_REG_INT_ENABLE 0x2E +#define ADXL345_REG_INT_MAP 0x2F +#define ADXL345_REG_INT_SOURCE 0x30 #define ADXL345_REG_DATA_FORMAT 0x31 -#define ADXL345_REG_DATAX0 0x32 -#define ADXL345_REG_DATAY0 0x34 -#define ADXL345_REG_DATAZ0 0x36 -#define ADXL345_REG_DATA_AXIS(index) \ - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) +#define ADXL345_REG_XYZ_BASE 0x32 +#define ADXL345_REG_DATA_AXIS(index) \ + (ADXL345_REG_XYZ_BASE + (index) * sizeof(__le16)) + +#define ADXL345_REG_FIFO_CTL 0x38 +#define ADXL345_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0) +/* 0: INT1, 1: INT2 */ +#define ADXL345_FIFO_CTL_TRIGGER_MSK BIT(5) +#define ADXL345_FIFO_CTL_MODE_MSK GENMASK(7, 6) +#define ADXL345_REG_FIFO_STATUS 0x39 +#define ADXL345_REG_FIFO_STATUS_MSK 0x3F + +#define ADXL345_INT_OVERRUN BIT(0) +#define ADXL345_INT_WATERMARK BIT(1) +#define ADXL345_INT_FREE_FALL BIT(2) +#define ADXL345_INT_INACTIVITY BIT(3) +#define ADXL345_INT_ACTIVITY BIT(4) +#define ADXL345_INT_DOUBLE_TAP BIT(5) +#define ADXL345_INT_SINGLE_TAP BIT(6) +#define ADXL345_INT_DATA_READY BIT(7) +/* + * BW_RATE bits - Bandwidth and output data rate. The default value is + * 0x0A, which translates to a 100 Hz output data rate + */ #define ADXL345_BW_RATE GENMASK(3, 0) +#define ADXL345_BW_LOW_POWER BIT(4) #define ADXL345_BASE_RATE_NANO_HZ 97656250LL -#define ADXL345_POWER_CTL_MEASURE BIT(3) #define ADXL345_POWER_CTL_STANDBY 0x00 +#define ADXL345_POWER_CTL_WAKEUP GENMASK(1, 0) +#define ADXL345_POWER_CTL_SLEEP BIT(2) +#define ADXL345_POWER_CTL_MEASURE BIT(3) +#define ADXL345_POWER_CTL_AUTO_SLEEP BIT(4) +#define ADXL345_POWER_CTL_LINK BIT(5) -#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */ -#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */ -#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */ -#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */ -#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */ - +/* Set the g range */ +#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) +/* Data is left justified */ +#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) +/* Up to 13-bits resolution */ +#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) +#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) +#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) #define ADXL345_DATA_FORMAT_2G 0 #define ADXL345_DATA_FORMAT_4G 1 #define ADXL345_DATA_FORMAT_8G 2 #define ADXL345_DATA_FORMAT_16G 3 #define ADXL345_DEVID 0xE5 +#define ADXL345_FIFO_SIZE 32 /* * In full-resolution mode, scale factor is maintained at ~4 mg/LSB @@ -56,12 +111,17 @@ */ #define ADXL375_USCALE 480000 +struct regmap; + +bool adxl345_is_volatile_reg(struct device *dev, unsigned int reg); + struct adxl345_chip_info { const char *name; int uscale; }; int adxl345_core_probe(struct device *dev, struct regmap *regmap, + bool fifo_delay_default, int (*setup)(struct device*, struct regmap*)); #endif /* _ADXL345_H_ */ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index b1efab0f6404..2e5fdd479e8d 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -7,6 +7,9 @@ * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf */ +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regmap.h> @@ -14,36 +17,377 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/kfifo_buf.h> #include "adxl345.h" -struct adxl345_data { +#define ADXL345_FIFO_BYPASS 0 +#define ADXL345_FIFO_FIFO 1 +#define ADXL345_FIFO_STREAM 2 + +#define ADXL345_DIRS 3 + +#define ADXL345_INT_NONE 0xff +#define ADXL345_INT1 0 +#define ADXL345_INT2 1 + +#define ADXL345_REG_TAP_AXIS_MSK GENMASK(2, 0) +#define ADXL345_REG_TAP_SUPPRESS_MSK BIT(3) +#define ADXL345_REG_TAP_SUPPRESS BIT(3) + +#define ADXL345_TAP_Z_EN BIT(0) +#define ADXL345_TAP_Y_EN BIT(1) +#define ADXL345_TAP_X_EN BIT(2) + +/* single/double tap */ +enum adxl345_tap_type { + ADXL345_SINGLE_TAP, + ADXL345_DOUBLE_TAP, +}; + +static const unsigned int adxl345_tap_int_reg[] = { + [ADXL345_SINGLE_TAP] = ADXL345_INT_SINGLE_TAP, + [ADXL345_DOUBLE_TAP] = ADXL345_INT_DOUBLE_TAP, +}; + +enum adxl345_tap_time_type { + ADXL345_TAP_TIME_LATENT, + ADXL345_TAP_TIME_WINDOW, + ADXL345_TAP_TIME_DUR, +}; + +static const unsigned int adxl345_tap_time_reg[] = { + [ADXL345_TAP_TIME_LATENT] = ADXL345_REG_LATENT, + [ADXL345_TAP_TIME_WINDOW] = ADXL345_REG_WINDOW, + [ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR, +}; + +struct adxl345_state { const struct adxl345_chip_info *info; struct regmap *regmap; + bool fifo_delay; /* delay: delay is needed for SPI */ + int irq; + u8 watermark; + u8 fifo_mode; + + u32 tap_duration_us; + u32 tap_latent_us; + u32 tap_window_us; + + __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); }; -#define ADXL345_CHANNEL(index, axis) { \ +static struct iio_event_spec adxl345_events[] = { + { + /* single tap */ + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_SINGLETAP, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_TIMEOUT), + }, + { + /* double tap */ + .type = IIO_EV_TYPE_GESTURE, + .dir = IIO_EV_DIR_DOUBLETAP, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_RESET_TIMEOUT) | + BIT(IIO_EV_INFO_TAP2_MIN_DELAY), + }, +}; + +#define ADXL345_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ - .address = index, \ + .address = (reg), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (index), \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ + .event_spec = adxl345_events, \ + .num_event_specs = ARRAY_SIZE(adxl345_events), \ } +enum adxl345_chans { + chan_x, chan_y, chan_z, +}; + static const struct iio_chan_spec adxl345_channels[] = { - ADXL345_CHANNEL(0, X), - ADXL345_CHANNEL(1, Y), - ADXL345_CHANNEL(2, Z), + ADXL345_CHANNEL(0, chan_x, X), + ADXL345_CHANNEL(1, chan_y, Y), + ADXL345_CHANNEL(2, chan_z, Z), }; +static const unsigned long adxl345_scan_masks[] = { + BIT(chan_x) | BIT(chan_y) | BIT(chan_z), + 0 +}; + +bool adxl345_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADXL345_REG_DATA_AXIS(0): + case ADXL345_REG_DATA_AXIS(1): + case ADXL345_REG_DATA_AXIS(2): + case ADXL345_REG_DATA_AXIS(3): + case ADXL345_REG_DATA_AXIS(4): + case ADXL345_REG_DATA_AXIS(5): + case ADXL345_REG_ACT_TAP_STATUS: + case ADXL345_REG_FIFO_STATUS: + case ADXL345_REG_INT_SOURCE: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(adxl345_is_volatile_reg, "IIO_ADXL345"); + +/** + * 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); +} + +/* tap */ + +static int _adxl345_set_tap_int(struct adxl345_state *st, + enum adxl345_tap_type type, bool state) +{ + unsigned int int_map = 0x00; + unsigned int tap_threshold; + bool axis_valid; + bool singletap_args_valid = false; + bool doubletap_args_valid = false; + bool en = false; + u32 axis_ctrl; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_TAP_AXIS, &axis_ctrl); + if (ret) + return ret; + + axis_valid = FIELD_GET(ADXL345_REG_TAP_AXIS_MSK, axis_ctrl) > 0; + + ret = regmap_read(st->regmap, ADXL345_REG_THRESH_TAP, &tap_threshold); + if (ret) + return ret; + + /* + * Note: A value of 0 for threshold and/or dur may result in undesirable + * behavior if single tap/double tap interrupts are enabled. + */ + singletap_args_valid = tap_threshold > 0 && st->tap_duration_us > 0; + + if (type == ADXL345_SINGLE_TAP) { + en = axis_valid && singletap_args_valid; + } else { + /* doubletap: Window must be equal or greater than latent! */ + doubletap_args_valid = st->tap_latent_us > 0 && + st->tap_window_us > 0 && + st->tap_window_us >= st->tap_latent_us; + + en = axis_valid && singletap_args_valid && doubletap_args_valid; + } + + if (state && en) + int_map |= adxl345_tap_int_reg[type]; + + return regmap_update_bits(st->regmap, ADXL345_REG_INT_ENABLE, + adxl345_tap_int_reg[type], int_map); +} + +static int adxl345_is_tap_en(struct adxl345_state *st, + enum iio_modifier axis, + enum adxl345_tap_type type, bool *en) +{ + unsigned int regval; + u32 axis_ctrl; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_TAP_AXIS, &axis_ctrl); + if (ret) + return ret; + + /* Verify if axis is enabled for the tap detection. */ + switch (axis) { + case IIO_MOD_X: + *en = FIELD_GET(ADXL345_TAP_X_EN, axis_ctrl); + break; + case IIO_MOD_Y: + *en = FIELD_GET(ADXL345_TAP_Y_EN, axis_ctrl); + break; + case IIO_MOD_Z: + *en = FIELD_GET(ADXL345_TAP_Z_EN, axis_ctrl); + break; + default: + *en = false; + return -EINVAL; + } + + if (*en) { + /* + * If axis allow for tap detection, verify if the interrupt is + * enabled for tap detection. + */ + ret = regmap_read(st->regmap, ADXL345_REG_INT_ENABLE, ®val); + if (ret) + return ret; + + *en = adxl345_tap_int_reg[type] & regval; + } + + return 0; +} + +static int adxl345_set_singletap_en(struct adxl345_state *st, + enum iio_modifier axis, bool en) +{ + int ret; + u32 axis_ctrl; + + switch (axis) { + case IIO_MOD_X: + axis_ctrl = ADXL345_TAP_X_EN; + break; + case IIO_MOD_Y: + axis_ctrl = ADXL345_TAP_Y_EN; + break; + case IIO_MOD_Z: + axis_ctrl = ADXL345_TAP_Z_EN; + break; + default: + return -EINVAL; + } + + if (en) + ret = regmap_set_bits(st->regmap, ADXL345_REG_TAP_AXIS, + axis_ctrl); + else + ret = regmap_clear_bits(st->regmap, ADXL345_REG_TAP_AXIS, + axis_ctrl); + if (ret) + return ret; + + return _adxl345_set_tap_int(st, ADXL345_SINGLE_TAP, en); +} + +static int adxl345_set_doubletap_en(struct adxl345_state *st, bool en) +{ + int ret; + + /* + * Generally suppress detection of spikes during the latency period as + * double taps here, this is fully optional for double tap detection + */ + ret = regmap_update_bits(st->regmap, ADXL345_REG_TAP_AXIS, + ADXL345_REG_TAP_SUPPRESS_MSK, + en ? ADXL345_REG_TAP_SUPPRESS : 0x00); + if (ret) + return ret; + + return _adxl345_set_tap_int(st, ADXL345_DOUBLE_TAP, en); +} + +static int _adxl345_set_tap_time(struct adxl345_state *st, + enum adxl345_tap_time_type type, u32 val_us) +{ + unsigned int regval; + + switch (type) { + case ADXL345_TAP_TIME_WINDOW: + st->tap_window_us = val_us; + break; + case ADXL345_TAP_TIME_LATENT: + st->tap_latent_us = val_us; + break; + case ADXL345_TAP_TIME_DUR: + st->tap_duration_us = val_us; + break; + } + + /* + * The scale factor is 1250us / LSB for tap_window_us and tap_latent_us. + * For tap_duration_us the scale factor is 625us / LSB. + */ + if (type == ADXL345_TAP_TIME_DUR) + regval = DIV_ROUND_CLOSEST(val_us, 625); + else + regval = DIV_ROUND_CLOSEST(val_us, 1250); + + return regmap_write(st->regmap, adxl345_tap_time_reg[type], regval); +} + +static int adxl345_set_tap_duration(struct adxl345_state *st, u32 val_int, + u32 val_fract_us) +{ + /* + * Max value is 255 * 625 us = 0.159375 seconds + * + * Note: the scaling is similar to the scaling in the ADXL380 + */ + if (val_int || val_fract_us > 159375) + return -EINVAL; + + return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_DUR, val_fract_us); +} + +static int adxl345_set_tap_window(struct adxl345_state *st, u32 val_int, + u32 val_fract_us) +{ + /* + * Max value is 255 * 1250 us = 0.318750 seconds + * + * Note: the scaling is similar to the scaling in the ADXL380 + */ + if (val_int || val_fract_us > 318750) + return -EINVAL; + + return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_WINDOW, val_fract_us); +} + +static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int, + u32 val_fract_us) +{ + /* + * Max value is 255 * 1250 us = 0.318750 seconds + * + * Note: the scaling is similar to the scaling in the ADXL380 + */ + if (val_int || val_fract_us > 318750) + return -EINVAL; + + return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us); +} + static int adxl345_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - struct adxl345_data *data = iio_priv(indio_dev); + struct adxl345_state *st = iio_priv(indio_dev); __le16 accel; long long samp_freq_nhz; unsigned int regval; @@ -56,22 +400,22 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte */ - ret = regmap_bulk_read(data->regmap, + ret = regmap_bulk_read(st->regmap, ADXL345_REG_DATA_AXIS(chan->address), &accel, sizeof(accel)); - if (ret < 0) + if (ret) return ret; *val = sign_extend32(le16_to_cpu(accel), 12); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; - *val2 = data->info->uscale; + *val2 = st->info->uscale; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_CALIBBIAS: - ret = regmap_read(data->regmap, + ret = regmap_read(st->regmap, ADXL345_REG_OFS_AXIS(chan->address), ®val); - if (ret < 0) + if (ret) return ret; /* * 8-bit resolution at +/- 2g, that is 4x accel data scale @@ -81,8 +425,8 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - ret = regmap_read(data->regmap, ADXL345_REG_BW_RATE, ®val); - if (ret < 0) + ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, ®val); + if (ret) return ret; samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ << @@ -99,7 +443,7 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct adxl345_data *data = iio_priv(indio_dev); + struct adxl345_state *st = iio_priv(indio_dev); s64 n; switch (mask) { @@ -108,14 +452,14 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, * 8-bit resolution at +/- 2g, that is 4x accel data scale * factor */ - return regmap_write(data->regmap, + return regmap_write(st->regmap, ADXL345_REG_OFS_AXIS(chan->address), val / 4); case IIO_CHAN_INFO_SAMP_FREQ: n = div_s64(val * NANOHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ); - return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE, + return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE, ADXL345_BW_RATE, clamp_val(ilog2(n), 0, ADXL345_BW_RATE)); @@ -124,6 +468,184 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, return -EINVAL; } +static int adxl345_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 adxl345_state *st = iio_priv(indio_dev); + bool int_en; + int ret; + + switch (type) { + case IIO_EV_TYPE_GESTURE: + switch (dir) { + case IIO_EV_DIR_SINGLETAP: + ret = adxl345_is_tap_en(st, chan->channel2, + ADXL345_SINGLE_TAP, &int_en); + if (ret) + return ret; + return int_en; + case IIO_EV_DIR_DOUBLETAP: + ret = adxl345_is_tap_en(st, chan->channel2, + ADXL345_DOUBLE_TAP, &int_en); + if (ret) + return ret; + return int_en; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl345_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 adxl345_state *st = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_GESTURE: + switch (dir) { + case IIO_EV_DIR_SINGLETAP: + return adxl345_set_singletap_en(st, chan->channel2, state); + case IIO_EV_DIR_DOUBLETAP: + return adxl345_set_doubletap_en(st, state); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl345_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 adxl345_state *st = iio_priv(indio_dev); + unsigned int tap_threshold; + int ret; + + switch (type) { + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + /* + * The scale factor would be 62.5mg/LSB (i.e. 0xFF = 16g) but + * not applied here. In context of this general purpose sensor, + * what imports is rather signal intensity than the absolute + * measured g value. + */ + ret = regmap_read(st->regmap, ADXL345_REG_THRESH_TAP, + &tap_threshold); + if (ret) + return ret; + *val = sign_extend32(tap_threshold, 7); + return IIO_VAL_INT; + case IIO_EV_INFO_TIMEOUT: + *val = st->tap_duration_us; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_INFO_RESET_TIMEOUT: + *val = st->tap_window_us; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + case IIO_EV_INFO_TAP2_MIN_DELAY: + *val = st->tap_latent_us; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int adxl345_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) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int ret; + + ret = adxl345_set_measure_en(st, false); + if (ret) + return ret; + + switch (type) { + case IIO_EV_TYPE_GESTURE: + switch (info) { + case IIO_EV_INFO_VALUE: + ret = regmap_write(st->regmap, ADXL345_REG_THRESH_TAP, + min(val, 0xFF)); + if (ret) + return ret; + break; + case IIO_EV_INFO_TIMEOUT: + ret = adxl345_set_tap_duration(st, val, val2); + if (ret) + return ret; + break; + case IIO_EV_INFO_RESET_TIMEOUT: + ret = adxl345_set_tap_window(st, val, val2); + if (ret) + return ret; + break; + case IIO_EV_INFO_TAP2_MIN_DELAY: + ret = adxl345_set_tap_latent(st, val, val2); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return adxl345_set_measure_en(st, true); +} + +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); + const unsigned int fifo_mask = 0x1F, watermark_mask = 0x02; + int ret; + + value = min(value, ADXL345_FIFO_SIZE - 1); + + ret = regmap_update_bits(st->regmap, ADXL345_REG_FIFO_CTL, fifo_mask, value); + if (ret) + return ret; + + st->watermark = value; + return regmap_update_bits(st->regmap, ADXL345_REG_INT_ENABLE, + watermark_mask, ADXL345_INT_WATERMARK); +} + static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) @@ -138,6 +660,13 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static void adxl345_powerdown(void *ptr) +{ + struct adxl345_state *st = ptr; + + adxl345_set_measure_en(st, false); +} + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( "0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200" ); @@ -151,69 +680,338 @@ static const struct attribute_group adxl345_attrs_group = { .attrs = adxl345_attrs, }; -static const struct iio_info adxl345_info = { - .attrs = &adxl345_attrs_group, - .read_raw = adxl345_read_raw, - .write_raw = adxl345_write_raw, - .write_raw_get_fmt = adxl345_write_raw_get_fmt, +static int adxl345_set_fifo(struct adxl345_state *st) +{ + unsigned int intio; + int ret; + + /* FIFO should only be configured while in standby mode */ + ret = adxl345_set_measure_en(st, false); + if (ret) + return ret; + + ret = regmap_read(st->regmap, ADXL345_REG_INT_MAP, &intio); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, + FIELD_PREP(ADXL345_FIFO_CTL_SAMPLES_MSK, + st->watermark) | + FIELD_PREP(ADXL345_FIFO_CTL_TRIGGER_MSK, intio) | + FIELD_PREP(ADXL345_FIFO_CTL_MODE_MSK, + st->fifo_mode)); + if (ret) + return ret; + + return adxl345_set_measure_en(st, true); +} + +/** + * adxl345_get_samples() - Read number of FIFO entries. + * @st: The initialized state instance of this driver. + * + * The sensor does not support treating any axis individually, or exclude them + * from measuring. + * + * Return: negative error, or value. + */ +static int adxl345_get_samples(struct adxl345_state *st) +{ + unsigned int regval = 0; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_FIFO_STATUS, ®val); + if (ret) + return ret; + + return FIELD_GET(ADXL345_REG_FIFO_STATUS_MSK, regval); +} + +/** + * adxl345_fifo_transfer() - Read samples number of elements. + * @st: The instance of the state object of this sensor. + * @samples: The number of lines in the FIFO referred to as fifo_entry. + * + * It is recommended that a multiple-byte read of all registers be performed to + * prevent a change in data between reads of sequential registers. That is to + * read out the data registers X0, X1, Y0, Y1, Z0, Z1, i.e. 6 bytes at once. + * + * Return: 0 or error value. + */ +static int adxl345_fifo_transfer(struct adxl345_state *st, int samples) +{ + size_t count; + int i, ret = 0; + + /* count is the 3x the fifo_buf element size, hence 6B */ + count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS; + for (i = 0; i < samples; i++) { + /* read 3x 2 byte elements from base address into next fifo_buf position */ + ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE, + st->fifo_buf + (i * count / 2), count); + if (ret) + return ret; + + /* + * To ensure that the FIFO has completely popped, there must be at least 5 + * us between the end of reading the data registers, signified by the + * transition to register 0x38 from 0x37 or the CS pin going high, and the + * start of new reads of the FIFO or reading the FIFO_STATUS register. For + * SPI operation at 1.5 MHz or lower, the register addressing portion of the + * transmission is sufficient delay to ensure the FIFO has completely + * popped. It is necessary for SPI operation greater than 1.5 MHz to + * de-assert the CS pin to ensure a total of 5 us, which is at most 3.4 us + * at 5 MHz operation. + */ + if (st->fifo_delay && samples > 1) + udelay(3); + } + return ret; +} + +/** + * adxl345_fifo_reset() - Empty the FIFO in error condition. + * @st: The instance to the state object of the sensor. + * + * Read all elements of the FIFO. Reading the interrupt source register + * resets the sensor. + */ +static void adxl345_fifo_reset(struct adxl345_state *st) +{ + int regval; + int samples; + + adxl345_set_measure_en(st, false); + + samples = adxl345_get_samples(st); + if (samples > 0) + adxl345_fifo_transfer(st, samples); + + regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val); + + adxl345_set_measure_en(st, true); +} + +static int adxl345_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl345_state *st = iio_priv(indio_dev); + + st->fifo_mode = ADXL345_FIFO_STREAM; + return adxl345_set_fifo(st); +} + +static int adxl345_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl345_state *st = iio_priv(indio_dev); + int ret; + + st->fifo_mode = ADXL345_FIFO_BYPASS; + ret = adxl345_set_fifo(st); + if (ret) + return ret; + + return regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, 0x00); +} + +static const struct iio_buffer_setup_ops adxl345_buffer_ops = { + .postenable = adxl345_buffer_postenable, + .predisable = adxl345_buffer_predisable, }; -static int adxl345_powerup(void *regmap) +static int adxl345_fifo_push(struct iio_dev *indio_dev, + int samples) { - return regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_MEASURE); + struct adxl345_state *st = iio_priv(indio_dev); + int i, ret; + + if (samples <= 0) + return -EINVAL; + + ret = adxl345_fifo_transfer(st, samples); + if (ret) + return ret; + + for (i = 0; i < ADXL345_DIRS * samples; i += ADXL345_DIRS) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + + return 0; } -static void adxl345_powerdown(void *regmap) +static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat, + enum iio_modifier tap_dir) { - regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY); + s64 ts = iio_get_time_ns(indio_dev); + struct adxl345_state *st = iio_priv(indio_dev); + int samples; + int ret = -ENOENT; + + if (FIELD_GET(ADXL345_INT_SINGLE_TAP, int_stat)) { + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, tap_dir, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_SINGLETAP), + ts); + if (ret) + return ret; + } + + if (FIELD_GET(ADXL345_INT_DOUBLE_TAP, int_stat)) { + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, tap_dir, + IIO_EV_TYPE_GESTURE, + IIO_EV_DIR_DOUBLETAP), + ts); + if (ret) + return ret; + } + + if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) { + samples = adxl345_get_samples(st); + if (samples < 0) + return -EINVAL; + + if (adxl345_fifo_push(indio_dev, samples) < 0) + return -EINVAL; + + ret = 0; + } + + return ret; +} + +/** + * adxl345_irq_handler() - Handle irqs of the ADXL345. + * @irq: The irq being handled. + * @p: The struct iio_device pointer for the device. + * + * Return: The interrupt was handled. + */ +static irqreturn_t adxl345_irq_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adxl345_state *st = iio_priv(indio_dev); + unsigned int regval; + enum iio_modifier tap_dir = IIO_NO_MOD; + u32 axis_ctrl; + int int_stat; + int ret; + + ret = regmap_read(st->regmap, ADXL345_REG_TAP_AXIS, &axis_ctrl); + if (ret) + return IRQ_NONE; + + if (FIELD_GET(ADXL345_REG_TAP_AXIS_MSK, axis_ctrl)) { + ret = regmap_read(st->regmap, ADXL345_REG_ACT_TAP_STATUS, ®val); + if (ret) + return IRQ_NONE; + + if (FIELD_GET(ADXL345_TAP_Z_EN, regval)) + tap_dir = IIO_MOD_Z; + else if (FIELD_GET(ADXL345_TAP_Y_EN, regval)) + tap_dir = IIO_MOD_Y; + else if (FIELD_GET(ADXL345_TAP_X_EN, regval)) + tap_dir = IIO_MOD_X; + } + + if (regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &int_stat)) + return IRQ_NONE; + + if (adxl345_push_event(indio_dev, int_stat, tap_dir)) + goto err; + + if (FIELD_GET(ADXL345_INT_OVERRUN, int_stat)) + goto err; + + return IRQ_HANDLED; + +err: + adxl345_fifo_reset(st); + + return IRQ_HANDLED; } +static const struct iio_info adxl345_info = { + .attrs = &adxl345_attrs_group, + .read_raw = adxl345_read_raw, + .write_raw = adxl345_write_raw, + .write_raw_get_fmt = adxl345_write_raw_get_fmt, + .read_event_config = adxl345_read_event_config, + .write_event_config = adxl345_write_event_config, + .read_event_value = adxl345_read_event_value, + .write_event_value = adxl345_write_event_value, + .debugfs_reg_access = &adxl345_reg_access, + .hwfifo_set_watermark = adxl345_set_watermark, +}; + /** - * adxl345_core_probe() - probe and setup for the adxl345 accelerometer, - * also covers the adlx375 accelerometer + * adxl345_core_probe() - Probe and setup for the accelerometer. * @dev: Driver model representation of the device * @regmap: Regmap instance for the device + * @fifo_delay_default: Using FIFO with SPI needs delay * @setup: Setup routine to be executed right before the standard device * setup * + * For SPI operation greater than 1.6 MHz, it is necessary to deassert the CS + * pin to ensure a total delay of 5 us; otherwise, the delay is not sufficient. + * The total delay necessary for 5 MHz operation is at most 3.4 us. This is not + * a concern when using I2C mode because the communication rate is low enough + * to ensure a sufficient delay between FIFO reads. + * Ref: "Retrieving Data from FIFO", p. 21 of 36, Data Sheet ADXL345 Rev. G + * * Return: 0 on success, negative errno on error */ int adxl345_core_probe(struct device *dev, struct regmap *regmap, + bool fifo_delay_default, int (*setup)(struct device*, struct regmap*)) { - struct adxl345_data *data; + struct adxl345_state *st; struct iio_dev *indio_dev; u32 regval; + u8 intio = ADXL345_INT1; unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE | ADXL345_DATA_FORMAT_JUSTIFY | ADXL345_DATA_FORMAT_FULL_RES | ADXL345_DATA_FORMAT_SELF_TEST); + unsigned int tap_threshold; int ret; - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; - data = iio_priv(indio_dev); - data->regmap = regmap; - data->info = device_get_match_data(dev); - if (!data->info) + st = iio_priv(indio_dev); + st->regmap = regmap; + st->info = device_get_match_data(dev); + if (!st->info) return -ENODEV; + st->fifo_delay = fifo_delay_default; - indio_dev->name = data->info->name; + /* Init with reasonable values */ + tap_threshold = 48; /* 48 [0x30] -> ~3g */ + st->tap_duration_us = 16; /* 16 [0x10] -> .010 */ + st->tap_window_us = 64; /* 64 [0x40] -> .080 */ + st->tap_latent_us = 16; /* 16 [0x10] -> .020 */ + + indio_dev->name = st->info->name; indio_dev->info = &adxl345_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl345_channels; indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); + indio_dev->available_scan_masks = adxl345_scan_masks; + + /* Reset interrupts at start up */ + ret = regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, 0x00); + if (ret) + return ret; if (setup) { /* Perform optional initial bus specific configuration */ - ret = setup(dev, data->regmap); + ret = setup(dev, st->regmap); if (ret) return ret; /* Enable full-resolution mode */ - ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT, + ret = regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT, data_format_mask, ADXL345_DATA_FORMAT_FULL_RES); if (ret) @@ -222,15 +1020,15 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, } else { /* Enable full-resolution mode (init all data_format bits) */ - ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, + ret = regmap_write(st->regmap, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT_FULL_RES); if (ret) return dev_err_probe(dev, ret, "Failed to set data range\n"); } - ret = regmap_read(data->regmap, ADXL345_REG_DEVID, ®val); - if (ret < 0) + ret = regmap_read(st->regmap, ADXL345_REG_DEVID, ®val); + if (ret) return dev_err_probe(dev, ret, "Error reading device ID\n"); if (regval != ADXL345_DEVID) @@ -238,14 +1036,57 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, regval, ADXL345_DEVID); /* Enable measurement mode */ - ret = adxl345_powerup(data->regmap); - if (ret < 0) + ret = adxl345_set_measure_en(st, true); + if (ret) return dev_err_probe(dev, ret, "Failed to enable measurement mode\n"); - ret = devm_add_action_or_reset(dev, adxl345_powerdown, data->regmap); - if (ret < 0) + ret = devm_add_action_or_reset(dev, adxl345_powerdown, st); + if (ret) return ret; + st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1"); + if (st->irq < 0) { + intio = ADXL345_INT2; + st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); + if (st->irq < 0) + intio = ADXL345_INT_NONE; + } + + if (intio != ADXL345_INT_NONE) { + /* + * Any bits set to 0 in the INT map register send their respective + * interrupts to the INT1 pin, whereas bits set to 1 send their respective + * interrupts to the INT2 pin. The intio shall convert this accordingly. + */ + regval = intio ? 0xff : 0; + + ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, regval); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADXL345_REG_THRESH_TAP, tap_threshold); + if (ret) + return ret; + + /* FIFO_STREAM mode is going to be activated later */ + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &adxl345_buffer_ops); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, st->irq, NULL, + &adxl345_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + } else { + ret = regmap_write(st->regmap, ADXL345_REG_FIFO_CTL, + FIELD_PREP(ADXL345_FIFO_CTL_MODE_MSK, + ADXL345_FIFO_BYPASS)); + if (ret) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(adxl345_core_probe, "IIO_ADXL345"); diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index cb23fb11fcd7..af84c0043c6c 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -17,6 +17,8 @@ static const struct regmap_config adxl345_i2c_regmap_config = { .reg_bits = 8, .val_bits = 8, + .volatile_reg = adxl345_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }; static int adxl345_i2c_probe(struct i2c_client *client) @@ -27,7 +29,7 @@ static int adxl345_i2c_probe(struct i2c_client *client) if (IS_ERR(regmap)) return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&client->dev, regmap, NULL); + return adxl345_core_probe(&client->dev, regmap, false, NULL); } static const struct adxl345_chip_info adxl345_i2c_info = { diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index 968e7b390d4b..0315f4bfd69a 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -12,12 +12,15 @@ #include "adxl345.h" #define ADXL345_MAX_SPI_FREQ_HZ 5000000 +#define ADXL345_MAX_FREQ_NO_FIFO_DELAY 1500000 static const struct regmap_config adxl345_spi_regmap_config = { .reg_bits = 8, .val_bits = 8, /* Setting bits 7 and 6 enables multiple-byte read */ .read_flag_mask = BIT(7) | BIT(6), + .volatile_reg = adxl345_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }; static int adxl345_spi_setup(struct device *dev, struct regmap *regmap) @@ -28,6 +31,7 @@ static int adxl345_spi_setup(struct device *dev, struct regmap *regmap) static int adxl345_spi_probe(struct spi_device *spi) { struct regmap *regmap; + bool needs_delay; /* Bail out if max_speed_hz exceeds 5 MHz */ if (spi->max_speed_hz > ADXL345_MAX_SPI_FREQ_HZ) @@ -38,10 +42,11 @@ static int adxl345_spi_probe(struct spi_device *spi) if (IS_ERR(regmap)) return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n"); + needs_delay = spi->max_speed_hz > ADXL345_MAX_FREQ_NO_FIFO_DELAY; if (spi->mode & SPI_3WIRE) - return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup); + return adxl345_core_probe(&spi->dev, regmap, needs_delay, adxl345_spi_setup); else - return adxl345_core_probe(&spi->dev, regmap, NULL); + return adxl345_core_probe(&spi->dev, regmap, needs_delay, NULL); } static const struct adxl345_chip_info adxl345_spi_info = { diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c index e8cd21fa77a6..2e00fd51b4d5 100644 --- a/drivers/iio/accel/adxl355_core.c +++ b/drivers/iio/accel/adxl355_core.c @@ -231,7 +231,7 @@ struct adxl355_data { u8 transf_buf[3]; struct { u8 buf[14]; - s64 ts; + aligned_s64 ts; } buffer; } __aligned(IIO_DMA_MINALIGN); }; @@ -666,8 +666,8 @@ static irqreturn_t adxl355_trigger_handler(int irq, void *p) if (ret) goto out_unlock_notify; - iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->buffer, + sizeof(data->buffer), pf->timestamp); out_unlock_notify: mutex_unlock(&data->lock); diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index a48ac0d7bd96..0c04b2bb7efb 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) @@ -604,39 +601,32 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) if (ret) return ret; + st->odr = odr; + /* Activity timers depend on ODR */ ret = _adxl367_set_act_time_ms(st, st->act_time_ms); if (ret) return ret; - ret = _adxl367_set_inact_time_ms(st, st->inact_time_ms); - if (ret) - return ret; - - st->odr = odr; - - return 0; + return _adxl367_set_inact_time_ms(st, st->inact_time_ms); } 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 +715,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 +839,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 +904,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 +918,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 +1071,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 +1092,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); - 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_act_interrupt_en(st, act, state); + if (ret) + return ret; - ret = adxl367_set_act_interrupt_en(st, act, state); - 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_en(st, act, state ? ADCL367_ACT_REF_ENABLED - : ADXL367_ACT_DISABLED); - if (ret) - return ret; + return adxl367_set_measure_en(st, true); +} - return adxl367_set_measure_en(st, true); - } - unreachable(); +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; + + 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/adxl367_i2c.c b/drivers/iio/accel/adxl367_i2c.c index 80f0b642b9b0..1c7d2eb054a2 100644 --- a/drivers/iio/accel/adxl367_i2c.c +++ b/drivers/iio/accel/adxl367_i2c.c @@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(i2c, adxl367_i2c_id); static const struct of_device_id adxl367_of_match[] = { { .compatible = "adi,adxl367" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, adxl367_of_match); diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c index 49d7c8fbe8ed..3fed56bb9054 100644 --- a/drivers/iio/accel/adxl367_spi.c +++ b/drivers/iio/accel/adxl367_spi.c @@ -139,13 +139,13 @@ static int adxl367_spi_probe(struct spi_device *spi) static const struct spi_device_id adxl367_spi_id[] = { { "adxl367", 0 }, - { }, + { } }; MODULE_DEVICE_TABLE(spi, adxl367_spi_id); static const struct of_device_id adxl367_of_match[] = { { .compatible = "adi,adxl367" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, adxl367_of_match); 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/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c index 43d5fd921be7..186d4fe9a556 100644 --- a/drivers/iio/accel/adxl372_i2c.c +++ b/drivers/iio/accel/adxl372_i2c.c @@ -43,7 +43,7 @@ static int adxl372_i2c_probe(struct i2c_client *client) static const struct i2c_device_id adxl372_i2c_id[] = { { "adxl372" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 1ab1997a55b1..39941b519c3b 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -34,7 +34,7 @@ static int adxl372_spi_probe(struct spi_device *spi) static const struct spi_device_id adxl372_spi_id[] = { { "adxl372", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adxl372_spi_id); 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..93a868678722 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') { @@ -888,7 +887,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) mutex_unlock(&data->mutex); - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), time_ns); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 009e6243c6cb..38f7498431ee 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -66,7 +66,7 @@ struct bma220_data { struct { s8 chans[3]; /* Ensure timestamp is naturally aligned. */ - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN); }; @@ -103,8 +103,8 @@ static irqreturn_t bma220_trigger_handler(int irq, void *p) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: mutex_unlock(&data->lock); iio_trigger_notify_done(indio_dev->trig); @@ -307,12 +307,12 @@ static DEFINE_SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume); static const struct spi_device_id bma220_spi_id[] = { {"bma220", 0}, - {} + { } }; static const struct acpi_device_id bma220_acpi_id[] = { {"BMA0220", 0}, - {} + { } }; MODULE_DEVICE_TABLE(spi, bma220_spi_id); diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index ae806ed60271..85e23badf733 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, }; @@ -1591,8 +1591,9 @@ static irqreturn_t bma400_trigger_handler(int irq, void *p) data->buffer.temperature = temp; } - iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->buffer, + sizeof(data->buffer), + iio_get_time_ns(indio_dev)); mutex_unlock(&data->mutex); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 0d4ce6c38931..b4604f441553 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -240,7 +240,7 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { {"BOSC0200"}, {"BSBA0150"}, {"DUAL250E"}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); @@ -255,7 +255,7 @@ static const struct i2c_device_id bmc150_accel_id[] = { {"bmc150_accel"}, {"bmc156_accel", BOSCH_BMC156}, {"bmi055_accel"}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bmc150_accel_id); @@ -271,7 +271,7 @@ static const struct of_device_id bmc150_accel_of_match[] = { { .compatible = "bosch,bmc150_accel" }, { .compatible = "bosch,bmc156_accel" }, { .compatible = "bosch,bmi055_accel" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bmc150_accel_of_match); diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 70b3642656ab..26ce50b37716 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -48,7 +48,7 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { {"BMC150A"}, {"BMI055A"}, {"BSBA0150"}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); @@ -62,7 +62,7 @@ static const struct spi_device_id bmc150_accel_id[] = { {"bmc150_accel"}, {"bmc156_accel", BOSCH_BMC156}, {"bmi055_accel"}, - {} + { } }; MODULE_DEVICE_TABLE(spi, bmc150_accel_id); 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/bmi088-accel-i2c.c b/drivers/iio/accel/bmi088-accel-i2c.c index bd22bd0d3c25..310f863029bb 100644 --- a/drivers/iio/accel/bmi088-accel-i2c.c +++ b/drivers/iio/accel/bmi088-accel-i2c.c @@ -40,7 +40,7 @@ static const struct of_device_id bmi088_of_match[] = { { .compatible = "bosch,bmi085-accel" }, { .compatible = "bosch,bmi088-accel" }, { .compatible = "bosch,bmi090l-accel" }, - {} + { } }; MODULE_DEVICE_TABLE(of, bmi088_of_match); @@ -48,7 +48,7 @@ static const struct i2c_device_id bmi088_accel_id[] = { { "bmi085-accel", BOSCH_BMI085 }, { "bmi088-accel", BOSCH_BMI088 }, { "bmi090l-accel", BOSCH_BMI090L }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bmi088_accel_id); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index c9d51a74c07f..44cb50c76cb1 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -67,7 +67,7 @@ static const struct of_device_id bmi088_of_match[] = { { .compatible = "bosch,bmi085-accel" }, { .compatible = "bosch,bmi088-accel" }, { .compatible = "bosch,bmi090l-accel" }, - {} + { } }; MODULE_DEVICE_TABLE(of, bmi088_of_match); @@ -75,7 +75,7 @@ static const struct spi_device_id bmi088_accel_id[] = { {"bmi085-accel", BOSCH_BMI085}, {"bmi088-accel", BOSCH_BMI088}, {"bmi090l-accel", BOSCH_BMI090L}, - {} + { } }; MODULE_DEVICE_TABLE(spi, bmi088_accel_id); diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 992286828844..c2dd123b9021 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -157,7 +157,7 @@ static const struct da280_match_data da280_match_data = { "da280", 3 }; static const struct acpi_device_id da280_acpi_match[] = { { "NSA2513", (kernel_ulong_t)&da217_match_data }, { "MIRAACC", (kernel_ulong_t)&da280_match_data }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, da280_acpi_match); @@ -165,7 +165,7 @@ static const struct i2c_device_id da280_i2c_id[] = { { "da217", (kernel_ulong_t)&da217_match_data }, { "da226", (kernel_ulong_t)&da226_match_data }, { "da280", (kernel_ulong_t)&da280_match_data }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, da280_i2c_id); diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index 94f827acdd1c..e1df7b009d89 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -269,7 +269,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume); static const struct i2c_device_id da311_i2c_id[] = { { "da311" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, da311_i2c_id); diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c index 35c0eefb741e..71cd1928baa6 100644 --- a/drivers/iio/accel/dmard10.c +++ b/drivers/iio/accel/dmard10.c @@ -232,7 +232,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, static const struct i2c_device_id dmard10_i2c_id[] = { { "dmard10" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id); diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index 65aac60f1245..b10a30960e1e 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -23,6 +23,7 @@ #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/types.h> +#include <linux/units.h> #include <linux/iio/buffer.h> #include <linux/iio/events.h> @@ -129,6 +130,8 @@ #define FXLS8962AF_DEVICE_ID 0x62 #define FXLS8964AF_DEVICE_ID 0x84 +#define FXLS8974CF_DEVICE_ID 0x86 +#define FXLS8967AF_DEVICE_ID 0x87 /* Raw temp channel offset */ #define FXLS8962AF_TEMP_CENTER_VAL 25 @@ -437,8 +440,16 @@ static int fxls8962af_read_raw(struct iio_dev *indio_dev, *val = FXLS8962AF_TEMP_CENTER_VAL; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = 0; - return fxls8962af_read_full_scale(data, val2); + switch (chan->type) { + case IIO_TEMP: + *val = MILLIDEGREE_PER_DEGREE; + return IIO_VAL_INT; + case IIO_ACCEL: + *val = 0; + return fxls8962af_read_full_scale(data, val2); + default: + return -EINVAL; + } case IIO_CHAN_INFO_SAMP_FREQ: return fxls8962af_read_samp_freq(data, val, val2); default: @@ -458,22 +469,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; @@ -681,14 +690,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; @@ -737,9 +745,11 @@ static const struct iio_event_spec fxls8962af_event[] = { .type = IIO_TEMP, \ .address = FXLS8962AF_TEMP_OUT, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_OFFSET),\ .scan_index = -1, \ .scan_type = { \ + .sign = 's', \ .realbits = 8, \ .storagebits = 8, \ }, \ @@ -766,6 +776,18 @@ static const struct fxls8962af_chip_info fxls_chip_info_table[] = { .channels = fxls8962af_channels, .num_channels = ARRAY_SIZE(fxls8962af_channels), }, + [fxls8967af] = { + .chip_id = FXLS8967AF_DEVICE_ID, + .name = "fxls8967af", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, + [fxls8974cf] = { + .chip_id = FXLS8974CF_DEVICE_ID, + .name = "fxls8974cf", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, }; static const struct iio_info fxls8962af_info = { @@ -855,6 +877,8 @@ static int fxls8962af_buffer_predisable(struct iio_dev *indio_dev) if (ret) return ret; + synchronize_irq(data->irq); + ret = __fxls8962af_fifo_set_mode(data, false); if (data->enable_event) @@ -972,8 +996,8 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) sizeof(data->scan.channels[0])); } - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - tstamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, + sizeof(data->scan), tstamp); tstamp += sample_period; } @@ -1215,8 +1239,11 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) if (ret) return ret; - if (device_property_read_bool(dev, "wakeup-source")) - device_init_wakeup(dev, true); + if (device_property_read_bool(dev, "wakeup-source")) { + ret = devm_device_init_wakeup(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to init wakeup\n"); + } return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c index 2e1bb43ef2a1..106198a12474 100644 --- a/drivers/iio/accel/fxls8962af-i2c.c +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -30,14 +30,16 @@ static int fxls8962af_probe(struct i2c_client *client) static const struct i2c_device_id fxls8962af_id[] = { { "fxls8962af", fxls8962af }, { "fxls8964af", fxls8964af }, - {} + { "fxls8967af", fxls8967af }, + { "fxls8974cf", fxls8974cf }, + { } }; MODULE_DEVICE_TABLE(i2c, fxls8962af_id); static const struct of_device_id fxls8962af_of_match[] = { { .compatible = "nxp,fxls8962af" }, { .compatible = "nxp,fxls8964af" }, - {} + { } }; MODULE_DEVICE_TABLE(of, fxls8962af_of_match); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c index 46fc6e002714..bdafd1f615d9 100644 --- a/drivers/iio/accel/fxls8962af-spi.c +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -30,14 +30,14 @@ static int fxls8962af_probe(struct spi_device *spi) static const struct of_device_id fxls8962af_spi_of_match[] = { { .compatible = "nxp,fxls8962af" }, { .compatible = "nxp,fxls8964af" }, - {} + { } }; MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match); static const struct spi_device_id fxls8962af_spi_id_table[] = { { "fxls8962af", fxls8962af }, { "fxls8964af", fxls8964af }, - {} + { } }; MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table); diff --git a/drivers/iio/accel/fxls8962af.h b/drivers/iio/accel/fxls8962af.h index 6eaa2803b26f..1c9adfc8c0dc 100644 --- a/drivers/iio/accel/fxls8962af.h +++ b/drivers/iio/accel/fxls8962af.h @@ -11,6 +11,8 @@ struct device; enum { fxls8962af, fxls8964af, + fxls8967af, + fxls8974cf, }; int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq); diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 078fab2abb68..2ff591b3458f 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -228,7 +228,7 @@ static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data, int len, int64_t timestamp) { dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); - iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp); + iio_push_to_buffers_with_ts(indio_dev, data, len, timestamp); } /* Callback handler to send event after all samples are received and captured */ @@ -440,7 +440,7 @@ static const struct platform_device_id hid_accel_3d_ids[] = { { /* gravity sensor */ .name = "HID-SENSOR-20007b", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids); diff --git a/drivers/iio/accel/kionix-kx022a-i2c.c b/drivers/iio/accel/kionix-kx022a-i2c.c index b39a43ecadff..42388636ca31 100644 --- a/drivers/iio/accel/kionix-kx022a-i2c.c +++ b/drivers/iio/accel/kionix-kx022a-i2c.c @@ -38,7 +38,9 @@ static int kx022a_i2c_probe(struct i2c_client *i2c) static const struct i2c_device_id kx022a_i2c_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, + { .name = "kx134-1211", .driver_data = (kernel_ulong_t)&kx134_chip_info }, { .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info }, + { .name = "kx134acr-lbz", .driver_data = (kernel_ulong_t)&kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); @@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(i2c, kx022a_i2c_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, + { .compatible = "kionix,kx134-1211", .data = &kx134_chip_info }, { .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info }, + { .compatible = "rohm,kx134acr-lbz", .data = &kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a-spi.c b/drivers/iio/accel/kionix-kx022a-spi.c index c38a47806a00..e30d21083dc8 100644 --- a/drivers/iio/accel/kionix-kx022a-spi.c +++ b/drivers/iio/accel/kionix-kx022a-spi.c @@ -38,7 +38,9 @@ static int kx022a_spi_probe(struct spi_device *spi) static const struct spi_device_id kx022a_id[] = { { .name = "kx022a", .driver_data = (kernel_ulong_t)&kx022a_chip_info }, { .name = "kx132-1211", .driver_data = (kernel_ulong_t)&kx132_chip_info }, + { .name = "kx134-1211", .driver_data = (kernel_ulong_t)&kx134_chip_info }, { .name = "kx132acr-lbz", .driver_data = (kernel_ulong_t)&kx132acr_chip_info }, + { .name = "kx134acr-lbz", .driver_data = (kernel_ulong_t)&kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, kx022a_id); @@ -46,7 +48,9 @@ MODULE_DEVICE_TABLE(spi, kx022a_id); static const struct of_device_id kx022a_of_match[] = { { .compatible = "kionix,kx022a", .data = &kx022a_chip_info }, { .compatible = "kionix,kx132-1211", .data = &kx132_chip_info }, + { .compatible = "kionix,kx134-1211", .data = &kx134_chip_info }, { .compatible = "rohm,kx132acr-lbz", .data = &kx132acr_chip_info }, + { .compatible = "rohm,kx134acr-lbz", .data = &kx134acr_chip_info }, { } }; MODULE_DEVICE_TABLE(of, kx022a_of_match); diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 670bac21965b..07dcf5f0599f 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -5,6 +5,7 @@ * ROHM/KIONIX accelerometer driver */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/interrupt.h> @@ -148,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 */ @@ -259,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 { @@ -407,11 +408,21 @@ static const int kx022a_scale_table[][2] = { { 0, 4788403 }, }; +/* KX134ACR-LBZ ranges are (+/-) 8, 16, 32, 64 G */ +static const int kx134acr_lbz_scale_table[][2] = { + { 0, 2394202 }, + { 0, 4788403 }, + { 0, 9576807 }, + { 0, 19153613 }, +}; + static int kx022a_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long mask) { + struct kx022a_data *data = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: *vals = (const int *)kx022a_accel_samp_freq_table; @@ -420,9 +431,8 @@ static int kx022a_read_avail(struct iio_dev *indio_dev, *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SCALE: - *vals = (const int *)kx022a_scale_table; - *length = ARRAY_SIZE(kx022a_scale_table) * - ARRAY_SIZE(kx022a_scale_table[0]); + *vals = (const int *)data->chip_info->scale_table; + *length = data->chip_info->scale_table_size; *type = IIO_VAL_INT_PLUS_NANO; return IIO_AVAIL_LIST; default: @@ -438,17 +448,17 @@ static void kx022a_reg2freq(unsigned int val, int *val1, int *val2) *val2 = kx022a_accel_samp_freq_table[val & KX022A_MASK_ODR][1]; } -static void kx022a_reg2scale(unsigned int val, unsigned int *val1, - unsigned int *val2) +static void kx022a_reg2scale(struct kx022a_data *data, unsigned int val, + unsigned int *val1, unsigned int *val2) { val &= KX022A_MASK_GSEL; val >>= KX022A_GSEL_SHIFT; - *val1 = kx022a_scale_table[val][0]; - *val2 = kx022a_scale_table[val][1]; + *val1 = data->chip_info->scale_table[val][0]; + *val2 = data->chip_info->scale_table[val][1]; } -static int kx022a_turn_on_off_unlocked(struct kx022a_data *data, bool on) +static int __kx022a_turn_on_off(struct kx022a_data *data, bool on) { int ret; @@ -469,7 +479,7 @@ static int kx022a_turn_off_lock(struct kx022a_data *data) int ret; mutex_lock(&data->mutex); - ret = kx022a_turn_on_off_unlocked(data, false); + ret = __kx022a_turn_on_off(data, false); if (ret) mutex_unlock(&data->mutex); @@ -480,7 +490,7 @@ static int kx022a_turn_on_unlock(struct kx022a_data *data) { int ret; - ret = kx022a_turn_on_off_unlocked(data, true); + ret = __kx022a_turn_on_off(data, true); mutex_unlock(&data->mutex); return ret; @@ -500,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); @@ -528,48 +525,64 @@ 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 = ARRAY_SIZE(kx022a_scale_table); + n = data->chip_info->scale_table_size / 2; while (n-- > 0) - if (val == kx022a_scale_table[n][0] && - val2 == kx022a_scale_table[n][1]) + 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; } +} + +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; -unlock_out: - iio_device_release_direct_mode(idev); + ret = __kx022a_write_raw(idev, chan, val, val2, mask); + + iio_device_release_direct(idev); return ret; } @@ -610,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; @@ -642,7 +654,7 @@ static int kx022a_read_raw(struct iio_dev *idev, if (ret < 0) return ret; - kx022a_reg2scale(regval, val, val2); + kx022a_reg2scale(data, regval, val, val2); return IIO_VAL_INT_PLUS_NANO; } @@ -912,18 +924,19 @@ static int kx022a_fifo_disable(struct kx022a_data *data) { int ret = 0; - ret = kx022a_turn_off_lock(data); + guard(mutex)(&data->mutex); + ret = __kx022a_turn_on_off(data, false); if (ret) return ret; ret = regmap_clear_bits(data->regmap, data->ien_reg, KX022A_MASK_WMI); if (ret) - goto unlock_out; + return ret; ret = regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) - goto unlock_out; + return ret; data->state &= ~KX022A_STATE_FIFO; @@ -931,12 +944,7 @@ static int kx022a_fifo_disable(struct kx022a_data *data) kfree(data->fifo_buffer); - return kx022a_turn_on_unlock(data); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return __kx022a_turn_on_off(data, true); } static int kx022a_buffer_predisable(struct iio_dev *idev) @@ -959,33 +967,29 @@ static int kx022a_fifo_enable(struct kx022a_data *data) if (!data->fifo_buffer) return -ENOMEM; - ret = kx022a_turn_off_lock(data); + guard(mutex)(&data->mutex); + ret = __kx022a_turn_on_off(data, false); if (ret) return ret; /* Update watermark to HW */ ret = kx022a_fifo_set_wmi(data); if (ret) - goto unlock_out; + return ret; /* Enable buffer */ ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2, KX022A_MASK_BUF_EN); if (ret) - goto unlock_out; + return ret; data->state |= KX022A_STATE_FIFO; ret = regmap_set_bits(data->regmap, data->ien_reg, KX022A_MASK_WMI); if (ret) - goto unlock_out; - - return kx022a_turn_on_unlock(data); - -unlock_out: - mutex_unlock(&data->mutex); + return ret; - return ret; + return __kx022a_turn_on_off(data, true); } static int kx022a_buffer_postenable(struct iio_dev *idev) @@ -1053,7 +1057,7 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) struct kx022a_data *data = iio_priv(idev); irqreturn_t ret = IRQ_NONE; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled) { iio_trigger_poll_nested(data->trig); @@ -1068,8 +1072,6 @@ static irqreturn_t kx022a_irq_thread_handler(int irq, void *private) ret = IRQ_HANDLED; } - mutex_unlock(&data->mutex); - return ret; } @@ -1079,32 +1081,26 @@ static int kx022a_trigger_set_state(struct iio_trigger *trig, struct kx022a_data *data = iio_trigger_get_drvdata(trig); int ret = 0; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled == state) - goto unlock_out; + return 0; if (data->state & KX022A_STATE_FIFO) { dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); - ret = -EBUSY; - goto unlock_out; + return -EBUSY; } - ret = kx022a_turn_on_off_unlocked(data, false); + ret = __kx022a_turn_on_off(data, false); if (ret) - goto unlock_out; + return ret; data->trigger_enabled = state; ret = kx022a_set_drdy_irq(data, state); if (ret) - goto unlock_out; - - ret = kx022a_turn_on_off_unlocked(data, true); - -unlock_out: - mutex_unlock(&data->mutex); + return ret; - return ret; + return __kx022a_turn_on_off(data, true); } static const struct iio_trigger_ops kx022a_trigger_ops = { @@ -1121,10 +1117,15 @@ static int kx022a_chip_init(struct kx022a_data *data) return ret; /* - * I've seen I2C read failures if we poll too fast after the sensor - * reset. Slight delay gives I2C block the time to recover. + * According to the power-on procedure documents, there is (at least) + * 2ms delay required after the software reset. This should be same for + * all, KX022ACR-Z, KX132-1211, KX132ACR-LBZ and KX134ACR-LBZ. + * + * https://fscdn.rohm.com/kionix/en/document/AN010_KX022ACR-Z_Power-on_Procedure_E.pdf + * https://fscdn.rohm.com/kionix/en/document/TN027-Power-On-Procedure.pdf + * https://fscdn.rohm.com/kionix/en/document/AN011_KX134ACR-LBZ_Power-on_Procedure_E.pdf */ - msleep(1); + msleep(2); ret = regmap_read_poll_timeout(data->regmap, data->chip_info->cntl2, val, !(val & KX022A_MASK_SRST), @@ -1158,6 +1159,9 @@ const struct kx022a_chip_info kx022a_chip_info = { .regmap_config = &kx022a_regmap_config, .channels = kx022a_channels, .num_channels = ARRAY_SIZE(kx022a_channels), + .scale_table = kx022a_scale_table, + .scale_table_size = ARRAY_SIZE(kx022a_scale_table) * + ARRAY_SIZE(kx022a_scale_table[0]), .fifo_length = KX022A_FIFO_LENGTH, .who = KX022A_REG_WHO, .id = KX022A_ID, @@ -1183,6 +1187,9 @@ const struct kx022a_chip_info kx132_chip_info = { .regmap_config = &kx132_regmap_config, .channels = kx132_channels, .num_channels = ARRAY_SIZE(kx132_channels), + .scale_table = kx022a_scale_table, + .scale_table_size = ARRAY_SIZE(kx022a_scale_table) * + ARRAY_SIZE(kx022a_scale_table[0]), .fifo_length = KX132_FIFO_LENGTH, .who = KX132_REG_WHO, .id = KX132_ID, @@ -1204,6 +1211,35 @@ const struct kx022a_chip_info kx132_chip_info = { }; EXPORT_SYMBOL_NS_GPL(kx132_chip_info, "IIO_KX022A"); +const struct kx022a_chip_info kx134_chip_info = { + .name = "kx134-1211", + .regmap_config = &kx132_regmap_config, + .channels = kx132_channels, + .num_channels = ARRAY_SIZE(kx132_channels), + .scale_table = kx134acr_lbz_scale_table, + .scale_table_size = ARRAY_SIZE(kx134acr_lbz_scale_table) * + ARRAY_SIZE(kx134acr_lbz_scale_table[0]), + .fifo_length = KX132_FIFO_LENGTH, + .who = KX132_REG_WHO, + .id = KX134_1211_ID, + .cntl = KX132_REG_CNTL, + .cntl2 = KX132_REG_CNTL2, + .odcntl = KX132_REG_ODCNTL, + .buf_cntl1 = KX132_REG_BUF_CNTL1, + .buf_cntl2 = KX132_REG_BUF_CNTL2, + .buf_clear = KX132_REG_BUF_CLEAR, + .buf_status1 = KX132_REG_BUF_STATUS_1, + .buf_smp_lvl_mask = KX132_MASK_BUF_SMP_LVL, + .buf_read = KX132_REG_BUF_READ, + .inc1 = KX132_REG_INC1, + .inc4 = KX132_REG_INC4, + .inc5 = KX132_REG_INC5, + .inc6 = KX132_REG_INC6, + .xout_l = KX132_REG_XOUT_L, + .get_fifo_bytes_available = kx132_get_fifo_bytes_available, +}; +EXPORT_SYMBOL_NS_GPL(kx134_chip_info, "IIO_KX022A"); + /* * Despite the naming, KX132ACR-LBZ is not similar to KX132-1211 but it is * exact subset of KX022A. KX132ACR-LBZ is meant to be used for industrial @@ -1216,6 +1252,9 @@ const struct kx022a_chip_info kx132acr_chip_info = { .regmap_config = &kx022a_regmap_config, .channels = kx022a_channels, .num_channels = ARRAY_SIZE(kx022a_channels), + .scale_table = kx022a_scale_table, + .scale_table_size = ARRAY_SIZE(kx022a_scale_table) * + ARRAY_SIZE(kx022a_scale_table[0]), .fifo_length = KX022A_FIFO_LENGTH, .who = KX022A_REG_WHO, .id = KX132ACR_LBZ_ID, @@ -1236,6 +1275,34 @@ const struct kx022a_chip_info kx132acr_chip_info = { }; EXPORT_SYMBOL_NS_GPL(kx132acr_chip_info, "IIO_KX022A"); +const struct kx022a_chip_info kx134acr_chip_info = { + .name = "kx134acr-lbz", + .regmap_config = &kx022a_regmap_config, + .channels = kx022a_channels, + .num_channels = ARRAY_SIZE(kx022a_channels), + .scale_table = kx134acr_lbz_scale_table, + .scale_table_size = ARRAY_SIZE(kx134acr_lbz_scale_table) * + ARRAY_SIZE(kx134acr_lbz_scale_table[0]), + .fifo_length = KX022A_FIFO_LENGTH, + .who = KX022A_REG_WHO, + .id = KX134ACR_LBZ_ID, + .cntl = KX022A_REG_CNTL, + .cntl2 = KX022A_REG_CNTL2, + .odcntl = KX022A_REG_ODCNTL, + .buf_cntl1 = KX022A_REG_BUF_CNTL1, + .buf_cntl2 = KX022A_REG_BUF_CNTL2, + .buf_clear = KX022A_REG_BUF_CLEAR, + .buf_status1 = KX022A_REG_BUF_STATUS_1, + .buf_read = KX022A_REG_BUF_READ, + .inc1 = KX022A_REG_INC1, + .inc4 = KX022A_REG_INC4, + .inc5 = KX022A_REG_INC5, + .inc6 = KX022A_REG_INC6, + .xout_l = KX022A_REG_XOUT_L, + .get_fifo_bytes_available = kx022a_get_fifo_bytes_available, +}; +EXPORT_SYMBOL_NS_GPL(kx134acr_chip_info, "IIO_KX022A"); + int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chip_info) { static const char * const regulator_names[] = {"io-vdd", "vdd"}; diff --git a/drivers/iio/accel/kionix-kx022a.h b/drivers/iio/accel/kionix-kx022a.h index 7060438ad88c..0ed54f584223 100644 --- a/drivers/iio/accel/kionix-kx022a.h +++ b/drivers/iio/accel/kionix-kx022a.h @@ -14,6 +14,7 @@ #define KX022A_REG_WHO 0x0f #define KX022A_ID 0xc8 #define KX132ACR_LBZ_ID 0xd8 +#define KX134ACR_LBZ_ID 0xcc #define KX022A_REG_CNTL2 0x19 #define KX022A_MASK_SRST BIT(7) @@ -77,6 +78,7 @@ #define KX132_REG_WHO 0x13 #define KX132_ID 0x3d +#define KX134_1211_ID 0x46 #define KX132_FIFO_LENGTH 86 @@ -135,6 +137,14 @@ struct kx022a_data; * * @name: name of the device * @regmap_config: pointer to register map configuration + * @scale_table: An array of tables of scaling factors for + * a supported acceleration measurement range. + * Each table containing a single scaling + * factor consisting of two integers. The first + * value in a table is the integer part, and + * the second value is the fractional part as + * parts per billion. + * @scale_table_size: Amount of values in tables. * @channels: pointer to iio_chan_spec array * @num_channels: number of iio_chan_spec channels * @fifo_length: number of 16-bit samples in a full buffer @@ -161,6 +171,8 @@ struct kx022a_data; struct kx022a_chip_info { const char *name; const struct regmap_config *regmap_config; + const int (*scale_table)[2]; + const int scale_table_size; const struct iio_chan_spec *channels; unsigned int num_channels; unsigned int fifo_length; @@ -187,6 +199,8 @@ int kx022a_probe_internal(struct device *dev, const struct kx022a_chip_info *chi extern const struct kx022a_chip_info kx022a_chip_info; extern const struct kx022a_chip_info kx132_chip_info; +extern const struct kx022a_chip_info kx134_chip_info; extern const struct kx022a_chip_info kx132acr_chip_info; +extern const struct kx022a_chip_info kx134acr_chip_info; #endif diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index f2496cad8ec2..910d7b5716e1 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -674,8 +674,8 @@ static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) return 0; } -static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, - bool status) +static int kxcjk1013_setup_interrupt(struct kxcjk1013_data *data, + bool status, bool is_new_data) { const struct kx_chipset_regs *regs = data->info->regs; int ret; @@ -690,69 +690,12 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = kxcjk1013_chip_update_thresholds(data); - if (ret < 0) - return ret; - - ret = i2c_smbus_read_byte_data(data->client, regs->int_ctrl1); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); - return ret; - } - - if (status) - ret |= KXCJK1013_REG_INT_CTRL1_BIT_IEN; - else - ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - - ret = i2c_smbus_write_byte_data(data->client, regs->int_ctrl1, ret); - if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); - return ret; - } - - ret = i2c_smbus_read_byte_data(data->client, regs->ctrl1); - if (ret < 0) { - dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); - return ret; - } - - if (status) - ret |= KXCJK1013_REG_CTRL1_BIT_WUFE; - else - ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE; - - ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); - if (ret < 0) { - dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); - return ret; - } - - if (store_mode == OPERATION) { - ret = kxcjk1013_set_mode(data, OPERATION); + if (is_new_data == true) { + ret = kxcjk1013_chip_update_thresholds(data); if (ret < 0) return ret; } - return 0; -} - -static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, - bool status) -{ - const struct kx_chipset_regs *regs = data->info->regs; - int ret; - enum kxcjk1013_mode store_mode; - - ret = kxcjk1013_get_mode(data, &store_mode); - if (ret < 0) - return ret; - - /* This is requirement by spec to change state to STANDBY */ - ret = kxcjk1013_set_mode(data, STANDBY); - if (ret < 0) - return ret; - ret = i2c_smbus_read_byte_data(data->client, regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); @@ -776,10 +719,17 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, return ret; } - if (status) - ret |= KXCJK1013_REG_CTRL1_BIT_DRDY; - else - ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; + if (is_new_data) { + if (status) + ret |= KXCJK1013_REG_CTRL1_BIT_DRDY; + else + ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; + } else { + if (status) + ret |= KXCJK1013_REG_CTRL1_BIT_WUFE; + else + ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE; + } ret = i2c_smbus_write_byte_data(data->client, regs->ctrl1, ret); if (ret < 0) { @@ -1112,7 +1062,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev, return ret; } - ret = kxcjk1013_setup_any_motion_interrupt(data, state); + ret = kxcjk1013_setup_interrupt(data, state, false); if (ret < 0) { kxcjk1013_set_power_state(data, false); data->ev_enable_state = 0; @@ -1253,8 +1203,8 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - data->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + data->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -1293,10 +1243,7 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig, mutex_unlock(&data->mutex); return ret; } - if (data->motion_trig == trig) - ret = kxcjk1013_setup_any_motion_interrupt(data, state); - else - ret = kxcjk1013_setup_new_data_interrupt(data, state); + ret = kxcjk1013_setup_interrupt(data, state, data->motion_trig != trig); if (ret < 0) { kxcjk1013_set_power_state(data, false); mutex_unlock(&data->mutex); diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index 3857d2edf250..1fa88b99149e 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -38,7 +38,7 @@ static void kxsd9_i2c_remove(struct i2c_client *client) static const struct of_device_id kxsd9_of_match[] = { { .compatible = "kionix,kxsd9", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, kxsd9_of_match); diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index a05f4467d94a..cbb6c6412665 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -38,7 +38,7 @@ static void kxsd9_spi_remove(struct spi_device *spi) static const struct spi_device_id kxsd9_spi_id[] = { {"kxsd9", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 0ededf8cfdca..cfc31265cdd0 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -229,9 +229,8 @@ static irqreturn_t kxsd9_trigger_handler(int irq, void *p) goto out; } - iio_push_to_buffers_with_timestamp(indio_dev, - &hw_values, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &hw_values, sizeof(hw_values), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -273,7 +272,7 @@ kxsd9_get_mount_matrix(const struct iio_dev *indio_dev, static const struct iio_chan_spec_ext_info kxsd9_ext_info[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxsd9_get_mount_matrix), - { }, + { } }; #define KXSD9_ACCEL_CHAN(axis, index) \ 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/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index 30746621052c..a2b5bdf14dde 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -103,8 +103,9 @@ static irqreturn_t mma7455_trigger_handler(int irq, void *p) if (ret) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, &mma7455->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &mma7455->scan, + sizeof(mma7455->scan), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index 2894aff80161..d0a16f227903 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -262,7 +262,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, static const struct i2c_device_id mma7660_i2c_id[] = { { "mma7660" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id); @@ -274,7 +274,7 @@ MODULE_DEVICE_TABLE(of, mma7660_of_match); static const struct acpi_device_id mma7660_acpi_id[] = { {"MMA7660", 0}, - {} + { } }; MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 962d289065ab..aba444a980d9 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; } @@ -1105,8 +1103,9 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p) if (ret < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->buffer, + sizeof(data->buffer), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 1b96687da01a..b89bad9e6fe6 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -578,14 +578,14 @@ static const struct dev_pm_ops mma9551_pm_ops = { static const struct acpi_device_id mma9551_acpi_match[] = { {"MMA9551", 0}, - {}, + { } }; MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match); static const struct i2c_device_id mma9551_id[] = { { "mma9551" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mma9551_id); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 00e224efc8ed..1bbe660b254f 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -919,7 +919,7 @@ static const struct iio_enum mma9553_calibgender_enum = { static const struct iio_chan_spec_ext_info mma9553_ext_info[] = { IIO_ENUM("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), IIO_ENUM_AVAILABLE("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), - {}, + { } }; #define MMA9553_PEDOMETER_CHANNEL(_type, _mask) { \ @@ -1216,14 +1216,14 @@ static const struct dev_pm_ops mma9553_pm_ops = { static const struct acpi_device_id mma9553_acpi_match[] = { {"MMA9553", 0}, - {}, + { } }; MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match); static const struct i2c_device_id mma9553_id[] = { { "mma9553" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mma9553_id); diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index e7fb860f3233..c31c53abc3d0 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)); @@ -917,8 +919,8 @@ static irqreturn_t msa311_buffer_thread(int irq, void *p) mutex_unlock(&msa311->lock); - iio_push_to_buffers_with_timestamp(indio_dev, &buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &buf, sizeof(buf), + iio_get_time_ns(indio_dev)); notify_done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index cb5c4e354fc0..1075c8ce0e37 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -335,8 +335,8 @@ static irqreturn_t mxc4005_trigger_handler(int irq, void *private) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -573,14 +573,14 @@ static const struct acpi_device_id mxc4005_acpi_match[] = { {"MXC4005", 0}, {"MXC6655", 0}, {"MDA6655", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match); static const struct of_device_id mxc4005_of_match[] = { { .compatible = "memsic,mxc4005", }, { .compatible = "memsic,mxc6655", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, mxc4005_of_match); diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 3fb0f386c3db..aabe4491efd7 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1541,7 +1541,7 @@ static const struct spi_device_id sca3000_id[] = { {"sca3000_e02", e02}, {"sca3000_e04", e04}, {"sca3000_e05", e05}, - {} + { } }; MODULE_DEVICE_TABLE(spi, sca3000_id); diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c index ca0ce83e42b2..67416a406e2f 100644 --- a/drivers/iio/accel/sca3300.c +++ b/drivers/iio/accel/sca3300.c @@ -58,15 +58,6 @@ enum sca3300_scan_indexes { SCA3300_SCAN_MAX }; -/* - * Buffer size max case: - * Three accel channels, two bytes per channel. - * Temperature channel, two bytes. - * Three incli channels, two bytes per channel. - * Timestamp channel, eight bytes. - */ -#define SCA3300_MAX_BUFFER_SIZE (ALIGN(sizeof(s16) * SCA3300_SCAN_MAX, sizeof(s64)) + sizeof(s64)) - #define SCA3300_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .address = reg, \ @@ -193,9 +184,6 @@ struct sca3300_chip_info { * @spi: SPI device structure * @lock: Data buffer lock * @chip: Sensor chip specific information - * @buffer: Triggered buffer: - * -SCA3300: 4 channel 16-bit data + 64-bit timestamp - * -SCL3300: 7 channel 16-bit data + 64-bit timestamp * @txbuf: Transmit buffer * @rxbuf: Receive buffer */ @@ -203,7 +191,6 @@ struct sca3300_data { struct spi_device *spi; struct mutex lock; const struct sca3300_chip_info *chip; - u8 buffer[SCA3300_MAX_BUFFER_SIZE] __aligned(sizeof(s64)); u8 txbuf[4] __aligned(IIO_DMA_MINALIGN); u8 rxbuf[4]; }; @@ -492,7 +479,7 @@ static irqreturn_t sca3300_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct sca3300_data *data = iio_priv(indio_dev); int bit, ret, val, i = 0; - s16 *channels = (s16 *)data->buffer; + IIO_DECLARE_BUFFER_WITH_TS(s16, channels, SCA3300_SCAN_MAX); iio_for_each_active_channel(indio_dev, bit) { ret = sca3300_read_reg(data, indio_dev->channels[bit].address, &val); @@ -505,8 +492,8 @@ static irqreturn_t sca3300_trigger_handler(int irq, void *p) channels[i++] = val; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, channels, sizeof(channels), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -674,14 +661,14 @@ static int sca3300_probe(struct spi_device *spi) static const struct of_device_id sca3300_dt_ids[] = { { .compatible = "murata,sca3300"}, { .compatible = "murata,scl3300"}, - {} + { } }; MODULE_DEVICE_TABLE(of, sca3300_dt_ids); static const struct spi_device_id sca3300_ids[] = { { "sca3300" }, { "scl3300" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, sca3300_ids); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 99cb661fabb2..a7961c610ed2 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1353,6 +1353,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) union acpi_object *ont; union acpi_object *elements; acpi_status status; + struct device *parent = indio_dev->dev.parent; int ret = -EINVAL; unsigned int val; int i, j; @@ -1371,7 +1372,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) }; - adev = ACPI_COMPANION(indio_dev->dev.parent); + adev = ACPI_COMPANION(parent); if (!adev) return -ENXIO; @@ -1380,8 +1381,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) if (status == AE_NOT_FOUND) { return -ENXIO; } else if (ACPI_FAILURE(status)) { - dev_warn(&indio_dev->dev, "failed to execute _ONT: %d\n", - status); + dev_warn(parent, "failed to execute _ONT: %d\n", status); return status; } @@ -1457,12 +1457,12 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) } ret = 0; - dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n"); + dev_info(parent, "computed mount matrix from ACPI\n"); out: kfree(buffer.pointer); if (ret) - dev_dbg(&indio_dev->dev, + dev_dbg(parent, "failed to apply ACPI orientation data: %d\n", ret); return ret; diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index ab4fdba75a0a..f24449500533 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -126,14 +126,14 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,iis328dq", .data = IIS328DQ_ACCEL_DEV_NAME, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_accel_of_match); static const struct acpi_device_id st_accel_acpi_match[] = { {"SMO8840", (kernel_ulong_t)LIS2DH12_ACCEL_DEV_NAME}, {"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match); @@ -164,7 +164,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LSM303C_ACCEL_DEV_NAME }, { SC7A20_ACCEL_DEV_NAME }, { IIS328DQ_ACCEL_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 6146754fe47f..d8ec0555f42a 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -108,7 +108,7 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,iis328dq", .data = IIS328DQ_ACCEL_DEV_NAME, }, - {} + { } }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -167,7 +167,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS302DL_ACCEL_DEV_NAME }, { LSM303C_ACCEL_DEV_NAME }, { IIS328DQ_ACCEL_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 471c154c3631..dfac2e44191f 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -460,8 +460,8 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p) } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -635,7 +635,7 @@ static const struct i2c_device_id stk8312_i2c_id[] = { /* Deprecated in favour of lowercase form */ { "STK8312" }, { "stk8312" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id); diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index cab592a68622..05d4fd540eb2 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -340,8 +340,8 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p) data->scan.chans[i++] = ret; } } - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: mutex_unlock(&data->lock); iio_trigger_notify_done(indio_dev->trig); @@ -526,13 +526,13 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, static const struct i2c_device_id stk8ba50_i2c_id[] = { { "stk8ba50" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, stk8ba50_i2c_id); static const struct acpi_device_id stk8ba50_acpi_id[] = { {"STK8BA50", 0}, - {} + { } }; MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 849c90203071..ea3ba1397392 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -6,6 +6,9 @@ menu "Analog to digital converters" +config IIO_ADC_HELPER + tristate + config AB8500_GPADC bool "ST-Ericsson AB8500 GPADC driver" depends on AB8500_CORE && REGULATOR_AB8500 @@ -25,14 +28,33 @@ config AD4000 tristate "Analog Devices AD4000 ADC Driver" depends on SPI select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD help Say yes here to build support for Analog Devices AD4000 high speed - SPI analog to digital converters (ADC). + SPI analog to digital converters (ADC). If intended to use with + SPI offloading support, it is recommended to enable + CONFIG_SPI_AXI_SPI_ENGINE, CONFIG_PWM_AXI_PWMGEN, and + CONFIG_SPI_OFFLOAD_TRIGGER_PWM. 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 +73,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 +85,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 @@ -99,8 +137,9 @@ config AD7173 tristate "Analog Devices AD7173 driver" depends on SPI_MASTER select AD_SIGMA_DELTA - select GPIO_REGMAP if GPIOLIB - select REGMAP_SPI if GPIOLIB + select GPIOLIB + select GPIO_REGMAP + select REGMAP_SPI help Say yes here to build support for Analog Devices AD7173 and similar ADC Currently supported models: @@ -112,6 +151,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 +237,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 @@ -239,6 +290,8 @@ config AD7606_IFACE_SPI tristate "Analog Devices AD7606 ADC driver with spi interface support" depends on SPI select AD7606 + select IIO_BUFFER_DMAENGINE + select SPI_OFFLOAD help Say yes here to build spi interface support for Analog Devices: ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). @@ -277,6 +330,7 @@ config AD7766 config AD7768_1 tristate "Analog Devices AD7768-1 ADC driver" depends on SPI + select REGMAP_SPI select IIO_BUFFER select IIO_TRIGGER select IIO_TRIGGERED_BUFFER @@ -360,7 +414,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 @@ -1048,6 +1104,17 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config NCT7201 + tristate "Nuvoton Instruments NCT7201 and NCT7202 Power Monitor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7201 and + NCT7202 Voltage Monitor. + + This driver can also be built as a module. If so, the module + will be called nct7201. + config NPCM_ADC tristate "Nuvoton NPCM ADC driver" depends on ARCH_NPCM || COMPILE_TEST @@ -1188,6 +1255,18 @@ config RN5T618_ADC This driver can also be built as a module. If so, the module will be called rn5t618-adc. +config ROHM_BD79124 + tristate "Rohm BD79124 ADC driver" + depends on I2C + select REGMAP_I2C + select IIO_ADC_HELPER + help + Say yes here to build support for the ROHM BD79124 ADC. The + ROHM BD79124 is a 12-bit, 8-channel, SAR ADC. The ADC supports + also an automatic measurement mode, with an alarm interrupt for + out-of-window measurements. The window is configurable for each + channel. + config ROCKCHIP_SARADC tristate "Rockchip SARADC driver" depends on ARCH_ROCKCHIP || COMPILE_TEST @@ -1219,6 +1298,7 @@ config RICHTEK_RTQ6056 config RZG2L_ADC tristate "Renesas RZ/G2L ADC driver" depends on ARCH_RZG2L || COMPILE_TEST + select IIO_ADC_HELPER help Say yes here to build support for the ADC found in Renesas RZ/G2L family. @@ -1353,6 +1433,7 @@ config SUN4I_GPADC config SUN20I_GPADC tristate "Allwinner D1/T113s/T507/R329 and similar GPADCs driver" depends on ARCH_SUNXI || COMPILE_TEST + select IIO_ADC_HELPER help Say yes here to build support for Allwinner (D1, T113, T507 and R329) SoCs GPADC. This ADC provides up to 16 channels. @@ -1396,18 +1477,6 @@ config TI_ADC084S021 This driver can also be built as a module. If so, the module will be called ti-adc084s021. -config TI_ADC12138 - tristate "Texas Instruments ADC12130/ADC12132/ADC12138" - depends on SPI - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - If you say yes here you get support for Texas Instruments ADC12130, - ADC12132 and ADC12138 chips. - - This driver can also be built as a module. If so, the module will be - called ti-adc12138. - config TI_ADC108S102 tristate "Texas Instruments ADC108S102 and ADC128S102 driver" depends on SPI @@ -1420,12 +1489,24 @@ config TI_ADC108S102 To compile this driver as a module, choose M here: the module will be called ti-adc108s102. +config TI_ADC12138 + tristate "Texas Instruments ADC12130/ADC12132/ADC12138" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC12130, + ADC12132 and ADC12138 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc12138. + config TI_ADC128S052 tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021" depends on SPI help If you say yes here you get support for Texas Instruments ADC128S052, - ADC122S021 and ADC124S021 chips. + ADC122S021, ADC124S021 and ROHM Semiconductor BD79104 chips. This driver can also be built as a module. If so, the module will be called ti-adc128s052. @@ -1455,6 +1536,16 @@ config TI_ADS1015 This driver can also be built as a module. If so, the module will be called ti-ads1015. +config TI_ADS1100 + tristate "Texas Instruments ADS1100 and ADS1000 ADC" + depends on I2C + help + If you say yes here you get support for Texas Instruments ADS1100 and + ADS1000 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads1100. + config TI_ADS1119 tristate "Texas Instruments ADS1119 ADC" depends on I2C @@ -1467,31 +1558,23 @@ config TI_ADS1119 This driver can also be built as a module. If so, the module will be called ti-ads1119. -config TI_ADS7924 - tristate "Texas Instruments ADS7924 ADC" - depends on I2C - select REGMAP_I2C - help - If you say yes here you get support for Texas Instruments ADS7924 - 4 channels, 12-bit I2C ADC chip. - - This driver can also be built as a module. If so, the module will be - called ti-ads7924. - -config TI_ADS1100 - tristate "Texas Instruments ADS1100 and ADS1000 ADC" - depends on I2C +config TI_ADS124S08 + tristate "Texas Instruments ADS124S08" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help - If you say yes here you get support for Texas Instruments ADS1100 and - ADS1000 ADC chips. + If you say yes here you get support for Texas Instruments ADS124S08 + and ADS124S06 ADC chips This driver can also be built as a module. If so, the module will be - called ti-ads1100. + called ti-ads124s08. config TI_ADS1298 tristate "Texas Instruments ADS1298" depends on SPI select IIO_BUFFER + select IIO_KFIFO_BUF help If you say yes here you get support for Texas Instruments ADS1298 medical ADC chips @@ -1499,6 +1582,39 @@ config TI_ADS1298 This driver can also be built as a module. If so, the module will be called ti-ads1298. +config TI_ADS131E08 + tristate "Texas Instruments ADS131E08" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to get support for Texas Instruments ADS131E04, ADS131E06 + and ADS131E08 chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads131e08. + +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 + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments ADS7924 + 4 channels, 12-bit I2C ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads7924. + config TI_ADS7950 tristate "Texas Instruments ADS7950 ADC driver" depends on SPI && GPIOLIB @@ -1534,30 +1650,6 @@ config TI_ADS8688 This driver can also be built as a module. If so, the module will be called ti-ads8688. -config TI_ADS124S08 - tristate "Texas Instruments ADS124S08" - depends on SPI - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - If you say yes here you get support for Texas Instruments ADS124S08 - and ADS124S06 ADC chips - - This driver can also be built as a module. If so, the module will be - called ti-ads124s08. - -config TI_ADS131E08 - tristate "Texas Instruments ADS131E08" - depends on SPI - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to get support for Texas Instruments ADS131E04, ADS131E06 - and ADS131E08 chips. - - This driver can also be built as a module. If so, the module will be - called ti-ads131e08. - config TI_AM335X_ADC tristate "TI's AM335X ADC driver" depends on MFD_TI_AM335X_TSCADC && HAS_DMA diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ee19afba62b7..09ae6edb2650 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -3,17 +3,22 @@ # Makefile for IIO ADC drivers # +obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o + # When adding new entries keep the list in alphabetical order 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 @@ -94,6 +99,7 @@ obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MP2629_ADC) += mp2629_adc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_NCT7201) += nct7201.o obj-$(CONFIG_NPCM_ADC) += npcm_adc.o obj-$(CONFIG_PAC1921) += pac1921.o obj-$(CONFIG_PAC1934) += pac1934.o @@ -107,6 +113,7 @@ obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o +obj-$(CONFIG_ROHM_BD79124) += rohm-bd79124.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o @@ -133,6 +140,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 b3b82535f5c1..5609a7845b6f 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -15,12 +15,14 @@ #include <linux/mod_devicetable.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> +#include <linux/spi/offload/consumer.h> #include <linux/spi/spi.h> #include <linux/units.h> #include <linux/util_macros.h> -#include <linux/iio/iio.h> +#include <linux/iio/iio.h> #include <linux/iio/buffer.h> +#include <linux/iio/buffer-dmaengine.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> @@ -32,14 +34,11 @@ /* AD4000 Configuration Register programmable bits */ #define AD4000_CFG_SPAN_COMP BIT(3) /* Input span compression */ #define AD4000_CFG_HIGHZ BIT(2) /* High impedance mode */ +#define AD4000_CFG_TURBO BIT(1) /* Turbo mode */ #define AD4000_SCALE_OPTIONS 2 -#define AD4000_TQUIET1_NS 190 -#define AD4000_TQUIET2_NS 60 -#define AD4000_TCONV_NS 320 - -#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access) \ +#define __AD4000_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access, _offl)\ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -47,42 +46,67 @@ .channel = 0, \ .channel2 = 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \ .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_index = 0, \ .scan_type = { \ .sign = _sign, \ .realbits = _real_bits, \ .storagebits = _storage_bits, \ - .shift = _storage_bits - _real_bits, \ - .endianness = IIO_BE, \ + .shift = (_offl ? 0 : _storage_bits - _real_bits), \ + .endianness = _offl ? IIO_CPU : IIO_BE \ }, \ } -#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \ +#define AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access, _offl) \ __AD4000_DIFF_CHANNEL((_sign), (_real_bits), \ - ((_real_bits) > 16 ? 32 : 16), (_reg_access)) + (((_offl) || ((_real_bits) > 16)) ? 32 : 16), \ + (_reg_access), (_offl)) -#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _reg_access)\ +/* + * When SPI offload is configured, transfers are executed without CPU + * intervention so no soft timestamp can be recorded when transfers run. + * Because of that, the macros that set timestamp channel are only used when + * transfers are not offloaded. + */ +#define AD4000_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \ +{ \ + AD4000_DIFF_CHANNEL(_sign, _real_bits, _reg_access, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +#define __AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, \ + _reg_access, _offl) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .channel = 0, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_OFFSET), \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \ .info_mask_separate_available = _reg_access ? BIT(IIO_CHAN_INFO_SCALE) : 0,\ + .scan_index = 0, \ .scan_type = { \ .sign = _sign, \ .realbits = _real_bits, \ .storagebits = _storage_bits, \ - .shift = _storage_bits - _real_bits, \ - .endianness = IIO_BE, \ + .shift = (_offl ? 0 : _storage_bits - _real_bits), \ + .endianness = _offl ? IIO_CPU : IIO_BE \ }, \ } -#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access) \ +#define AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access, _offl) \ __AD4000_PSEUDO_DIFF_CHANNEL((_sign), (_real_bits), \ - ((_real_bits) > 16 ? 32 : 16), (_reg_access)) + (((_offl) || ((_real_bits) > 16)) ? 32 : 16),\ + (_reg_access), (_offl)) + +#define AD4000_PSEUDO_DIFF_CHANNELS(_sign, _real_bits, _reg_access) \ +{ \ + AD4000_PSEUDO_DIFF_CHANNEL(_sign, _real_bits, _reg_access, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} static const char * const ad4000_power_supplies[] = { "vdd", "vio" @@ -108,109 +132,364 @@ static const int ad4000_gains[] = { 454, 909, 1000, 1900, }; +struct ad4000_time_spec { + int t_conv_ns; + int t_quiet2_ns; +}; + +/* + * Same timing specifications for all of AD4000, AD4001, ..., AD4008, AD4010, + * ADAQ4001, and ADAQ4003. + */ +static const struct ad4000_time_spec ad4000_t_spec = { + .t_conv_ns = 320, + .t_quiet2_ns = 60, +}; + +/* AD4020, AD4021, AD4022 */ +static const struct ad4000_time_spec ad4020_t_spec = { + .t_conv_ns = 350, + .t_quiet2_ns = 60, +}; + +/* AD7983, AD7984 */ +static const struct ad4000_time_spec ad7983_t_spec = { + .t_conv_ns = 500, + .t_quiet2_ns = 0, +}; + +/* AD7980, AD7982 */ +static const struct ad4000_time_spec ad7980_t_spec = { + .t_conv_ns = 800, + .t_quiet2_ns = 0, +}; + +/* AD7946, AD7686, AD7688, AD7988-5, AD7693 */ +static const struct ad4000_time_spec ad7686_t_spec = { + .t_conv_ns = 1600, + .t_quiet2_ns = 0, +}; + +/* AD7690 */ +static const struct ad4000_time_spec ad7690_t_spec = { + .t_conv_ns = 2100, + .t_quiet2_ns = 0, +}; + +/* AD7942, AD7685, AD7687 */ +static const struct ad4000_time_spec ad7687_t_spec = { + .t_conv_ns = 3200, + .t_quiet2_ns = 0, +}; + +/* AD7691 */ +static const struct ad4000_time_spec ad7691_t_spec = { + .t_conv_ns = 3700, + .t_quiet2_ns = 0, +}; + +/* AD7988-1 */ +static const struct ad4000_time_spec ad7988_1_t_spec = { + .t_conv_ns = 9500, + .t_quiet2_ns = 0, +}; + struct ad4000_chip_info { const char *dev_name; - struct iio_chan_spec chan_spec; - struct iio_chan_spec reg_access_chan_spec; + struct iio_chan_spec chan_spec[2]; + struct iio_chan_spec reg_access_chan_spec[2]; + struct iio_chan_spec offload_chan_spec; + struct iio_chan_spec reg_access_offload_chan_spec; + const struct ad4000_time_spec *time_spec; bool has_hardware_gain; + int max_rate_hz; }; static const struct ad4000_chip_info ad4000_chip_info = { .dev_name = "ad4000", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, }; static const struct ad4000_chip_info ad4001_chip_info = { .dev_name = "ad4001", - .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, }; static const struct ad4000_chip_info ad4002_chip_info = { .dev_name = "ad4002", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, }; static const struct ad4000_chip_info ad4003_chip_info = { .dev_name = "ad4003", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 2 * MEGA, }; static const struct ad4000_chip_info ad4004_chip_info = { .dev_name = "ad4004", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, }; static const struct ad4000_chip_info ad4005_chip_info = { .dev_name = "ad4005", - .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, }; static const struct ad4000_chip_info ad4006_chip_info = { .dev_name = "ad4006", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, }; static const struct ad4000_chip_info ad4007_chip_info = { .dev_name = "ad4007", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 1 * MEGA, }; static const struct ad4000_chip_info ad4008_chip_info = { .dev_name = "ad4008", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 500 * KILO, }; static const struct ad4000_chip_info ad4010_chip_info = { .dev_name = "ad4010", - .chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0), - .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1), + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 0), + .reg_access_chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 18, 1), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 500 * KILO, }; static const struct ad4000_chip_info ad4011_chip_info = { .dev_name = "ad4011", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, + .max_rate_hz = 500 * KILO, }; static const struct ad4000_chip_info ad4020_chip_info = { .dev_name = "ad4020", - .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1), + .time_spec = &ad4020_t_spec, + .max_rate_hz = 1800 * KILO, }; static const struct ad4000_chip_info ad4021_chip_info = { .dev_name = "ad4021", - .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1), + .time_spec = &ad4020_t_spec, + .max_rate_hz = 1 * MEGA, }; static const struct ad4000_chip_info ad4022_chip_info = { .dev_name = "ad4022", - .chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 20, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 20, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 20, 1, 1), + .time_spec = &ad4020_t_spec, + .max_rate_hz = 500 * KILO, }; static const struct ad4000_chip_info adaq4001_chip_info = { .dev_name = "adaq4001", - .chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 16, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 1, 1), + .time_spec = &ad4000_t_spec, .has_hardware_gain = true, + .max_rate_hz = 2 * MEGA, }; static const struct ad4000_chip_info adaq4003_chip_info = { .dev_name = "adaq4003", - .chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0), - .reg_access_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1), + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .reg_access_chan_spec = AD4000_DIFF_CHANNELS('s', 18, 1), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .reg_access_offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 1, 1), + .time_spec = &ad4000_t_spec, .has_hardware_gain = true, + .max_rate_hz = 2 * MEGA, +}; + +static const struct ad4000_chip_info ad7685_chip_info = { + .dev_name = "ad7685", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7687_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7686_chip_info = { + .dev_name = "ad7686", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7687_chip_info = { + .dev_name = "ad7687", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .time_spec = &ad7687_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7688_chip_info = { + .dev_name = "ad7688", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7690_chip_info = { + .dev_name = "ad7690", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7690_t_spec, + .max_rate_hz = 400 * KILO, +}; + +static const struct ad4000_chip_info ad7691_chip_info = { + .dev_name = "ad7691", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7691_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7693_chip_info = { + .dev_name = "ad7693", + .chan_spec = AD4000_DIFF_CHANNELS('s', 16, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7942_chip_info = { + .dev_name = "ad7942", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 14, 0, 1), + .time_spec = &ad7687_t_spec, + .max_rate_hz = 250 * KILO, +}; + +static const struct ad4000_chip_info ad7946_chip_info = { + .dev_name = "ad7946", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 14, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 14, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct ad4000_chip_info ad7980_chip_info = { + .dev_name = "ad7980", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7980_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad7982_chip_info = { + .dev_name = "ad7982", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7980_t_spec, + .max_rate_hz = 1 * MEGA, +}; + +static const struct ad4000_chip_info ad7983_chip_info = { + .dev_name = "ad7983", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7983_t_spec, + .max_rate_hz = 1 * MEGA + 333 * KILO + 333, +}; + +static const struct ad4000_chip_info ad7984_chip_info = { + .dev_name = "ad7984", + .chan_spec = AD4000_DIFF_CHANNELS('s', 18, 0), + .offload_chan_spec = AD4000_DIFF_CHANNEL('s', 18, 0, 1), + .time_spec = &ad7983_t_spec, + .max_rate_hz = 1 * MEGA + 333 * KILO + 333, +}; + +static const struct ad4000_chip_info ad7988_1_chip_info = { + .dev_name = "ad7988-1", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7988_1_t_spec, + .max_rate_hz = 100 * KILO, +}; + +static const struct ad4000_chip_info ad7988_5_chip_info = { + .dev_name = "ad7988-5", + .chan_spec = AD4000_PSEUDO_DIFF_CHANNELS('u', 16, 0), + .offload_chan_spec = AD4000_PSEUDO_DIFF_CHANNEL('u', 16, 0, 1), + .time_spec = &ad7686_t_spec, + .max_rate_hz = 500 * KILO, +}; + +static const struct spi_offload_config ad4000_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, }; struct ad4000_state { @@ -218,12 +497,20 @@ struct ad4000_state { struct gpio_desc *cnv_gpio; struct spi_transfer xfers[2]; struct spi_message msg; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + bool using_offload; + unsigned long offload_trigger_hz; + int max_rate_hz; struct mutex lock; /* Protect read modify write cycle */ int vref_mv; enum ad4000_sdi sdi_pin; bool span_comp; u16 gain_milli; int scale_tbl[AD4000_SCALE_OPTIONS][2]; + const struct ad4000_time_spec *time_spec; /* * DMA (thus cache coherency maintenance) requires the transfer buffers @@ -231,10 +518,12 @@ struct ad4000_state { */ struct { union { - __be16 sample_buf16; - __be32 sample_buf32; + __be16 sample_buf16_be; + __be32 sample_buf32_be; + u16 sample_buf16; + u32 sample_buf32; } data; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan __aligned(IIO_DMA_MINALIGN); u8 tx_buf[2]; u8 rx_buf[2]; @@ -307,6 +596,25 @@ static int ad4000_read_reg(struct ad4000_state *st, unsigned int *val) return ret; } +static int ad4000_set_sampling_freq(struct ad4000_state *st, int freq) +{ + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = freq, + }, + }; + 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 ad4000_convert_and_acquire(struct ad4000_state *st) { int ret; @@ -335,10 +643,17 @@ static int ad4000_single_conversion(struct iio_dev *indio_dev, if (ret < 0) return ret; - if (chan->scan_type.storagebits > 16) - sample = be32_to_cpu(st->scan.data.sample_buf32); - else - sample = be16_to_cpu(st->scan.data.sample_buf16); + if (chan->scan_type.endianness == IIO_BE) { + if (chan->scan_type.realbits > 16) + sample = be32_to_cpu(st->scan.data.sample_buf32_be); + else + sample = be16_to_cpu(st->scan.data.sample_buf16_be); + } else { + if (chan->scan_type.realbits > 16) + sample = st->scan.data.sample_buf32; + else + sample = st->scan.data.sample_buf16; + } sample >>= chan->scan_type.shift; @@ -355,12 +670,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]; @@ -371,6 +690,9 @@ static int ad4000_read_raw(struct iio_dev *indio_dev, *val = mult_frac(st->vref_mv, 1, 10); return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->offload_trigger_hz; + return IIO_VAL_INT; default: return -EINVAL; } @@ -405,36 +727,56 @@ 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; + 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); + 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_write_reg(st, reg_val); - if (ret < 0) - return ret; + ret = ad4000_write_reg(st, reg_val); + if (ret < 0) + return ret; - st->span_comp = span_comp_en; - return 0; - } - unreachable(); + st->span_comp = span_comp_en; + return 0; +} + +static int ad4000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4000_state *st = iio_priv(indio_dev); + int ret; + + 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; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < 1 || val > st->max_rate_hz) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad4000_set_sampling_freq(st, val); + iio_device_release_direct(indio_dev); + return ret; default: return -EINVAL; } @@ -451,7 +793,8 @@ static irqreturn_t ad4000_trigger_handler(int irq, void *p) if (ret < 0) goto err_out; - iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + pf->timestamp); err_out: iio_trigger_notify_done(indio_dev->trig); @@ -465,10 +808,114 @@ static const struct iio_info ad4000_reg_access_info = { .write_raw_get_fmt = &ad4000_write_raw_get_fmt, }; +static const struct iio_info ad4000_offload_info = { + .read_raw = &ad4000_read_raw, + .write_raw = &ad4000_write_raw, + .write_raw_get_fmt = &ad4000_write_raw_get_fmt, +}; + static const struct iio_info ad4000_info = { .read_raw = &ad4000_read_raw, }; +static int ad4000_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4000_state *st = iio_priv(indio_dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic = { + .frequency_hz = st->offload_trigger_hz, + }, + }; + + return spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); +} + +static int ad4000_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4000_state *st = iio_priv(indio_dev); + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + + return 0; +} + +static const struct iio_buffer_setup_ops ad4000_offload_buffer_setup_ops = { + .postenable = &ad4000_offload_buffer_postenable, + .predisable = &ad4000_offload_buffer_predisable, +}; + +static int ad4000_spi_offload_setup(struct iio_dev *indio_dev, + struct ad4000_state *st) +{ + struct spi_device *spi = st->spi; + struct device *dev = &spi->dev; + struct dma_chan *rx_dma; + int ret; + + 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"); + + ret = ad4000_set_sampling_freq(st, st->max_rate_hz); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set sampling frequency\n"); + + 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, "Failed to setup DMA buffer\n"); + + return 0; +} + +/* + * This executes a data sample transfer when using SPI offloading. The device + * connections should be in "3-wire" mode, selected either when the adi,sdi-pin + * device tree property is absent or set to "high". Also, the ADC CNV pin must + * be connected to a SPI controller CS (it can't be connected to a GPIO). + * + * In order to achieve the maximum sample rate, we only do one transfer per + * SPI offload trigger. Because the ADC output has a one sample latency (delay) + * when the device is wired in "3-wire" mode and only one transfer per sample is + * being made in turbo mode, the first data sample is not valid because it + * contains the output of an earlier conversion result. We also set transfer + * `bits_per_word` to achieve higher throughput by using the minimum number of + * SCLK cycles. Also, a delay is added to make sure we meet the minimum quiet + * time before releasing the CS line. + * + * Note that, with `bits_per_word` set to the number of ADC precision bits, + * transfers use larger word sizes that get stored in 'in-memory wordsizes' that + * are always in native CPU byte order. Because of that, IIO buffer elements + * ought to be read in CPU endianness which requires setting IIO scan_type + * endianness accordingly (i.e. IIO_CPU). + */ +static int ad4000_prepare_offload_message(struct ad4000_state *st, + const struct iio_chan_spec *chan) +{ + struct spi_transfer *xfer = &st->offload_xfer; + + xfer->bits_per_word = chan->scan_type.realbits; + xfer->len = chan->scan_type.realbits > 16 ? 4 : 2; + xfer->delay.value = st->time_spec->t_quiet2_ns; + xfer->delay.unit = SPI_DELAY_UNIT_NSECS; + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + + spi_message_init_with_transfers(&st->offload_msg, xfer, 1); + st->offload_msg.offload = st->offload; + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->offload_msg); +} + /* * This executes a data sample transfer for when the device connections are * in "3-wire" mode, selected when the adi,sdi-pin device tree property is @@ -488,16 +935,24 @@ static const struct iio_info ad4000_info = { static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st, const struct iio_chan_spec *chan) { - unsigned int cnv_pulse_time = AD4000_TCONV_NS; struct spi_transfer *xfers = st->xfers; xfers[0].cs_change = 1; - xfers[0].cs_change_delay.value = cnv_pulse_time; + xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns; xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; xfers[1].rx_buf = &st->scan.data; - xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); - xfers[1].delay.value = AD4000_TQUIET2_NS; + xfers[1].len = chan->scan_type.realbits > 16 ? 4 : 2; + + /* + * If the device is set up for SPI offloading, IIO channel scan_type is + * set to IIO_CPU. When that is the case, use larger SPI word sizes for + * single-shot reads too. Thus, sample data can be correctly handled in + * ad4000_single_conversion() according to scan_type endianness. + */ + if (chan->scan_type.endianness != IIO_BE) + xfers[1].bits_per_word = chan->scan_type.realbits; + xfers[1].delay.value = st->time_spec->t_quiet2_ns; xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; spi_message_init_with_transfers(&st->msg, st->xfers, 2); @@ -515,7 +970,6 @@ static int ad4000_prepare_3wire_mode_message(struct ad4000_state *st, static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st, const struct iio_chan_spec *chan) { - unsigned int cnv_to_sdi_time = AD4000_TCONV_NS; struct spi_transfer *xfers = st->xfers; /* @@ -523,7 +977,7 @@ static int ad4000_prepare_4wire_mode_message(struct ad4000_state *st, * going low. */ xfers[0].cs_off = 1; - xfers[0].delay.value = cnv_to_sdi_time; + xfers[0].delay.value = st->time_spec->t_conv_ns; xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; xfers[1].rx_buf = &st->scan.data; @@ -541,6 +995,9 @@ static int ad4000_config(struct ad4000_state *st) if (device_property_present(&st->spi->dev, "adi,high-z-input")) reg_val |= FIELD_PREP(AD4000_CFG_HIGHZ, 1); + if (st->using_offload) + reg_val |= FIELD_PREP(AD4000_CFG_TURBO, 1); + return ad4000_write_reg(st, reg_val); } @@ -562,6 +1019,8 @@ static int ad4000_probe(struct spi_device *spi) st = iio_priv(indio_dev); st->spi = spi; + st->time_spec = chip->time_spec; + st->max_rate_hz = chip->max_rate_hz; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4000_power_supplies), ad4000_power_supplies); @@ -579,6 +1038,26 @@ static int ad4000_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(st->cnv_gpio), "Failed to get CNV GPIO"); + st->offload = devm_spi_offload_get(dev, spi, &ad4000_offload_config); + ret = PTR_ERR_OR_ZERO(st->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get offload\n"); + + st->using_offload = !IS_ERR(st->offload); + if (st->using_offload) { + indio_dev->setup_ops = &ad4000_offload_buffer_setup_ops; + ret = ad4000_spi_offload_setup(indio_dev, st); + if (ret) + return ret; + } else { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad4000_trigger_handler, + NULL); + if (ret) + return ret; + } + ret = device_property_match_property_string(dev, "adi,sdi-pin", ad4000_sdi_pin, ARRAY_SIZE(ad4000_sdi_pin)); @@ -591,7 +1070,6 @@ static int ad4000_probe(struct spi_device *spi) switch (st->sdi_pin) { case AD4000_SDI_MOSI: indio_dev->info = &ad4000_reg_access_info; - indio_dev->channels = &chip->reg_access_chan_spec; /* * In "3-wire mode", the ADC SDI line must be kept high when @@ -603,9 +1081,26 @@ static int ad4000_probe(struct spi_device *spi) if (ret < 0) return ret; - ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels); + if (st->using_offload) { + indio_dev->channels = &chip->reg_access_offload_chan_spec; + indio_dev->num_channels = 1; + ret = ad4000_prepare_offload_message(st, indio_dev->channels); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + } else { + indio_dev->channels = chip->reg_access_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(chip->reg_access_chan_spec); + } + + /* + * Call ad4000_prepare_3wire_mode_message() so single-shot read + * SPI messages are always initialized. + */ + ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); ret = ad4000_config(st); if (ret < 0) @@ -613,19 +1108,38 @@ static int ad4000_probe(struct spi_device *spi) break; case AD4000_SDI_VIO: - indio_dev->info = &ad4000_info; - indio_dev->channels = &chip->chan_spec; - ret = ad4000_prepare_3wire_mode_message(st, indio_dev->channels); + if (st->using_offload) { + indio_dev->info = &ad4000_offload_info; + indio_dev->channels = &chip->offload_chan_spec; + indio_dev->num_channels = 1; + + ret = ad4000_prepare_offload_message(st, indio_dev->channels); + if (ret) + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); + } else { + indio_dev->info = &ad4000_info; + indio_dev->channels = chip->chan_spec; + indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec); + } + + ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); break; case AD4000_SDI_CS: + if (st->using_offload) + return dev_err_probe(dev, -EPROTONOSUPPORT, + "Unsupported sdi-pin + offload config\n"); indio_dev->info = &ad4000_info; - indio_dev->channels = &chip->chan_spec; - ret = ad4000_prepare_4wire_mode_message(st, indio_dev->channels); + indio_dev->channels = chip->chan_spec; + indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec); + ret = ad4000_prepare_4wire_mode_message(st, &indio_dev->channels[0]); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to optimize SPI msg\n"); break; case AD4000_SDI_GND: @@ -637,7 +1151,6 @@ static int ad4000_probe(struct spi_device *spi) } indio_dev->name = chip->dev_name; - indio_dev->num_channels = 1; ret = devm_mutex_init(dev, &st->lock); if (ret) @@ -658,13 +1171,7 @@ static int ad4000_probe(struct spi_device *spi) } } - ad4000_fill_scale_tbl(st, indio_dev->channels); - - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - &iio_pollfunc_store_time, - &ad4000_trigger_handler, NULL); - if (ret) - return ret; + ad4000_fill_scale_tbl(st, &indio_dev->channels[0]); return devm_iio_device_register(dev, indio_dev); } @@ -686,6 +1193,21 @@ static const struct spi_device_id ad4000_id[] = { { "ad4022", (kernel_ulong_t)&ad4022_chip_info }, { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info }, { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info }, + { "ad7685", (kernel_ulong_t)&ad7685_chip_info }, + { "ad7686", (kernel_ulong_t)&ad7686_chip_info }, + { "ad7687", (kernel_ulong_t)&ad7687_chip_info }, + { "ad7688", (kernel_ulong_t)&ad7688_chip_info }, + { "ad7690", (kernel_ulong_t)&ad7690_chip_info }, + { "ad7691", (kernel_ulong_t)&ad7691_chip_info }, + { "ad7693", (kernel_ulong_t)&ad7693_chip_info }, + { "ad7942", (kernel_ulong_t)&ad7942_chip_info }, + { "ad7946", (kernel_ulong_t)&ad7946_chip_info }, + { "ad7980", (kernel_ulong_t)&ad7980_chip_info }, + { "ad7982", (kernel_ulong_t)&ad7982_chip_info }, + { "ad7983", (kernel_ulong_t)&ad7983_chip_info }, + { "ad7984", (kernel_ulong_t)&ad7984_chip_info }, + { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info }, + { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4000_id); @@ -707,6 +1229,21 @@ static const struct of_device_id ad4000_of_match[] = { { .compatible = "adi,ad4022", .data = &ad4022_chip_info }, { .compatible = "adi,adaq4001", .data = &adaq4001_chip_info }, { .compatible = "adi,adaq4003", .data = &adaq4003_chip_info }, + { .compatible = "adi,ad7685", .data = &ad7685_chip_info }, + { .compatible = "adi,ad7686", .data = &ad7686_chip_info }, + { .compatible = "adi,ad7687", .data = &ad7687_chip_info }, + { .compatible = "adi,ad7688", .data = &ad7688_chip_info }, + { .compatible = "adi,ad7690", .data = &ad7690_chip_info }, + { .compatible = "adi,ad7691", .data = &ad7691_chip_info }, + { .compatible = "adi,ad7693", .data = &ad7693_chip_info }, + { .compatible = "adi,ad7942", .data = &ad7942_chip_info }, + { .compatible = "adi,ad7946", .data = &ad7946_chip_info }, + { .compatible = "adi,ad7980", .data = &ad7980_chip_info }, + { .compatible = "adi,ad7982", .data = &ad7982_chip_info }, + { .compatible = "adi,ad7983", .data = &ad7983_chip_info }, + { .compatible = "adi,ad7984", .data = &ad7984_chip_info }, + { .compatible = "adi,ad7988-1", .data = &ad7988_1_chip_info }, + { .compatible = "adi,ad7988-5", .data = &ad7988_5_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4000_of_match); @@ -724,3 +1261,4 @@ module_spi_driver(ad4000_driver); MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD4000 ADC driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c new file mode 100644 index 000000000000..1bc2f9a22470 --- /dev/null +++ b/drivers/iio/adc/ad4030.c @@ -0,0 +1,1228 @@ +// 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; + 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, + .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, + .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, + .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, + .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; + + scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + if (chan->differential) + *val = (st->vref_uv * 2) / MILLI; + else + *val = st->vref_uv / MILLI; + + *val2 = 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; + } + + 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); + const struct iio_scan_type *scan_type; + unsigned char diff_realbytes, diff_storagebytes; + unsigned int bytes_to_read; + unsigned long cnv_nb = BIT(st->avg_log2); + unsigned int i; + int ret; + + scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + diff_realbytes = BITS_TO_BYTES(scan_type->realbits); + diff_storagebytes = BITS_TO_BYTES(scan_type->storagebits); + + /* 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 no common mode voltage channel is enabled, we can use the raw + * data as is. Otherwise, we need to rearrange the data a bit to match + * the natural alignment of the IIO buffer. + */ + + 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; + + 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_ts(indio_dev, &st->rx_data, sizeof(st->rx_data), + 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 int ad4030_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + return ad4030_set_mode(indio_dev, *scan_mask); +} + +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, + .update_scan_mode = ad4030_update_scan_mode, +}; + +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 = { + .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..6cf790ff3eb5 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", @@ -517,15 +522,15 @@ static int ad4130_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) return GPIO_LINE_DIRECTION_OUT; } -static void ad4130_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int ad4130_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct ad4130_state *st = gpiochip_get_data(gc); unsigned int mask = FIELD_PREP(AD4130_IO_CONTROL_GPIO_DATA_MASK, BIT(offset)); - regmap_update_bits(st->regmap, AD4130_IO_CONTROL_REG, mask, - value ? mask : 0); + return regmap_update_bits(st->regmap, AD4130_IO_CONTROL_REG, mask, + value ? mask : 0); } static int ad4130_set_mode(struct ad4130_state *st, enum ad4130_mode mode) @@ -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; @@ -2015,7 +2064,7 @@ static int ad4130_probe(struct spi_device *spi) st->gc.can_sleep = true; st->gc.init_valid_mask = ad4130_gpio_init_valid_mask; st->gc.get_direction = ad4130_gpio_get_direction; - st->gc.set = ad4130_gpio_set; + st->gc.set_rv = ad4130_gpio_set; ret = devm_gpiochip_add_data(dev, &st->gc, st); if (ret) diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c index 0146aed9069f..cda419638d9a 100644 --- a/drivers/iio/adc/ad4695.c +++ b/drivers/iio/adc/ad4695.c @@ -19,18 +19,23 @@ #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> -#include <dt-bindings/iio/adi,ad4695.h> +#include <dt-bindings/iio/adc/adi,ad4695.h> /* AD4695 registers */ #define AD4695_REG_SPI_CONFIG_A 0x0000 @@ -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,12 +100,12 @@ #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. */ -#define AD4695_MAX_CHANNELS 16 -/* Max size of 1 raw sample in bytes. */ -#define AD4695_MAX_CHANNEL_SIZE 2 +#define AD4695_MAX_VIN_CHANNELS 16 enum ad4695_in_pair { AD4695_IN_PAIR_REFGND, @@ -118,17 +126,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]; + struct iio_chan_spec iio_chan[AD4695_MAX_VIN_CHANNELS + 2]; + struct ad4695_channel_config channels_cfg[AD4695_MAX_VIN_CHANNELS]; const struct ad4695_chip_info *chip_info; + int sample_freq_range[3]; /* Reference voltage. */ unsigned int vref_mv; /* Common mode input pin voltage. */ @@ -139,15 +157,16 @@ struct ad4695_state { * to control CS and add a delay between the last SCLK and next * CNV rising edges. */ - struct spi_transfer buf_read_xfer[AD4695_MAX_CHANNELS * 2 + 3]; + struct spi_transfer buf_read_xfer[AD4695_MAX_VIN_CHANNELS * 2 + 3]; struct spi_message buf_read_msg; /* Raw conversion data received. */ - u8 buf[ALIGN((AD4695_MAX_CHANNELS + 2) * AD4695_MAX_CHANNEL_SIZE, - sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN); + IIO_DECLARE_DMA_BUFFER_WITH_TS(u16, buf, AD4695_MAX_VIN_CHANNELS + 1); u16 raw_data; /* 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 +211,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 +242,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 +401,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 +433,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 +512,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 +545,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 +581,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) { @@ -455,9 +657,8 @@ static int ad4695_buffer_preenable(struct iio_dev *indio_dev) iio_for_each_active_channel(indio_dev, bit) { xfer = &st->buf_read_xfer[num_xfer]; xfer->bits_per_word = 16; - xfer->rx_buf = &st->buf[rx_buf_offset]; + xfer->rx_buf = &st->buf[rx_buf_offset++]; xfer->len = 2; - rx_buf_offset += xfer->len; if (bit == temp_chan_bit) { temp_en = 1; @@ -596,7 +797,8 @@ static irqreturn_t ad4695_trigger_handler(int irq, void *p) if (ret) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, st->buf, pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, st->buf, sizeof(st->buf), + pf->timestamp); out: iio_trigger_notify_done(indio_dev->trig); @@ -604,6 +806,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 +993,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 +1010,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 +1026,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 +1110,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 +1356,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 +1407,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 +1448,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 +1525,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 +1621,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 +1814,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 +1962,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 +2023,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..f1d2e2896f2a --- /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, i = 0; + + 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[i].has_ext_scan_type = 1; + if (fwnode_property_read_bool(child, "bipolar")) { + ad4851_channels[i].ext_scan_type = ad4851_scan_type_20_b; + ad4851_channels[i].num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_b); + } else { + ad4851_channels[i].ext_scan_type = ad4851_scan_type_20_u; + ad4851_channels[i].num_ext_scan_type = ARRAY_SIZE(ad4851_scan_type_20_u); + } + i++; + } + + 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..647a7852dd8d 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> @@ -386,13 +387,8 @@ EXPORT_SYMBOL_NS_GPL(ad7091r_writeable_reg, "IIO_AD7091R"); bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) { - switch (reg) { - case AD7091R_REG_RESULT: - case AD7091R_REG_ALERT: - return true; - default: - return false; - } + /* The volatile ad7091r registers are also the only RO ones. */ + return !ad7091r_writeable_reg(dev, reg); } EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, "IIO_AD7091R"); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 3d678c420cbf..92596f15e797 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -32,7 +32,7 @@ #define AD7124_IO_CONTROL_2 0x04 #define AD7124_ID 0x05 #define AD7124_ERROR 0x06 -#define AD7124_ERROR_EN 0x07 +#define AD7124_ERROR_EN 0x07 #define AD7124_MCLK_COUNT 0x08 #define AD7124_CHANNEL(x) (0x09 + (x)) #define AD7124_CONFIG(x) (0x19 + (x)) @@ -41,60 +41,59 @@ #define AD7124_GAIN(x) (0x31 + (x)) /* AD7124_STATUS */ -#define AD7124_STATUS_POR_FLAG_MSK BIT(4) +#define AD7124_STATUS_POR_FLAG BIT(4) /* AD7124_ADC_CONTROL */ -#define AD7124_ADC_STATUS_EN_MSK BIT(10) -#define AD7124_ADC_STATUS_EN(x) FIELD_PREP(AD7124_ADC_STATUS_EN_MSK, x) -#define AD7124_ADC_CTRL_REF_EN_MSK BIT(8) -#define AD7124_ADC_CTRL_REF_EN(x) FIELD_PREP(AD7124_ADC_CTRL_REF_EN_MSK, x) -#define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6) -#define AD7124_ADC_CTRL_PWR(x) FIELD_PREP(AD7124_ADC_CTRL_PWR_MSK, x) -#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2) -#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x) - -/* AD7124 ID */ -#define AD7124_DEVICE_ID_MSK GENMASK(7, 4) -#define AD7124_DEVICE_ID_GET(x) FIELD_GET(AD7124_DEVICE_ID_MSK, x) -#define AD7124_SILICON_REV_MSK GENMASK(3, 0) -#define AD7124_SILICON_REV_GET(x) FIELD_GET(AD7124_SILICON_REV_MSK, x) - -#define CHIPID_AD7124_4 0x0 -#define CHIPID_AD7124_8 0x1 +#define AD7124_ADC_CONTROL_MODE GENMASK(5, 2) +#define AD7124_ADC_CONTROL_MODE_CONTINUOUS 0 +#define AD7124_ADC_CONTROL_MODE_SINGLE 1 +#define AD7124_ADC_CONTROL_MODE_STANDBY 2 +#define AD7124_ADC_CONTROL_MODE_POWERDOWN 3 +#define AD7124_ADC_CONTROL_MODE_IDLE 4 +#define AD7124_ADC_CONTROL_MODE_INT_OFFSET_CALIB 5 /* Internal Zero-Scale Calibration */ +#define AD7124_ADC_CONTROL_MODE_INT_GAIN_CALIB 6 /* Internal Full-Scale Calibration */ +#define AD7124_ADC_CONTROL_MODE_SYS_OFFSET_CALIB 7 /* System Zero-Scale Calibration */ +#define AD7124_ADC_CONTROL_MODE_SYS_GAIN_CALIB 8 /* System Full-Scale Calibration */ +#define AD7124_ADC_CONTROL_POWER_MODE GENMASK(7, 6) +#define AD7124_ADC_CONTROL_POWER_MODE_LOW 0 +#define AD7124_ADC_CONTROL_POWER_MODE_MID 1 +#define AD7124_ADC_CONTROL_POWER_MODE_FULL 2 +#define AD7124_ADC_CONTROL_REF_EN BIT(8) +#define AD7124_ADC_CONTROL_DATA_STATUS BIT(10) + +/* AD7124_ID */ +#define AD7124_ID_SILICON_REVISION GENMASK(3, 0) +#define AD7124_ID_DEVICE_ID GENMASK(7, 4) +#define AD7124_ID_DEVICE_ID_AD7124_4 0x0 +#define AD7124_ID_DEVICE_ID_AD7124_8 0x1 /* AD7124_CHANNEL_X */ -#define AD7124_CHANNEL_EN_MSK BIT(15) -#define AD7124_CHANNEL_EN(x) FIELD_PREP(AD7124_CHANNEL_EN_MSK, x) -#define AD7124_CHANNEL_SETUP_MSK GENMASK(14, 12) -#define AD7124_CHANNEL_SETUP(x) FIELD_PREP(AD7124_CHANNEL_SETUP_MSK, x) -#define AD7124_CHANNEL_AINP_MSK GENMASK(9, 5) -#define AD7124_CHANNEL_AINP(x) FIELD_PREP(AD7124_CHANNEL_AINP_MSK, x) -#define AD7124_CHANNEL_AINM_MSK GENMASK(4, 0) -#define AD7124_CHANNEL_AINM(x) FIELD_PREP(AD7124_CHANNEL_AINM_MSK, x) +#define AD7124_CHANNEL_ENABLE BIT(15) +#define AD7124_CHANNEL_SETUP GENMASK(14, 12) +#define AD7124_CHANNEL_AINP GENMASK(9, 5) +#define AD7124_CHANNEL_AINM GENMASK(4, 0) +#define AD7124_CHANNEL_AINx_TEMPSENSOR 16 +#define AD7124_CHANNEL_AINx_AVSS 17 /* AD7124_CONFIG_X */ -#define AD7124_CONFIG_BIPOLAR_MSK BIT(11) -#define AD7124_CONFIG_BIPOLAR(x) FIELD_PREP(AD7124_CONFIG_BIPOLAR_MSK, x) -#define AD7124_CONFIG_REF_SEL_MSK GENMASK(4, 3) -#define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x) -#define AD7124_CONFIG_PGA_MSK GENMASK(2, 0) -#define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x) -#define AD7124_CONFIG_IN_BUFF_MSK GENMASK(6, 5) -#define AD7124_CONFIG_IN_BUFF(x) FIELD_PREP(AD7124_CONFIG_IN_BUFF_MSK, x) +#define AD7124_CONFIG_BIPOLAR BIT(11) +#define AD7124_CONFIG_IN_BUFF GENMASK(6, 5) +#define AD7124_CONFIG_AIN_BUFP BIT(6) +#define AD7124_CONFIG_AIN_BUFM BIT(5) +#define AD7124_CONFIG_REF_SEL GENMASK(4, 3) +#define AD7124_CONFIG_PGA GENMASK(2, 0) /* AD7124_FILTER_X */ -#define AD7124_FILTER_FS_MSK GENMASK(10, 0) -#define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x) -#define AD7124_FILTER_TYPE_MSK GENMASK(23, 21) -#define AD7124_FILTER_TYPE_SEL(x) FIELD_PREP(AD7124_FILTER_TYPE_MSK, x) +#define AD7124_FILTER_FS GENMASK(10, 0) +#define AD7124_FILTER_FILTER GENMASK(23, 21) +#define AD7124_FILTER_FILTER_SINC4 0 +#define AD7124_FILTER_FILTER_SINC3 2 -#define AD7124_SINC3_FILTER 2 -#define AD7124_SINC4_FILTER 0 - -#define AD7124_CONF_ADDR_OFFSET 20 #define AD7124_MAX_CONFIGS 8 #define AD7124_MAX_CHANNELS 16 +/* AD7124 input sources */ + enum ad7124_ids { ID_AD7124_4, ID_AD7124_8, @@ -147,7 +146,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; @@ -158,6 +161,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; ); }; @@ -166,6 +171,7 @@ struct ad7124_channel { struct ad7124_channel_config cfg; unsigned int ain; unsigned int slot; + u8 syscalib_mode; }; struct ad7124_state { @@ -178,35 +184,24 @@ 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[] = { [ID_AD7124_4] = { .name = "ad7124-4", - .chip_id = CHIPID_AD7124_4, + .chip_id = AD7124_ID_DEVICE_ID_AD7124_4, .num_inputs = 8, }, [ID_AD7124_8] = { .name = "ad7124-8", - .chip_id = CHIPID_AD7124_8, + .chip_id = AD7124_ID_DEVICE_ID_AD7124_8, .num_inputs = 16, }, }; @@ -255,8 +250,8 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd, { struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); - st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK; - st->adc_control |= AD7124_ADC_CTRL_MODE(mode); + st->adc_control &= ~AD7124_ADC_CONTROL_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_MODE, mode); return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); } @@ -295,54 +290,55 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state *st, fadc = st->channels[channel].cfg.odr; switch (st->channels[channel].cfg.filter_type) { - case AD7124_SINC3_FILTER: + case AD7124_FILTER_FILTER_SINC3: + return DIV_ROUND_CLOSEST(fadc * 272, 1000); + case AD7124_FILTER_FILTER_SINC4: return DIV_ROUND_CLOSEST(fadc * 230, 1000); - case AD7124_SINC4_FILTER: - return DIV_ROUND_CLOSEST(fadc * 262, 1000); default: return -EINVAL; } } -static void ad7124_set_3db_filter_freq(struct ad7124_state *st, unsigned int channel, - unsigned int freq) -{ - unsigned int sinc4_3db_odr; - unsigned int sinc3_3db_odr; - unsigned int new_filter; - unsigned int new_odr; - - sinc4_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 230); - sinc3_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 262); - - if (sinc4_3db_odr > sinc3_3db_odr) { - new_filter = AD7124_SINC3_FILTER; - new_odr = sinc4_3db_odr; - } else { - new_filter = AD7124_SINC4_FILTER; - new_odr = sinc3_3db_odr; - } - - if (new_odr != st->channels[channel].cfg.odr) - st->channels[channel].cfg.live = false; - - st->channels[channel].cfg.filter_type = new_filter; - st->channels[channel].cfg.odr = new_odr; -} - static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_state *st, 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; } @@ -360,32 +356,31 @@ static int ad7124_find_free_config_slot(struct ad7124_state *st) return free_cfg_slot; } +/* Only called during probe, so dev_err_probe() can be used */ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg) { + struct device *dev = &st->sd.spi->dev; unsigned int refsel = cfg->refsel; switch (refsel) { case AD7124_REFIN1: case AD7124_REFIN2: case AD7124_AVDD_REF: - if (IS_ERR(st->vref[refsel])) { - dev_err(&st->sd.spi->dev, - "Error, trying to use external voltage reference without a %s regulator.\n", - ad7124_ref_names[refsel]); - return PTR_ERR(st->vref[refsel]); - } + if (IS_ERR(st->vref[refsel])) + return dev_err_probe(dev, PTR_ERR(st->vref[refsel]), + "Error, trying to use external voltage reference without a %s regulator.\n", + ad7124_ref_names[refsel]); + cfg->vref_mv = regulator_get_voltage(st->vref[refsel]); /* Conversion from uV to mV */ cfg->vref_mv /= 1000; return 0; case AD7124_INT_REF: cfg->vref_mv = 2500; - st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; - st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); + st->adc_control |= AD7124_ADC_CONTROL_REF_EN; return 0; default: - dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, "Invalid reference %d\n", refsel); } } @@ -398,18 +393,28 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co cfg->cfg_slot = cfg_slot; - 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); + 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; + + val = FIELD_PREP(AD7124_CONFIG_BIPOLAR, cfg->bipolar) | + FIELD_PREP(AD7124_CONFIG_REF_SEL, cfg->refsel) | + (cfg->buf_positive ? AD7124_CONFIG_AIN_BUFP : 0) | + (cfg->buf_negative ? AD7124_CONFIG_AIN_BUFM : 0) | + FIELD_PREP(AD7124_CONFIG_PGA, cfg->pga_bits); ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val); if (ret < 0) return ret; - tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type) | - AD7124_FILTER_FS(cfg->odr_sel_bits); + tmp = FIELD_PREP(AD7124_FILTER_FILTER, cfg->filter_type) | + FIELD_PREP(AD7124_FILTER_FS, cfg->odr_sel_bits); return ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), - AD7124_FILTER_TYPE_MSK | AD7124_FILTER_FS_MSK, + AD7124_FILTER_FILTER | AD7124_FILTER_FS, tmp, 3); } @@ -474,7 +479,8 @@ static int ad7124_enable_channel(struct ad7124_state *st, struct ad7124_channel { ch->cfg.live = true; return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(ch->nr), 2, ch->ain | - AD7124_CHANNEL_SETUP(ch->cfg.cfg_slot) | AD7124_CHANNEL_EN(1)); + FIELD_PREP(AD7124_CHANNEL_SETUP, ch->cfg.cfg_slot) | + AD7124_CHANNEL_ENABLE); } static int ad7124_prepare_read(struct ad7124_state *st, int address) @@ -524,8 +530,10 @@ static int ad7124_append_status(struct ad_sigma_delta *sd, bool append) unsigned int adc_control = st->adc_control; int ret; - adc_control &= ~AD7124_ADC_STATUS_EN_MSK; - adc_control |= AD7124_ADC_STATUS_EN(append); + if (append) + adc_control |= AD7124_ADC_CONTROL_DATA_STATUS; + else + adc_control &= ~AD7124_ADC_CONTROL_DATA_STATUS; ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, adc_control); if (ret < 0) @@ -536,14 +544,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_ENABLE 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; } @@ -551,13 +566,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, @@ -571,6 +579,7 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { .data_reg = AD7124_DATA, .num_slots = 8, .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 64, }; static int ad7124_read_raw(struct iio_dev *indio_dev, @@ -588,26 +597,59 @@ static int ad7124_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - mutex_lock(&st->cfgs_lock); + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->cfgs_lock); - idx = st->channels[chan->address].cfg.pga_bits; - *val = st->channels[chan->address].cfg.vref_mv; - if (st->channels[chan->address].cfg.bipolar) - *val2 = chan->scan_type.realbits - 1 + idx; - else - *val2 = chan->scan_type.realbits + idx; + idx = st->channels[chan->address].cfg.pga_bits; + *val = st->channels[chan->address].cfg.vref_mv; + if (st->channels[chan->address].cfg.bipolar) + *val2 = chan->scan_type.realbits - 1 + idx; + else + *val2 = chan->scan_type.realbits + idx; + + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_TEMP: + /* + * According to the data sheet + * Temperature (°C) + * = ((Conversion − 0x800000)/13584) − 272.5 + * = (Conversion − 0x800000 - 13584 * 272.5) / 13584 + * = (Conversion − 12090248) / 13584 + * So scale with 1000/13584 to yield °mC. Reduce by 8 to + * 125/1698. + */ + *val = 125; + *val2 = 1698; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } - mutex_unlock(&st->cfgs_lock); - return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_OFFSET: - mutex_lock(&st->cfgs_lock); - if (st->channels[chan->address].cfg.bipolar) - *val = -(1 << (chan->scan_type.realbits - 1)); - else - *val = 0; + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->cfgs_lock); + if (st->channels[chan->address].cfg.bipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_INT; + + case IIO_TEMP: + /* see calculation above */ + *val = -12090248; + return IIO_VAL_INT; + + default: + return -EINVAL; + } - mutex_unlock(&st->cfgs_lock); - return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&st->cfgs_lock); *val = st->channels[chan->address].cfg.odr; @@ -665,16 +707,8 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, st->channels[chan->address].cfg.pga_bits = res; break; - case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - if (val2 != 0) { - ret = -EINVAL; - break; - } - - ad7124_set_3db_filter_freq(st, chan->address, val); - break; default: - ret = -EINVAL; + ret = -EINVAL; } mutex_unlock(&st->cfgs_lock); @@ -728,7 +762,7 @@ static int ad7124_update_scan_mode(struct iio_dev *indio_dev, if (bit_set) ret = __ad7124_set_channel(&st->sd, i); else - ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_EN_MSK, + ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_ENABLE, 0, 2); if (ret < 0) { mutex_unlock(&st->cfgs_lock); @@ -751,12 +785,14 @@ static const struct iio_info ad7124_info = { .attrs = &ad7124_attrs_group, }; +/* Only called during probe, so dev_err_probe() can be used */ static int ad7124_soft_reset(struct ad7124_state *st) { + struct device *dev = &st->sd.spi->dev; unsigned int readval, timeout; int ret; - ret = ad_sd_reset(&st->sd, 64); + ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; @@ -765,48 +801,199 @@ static int ad7124_soft_reset(struct ad7124_state *st) do { ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Error reading status register\n"); - if (!(readval & AD7124_STATUS_POR_FLAG_MSK)) - return 0; + if (!(readval & AD7124_STATUS_POR_FLAG)) + break; /* The AD7124 requires typically 2ms to power up and settle */ usleep_range(100, 2000); } while (--timeout); - dev_err(&st->sd.spi->dev, "Soft reset failed\n"); + if (readval & AD7124_STATUS_POR_FLAG) + return dev_err_probe(dev, -EIO, "Soft reset failed\n"); - return -EIO; + 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) { + struct device *dev = &st->sd.spi->dev; unsigned int readval, chip_id, silicon_rev; int ret; ret = ad_sd_read_reg(&st->sd, AD7124_ID, 1, &readval); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "Failure to read ID register\n"); - chip_id = AD7124_DEVICE_ID_GET(readval); - silicon_rev = AD7124_SILICON_REV_GET(readval); + chip_id = FIELD_GET(AD7124_ID_DEVICE_ID, readval); + silicon_rev = FIELD_GET(AD7124_ID_SILICON_REVISION, readval); - if (chip_id != st->chip_info->chip_id) { - dev_err(&st->sd.spi->dev, - "Chip ID mismatch: expected %u, got %u\n", - st->chip_info->chip_id, chip_id); - return -ENODEV; - } + if (chip_id != st->chip_info->chip_id) + return dev_err_probe(dev, -ENODEV, + "Chip ID mismatch: expected %u, got %u\n", + st->chip_info->chip_id, chip_id); + + if (silicon_rev == 0) + return dev_err_probe(dev, -ENODEV, + "Silicon revision empty. Chip may not be present\n"); + + 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_ADC_CONTROL_MODE_SYS_OFFSET_CALIB, + 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_ADC_CONTROL_MODE_SYS_GAIN_CALIB, + 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; - if (silicon_rev == 0) { - dev_err(&st->sd.spi->dev, - "Silicon revision empty. Chip may not be present\n"); - return -ENODEV; + 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 + * into the register field and so are invalid for sure. + */ +static bool ad7124_valid_input_select(unsigned int ain, const struct ad7124_chip_info *info) +{ + if (ain >= info->num_inputs && ain < 16) + return false; + + return ain <= FIELD_MAX(AD7124_CHANNEL_AINM); +} + static int ad7124_parse_channel_config(struct iio_dev *indio_dev, struct device *dev) { @@ -815,18 +1002,30 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, struct ad7124_channel *channels; struct iio_chan_spec *chan; unsigned int ain[2], channel = 0, tmp; + unsigned int num_channels; int ret; - st->num_channels = device_get_child_node_count(dev); - if (!st->num_channels) - return dev_err_probe(dev, -ENODEV, "no channel children\n"); + num_channels = device_get_child_node_count(dev); - chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels, + /* + * The driver assigns each logical channel defined in the device tree + * statically one channel register. So only accept 16 such logical + * channels to not treat CONFIG_0 (i.e. the register following + * CHANNEL_15) as an additional channel register. The driver could be + * improved to lift this limitation. + */ + if (num_channels > AD7124_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels defined\n"); + + /* Add one for temperature */ + st->num_channels = min(num_channels + 1, AD7124_MAX_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; @@ -838,20 +1037,27 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, device_for_each_child_node_scoped(dev, child) { ret = fwnode_property_read_u32(child, "reg", &channel); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to parse reg property of %pfwP\n", child); - if (channel >= indio_dev->num_channels) + if (channel >= num_channels) return dev_err_probe(dev, -EINVAL, - "Channel index >= number of channels\n"); + "Channel index >= number of channels in %pfwP\n", child); ret = fwnode_property_read_u32_array(child, "diff-channels", ain, 2); if (ret) - return ret; + return dev_err_probe(dev, ret, + "Failed to parse diff-channels property of %pfwP\n", child); + + if (!ad7124_valid_input_select(ain[0], st->chip_info) || + !ad7124_valid_input_select(ain[1], st->chip_info)) + return dev_err_probe(dev, -EINVAL, + "diff-channels property of %pfwP contains invalid data\n", child); st->channels[channel].nr = channel; - st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | - AD7124_CHANNEL_AINM(ain[1]); + st->channels[channel].ain = FIELD_PREP(AD7124_CHANNEL_AINP, ain[0]) | + FIELD_PREP(AD7124_CHANNEL_AINM, ain[1]); cfg = &st->channels[channel].cfg; cfg->bipolar = fwnode_property_read_bool(child, "bipolar"); @@ -874,17 +1080,49 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev, chan[channel].channel2 = ain[1]; } + if (num_channels < AD7124_MAX_CHANNELS) { + st->channels[num_channels] = (struct ad7124_channel) { + .nr = num_channels, + .ain = FIELD_PREP(AD7124_CHANNEL_AINP, AD7124_CHANNEL_AINx_TEMPSENSOR) | + FIELD_PREP(AD7124_CHANNEL_AINM, AD7124_CHANNEL_AINx_AVSS), + .cfg = { + .bipolar = true, + }, + }; + + chan[num_channels] = (struct iio_chan_spec) { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + /* + * You might find it strange that a bipolar + * measurement yields an unsigned value, but + * this matches the device's manual. + */ + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + .address = num_channels, + .scan_index = num_channels, + }; + } + return 0; } static int ad7124_setup(struct ad7124_state *st) { + struct device *dev = &st->sd.spi->dev; unsigned int fclk, power_mode; int i, ret; fclk = clk_get_rate(st->mclk); if (!fclk) - return -EINVAL; + return dev_err_probe(dev, -EINVAL, "Failed to get mclk rate\n"); /* The power mode changes the master clock frequency */ power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz, @@ -893,15 +1131,15 @@ static int ad7124_setup(struct ad7124_state *st) if (fclk != ad7124_master_clk_freq_hz[power_mode]) { ret = clk_set_rate(st->mclk, fclk); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to set mclk rate\n"); } /* Set the power mode */ - st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK; - st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode); + st->adc_control &= ~AD7124_ADC_CONTROL_POWER_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_POWER_MODE, power_mode); - st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK; - st->adc_control |= AD7124_ADC_CTRL_MODE(AD_SD_MODE_IDLE); + st->adc_control &= ~AD7124_ADC_CONTROL_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_MODE, AD_SD_MODE_IDLE); mutex_init(&st->cfgs_lock); INIT_KFIFO(st->live_cfgs_fifo); @@ -917,14 +1155,98 @@ 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 ret; + return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n"); + + 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_ADC_CONTROL_MODE_INT_GAIN_CALIB, 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_ADC_CONTROL_MODE_INT_OFFSET_CALIB, 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_CONTROL_POWER_MODE, adc_control) >= AD7124_FULL_POWER) { + st->adc_control &= ~AD7124_ADC_CONTROL_POWER_MODE; + st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_POWER_MODE, AD7124_MID_POWER); + } + + ret = __ad7124_calibrate_all(st, indio_dev); + + st->adc_control = adc_control; return ret; } @@ -937,13 +1259,14 @@ static void ad7124_reg_disable(void *r) static int ad7124_probe(struct spi_device *spi) { const struct ad7124_chip_info *info; + struct device *dev = &spi->dev; struct ad7124_state *st; struct iio_dev *indio_dev; int i, ret; info = spi_get_device_match_data(spi); if (!info) - return -ENODEV; + return dev_err_probe(dev, -ENODEV, "Failed to get match data\n"); indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) @@ -978,17 +1301,17 @@ static int ad7124_probe(struct spi_device *spi) ret = regulator_enable(st->vref[i]); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to enable regulator #%d\n", i); ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable, st->vref[i]); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to register disable handler for regulator #%d\n", i); } st->mclk = devm_clk_get_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) - return PTR_ERR(st->mclk); + return dev_err_probe(dev, PTR_ERR(st->mclk), "Failed to get mclk\n"); ret = ad7124_soft_reset(st); if (ret < 0) @@ -1004,10 +1327,17 @@ static int ad7124_probe(struct spi_device *spi) ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); 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; - return devm_iio_device_register(&spi->dev, indio_dev); + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); + return 0; } static const struct of_device_id ad7124_of_match[] = { diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 8b03c1e5567e..b3e6bd2a55d7 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,12 @@ #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 */ +#define AD7173_MODE_CAL_SYS_ZERO 0x6 /* System Zero-Scale Calibration */ +#define AD7173_MODE_CAL_SYS_FULL 0x7 /* System Full-Scale Calibration */ struct ad7173_device_info { const unsigned int *sinc5_data_rates; @@ -166,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; @@ -175,15 +184,24 @@ struct ad7173_device_info { bool has_input_buf; 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; @@ -193,14 +211,14 @@ struct ad7173_channel_config { }; struct ad7173_channel { - unsigned int chan_reg; 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]; @@ -212,10 +230,8 @@ struct ad7173_state { unsigned long long *config_cnts; struct clk *ext_clk; struct clk_hw int_clk_hw; -#if IS_ENABLED(CONFIG_GPIOLIB) struct regmap *reg_gpiocon_regmap; struct gpio_regmap *gpio_regmap; -#endif }; static unsigned int ad4115_sinc5_data_rates[] = { @@ -259,253 +275,208 @@ 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, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_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", + [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss", + [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd", }; -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, - .clock = 2 * HZ_PER_MHZ, - .sinc5_data_rates = ad7173_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), +static const char *const ad7173_clk_sel[] = { + "ext-clk", "xtal" }; -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 regmap_range ad7173_range_gpio[] = { + regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), }; -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, - .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 regmap_access_table ad7173_access_table = { + .yes_ranges = ad7173_range_gpio, + .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio), }; -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, - .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 regmap_config ad7173_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .rd_table = &ad7173_access_table, + .wr_table = &ad7173_access_table, + .read_flag_mask = BIT(6), }; -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, - .clock = 4 * HZ_PER_MHZ, - .sinc5_data_rates = ad4116_sinc5_data_rates, - .num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates), +enum { + AD7173_SYSCALIB_ZERO_SCALE, + AD7173_SYSCALIB_FULL_SCALE, }; -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 char * const ad7173_syscalib_modes[] = { + [AD7173_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7173_SYSCALIB_FULL_SCALE] = "full_scale", }; -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 int ad7173_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7173_state *st = iio_priv(indio_dev); -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), -}; + st->channels[chan->channel].syscalib_mode = mode; -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), -}; + return 0; +} -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 int ad7173_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); -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), -}; + return st->channels[chan->channel].syscalib_mode; +} -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 ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7173_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, mode; -static const char *const ad7173_ref_sel_str[] = { - [AD7173_SETUP_REF_SEL_EXT_REF] = "vref", - [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2", - [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss", - [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd", -}; + ret = kstrtobool(buf, &sys_calib); + if (ret) + return ret; -static const char *const ad7173_clk_sel[] = { - "ext-clk", "xtal" -}; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; -#if IS_ENABLED(CONFIG_GPIOLIB) + mode = st->channels[chan->channel].syscalib_mode; + if (sys_calib) { + if (mode == AD7173_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_FULL, + chan->address); + } -static const struct regmap_range ad7173_range_gpio[] = { - regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), + iio_device_release_direct(indio_dev); + + return ret ? : len; +} + +static const struct iio_enum ad7173_syscalib_mode_enum = { + .items = ad7173_syscalib_modes, + .num_items = ARRAY_SIZE(ad7173_syscalib_modes), + .set = ad7173_set_syscalib_mode, + .get = ad7173_get_syscalib_mode }; -static const struct regmap_access_table ad7173_access_table = { - .yes_ranges = ad7173_range_gpio, - .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio), +static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7173_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7173_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7173_syscalib_mode_enum), + { } }; -static const struct regmap_config ad7173_regmap_config = { - .reg_bits = 8, - .val_bits = 16, - .rd_table = &ad7173_access_table, - .wr_table = &ad7173_access_table, - .read_flag_mask = BIT(6), +static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_dev) +{ + int ret; + int i; + + for (i = 0; i < st->num_channels; i++) { + if (indio_dev->channels[i].type != IIO_VOLTAGE) + continue; + + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain); + if (ret < 0) + return ret; + + if (st->info->has_internal_fs_calibration) { + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, + st->channels[i].ain); + if (ret < 0) + return ret; + } + } + + 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) @@ -568,12 +539,6 @@ static int ad7173_gpio_init(struct ad7173_state *st) return 0; } -#else -static int ad7173_gpio_init(struct ad7173_state *st) -{ - return 0; -} -#endif /* CONFIG_GPIOLIB */ static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd) { @@ -602,15 +567,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; @@ -703,6 +681,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); } @@ -751,20 +732,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) @@ -802,6 +1043,10 @@ static int ad7173_setup(struct iio_dev *indio_dev) if (!st->config_cnts) return -ENOMEM; + ret = ad7173_calibrate_all(st, indio_dev); + if (ret) + return ret; + /* All channels are enabled by default after a reset */ return ad7173_disable_all(&st->sd); } @@ -854,6 +1099,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: @@ -918,11 +1169,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) { /* @@ -956,7 +1206,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; } @@ -998,12 +1248,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 = { @@ -1024,6 +1319,7 @@ static const struct iio_chan_spec ad7173_channel_template = { .storagebits = 32, .endianness = IIO_BE, }, + .ext_info = ad7173_calibsys_ext_info, }; static const struct iio_chan_spec ad7173_temp_iio_channel_template = { @@ -1206,6 +1502,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; @@ -1253,6 +1580,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; @@ -1317,9 +1645,9 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->address = chan_index; chan->scan_index = chan_index; chan->channel = ain[0]; - chan_st_priv->chan_reg = chan_index; 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) @@ -1334,6 +1662,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) @@ -1400,12 +1736,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); } @@ -1437,9 +1767,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; @@ -1459,10 +1787,7 @@ static int ad7173_probe(struct spi_device *spi) if (ret) return ret; - if (IS_ENABLED(CONFIG_GPIOLIB)) - return ad7173_gpio_init(st); - - return 0; + return ad7173_gpio_init(st); } static const struct of_device_id ad7173_of_match[] = { 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 1c87db0e0460..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; } @@ -361,6 +367,7 @@ static const struct ad_sigma_delta_info ad7192_sigma_delta_info = { .status_ch_mask = GENMASK(3, 0), .num_slots = 4, .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, }; static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { @@ -373,6 +380,7 @@ static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, }; static const struct ad_sd_calib_data ad7192_calib_arr[8] = { @@ -565,7 +573,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) int i, ret, id; /* reset the serial interface */ - ret = ad_sd_reset(&st->sd, 48); + ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; usleep_range(500, 1000); /* Wait for at least 500us */ @@ -691,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: @@ -716,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; } @@ -943,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; } @@ -1082,7 +1090,7 @@ static int ad7192_update_scan_mode(struct iio_dev *indio_dev, const unsigned lon conf &= ~AD7192_CONF_CHAN_MASK; for_each_set_bit(i, scan_mask, 8) - conf |= FIELD_PREP(AD7192_CONF_CHAN_MASK, i); + conf |= FIELD_PREP(AD7192_CONF_CHAN_MASK, BIT(i)); ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, conf); if (ret < 0) diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 858c8be2ff1a..3364ac6c4631 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -45,7 +45,7 @@ struct ad7266_state { */ struct { __be16 sample[2]; - s64 timestamp; + aligned_s64 timestamp; } data __aligned(IIO_DMA_MINALIGN); }; @@ -86,10 +86,9 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p) int ret; ret = spi_read(st->spi, st->data.sample, 4); - if (ret == 0) { - iio_push_to_buffers_with_timestamp(indio_dev, &st->data, - pf->timestamp); - } + if (ret == 0) + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + pf->timestamp); iio_trigger_notify_done(indio_dev->trig); @@ -153,11 +152,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/ad7280a.c b/drivers/iio/adc/ad7280a.c index f9f32737db80..dda2986ccda0 100644 --- a/drivers/iio/adc/ad7280a.c +++ b/drivers/iio/adc/ad7280a.c @@ -572,7 +572,7 @@ static const struct iio_chan_spec_ext_info ad7280_cell_ext_info[] = { .write = ad7280_store_balance_timer, .shared = IIO_SEPARATE, }, - {} + { } }; static const struct iio_event_spec ad7280_events[] = { diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index b35bd4d9ef81..7c0538ea15c8 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -155,8 +155,8 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) if (b_sent) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, st->rx_buf, sizeof(st->rx_buf), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -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..cabf5511d116 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -13,8 +13,13 @@ * ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf * ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf * ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf + * ad7389-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7389-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 +34,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 +99,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 +112,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; @@ -105,11 +120,31 @@ struct ad7380_chip_info { const char * const *supplies; unsigned int num_supplies; bool external_ref_only; - bool adaq_internal_ref_only; + bool internal_ref_only; + unsigned int internal_ref_mv; const char * const *vcm_supplies; 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 +232,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 +334,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 +463,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 +478,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,90 +606,112 @@ 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), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), .vcm_supplies = ad7380_2_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), .vcm_supplies = ad7380_2_channel_vcm_supplies, .num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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,112 +719,171 @@ 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), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .vcm_supplies = ad7380_4_channel_vcm_supplies, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .vcm_supplies = ad7380_4_channel_vcm_supplies, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_mv = AD7380_INTERNAL_REF_MV, .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 ad7389_4_chip_info = { + .name = "ad7389-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, + .num_supplies = ARRAY_SIZE(ad7380_supplies), + .internal_ref_only = true, + .internal_ref_mv = AD7380_INTERNAL_REF_MV, + .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 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, .num_supplies = ARRAY_SIZE(adaq4380_supplies), - .adaq_internal_ref_only = true, + .internal_ref_only = true, + .internal_ref_mv = ADAQ4380_INTERNAL_REF_MV, .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, .num_supplies = ARRAY_SIZE(adaq4380_supplies), - .adaq_internal_ref_only = true, + .internal_ref_only = true, + .internal_ref_mv = ADAQ4380_INTERNAL_REF_MV, .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), + .internal_ref_only = true, + .internal_ref_mv = ADAQ4380_INTERNAL_REF_MV, + .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 +895,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. @@ -601,8 +909,7 @@ struct ad7380_state { * Make the buffer large enough for MAX_NUM_CHANNELS 32-bit samples and * one 64-bit aligned 64-bit timestamp. */ - u8 scan_data[ALIGN(MAX_NUM_CHANNELS * sizeof(u32), sizeof(s64)) - + sizeof(s64)] __aligned(IIO_DMA_MINALIGN); + IIO_DECLARE_DMA_BUFFER_WITH_TS(u8, scan_data, MAX_NUM_CHANNELS * sizeof(u32)); /* buffers for reading/writing registers */ u16 tx; u16 rx; @@ -663,6 +970,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 +991,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 +1061,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 +1080,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 +1093,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 +1120,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 +1133,144 @@ 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; + + spi_offload_trigger_disable(st->offload, st->offload_trigger); + spi_unoptimize_message(&st->offload_msg); + + 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; + } + + 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 +1282,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 +1312,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); } @@ -861,8 +1359,8 @@ static irqreturn_t ad7380_trigger_handler(int irq, void *p) if (ret) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, &st->scan_data, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &st->scan_data, sizeof(st->scan_data), + pf->timestamp); out: iio_trigger_notify_done(indio_dev->trig); @@ -889,13 +1387,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 +1420,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 +1429,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,9 +1466,20 @@ 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 +1490,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 +1499,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 +1530,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 +1599,192 @@ 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 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; + int ret, tmp, shift; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + /* + * The register value is 12-bits and is compared to the most significant + * bits of raw value, therefore a shift is required to convert this to + * the same scale as the raw value. + */ + shift = scan_type->realbits - 12; + + 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) << shift; + 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) << shift; + 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) +{ + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad7380_get_alert_th(indio_dev, chan, 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 +1808,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 +1818,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; @@ -1129,7 +1891,7 @@ static int ad7380_probe(struct spi_device *spi) "Failed to enable power supplies\n"); fsleep(T_POWERUP_US); - if (st->chip_info->adaq_internal_ref_only) { + if (st->chip_info->internal_ref_only) { /* * ADAQ chips use fixed internal reference but still * require a specific reference supply to power it. @@ -1137,7 +1899,7 @@ static int ad7380_probe(struct spi_device *spi) * in bulk_get_enable(). */ - st->vref_mv = ADAQ4380_INTERNAL_REF_MV; + st->vref_mv = st->chip_info->internal_ref_mv; /* these chips don't have a register bit for this */ external_ref_en = false; @@ -1162,7 +1924,8 @@ static int ad7380_probe(struct spi_device *spi) "Failed to get refio regulator\n"); external_ref_en = ret != -ENODEV; - st->vref_mv = external_ref_en ? ret / 1000 : AD7380_INTERNAL_REF_MV; + st->vref_mv = external_ref_en ? ret / 1000 + : st->chip_info->internal_ref_mv; } if (st->chip_info->num_vcm_supplies > ARRAY_SIZE(st->vcm_mv)) @@ -1190,8 +1953,9 @@ static int ad7380_probe(struct spi_device *spi) if (st->chip_info->has_hardware_gain) { device_for_each_child_node_scoped(dev, node) { - unsigned int channel, gain; + unsigned int channel; int gain_idx; + u16 gain; ret = fwnode_property_read_u32(node, "reg", &channel); if (ret) @@ -1203,7 +1967,7 @@ static int ad7380_probe(struct spi_device *spi) "Invalid channel number %i\n", channel); - ret = fwnode_property_read_u32(node, "adi,gain-milli", + ret = fwnode_property_read_u16(node, "adi,gain-milli", &gain); if (ret && ret != -EINVAL) return dev_err_probe(dev, ret, @@ -1274,12 +2038,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) @@ -1303,8 +2079,10 @@ static const struct of_device_id ad7380_of_match_table[] = { { .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info }, { .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info }, { .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info }, + { .compatible = "adi,ad7389-4", .data = &ad7389_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 }, { } }; @@ -1323,8 +2101,10 @@ static const struct spi_device_id ad7380_id_table[] = { { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info }, { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info }, { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info }, + { "ad7389-4", (kernel_ulong_t)&ad7389_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 +2122,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..ddb607ac1860 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -99,8 +99,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) if (b_sent < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, st->data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, st->data, sizeof(st->data), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -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 e35d55d03d86..185243dee86e 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> @@ -12,6 +13,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/property.h> #include <linux/pwm.h> #include <linux/regulator/consumer.h> @@ -85,123 +87,43 @@ static const unsigned int ad7606_oversampling_avail[7] = { 1, 2, 4, 8, 16, 32, 64, }; -static const unsigned int ad7616_oversampling_avail[8] = { - 1, 2, 4, 8, 16, 32, 64, 128, +static const unsigned int ad7606b_oversampling_avail[9] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, }; -static const struct iio_chan_spec ad7605_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(4), - AD7605_CHANNEL(0), - AD7605_CHANNEL(1), - AD7605_CHANNEL(2), - AD7605_CHANNEL(3), -}; - -static const struct iio_chan_spec ad7606_channels_16bit[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_CHANNEL(0, 16), - AD7606_CHANNEL(1, 16), - AD7606_CHANNEL(2, 16), - AD7606_CHANNEL(3, 16), - AD7606_CHANNEL(4, 16), - AD7606_CHANNEL(5, 16), - AD7606_CHANNEL(6, 16), - AD7606_CHANNEL(7, 16), -}; - -static const struct iio_chan_spec ad7606_channels_18bit[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_CHANNEL(0, 18), - AD7606_CHANNEL(1, 18), - AD7606_CHANNEL(2, 18), - AD7606_CHANNEL(3, 18), - AD7606_CHANNEL(4, 18), - AD7606_CHANNEL(5, 18), - AD7606_CHANNEL(6, 18), - AD7606_CHANNEL(7, 18), -}; - -static const struct iio_chan_spec ad7607_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_CHANNEL(0, 14), - AD7606_CHANNEL(1, 14), - AD7606_CHANNEL(2, 14), - AD7606_CHANNEL(3, 14), - AD7606_CHANNEL(4, 14), - AD7606_CHANNEL(5, 14), - AD7606_CHANNEL(6, 14), - AD7606_CHANNEL(7, 14), -}; - -static const struct iio_chan_spec ad7608_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_CHANNEL(0, 18), - AD7606_CHANNEL(1, 18), - AD7606_CHANNEL(2, 18), - AD7606_CHANNEL(3, 18), - AD7606_CHANNEL(4, 18), - AD7606_CHANNEL(5, 18), - AD7606_CHANNEL(6, 18), - AD7606_CHANNEL(7, 18), -}; - -/* - * The current assumption that this driver makes for AD7616, is that it's - * working in Hardware Mode with Serial, Burst and Sequencer modes activated. - * To activate them, following pins must be pulled high: - * -SER/PAR - * -SEQEN - * And following pins must be pulled low: - * -WR/BURST - * -DB4/SER1W - */ -static const struct iio_chan_spec ad7616_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(16), - AD7606_CHANNEL(0, 16), - AD7606_CHANNEL(1, 16), - AD7606_CHANNEL(2, 16), - AD7606_CHANNEL(3, 16), - AD7606_CHANNEL(4, 16), - AD7606_CHANNEL(5, 16), - AD7606_CHANNEL(6, 16), - AD7606_CHANNEL(7, 16), - AD7606_CHANNEL(8, 16), - AD7606_CHANNEL(9, 16), - AD7606_CHANNEL(10, 16), - AD7606_CHANNEL(11, 16), - AD7606_CHANNEL(12, 16), - AD7606_CHANNEL(13, 16), - AD7606_CHANNEL(14, 16), - AD7606_CHANNEL(15, 16), +static const unsigned int ad7616_oversampling_avail[8] = { + 1, 2, 4, 8, 16, 32, 64, 128, }; -static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); -static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); -static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); -static int ad7607_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); -static int ad7608_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); -static int ad7609_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); +static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +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, + .max_samplerate = 300 * KILO, .name = "ad7605-4", + .bits = 16, .num_adc_channels = 4, - .num_channels = 5, .scale_setup_cb = ad7606_16bit_chan_scale_setup, }; EXPORT_SYMBOL_NS_GPL(ad7605_4_info, "IIO_AD7606"); const struct ad7606_chip_info ad7606_8_info = { - .channels = ad7606_channels_16bit, + .max_samplerate = 200 * KILO, .name = "ad7606-8", + .bits = 16, .num_adc_channels = 8, - .num_channels = 9, .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7606_16bit_chan_scale_setup, @@ -209,104 +131,116 @@ const struct ad7606_chip_info ad7606_8_info = { EXPORT_SYMBOL_NS_GPL(ad7606_8_info, "IIO_AD7606"); const struct ad7606_chip_info ad7606_6_info = { - .channels = ad7606_channels_16bit, + .max_samplerate = 200 * KILO, .name = "ad7606-6", + .bits = 16, .num_adc_channels = 6, - .num_channels = 7, .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7606_6_info, "IIO_AD7606"); const struct ad7606_chip_info ad7606_4_info = { - .channels = ad7606_channels_16bit, + .max_samplerate = 200 * KILO, .name = "ad7606-4", + .bits = 16, .num_adc_channels = 4, - .num_channels = 5, .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7606_16bit_chan_scale_setup, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7606_4_info, "IIO_AD7606"); const struct ad7606_chip_info ad7606b_info = { - .channels = ad7606_channels_16bit, .max_samplerate = 800 * KILO, .name = "ad7606b", + .bits = 16, .num_adc_channels = 8, - .num_channels = 9, .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, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606"); const struct ad7606_chip_info ad7606c_16_info = { - .channels = ad7606_channels_16bit, + .max_samplerate = 1 * MEGA, .name = "ad7606c16", + .bits = 16, .num_adc_channels = 8, - .num_channels = 9, .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, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606"); const struct ad7606_chip_info ad7607_info = { - .channels = ad7607_channels, + .max_samplerate = 200 * KILO, .name = "ad7607", + .bits = 14, .num_adc_channels = 8, - .num_channels = 9, .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7607_chan_scale_setup, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7607_info, "IIO_AD7606"); const struct ad7606_chip_info ad7608_info = { - .channels = ad7608_channels, + .max_samplerate = 200 * KILO, .name = "ad7608", + .bits = 18, .num_adc_channels = 8, - .num_channels = 9, .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7608_chan_scale_setup, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7608_info, "IIO_AD7606"); const struct ad7606_chip_info ad7609_info = { - .channels = ad7608_channels, + .max_samplerate = 200 * KILO, .name = "ad7609", + .bits = 18, .num_adc_channels = 8, - .num_channels = 9, .oversampling_avail = ad7606_oversampling_avail, .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail), .scale_setup_cb = ad7609_chan_scale_setup, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7609_info, "IIO_AD7606"); const struct ad7606_chip_info ad7606c_18_info = { - .channels = ad7606_channels_18bit, + .max_samplerate = 1 * MEGA, .name = "ad7606c18", + .bits = 18, .num_adc_channels = 8, - .num_channels = 9, .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, + .offload_storagebits = 32, }; EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606"); const struct ad7606_chip_info ad7616_info = { - .channels = ad7616_channels, + .max_samplerate = 1 * MEGA, .init_delay_ms = 15, .name = "ad7616", + .bits = 16, .num_adc_channels = 16, - .num_channels = 17, .oversampling_avail = ad7616_oversampling_avail, .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, + .offload_storagebits = 16, }; EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606"); @@ -323,10 +257,11 @@ int ad7606_reset(struct ad7606_state *st) } EXPORT_SYMBOL_NS_GPL(ad7606_reset, "IIO_AD7606"); -static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch) +static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) { - struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; if (!st->sw_mode_en) { /* tied to logic low, analog input range is +/- 5V */ @@ -345,10 +280,11 @@ static int ad7606_16bit_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7606_get_chan_config(struct ad7606_state *st, int ch, +static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, bool *bipolar, bool *differential) { - unsigned int num_channels = st->chip_info->num_channels - 1; + struct ad7606_state *st = iio_priv(indio_dev); + unsigned int num_channels = st->chip_info->num_adc_channels; struct device *dev = st->dev; int ret; @@ -364,7 +300,7 @@ static int ad7606_get_chan_config(struct ad7606_state *st, int ch, continue; /* channel number (here) is from 1 to num_channels */ - if (reg == 0 || reg > num_channels) { + if (reg < 1 || reg > num_channels) { dev_warn(dev, "Invalid channel number (ignoring): %d\n", reg); continue; @@ -399,10 +335,11 @@ static int ad7606_get_chan_config(struct ad7606_state *st, int ch, return 0; } -static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch) +static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) { - struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; bool bipolar, differential; int ret; @@ -413,7 +350,8 @@ static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, return 0; } - ret = ad7606_get_chan_config(st, ch, &bipolar, &differential); + ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar, + &differential); if (ret) return ret; @@ -455,10 +393,11 @@ static int ad7606c_18bit_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch) +static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) { - struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; bool bipolar, differential; int ret; @@ -469,7 +408,8 @@ static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, return 0; } - ret = ad7606_get_chan_config(st, ch, &bipolar, &differential); + ret = ad7606_get_chan_config(indio_dev, chan->scan_index, &bipolar, + &differential); if (ret) return ret; @@ -512,10 +452,11 @@ static int ad7606c_16bit_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7607_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch) +static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) { - struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; cs->range = 0; cs->scale_avail = ad7607_hw_scale_avail; @@ -523,10 +464,11 @@ static int ad7607_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7608_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch) +static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) { - struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; cs->range = 0; cs->scale_avail = ad7606_18bit_hw_scale_avail; @@ -534,10 +476,11 @@ static int ad7608_chan_scale_setup(struct ad7606_state *st, return 0; } -static int ad7609_chan_scale_setup(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch) +static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, + struct iio_chan_spec *chan) { - struct ad7606_chan_scale *cs = &st->chan_scales[ch]; + struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; cs->range = 0; cs->scale_avail = ad7609_hw_scale_avail; @@ -580,7 +523,7 @@ static int ad7606_pwm_set_high(struct ad7606_state *st) return ret; } -static int ad7606_pwm_set_low(struct ad7606_state *st) +int ad7606_pwm_set_low(struct ad7606_state *st) { struct pwm_state cnvst_pwm_state; int ret; @@ -593,8 +536,9 @@ static int ad7606_pwm_set_low(struct ad7606_state *st) return ret; } +EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_low, "IIO_AD7606"); -static int ad7606_pwm_set_swing(struct ad7606_state *st) +int ad7606_pwm_set_swing(struct ad7606_state *st) { struct pwm_state cnvst_pwm_state; @@ -604,6 +548,7 @@ static int ad7606_pwm_set_swing(struct ad7606_state *st) return pwm_apply_might_sleep(st->cnvst_pwm, &cnvst_pwm_state); } +EXPORT_SYMBOL_NS_GPL(ad7606_pwm_set_swing, "IIO_AD7606"); static bool ad7606_pwm_is_swinging(struct ad7606_state *st) { @@ -660,8 +605,8 @@ static irqreturn_t ad7606_trigger_handler(int irq, void *p) if (ret) goto error_ret; - iio_push_to_buffers_with_timestamp(indio_dev, &st->data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + iio_get_time_ns(indio_dev)); error_ret: iio_trigger_notify_done(indio_dev->trig); /* The rising edge of the CONVST signal starts a new conversion. */ @@ -674,8 +619,8 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch, int *val) { struct ad7606_state *st = iio_priv(indio_dev); - unsigned int realbits = st->chip_info->channels[1].scan_type.realbits; const struct iio_chan_spec *chan; + unsigned int realbits; int ret; if (st->gpio_convst) { @@ -692,7 +637,7 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch, * will not happen because IIO_CHAN_INFO_RAW is not supported for the backend. * TODO: Add support for reading a single value when the backend is used. */ - if (!st->back) { + if (st->trig) { ret = wait_for_completion_timeout(&st->completion, msecs_to_jiffies(1000)); if (!ret) { @@ -700,25 +645,30 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch, goto error_ret; } } else { - fsleep(1); + /* + * If the BUSY interrupt is not available, wait enough time for + * the longest possible conversion (max for the whole family is + * around 350us). + */ + fsleep(400); } ret = ad7606_read_samples(st); if (ret) goto error_ret; - chan = &indio_dev->channels[ch + 1]; - if (chan->scan_type.sign == 'u') { - if (realbits > 16) - *val = st->data.buf32[ch]; - else - *val = st->data.buf16[ch]; - } else { - if (realbits > 16) - *val = sign_extend32(st->data.buf32[ch], realbits - 1); - else - *val = sign_extend32(st->data.buf16[ch], realbits - 1); - } + chan = &indio_dev->channels[ch]; + realbits = chan->scan_type.realbits; + + if (realbits > 16) + *val = st->data.buf32[ch]; + else + *val = st->data.buf16[ch]; + + *val &= GENMASK(realbits - 1, 0); + + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, realbits - 1); error_ret: if (!st->gpio_convst) { @@ -744,16 +694,16 @@ 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->scan_index, 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; + ch = chan->scan_index; cs = &st->chan_scales[ch]; *val = cs->scale_avail[cs->range][0]; *val2 = cs->scale_avail[cs->range][1]; @@ -762,10 +712,6 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, *val = st->oversampling; return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - /* - * TODO: return the real frequency intead of the requested one once - * pwm_get_state_hw comes upstream. - */ pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period); return IIO_VAL_INT; @@ -810,8 +756,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) @@ -836,7 +781,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SCALE: if (st->sw_mode_en) - ch = chan->address; + ch = chan->scan_index; cs = &st->chan_scales[ch]; for (i = 0; i < cs->num_scales; i++) { scale_avail_uv[i] = cs->scale_avail[i][0] * MICRO + @@ -844,7 +789,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; @@ -855,7 +804,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]; @@ -1035,11 +988,11 @@ static int ad7606_read_avail(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: if (st->sw_mode_en) - ch = chan->address; + ch = chan->scan_index; cs = &st->chan_scales[ch]; *vals = (int *)cs->scale_avail; - *length = cs->num_scales; + *length = cs->num_scales * 2; *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_LIST; @@ -1130,43 +1083,211 @@ 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); - indio_dev->info = &ad7606_info_sw_mode; + 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; + + if (st->bops->sw_mode_config) { + ret = st->bops->sw_mode_config(indio_dev); + if (ret) + return ret; + } + + /* 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; + + if (!st->bops->sw_mode_config) + return 0; return st->bops->sw_mode_config(indio_dev); } -static int ad7606_chan_scales_setup(struct iio_dev *indio_dev) +static int ad7606_probe_channels(struct iio_dev *indio_dev) { - unsigned int num_channels = indio_dev->num_channels - 1; struct ad7606_state *st = iio_priv(indio_dev); - struct iio_chan_spec *chans; - size_t size; - int ch, ret; - - /* Clone IIO channels, since some may be differential */ - size = indio_dev->num_channels * sizeof(*indio_dev->channels); - chans = devm_kzalloc(st->dev, size, GFP_KERNEL); - if (!chans) + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *channels; + bool slow_bus; + int ret, i; + + slow_bus = !(st->bops->iio_backend_config || st->offload_en); + indio_dev->num_channels = st->chip_info->num_adc_channels; + + /* Slow buses also get 1 more channel for soft timestamp */ + if (slow_bus) + indio_dev->num_channels++; + + channels = devm_kcalloc(dev, indio_dev->num_channels, sizeof(*channels), + GFP_KERNEL); + if (!channels) return -ENOMEM; - memcpy(chans, indio_dev->channels, size); - indio_dev->channels = chans; + for (i = 0; i < st->chip_info->num_adc_channels; i++) { + struct iio_chan_spec *chan = &channels[i]; + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = i; + chan->scan_index = i; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = st->chip_info->bits; + /* + * If in SPI offload mode, storagebits are set based + * on the spi-engine hw implementation. + */ + chan->scan_type.storagebits = st->offload_en ? + st->chip_info->offload_storagebits : + (st->chip_info->bits > 16 ? 32 : 16); + + chan->scan_type.endianness = IIO_CPU; + + if (indio_dev->modes & INDIO_DIRECT_MODE) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); + + if (st->sw_mode_en) { + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_separate_available |= + BIT(IIO_CHAN_INFO_SCALE); + + /* + * All chips with software mode support oversampling, + * so we skip the oversampling_available check. And the + * shared_by_type instead of shared_by_all on slow + * buses is for backward compatibility. + */ + if (slow_bus) + chan->info_mask_shared_by_type |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + else + chan->info_mask_shared_by_all |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + + chan->info_mask_shared_by_all_available |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + } else { + chan->info_mask_shared_by_type |= + BIT(IIO_CHAN_INFO_SCALE); - for (ch = 0; ch < num_channels; ch++) { - ret = st->chip_info->scale_setup_cb(st, &chans[ch + 1], ch); + if (st->chip_info->oversampling_avail) + chan->info_mask_shared_by_all |= + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + } + + if (!slow_bus) + chan->info_mask_shared_by_all |= + BIT(IIO_CHAN_INFO_SAMP_FREQ); + + ret = st->chip_info->scale_setup_cb(indio_dev, chan); if (ret) return ret; } + if (slow_bus) + channels[i] = (struct iio_chan_spec)IIO_CHAN_SOFT_TIMESTAMP(i); + + indio_dev->channels = channels; + return 0; } @@ -1190,11 +1311,19 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, st = iio_priv(indio_dev); dev_set_drvdata(dev, indio_dev); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + st->dev = dev; - mutex_init(&st->lock); st->bops = bops; st->base_address = base_address; st->oversampling = 1; + st->sw_mode_en = device_property_read_bool(dev, "adi,sw-mode"); + + if (st->sw_mode_en && !chip_info->sw_setup_cb) + return dev_err_probe(dev, -EINVAL, + "Software mode is not supported for this chip\n"); ret = devm_regulator_get_enable(dev, "avcc"); if (ret) @@ -1223,10 +1352,21 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, else indio_dev->info = &ad7606_info_no_os_or_range; } - indio_dev->modes = INDIO_DIRECT_MODE; + + /* AXI ADC backend doesn't support single read. */ + indio_dev->modes = st->bops->iio_backend_config ? 0 : INDIO_DIRECT_MODE; indio_dev->name = chip_info->name; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; + + /* Using spi-engine with offload support ? */ + if (st->bops->offload_config) { + ret = st->bops->offload_config(dev, indio_dev); + if (ret) + return ret; + } + + ret = ad7606_probe_channels(indio_dev); + if (ret) + return ret; ret = ad7606_reset(st); if (ret) @@ -1238,19 +1378,8 @@ 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) { + if (!st->gpio_convst || st->offload_en) { st->cnvst_pwm = devm_pwm_get(dev, NULL); if (IS_ERR(st->cnvst_pwm)) return PTR_ERR(st->cnvst_pwm); @@ -1280,8 +1409,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, * If there is a backend, the PWM should not overpass the maximum sampling * frequency the chip supports. */ - ret = ad7606_set_sampling_freq(st, - chip_info->max_samplerate ? : 2 * KILO); + ret = ad7606_set_sampling_freq(st, chip_info->max_samplerate); if (ret) return ret; @@ -1290,8 +1418,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return ret; indio_dev->setup_ops = &ad7606_backend_buffer_ops; - } else { - + } else if (!st->offload_en) { /* Reserve the PWM use only for backend (force gpio_convst definition) */ if (!st->gpio_convst) return dev_err_probe(dev, -EINVAL, @@ -1326,6 +1453,15 @@ 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; + + /* Offload needs 1 DOUT line, applying this setting in sw_setup_cb. */ + if (st->sw_mode_en || st->offload_en) { + indio_dev->info = &ad7606_info_sw_mode; + st->chip_info->sw_setup_cb(indio_dev); + } + 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 998814a92b82..441e62c521bc 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -10,102 +10,78 @@ #define AD760X_MAX_CHANNELS 16 -#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, bits) { \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = num, \ - .address = num, \ - .info_mask_separate = mask_sep, \ - .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), \ - .scan_index = num, \ - .scan_type = { \ - .sign = 's', \ - .realbits = (bits), \ - .storagebits = (bits) > 16 ? 32 : 16, \ - .endianness = IIO_CPU, \ - }, \ -} - -#define AD7605_CHANNEL(num) \ - AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \ - BIT(IIO_CHAN_INFO_SCALE), 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) - -#define AD7616_CHANNEL(num) AD7606_SW_CHANNEL(num, 16) - -#define AD7606_BI_CHANNEL(num) \ - AD760X_CHANNEL(num, 0, \ - BIT(IIO_CHAN_INFO_SCALE), \ - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 16) +#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 struct ad7606_state; -typedef int (*ad7606_scale_setup_cb_t)(struct ad7606_state *st, - struct iio_chan_spec *chan, int ch); +typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev, + struct iio_chan_spec *chan); +typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev); /** * struct ad7606_chip_info - chip specific information - * @channels: channel specification - * @max_samplerate: maximum supported samplerate - * @name device name - * @num_channels: number of channels - * @num_adc_channels the number of channels the ADC actually inputs. + * @max_samplerate: maximum supported sample rate + * @name: device name + * @bits: data width in bits + * @num_adc_channels: the number of physical voltage inputs * @scale_setup_cb: callback to setup the scales for each channel - * @oversampling_avail pointer to the array which stores the available + * @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 - * @os_req_reset some devices require a reset to update oversampling - * @init_delay_ms required delay in milliseconds for initialization + * @oversampling_num: number of elements stored in oversampling_avail array + * @os_req_reset: some devices require a reset to update oversampling + * @init_delay_ms: required delay in milliseconds for initialization * after a restart + * @offload_storagebits: storage bits used by the offload hw implementation */ struct ad7606_chip_info { - const struct iio_chan_spec *channels; unsigned int max_samplerate; const char *name; + unsigned int bits; 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; unsigned long init_delay_ms; + u8 offload_storagebits; }; /** * struct ad7606_chan_scale - channel scale configuration - * @scale_avail pointer to the array which stores the available scales - * @num_scales number of elements stored in the scale_avail array - * @range voltage range selection, selects which scale to apply - * @reg_offset offset for the register value, to be applied when + * @scale_avail: pointer to the array which stores the available scales + * @num_scales: number of elements stored in the scale_avail array + * @range: voltage range selection, selects which scale to apply + * @reg_offset: offset for the register value, to be applied when * writing the value of 'range' to the register value */ struct ad7606_chan_scale { @@ -118,32 +94,35 @@ struct ad7606_chan_scale { /** * struct ad7606_state - driver instance specific data - * @dev pointer to kernel device - * @chip_info entry in the table of chips that describes this device - * @bops bus operations (SPI or parallel) - * @chan_scales scale configuration for channels - * @oversampling oversampling selection - * @cnvst_pwm pointer to the PWM device connected to the cnvst pin - * @base_address address from where to read data in parallel operation - * @sw_mode_en software mode enabled - * @oversampling_avail pointer to the array which stores the available + * @dev: pointer to kernel device + * @chip_info: entry in the table of chips that describes this device + * @bops: bus operations (SPI or parallel) + * @chan_scales: scale configuration for channels + * @oversampling: oversampling selection + * @cnvst_pwm: pointer to the PWM device connected to the cnvst pin + * @base_address: address from where to read data in parallel operation + * @sw_mode_en: software mode enabled + * @oversampling_avail: pointer to the array which stores the available * oversampling ratios. - * @num_os_ratios number of elements stored in oversampling_avail array - * @write_scale pointer to the function which writes the scale - * @write_os pointer to the function which writes the os - * @lock protect sensor state from concurrent accesses to GPIOs - * @gpio_convst GPIO descriptor for conversion start signal (CONVST) - * @gpio_reset GPIO descriptor for device hard-reset - * @gpio_range GPIO descriptor for range selection - * @gpio_standby GPIO descriptor for stand-by signal (STBY), + * @num_os_ratios: number of elements stored in oversampling_avail array + * @back: pointer to the iio_backend structure, if used + * @write_scale: pointer to the function which writes the scale + * @write_os: pointer to the function which writes the os + * @lock: protect sensor state from concurrent accesses to GPIOs + * @gpio_convst: GPIO descriptor for conversion start signal (CONVST) + * @gpio_reset: GPIO descriptor for device hard-reset + * @gpio_range: GPIO descriptor for range selection + * @gpio_standby: GPIO descriptor for stand-by signal (STBY), * controls power-down mode of device - * @gpio_frstdata GPIO descriptor for reading from device when data + * @gpio_frstdata: GPIO descriptor for reading from device when data * is being read on the first channel - * @gpio_os GPIO descriptors to control oversampling on the device - * @complete completion to indicate end of conversion - * @trig The IIO trigger associated with the device. - * @data buffer for reading data from the device - * @d16 be16 buffer for reading data from the device + * @gpio_os: GPIO descriptors to control oversampling on the device + * @trig: The IIO trigger associated with the device. + * @completion: completion to indicate end of conversion + * @data: buffer for reading data from the device + * @offload_en: SPI offload enabled + * @bus_data: bus-specific variables + * @d16: be16 buffer for reading data from the device */ struct ad7606_state { struct device *dev; @@ -170,54 +149,58 @@ struct ad7606_state { struct iio_trigger *trig; struct completion completion; + bool offload_en; + void *bus_data; + /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. - * 16 * 16-bit samples + 64-bit timestamp - for AD7616 - * 8 * 32-bit samples + 64-bit timestamp - for AD7616C-18 (and similar) + * 16 * 16-bit samples for AD7616 + * 8 * 32-bit samples for AD7616C-18 (and similar) */ - union { - u16 buf16[20]; - u32 buf32[10]; + struct { + union { + u16 buf16[16]; + u32 buf32[8]; + }; + aligned_s64 timestamp; } data __aligned(IIO_DMA_MINALIGN); __be16 d16[2]; }; /** * struct ad7606_bus_ops - driver bus operations - * @iio_backend_config function pointer for configuring the iio_backend for + * @iio_backend_config: function pointer for configuring the iio_backend for * the compatibles that use it - * @read_block function pointer for reading blocks of data + * @read_block: function pointer for reading blocks of data * @sw_mode_config: pointer to a function which configured the device * for software mode - * @reg_read function pointer for reading spi register - * @reg_write function pointer for writing spi register - * @write_mask function pointer for write spi register with mask - * @update_scan_mode function pointer for handling the calls to iio_info's update_scan - * mode when enabling/disabling channels. - * @rd_wr_cmd pointer to the function which calculates the spi address + * @offload_config: function pointer for configuring offload support, + * where any + * @reg_read: function pointer for reading spi register + * @reg_write: function pointer for writing spi register + * @update_scan_mode: function pointer for handling the calls to iio_info's + * update_scan mode when enabling/disabling channels. + * @rd_wr_cmd: pointer to the function which calculates the spi address */ struct ad7606_bus_ops { /* more methods added in future? */ int (*iio_backend_config)(struct device *dev, struct iio_dev *indio_dev); + int (*offload_config)(struct device *dev, struct iio_dev *indio_dev); int (*read_block)(struct device *dev, int num, void *data); int (*sw_mode_config)(struct iio_dev *indio_dev); int (*reg_read)(struct ad7606_state *st, unsigned int addr); 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); + u16 (*rd_wr_cmd)(int addr, char is_write_op); }; /** - * struct ad7606_bus_info - agregate ad7606_chip_info and ad7606_bus_ops - * @chip_info entry in the table of chips that describes this device - * @bops bus operations (SPI or parallel) + * struct ad7606_bus_info - aggregate ad7606_chip_info and ad7606_bus_ops + * @chip_info: entry in the table of chips that describes this device + * @bops: bus operations (SPI or parallel) */ struct ad7606_bus_info { const struct ad7606_chip_info *chip_info; @@ -229,6 +212,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, const struct ad7606_bus_ops *bops); int ad7606_reset(struct ad7606_state *st); +int ad7606_pwm_set_swing(struct ad7606_state *st); +int ad7606_pwm_set_low(struct ad7606_state *st); extern const struct ad7606_chip_info ad7605_4_info; extern const struct ad7606_chip_info ad7606_8_info; 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..634852c4bbd2 100644 --- a/drivers/iio/adc/ad7606_par.c +++ b/drivers/iio/adc/ad7606_par.c @@ -19,19 +19,10 @@ #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), - AD7606_BI_CHANNEL(1), - AD7606_BI_CHANNEL(2), - AD7606_BI_CHANNEL(3), - AD7606_BI_CHANNEL(4), - AD7606_BI_CHANNEL(5), - AD7606_BI_CHANNEL(6), - AD7606_BI_CHANNEL(7), -}; - -static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) +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 +39,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; @@ -80,15 +72,34 @@ static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio return ret; } - indio_dev->channels = ad7606b_bi_channels; - indio_dev->num_channels = 8; - 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 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, }; static int ad7606_par16_read_block(struct device *dev, @@ -211,6 +222,8 @@ static const struct platform_device_id ad7606_driver_ids[] = { { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_info, }, { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_info, }, { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_info, }, + { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_info }, + { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_info }, { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_info, }, { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_info, }, { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_info, }, @@ -224,6 +237,8 @@ static const struct of_device_id ad7606_of_match[] = { { .compatible = "adi,ad7606-6", .data = &ad7606_6_info }, { .compatible = "adi,ad7606-8", .data = &ad7606_8_info }, { .compatible = "adi,ad7606b", .data = &ad7606b_info }, + { .compatible = "adi,ad7606c-16", .data = &ad7606c_16_info }, + { .compatible = "adi,ad7606c-18", .data = &ad7606c_18_info }, { .compatible = "adi,ad7607", .data = &ad7607_info }, { .compatible = "adi,ad7608", .data = &ad7608_info }, { .compatible = "adi,ad7609", .data = &ad7609_info }, diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index e2c147525706..f28e4ca37707 100644 --- a/drivers/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -5,104 +5,43 @@ * Copyright 2011 Analog Devices Inc. */ +#include <linux/bitmap.h> #include <linux/err.h> +#include <linux/math.h> #include <linux/module.h> +#include <linux/pwm.h> +#include <linux/spi/offload/consumer.h> +#include <linux/spi/offload/provider.h> #include <linux/spi/spi.h> #include <linux/types.h> +#include <linux/units.h> +#include <linux/iio/buffer-dmaengine.h> #include <linux/iio/iio.h> -#include "ad7606.h" - -#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), - AD7616_CHANNEL(1), - AD7616_CHANNEL(2), - AD7616_CHANNEL(3), - AD7616_CHANNEL(4), - AD7616_CHANNEL(5), - AD7616_CHANNEL(6), - AD7616_CHANNEL(7), - AD7616_CHANNEL(8), - AD7616_CHANNEL(9), - AD7616_CHANNEL(10), - AD7616_CHANNEL(11), - AD7616_CHANNEL(12), - AD7616_CHANNEL(13), - AD7616_CHANNEL(14), - AD7616_CHANNEL(15), -}; +#include <dt-bindings/iio/adc/adi,ad7606.h> -static const struct iio_chan_spec ad7606b_sw_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_SW_CHANNEL(0, 16), - AD7606_SW_CHANNEL(1, 16), - AD7606_SW_CHANNEL(2, 16), - AD7606_SW_CHANNEL(3, 16), - AD7606_SW_CHANNEL(4, 16), - AD7606_SW_CHANNEL(5, 16), - AD7606_SW_CHANNEL(6, 16), - AD7606_SW_CHANNEL(7, 16), -}; +#include "ad7606.h" -static const struct iio_chan_spec ad7606c_18_sw_channels[] = { - IIO_CHAN_SOFT_TIMESTAMP(8), - AD7606_SW_CHANNEL(0, 18), - AD7606_SW_CHANNEL(1, 18), - AD7606_SW_CHANNEL(2, 18), - AD7606_SW_CHANNEL(3, 18), - AD7606_SW_CHANNEL(4, 18), - AD7606_SW_CHANNEL(5, 18), - AD7606_SW_CHANNEL(6, 18), - AD7606_SW_CHANNEL(7, 18), -}; +#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */ -static const unsigned int ad7606B_oversampling_avail[9] = { - 1, 2, 4, 8, 16, 32, 64, 128, 256 +struct spi_bus_data { + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_transfer offload_xfer; + struct spi_message offload_msg; }; -static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp) +static u16 ad7616_spi_rd_wr_cmd(int addr, char is_write_op) { /* * The address of register consist of one w/r bit * 6 bits of address followed by one reserved bit. */ - return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7); + return ((addr & 0x7F) << 1) | ((is_write_op & 0x1) << 7); } -static u16 ad7606B_spi_rd_wr_cmd(int addr, char is_write_op) +static u16 ad7606b_spi_rd_wr_cmd(int addr, char is_write_op) { /* * The address of register consists of one bit which @@ -165,7 +104,7 @@ static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr) { .tx_buf = &st->d16[0], .len = 2, - .cs_change = 0, + .cs_change = 1, }, { .rx_buf = &st->d16[1], .len = 2, @@ -189,188 +128,275 @@ static int ad7606_spi_reg_write(struct ad7606_state *st, struct spi_device *spi = to_spi_device(st->dev); st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) | - (val & 0x1FF)); + (val & 0xFF)); 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) +static int ad7606b_sw_mode_config(struct iio_dev *indio_dev) { - int readval; - - readval = st->bops->reg_read(st, addr); - if (readval < 0) - return readval; - - readval &= ~mask; - readval |= val; + struct ad7606_state *st = iio_priv(indio_dev); - return st->bops->reg_write(st, addr, readval); + /* Configure device spi to output on a single channel */ + return st->bops->reg_write(st, AD7606_CONFIGURATION_REGISTER, + AD7606_SINGLE_DOUT); } -static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) +static const struct spi_offload_config ad7606_spi_offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static int ad7606_spi_offload_buffer_postenable(struct iio_dev *indio_dev) { + const struct iio_scan_type *scan_type; struct ad7606_state *st = iio_priv(indio_dev); - unsigned int ch_addr, mode, ch_index; + struct spi_bus_data *bus_data = st->bus_data; + struct spi_transfer *xfer = &bus_data->offload_xfer; + struct spi_device *spi = to_spi_device(st->dev); + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + int ret; + scan_type = &indio_dev->channels[0].scan_type; + xfer->bits_per_word = scan_type->realbits; + xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; /* - * 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... + * Using SPI offload, storagebits are related to the spi-engine + * hw implementation, can be 16 or 32, so can't be used to compute + * struct spi_transfer.len. Using realbits instead. */ - ch_index = ch >> 1; + xfer->len = (scan_type->realbits > 16 ? 4 : 2) * + st->chip_info->num_adc_channels; - ch_addr = AD7616_RANGE_CH_ADDR(ch_index); + spi_message_init_with_transfers(&bus_data->offload_msg, xfer, 1); + bus_data->offload_msg.offload = bus_data->offload; - 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; + ret = spi_optimize_message(spi, &bus_data->offload_msg); + if (ret) { + dev_err(st->dev, "failed to prepare offload, err: %d\n", ret); + return ret; + } - /* 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); -} + ret = spi_offload_trigger_enable(bus_data->offload, + bus_data->offload_trigger, + &config); + if (ret) + goto err_unoptimize_message; -static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val) -{ - struct ad7606_state *st = iio_priv(indio_dev); + ret = ad7606_pwm_set_swing(st); + if (ret) + goto err_offload_exit_conversion_mode; - return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER, - AD7616_OS_MASK, val << 2); -} + return 0; -static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val) -{ - struct ad7606_state *st = iio_priv(indio_dev); +err_offload_exit_conversion_mode: + spi_offload_trigger_disable(bus_data->offload, + bus_data->offload_trigger); - return ad7606_spi_write_mask(st, - AD7606_RANGE_CH_ADDR(ch), - AD7606_RANGE_CH_MSK(ch), - AD7606_RANGE_CH_MODE(ch, val)); -} +err_unoptimize_message: + spi_unoptimize_message(&bus_data->offload_msg); -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); + return ret; } -static int ad7616_sw_mode_config(struct iio_dev *indio_dev) +static int ad7606_spi_offload_buffer_predisable(struct iio_dev *indio_dev) { struct ad7606_state *st = iio_priv(indio_dev); + struct spi_bus_data *bus_data = st->bus_data; + int ret; - /* - * Scale can be configured individually for each channel - * in software mode. - */ - indio_dev->channels = ad7616_sw_channels; + ret = ad7606_pwm_set_low(st); + if (ret) + return ret; - st->write_scale = ad7616_write_scale_sw; - st->write_os = &ad7616_write_os_sw; + spi_offload_trigger_disable(bus_data->offload, + bus_data->offload_trigger); + spi_unoptimize_message(&bus_data->offload_msg); - /* 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) +static const struct iio_buffer_setup_ops ad7606_offload_buffer_setup_ops = { + .postenable = ad7606_spi_offload_buffer_postenable, + .predisable = ad7606_spi_offload_buffer_predisable, +}; + +static bool ad7606_spi_offload_trigger_match( + struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) { - struct ad7606_state *st = iio_priv(indio_dev); - DECLARE_BITMAP(os, 3); + if (type != SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; - 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 + * Requires 1 arg: + * args[0] is the trigger event. */ - 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); + if (nargs != 1 || args[0] != AD7606_TRIGGER_EVENT_BUSY) + return false; - st->write_scale = ad7606_write_scale_sw; - st->write_os = &ad7606_write_os_sw; + return true; +} - /* Configure device spi to output on a single channel */ - st->bops->reg_write(st, - AD7606_CONFIGURATION_REGISTER, - AD7606_SINGLE_DOUT); +static int ad7606_spi_offload_trigger_request( + struct spi_offload_trigger *trigger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* Should already be validated by match, but just in case. */ + if (nargs != 1) + return -EINVAL; - /* - * Scale can be configured individually for each channel - * in software mode. - */ - indio_dev->channels = ad7606b_sw_channels; + return 0; +} + +static int ad7606_spi_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; } -static int ad7606c_18_sw_mode_config(struct iio_dev *indio_dev) +static const struct spi_offload_trigger_ops ad7606_offload_trigger_ops = { + .match = ad7606_spi_offload_trigger_match, + .request = ad7606_spi_offload_trigger_request, + .validate = ad7606_spi_offload_trigger_validate, +}; + +static int ad7606_spi_offload_probe(struct device *dev, + struct iio_dev *indio_dev) { + struct ad7606_state *st = iio_priv(indio_dev); + struct spi_device *spi = to_spi_device(dev); + struct spi_bus_data *bus_data; + struct dma_chan *rx_dma; + struct spi_offload_trigger_info trigger_info = { + .fwnode = dev_fwnode(dev), + .ops = &ad7606_offload_trigger_ops, + .priv = st, + }; int ret; - ret = ad7606B_sw_mode_config(indio_dev); + bus_data = devm_kzalloc(dev, sizeof(*bus_data), GFP_KERNEL); + if (!bus_data) + return -ENOMEM; + st->bus_data = bus_data; + + bus_data->offload = devm_spi_offload_get(dev, spi, + &ad7606_spi_offload_config); + ret = PTR_ERR_OR_ZERO(bus_data->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to get SPI offload\n"); + /* Allow main ad7606_probe function to continue. */ + if (ret == -ENODEV) + return 0; + + ret = devm_spi_offload_trigger_register(dev, &trigger_info); if (ret) - return ret; + return dev_err_probe(dev, ret, + "failed to register offload trigger\n"); + + bus_data->offload_trigger = devm_spi_offload_trigger_get(dev, + bus_data->offload, SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(bus_data->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(bus_data->offload_trigger), + "failed to get offload trigger\n"); + + /* TODO: PWM setup should be ok, done for the backend. PWM mutex ? */ + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + bus_data->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, + "failed to setup offload RX DMA\n"); - indio_dev->channels = ad7606c_18_sw_channels; + /* Use offload ops. */ + indio_dev->setup_ops = &ad7606_offload_buffer_setup_ops; + + st->offload_en = true; + + return 0; +} + +static int ad7606_spi_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + if (st->offload_en) { + unsigned int num_adc_ch = st->chip_info->num_adc_channels; + + /* + * SPI offload requires that all channels are enabled since + * there isn't a way to selectively disable channels that get + * read (this is simultaneous sampling ADC) and the DMA buffer + * has no way of demuxing the data to filter out unwanted + * channels. + */ + if (bitmap_weight(scan_mask, num_adc_ch) != num_adc_ch) + return -EINVAL; + } return 0; } static const struct ad7606_bus_ops ad7606_spi_bops = { + .offload_config = ad7606_spi_offload_probe, .read_block = ad7606_spi_read_block, + .update_scan_mode = ad7606_spi_update_scan_mode, }; static const struct ad7606_bus_ops ad7607_spi_bops = { + .offload_config = ad7606_spi_offload_probe, .read_block = ad7606_spi_read_block14to16, + .update_scan_mode = ad7606_spi_update_scan_mode, }; static const struct ad7606_bus_ops ad7608_spi_bops = { + .offload_config = ad7606_spi_offload_probe, .read_block = ad7606_spi_read_block18to32, + .update_scan_mode = ad7606_spi_update_scan_mode, }; static const struct ad7606_bus_ops ad7616_spi_bops = { + .offload_config = ad7606_spi_offload_probe, .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, + .update_scan_mode = ad7606_spi_update_scan_mode, }; static const struct ad7606_bus_ops ad7606b_spi_bops = { + .offload_config = ad7606_spi_offload_probe, .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, + .rd_wr_cmd = ad7606b_spi_rd_wr_cmd, + .sw_mode_config = ad7606b_sw_mode_config, + .update_scan_mode = ad7606_spi_update_scan_mode, }; static const struct ad7606_bus_ops ad7606c_18_spi_bops = { + .offload_config = ad7606_spi_offload_probe, .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, + .rd_wr_cmd = ad7606b_spi_rd_wr_cmd, + .sw_mode_config = ad7606b_sw_mode_config, + .update_scan_mode = ad7606_spi_update_scan_mode, }; static const struct ad7606_bus_info ad7605_4_bus_info = { @@ -483,3 +509,4 @@ MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); MODULE_IMPORT_NS("IIO_AD7606"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/ad7625.c b/drivers/iio/adc/ad7625.c index aefe3bf75c91..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; } @@ -477,12 +480,12 @@ static int devm_ad7625_pwm_get(struct device *dev, ref_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(ref_clk)) return dev_err_probe(dev, PTR_ERR(ref_clk), - "failed to get ref_clk"); + "failed to get ref_clk\n"); ref_clk_rate_hz = clk_get_rate(ref_clk); if (!ref_clk_rate_hz) return dev_err_probe(dev, -EINVAL, - "failed to get ref_clk rate"); + "failed to get ref_clk rate\n"); st->ref_clk_rate_hz = ref_clk_rate_hz; @@ -533,7 +536,7 @@ static int devm_ad7625_regulator_setup(struct device *dev, if (!st->info->has_internal_vref && !st->have_refin && !ref_mv) return dev_err_probe(dev, -EINVAL, - "Need either REFIN or REF"); + "Need either REFIN or REF\n"); if (st->have_refin && ref_mv) return dev_err_probe(dev, -EINVAL, @@ -623,7 +626,7 @@ static int ad7625_probe(struct platform_device *pdev) st->back = devm_iio_backend_get(dev, NULL); if (IS_ERR(st->back)) return dev_err_probe(dev, PTR_ERR(st->back), - "failed to get IIO backend"); + "failed to get IIO backend\n"); ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); if (ret) @@ -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..8b414a102864 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -4,14 +4,17 @@ * * Copyright 2017 Analog Devices Inc. */ +#include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/completion.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/kernel.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/sysfs.h> #include <linux/spi/spi.h> @@ -53,12 +56,15 @@ #define AD7768_REG_SPI_DIAG_ENABLE 0x28 #define AD7768_REG_ADC_DIAG_ENABLE 0x29 #define AD7768_REG_DIG_DIAG_ENABLE 0x2A -#define AD7768_REG_ADC_DATA 0x2C +#define AD7768_REG24_ADC_DATA 0x2C #define AD7768_REG_MASTER_STATUS 0x2D #define AD7768_REG_SPI_DIAG_STATUS 0x2E #define AD7768_REG_ADC_DIAG_STATUS 0x2F #define AD7768_REG_DIG_DIAG_STATUS 0x30 #define AD7768_REG_MCLK_COUNTER 0x31 +#define AD7768_REG_COEFF_CONTROL 0x32 +#define AD7768_REG24_COEFF_DATA 0x33 +#define AD7768_REG_ACCESS_KEY 0x34 /* AD7768_REG_POWER_CLOCK */ #define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4) @@ -76,9 +82,6 @@ #define AD7768_CONV_MODE_MSK GENMASK(2, 0) #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) -#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F)) -#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F) - enum ad7768_conv_mode { AD7768_CONTINUOUS, AD7768_ONE_SHOT, @@ -142,7 +145,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, @@ -153,14 +156,16 @@ static const struct iio_chan_spec ad7768_channels[] = { struct ad7768_state { struct spi_device *spi; + struct regmap *regmap; + struct regmap *regmap24; struct regulator *vref; - struct mutex lock; struct clk *mclk; unsigned int mclk_freq; unsigned int samp_freq; struct completion completion; struct iio_trigger *trig; struct gpio_desc *gpio_sync_in; + struct gpio_desc *gpio_reset; const char *labels[ARRAY_SIZE(ad7768_channels)]; /* * DMA (thus cache coherency maintenance) may require the @@ -169,53 +174,107 @@ struct ad7768_state { union { struct { __be32 chan; - s64 timestamp; + aligned_s64 timestamp; } scan; __be32 d32; u8 d8[2]; } data __aligned(IIO_DMA_MINALIGN); }; -static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr, - unsigned int len) -{ - unsigned int shift; - int ret; +static const struct regmap_range ad7768_regmap_rd_ranges[] = { + regmap_reg_range(AD7768_REG_CHIP_TYPE, AD7768_REG_CHIP_GRADE), + regmap_reg_range(AD7768_REG_SCRATCH_PAD, AD7768_REG_SCRATCH_PAD), + regmap_reg_range(AD7768_REG_VENDOR_L, AD7768_REG_VENDOR_H), + regmap_reg_range(AD7768_REG_INTERFACE_FORMAT, AD7768_REG_GAIN_LO), + regmap_reg_range(AD7768_REG_SPI_DIAG_ENABLE, AD7768_REG_DIG_DIAG_ENABLE), + regmap_reg_range(AD7768_REG_MASTER_STATUS, AD7768_REG_COEFF_CONTROL), + regmap_reg_range(AD7768_REG_ACCESS_KEY, AD7768_REG_ACCESS_KEY), +}; - shift = 32 - (8 * len); - st->data.d8[0] = AD7768_RD_FLAG_MSK(addr); +static const struct regmap_access_table ad7768_regmap_rd_table = { + .yes_ranges = ad7768_regmap_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap_rd_ranges), +}; - ret = spi_write_then_read(st->spi, st->data.d8, 1, - &st->data.d32, len); - if (ret < 0) - return ret; +static const struct regmap_range ad7768_regmap_wr_ranges[] = { + regmap_reg_range(AD7768_REG_SCRATCH_PAD, AD7768_REG_SCRATCH_PAD), + regmap_reg_range(AD7768_REG_INTERFACE_FORMAT, AD7768_REG_GPIO_WRITE), + regmap_reg_range(AD7768_REG_OFFSET_HI, AD7768_REG_GAIN_LO), + regmap_reg_range(AD7768_REG_SPI_DIAG_ENABLE, AD7768_REG_DIG_DIAG_ENABLE), + regmap_reg_range(AD7768_REG_SPI_DIAG_STATUS, AD7768_REG_SPI_DIAG_STATUS), + regmap_reg_range(AD7768_REG_COEFF_CONTROL, AD7768_REG_COEFF_CONTROL), + regmap_reg_range(AD7768_REG_ACCESS_KEY, AD7768_REG_ACCESS_KEY), +}; - return (be32_to_cpu(st->data.d32) >> shift); -} +static const struct regmap_access_table ad7768_regmap_wr_table = { + .yes_ranges = ad7768_regmap_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap_wr_ranges), +}; + +static const struct regmap_config ad7768_regmap_config = { + .name = "ad7768-1-8", + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = BIT(6), + .rd_table = &ad7768_regmap_rd_table, + .wr_table = &ad7768_regmap_wr_table, + .max_register = AD7768_REG_ACCESS_KEY, + .use_single_write = true, + .use_single_read = true, +}; -static int ad7768_spi_reg_write(struct ad7768_state *st, - unsigned int addr, - unsigned int val) +static const struct regmap_range ad7768_regmap24_rd_ranges[] = { + regmap_reg_range(AD7768_REG24_ADC_DATA, AD7768_REG24_ADC_DATA), + regmap_reg_range(AD7768_REG24_COEFF_DATA, AD7768_REG24_COEFF_DATA), +}; + +static const struct regmap_access_table ad7768_regmap24_rd_table = { + .yes_ranges = ad7768_regmap24_rd_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap24_rd_ranges), +}; + +static const struct regmap_range ad7768_regmap24_wr_ranges[] = { + regmap_reg_range(AD7768_REG24_COEFF_DATA, AD7768_REG24_COEFF_DATA), +}; + +static const struct regmap_access_table ad7768_regmap24_wr_table = { + .yes_ranges = ad7768_regmap24_wr_ranges, + .n_yes_ranges = ARRAY_SIZE(ad7768_regmap24_wr_ranges), +}; + +static const struct regmap_config ad7768_regmap24_config = { + .name = "ad7768-1-24", + .reg_bits = 8, + .val_bits = 24, + .read_flag_mask = BIT(6), + .rd_table = &ad7768_regmap24_rd_table, + .wr_table = &ad7768_regmap24_wr_table, + .max_register = AD7768_REG24_COEFF_DATA, +}; + +static int ad7768_send_sync_pulse(struct ad7768_state *st) { - st->data.d8[0] = AD7768_WR_FLAG_MSK(addr); - st->data.d8[1] = val & 0xFF; + /* + * The datasheet specifies a minimum SYNC_IN pulse width of 1.5 × Tmclk, + * where Tmclk is the MCLK period. The supported MCLK frequencies range + * from 0.6 MHz to 17 MHz, which corresponds to a minimum SYNC_IN pulse + * width of approximately 2.5 µs in the worst-case scenario (0.6 MHz). + * + * Add a delay to ensure the pulse width is always sufficient to + * trigger synchronization. + */ + gpiod_set_value_cansleep(st->gpio_sync_in, 1); + fsleep(3); + gpiod_set_value_cansleep(st->gpio_sync_in, 0); - return spi_write(st->spi, st->data.d8, 2); + return 0; } static int ad7768_set_mode(struct ad7768_state *st, enum ad7768_conv_mode mode) { - int regval; - - regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1); - if (regval < 0) - return regval; - - regval &= ~AD7768_CONV_MODE_MSK; - regval |= AD7768_CONV_MODE(mode); - - return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval); + return regmap_update_bits(st->regmap, AD7768_REG_CONVERSION, + AD7768_CONV_MODE_MSK, AD7768_CONV_MODE(mode)); } static int ad7768_scan_direct(struct iio_dev *indio_dev) @@ -234,9 +293,10 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) if (!ret) return -ETIMEDOUT; - readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); - if (readval < 0) - return readval; + ret = regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &readval); + if (ret) + return ret; + /* * Any SPI configuration of the AD7768-1 can only be * performed in continuous conversion mode. @@ -256,18 +316,27 @@ 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; + + ret = -EINVAL; if (readval) { - ret = ad7768_spi_reg_read(st, reg, 1); - if (ret < 0) - goto err_unlock; - *readval = ret; - ret = 0; + if (regmap_check_range_table(st->regmap, reg, &ad7768_regmap_rd_table)) + ret = regmap_read(st->regmap, reg, readval); + + if (regmap_check_range_table(st->regmap24, reg, &ad7768_regmap24_rd_table)) + ret = regmap_read(st->regmap24, reg, readval); + } else { - ret = ad7768_spi_reg_write(st, reg, writeval); + if (regmap_check_range_table(st->regmap, reg, &ad7768_regmap_wr_table)) + ret = regmap_write(st->regmap, reg, writeval); + + if (regmap_check_range_table(st->regmap24, reg, &ad7768_regmap24_wr_table)) + ret = regmap_write(st->regmap24, reg, writeval); + } -err_unlock: - mutex_unlock(&st->lock); + + iio_device_release_direct(indio_dev); return ret; } @@ -283,15 +352,12 @@ static int ad7768_set_dig_fil(struct ad7768_state *st, else mode = AD7768_DIG_FIL_DEC_RATE(dec_rate); - ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode); + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode); if (ret < 0) return ret; /* A sync-in pulse is required every time the filter dec rate changes */ - gpiod_set_value(st->gpio_sync_in, 1); - gpiod_set_value(st->gpio_sync_in, 0); - - return 0; + return ad7768_send_sync_pulse(st); } static int ad7768_set_freq(struct ad7768_state *st, @@ -320,7 +386,7 @@ static int ad7768_set_freq(struct ad7768_state *st, */ pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) | AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode); - ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode); + ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode); if (ret < 0) return ret; @@ -365,17 +431,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; @@ -441,19 +505,30 @@ static int ad7768_setup(struct ad7768_state *st) { int ret; - /* - * Two writes to the SPI_RESET[1:0] bits are required to initiate - * a software reset. The bits must first be set to 11, and then - * to 10. When the sequence is detected, the reset occurs. - * See the datasheet, page 70. - */ - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3); - if (ret) - return ret; + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); - ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2); - if (ret) - return ret; + if (st->gpio_reset) { + fsleep(10); + gpiod_set_value_cansleep(st->gpio_reset, 0); + fsleep(200); + } else { + /* + * Two writes to the SPI_RESET[1:0] bits are required to initiate + * a software reset. The bits must first be set to 11, and then + * to 10. When the sequence is detected, the reset occurs. + * See the datasheet, page 70. + */ + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x3); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x2); + if (ret) + return ret; + } st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in", GPIOD_OUT_LOW); @@ -471,18 +546,16 @@ 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)); + iio_push_to_buffers_with_ts(indio_dev, &st->data.scan, + sizeof(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; } @@ -509,18 +582,19 @@ static int ad7768_buffer_postenable(struct iio_dev *indio_dev) * continuous read mode. Subsequent data reads do not require an * initial 8-bit write to query the ADC_DATA register. */ - return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01); + return regmap_write(st->regmap, AD7768_REG_INTERFACE_FORMAT, 0x01); } static int ad7768_buffer_predisable(struct iio_dev *indio_dev) { struct ad7768_state *st = iio_priv(indio_dev); + unsigned int unused; /* * To exit continuous read mode, perform a single read of the ADC_DATA * reg (0x2C), which allows further configuration of the device. */ - return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); + return regmap_read(st->regmap24, AD7768_REG24_ADC_DATA, &unused); } static const struct iio_buffer_setup_ops ad7768_buffer_ops = { @@ -563,6 +637,31 @@ static int ad7768_set_channel_label(struct iio_dev *indio_dev, return 0; } +static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + st->trig = devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7768_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + &iio_pollfunc_store_time, + &ad7768_trigger_handler, + &ad7768_buffer_ops); +} + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; @@ -574,8 +673,33 @@ 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->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(st->regmap), + "Failed to initialize regmap"); + + st->regmap24 = devm_regmap_init_spi(spi, &ad7768_regmap24_config); + if (IS_ERR(st->regmap24)) + return dev_err_probe(&spi->dev, PTR_ERR(st->regmap24), + "Failed to initialize regmap24"); + st->vref = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(st->vref)) return PTR_ERR(st->vref); @@ -596,8 +720,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; @@ -610,20 +732,6 @@ static int ad7768_probe(struct spi_device *spi) return ret; } - st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", - indio_dev->name, - iio_device_id(indio_dev)); - if (!st->trig) - return -ENOMEM; - - st->trig->ops = &ad7768_trigger_ops; - iio_trigger_set_drvdata(st->trig, indio_dev); - ret = devm_iio_trigger_register(&spi->dev, st->trig); - if (ret) - return ret; - - indio_dev->trig = iio_trigger_get(st->trig); - init_completion(&st->completion); ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels)); @@ -637,10 +745,7 @@ static int ad7768_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - &iio_pollfunc_store_time, - &ad7768_trigger_handler, - &ad7768_buffer_ops); + ret = ad7768_triggered_buffer_alloc(indio_dev); if (ret) return ret; @@ -649,7 +754,7 @@ static int ad7768_probe(struct spi_device *spi) static const struct spi_device_id ad7768_id_table[] = { { "ad7768-1", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7768_id_table); diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c index 2537dab69a35..845adc510239 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) @@ -572,7 +595,8 @@ static irqreturn_t ad7779_trigger_handler(int irq, void *p) goto exit_handler; } - iio_push_to_buffers_with_timestamp(indio_dev, &st->data, pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + pf->timestamp); exit_handler: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index e1bf13fe2cd7..041fc25e3209 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -254,6 +254,7 @@ static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { .addr_shift = 4, .read_mask = BIT(3), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, }; static int ad7791_read_raw(struct iio_dev *indio_dev, @@ -309,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: @@ -327,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; } @@ -458,7 +464,7 @@ static const struct spi_device_id ad7791_spi_ids[] = { { "ad7789", AD7789 }, { "ad7790", AD7790 }, { "ad7791", AD7791 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index d55c71566707..ccf18ce48e34 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -206,6 +206,7 @@ static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { .addr_shift = 3, .read_mask = BIT(6), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, }; static const struct ad_sd_calib_data ad7793_calib_arr[6] = { @@ -265,7 +266,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, return ret; /* reset the serial interface */ - ret = ad_sd_reset(&st->sd, 32); + ret = ad_sd_reset(&st->sd); if (ret < 0) goto out; usleep_range(500, 2000); /* Wait for at least 500us */ @@ -461,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..0369151c7db1 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -207,8 +207,8 @@ static irqreturn_t ad7923_trigger_handler(int irq, void *p) if (b_sent) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, st->rx_buf, sizeof(st->rx_buf), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -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 a5aea4e9f1a7..7722cf9e8214 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; @@ -75,12 +84,14 @@ struct ad7944_adc { u16 u16; u32 u32; } raw; - u64 timestamp __aligned(8); + aligned_s64 timestamp; } sample __aligned(IIO_DMA_MINALIGN); }; /* 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) @@ -142,11 +190,6 @@ static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc * struct spi_transfer *xfers = adc->xfers; /* - * NB: can get better performance from some SPI controllers if we use - * the same bits_per_word in every transfer. - */ - xfers[0].bits_per_word = chan->scan_type.realbits; - /* * 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. */ @@ -160,11 +203,10 @@ static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc * xfers[1].cs_off = 1; xfers[1].delay.value = t_conv_ns; xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; - xfers[1].bits_per_word = chan->scan_type.realbits; /* 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); @@ -180,11 +222,6 @@ static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc struct spi_transfer *xfers = adc->xfers; /* - * NB: can get better performance from some SPI controllers if we use - * the same bits_per_word in every transfer. - */ - xfers[0].bits_per_word = chan->scan_type.realbits; - /* * CS has to be high for full conversion time to avoid triggering the * busy indication. */ @@ -193,7 +230,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 +265,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 +273,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 +353,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; @@ -287,10 +366,29 @@ static int ad7944_single_conversion(struct ad7944_adc *adc, if (chan->scan_type.sign == 's') *val = sign_extend32(*val, chan->scan_type.realbits - 1); + else + *val &= GENMASK(chan->scan_type.realbits - 1, 0); 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 +398,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 +420,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 +597,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 +631,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 +661,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 +784,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 +886,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/ad7949.c b/drivers/iio/adc/ad7949.c index edd0c3a35ab7..202561cad401 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -308,7 +308,6 @@ static void ad7949_disable_reg(void *reg) static int ad7949_spi_probe(struct spi_device *spi) { - u32 spi_ctrl_mask = spi->controller->bits_per_word_mask; struct device *dev = &spi->dev; const struct ad7949_adc_spec *spec; struct ad7949_adc_chip *ad7949_adc; @@ -337,11 +336,11 @@ static int ad7949_spi_probe(struct spi_device *spi) ad7949_adc->resolution = spec->resolution; /* Set SPI bits per word */ - if (spi_ctrl_mask & SPI_BPW_MASK(ad7949_adc->resolution)) { + if (spi_is_bpw_supported(spi, ad7949_adc->resolution)) { spi->bits_per_word = ad7949_adc->resolution; - } else if (spi_ctrl_mask == SPI_BPW_MASK(16)) { + } else if (spi_is_bpw_supported(spi, 16)) { spi->bits_per_word = 16; - } else if (spi_ctrl_mask == SPI_BPW_MASK(8)) { + } else if (spi_is_bpw_supported(spi, 8)) { spi->bits_per_word = 8; } else { dev_err(dev, "unable to find common BPW with spi controller\n"); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index aa44b4e2542b..9c02f9199139 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; } @@ -960,7 +958,7 @@ static const struct i2c_device_id ad799x_id[] = { { "ad7994", ad7994 }, { "ad7997", ad7997 }, { "ad7998", ad7998 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad799x_id); 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 3fd200b34161..6b3ef7ef403e 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -29,8 +29,11 @@ #define AD_SD_COMM_CHAN_MASK 0x3 #define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_STATUS 0x00 #define AD_SD_REG_DATA 0x03 +#define AD_SD_REG_STATUS_RDY 0x80 + /** * ad_sd_set_comm() - Set communications register * @@ -109,7 +112,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, }, { .rx_buf = val, .len = size, - .cs_change = sigma_delta->bus_locked, + .cs_change = sigma_delta->keep_cs_asserted, }, }; struct spi_message m; @@ -178,13 +181,12 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); * ad_sd_reset() - Reset the serial interface * * @sigma_delta: The sigma delta device - * @reset_length: Number of SCLKs with DIN = 1 * * Returns 0 on success, an error code otherwise. **/ -int ad_sd_reset(struct ad_sigma_delta *sigma_delta, - unsigned int reset_length) +int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { + unsigned int reset_length = sigma_delta->info->num_resetclks; uint8_t *buf; unsigned int size; int ret; @@ -202,6 +204,107 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, } EXPORT_SYMBOL_NS_GPL(ad_sd_reset, "IIO_AD_SIGMA_DELTA"); +static bool ad_sd_disable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + /* It's already off, return false to indicate nothing was changed */ + if (sigma_delta->irq_dis) + return false; + + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->irq_line); + return true; +} + +static void ad_sd_enable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->irq_line); +} + +#define AD_SD_CLEAR_DATA_BUFLEN 9 + +/* Called with `sigma_delta->bus_locked == true` only. */ +static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta) +{ + bool pending_event; + unsigned int data_read_len = BITS_TO_BYTES(sigma_delta->info->num_resetclks); + u8 *data; + struct spi_transfer t[] = { + { + .len = 1, + }, { + .len = data_read_len, + } + }; + struct spi_message m; + int ret; + + /* + * Read RÌ…DÌ…YÌ… pin (if possible) or status register to check if there is an + * old event. + */ + if (sigma_delta->rdy_gpiod) { + pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); + } else { + unsigned int status_reg; + + ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); + if (ret) + return ret; + + pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } + + if (!pending_event) + return 0; + + /* + * In general the size of the data register is unknown. It varies from + * device to device, might be one byte longer if CONTROL.DATA_STATUS is + * set and even varies on some devices depending on which input is + * selected. So send one byte to start reading the data register and + * then just clock for some bytes with DIN (aka MOSI) high to not + * confuse the register access state machine after the data register was + * completely read. Note however that the sequence length must be + * shorter than the reset procedure. + */ + + data = kzalloc(data_read_len + 1, GFP_KERNEL); + if (!data) + return -ENOMEM; + + spi_message_init(&m); + if (sigma_delta->info->has_registers) { + unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; + + data[0] = data_reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + data[0] |= sigma_delta->comm; + t[0].tx_buf = data; + spi_message_add_tail(&t[0], &m); + } + + /* + * The first transferred byte is part of the real data register, + * so this doesn't need to be 0xff. In the remaining + * `data_read_len - 1` bytes are less than $num_resetclks ones. + */ + t[1].tx_buf = data + 1; + data[1] = 0x00; + memset(data + 2, 0xff, data_read_len - 1); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync_locked(sigma_delta->spi, &m); + + kfree(data); + + return ret; +} + int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { @@ -217,16 +320,18 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out; + ret = ad_sigma_delta_set_mode(sigma_delta, mode); if (ret < 0) goto out; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); if (time_left == 0) { - sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->irq_line); + ad_sd_disable_irq(sigma_delta); ret = -EIO; } else { ret = 0; @@ -234,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); @@ -281,21 +387,25 @@ 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; sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out_unlock; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -314,17 +424,17 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, &raw_sample); out: - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); - sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); ad_sigma_delta_disable_one(sigma_delta, chan->address); + +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; @@ -379,7 +489,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return ret; } - samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits, 8); + samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits / 8, 8); samples_buf_size += sizeof(int64_t); samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf, samples_buf_size, GFP_KERNEL); @@ -392,12 +502,15 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) sigma_delta->bus_locked = true; sigma_delta->keep_cs_asserted = true; + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto err_unlock; + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); if (ret) goto err_unlock; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return 0; @@ -414,10 +527,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) reinit_completion(&sigma_delta->completion); wait_for_completion_timeout(&sigma_delta->completion, HZ); - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -477,6 +587,10 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) * byte set to zero. */ ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]); break; + + default: + dev_err_ratelimited(&indio_dev->dev, "Unsupported reg_size: %u\n", reg_size); + goto irq_handled; } /* @@ -516,8 +630,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return IRQ_HANDLED; } @@ -539,12 +652,31 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) { struct ad_sigma_delta *sigma_delta = private; - complete(&sigma_delta->completion); - disable_irq_nosync(irq); - sigma_delta->irq_dis = true; - iio_trigger_poll(sigma_delta->trig); + /* + * AD7124 and a few others use the same physical line for interrupt + * reporting (RÌ…DÌ…YÌ…) and MISO. + * As MISO toggles when reading a register, this likely results in a + * pending interrupt. This has two consequences: a) The irq might + * trigger immediately after it's enabled even though the conversion + * isn't done yet; and b) checking the STATUS register's RÌ…DÌ…YÌ… flag is + * off-limits as reading that would trigger another irq event. + * + * So read the MOSI line as GPIO (if available) and only trigger the irq + * if the line is active. Without such a GPIO assume this is a valid + * interrupt. + * + * Also as disable_irq_nosync() is used to disable the irq, only act if + * the irq wasn't disabled before. + */ + if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && + ad_sd_disable_irq(sigma_delta)) { + complete(&sigma_delta->completion); + iio_trigger_poll(sigma_delta->trig); - return IRQ_HANDLED; + return IRQ_HANDLED; + } + + return IRQ_NONE; } /** @@ -674,10 +806,28 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } - if (info->irq_line) - sigma_delta->irq_line = info->irq_line; - else + spin_lock_init(&sigma_delta->irq_lock); + + 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)) + return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod), + "Failed to find rdy gpio\n"); + + if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) { + sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod); + if (sigma_delta->irq_line < 0) + return sigma_delta->irq_line; + } iio_device_set_drvdata(indio_dev, sigma_delta); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index c7357601f0f8..2dbaa0b5b3d6 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,81 @@ 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); + u32 addr, reg_val; + + 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); + + *val = FIELD_GET(ADI_AXI_REG_VALUE_MASK, reg_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 +508,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 +550,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 +560,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 +607,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 +628,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,12 +676,35 @@ 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 }, - { /* end of list */ } + { .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 }, + { } }; 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 8e5aaf15a921..c3450246730e 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> @@ -329,7 +330,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = { #define AT91_HWFIFO_MAX_SIZE_STR "128" #define AT91_HWFIFO_MAX_SIZE 128 -#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ +#define AT91_SAMA_CHAN_SINGLE(index, num, addr, rbits) \ { \ .type = IIO_VOLTAGE, \ .channel = num, \ @@ -337,7 +338,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .scan_index = index, \ .scan_type = { \ .sign = 'u', \ - .realbits = 14, \ + .realbits = rbits, \ .storagebits = 16, \ }, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ @@ -350,7 +351,13 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .indexed = 1, \ } -#define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \ +#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ + AT91_SAMA_CHAN_SINGLE(index, num, addr, 14) + +#define AT91_SAMA7G5_CHAN_SINGLE(index, num, addr) \ + AT91_SAMA_CHAN_SINGLE(index, num, addr, 16) + +#define AT91_SAMA_CHAN_DIFF(index, num, num2, addr, rbits) \ { \ .type = IIO_VOLTAGE, \ .differential = 1, \ @@ -360,7 +367,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .scan_index = index, \ .scan_type = { \ .sign = 's', \ - .realbits = 14, \ + .realbits = rbits, \ .storagebits = 16, \ }, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ @@ -373,6 +380,12 @@ static const struct at91_adc_reg_layout sama7g5_layout = { .indexed = 1, \ } +#define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \ + AT91_SAMA_CHAN_DIFF(index, num, num2, addr, 14) + +#define AT91_SAMA7G5_CHAN_DIFF(index, num, num2, addr) \ + AT91_SAMA_CHAN_DIFF(index, num, num2, addr, 16) + #define AT91_SAMA5D2_CHAN_TOUCH(num, name, mod) \ { \ .type = IIO_POSITIONRELATIVE, \ @@ -573,15 +586,6 @@ struct at91_adc_temp { u16 saved_oversampling; }; -/* - * Buffer size requirements: - * No channels * bytes_per_channel(2) + timestamp bytes (8) - * Divided by 2 because we need half words. - * We assume 32 channels for now, has to be increased if needed. - * Nobody minds a buffer being too big. - */ -#define AT91_BUFFER_MAX_HWORDS ((32 * 2 + 8) / 2) - struct at91_adc_state { void __iomem *base; int irq; @@ -603,8 +607,8 @@ struct at91_adc_state { struct at91_adc_temp temp_st; struct iio_dev *indio_dev; struct device *dev; - /* Ensure naturally aligned timestamp */ - u16 buffer[AT91_BUFFER_MAX_HWORDS] __aligned(8); + /* We assume 32 channels for now, has to be increased if needed. */ + IIO_DECLARE_BUFFER_WITH_TS(u16, buffer, 32); /* * lock to prevent concurrent 'single conversion' requests through * sysfs. @@ -666,30 +670,30 @@ static const struct iio_chan_spec at91_sama5d2_adc_channels[] = { }; static const struct iio_chan_spec at91_sama7g5_adc_channels[] = { - AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x60), - AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x64), - AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x68), - AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x6c), - AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x70), - AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x74), - AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x78), - AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x7c), - AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x80), - AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x84), - AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x88), - AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x8c), - AT91_SAMA5D2_CHAN_SINGLE(12, 12, 0x90), - AT91_SAMA5D2_CHAN_SINGLE(13, 13, 0x94), - AT91_SAMA5D2_CHAN_SINGLE(14, 14, 0x98), - AT91_SAMA5D2_CHAN_SINGLE(15, 15, 0x9c), - AT91_SAMA5D2_CHAN_DIFF(16, 0, 1, 0x60), - AT91_SAMA5D2_CHAN_DIFF(17, 2, 3, 0x68), - AT91_SAMA5D2_CHAN_DIFF(18, 4, 5, 0x70), - AT91_SAMA5D2_CHAN_DIFF(19, 6, 7, 0x78), - AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x80), - AT91_SAMA5D2_CHAN_DIFF(21, 10, 11, 0x88), - AT91_SAMA5D2_CHAN_DIFF(22, 12, 13, 0x90), - AT91_SAMA5D2_CHAN_DIFF(23, 14, 15, 0x98), + AT91_SAMA7G5_CHAN_SINGLE(0, 0, 0x60), + AT91_SAMA7G5_CHAN_SINGLE(1, 1, 0x64), + AT91_SAMA7G5_CHAN_SINGLE(2, 2, 0x68), + AT91_SAMA7G5_CHAN_SINGLE(3, 3, 0x6c), + AT91_SAMA7G5_CHAN_SINGLE(4, 4, 0x70), + AT91_SAMA7G5_CHAN_SINGLE(5, 5, 0x74), + AT91_SAMA7G5_CHAN_SINGLE(6, 6, 0x78), + AT91_SAMA7G5_CHAN_SINGLE(7, 7, 0x7c), + AT91_SAMA7G5_CHAN_SINGLE(8, 8, 0x80), + AT91_SAMA7G5_CHAN_SINGLE(9, 9, 0x84), + AT91_SAMA7G5_CHAN_SINGLE(10, 10, 0x88), + AT91_SAMA7G5_CHAN_SINGLE(11, 11, 0x8c), + AT91_SAMA7G5_CHAN_SINGLE(12, 12, 0x90), + AT91_SAMA7G5_CHAN_SINGLE(13, 13, 0x94), + AT91_SAMA7G5_CHAN_SINGLE(14, 14, 0x98), + AT91_SAMA7G5_CHAN_SINGLE(15, 15, 0x9c), + AT91_SAMA7G5_CHAN_DIFF(16, 0, 1, 0x60), + AT91_SAMA7G5_CHAN_DIFF(17, 2, 3, 0x68), + AT91_SAMA7G5_CHAN_DIFF(18, 4, 5, 0x70), + AT91_SAMA7G5_CHAN_DIFF(19, 6, 7, 0x78), + AT91_SAMA7G5_CHAN_DIFF(20, 8, 9, 0x80), + AT91_SAMA7G5_CHAN_DIFF(21, 10, 11, 0x88), + AT91_SAMA7G5_CHAN_DIFF(22, 12, 13, 0x90), + AT91_SAMA7G5_CHAN_DIFF(23, 14, 15, 0x98), IIO_CHAN_SOFT_TIMESTAMP(24), AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc), }; @@ -1814,19 +1818,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); + guard(mutex)(&st->lock); - iio_device_release_direct_mode(indio_dev); - - return ret; + return at91_adc_read_info_raw(indio_dev, chan, val); } static void at91_adc_temp_sensor_configure(struct at91_adc_state *st, @@ -1871,14 +1866,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); @@ -1900,9 +1892,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; @@ -1924,10 +1913,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; @@ -1939,7 +1934,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); @@ -1967,28 +1968,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/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 9fd7027623d0..1b49325ec1ce 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -163,14 +163,14 @@ static const struct iio_map axp20x_maps[] = { IIO_MAP("batt_v", "axp20x-battery-power-supply", "batt_v"), IIO_MAP("batt_chrg_i", "axp20x-battery-power-supply", "batt_chrg_i"), IIO_MAP("batt_dischrg_i", "axp20x-battery-power-supply", "batt_dischrg_i"), - { /* sentinel */ } + { } }; static const struct iio_map axp22x_maps[] = { IIO_MAP("batt_v", "axp20x-battery-power-supply", "batt_v"), IIO_MAP("batt_chrg_i", "axp20x-battery-power-supply", "batt_chrg_i"), IIO_MAP("batt_dischrg_i", "axp20x-battery-power-supply", "batt_dischrg_i"), - { /* sentinel */ } + { } }; static struct iio_map axp717_maps[] = { @@ -187,6 +187,7 @@ static struct iio_map axp717_maps[] = { .consumer_channel = "batt_chrg_i", .adc_channel_label = "batt_chrg_i", }, + { } }; /* @@ -1074,7 +1075,7 @@ static const struct of_device_id axp20x_adc_of_match[] = { { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, }, { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, }, { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, axp20x_adc_of_match); @@ -1084,7 +1085,7 @@ static const struct platform_device_id axp20x_adc_id_match[] = { { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, }, { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, }, - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match); diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 45542efc3ece..c8283279c477 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -110,7 +110,7 @@ static const struct iio_map axp288_adc_default_maps[] = { IIO_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), IIO_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), IIO_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), - {}, + { } }; static int axp288_adc_read_channel(int *val, unsigned long address, @@ -207,7 +207,7 @@ static const struct dmi_system_id axp288_adc_ts_bias_override[] = { }, .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, }, - {} + { } }; static int axp288_adc_initialize(struct axp288_adc_info *info) diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index c218acf6c9c6..ba7cbd3b4822 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -942,7 +942,7 @@ static const struct of_device_id cpcap_adc_id_table[] = { .compatible = "motorola,mapphone-cpcap-adc", .data = &mapphone_adc, }, - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c index 0290345ade84..b99291ce2a45 100644 --- a/drivers/iio/adc/da9150-gpadc.c +++ b/drivers/iio/adc/da9150-gpadc.c @@ -296,7 +296,7 @@ static const struct iio_map da9150_gpadc_default_maps[] = { IIO_MAP("VBUS", "da9150-charger", "CHAN_VBUS"), IIO_MAP("TJUNC_CORE", "da9150-charger", "CHAN_TJUNC"), IIO_MAP("VBAT", "da9150-charger", "CHAN_VBAT"), - {}, + { } }; static int da9150_gpadc_probe(struct platform_device *pdev) diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 30328626d9be..9dbd2c87938c 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -66,8 +66,6 @@ struct dln2_adc { /* Demux table */ unsigned int demux_count; struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS]; - /* Precomputed timestamp padding offset and length */ - unsigned int ts_pad_offset, ts_pad_length; }; struct dln2_adc_port_chan { @@ -111,8 +109,6 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2) if (iio_get_masklength(indio_dev) && (*indio_dev->active_scan_mask & 0xff) == 0xff) { dln2_adc_add_demux(dln2, 0, 0, 16); - dln2->ts_pad_offset = 0; - dln2->ts_pad_length = 0; return; } @@ -127,16 +123,6 @@ static void dln2_adc_update_demux(struct dln2_adc *dln2) out_loc += 2; in_loc += 2; } - - if (indio_dev->scan_timestamp) { - size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1; - - dln2->ts_pad_offset = out_loc; - dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc; - } else { - dln2->ts_pad_offset = 0; - dln2->ts_pad_length = 0; - } } static int dln2_adc_get_chan_count(struct dln2_adc *dln2) @@ -328,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; @@ -481,7 +466,7 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct { __le16 values[DLN2_ADC_MAX_CHANNELS]; - int64_t timestamp_space; + aligned_s64 timestamp_space; } data; struct dln2_adc_get_all_vals dev_data; struct dln2_adc *dln2 = iio_priv(indio_dev); @@ -494,6 +479,8 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) if (ret < 0) goto done; + memset(&data, 0, sizeof(data)); + /* Demux operation */ for (i = 0; i < dln2->demux_count; ++i) { t = &dln2->demux[i]; @@ -501,13 +488,8 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) (void *)dev_data.values + t->from, t->length); } - /* Zero padding space between values and timestamp */ - if (dln2->ts_pad_length) - memset((void *)data.values + dln2->ts_pad_offset, - 0, dln2->ts_pad_length); - - iio_push_to_buffers_with_timestamp(indio_dev, &data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data, sizeof(data), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index e911c25d106d..5b16fe737659 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -305,7 +305,7 @@ static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = { { .name = "compare_interval", .read = envelope_show_comp_interval, .write = envelope_store_comp_interval, }, - { /* sentinel */ } + { } }; static const struct iio_chan_spec envelope_detector_iio_channel = { @@ -390,7 +390,7 @@ static int envelope_detector_probe(struct platform_device *pdev) static const struct of_device_id envelope_detector_match[] = { { .compatible = "axentia,tse850-envelope-detector", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, envelope_detector_match); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index b3f037510e35..f8c220f6a7b4 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -372,7 +372,7 @@ static int mx25_gcq_probe(struct platform_device *pdev) static const struct of_device_id mx25_gcq_ids[] = { { .compatible = "fsl,imx25-gcq", }, - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, mx25_gcq_ids); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index 689e34f06987..b4562aae8477 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -351,7 +351,7 @@ static const struct iio_enum hi8435_sensing_mode = { static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), IIO_ENUM_AVAILABLE("sensing_mode", IIO_SHARED_BY_TYPE, &hi8435_sensing_mode), - {}, + { } }; #define HI8435_VOLTAGE_CHANNEL(num) \ diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 8da0419ecfa3..7235fa9e13d5 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -87,7 +87,10 @@ struct hx711_data { * triggered buffer * 2x32-bit channel + 64-bit naturally aligned timestamp */ - u32 buffer[4] __aligned(8); + struct { + u32 channel[2]; + aligned_s64 timestamp; + } buffer; /* * delay after a rising edge on SCK until the data is ready DOUT * this is dependent on the hx711 where the datasheet tells a @@ -361,15 +364,15 @@ static irqreturn_t hx711_trigger(int irq, void *p) mutex_lock(&hx711_data->lock); - memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer)); + memset(&hx711_data->buffer, 0, sizeof(hx711_data->buffer)); iio_for_each_active_channel(indio_dev, i) { - hx711_data->buffer[j] = hx711_reset_read(hx711_data, + hx711_data->buffer.channel[j] = hx711_reset_read(hx711_data, indio_dev->channels[i].channel); j++; } - iio_push_to_buffers_with_timestamp(indio_dev, hx711_data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &hx711_data->buffer, pf->timestamp); mutex_unlock(&hx711_data->lock); diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 828d3fea6d43..09ce71f6e941 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -413,7 +413,7 @@ static const struct iio_info imx7d_adc_iio_info = { static const struct of_device_id imx7d_adc_match[] = { { .compatible = "fsl,imx7d-adc", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, imx7d_adc_match); diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c index 3d19d7d744aa..be13a6ed7e00 100644 --- a/drivers/iio/adc/imx8qxp-adc.c +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -481,7 +481,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(imx8qxp_adc_pm_ops, static const struct of_device_id imx8qxp_adc_match[] = { { .compatible = "nxp,imx8qxp-adc", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, imx8qxp_adc_match); diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c index 002eb19587d6..7feaafd2316f 100644 --- a/drivers/iio/adc/imx93_adc.c +++ b/drivers/iio/adc/imx93_adc.c @@ -464,7 +464,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(imx93_adc_pm_ops, static const struct of_device_id imx93_adc_match[] = { { .compatible = "nxp,imx93-adc", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, imx93_adc_match); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 48c95e12e791..857e1b69d6cd 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -150,7 +150,7 @@ struct ina2xx_chip_info { /* data buffer needs space for channel data and timestamp */ struct { u16 chan[4]; - u64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -766,7 +766,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) chip->scan.chan[i++] = val; } - iio_push_to_buffers_with_timestamp(indio_dev, &chip->scan, time); + iio_push_to_buffers_with_ts(indio_dev, &chip->scan, sizeof(chip->scan), time); return 0; }; diff --git a/drivers/iio/adc/industrialio-adc.c b/drivers/iio/adc/industrialio-adc.c new file mode 100644 index 000000000000..b4057230e749 --- /dev/null +++ b/drivers/iio/adc/industrialio-adc.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Helpers for parsing common ADC information from a firmware node. + * + * Copyright (c) 2025 Matti Vaittinen <mazziesaccount@gmail.com> + */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/types.h> + +#include <linux/iio/adc-helpers.h> +#include <linux/iio/iio.h> + +/** + * devm_iio_adc_device_alloc_chaninfo_se - allocate and fill iio_chan_spec for ADC + * + * Scan the device node for single-ended ADC channel information. Channel ID is + * expected to be found from the "reg" property. Allocate and populate the + * iio_chan_spec structure corresponding to channels that are found. The memory + * for iio_chan_spec structure will be freed upon device detach. + * + * @dev: Pointer to the ADC device. + * @template: Template iio_chan_spec from which the fields of all + * found and allocated channels are initialized. + * @max_chan_id: Maximum value of a channel ID. Use negative value if no + * checking is required. + * @cs: Location where pointer to allocated iio_chan_spec + * should be stored. + * + * Return: Number of found channels on success. Negative value to indicate + * failure. Specifically, -ENOENT if no channel nodes were found. + */ +int devm_iio_adc_device_alloc_chaninfo_se(struct device *dev, + const struct iio_chan_spec *template, + int max_chan_id, + struct iio_chan_spec **cs) +{ + struct iio_chan_spec *chan_array, *chan; + int num_chan, ret; + + num_chan = iio_adc_device_num_channels(dev); + if (num_chan < 0) + return num_chan; + + if (!num_chan) + return -ENOENT; + + chan_array = devm_kcalloc(dev, num_chan, sizeof(*chan_array), + GFP_KERNEL); + if (!chan_array) + return -ENOMEM; + + chan = &chan_array[0]; + + device_for_each_named_child_node_scoped(dev, child, "channel") { + u32 ch; + + ret = fwnode_property_read_u32(child, "reg", &ch); + if (ret) + return ret; + + if (max_chan_id >= 0 && ch > max_chan_id) + return -ERANGE; + + *chan = *template; + chan->channel = ch; + chan++; + } + + *cs = chan_array; + + return num_chan; +} +EXPORT_SYMBOL_NS_GPL(devm_iio_adc_device_alloc_chaninfo_se, "IIO_DRIVER"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); +MODULE_DESCRIPTION("IIO ADC fwnode parsing helpers"); diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index c178850eaaab..101c1a0ce591 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -174,7 +174,7 @@ static const struct iio_map iio_maps[] = { IIO_MAP("CH6", "bcove-temp", "SYSTEMP0"), IIO_MAP("CH7", "bcove-temp", "SYSTEMP1"), IIO_MAP("CH8", "bcove-temp", "SYSTEMP2"), - {} + { } }; static int mrfld_adc_probe(struct platform_device *pdev) @@ -222,7 +222,7 @@ static int mrfld_adc_probe(struct platform_device *pdev) static const struct platform_device_id mrfld_adc_id_table[] = { { .name = "mrfld_bcove_adc" }, - {} + { } }; MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table); diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index 450a243d1f7c..7e5d181ff702 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -188,7 +188,7 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) static const struct of_device_id lpc18xx_adc_match[] = { { .compatible = "nxp,lpc1850-adc" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, lpc18xx_adc_match); diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c index 97c417c3a4eb..a579107fd5c9 100644 --- a/drivers/iio/adc/ltc2471.c +++ b/drivers/iio/adc/ltc2471.c @@ -138,7 +138,7 @@ static int ltc2471_i2c_probe(struct i2c_client *client) static const struct i2c_device_id ltc2471_i2c_id[] = { { "ltc2471", ltc2471 }, { "ltc2473", ltc2473 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id); 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/max1118.c b/drivers/iio/adc/max1118.c index 3d0a7d0eb7ee..7d7001e8e3d9 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -39,7 +39,7 @@ struct max1118 { /* Ensure natural alignment of buffer elements */ struct { u8 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; u8 data __aligned(IIO_DMA_MINALIGN); @@ -188,8 +188,8 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p) adc->scan.channels[i] = ret; i++; } - iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &adc->scan, sizeof(adc->scan), + iio_get_time_ns(indio_dev)); out: mutex_unlock(&adc->lock); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index f0dc4b460903..511b2f14dfaf 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -143,7 +143,7 @@ struct max11410_state { int irq; struct { u32 data __aligned(IIO_DMA_MINALIGN); - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -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: @@ -632,8 +632,8 @@ static irqreturn_t max11410_trigger_handler(int irq, void *p) goto out; } - iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9a0baea08ab6..9dd547e62b6c 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -161,6 +161,7 @@ struct max1363_chip_info { * @vref_uv: Actual (external or internal) reference voltage * @send: function used to send data to the chip * @recv: function used to receive data from the chip + * @data: buffer to store channel data and timestamp */ struct max1363_state { struct i2c_client *client; @@ -186,6 +187,10 @@ struct max1363_state { const char *buf, int count); int (*recv)(const struct i2c_client *client, char *buf, int count); + struct { + u8 buf[MAX1363_MAX_CHANNELS * 2]; + aligned_s64 ts; + } data; }; #define MAX1363_MODE_SINGLE(_num, _mask) { \ @@ -359,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, @@ -421,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; @@ -505,10 +511,10 @@ static const struct iio_event_spec max1363_events[] = { MAX1363_CHAN_U(1, _s1, 1, bits, ev_spec, num_ev_spec), \ MAX1363_CHAN_U(2, _s2, 2, bits, ev_spec, num_ev_spec), \ MAX1363_CHAN_U(3, _s3, 3, bits, ev_spec, num_ev_spec), \ - MAX1363_CHAN_B(0, 1, d0m1, 4, bits, ev_spec, num_ev_spec), \ - MAX1363_CHAN_B(2, 3, d2m3, 5, bits, ev_spec, num_ev_spec), \ - MAX1363_CHAN_B(1, 0, d1m0, 6, bits, ev_spec, num_ev_spec), \ - MAX1363_CHAN_B(3, 2, d3m2, 7, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, ev_spec, num_ev_spec), \ IIO_CHAN_SOFT_TIMESTAMP(8) \ } @@ -526,23 +532,23 @@ static const struct iio_chan_spec max1363_channels[] = /* Applies to max1236, max1237 */ static const enum max1363_modes max1236_mode_list[] = { _s0, _s1, _s2, _s3, - s0to1, s0to2, s0to3, + s0to1, s0to2, s2to3, s0to3, d0m1, d2m3, d1m0, d3m2, d0m1to2m3, d1m0to3m2, - s2to3, }; /* Applies to max1238, max1239 */ static const enum max1363_modes max1238_mode_list[] = { _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, + s6to7, s6to8, s6to9, s6to10, s6to11, s0to7, s0to8, s0to9, s0to10, s0to11, d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, - d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, - d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, - s6to7, s6to8, s6to9, s6to10, s6to11, - d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, }; #define MAX1363_12X_CHANS(bits) { \ @@ -578,16 +584,15 @@ static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12); static const enum max1363_modes max11607_mode_list[] = { _s0, _s1, _s2, _s3, - s0to1, s0to2, s0to3, - s2to3, + s0to1, s0to2, s2to3, + s0to3, d0m1, d2m3, d1m0, d3m2, d0m1to2m3, d1m0to3m2, }; static const enum max1363_modes max11608_mode_list[] = { _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, - s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, - s6to7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s6to7, s0to7, d0m1, d2m3, d4m5, d6m7, d1m0, d3m2, d5m4, d7m6, d0m1to2m3, d0m1to4m5, d0m1to6m7, @@ -603,14 +608,14 @@ static const enum max1363_modes max11608_mode_list[] = { MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \ MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \ MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \ - MAX1363_CHAN_B(0, 1, d0m1, 8, bits, NULL, 0), \ - MAX1363_CHAN_B(2, 3, d2m3, 9, bits, NULL, 0), \ - MAX1363_CHAN_B(4, 5, d4m5, 10, bits, NULL, 0), \ - MAX1363_CHAN_B(6, 7, d6m7, 11, bits, NULL, 0), \ - MAX1363_CHAN_B(1, 0, d1m0, 12, bits, NULL, 0), \ - MAX1363_CHAN_B(3, 2, d3m2, 13, bits, NULL, 0), \ - MAX1363_CHAN_B(5, 4, d5m4, 14, bits, NULL, 0), \ - MAX1363_CHAN_B(7, 6, d7m6, 15, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, NULL, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, NULL, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 14, bits, NULL, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 15, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, NULL, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, NULL, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 20, bits, NULL, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 21, bits, NULL, 0), \ IIO_CHAN_SOFT_TIMESTAMP(16) \ } static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); @@ -942,46 +947,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; } /* @@ -1462,22 +1479,10 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct max1363_state *st = iio_priv(indio_dev); - __u8 *rxbuf; int b_sent; - size_t d_size; unsigned long numvals = bitmap_weight(st->current_mode->modemask, MAX1363_MAX_CHANNELS); - /* Ensure the timestamp is 8 byte aligned */ - if (st->chip_info->bits != 8) - d_size = numvals*2; - else - d_size = numvals; - if (indio_dev->scan_timestamp) { - d_size += sizeof(s64); - if (d_size % sizeof(s64)) - d_size += sizeof(s64) - (d_size % sizeof(s64)); - } /* Monitor mode prevents reading. Whilst not currently implemented * might as well have this test in here in the meantime as it does * no harm. @@ -1485,21 +1490,16 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (numvals == 0) goto done; - rxbuf = kmalloc(d_size, GFP_KERNEL); - if (rxbuf == NULL) - goto done; if (st->chip_info->bits != 8) - b_sent = st->recv(st->client, rxbuf, numvals * 2); + b_sent = st->recv(st->client, st->data.buf, numvals * 2); else - b_sent = st->recv(st->client, rxbuf, numvals); + b_sent = st->recv(st->client, st->data.buf, numvals); if (b_sent < 0) - goto done_free; + goto done; - iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &st->data, sizeof(st->data), + iio_get_time_ns(indio_dev)); -done_free: - kfree(rxbuf); done: iio_trigger_notify_done(indio_dev->trig); @@ -1550,7 +1550,7 @@ static const struct of_device_id max1363_of_match[] = { MAX1363_COMPATIBLE("maxim,max11645", max11645), MAX1363_COMPATIBLE("maxim,max11646", max11646), MAX1363_COMPATIBLE("maxim,max11647", max11647), - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, max1363_of_match); @@ -1671,7 +1671,7 @@ static const struct i2c_device_id max1363_id[] = { MAX1363_ID_TABLE("max11645", max11645), MAX1363_ID_TABLE("max11646", max11646), MAX1363_ID_TABLE("max11647", max11647), - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, max1363_id); 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/max77541-adc.c b/drivers/iio/adc/max77541-adc.c index 21d024bde16b..0aa04d143ad4 100644 --- a/drivers/iio/adc/max77541-adc.c +++ b/drivers/iio/adc/max77541-adc.c @@ -176,7 +176,7 @@ static int max77541_adc_probe(struct platform_device *pdev) static const struct platform_device_id max77541_adc_platform_id[] = { { "max77541-adc" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, max77541_adc_platform_id); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index b097f04172c8..a6f21791c685 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -6,11 +6,13 @@ * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se> */ #include <linux/bitfield.h> -#include <linux/bits.h> +#include <linux/bitops.h> #include <linux/cleanup.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/dev_printk.h> #include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/property.h> @@ -79,6 +81,8 @@ #define MCP3910_CONFIG1_CLKEXT BIT(6) #define MCP3910_CONFIG1_VREFEXT BIT(7) +#define MCP3910_CHANNEL(ch) (MCP3911_REG_CHANNEL0 + (ch)) + #define MCP3910_REG_OFFCAL_CH0 0x0f #define MCP3910_OFFCAL(ch) (MCP3910_REG_OFFCAL_CH0 + (ch) * 6) @@ -110,6 +114,7 @@ struct mcp3911_chip_info { int (*get_offset)(struct mcp3911 *adc, int channel, int *val); int (*set_offset)(struct mcp3911 *adc, int channel, int val); int (*set_scale)(struct mcp3911 *adc, int channel, u32 val); + int (*get_raw)(struct mcp3911 *adc, int channel, int *val); }; struct mcp3911 { @@ -122,7 +127,7 @@ struct mcp3911 { const struct mcp3911_chip_info *chip; struct { u32 channels[MCP39XX_MAX_NUM_CHANNELS]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; u8 tx_buf __aligned(IIO_DMA_MINALIGN); @@ -170,6 +175,18 @@ static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, u32 val, u8 len return mcp3911_write(adc, reg, val, len); } +static int mcp3911_read_s24(struct mcp3911 *const adc, u8 const reg, s32 *const val) +{ + u32 uval; + int const ret = mcp3911_read(adc, reg, &uval, 3); + + if (ret) + return ret; + + *val = sign_extend32(uval, 23); + return ret; +} + static int mcp3910_enable_offset(struct mcp3911 *adc, bool enable) { unsigned int mask = MCP3910_CONFIG0_EN_OFFCAL; @@ -194,6 +211,11 @@ static int mcp3910_set_offset(struct mcp3911 *adc, int channel, int val) return adc->chip->enable_offset(adc, 1); } +static int mcp3910_get_raw(struct mcp3911 *adc, int channel, s32 *val) +{ + return mcp3911_read_s24(adc, MCP3910_CHANNEL(channel), val); +} + static int mcp3911_enable_offset(struct mcp3911 *adc, bool enable) { unsigned int mask = MCP3911_STATUSCOM_EN_OFFCAL; @@ -218,6 +240,11 @@ static int mcp3911_set_offset(struct mcp3911 *adc, int channel, int val) return adc->chip->enable_offset(adc, 1); } +static int mcp3911_get_raw(struct mcp3911 *adc, int channel, s32 *val) +{ + return mcp3911_read_s24(adc, MCP3911_CHANNEL(channel), val); +} + static int mcp3910_get_osr(struct mcp3911 *adc, u32 *val) { int ret; @@ -321,12 +348,9 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev, guard(mutex)(&adc->lock); switch (mask) { case IIO_CHAN_INFO_RAW: - ret = mcp3911_read(adc, - MCP3911_CHANNEL(channel->channel), val, 3); + ret = adc->chip->get_raw(adc, channel->channel, val); if (ret) return ret; - - *val = sign_extend32(*val, 23); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: ret = adc->chip->get_offset(adc, channel->channel, val); @@ -516,8 +540,8 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p) adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]); i++; } - iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &adc->scan, sizeof(adc->scan), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -684,6 +708,7 @@ static const struct iio_trigger_ops mcp3911_trigger_ops = { static int mcp3911_probe(struct spi_device *spi) { struct device *dev = &spi->dev; + struct gpio_desc *gpio_reset; struct iio_dev *indio_dev; struct mcp3911 *adc; bool external_vref; @@ -728,6 +753,22 @@ static int mcp3911_probe(struct spi_device *spi) } dev_dbg(dev, "use device address %i\n", adc->dev_addr); + gpio_reset = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio_reset)) + return dev_err_probe(dev, PTR_ERR(gpio_reset), + "Cannot get reset GPIO\n"); + + if (gpio_reset) { + gpiod_set_value_cansleep(gpio_reset, 0); + + /* + * Settling time after Hard Reset Mode (determined experimentally): + * 330 micro-seconds are too few; 470 micro-seconds are sufficient. + * Just in case, we add some safety factor... + */ + fsleep(600); + } + ret = adc->chip->config(adc, external_vref); if (ret) return ret; @@ -799,6 +840,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3910_get_offset, .set_offset = mcp3910_set_offset, .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, }, [MCP3911] = { .channels = mcp3911_channels, @@ -810,6 +852,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3911_get_offset, .set_offset = mcp3911_set_offset, .set_scale = mcp3911_set_scale, + .get_raw = mcp3911_get_raw, }, [MCP3912] = { .channels = mcp3912_channels, @@ -821,6 +864,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3910_get_offset, .set_offset = mcp3910_set_offset, .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, }, [MCP3913] = { .channels = mcp3913_channels, @@ -832,6 +876,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3910_get_offset, .set_offset = mcp3910_set_offset, .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, }, [MCP3914] = { .channels = mcp3914_channels, @@ -843,6 +888,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3910_get_offset, .set_offset = mcp3910_set_offset, .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, }, [MCP3918] = { .channels = mcp3918_channels, @@ -854,6 +900,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3910_get_offset, .set_offset = mcp3910_set_offset, .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, }, [MCP3919] = { .channels = mcp3919_channels, @@ -865,6 +912,7 @@ static const struct mcp3911_chip_info mcp3911_chip_info[] = { .get_offset = mcp3910_get_offset, .set_offset = mcp3910_set_offset, .set_scale = mcp3910_set_scale, + .get_raw = mcp3910_get_raw, }, }; static const struct of_device_id mcp3911_dt_ids[] = { diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 2d475b43e717..4ff88603e4fc 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -155,10 +155,15 @@ */ #define MESON_SAR_ADC_REG11 0x2c #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) - #define MESON_SAR_ADC_REG11_CMV_SEL BIT(6) - #define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5) - #define MESON_SAR_ADC_REG11_EOC BIT(1) - #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) + #define MESON_SAR_ADC_REG11_CMV_SEL BIT(6) + #define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5) + #define MESON_SAR_ADC_REG11_EOC BIT(1) + #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) + +#define MESON_SAR_ADC_REG12 0x30 + #define MESON_SAR_ADC_REG12_MPLL0_UNKNOWN BIT(0) + #define MESON_SAR_ADC_REG12_MPLL1_UNKNOWN BIT(1) + #define MESON_SAR_ADC_REG12_MPLL2_UNKNOWN BIT(2) #define MESON_SAR_ADC_REG13 0x34 #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) @@ -315,19 +320,18 @@ static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { struct meson_sar_adc_param { bool has_bl30_integration; unsigned long clock_rate; - u32 bandgap_reg; unsigned int resolution; const struct regmap_config *regmap_config; u8 temperature_trimming_bits; unsigned int temperature_multiplier; unsigned int temperature_divider; u8 disable_ring_counter; - bool has_reg11; bool has_vref_select; u8 vref_select; u8 cmv_select; u8 adc_eoc; - enum meson_sar_adc_vref_sel vref_volatge; + enum meson_sar_adc_vref_sel vref_voltage; + bool enable_mpll_clock_workaround; }; struct meson_sar_adc_data { @@ -976,7 +980,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN, regval); - if (priv->param->has_reg11) { + if (priv->param->regmap_config->max_register >= MESON_SAR_ADC_REG11) { regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_EOC, regval); @@ -989,7 +993,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) } regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE, - priv->param->vref_volatge); + priv->param->vref_voltage); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval); @@ -997,6 +1001,15 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) priv->param->cmv_select); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_CMV_SEL, regval); + + if (priv->param->enable_mpll_clock_workaround) { + dev_warn(dev, + "Enabling unknown bits to make the MPLL clocks work. This may change so always update dtbs and kernel together\n"); + regmap_write(priv->regmap, MESON_SAR_ADC_REG12, + MESON_SAR_ADC_REG12_MPLL0_UNKNOWN | + MESON_SAR_ADC_REG12_MPLL1_UNKNOWN | + MESON_SAR_ADC_REG12_MPLL2_UNKNOWN); + } } ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); @@ -1013,16 +1026,15 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - const struct meson_sar_adc_param *param = priv->param; - u32 enable_mask; - if (param->bandgap_reg == MESON_SAR_ADC_REG11) - enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN; + if (priv->param->regmap_config->max_register >= MESON_SAR_ADC_REG11) + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_BANDGAP_EN, + on_off ? MESON_SAR_ADC_REG11_BANDGAP_EN : 0); else - enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN; - - regmap_update_bits(priv->regmap, param->bandgap_reg, enable_mask, - on_off ? enable_mask : 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_VBG_EN, + on_off ? MESON_SAR_ADC_DELTA_10_TS_VBG_EN : 0); } static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) @@ -1186,7 +1198,6 @@ static const struct iio_info meson_sar_adc_iio_info = { static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { .has_bl30_integration = false, .clock_rate = 1150000, - .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, .temperature_trimming_bits = 4, @@ -1197,7 +1208,6 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { .has_bl30_integration = false, .clock_rate = 1150000, - .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, .temperature_trimming_bits = 5, @@ -1208,35 +1218,40 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = { static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { .has_bl30_integration = true, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 10, - .has_reg11 = true, - .vref_volatge = 1, + .vref_voltage = 1, .cmv_select = 1, }; static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { .has_bl30_integration = true, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, .disable_ring_counter = 1, - .has_reg11 = true, - .vref_volatge = 1, + .vref_voltage = 1, .cmv_select = 1, }; +static const struct meson_sar_adc_param meson_sar_adc_gxlx_param = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 12, + .disable_ring_counter = 1, + .vref_voltage = 1, + .cmv_select = true, + .enable_mpll_clock_workaround = true, +}; + static const struct meson_sar_adc_param meson_sar_adc_axg_param = { .has_bl30_integration = true, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, .disable_ring_counter = 1, - .has_reg11 = true, - .vref_volatge = 1, + .vref_voltage = 1, .has_vref_select = true, .vref_select = VREF_VDDA, .cmv_select = 1, @@ -1245,11 +1260,9 @@ static const struct meson_sar_adc_param meson_sar_adc_axg_param = { static const struct meson_sar_adc_param meson_sar_adc_g12a_param = { .has_bl30_integration = false, .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, .disable_ring_counter = 1, - .has_reg11 = true, .adc_eoc = 1, .has_vref_select = true, .vref_select = VREF_VDDA, @@ -1280,6 +1293,11 @@ static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { .name = "meson-gxl-saradc", }; +static const struct meson_sar_adc_data meson_sar_adc_gxlx_data = { + .param = &meson_sar_adc_gxlx_param, + .name = "meson-gxlx-saradc", +}; + static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { .param = &meson_sar_adc_gxl_param, .name = "meson-gxm-saradc", @@ -1312,6 +1330,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = { .compatible = "amlogic,meson-gxl-saradc", .data = &meson_sar_adc_gxl_data, }, { + .compatible = "amlogic,meson-gxlx-saradc", + .data = &meson_sar_adc_gxlx_data, + }, { .compatible = "amlogic,meson-gxm-saradc", .data = &meson_sar_adc_gxm_data, }, { @@ -1321,7 +1342,7 @@ static const struct of_device_id meson_sar_adc_of_match[] = { .compatible = "amlogic,meson-g12a-saradc", .data = &meson_sar_adc_g12a_data, }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match); diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index a4970cfb49a5..eecf88b05c6f 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -588,7 +588,7 @@ static const struct of_device_id mt6359_auxadc_of_match[] = { { .compatible = "mediatek,mt6357-auxadc", .data = &mt6357_chip_info }, { .compatible = "mediatek,mt6358-auxadc", .data = &mt6358_chip_info }, { .compatible = "mediatek,mt6359-auxadc", .data = &mt6359_chip_info }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, mt6359_auxadc_of_match); diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index 4eb2455d6ffa..f8e98b6fa7e9 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -263,8 +263,8 @@ static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p) struct mt6360_adc_data *mad = iio_priv(indio_dev); struct { u16 values[MT6360_CHAN_MAX]; - int64_t timestamp; - } data __aligned(8); + aligned_s64 timestamp; + } data; int i = 0, bit, val, ret; memset(&data, 0, sizeof(data)); diff --git a/drivers/iio/adc/mt6370-adc.c b/drivers/iio/adc/mt6370-adc.c index 0bc112135bca..7c71fe5e8d31 100644 --- a/drivers/iio/adc/mt6370-adc.c +++ b/drivers/iio/adc/mt6370-adc.c @@ -336,7 +336,7 @@ static int mt6370_adc_probe(struct platform_device *pdev) static const struct of_device_id mt6370_adc_of_id[] = { { .compatible = "mediatek,mt6370-adc", }, - {} + { } }; MODULE_DEVICE_TABLE(of, mt6370_adc_of_id); diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index 152cbe265e1a..92baf3f5f560 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -141,9 +141,8 @@ static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan, * the same time, yet the code becomes horribly complicated. Therefore I * applied KISS principle here. */ - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; reinit_completion(&adc->completion); @@ -192,7 +191,7 @@ err: writel(LRADC_CTRL1_LRADC_IRQ_EN(0), adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); return ret; } @@ -275,9 +274,8 @@ static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev, adc->scale_avail[chan->channel]; int ret; - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; switch (m) { case IIO_CHAN_INFO_SCALE: @@ -300,7 +298,7 @@ static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev, break; } - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); return ret; } @@ -427,7 +425,8 @@ static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p) j++; } - iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp); + iio_push_to_buffers_with_ts(iio, adc->buffer, sizeof(adc->buffer), + pf->timestamp); iio_trigger_notify_done(iio->trig); diff --git a/drivers/iio/adc/nct7201.c b/drivers/iio/adc/nct7201.c new file mode 100644 index 000000000000..d87824e5490f --- /dev/null +++ b/drivers/iio/adc/nct7201.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Nuvoton nct7201 and nct7202 power monitor chips. + * + * Copyright (c) 2024-2025 Nuvoton Technology corporation. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> + +#define NCT7201_REG_INTERRUPT_STATUS 0x0C +#define NCT7201_REG_VOLT_LOW_BYTE 0x0F +#define NCT7201_REG_CONFIGURATION 0x10 +#define NCT7201_BIT_CONFIGURATION_START BIT(0) +#define NCT7201_BIT_CONFIGURATION_ALERT_MSK BIT(1) +#define NCT7201_BIT_CONFIGURATION_CONV_RATE BIT(2) +#define NCT7201_BIT_CONFIGURATION_RESET BIT(7) + +#define NCT7201_REG_ADVANCED_CONFIGURATION 0x11 +#define NCT7201_BIT_ADVANCED_CONF_MOD_ALERT BIT(0) +#define NCT7201_BIT_ADVANCED_CONF_MOD_STS BIT(1) +#define NCT7201_BIT_ADVANCED_CONF_FAULT_QUEUE BIT(2) +#define NCT7201_BIT_ADVANCED_CONF_EN_DEEP_SHUTDOWN BIT(4) +#define NCT7201_BIT_ADVANCED_CONF_EN_SMB_TIMEOUT BIT(5) +#define NCT7201_BIT_ADVANCED_CONF_MOD_RSTIN BIT(7) + +#define NCT7201_REG_CHANNEL_INPUT_MODE 0x12 +#define NCT7201_REG_CHANNEL_ENABLE 0x13 +#define NCT7201_REG_INTERRUPT_MASK_1 0x15 +#define NCT7201_REG_INTERRUPT_MASK_2 0x16 +#define NCT7201_REG_BUSY_STATUS 0x1E +#define NCT7201_BIT_BUSY BIT(0) +#define NCT7201_BIT_PWR_UP BIT(1) +#define NCT7201_REG_ONE_SHOT 0x1F +#define NCT7201_REG_SMUS_ADDRESS 0xFC +#define NCT7201_REG_VIN_MASK GENMASK(15, 3) + +#define NCT7201_REG_VIN(i) (0x00 + i) +#define NCT7201_REG_VIN_HIGH_LIMIT(i) (0x20 + (i) * 2) +#define NCT7201_REG_VIN_LOW_LIMIT(i) (0x21 + (i) * 2) +#define NCT7201_MAX_CHANNEL 12 + +static const struct regmap_range nct7201_read_reg_range[] = { + regmap_reg_range(NCT7201_REG_INTERRUPT_STATUS, NCT7201_REG_BUSY_STATUS), + regmap_reg_range(NCT7201_REG_SMUS_ADDRESS, NCT7201_REG_SMUS_ADDRESS), +}; + +static const struct regmap_access_table nct7201_readable_regs_tbl = { + .yes_ranges = nct7201_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_read_reg_range), +}; + +static const struct regmap_range nct7201_write_reg_range[] = { + regmap_reg_range(NCT7201_REG_CONFIGURATION, NCT7201_REG_INTERRUPT_MASK_2), + regmap_reg_range(NCT7201_REG_ONE_SHOT, NCT7201_REG_ONE_SHOT), +}; + +static const struct regmap_access_table nct7201_writeable_regs_tbl = { + .yes_ranges = nct7201_write_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_write_reg_range), +}; + +static const struct regmap_range nct7201_read_vin_reg_range[] = { + regmap_reg_range(NCT7201_REG_VIN(0), NCT7201_REG_VIN(NCT7201_MAX_CHANNEL - 1)), + regmap_reg_range(NCT7201_REG_VIN_HIGH_LIMIT(0), + NCT7201_REG_VIN_LOW_LIMIT(NCT7201_MAX_CHANNEL - 1)), +}; + +static const struct regmap_access_table nct7201_readable_vin_regs_tbl = { + .yes_ranges = nct7201_read_vin_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_read_vin_reg_range), +}; + +static const struct regmap_range nct7201_write_vin_reg_range[] = { + regmap_reg_range(NCT7201_REG_VIN_HIGH_LIMIT(0), + NCT7201_REG_VIN_LOW_LIMIT(NCT7201_MAX_CHANNEL - 1)), +}; + +static const struct regmap_access_table nct7201_writeable_vin_regs_tbl = { + .yes_ranges = nct7201_write_vin_reg_range, + .n_yes_ranges = ARRAY_SIZE(nct7201_write_vin_reg_range), +}; + +static const struct regmap_config nct7201_regmap8_config = { + .name = "vin-data-read-byte", + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + .max_register = 0xff, + .rd_table = &nct7201_readable_regs_tbl, + .wr_table = &nct7201_writeable_regs_tbl, +}; + +static const struct regmap_config nct7201_regmap16_config = { + .name = "vin-data-read-word", + .reg_bits = 8, + .val_bits = 16, + .max_register = 0xff, + .rd_table = &nct7201_readable_vin_regs_tbl, + .wr_table = &nct7201_writeable_vin_regs_tbl, +}; + +struct nct7201_chip_info { + struct regmap *regmap; + struct regmap *regmap16; + int num_vin_channels; + __le16 vin_mask; +}; + +struct nct7201_adc_model_data { + const char *model_name; + const struct iio_chan_spec *channels; + unsigned int num_channels; + int num_vin_channels; +}; + +static const struct iio_event_spec nct7201_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define NCT7201_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num + 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = num, \ + .event_spec = nct7201_events, \ + .num_event_specs = ARRAY_SIZE(nct7201_events), \ + } + +static const struct iio_chan_spec nct7201_channels[] = { + NCT7201_VOLTAGE_CHANNEL(0), + NCT7201_VOLTAGE_CHANNEL(1), + NCT7201_VOLTAGE_CHANNEL(2), + NCT7201_VOLTAGE_CHANNEL(3), + NCT7201_VOLTAGE_CHANNEL(4), + NCT7201_VOLTAGE_CHANNEL(5), + NCT7201_VOLTAGE_CHANNEL(6), + NCT7201_VOLTAGE_CHANNEL(7), +}; + +static const struct iio_chan_spec nct7202_channels[] = { + NCT7201_VOLTAGE_CHANNEL(0), + NCT7201_VOLTAGE_CHANNEL(1), + NCT7201_VOLTAGE_CHANNEL(2), + NCT7201_VOLTAGE_CHANNEL(3), + NCT7201_VOLTAGE_CHANNEL(4), + NCT7201_VOLTAGE_CHANNEL(5), + NCT7201_VOLTAGE_CHANNEL(6), + NCT7201_VOLTAGE_CHANNEL(7), + NCT7201_VOLTAGE_CHANNEL(8), + NCT7201_VOLTAGE_CHANNEL(9), + NCT7201_VOLTAGE_CHANNEL(10), + NCT7201_VOLTAGE_CHANNEL(11), +}; + +static int nct7201_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + unsigned int value; + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = regmap_read(chip->regmap16, NCT7201_REG_VIN(chan->address), &value); + if (err) + return err; + *val = FIELD_GET(NCT7201_REG_VIN_MASK, value); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* From the datasheet, we have to multiply by 0.0004995 */ + *val = 0; + *val2 = 499500; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int nct7201_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 nct7201_chip_info *chip = iio_priv(indio_dev); + unsigned int value; + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + if (info != IIO_EV_INFO_VALUE) + return -EINVAL; + + if (dir == IIO_EV_DIR_FALLING) + err = regmap_read(chip->regmap16, NCT7201_REG_VIN_LOW_LIMIT(chan->address), + &value); + else + err = regmap_read(chip->regmap16, NCT7201_REG_VIN_HIGH_LIMIT(chan->address), + &value); + if (err) + return err; + + *val = FIELD_GET(NCT7201_REG_VIN_MASK, value); + + return IIO_VAL_INT; +} + +static int nct7201_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) +{ + struct nct7201_chip_info *chip = iio_priv(indio_dev); + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + if (info != IIO_EV_INFO_VALUE) + return -EOPNOTSUPP; + + if (dir == IIO_EV_DIR_FALLING) + err = regmap_write(chip->regmap16, NCT7201_REG_VIN_LOW_LIMIT(chan->address), + FIELD_PREP(NCT7201_REG_VIN_MASK, val)); + else + err = regmap_write(chip->regmap16, NCT7201_REG_VIN_HIGH_LIMIT(chan->address), + FIELD_PREP(NCT7201_REG_VIN_MASK, val)); + + return err; +} + +static int nct7201_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 nct7201_chip_info *chip = iio_priv(indio_dev); + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + return !!(le16_to_cpu(chip->vin_mask) & BIT(chan->address)); +} + +static int nct7201_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 nct7201_chip_info *chip = iio_priv(indio_dev); + __le16 mask = cpu_to_le16(BIT(chan->address)); + int err; + + if (chan->type != IIO_VOLTAGE) + return -EOPNOTSUPP; + + if (state) + chip->vin_mask |= mask; + else + chip->vin_mask &= ~mask; + + if (chip->num_vin_channels <= 8) + err = regmap_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + le16_to_cpu(chip->vin_mask)); + else + err = regmap_bulk_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + &chip->vin_mask, sizeof(chip->vin_mask)); + + return err; +} + +static const struct iio_info nct7201_info = { + .read_raw = nct7201_read_raw, + .read_event_config = nct7201_read_event_config, + .write_event_config = nct7201_write_event_config, + .read_event_value = nct7201_read_event_value, + .write_event_value = nct7201_write_event_value, +}; + +static const struct iio_info nct7201_info_no_irq = { + .read_raw = nct7201_read_raw, +}; + +static const struct nct7201_adc_model_data nct7201_model_data = { + .model_name = "nct7201", + .channels = nct7201_channels, + .num_channels = ARRAY_SIZE(nct7201_channels), + .num_vin_channels = 8, +}; + +static const struct nct7201_adc_model_data nct7202_model_data = { + .model_name = "nct7202", + .channels = nct7202_channels, + .num_channels = ARRAY_SIZE(nct7202_channels), + .num_vin_channels = 12, +}; + +static int nct7201_init_chip(struct nct7201_chip_info *chip) +{ + struct device *dev = regmap_get_device(chip->regmap); + __le16 data = cpu_to_le16(GENMASK(chip->num_vin_channels - 1, 0)); + unsigned int value; + int err; + + err = regmap_write(chip->regmap, NCT7201_REG_CONFIGURATION, + NCT7201_BIT_CONFIGURATION_RESET); + if (err) + return dev_err_probe(dev, err, "Failed to reset chip\n"); + + /* + * After about 25 msecs, the device should be ready and then the power-up + * bit will be set to 1. + */ + fsleep(25 * USEC_PER_MSEC); + + err = regmap_read(chip->regmap, NCT7201_REG_BUSY_STATUS, &value); + if (err) + return dev_err_probe(dev, err, "Failed to read busy status\n"); + if (!(value & NCT7201_BIT_PWR_UP)) + return dev_err_probe(dev, -EIO, "Failed to power up after reset\n"); + + /* Enable Channels */ + if (chip->num_vin_channels <= 8) + err = regmap_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + le16_to_cpu(data)); + else + err = regmap_bulk_write(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + &data, sizeof(data)); + if (err) + return dev_err_probe(dev, err, "Failed to enable channels\n"); + + err = regmap_bulk_read(chip->regmap, NCT7201_REG_CHANNEL_ENABLE, + &chip->vin_mask, sizeof(chip->vin_mask)); + if (err) + return dev_err_probe(dev, err, + "Failed to read channel enable register\n"); + + /* Start monitoring if needed */ + err = regmap_set_bits(chip->regmap, NCT7201_REG_CONFIGURATION, + NCT7201_BIT_CONFIGURATION_START); + if (err) + return dev_err_probe(dev, err, "Failed to start monitoring\n"); + + return 0; +} + +static irqreturn_t nct7201_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct nct7201_chip_info *chip = iio_priv(indio_dev); + __le16 data; + int err; + + err = regmap_bulk_read(chip->regmap, NCT7201_REG_INTERRUPT_STATUS, + &data, sizeof(data)); + if (err) + return IRQ_NONE; + + if (data) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + + return IRQ_HANDLED; +} + +static int nct7201_probe(struct i2c_client *client) +{ + const struct nct7201_adc_model_data *model_data; + struct device *dev = &client->dev; + struct nct7201_chip_info *chip; + struct iio_dev *indio_dev; + int ret; + + model_data = i2c_get_match_data(client); + if (!model_data) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + + chip->regmap = devm_regmap_init_i2c(client, &nct7201_regmap8_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "Failed to init regmap\n"); + + chip->regmap16 = devm_regmap_init_i2c(client, &nct7201_regmap16_config); + if (IS_ERR(chip->regmap16)) + return dev_err_probe(dev, PTR_ERR(chip->regmap16), + "Failed to init regmap16\n"); + + chip->num_vin_channels = model_data->num_vin_channels; + + ret = nct7201_init_chip(chip); + if (ret) + return ret; + + indio_dev->name = model_data->model_name; + indio_dev->channels = model_data->channels; + indio_dev->num_channels = model_data->num_channels; + if (client->irq) { + /* Enable alert function */ + ret = regmap_clear_bits(chip->regmap, NCT7201_REG_CONFIGURATION, + NCT7201_BIT_CONFIGURATION_ALERT_MSK); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable alert function\n"); + + ret = devm_request_threaded_irq(dev, client->irq, + NULL, nct7201_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Failed to assign interrupt.\n"); + + indio_dev->info = &nct7201_info; + } else { + indio_dev->info = &nct7201_info_no_irq; + } + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id nct7201_id[] = { + { .name = "nct7201", .driver_data = (kernel_ulong_t)&nct7201_model_data }, + { .name = "nct7202", .driver_data = (kernel_ulong_t)&nct7202_model_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct7201_id); + +static const struct of_device_id nct7201_of_match[] = { + { + .compatible = "nuvoton,nct7201", + .data = &nct7201_model_data, + }, + { + .compatible = "nuvoton,nct7202", + .data = &nct7202_model_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, nct7201_of_match); + +static struct i2c_driver nct7201_driver = { + .driver = { + .name = "nct7201", + .of_match_table = nct7201_of_match, + }, + .probe = nct7201_probe, + .id_table = nct7201_id, +}; +module_i2c_driver(nct7201_driver); + +MODULE_AUTHOR("Eason Yang <j2anfernee@gmail.com>"); +MODULE_DESCRIPTION("Nuvoton NCT7201 voltage monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index 7c1511ee3a4b..c8283873cdee 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -196,7 +196,7 @@ static const struct iio_info npcm_adc_iio_info = { static const struct of_device_id npcm_adc_match[] = { { .compatible = "nuvoton,npcm750-adc", .data = &npxm7xx_adc_info}, { .compatible = "nuvoton,npcm845-adc", .data = &npxm8xx_adc_info}, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, npcm_adc_match); diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c index b0f6727cfe38..72aa4ca2e5a4 100644 --- a/drivers/iio/adc/pac1921.c +++ b/drivers/iio/adc/pac1921.c @@ -7,11 +7,13 @@ #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> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +#include <linux/limits.h> #include <linux/regmap.h> #include <linux/units.h> @@ -67,6 +69,14 @@ enum pac1921_mxsl { #define PAC1921_DEFAULT_DI_GAIN 0 /* 2^(value): 1x gain (HW default) */ #define PAC1921_DEFAULT_NUM_SAMPLES 0 /* 2^(value): 1 sample (HW default) */ +#define PAC1921_ACPI_GET_uOHMS_VALS 0 +#define PAC1921_ACPI_GET_LABEL 1 + +/* f7bb9932-86ee-4516-a236-7a7a742e55cb */ +static const guid_t pac1921_guid = + GUID_INIT(0xf7bb9932, 0x86ee, 0x4516, 0xa2, + 0x36, 0x7a, 0x7a, 0x74, 0x2e, 0x55, 0xcb); + /* * Pre-computed scale factors for BUS voltage * format: IIO_VAL_INT_PLUS_NANO @@ -200,7 +210,7 @@ struct pac1921_priv { struct { u16 chan[PAC1921_NUM_MEAS_CHANS]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -782,7 +792,7 @@ static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, const char *buf, size_t len) { struct pac1921_priv *priv = iio_priv(indio_dev); - u64 rshunt_uohm; + u32 rshunt_uohm; int val, val_fract; int ret; @@ -793,10 +803,17 @@ static ssize_t pac1921_write_shunt_resistor(struct iio_dev *indio_dev, if (ret) return ret; - rshunt_uohm = val * MICRO + val_fract; - if (rshunt_uohm == 0 || rshunt_uohm > INT_MAX) + /* + * This check validates the shunt is not zero and does not surpass + * INT_MAX. The check is done before calculating in order to avoid + * val * MICRO overflowing. + */ + if ((!val && !val_fract) || val > INT_MAX / MICRO || + (val == INT_MAX / MICRO && val_fract > INT_MAX % MICRO)) return -EINVAL; + rshunt_uohm = val * MICRO + val_fract; + guard(mutex)(&priv->lock); priv->rshunt_uohm = rshunt_uohm; @@ -883,7 +900,7 @@ static ssize_t pac1921_read_scale_avail(struct iio_dev *indio_dev, static const struct iio_chan_spec_ext_info pac1921_ext_info_voltage[] = { PAC1921_EXT_INFO_SCALE_AVAIL, - {} + { } }; static const struct iio_chan_spec_ext_info pac1921_ext_info_current[] = { @@ -894,7 +911,7 @@ static const struct iio_chan_spec_ext_info pac1921_ext_info_current[] = { .write = pac1921_write_shunt_resistor, .shared = IIO_SEPARATE, }, - {} + { } }; static const struct iio_event_spec pac1921_overflow_event[] = { @@ -1027,7 +1044,8 @@ static irqreturn_t pac1921_trigger_handler(int irq, void *p) priv->scan.chan[ch++] = val; } - iio_push_to_buffers_with_timestamp(idev, &priv->scan, pf->timestamp); + iio_push_to_buffers_with_ts(idev, &priv->scan, sizeof(priv->scan), + pf->timestamp); done: iio_trigger_notify_done(idev->trig); @@ -1151,6 +1169,61 @@ static void pac1921_regulator_disable(void *data) regulator_disable(regulator); } +/* + * Documentation related to the ACPI device definition + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static int pac1921_match_acpi_device(struct iio_dev *indio_dev) +{ + acpi_handle handle; + union acpi_object *status; + char *label; + struct pac1921_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->client->dev; + + handle = ACPI_HANDLE(dev); + + status = acpi_evaluate_dsm(handle, &pac1921_guid, 1, + PAC1921_ACPI_GET_uOHMS_VALS, NULL); + if (!status) + return dev_err_probe(dev, -EINVAL, + "Could not read shunt from ACPI table\n"); + + priv->rshunt_uohm = status->package.elements[0].integer.value; + ACPI_FREE(status); + + status = acpi_evaluate_dsm(handle, &pac1921_guid, 1, + PAC1921_ACPI_GET_LABEL, NULL); + if (!status) + return dev_err_probe(dev, -EINVAL, + "Could not read label from ACPI table\n"); + + label = devm_kstrdup(dev, status->package.elements[0].string.pointer, + GFP_KERNEL); + ACPI_FREE(status); + if (!label) + return -ENOMEM; + + indio_dev->label = label; + + return 0; +} + +static int pac1921_parse_of_fw(struct iio_dev *indio_dev) +{ + int ret; + struct pac1921_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->client->dev; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &priv->rshunt_uohm); + if (ret) + return dev_err_probe(dev, ret, + "Cannot read shunt resistor property\n"); + + return 0; +} + static int pac1921_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1179,11 +1252,14 @@ static int pac1921_probe(struct i2c_client *client) priv->di_gain = PAC1921_DEFAULT_DI_GAIN; priv->n_samples = PAC1921_DEFAULT_NUM_SAMPLES; - ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", - &priv->rshunt_uohm); + if (is_acpi_device_node(dev->fwnode)) + ret = pac1921_match_acpi_device(indio_dev); + else + ret = pac1921_parse_of_fw(indio_dev); if (ret) return dev_err_probe(dev, ret, - "Cannot read shunt resistor property\n"); + "Parameter parsing error\n"); + if (priv->rshunt_uohm == 0 || priv->rshunt_uohm > INT_MAX) return dev_err_probe(dev, -EINVAL, "Invalid shunt resistor: %u\n", @@ -1246,11 +1322,18 @@ static const struct of_device_id pac1921_of_match[] = { }; MODULE_DEVICE_TABLE(of, pac1921_of_match); +static const struct acpi_device_id pac1921_acpi_match[] = { + { "MCHP1921" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, pac1921_acpi_match); + static struct i2c_driver pac1921_driver = { .driver = { .name = "pac1921", .pm = pm_sleep_ptr(&pac1921_pm_ops), .of_match_table = pac1921_of_match, + .acpi_match_table = pac1921_acpi_match, }, .probe = pac1921_probe, .id_table = pac1921_id, diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 20802b7f49ea..09fe88eb3fb0 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1081,7 +1081,7 @@ static int pac1934_chip_identify(struct pac1934_chip_info *info) /* * documentation related to the ACPI device definition - * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC193X-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf */ static int pac1934_acpi_parse_channel_config(struct i2c_client *client, struct pac1934_chip_info *info) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index d283ee8fb1d2..7c01e33be04c 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -1164,7 +1164,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_gpadc_suspend, static const struct of_device_id of_palmas_gpadc_match_tbl[] = { { .compatible = "ti,palmas-gpadc", }, - { /* end */ } + { } }; MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl); diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c index 7fb8b2499a1d..b64a8a407168 100644 --- a/drivers/iio/adc/qcom-spmi-iadc.c +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -543,7 +543,9 @@ static int iadc_probe(struct platform_device *pdev) else return ret; } else { - device_init_wakeup(iadc->dev, 1); + ret = devm_device_init_wakeup(iadc->dev); + if (ret) + return dev_err_probe(iadc->dev, ret, "Failed to init wakeup\n"); } ret = iadc_update_offset(iadc); diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c index 63ebaf13ef19..f61ad0510f04 100644 --- a/drivers/iio/adc/qcom-spmi-rradc.c +++ b/drivers/iio/adc/qcom-spmi-rradc.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2022 Linaro Limited. - * Author: Caleb Connolly <caleb.connolly@linaro.org> + * Author: Casey Connolly <casey.connolly@linaro.org> * * This driver is for the Round Robin ADC found in the pmi8998 and pm660 PMICs. */ @@ -1016,5 +1016,5 @@ static struct platform_driver rradc_driver = { module_platform_driver(rradc_driver); MODULE_DESCRIPTION("QCOM SPMI PMIC RR ADC driver"); -MODULE_AUTHOR("Caleb Connolly <caleb.connolly@linaro.org>"); +MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 11170b5852d1..cc326f21d398 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -199,13 +199,12 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, if (!consumer) return -EINVAL; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = rcar_gyroadc_set_power(priv, true); if (ret < 0) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -213,7 +212,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, *val &= BIT(priv->sample_width) - 1; ret = rcar_gyroadc_set_power(priv, false); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -308,7 +307,7 @@ static const struct of_device_id rcar_gyroadc_child_match[] __maybe_unused = { .compatible = "maxim,max11100", .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, }, - { /* sentinel */ } + { } }; static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c index b33536157adc..d6f6b351f2af 100644 --- a/drivers/iio/adc/rn5t618-adc.c +++ b/drivers/iio/adc/rn5t618-adc.c @@ -188,7 +188,7 @@ static const struct iio_chan_spec rn5t618_adc_iio_channels[] = { static const struct iio_map rn5t618_maps[] = { IIO_MAP("VADP", "rn5t618-power", "vadp"), IIO_MAP("VUSB", "rn5t618-power", "vusb"), - { /* sentinel */ } + { } }; static int rn5t618_adc_probe(struct platform_device *pdev) diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index dfd47a6e1f4a..325e3747a134 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, }, { @@ -363,7 +403,7 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) */ struct { u16 values[SARADC_MAX_CHANNELS]; - int64_t timestamp; + aligned_s64 timestamp; } data; int ret; int i, j = 0; @@ -385,7 +425,8 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) j++; } - iio_push_to_buffers_with_timestamp(i_dev, &data, iio_get_time_ns(i_dev)); + iio_push_to_buffers_with_ts(i_dev, &data, sizeof(data), + iio_get_time_ns(i_dev)); out: mutex_unlock(&info->lock); @@ -480,15 +521,6 @@ static int rockchip_saradc_probe(struct platform_device *pdev) if (info->reset) rockchip_saradc_reset_controller(info->reset); - /* - * Use a default value for the converter clock. - * This may become user-configurable in the future. - */ - ret = clk_set_rate(info->clk, info->data->clk_rate); - if (ret < 0) - return dev_err_probe(&pdev->dev, ret, - "failed to set adc clk rate\n"); - ret = regulator_enable(info->vref); if (ret < 0) return dev_err_probe(&pdev->dev, ret, @@ -515,6 +547,14 @@ static int rockchip_saradc_probe(struct platform_device *pdev) if (IS_ERR(info->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), "failed to get adc clock\n"); + /* + * Use a default value for the converter clock. + * This may become user-configurable in the future. + */ + ret = clk_set_rate(info->clk, info->data->clk_rate); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "failed to set adc clk rate\n"); platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c new file mode 100644 index 000000000000..bb7c93ae4055 --- /dev/null +++ b/drivers/iio/adc/rohm-bd79124.c @@ -0,0 +1,1146 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ROHM ADC driver for BD79124 ADC/GPO device + * https://fscdn.rohm.com/en/products/databook/datasheet/ic/data_converter/dac/bd79124muf-c-e.pdf + * + * Copyright (c) 2025, ROHM Semiconductor. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/devm-helpers.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#include <asm/byteorder.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/adc-helpers.h> + +#define BD79124_I2C_MULTI_READ 0x30 +#define BD79124_I2C_MULTI_WRITE 0x28 +#define BD79124_REG_MAX 0xaf + +#define BD79124_REG_SYSTEM_STATUS 0x00 +#define BD79124_REG_GEN_CFG 0x01 +#define BD79124_REG_OPMODE_CFG 0x04 +#define BD79124_REG_PINCFG 0x05 +#define BD79124_REG_GPO_VAL 0x0B +#define BD79124_REG_SEQ_CFG 0x10 +#define BD79124_REG_MANUAL_CHANNELS 0x11 +#define BD79124_REG_AUTO_CHANNELS 0x12 +#define BD79124_REG_ALERT_CH_SEL 0x14 +#define BD79124_REG_EVENT_FLAG 0x18 +#define BD79124_REG_EVENT_FLAG_HI 0x1a +#define BD79124_REG_EVENT_FLAG_LO 0x1c +#define BD79124_REG_HYSTERESIS_CH0 0x20 +#define BD79124_REG_EVENTCOUNT_CH0 0x22 +#define BD79124_REG_RECENT_CH0_LSB 0xa0 +#define BD79124_REG_RECENT_CH7_MSB 0xaf + +#define BD79124_ADC_BITS 12 + +/* Masks for the BD79124_REG_OPMODE_CFG */ +#define BD79124_MSK_CONV_MODE GENMASK(6, 5) +#define BD79124_CONV_MODE_MANSEQ 0 +#define BD79124_CONV_MODE_AUTO 1 +#define BD79124_MSK_AUTO_INTERVAL GENMASK(1, 0) +#define BD79124_INTERVAL_750_US 0 + +/* Masks for the BD79124_REG_GEN_CFG */ +#define BD79124_MSK_DWC_EN BIT(4) +#define BD79124_MSK_STATS_EN BIT(5) + +/* Masks for the BD79124_REG_SEQ_CFG */ +#define BD79124_MSK_SEQ_START BIT(4) +#define BD79124_MSK_SEQ_MODE GENMASK(1, 0) +#define BD79124_MSK_SEQ_MANUAL 0 +#define BD79124_MSK_SEQ_SEQ 1 + +#define BD79124_MSK_HYSTERESIS GENMASK(3, 0) +#define BD79124_LOW_LIMIT_MIN 0 +#define BD79124_HIGH_LIMIT_MAX GENMASK(11, 0) + +/* + * The high limit, low limit and last measurement result are each stored in + * 2 consequtive registers. 4 bits are in the high bits of the first register + * and 8 bits in the next register. + * + * These macros return the address of the first reg for the given channel. + */ +#define BD79124_GET_HIGH_LIMIT_REG(ch) (BD79124_REG_HYSTERESIS_CH0 + (ch) * 4) +#define BD79124_GET_LOW_LIMIT_REG(ch) (BD79124_REG_EVENTCOUNT_CH0 + (ch) * 4) +#define BD79124_GET_LIMIT_REG(ch, dir) ((dir) == IIO_EV_DIR_RISING ? \ + BD79124_GET_HIGH_LIMIT_REG(ch) : BD79124_GET_LOW_LIMIT_REG(ch)) +#define BD79124_GET_RECENT_RES_REG(ch) (BD79124_REG_RECENT_CH0_LSB + (ch) * 2) + +/* + * The hysteresis for a channel is stored in the same register where the + * 4 bits of high limit reside. + */ +#define BD79124_GET_HYSTERESIS_REG(ch) BD79124_GET_HIGH_LIMIT_REG(ch) + +#define BD79124_MAX_NUM_CHANNELS 8 + +struct bd79124_data { + s64 timestamp; + struct regmap *map; + struct device *dev; + int vmax; + /* + * Keep measurement status so read_raw() knows if the measurement needs + * to be started. + */ + int alarm_monitored[BD79124_MAX_NUM_CHANNELS]; + /* + * The BD79124 does not allow disabling/enabling limit separately for + * one direction only. Hence, we do the disabling by changing the limit + * to maximum/minimum measurable value. This means we need to cache + * the limit in order to maintain it over the time limit is disabled. + */ + u16 alarm_r_limit[BD79124_MAX_NUM_CHANNELS]; + u16 alarm_f_limit[BD79124_MAX_NUM_CHANNELS]; + /* Bitmask of disabled events (for rate limiting) for each channel. */ + int alarm_suppressed[BD79124_MAX_NUM_CHANNELS]; + /* + * The BD79124 is configured to run the measurements in the background. + * This is done for the event monitoring as well as for the read_raw(). + * Protect the measurement starting/stopping using a mutex. + */ + struct mutex mutex; + struct delayed_work alm_enable_work; + struct gpio_chip gc; + u8 gpio_valid_mask; +}; + +static const struct regmap_range bd79124_ro_ranges[] = { + { + .range_min = BD79124_REG_EVENT_FLAG, + .range_max = BD79124_REG_EVENT_FLAG, + }, { + .range_min = BD79124_REG_RECENT_CH0_LSB, + .range_max = BD79124_REG_RECENT_CH7_MSB, + }, +}; + +static const struct regmap_access_table bd79124_ro_regs = { + .no_ranges = &bd79124_ro_ranges[0], + .n_no_ranges = ARRAY_SIZE(bd79124_ro_ranges), +}; + +static const struct regmap_range bd79124_volatile_ranges[] = { + { + .range_min = BD79124_REG_RECENT_CH0_LSB, + .range_max = BD79124_REG_RECENT_CH7_MSB, + }, { + .range_min = BD79124_REG_EVENT_FLAG, + .range_max = BD79124_REG_EVENT_FLAG, + }, { + .range_min = BD79124_REG_EVENT_FLAG_HI, + .range_max = BD79124_REG_EVENT_FLAG_HI, + }, { + .range_min = BD79124_REG_EVENT_FLAG_LO, + .range_max = BD79124_REG_EVENT_FLAG_LO, + }, { + .range_min = BD79124_REG_SYSTEM_STATUS, + .range_max = BD79124_REG_SYSTEM_STATUS, + }, +}; + +static const struct regmap_access_table bd79124_volatile_regs = { + .yes_ranges = &bd79124_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bd79124_volatile_ranges), +}; + +static const struct regmap_range bd79124_precious_ranges[] = { + { + .range_min = BD79124_REG_EVENT_FLAG_HI, + .range_max = BD79124_REG_EVENT_FLAG_HI, + }, { + .range_min = BD79124_REG_EVENT_FLAG_LO, + .range_max = BD79124_REG_EVENT_FLAG_LO, + }, +}; + +static const struct regmap_access_table bd79124_precious_regs = { + .yes_ranges = &bd79124_precious_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bd79124_precious_ranges), +}; + +static const struct regmap_config bd79124_regmap = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BD79124_I2C_MULTI_READ, + .write_flag_mask = BD79124_I2C_MULTI_WRITE, + .max_register = BD79124_REG_MAX, + .cache_type = REGCACHE_MAPLE, + .volatile_table = &bd79124_volatile_regs, + .wr_table = &bd79124_ro_regs, + .precious_table = &bd79124_precious_regs, +}; + +static int bd79124gpo_direction_get(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int bd79124gpo_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct bd79124_data *data = gpiochip_get_data(gc); + + return regmap_assign_bits(data->map, BD79124_REG_GPO_VAL, BIT(offset), + value); +} + +static int bd79124gpo_set_multiple(struct gpio_chip *gc, unsigned long *mask, + unsigned long *bits) +{ + unsigned int all_gpos; + int ret; + struct bd79124_data *data = gpiochip_get_data(gc); + + /* + * Ensure all GPIOs in 'mask' are set to be GPIOs + * The valid_mask was not obeyed by the gpiolib in all cases prior the + * https://lore.kernel.org/all/cd5e067b80e1bb590027bc3bfa817e7f794f21c3.1741180097.git.mazziesaccount@gmail.com/ + * + * Keep this check here for a couple of cycles. + */ + ret = regmap_read(data->map, BD79124_REG_PINCFG, &all_gpos); + if (ret) + return ret; + + if (all_gpos ^ *mask) { + dev_dbg(data->dev, "Invalid mux config. Can't set value.\n"); + + return -EINVAL; + } + + return regmap_update_bits(data->map, BD79124_REG_GPO_VAL, *mask, *bits); +} + +static int bd79124_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct bd79124_data *data = gpiochip_get_data(gc); + + *valid_mask = data->gpio_valid_mask; + + return 0; +} + +/* Template for GPIO chip */ +static const struct gpio_chip bd79124gpo_chip = { + .label = "bd79124-gpo", + .get_direction = bd79124gpo_direction_get, + .set_rv = bd79124gpo_set, + .set_multiple_rv = bd79124gpo_set_multiple, + .init_valid_mask = bd79124_init_valid_mask, + .can_sleep = true, + .ngpio = 8, + .base = -1, +}; + +struct bd79124_raw { + u8 val_bit3_0; /* Is set in high bits of the byte */ + u8 val_bit11_4; +}; +#define BD79124_RAW_TO_INT(r) ((r.val_bit11_4 << 4) | (r.val_bit3_0 >> 4)) +#define BD79124_INT_TO_RAW(val) { \ + .val_bit11_4 = (val) >> 4, \ + .val_bit3_0 = (val) << 4, \ +} + +/* + * The high and low limits as well as the recent result values are stored in + * the same way in 2 consequent registers. The first register contains 4 bits + * of the value. These bits are stored in the high bits [7:4] of register, but + * they represent the low bits [3:0] of the value. + * The value bits [11:4] are stored in the next register. + * + * Read data from register and convert to integer. + */ +static int bd79124_read_reg_to_int(struct bd79124_data *data, int reg, + unsigned int *val) +{ + int ret; + struct bd79124_raw raw; + + ret = regmap_bulk_read(data->map, reg, &raw, sizeof(raw)); + if (ret) { + dev_dbg(data->dev, "bulk_read failed %d\n", ret); + + return ret; + } + + *val = BD79124_RAW_TO_INT(raw); + + return 0; +} + +/* + * The high and low limits as well as the recent result values are stored in + * the same way in 2 consequent registers. The first register contains 4 bits + * of the value. These bits are stored in the high bits [7:4] of register, but + * they represent the low bits [3:0] of the value. + * The value bits [11:4] are stored in the next register. + * + * Convert the integer to register format and write it using rmw cycle. + */ +static int bd79124_write_int_to_reg(struct bd79124_data *data, int reg, + unsigned int val) +{ + struct bd79124_raw raw = BD79124_INT_TO_RAW(val); + unsigned int tmp; + int ret; + + ret = regmap_read(data->map, reg, &tmp); + if (ret) + return ret; + + raw.val_bit3_0 |= (tmp & 0xf); + + return regmap_bulk_write(data->map, reg, &raw, sizeof(raw)); +} + +static const struct iio_event_spec bd79124_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +static const struct iio_chan_spec bd79124_chan_template_noirq = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, +}; + +static const struct iio_chan_spec bd79124_chan_template = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .event_spec = bd79124_events, + .num_event_specs = ARRAY_SIZE(bd79124_events), +}; + +static int bd79124_read_event_value(struct iio_dev *iio_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 bd79124_data *data = iio_priv(iio_dev); + int ret, reg; + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_RISING) + *val = data->alarm_r_limit[chan->channel]; + else if (dir == IIO_EV_DIR_FALLING) + *val = data->alarm_f_limit[chan->channel]; + else + return -EINVAL; + + return IIO_VAL_INT; + + case IIO_EV_INFO_HYSTERESIS: + reg = BD79124_GET_HYSTERESIS_REG(chan->channel); + ret = regmap_read(data->map, reg, val); + if (ret) + return ret; + + *val &= BD79124_MSK_HYSTERESIS; + /* + * The data-sheet says the hysteresis register value needs to be + * shifted left by 3. + */ + *val <<= 3; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int bd79124_start_measurement(struct bd79124_data *data, int chan) +{ + unsigned int val, regval; + int ret; + + /* See if already started */ + ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &val); + if (val & BIT(chan)) + return 0; + + /* + * The sequencer must be stopped when channels are added/removed from + * the list of the measured channels to ensure the new channel + * configuration is used. + */ + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, val | BIT(chan)); + if (ret) + return ret; + + ret = regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + /* + * Start the measurement at the background. Don't bother checking if + * it was started, regmap has cache. + */ + regval = FIELD_PREP(BD79124_MSK_CONV_MODE, BD79124_CONV_MODE_AUTO); + + return regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); +} + +static int bd79124_stop_measurement(struct bd79124_data *data, int chan) +{ + unsigned int enabled_chans; + int ret; + + /* See if already stopped */ + ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &enabled_chans); + if (!(enabled_chans & BIT(chan))) + return 0; + + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + + /* Clear the channel from the measured channels */ + enabled_chans &= ~BIT(chan); + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, + enabled_chans); + if (ret) + return ret; + + /* + * Stop background conversion for power saving if it was the last + * channel. + */ + if (!enabled_chans) { + int regval = FIELD_PREP(BD79124_MSK_CONV_MODE, + BD79124_CONV_MODE_MANSEQ); + + ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); + if (ret) + return ret; + } + + return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); +} + +static int bd79124_read_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct bd79124_data *data = iio_priv(iio_dev); + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + return !!(data->alarm_monitored[chan->channel] & BIT(dir)); +} + +static int bd79124_disable_event(struct bd79124_data *data, + enum iio_event_direction dir, int channel) +{ + int dir_bit = BIT(dir); + int reg; + unsigned int limit; + + guard(mutex)(&data->mutex); + + /* + * Set thresholds either to 0 or to 2^12 - 1 as appropriate to prevent + * alerts and thus disable event generation. + */ + if (dir == IIO_EV_DIR_RISING) { + reg = BD79124_GET_HIGH_LIMIT_REG(channel); + limit = BD79124_HIGH_LIMIT_MAX; + } else if (dir == IIO_EV_DIR_FALLING) { + reg = BD79124_GET_LOW_LIMIT_REG(channel); + limit = BD79124_LOW_LIMIT_MIN; + } else { + return -EINVAL; + } + + data->alarm_monitored[channel] &= ~dir_bit; + + /* + * Stop measurement if there is no more events to monitor. + * We don't bother checking the retval because the limit + * setting should in any case effectively disable the alarm. + */ + if (!data->alarm_monitored[channel]) { + bd79124_stop_measurement(data, channel); + regmap_clear_bits(data->map, BD79124_REG_ALERT_CH_SEL, + BIT(channel)); + } + + return bd79124_write_int_to_reg(data, reg, limit); +} + +static int bd79124_enable_event(struct bd79124_data *data, + enum iio_event_direction dir, + unsigned int channel) +{ + int dir_bit = BIT(dir); + int reg, ret; + u16 *limit; + + guard(mutex)(&data->mutex); + ret = bd79124_start_measurement(data, channel); + if (ret) + return ret; + + data->alarm_monitored[channel] |= dir_bit; + + /* Add the channel to the list of monitored channels */ + ret = regmap_set_bits(data->map, BD79124_REG_ALERT_CH_SEL, BIT(channel)); + if (ret) + return ret; + + if (dir == IIO_EV_DIR_RISING) { + limit = &data->alarm_f_limit[channel]; + reg = BD79124_GET_HIGH_LIMIT_REG(channel); + } else { + limit = &data->alarm_f_limit[channel]; + reg = BD79124_GET_LOW_LIMIT_REG(channel); + } + /* + * Don't write the new limit to the hardware if we are in the + * rate-limit period. The timer which re-enables the event will set + * the limit. + */ + if (!(data->alarm_suppressed[channel] & dir_bit)) { + ret = bd79124_write_int_to_reg(data, reg, *limit); + if (ret) + return ret; + } + + /* + * Enable comparator. Trust the regmap cache, no need to check + * if it was already enabled. + * + * We could do this in the hw-init, but there may be users who + * never enable alarms and for them it makes sense to not + * enable the comparator at probe. + */ + return regmap_set_bits(data->map, BD79124_REG_GEN_CFG, + BD79124_MSK_DWC_EN); +} + +static int bd79124_write_event_config(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct bd79124_data *data = iio_priv(iio_dev); + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + if (state) + return bd79124_enable_event(data, dir, chan->channel); + + return bd79124_disable_event(data, dir, chan->channel); +} + +static int bd79124_write_event_value(struct iio_dev *iio_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 bd79124_data *data = iio_priv(iio_dev); + int reg; + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + { + guard(mutex)(&data->mutex); + + if (dir == IIO_EV_DIR_RISING) { + data->alarm_r_limit[chan->channel] = val; + reg = BD79124_GET_HIGH_LIMIT_REG(chan->channel); + } else if (dir == IIO_EV_DIR_FALLING) { + data->alarm_f_limit[chan->channel] = val; + reg = BD79124_GET_LOW_LIMIT_REG(chan->channel); + } else { + return -EINVAL; + } + /* + * We don't want to enable the alarm if it is not enabled or + * if it is suppressed. In that case skip writing to the + * register. + */ + if (!(data->alarm_monitored[chan->channel] & BIT(dir)) || + data->alarm_suppressed[chan->channel] & BIT(dir)) + return 0; + + return bd79124_write_int_to_reg(data, reg, val); + } + case IIO_EV_INFO_HYSTERESIS: + reg = BD79124_GET_HYSTERESIS_REG(chan->channel); + val >>= 3; + + return regmap_update_bits(data->map, reg, BD79124_MSK_HYSTERESIS, + val); + default: + return -EINVAL; + } +} + +static int bd79124_single_chan_seq(struct bd79124_data *data, int chan, unsigned int *old) +{ + int ret; + + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + /* + * It may be we have some channels monitored for alarms so we want to + * cache the old config and return it when the single channel + * measurement has been completed. + */ + ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, old); + if (ret) + return ret; + + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, BIT(chan)); + if (ret) + return ret; + + /* Restart the sequencer */ + return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); +} + +static int bd79124_single_chan_seq_end(struct bd79124_data *data, unsigned int old) +{ + int ret; + + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, old); + if (ret) + return ret; + + return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); +} + +static int bd79124_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct bd79124_data *data = iio_priv(iio_dev); + int ret; + + if (chan->channel >= BD79124_MAX_NUM_CHANNELS) + return -EINVAL; + + switch (m) { + case IIO_CHAN_INFO_RAW: + { + unsigned int old_chan_cfg, regval; + int tmp; + + guard(mutex)(&data->mutex); + + /* + * Start the automatic conversion. This is needed here if no + * events have been enabled. + */ + regval = FIELD_PREP(BD79124_MSK_CONV_MODE, + BD79124_CONV_MODE_AUTO); + ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); + if (ret) + return ret; + + ret = bd79124_single_chan_seq(data, chan->channel, &old_chan_cfg); + if (ret) + return ret; + + /* The maximum conversion time is 6 uS. */ + udelay(6); + + ret = bd79124_read_reg_to_int(data, + BD79124_GET_RECENT_RES_REG(chan->channel), val); + /* + * Return the old chan config even if data reading failed in + * order to re-enable the event monitoring. + */ + tmp = bd79124_single_chan_seq_end(data, old_chan_cfg); + if (tmp) + dev_err(data->dev, + "Failed to return config. Alarms may be disabled\n"); + + if (ret) + return ret; + + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = data->vmax / 1000; + *val2 = BD79124_ADC_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info bd79124_info = { + .read_raw = bd79124_read_raw, + .read_event_config = &bd79124_read_event_config, + .write_event_config = &bd79124_write_event_config, + .read_event_value = &bd79124_read_event_value, + .write_event_value = &bd79124_write_event_value, +}; + +static void bd79124_re_enable_lo(struct bd79124_data *data, unsigned int channel) +{ + int ret, evbit = BIT(IIO_EV_DIR_FALLING); + + /* + * We should not re-enable the event if user has disabled it while + * rate-limiting was enabled. + */ + if (!(data->alarm_suppressed[channel] & evbit)) + return; + + data->alarm_suppressed[channel] &= ~evbit; + + if (!(data->alarm_monitored[channel] & evbit)) + return; + + ret = bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel), + data->alarm_f_limit[channel]); + if (ret) + dev_warn(data->dev, "Low limit enabling failed for channel%d\n", + channel); +} + +static void bd79124_re_enable_hi(struct bd79124_data *data, unsigned int channel) +{ + int ret, evbit = BIT(IIO_EV_DIR_RISING); + + /* + * We should not re-enable the event if user has disabled it while + * rate-limiting was enabled. + */ + if (!(data->alarm_suppressed[channel] & evbit)) + return; + + data->alarm_suppressed[channel] &= ~evbit; + + if (!(data->alarm_monitored[channel] & evbit)) + return; + + ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel), + data->alarm_r_limit[channel]); + if (ret) + dev_warn(data->dev, "High limit enabling failed for channel%d\n", + channel); +} + +static void bd79124_alm_enable_worker(struct work_struct *work) +{ + int i; + struct bd79124_data *data = container_of(work, struct bd79124_data, + alm_enable_work.work); + + /* Take the mutex so there is no race with user disabling the alarm */ + guard(mutex)(&data->mutex); + for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { + bd79124_re_enable_hi(data, i); + bd79124_re_enable_lo(data, i); + } +} + +static int __bd79124_event_ratelimit(struct bd79124_data *data, int reg, + unsigned int limit) +{ + int ret; + + if (limit > BD79124_HIGH_LIMIT_MAX) + return -EINVAL; + + ret = bd79124_write_int_to_reg(data, reg, limit); + if (ret) + return ret; + + /* + * We use 1 sec 'grace period'. At the moment I see no reason to make + * this user configurable. We need an ABI for this if configuration is + * needed. + */ + schedule_delayed_work(&data->alm_enable_work, msecs_to_jiffies(1000)); + + return 0; +} + +static int bd79124_event_ratelimit_hi(struct bd79124_data *data, + unsigned int channel) +{ + guard(mutex)(&data->mutex); + data->alarm_suppressed[channel] |= BIT(IIO_EV_DIR_RISING); + + return __bd79124_event_ratelimit(data, + BD79124_GET_HIGH_LIMIT_REG(channel), + BD79124_HIGH_LIMIT_MAX); +} + +static int bd79124_event_ratelimit_lo(struct bd79124_data *data, + unsigned int channel) +{ + guard(mutex)(&data->mutex); + data->alarm_suppressed[channel] |= BIT(IIO_EV_DIR_FALLING); + + return __bd79124_event_ratelimit(data, + BD79124_GET_LOW_LIMIT_REG(channel), + BD79124_LOW_LIMIT_MIN); +} + +static irqreturn_t bd79124_event_handler(int irq, void *priv) +{ + unsigned int i_hi, i_lo; + int i, ret; + struct iio_dev *iio_dev = priv; + struct bd79124_data *data = iio_priv(iio_dev); + + /* + * Return IRQ_NONE if bailing-out without acking. This allows the IRQ + * subsystem to disable the offending IRQ line if we get a hardware + * problem. This behaviour has saved my poor bottom a few times in the + * past as, instead of getting unusably unresponsive, the system has + * spilled out the magic words "...nobody cared". + */ + ret = regmap_read(data->map, BD79124_REG_EVENT_FLAG_HI, &i_hi); + if (ret) + return IRQ_NONE; + + ret = regmap_read(data->map, BD79124_REG_EVENT_FLAG_LO, &i_lo); + if (ret) + return IRQ_NONE; + + if (!i_lo && !i_hi) + return IRQ_NONE; + + for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { + u64 ecode; + + if (BIT(i) & i_hi) { + ecode = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + + iio_push_event(iio_dev, ecode, data->timestamp); + /* + * The BD79124 keeps the IRQ asserted for as long as + * the voltage exceeds the threshold. It causes the IRQ + * to keep firing. + * + * Disable the event for the channel and schedule the + * re-enabling the event later to prevent storm of + * events. + */ + ret = bd79124_event_ratelimit_hi(data, i); + if (ret) + return IRQ_NONE; + } + if (BIT(i) & i_lo) { + ecode = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + + iio_push_event(iio_dev, ecode, data->timestamp); + ret = bd79124_event_ratelimit_lo(data, i); + if (ret) + return IRQ_NONE; + } + } + + ret = regmap_write(data->map, BD79124_REG_EVENT_FLAG_HI, i_hi); + if (ret) + return IRQ_NONE; + + ret = regmap_write(data->map, BD79124_REG_EVENT_FLAG_LO, i_lo); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static irqreturn_t bd79124_irq_handler(int irq, void *priv) +{ + struct iio_dev *iio_dev = priv; + struct bd79124_data *data = iio_priv(iio_dev); + + data->timestamp = iio_get_time_ns(iio_dev); + + return IRQ_WAKE_THREAD; +} + +static int bd79124_chan_init(struct bd79124_data *data, int channel) +{ + int ret; + + ret = regmap_write(data->map, BD79124_GET_HIGH_LIMIT_REG(channel), + BD79124_HIGH_LIMIT_MAX); + if (ret) + return ret; + + return regmap_write(data->map, BD79124_GET_LOW_LIMIT_REG(channel), + BD79124_LOW_LIMIT_MIN); +} + +static int bd79124_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels) +{ + int i, gpio_channels; + + /* + * Let's initialize the mux config to say that all 8 channels are + * GPIOs. Then we can just loop through the iio_chan_spec and clear the + * bits for found ADC channels. + */ + gpio_channels = GENMASK(7, 0); + for (i = 0; i < num_channels; i++) + gpio_channels &= ~BIT(cs[i].channel); + + return gpio_channels; +} + +static int bd79124_hw_init(struct bd79124_data *data) +{ + unsigned int regval; + int ret, i; + + for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { + ret = bd79124_chan_init(data, i); + if (ret) + return ret; + data->alarm_r_limit[i] = BD79124_HIGH_LIMIT_MAX; + } + /* Stop auto sequencer */ + ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_START); + if (ret) + return ret; + + /* Enable writing the measured values to the regsters */ + ret = regmap_set_bits(data->map, BD79124_REG_GEN_CFG, + BD79124_MSK_STATS_EN); + if (ret) + return ret; + + /* Set no channels to be auto-measured */ + ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, 0x0); + if (ret) + return ret; + + /* Set no channels to be manually measured */ + ret = regmap_write(data->map, BD79124_REG_MANUAL_CHANNELS, 0x0); + if (ret) + return ret; + + regval = FIELD_PREP(BD79124_MSK_AUTO_INTERVAL, BD79124_INTERVAL_750_US); + ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_AUTO_INTERVAL, regval); + if (ret) + return ret; + + /* Sequencer mode to auto */ + ret = regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, + BD79124_MSK_SEQ_SEQ); + if (ret) + return ret; + + /* Don't start the measurement */ + regval = FIELD_PREP(BD79124_MSK_CONV_MODE, BD79124_CONV_MODE_MANSEQ); + return regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, + BD79124_MSK_CONV_MODE, regval); +} + +static int bd79124_probe(struct i2c_client *i2c) +{ + struct bd79124_data *data; + struct iio_dev *iio_dev; + const struct iio_chan_spec *template; + struct iio_chan_spec *cs; + struct device *dev = &i2c->dev; + unsigned int gpio_pins; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!iio_dev) + return -ENOMEM; + + data = iio_priv(iio_dev); + data->dev = dev; + data->map = devm_regmap_init_i2c(i2c, &bd79124_regmap); + if (IS_ERR(data->map)) + return dev_err_probe(dev, PTR_ERR(data->map), + "Failed to initialize Regmap\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get the Vdd\n"); + + data->vmax = ret; + + ret = devm_regulator_get_enable(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable I/O voltage\n"); + + ret = devm_delayed_work_autocancel(dev, &data->alm_enable_work, + bd79124_alm_enable_worker); + if (ret) + return ret; + + if (i2c->irq) { + template = &bd79124_chan_template; + } else { + template = &bd79124_chan_template_noirq; + dev_dbg(dev, "No IRQ found, events disabled\n"); + } + + ret = devm_mutex_init(dev, &data->mutex); + if (ret) + return ret; + + ret = devm_iio_adc_device_alloc_chaninfo_se(dev, template, + BD79124_MAX_NUM_CHANNELS - 1, &cs); + if (ret < 0) { + /* Register all pins as GPOs if there are no ADC channels */ + if (ret == -ENOENT) + goto register_gpios; + return ret; + } + iio_dev->channels = cs; + iio_dev->num_channels = ret; + iio_dev->info = &bd79124_info; + iio_dev->name = "bd79124"; + iio_dev->modes = INDIO_DIRECT_MODE; + + ret = bd79124_hw_init(data); + if (ret) + return ret; + + if (i2c->irq > 0) { + ret = devm_request_threaded_irq(dev, i2c->irq, + bd79124_irq_handler, &bd79124_event_handler, + IRQF_ONESHOT, "adc-thresh-alert", iio_dev); + if (ret) + return dev_err_probe(data->dev, ret, + "Failed to register IRQ\n"); + } + + ret = devm_iio_device_register(data->dev, iio_dev); + if (ret) + return dev_err_probe(data->dev, ret, "Failed to register ADC\n"); + +register_gpios: + gpio_pins = bd79124_get_gpio_pins(iio_dev->channels, + iio_dev->num_channels); + + /* + * The mux should default to "all ADCs", but better to not trust it. + * Thus we do set the mux even when we have only ADCs and no GPOs. + */ + ret = regmap_write(data->map, BD79124_REG_PINCFG, gpio_pins); + if (ret) + return ret; + + /* No GPOs if all channels are reserved for ADC, so we're done. */ + if (!gpio_pins) + return 0; + + data->gpio_valid_mask = gpio_pins; + data->gc = bd79124gpo_chip; + data->gc.parent = dev; + + return devm_gpiochip_add_data(dev, &data->gc, data); +} + +static const struct of_device_id bd79124_of_match[] = { + { .compatible = "rohm,bd79124" }, + { } +}; +MODULE_DEVICE_TABLE(of, bd79124_of_match); + +static const struct i2c_device_id bd79124_id[] = { + { "bd79124" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bd79124_id); + +static struct i2c_driver bd79124_driver = { + .driver = { + .name = "bd79124", + .of_match_table = bd79124_of_match, + }, + .probe = bd79124_probe, + .id_table = bd79124_id, +}; +module_i2c_driver(bd79124_driver); + +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); +MODULE_DESCRIPTION("Driver for ROHM BD79124 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index 56ed948a8ae1..6ff47415a222 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; } @@ -634,7 +644,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) struct device *dev = priv->dev; struct { u16 vals[RTQ6056_MAX_CHANNEL]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } data; unsigned int raw; int i = 0, bit, ret; @@ -656,7 +666,8 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) data.vals[i++] = raw; } - iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data, sizeof(data), + iio_get_time_ns(indio_dev)); out: pm_runtime_mark_last_busy(dev); diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index cd3a7e46ea53..9674d48074c9 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -8,12 +8,14 @@ */ #include <linux/bitfield.h> -#include <linux/clk.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> +#include <linux/iio/adc-helpers.h> #include <linux/iio/iio.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -32,20 +34,15 @@ #define RZG2L_ADM1_MS BIT(2) #define RZG2L_ADM1_BS BIT(4) #define RZG2L_ADM1_EGA_MASK GENMASK(13, 12) -#define RZG2L_ADM2_CHSEL_MASK GENMASK(7, 0) #define RZG2L_ADM3_ADIL_MASK GENMASK(31, 24) #define RZG2L_ADM3_ADCMP_MASK GENMASK(23, 16) -#define RZG2L_ADM3_ADCMP_E FIELD_PREP(RZG2L_ADM3_ADCMP_MASK, 0xe) -#define RZG2L_ADM3_ADSMP_MASK GENMASK(15, 0) #define RZG2L_ADINT 0x20 -#define RZG2L_ADINT_INTEN_MASK GENMASK(7, 0) #define RZG2L_ADINT_CSEEN BIT(16) #define RZG2L_ADINT_INTS BIT(31) #define RZG2L_ADSTS 0x24 #define RZG2L_ADSTS_CSEST BIT(16) -#define RZG2L_ADSTS_INTST_MASK GENMASK(7, 0) #define RZG2L_ADIVC 0x28 #define RZG2L_ADIVC_DIVADC_MASK GENMASK(8, 0) @@ -56,12 +53,28 @@ #define RZG2L_ADCR(n) (0x30 + ((n) * 0x4)) #define RZG2L_ADCR_AD_MASK GENMASK(11, 0) -#define RZG2L_ADSMP_DEFAULT_SAMPLING 0x578 - -#define RZG2L_ADC_MAX_CHANNELS 8 -#define RZG2L_ADC_CHN_MASK 0x7 +#define RZG2L_ADC_MAX_CHANNELS 9 #define RZG2L_ADC_TIMEOUT usecs_to_jiffies(1 * 4) +/** + * struct rzg2l_adc_hw_params - ADC hardware specific parameters + * @default_adsmp: default ADC sampling period (see ADM3 register); index 0 is + * used for voltage channels, index 1 is used for temperature channel + * @adsmp_mask: ADC sampling period mask (see ADM3 register) + * @adint_inten_mask: conversion end interrupt mask (see ADINT register) + * @default_adcmp: default ADC cmp (see ADM3 register) + * @num_channels: number of supported channels + * @adivc: specifies if ADVIC register is available + */ +struct rzg2l_adc_hw_params { + u16 default_adsmp[2]; + u16 adsmp_mask; + u16 adint_inten_mask; + u8 default_adcmp; + u8 num_channels; + bool adivc; +}; + struct rzg2l_adc_data { const struct iio_chan_spec *channels; u8 num_channels; @@ -69,25 +82,36 @@ struct rzg2l_adc_data { struct rzg2l_adc { void __iomem *base; - struct clk *pclk; - struct clk *adclk; struct reset_control *presetn; struct reset_control *adrstn; - struct completion completion; const struct rzg2l_adc_data *data; + const struct rzg2l_adc_hw_params *hw_params; + struct completion completion; struct mutex lock; u16 last_val[RZG2L_ADC_MAX_CHANNELS]; + bool was_rpm_active; }; -static const char * const rzg2l_adc_channel_name[] = { - "adc0", - "adc1", - "adc2", - "adc3", - "adc4", - "adc5", - "adc6", - "adc7", +/** + * struct rzg2l_adc_channel - ADC channel descriptor + * @name: ADC channel name + * @type: ADC channel type + */ +struct rzg2l_adc_channel { + const char * const name; + enum iio_chan_type type; +}; + +static const struct rzg2l_adc_channel rzg2l_adc_channels[] = { + { "adc0", IIO_VOLTAGE }, + { "adc1", IIO_VOLTAGE }, + { "adc2", IIO_VOLTAGE }, + { "adc3", IIO_VOLTAGE }, + { "adc4", IIO_VOLTAGE }, + { "adc5", IIO_VOLTAGE }, + { "adc6", IIO_VOLTAGE }, + { "adc7", IIO_VOLTAGE }, + { "adc8", IIO_TEMP }, }; static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg) @@ -115,7 +139,7 @@ static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on) static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start) { - int timeout = 5; + int ret; u32 reg; reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); @@ -128,15 +152,10 @@ static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start) if (start) return; - do { - usleep_range(100, 200); - reg = rzg2l_adc_readl(adc, RZG2L_ADM(0)); - timeout--; - if (!timeout) { - pr_err("%s stopping ADC timed out\n", __func__); - break; - } - } while (((reg & RZG2L_ADM0_ADBSY) || (reg & RZG2L_ADM0_ADCE))); + ret = read_poll_timeout(rzg2l_adc_readl, reg, !(reg & (RZG2L_ADM0_ADBSY | RZG2L_ADM0_ADCE)), + 200, 1000, true, adc, RZG2L_ADM(0)); + if (ret) + pr_err("%s stopping ADC timed out\n", __func__); } static void rzg2l_set_trigger(struct rzg2l_adc *adc) @@ -158,8 +177,18 @@ static void rzg2l_set_trigger(struct rzg2l_adc *adc) rzg2l_adc_writel(adc, RZG2L_ADM(1), reg); } +static u8 rzg2l_adc_ch_to_adsmp_index(u8 ch) +{ + if (rzg2l_adc_channels[ch].type == IIO_VOLTAGE) + return 0; + + return 1; +} + static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) { + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + u8 index = rzg2l_adc_ch_to_adsmp_index(ch); u32 reg; if (rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_ADBSY) @@ -169,10 +198,15 @@ static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) /* Select analog input channel subjected to conversion. */ reg = rzg2l_adc_readl(adc, RZG2L_ADM(2)); - reg &= ~RZG2L_ADM2_CHSEL_MASK; + reg &= ~GENMASK(hw_params->num_channels - 1, 0); reg |= BIT(ch); rzg2l_adc_writel(adc, RZG2L_ADM(2), reg); + reg = rzg2l_adc_readl(adc, RZG2L_ADM(3)); + reg &= ~hw_params->adsmp_mask; + reg |= hw_params->default_adsmp[index]; + rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); + /* * Setup ADINT * INTS[31] - Select pulse signal @@ -181,36 +215,26 @@ static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch) */ reg = rzg2l_adc_readl(adc, RZG2L_ADINT); reg &= ~RZG2L_ADINT_INTS; - reg &= ~RZG2L_ADINT_INTEN_MASK; + reg &= ~hw_params->adint_inten_mask; reg |= (RZG2L_ADINT_CSEEN | BIT(ch)); rzg2l_adc_writel(adc, RZG2L_ADINT, reg); return 0; } -static int rzg2l_adc_set_power(struct iio_dev *indio_dev, bool on) -{ - struct device *dev = indio_dev->dev.parent; - - if (on) - return pm_runtime_resume_and_get(dev); - - return pm_runtime_put_sync(dev); -} - static int rzg2l_adc_conversion(struct iio_dev *indio_dev, struct rzg2l_adc *adc, u8 ch) { + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; + struct device *dev = indio_dev->dev.parent; int ret; - ret = rzg2l_adc_set_power(indio_dev, true); + ret = pm_runtime_resume_and_get(dev); if (ret) return ret; ret = rzg2l_adc_conversion_setup(adc, ch); - if (ret) { - rzg2l_adc_set_power(indio_dev, false); - return ret; - } + if (ret) + goto rpm_put; reinit_completion(&adc->completion); @@ -218,13 +242,16 @@ static int rzg2l_adc_conversion(struct iio_dev *indio_dev, struct rzg2l_adc *adc if (!wait_for_completion_timeout(&adc->completion, RZG2L_ADC_TIMEOUT)) { rzg2l_adc_writel(adc, RZG2L_ADINT, - rzg2l_adc_readl(adc, RZG2L_ADINT) & ~RZG2L_ADINT_INTEN_MASK); - rzg2l_adc_start_stop(adc, false); - rzg2l_adc_set_power(indio_dev, false); - return -ETIMEDOUT; + rzg2l_adc_readl(adc, RZG2L_ADINT) & ~hw_params->adint_inten_mask); + ret = -ETIMEDOUT; } - return rzg2l_adc_set_power(indio_dev, false); + rzg2l_adc_start_stop(adc, false); + +rpm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; } static int rzg2l_adc_read_raw(struct iio_dev *indio_dev, @@ -233,24 +260,22 @@ static int rzg2l_adc_read_raw(struct iio_dev *indio_dev, { struct rzg2l_adc *adc = iio_priv(indio_dev); int ret; - u8 ch; switch (mask) { - case IIO_CHAN_INFO_RAW: - if (chan->type != IIO_VOLTAGE) + case IIO_CHAN_INFO_RAW: { + if (chan->type != IIO_VOLTAGE && chan->type != IIO_TEMP) return -EINVAL; - mutex_lock(&adc->lock); - ch = chan->channel & RZG2L_ADC_CHN_MASK; - ret = rzg2l_adc_conversion(indio_dev, adc, ch); - if (ret) { - mutex_unlock(&adc->lock); + guard(mutex)(&adc->lock); + + ret = rzg2l_adc_conversion(indio_dev, adc, chan->channel); + if (ret) return ret; - } - *val = adc->last_val[ch]; - mutex_unlock(&adc->lock); + + *val = adc->last_val[chan->channel]; return IIO_VAL_INT; + } default: return -EINVAL; @@ -261,7 +286,7 @@ static int rzg2l_adc_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan, char *label) { - return sysfs_emit(label, "%s\n", rzg2l_adc_channel_name[chan->channel]); + return sysfs_emit(label, "%s\n", rzg2l_adc_channels[chan->channel].name); } static const struct iio_info rzg2l_adc_iio_info = { @@ -272,6 +297,7 @@ static const struct iio_info rzg2l_adc_iio_info = { static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) { struct rzg2l_adc *adc = dev_id; + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; unsigned long intst; u32 reg; int ch; @@ -284,11 +310,11 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) return IRQ_HANDLED; } - intst = reg & RZG2L_ADSTS_INTST_MASK; + intst = reg & GENMASK(hw_params->num_channels - 1, 0); if (!intst) return IRQ_NONE; - for_each_set_bit(ch, &intst, RZG2L_ADC_MAX_CHANNELS) + for_each_set_bit(ch, &intst, hw_params->num_channels) adc->last_val[ch] = rzg2l_adc_readl(adc, RZG2L_ADCR(ch)) & RZG2L_ADCR_AD_MASK; /* clear the channel interrupt */ @@ -299,50 +325,39 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static const struct iio_chan_spec rzg2l_adc_chan_template = { + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +}; + static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc) { + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; struct iio_chan_spec *chan_array; struct rzg2l_adc_data *data; - unsigned int channel; int num_channels; - int ret; u8 i; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - num_channels = device_get_child_node_count(&pdev->dev); - if (!num_channels) { - dev_err(&pdev->dev, "no channel children\n"); - return -ENODEV; - } - - if (num_channels > RZG2L_ADC_MAX_CHANNELS) { - dev_err(&pdev->dev, "num of channel children out of range\n"); - return -EINVAL; - } - - chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array), - GFP_KERNEL); - if (!chan_array) - return -ENOMEM; + num_channels = devm_iio_adc_device_alloc_chaninfo_se(&pdev->dev, + &rzg2l_adc_chan_template, + hw_params->num_channels - 1, + &chan_array); + if (num_channels < 0) + return num_channels; - i = 0; - device_for_each_child_node_scoped(&pdev->dev, fwnode) { - ret = fwnode_property_read_u32(fwnode, "reg", &channel); - if (ret) - return ret; + if (num_channels > hw_params->num_channels) + return dev_err_probe(&pdev->dev, -EINVAL, + "num of channel children out of range\n"); - if (channel >= RZG2L_ADC_MAX_CHANNELS) - return -EINVAL; + for (i = 0; i < num_channels; i++) { + int channel = chan_array[i].channel; - chan_array[i].type = IIO_VOLTAGE; - chan_array[i].indexed = 1; - chan_array[i].channel = channel; - chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - chan_array[i].datasheet_name = rzg2l_adc_channel_name[channel]; - i++; + chan_array[i].datasheet_name = rzg2l_adc_channels[channel].name; + chan_array[i].type = rzg2l_adc_channels[channel].type; } data->num_channels = num_channels; @@ -352,13 +367,13 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l return 0; } -static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) +static int rzg2l_adc_hw_init(struct device *dev, struct rzg2l_adc *adc) { - int timeout = 5; + const struct rzg2l_adc_hw_params *hw_params = adc->hw_params; u32 reg; int ret; - ret = clk_prepare_enable(adc->pclk); + ret = pm_runtime_resume_and_get(dev); if (ret) return ret; @@ -367,21 +382,19 @@ static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) reg |= RZG2L_ADM0_SRESB; rzg2l_adc_writel(adc, RZG2L_ADM(0), reg); - while (!(rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_SRESB)) { - if (!timeout) { - ret = -EBUSY; - goto exit_hw_init; - } - timeout--; - usleep_range(100, 200); + ret = read_poll_timeout(rzg2l_adc_readl, reg, reg & RZG2L_ADM0_SRESB, + 200, 1000, false, adc, RZG2L_ADM(0)); + if (ret) + goto exit_hw_init; + + if (hw_params->adivc) { + /* Only division by 4 can be set */ + reg = rzg2l_adc_readl(adc, RZG2L_ADIVC); + reg &= ~RZG2L_ADIVC_DIVADC_MASK; + reg |= RZG2L_ADIVC_DIVADC_4; + rzg2l_adc_writel(adc, RZG2L_ADIVC, reg); } - /* Only division by 4 can be set */ - reg = rzg2l_adc_readl(adc, RZG2L_ADIVC); - reg &= ~RZG2L_ADIVC_DIVADC_MASK; - reg |= RZG2L_ADIVC_DIVADC_4; - rzg2l_adc_writel(adc, RZG2L_ADIVC, reg); - /* * Setup AMD3 * ADIL[31:24] - Should be always set to 0 @@ -391,35 +404,18 @@ static int rzg2l_adc_hw_init(struct rzg2l_adc *adc) reg = rzg2l_adc_readl(adc, RZG2L_ADM(3)); reg &= ~RZG2L_ADM3_ADIL_MASK; reg &= ~RZG2L_ADM3_ADCMP_MASK; - reg &= ~RZG2L_ADM3_ADSMP_MASK; - reg |= (RZG2L_ADM3_ADCMP_E | RZG2L_ADSMP_DEFAULT_SAMPLING); + reg &= ~hw_params->adsmp_mask; + reg |= FIELD_PREP(RZG2L_ADM3_ADCMP_MASK, hw_params->default_adcmp) | + hw_params->default_adsmp[0]; + rzg2l_adc_writel(adc, RZG2L_ADM(3), reg); exit_hw_init: - clk_disable_unprepare(adc->pclk); - + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return ret; } -static void rzg2l_adc_pm_runtime_disable(void *data) -{ - struct device *dev = data; - - pm_runtime_disable(dev->parent); -} - -static void rzg2l_adc_pm_runtime_set_suspended(void *data) -{ - struct device *dev = data; - - pm_runtime_set_suspended(dev->parent); -} - -static void rzg2l_adc_reset_assert(void *data) -{ - reset_control_assert(data); -} - static int rzg2l_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -434,6 +430,10 @@ static int rzg2l_adc_probe(struct platform_device *pdev) adc = iio_priv(indio_dev); + adc->hw_params = device_get_match_data(dev); + if (!adc->hw_params || adc->hw_params->num_channels > RZG2L_ADC_MAX_CHANNELS) + return -EINVAL; + ret = rzg2l_adc_parse_properties(pdev, adc); if (ret) return ret; @@ -444,63 +444,28 @@ static int rzg2l_adc_probe(struct platform_device *pdev) if (IS_ERR(adc->base)) return PTR_ERR(adc->base); - adc->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(adc->pclk)) { - dev_err(dev, "Failed to get pclk"); - return PTR_ERR(adc->pclk); - } - - adc->adclk = devm_clk_get(dev, "adclk"); - if (IS_ERR(adc->adclk)) { - dev_err(dev, "Failed to get adclk"); - return PTR_ERR(adc->adclk); - } - - adc->adrstn = devm_reset_control_get_exclusive(dev, "adrst-n"); - if (IS_ERR(adc->adrstn)) { - dev_err(dev, "failed to get adrstn\n"); - return PTR_ERR(adc->adrstn); - } + adc->adrstn = devm_reset_control_get_exclusive_deasserted(dev, "adrst-n"); + if (IS_ERR(adc->adrstn)) + return dev_err_probe(dev, PTR_ERR(adc->adrstn), + "failed to get/deassert adrst-n\n"); - adc->presetn = devm_reset_control_get_exclusive(dev, "presetn"); - if (IS_ERR(adc->presetn)) { - dev_err(dev, "failed to get presetn\n"); - return PTR_ERR(adc->presetn); - } - - ret = reset_control_deassert(adc->adrstn); - if (ret) { - dev_err(&pdev->dev, "failed to deassert adrstn pin, %d\n", ret); - return ret; - } + adc->presetn = devm_reset_control_get_exclusive_deasserted(dev, "presetn"); + if (IS_ERR(adc->presetn)) + return dev_err_probe(dev, PTR_ERR(adc->presetn), + "failed to get/deassert presetn\n"); - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_reset_assert, adc->adrstn); - if (ret) { - dev_err(&pdev->dev, "failed to register adrstn assert devm action, %d\n", - ret); - return ret; - } - - ret = reset_control_deassert(adc->presetn); - if (ret) { - dev_err(&pdev->dev, "failed to deassert presetn pin, %d\n", ret); + pm_runtime_set_autosuspend_delay(dev, 300); + pm_runtime_use_autosuspend(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) return ret; - } - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_reset_assert, adc->presetn); - if (ret) { - dev_err(&pdev->dev, "failed to register presetn assert devm action, %d\n", - ret); - return ret; - } + platform_set_drvdata(pdev, indio_dev); - ret = rzg2l_adc_hw_init(adc); - if (ret) { - dev_err(&pdev->dev, "failed to initialize ADC HW, %d\n", ret); - return ret; - } + ret = rzg2l_adc_hw_init(dev, adc); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to initialize ADC HW\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -513,72 +478,130 @@ static int rzg2l_adc_probe(struct platform_device *pdev) init_completion(&adc->completion); - platform_set_drvdata(pdev, indio_dev); - indio_dev->name = DRIVER_NAME; indio_dev->info = &rzg2l_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adc->data->channels; indio_dev->num_channels = adc->data->num_channels; - pm_runtime_set_suspended(dev); - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_pm_runtime_set_suspended, &indio_dev->dev); - if (ret) - return ret; - - pm_runtime_enable(dev); - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_adc_pm_runtime_disable, &indio_dev->dev); - if (ret) - return ret; - return devm_iio_device_register(dev, indio_dev); } +static const struct rzg2l_adc_hw_params rzg2l_hw_params = { + .num_channels = 8, + .default_adcmp = 0xe, + .default_adsmp = { 0x578 }, + .adsmp_mask = GENMASK(15, 0), + .adint_inten_mask = GENMASK(7, 0), + .adivc = true +}; + +static const struct rzg2l_adc_hw_params rzg3s_hw_params = { + .num_channels = 9, + .default_adcmp = 0x1d, + .default_adsmp = { 0x7f, 0xff }, + .adsmp_mask = GENMASK(7, 0), + .adint_inten_mask = GENMASK(11, 0), +}; + static const struct of_device_id rzg2l_adc_match[] = { - { .compatible = "renesas,rzg2l-adc",}, - { /* sentinel */ } + { .compatible = "renesas,r9a08g045-adc", .data = &rzg3s_hw_params }, + { .compatible = "renesas,rzg2l-adc", .data = &rzg2l_hw_params }, + { } }; MODULE_DEVICE_TABLE(of, rzg2l_adc_match); -static int __maybe_unused rzg2l_adc_pm_runtime_suspend(struct device *dev) +static int rzg2l_adc_pm_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct rzg2l_adc *adc = iio_priv(indio_dev); rzg2l_adc_pwr(adc, false); - clk_disable_unprepare(adc->adclk); - clk_disable_unprepare(adc->pclk); return 0; } -static int __maybe_unused rzg2l_adc_pm_runtime_resume(struct device *dev) +static int rzg2l_adc_pm_runtime_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct rzg2l_adc *adc = iio_priv(indio_dev); + + rzg2l_adc_pwr(adc, true); + + return 0; +} + +static int rzg2l_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + struct reset_control_bulk_data resets[] = { + { .rstc = adc->presetn }, + { .rstc = adc->adrstn }, + }; int ret; - ret = clk_prepare_enable(adc->pclk); + if (pm_runtime_suspended(dev)) { + adc->was_rpm_active = false; + } else { + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + adc->was_rpm_active = true; + } + + ret = reset_control_bulk_assert(ARRAY_SIZE(resets), resets); if (ret) - return ret; + goto rpm_restore; + + return 0; + +rpm_restore: + if (adc->was_rpm_active) + pm_runtime_force_resume(dev); - ret = clk_prepare_enable(adc->adclk); - if (ret) { - clk_disable_unprepare(adc->pclk); + return ret; +} + +static int rzg2l_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rzg2l_adc *adc = iio_priv(indio_dev); + struct reset_control_bulk_data resets[] = { + { .rstc = adc->adrstn }, + { .rstc = adc->presetn }, + }; + int ret; + + ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets); + if (ret) return ret; + + if (adc->was_rpm_active) { + ret = pm_runtime_force_resume(dev); + if (ret) + goto resets_restore; } - rzg2l_adc_pwr(adc, true); + ret = rzg2l_adc_hw_init(dev, adc); + if (ret) + goto rpm_restore; return 0; + +rpm_restore: + if (adc->was_rpm_active) { + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } +resets_restore: + reset_control_bulk_assert(ARRAY_SIZE(resets), resets); + return ret; } static const struct dev_pm_ops rzg2l_adc_pm_ops = { - SET_RUNTIME_PM_OPS(rzg2l_adc_pm_runtime_suspend, - rzg2l_adc_pm_runtime_resume, - NULL) + RUNTIME_PM_OPS(rzg2l_adc_pm_runtime_suspend, rzg2l_adc_pm_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(rzg2l_adc_suspend, rzg2l_adc_resume) }; static struct platform_driver rzg2l_adc_driver = { @@ -586,7 +609,7 @@ static struct platform_driver rzg2l_adc_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = rzg2l_adc_match, - .pm = &rzg2l_adc_pm_ops, + .pm = pm_ptr(&rzg2l_adc_pm_ops), }, }; @@ -595,3 +618,4 @@ module_platform_driver(rzg2l_adc_driver); MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); MODULE_DESCRIPTION("Renesas RZ/G2L ADC driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index b6dd096391c1..e3a865c79686 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -345,7 +345,7 @@ static int spear_adc_probe(struct platform_device *pdev) static const struct of_device_id spear_adc_dt_ids[] = { { .compatible = "st,spear600-adc", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 2201ee9987ae..21c04a98b3b6 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -421,18 +421,18 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, return priv->irq[i]; } - priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0, - &stm32_adc_domain_ops, - priv); + priv->domain = irq_domain_create_simple(of_fwnode_handle(np), + STM32_ADC_MAX_ADCS, 0, + &stm32_adc_domain_ops, + priv); if (!priv->domain) { dev_err(&pdev->dev, "Failed to add irq domain\n"); return -ENOMEM; } - for (i = 0; i < priv->cfg->num_irqs; i++) { - irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler); - irq_set_handler_data(priv->irq[i], priv); - } + for (i = 0; i < priv->cfg->num_irqs; i++) + irq_set_chained_handler_and_data(priv->irq[i], + stm32_adc_irq_handler, priv); return 0; } @@ -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-core.h b/drivers/iio/adc/stm32-adc-core.h index 73b2c2e91c08..db50a9f3b922 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -10,6 +10,9 @@ #ifndef __STM32_ADC_H #define __STM32_ADC_H +#include <linux/bitfield.h> +#include <linux/bits.h> + /* * STM32 - ADC global register map * ________________________________________________________ @@ -91,6 +94,7 @@ #define STM32H7_ADC_IER 0x04 #define STM32H7_ADC_CR 0x08 #define STM32H7_ADC_CFGR 0x0C +#define STM32H7_ADC_CFGR2 0x10 #define STM32H7_ADC_SMPR1 0x14 #define STM32H7_ADC_SMPR2 0x18 #define STM32H7_ADC_PCSEL 0x1C @@ -160,6 +164,13 @@ #define STM32H7_DMNGT_SHIFT 0 #define STM32H7_DMNGT_MASK GENMASK(1, 0) +/* STM32H7_ADC_CFGR2 bit fields */ +#define STM32H7_OVSR_MASK GENMASK(25, 16) /* Correspond to OSVR field in datasheet */ +#define STM32H7_OVSR(v) FIELD_PREP(STM32H7_OVSR_MASK, v) +#define STM32H7_OVSS_MASK GENMASK(8, 5) +#define STM32H7_OVSS(v) FIELD_PREP(STM32H7_OVSS_MASK, v) +#define STM32H7_ROVSE BIT(0) + enum stm32h7_adc_dmngt { STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */ STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */ @@ -226,6 +237,12 @@ enum stm32h7_adc_dmngt { #define STM32MP13_RES_SHIFT 3 #define STM32MP13_RES_MASK GENMASK(4, 3) +/* STM32MP13_ADC_CFGR2 bit fields */ +#define STM32MP13_OVSR_MASK GENMASK(4, 2) +#define STM32MP13_OVSR(v) FIELD_PREP(STM32MP13_OVSR_MASK, v) +#define STM32MP13_OVSS_MASK GENMASK(8, 5) +#define STM32MP13_OVSS(v) FIELD_PREP(STM32MP13_OVSS_MASK, v) + /* STM32MP13_ADC_DIFSEL - bit fields */ #define STM32MP13_DIFSEL_MASK GENMASK(18, 0) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 9d3b23efcc06..e84babf43385 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -6,6 +6,7 @@ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>. */ +#include <linux/array_size.h> #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/delay.h> @@ -202,11 +203,13 @@ struct stm32_adc; * @has_boostmode: boost mode support flag * @has_linearcal: linear calibration support flag * @has_presel: channel preselection support flag + * @has_oversampling: oversampling support flag * @prepare: optional prepare routine (power-up, enable) * @start_conv: routine to start conversions * @stop_conv: routine to stop conversions * @unprepare: optional unprepare routine (disable, power-down) * @irq_clear: routine to clear irqs + * @set_ovs: routine to set oversampling configuration * @smp_cycles: programmable sampling time (ADC clock cycles) * @ts_int_ch: pointer to array of internal channels minimum sampling time in ns */ @@ -219,11 +222,13 @@ struct stm32_adc_cfg { bool has_boostmode; bool has_linearcal; bool has_presel; + bool has_oversampling; int (*prepare)(struct iio_dev *); void (*start_conv)(struct iio_dev *, bool dma); void (*stop_conv)(struct iio_dev *); void (*unprepare)(struct iio_dev *); void (*irq_clear)(struct iio_dev *indio_dev, u32 msk); + void (*set_ovs)(struct iio_dev *indio_dev, u32 ovs_idx); const unsigned int *smp_cycles; const unsigned int *ts_int_ch; }; @@ -255,6 +260,7 @@ struct stm32_adc_cfg { * @num_diff: number of differential channels * @int_ch: internal channel indexes array * @nsmps: number of channels with optional sample time + * @ovs_idx: current oversampling ratio index (in oversampling array) */ struct stm32_adc { struct stm32_adc_common *common; @@ -282,6 +288,7 @@ struct stm32_adc { u32 num_diff; int int_ch[STM32_ADC_INT_CH_NB]; int nsmps; + int ovs_idx; }; struct stm32_adc_diff_channel { @@ -293,12 +300,24 @@ struct stm32_adc_diff_channel { * struct stm32_adc_info - stm32 ADC, per instance config data * @max_channels: Number of channels * @resolutions: available resolutions + * @oversampling: available oversampling ratios * @num_res: number of available resolutions + * @num_ovs: number of available oversampling ratios */ struct stm32_adc_info { int max_channels; const unsigned int *resolutions; + const unsigned int *oversampling; const unsigned int num_res; + const unsigned int num_ovs; +}; + +static const unsigned int stm32h7_adc_oversampling_avail[] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +static const unsigned int stm32mp13_adc_oversampling_avail[] = { + 1, 2, 4, 8, 16, 32, 64, 128, 256, }; static const unsigned int stm32f4_adc_resolutions[] = { @@ -322,14 +341,18 @@ static const unsigned int stm32h7_adc_resolutions[] = { static const struct stm32_adc_info stm32h7_adc_info = { .max_channels = STM32_ADC_CH_MAX, .resolutions = stm32h7_adc_resolutions, + .oversampling = stm32h7_adc_oversampling_avail, .num_res = ARRAY_SIZE(stm32h7_adc_resolutions), + .num_ovs = ARRAY_SIZE(stm32h7_adc_oversampling_avail), }; /* stm32mp13 can have up to 19 channels */ static const struct stm32_adc_info stm32mp13_adc_info = { .max_channels = 19, .resolutions = stm32f4_adc_resolutions, + .oversampling = stm32mp13_adc_oversampling_avail, .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), + .num_ovs = ARRAY_SIZE(stm32mp13_adc_oversampling_avail), }; /* @@ -469,7 +492,7 @@ static struct stm32_adc_trig_info stm32h7_adc_trigs[] = { { LPTIM1_OUT, STM32_EXT18 }, { LPTIM2_OUT, STM32_EXT19 }, { LPTIM3_OUT, STM32_EXT20 }, - {}, + { } }; /* @@ -889,6 +912,56 @@ static void stm32mp13_adc_start_conv(struct iio_dev *indio_dev, bool dma) stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); } +static void stm32h7_adc_set_ovs(struct iio_dev *indio_dev, u32 ovs_idx) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 ovsr_bits, bits, msk; + + msk = STM32H7_ROVSE | STM32H7_OVSR_MASK | STM32H7_OVSS_MASK; + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR2, msk); + + if (!ovs_idx) + return; + + /* + * Only the oversampling ratios corresponding to 2^ovs_idx are exposed in sysfs. + * Oversampling ratios [2,3,...,1024] are mapped on OVSR register values [1,2,...,1023]. + * OVSR = 2^ovs_idx - 1 + * These ratio increase the resolution by ovs_idx bits. Apply a right shift to keep initial + * resolution given by "assigned-resolution-bits" property. + * OVSS = ovs_idx + */ + ovsr_bits = GENMASK(ovs_idx - 1, 0); + bits = STM32H7_ROVSE | STM32H7_OVSS(ovs_idx) | STM32H7_OVSR(ovsr_bits); + + stm32_adc_set_bits(adc, STM32H7_ADC_CFGR2, bits & msk); +} + +static void stm32mp13_adc_set_ovs(struct iio_dev *indio_dev, u32 ovs_idx) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 bits, msk; + + msk = STM32H7_ROVSE | STM32MP13_OVSR_MASK | STM32MP13_OVSS_MASK; + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR2, msk); + + if (!ovs_idx) + return; + + /* + * The oversampling ratios [2,4,8,..,256] are mapped on OVSR register values [0,1,...,7]. + * OVSR = ovs_idx - 1 + * These ratio increase the resolution by ovs_idx bits. Apply a right shift to keep initial + * resolution given by "assigned-resolution-bits" property. + * OVSS = ovs_idx + */ + bits = STM32H7_ROVSE | STM32MP13_OVSS(ovs_idx); + if (ovs_idx - 1) + bits |= STM32MP13_OVSR(ovs_idx - 1); + + stm32_adc_set_bits(adc, STM32H7_ADC_CFGR2, bits & msk); +} + static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev) { struct stm32_adc *adc = iio_priv(indio_dev); @@ -1461,6 +1534,67 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, return ret; } +static int stm32_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + int nb = adc->cfg->adc_info->num_ovs; + unsigned int idx; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val2) + return -EINVAL; + + for (idx = 0; idx < nb; idx++) + if (adc->cfg->adc_info->oversampling[idx] == val) + break; + if (idx >= nb) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + goto err; + + adc->cfg->set_ovs(indio_dev, idx); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + adc->ovs_idx = idx; + +err: + iio_device_release_direct(indio_dev); + + return ret; + default: + return -EINVAL; + } +} + +static int stm32_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long m) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *type = IIO_VAL_INT; + *length = adc->cfg->adc_info->num_ovs; + *vals = adc->cfg->adc_info->oversampling; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + static int stm32_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -1471,9 +1605,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 +1615,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: @@ -1503,6 +1636,10 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, *val = 0; return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = adc->cfg->adc_info->oversampling[adc->ovs_idx]; + return IIO_VAL_INT; + default: return -EINVAL; } @@ -1679,6 +1816,8 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, static const struct iio_info stm32_adc_iio_info = { .read_raw = stm32_adc_read_raw, + .write_raw = stm32_adc_write_raw, + .read_avail = stm32_adc_read_avail, .validate_trigger = stm32_adc_validate_trigger, .hwfifo_set_watermark = stm32_adc_set_watermark, .update_scan_mode = stm32_adc_update_scan_mode, @@ -1859,8 +1998,8 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p) /* reset buffer index */ adc->bufi = 0; - iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, adc->buffer, sizeof(adc->buffer), + pf->timestamp); iio_trigger_notify_done(indio_dev->trig); /* re-enable eoc irq */ @@ -1877,7 +2016,7 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = { .read = iio_enum_available_read, .private = (uintptr_t)&stm32_adc_trig_pol, }, - {}, + { } }; static void stm32_adc_debugfs_init(struct iio_dev *indio_dev) @@ -1972,6 +2111,10 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET); + if (adc->cfg->has_oversampling) { + chan->info_mask_shared_by_all |= BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + chan->info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + } chan->scan_type.sign = 'u'; chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res]; chan->scan_type.storagebits = 16; @@ -2588,6 +2731,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .has_boostmode = true, .has_linearcal = true, .has_presel = true, + .has_oversampling = true, .start_conv = stm32h7_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -2595,6 +2739,7 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .smp_cycles = stm32h7_adc_smp_cycles, .irq_clear = stm32h7_adc_irq_clear, .ts_int_ch = stm32_adc_min_ts_h7, + .set_ovs = stm32h7_adc_set_ovs, }; static const unsigned int stm32_adc_min_ts_mp1[] = { 100, 100, 100, 4300, 9800 }; @@ -2608,6 +2753,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .has_boostmode = true, .has_linearcal = true, .has_presel = true, + .has_oversampling = true, .start_conv = stm32h7_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -2615,6 +2761,7 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = { .smp_cycles = stm32h7_adc_smp_cycles, .irq_clear = stm32h7_adc_irq_clear, .ts_int_ch = stm32_adc_min_ts_mp1, + .set_ovs = stm32h7_adc_set_ovs, }; static const unsigned int stm32_adc_min_ts_mp13[] = { 100, 0, 0, 4300, 9800 }; @@ -2624,6 +2771,7 @@ static const struct stm32_adc_cfg stm32mp13_adc_cfg = { .regs = &stm32mp13_adc_regspec, .adc_info = &stm32mp13_adc_info, .trigs = stm32h7_adc_trigs, + .has_oversampling = true, .start_conv = stm32mp13_adc_start_conv, .stop_conv = stm32h7_adc_stop_conv, .prepare = stm32h7_adc_prepare, @@ -2631,6 +2779,7 @@ static const struct stm32_adc_cfg stm32mp13_adc_cfg = { .smp_cycles = stm32mp13_adc_smp_cycles, .irq_clear = stm32h7_adc_irq_clear, .ts_int_ch = stm32_adc_min_ts_mp13, + .set_ovs = stm32mp13_adc_set_ovs, }; static const struct of_device_id stm32_adc_of_match[] = { diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index fe11b0d8eab3..f583924eb16b 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -108,7 +108,7 @@ static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = { { "SPI_F", 1 }, /* SPI with data on falling edge */ { "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */ { "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */ - {}, + { } }; /* DFSDM channel clock source */ @@ -121,7 +121,7 @@ static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = { { "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING }, /* Internal SPI clock divided by 2 (falling edge) */ { "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING }, - {}, + { } }; static int stm32_dfsdm_str2val(const char *str, @@ -167,7 +167,7 @@ static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = { { LPTIM1_OUT, 26 }, { LPTIM2_OUT, 27 }, { LPTIM3_OUT, 28 }, - {}, + { } }; static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev, @@ -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: @@ -1737,7 +1747,7 @@ static const struct of_device_id stm32_dfsdm_adc_match[] = { .compatible = "st,stm32-dfsdm-dmic", .data = &stm32h7_dfsdm_audio_data, }, - {} + { } }; MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match); diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c index 136b8d9c294f..e4dfe76e6362 100644 --- a/drivers/iio/adc/sun20i-gpadc-iio.c +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -15,6 +15,7 @@ #include <linux/property.h> #include <linux/reset.h> +#include <linux/iio/adc-helpers.h> #include <linux/iio/iio.h> #define SUN20I_GPADC_DRIVER_NAME "sun20i-gpadc" @@ -149,36 +150,23 @@ static void sun20i_gpadc_reset_assert(void *data) reset_control_assert(rst); } +static const struct iio_chan_spec sun20i_gpadc_chan_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev, struct device *dev) { - unsigned int channel; - int num_channels, i, ret; + int num_channels; struct iio_chan_spec *channels; - num_channels = device_get_child_node_count(dev); - if (num_channels == 0) - return dev_err_probe(dev, -ENODEV, "no channel children\n"); - - channels = devm_kcalloc(dev, num_channels, sizeof(*channels), - GFP_KERNEL); - if (!channels) - return -ENOMEM; - - i = 0; - device_for_each_child_node_scoped(dev, node) { - ret = fwnode_property_read_u32(node, "reg", &channel); - if (ret) - return dev_err_probe(dev, ret, "invalid channel number\n"); - - channels[i].type = IIO_VOLTAGE; - channels[i].indexed = 1; - channels[i].channel = channel; - channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); - - i++; - } + num_channels = devm_iio_adc_device_alloc_chaninfo_se(dev, + &sun20i_gpadc_chan_template, -1, &channels); + if (num_channels < 0) + return num_channels; indio_dev->channels = channels; indio_dev->num_channels = num_channels; @@ -255,7 +243,7 @@ static int sun20i_gpadc_probe(struct platform_device *pdev) static const struct of_device_id sun20i_gpadc_of_id[] = { { .compatible = "allwinner,sun20i-d1-gpadc" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id); @@ -271,3 +259,4 @@ module_platform_driver(sun20i_gpadc_driver); MODULE_DESCRIPTION("ADC driver for sunxi platforms"); MODULE_AUTHOR("Maksim Kiselev <bigunclemax@gmail.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DRIVER"); diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 8b27458dcd66..6b8d6bee1873 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -116,7 +116,7 @@ struct sun4i_gpadc_iio { static const struct iio_map sun4i_gpadc_hwmon_maps[] = { IIO_MAP("temp_adc", "iio_hwmon.0", NULL), - { /* sentinel */ }, + { } }; static const struct iio_chan_spec sun4i_gpadc_channels[] = { @@ -485,7 +485,7 @@ static const struct of_device_id sun4i_gpadc_of_id[] = { .compatible = "allwinner,sun8i-a33-ths", .data = &sun8i_a33_gpadc_data, }, - { /* sentinel */ } + { } }; static int sun4i_gpadc_probe_dt(struct platform_device *pdev, @@ -685,7 +685,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = { { "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data }, { "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data }, { "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data }, - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(platform, sun4i_gpadc_id); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 6c2cb3dabbbf..4f514db5c26e 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -37,7 +37,7 @@ struct adc081c { /* Ensure natural alignment of buffer elements */ struct { u16 channel; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -140,8 +140,8 @@ static irqreturn_t adc081c_trigger_handler(int irq, void *p) if (ret < 0) goto out; data->scan.channel = ret; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index e2dbd070c7c4..cfcdafbe284b 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -225,8 +225,8 @@ static irqreturn_t adc0832_trigger_handler(int irq, void *p) adc->data[i] = ret; i++; } - iio_push_to_buffers_with_timestamp(indio_dev, adc->data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, adc->data, sizeof(adc->data), + iio_get_time_ns(indio_dev)); out: mutex_unlock(&adc->lock); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index bf98f9bf942a..50a474f4d9f5 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -29,7 +29,7 @@ struct adc084s021 { /* Buffer used to align data */ struct { __be16 channels[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; /* * DMA (thus cache coherency maintenance) may require the @@ -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; @@ -152,8 +151,8 @@ static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc) if (adc084s021_adc_conversion(adc, adc->scan.channels) < 0) dev_err(&adc->spi->dev, "Failed to read data\n"); - iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &adc->scan, sizeof(adc->scan), + iio_get_time_ns(indio_dev)); mutex_unlock(&adc->lock); iio_trigger_notify_done(indio_dev->trig); 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-adc12138.c b/drivers/iio/adc/ti-adc12138.c index 7f065f457b36..9dc465a10ffc 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -376,8 +376,8 @@ static irqreturn_t adc12138_trigger_handler(int irq, void *p) } } - iio_push_to_buffers_with_timestamp(indio_dev, adc->data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, adc->data, sizeof(adc->data), + iio_get_time_ns(indio_dev)); out: mutex_unlock(&adc->lock); diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index a456ea78462f..1b46a8155803 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -9,6 +9,7 @@ * https://www.ti.com/lit/ds/symlink/adc124s021.pdf */ +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/iio/iio.h> #include <linux/mod_devicetable.h> @@ -20,40 +21,44 @@ struct adc128_configuration { const struct iio_chan_spec *channels; u8 num_channels; + const char *refname; + int num_other_regulators; + const char * const (*other_regulators)[]; }; struct adc128 { struct spi_device *spi; - struct regulator *reg; + /* + * Serialize the SPI 'write-channel + read data' accesses and protect + * the shared buffer. + */ struct mutex lock; - - u8 buffer[2] __aligned(IIO_DMA_MINALIGN); + int vref_mv; + union { + __be16 buffer16; + u8 buffer[2]; + } __aligned(IIO_DMA_MINALIGN); }; static int adc128_adc_conversion(struct adc128 *adc, u8 channel) { int ret; - mutex_lock(&adc->lock); + guard(mutex)(&adc->lock); adc->buffer[0] = channel << 3; adc->buffer[1] = 0; - ret = spi_write(adc->spi, &adc->buffer, 2); - if (ret < 0) { - mutex_unlock(&adc->lock); + ret = spi_write(adc->spi, &adc->buffer, sizeof(adc->buffer)); + if (ret < 0) return ret; - } - - ret = spi_read(adc->spi, &adc->buffer, 2); - - mutex_unlock(&adc->lock); + ret = spi_read(adc->spi, &adc->buffer16, sizeof(adc->buffer16)); if (ret < 0) return ret; - return ((adc->buffer[0] << 8 | adc->buffer[1]) & 0xFFF); + return be16_to_cpu(adc->buffer16) & 0xFFF; } static int adc128_read_raw(struct iio_dev *indio_dev, @@ -75,11 +80,7 @@ static int adc128_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(adc->reg); - if (ret < 0) - return ret; - - *val = ret / 1000; + *val = adc->vref_mv; *val2 = 12; return IIO_VAL_FRACTIONAL_LOG2; @@ -121,21 +122,34 @@ static const struct iio_chan_spec adc124s021_channels[] = { ADC128_VOLTAGE_CHANNEL(3), }; +static const char * const bd79104_regulators[] = { "iovdd" }; + static const struct adc128_configuration adc128_config[] = { - { adc128s052_channels, ARRAY_SIZE(adc128s052_channels) }, - { adc122s021_channels, ARRAY_SIZE(adc122s021_channels) }, - { adc124s021_channels, ARRAY_SIZE(adc124s021_channels) }, + { + .channels = adc128s052_channels, + .num_channels = ARRAY_SIZE(adc128s052_channels), + .refname = "vref", + }, { + .channels = adc122s021_channels, + .num_channels = ARRAY_SIZE(adc122s021_channels), + .refname = "vref", + }, { + .channels = adc124s021_channels, + .num_channels = ARRAY_SIZE(adc124s021_channels), + .refname = "vref", + }, { + .channels = adc128s052_channels, + .num_channels = ARRAY_SIZE(adc128s052_channels), + .refname = "vdd", + .other_regulators = &bd79104_regulators, + .num_other_regulators = 1, + }, }; static const struct iio_info adc128_info = { .read_raw = adc128_read_raw, }; -static void adc128_disable_regulator(void *reg) -{ - regulator_disable(reg); -} - static int adc128_probe(struct spi_device *spi) { const struct adc128_configuration *config; @@ -159,20 +173,28 @@ static int adc128_probe(struct spi_device *spi) indio_dev->channels = config->channels; indio_dev->num_channels = config->num_channels; - adc->reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(adc->reg)) - return PTR_ERR(adc->reg); - - ret = regulator_enable(adc->reg); + ret = devm_regulator_get_enable_read_voltage(&spi->dev, + config->refname); if (ret < 0) - return ret; - ret = devm_add_action_or_reset(&spi->dev, adc128_disable_regulator, - adc->reg); + return dev_err_probe(&spi->dev, ret, + "failed to read '%s' voltage", + config->refname); + + adc->vref_mv = ret / 1000; + + if (config->num_other_regulators) { + ret = devm_regulator_bulk_get_enable(&spi->dev, + config->num_other_regulators, + *config->other_regulators); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to enable regulators\n"); + } + + ret = devm_mutex_init(&spi->dev, &adc->lock); if (ret) return ret; - mutex_init(&adc->lock); - return devm_iio_device_register(&spi->dev, indio_dev); } @@ -184,7 +206,8 @@ static const struct of_device_id adc128_of_match[] = { { .compatible = "ti,adc124s021", .data = &adc128_config[2] }, { .compatible = "ti,adc124s051", .data = &adc128_config[2] }, { .compatible = "ti,adc124s101", .data = &adc128_config[2] }, - { /* sentinel */ }, + { .compatible = "rohm,bd79104", .data = &adc128_config[3] }, + { } }; MODULE_DEVICE_TABLE(of, adc128_of_match); @@ -196,6 +219,7 @@ static const struct spi_device_id adc128_id[] = { { "adc124s021", (kernel_ulong_t)&adc128_config[2] }, { "adc124s051", (kernel_ulong_t)&adc128_config[2] }, { "adc124s101", (kernel_ulong_t)&adc128_config[2] }, + { "bd79104", (kernel_ulong_t)&adc128_config[3] }, { } }; MODULE_DEVICE_TABLE(spi, adc128_id); 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-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 47fe8e16aee4..21181cc3bd85 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -12,6 +12,7 @@ */ #include <linux/module.h> +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/irq.h> #include <linux/i2c.h> @@ -448,7 +449,7 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) /* Ensure natural alignment of timestamp */ struct { s16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; int chan, ret, res; @@ -466,8 +467,8 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) scan.chan = res; mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, &scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); @@ -533,6 +534,31 @@ static int ads1015_read_avail(struct iio_dev *indio_dev, } } +static int __ads1015_read_info_raw(struct ads1015_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + if (ads1015_event_channel_enabled(data) && + data->event_channel != chan->address) + return -EBUSY; + + ret = ads1015_set_power_state(data, true); + if (ret < 0) + return ret; + + ret = ads1015_get_adc_result(data, chan->address, val); + if (ret < 0) { + ads1015_set_power_state(data, false); + return ret; + } + + *val = sign_extend32(*val >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + + return ads1015_set_power_state(data, false); +} + static int ads1015_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -540,58 +566,29 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, int ret, idx; struct ads1015_data *data = iio_priv(indio_dev); - mutex_lock(&data->lock); + guard(mutex)(&data->lock); 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 = __ads1015_read_info_raw(data, chan, val); + iio_device_release_direct(indio_dev); if (ret) - break; - - if (ads1015_event_channel_enabled(data) && - data->event_channel != chan->address) { - ret = -EBUSY; - goto release_direct; - } - - ret = ads1015_set_power_state(data, true); - if (ret < 0) - goto release_direct; - - ret = ads1015_get_adc_result(data, chan->address, val); - if (ret < 0) { - ads1015_set_power_state(data, false); - goto release_direct; - } - - *val = sign_extend32(*val >> chan->scan_type.shift, - chan->scan_type.realbits - 1); - - ret = ads1015_set_power_state(data, false); - if (ret < 0) - goto release_direct; + return ret; - ret = IIO_VAL_INT; -release_direct: - iio_device_release_direct_mode(indio_dev); - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: idx = data->channel_data[chan->address].pga; *val = ads1015_fullscale_range[idx]; *val2 = chan->scan_type.realbits - 1; - ret = IIO_VAL_FRACTIONAL_LOG2; - break; + return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: idx = data->channel_data[chan->address].data_rate; *val = data->chip->data_rate[idx]; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; default: - ret = -EINVAL; - break; + return -EINVAL; } - mutex_unlock(&data->lock); - - return ret; } static int ads1015_write_raw(struct iio_dev *indio_dev, @@ -599,23 +596,16 @@ static int ads1015_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct ads1015_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (mask) { case IIO_CHAN_INFO_SCALE: - ret = ads1015_set_scale(data, chan, val, val2); - break; + return ads1015_set_scale(data, chan, val, val2); case IIO_CHAN_INFO_SAMP_FREQ: - ret = ads1015_set_data_rate(data, chan->address, val); - break; + return ads1015_set_data_rate(data, chan->address, val); default: - ret = -EINVAL; - break; + return -EINVAL; } - mutex_unlock(&data->lock); - - return ret; } static int ads1015_read_event(struct iio_dev *indio_dev, @@ -624,20 +614,18 @@ static int ads1015_read_event(struct iio_dev *indio_dev, int *val2) { struct ads1015_data *data = iio_priv(indio_dev); - int ret; unsigned int comp_queue; int period; int dr; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (info) { case IIO_EV_INFO_VALUE: *val = (dir == IIO_EV_DIR_RISING) ? data->thresh_data[chan->address].high_thresh : data->thresh_data[chan->address].low_thresh; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_EV_INFO_PERIOD: dr = data->channel_data[chan->address].data_rate; comp_queue = data->thresh_data[chan->address].comp_queue; @@ -646,16 +634,10 @@ static int ads1015_read_event(struct iio_dev *indio_dev, *val = period / USEC_PER_SEC; *val2 = period % USEC_PER_SEC; - ret = IIO_VAL_INT_PLUS_MICRO; - break; + return IIO_VAL_INT_PLUS_MICRO; default: - ret = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return ret; } static int ads1015_write_event(struct iio_dev *indio_dev, @@ -666,24 +648,22 @@ static int ads1015_write_event(struct iio_dev *indio_dev, struct ads1015_data *data = iio_priv(indio_dev); const int *data_rate = data->chip->data_rate; int realbits = chan->scan_type.realbits; - int ret = 0; long long period; int i; int dr; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (info) { case IIO_EV_INFO_VALUE: - if (val >= 1 << (realbits - 1) || val < -1 << (realbits - 1)) { - ret = -EINVAL; - break; - } + if (val >= 1 << (realbits - 1) || val < -1 << (realbits - 1)) + return -EINVAL; + if (dir == IIO_EV_DIR_RISING) data->thresh_data[chan->address].high_thresh = val; else data->thresh_data[chan->address].low_thresh = val; - break; + return 0; case IIO_EV_INFO_PERIOD: dr = data->channel_data[chan->address].data_rate; period = val * USEC_PER_SEC + val2; @@ -694,15 +674,10 @@ static int ads1015_write_event(struct iio_dev *indio_dev, break; } data->thresh_data[chan->address].comp_queue = i; - break; + return 0; default: - ret = -EINVAL; - break; + return -EINVAL; } - - mutex_unlock(&data->lock); - - return ret; } static int ads1015_read_event_config(struct iio_dev *indio_dev, @@ -710,25 +685,19 @@ static int ads1015_read_event_config(struct iio_dev *indio_dev, enum iio_event_direction dir) { struct ads1015_data *data = iio_priv(indio_dev); - int ret = 0; - mutex_lock(&data->lock); - if (data->event_channel == chan->address) { - switch (dir) { - case IIO_EV_DIR_RISING: - ret = 1; - break; - case IIO_EV_DIR_EITHER: - ret = (data->comp_mode == ADS1015_CFG_COMP_MODE_WINDOW); - break; - default: - ret = -EINVAL; - break; - } - } - mutex_unlock(&data->lock); + guard(mutex)(&data->lock); + if (data->event_channel != chan->address) + return 0; - return ret; + switch (dir) { + case IIO_EV_DIR_RISING: + return 1; + case IIO_EV_DIR_EITHER: + return (data->comp_mode == ADS1015_CFG_COMP_MODE_WINDOW); + default: + return -EINVAL; + } } static int ads1015_enable_event_config(struct ads1015_data *data, @@ -813,23 +782,18 @@ static int ads1015_write_event_config(struct iio_dev *indio_dev, int comp_mode = (dir == IIO_EV_DIR_EITHER) ? ADS1015_CFG_COMP_MODE_WINDOW : ADS1015_CFG_COMP_MODE_TRAD; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); /* Prevent from enabling both buffer and event at a time */ - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) { - mutex_unlock(&data->lock); - return ret; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; if (state) ret = ads1015_enable_event_config(data, chan, comp_mode); else ret = ads1015_disable_event_config(data, chan, comp_mode); - iio_device_release_direct_mode(indio_dev); - mutex_unlock(&data->lock); - + iio_device_release_direct(indio_dev); return ret; } diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c index 1e46f07a9ca6..b0790e300b18 100644 --- a/drivers/iio/adc/ti-ads1100.c +++ b/drivers/iio/adc/ti-ads1100.c @@ -10,6 +10,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/init.h> @@ -219,36 +220,30 @@ static int ads1100_read_raw(struct iio_dev *indio_dev, int ret; struct ads1100_data *data = iio_priv(indio_dev); - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - break; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = ads1100_get_adc_result(data, chan->address, val); - if (ret >= 0) - ret = IIO_VAL_INT; - iio_device_release_direct_mode(indio_dev); - break; + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* full-scale is the supply voltage in millivolts */ *val = ads1100_get_vdd_millivolts(data); *val2 = 15 + FIELD_GET(ADS1100_PGA_MASK, data->config); - ret = IIO_VAL_FRACTIONAL_LOG2; - break; + return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_SAMP_FREQ: *val = ads1100_data_rate[FIELD_GET(ADS1100_DR_MASK, data->config)]; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; default: - ret = -EINVAL; - break; + return -EINVAL; } - mutex_unlock(&data->lock); - - return ret; } static int ads1100_write_raw(struct iio_dev *indio_dev, @@ -256,23 +251,16 @@ static int ads1100_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct ads1100_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); switch (mask) { case IIO_CHAN_INFO_SCALE: - ret = ads1100_set_scale(data, val, val2); - break; + return ads1100_set_scale(data, val, val2); case IIO_CHAN_INFO_SAMP_FREQ: - ret = ads1100_set_data_rate(data, chan->address, val); - break; + return ads1100_set_data_rate(data, chan->address, val); default: - ret = -EINVAL; - break; + return -EINVAL; } - mutex_unlock(&data->lock); - - return ret; } static const struct iio_info ads1100_info = { diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index c268e27eec12..d280c949cf47 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; @@ -501,7 +506,7 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private) struct ads1119_state *st = iio_priv(indio_dev); struct { s16 sample; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; unsigned int index; int ret; @@ -529,8 +534,8 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private) scan.sample = ret; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index f452f57f11c9..8ea1269f74db 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); @@ -297,8 +297,8 @@ static irqreturn_t ads124s_trigger_handler(int irq, void *p) j++; } - iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, priv->buffer, sizeof(priv->buffer), + pf->timestamp); iio_trigger_notify_done(indio_dev->trig); 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 31f1f229d97a..085f0d6fb39e 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -102,7 +102,7 @@ struct ads131e08_state { struct completion completion; struct { u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; - s64 ts __aligned(8); + aligned_s64 ts; } tmp_buf; u8 tx_buf[3] __aligned(IIO_DMA_MINALIGN); @@ -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: @@ -666,8 +664,8 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private) i++; } - iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &st->tmp_buf, sizeof(st->tmp_buf), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); 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-ads7950.c b/drivers/iio/adc/ti-ads7950.c index af28672aa803..0356ccf23fea 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -403,10 +403,11 @@ static const struct iio_info ti_ads7950_info = { .update_scan_mode = ti_ads7950_update_scan_mode, }; -static void ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct ti_ads7950_state *st = gpiochip_get_data(chip); + int ret; mutex_lock(&st->slock); @@ -416,9 +417,11 @@ static void ti_ads7950_set(struct gpio_chip *chip, unsigned int offset, st->cmd_settings_bitmask &= ~BIT(offset); st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st); - spi_sync(st->spi, &st->scan_single_msg); + ret = spi_sync(st->spi, &st->scan_single_msg); mutex_unlock(&st->slock); + + return ret; } static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset) @@ -499,7 +502,11 @@ static int ti_ads7950_direction_input(struct gpio_chip *chip, static int ti_ads7950_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { - ti_ads7950_set(chip, offset, value); + int ret; + + ret = ti_ads7950_set(chip, offset, value); + if (ret) + return ret; return _ti_ads7950_set_direction(chip, offset, 0); } @@ -641,7 +648,7 @@ static int ti_ads7950_probe(struct spi_device *spi) st->chip.direction_input = ti_ads7950_direction_input; st->chip.direction_output = ti_ads7950_direction_output; st->chip.get = ti_ads7950_get; - st->chip.set = ti_ads7950_set; + st->chip.set_rv = ti_ads7950_set; ret = gpiochip_add_data(&st->chip, st); if (ret) { diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index a31658b760a4..b0bf46cae0b6 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -389,8 +389,8 @@ static irqreturn_t ads8688_trigger_handler(int irq, void *p) j++; } - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index 169e3591320b..3f375c1f586c 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -199,7 +199,7 @@ static irqreturn_t lmp92064_trigger_handler(int irq, void *p) struct lmp92064_adc_priv *priv = iio_priv(indio_dev); struct { u16 values[2]; - int64_t timestamp __aligned(8); + aligned_s64 timestamp; } data; int ret; @@ -209,8 +209,8 @@ static irqreturn_t lmp92064_trigger_handler(int irq, void *p) if (ret) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data, sizeof(data), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); @@ -366,7 +366,7 @@ MODULE_DEVICE_TABLE(spi, lmp92064_id_table); static const struct of_device_id lmp92064_of_table[] = { { .compatible = "ti,lmp92064" }, - {} + { } }; MODULE_DEVICE_TABLE(of, lmp92064_of_table); diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c index 08de997584fd..f67945c62c99 100644 --- a/drivers/iio/adc/ti-tlc4541.c +++ b/drivers/iio/adc/ti-tlc4541.c @@ -99,8 +99,8 @@ static irqreturn_t tlc4541_trigger_handler(int irq, void *p) if (ret < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, st->rx_buf, sizeof(st->rx_buf), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -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 b56f2503f14c..c2d2aada6772 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -157,7 +157,7 @@ struct tsc2046_adc_priv { /* Scan data for each channel */ u16 data[TI_TSC2046_MAX_CHAN]; /* Timestamp */ - s64 ts __aligned(8); + aligned_s64 ts; } scan_buf; /* @@ -418,8 +418,9 @@ static int tsc2046_adc_scan(struct iio_dev *indio_dev) for (group = 0; group < priv->groups; group++) priv->scan_buf.data[group] = tsc2046_adc_get_val(priv, group); - ret = iio_push_to_buffers_with_timestamp(indio_dev, &priv->scan_buf, - iio_get_time_ns(indio_dev)); + ret = iio_push_to_buffers_with_ts(indio_dev, &priv->scan_buf, + sizeof(priv->scan_buf), + iio_get_time_ns(indio_dev)); /* If the consumer is kfifo, we may get a EBUSY here - ignore it. */ if (ret < 0 && ret != -EBUSY) { dev_err_ratelimited(dev, "Failed to push scan buffer %pe\n", @@ -760,7 +761,6 @@ static int tsc2046_adc_probe(struct spi_device *spi) if (!dcfg) return -EINVAL; - spi->bits_per_word = 8; spi->mode &= ~SPI_MODE_X_MASK; spi->mode |= SPI_MODE_0; ret = spi_setup(spi); @@ -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/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index ef7430e6877d..3ac774ebf678 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -871,7 +871,7 @@ static const struct of_device_id of_twl6030_match_tbl[] = { .compatible = "ti,twl6032-gpadc", .data = &twl6032_pdata, }, - { /* end */ } + { } }; MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 4d83c12975c5..6404b015234a 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -11,6 +11,7 @@ #include <linux/property.h> #include <linux/platform_device.h> #include <linux/interrupt.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -173,10 +174,14 @@ struct vf610_adc { /* Ensure the timestamp is naturally aligned */ struct { u16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; +struct vf610_chip_info { + u8 num_channels; +}; + static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 }; @@ -500,7 +505,7 @@ static const struct iio_enum vf610_conversion_mode = { static const struct iio_chan_spec_ext_info vf610_ext_info[] = { IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode), - {}, + { } }; #define VF610_ADC_CHAN(_idx, _chan_type) { \ @@ -587,9 +592,9 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) info->value = vf610_adc_read_data(info); if (iio_buffer_enabled(indio_dev)) { info->scan.chan = info->value; - iio_push_to_buffers_with_timestamp(indio_dev, - &info->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &info->scan, + sizeof(info->scan), + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); } else complete(&info->completion); @@ -626,36 +631,29 @@ static const struct attribute_group vf610_attribute_group = { .attrs = vf610_attributes, }; -static int vf610_read_sample(struct iio_dev *indio_dev, +static int vf610_read_sample(struct vf610_adc *info, struct iio_chan_spec const *chan, int *val) { - struct vf610_adc *info = iio_priv(indio_dev); unsigned int hc_cfg; int ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - mutex_lock(&info->lock); + guard(mutex)(&info->lock); reinit_completion(&info->completion); hc_cfg = VF610_ADC_ADCHC(chan->channel); hc_cfg |= VF610_ADC_AIEN; writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); ret = wait_for_completion_interruptible_timeout(&info->completion, VF610_ADC_TIMEOUT); - if (ret == 0) { - ret = -ETIMEDOUT; - goto out_unlock; - } + if (ret == 0) + return -ETIMEDOUT; if (ret < 0) - goto out_unlock; + return ret; switch (chan->type) { case IIO_VOLTAGE: *val = info->value; - break; + return 0; case IIO_TEMP: /* * Calculate in degree Celsius times 1000 @@ -665,17 +663,10 @@ static int vf610_read_sample(struct iio_dev *indio_dev, *val = 25000 - ((int)info->value - VF610_VTEMP25_3V3) * 1000000 / VF610_TEMP_SLOPE_COEFF; - break; + return 0; default: - ret = -EINVAL; - break; + return -EINVAL; } - -out_unlock: - mutex_unlock(&info->lock); - iio_device_release_direct_mode(indio_dev); - - return ret; } static int vf610_read_raw(struct iio_dev *indio_dev, @@ -690,7 +681,10 @@ static int vf610_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: - ret = vf610_read_sample(indio_dev, chan, val); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = vf610_read_sample(info, chan, val); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -808,14 +802,31 @@ static const struct iio_info vf610_adc_iio_info = { .attrs = &vf610_attribute_group, }; +static const struct vf610_chip_info vf610_chip_info = { + .num_channels = ARRAY_SIZE(vf610_adc_iio_channels), +}; + +static const struct vf610_chip_info imx6sx_chip_info = { + .num_channels = 4, +}; + static const struct of_device_id vf610_adc_match[] = { - { .compatible = "fsl,vf610-adc", }, - { /* sentinel */ } + { .compatible = "fsl,imx6sx-adc", .data = &imx6sx_chip_info}, + { .compatible = "fsl,vf610-adc", .data = &vf610_chip_info}, + { } }; MODULE_DEVICE_TABLE(of, vf610_adc_match); +static void vf610_adc_action_remove(void *d) +{ + struct vf610_adc *info = d; + + regulator_disable(info->vref); +} + static int vf610_adc_probe(struct platform_device *pdev) { + const struct vf610_chip_info *chip_info; struct device *dev = &pdev->dev; struct vf610_adc *info; struct iio_dev *indio_dev; @@ -823,10 +834,8 @@ static int vf610_adc_probe(struct platform_device *pdev) int ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc)); - if (!indio_dev) { - dev_err(&pdev->dev, "Failed allocating iio device\n"); - return -ENOMEM; - } + if (!indio_dev) + return dev_err_probe(&pdev->dev, -ENOMEM, "Failed allocating iio device\n"); info = iio_priv(indio_dev); info->dev = &pdev->dev; @@ -835,6 +844,8 @@ static int vf610_adc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + chip_info = device_get_match_data(dev); + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -842,17 +853,12 @@ static int vf610_adc_probe(struct platform_device *pdev) ret = devm_request_irq(info->dev, irq, vf610_adc_isr, 0, dev_name(&pdev->dev), indio_dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed requesting irq, irq = %d\n", irq); - info->clk = devm_clk_get(&pdev->dev, "adc"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed getting clock, err = %ld\n", - PTR_ERR(info->clk)); - return PTR_ERR(info->clk); - } + info->clk = devm_clk_get_enabled(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), "failed getting clock\n"); info->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(info->vref)) @@ -862,6 +868,10 @@ static int vf610_adc_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_add_action_or_reset(&pdev->dev, vf610_adc_action_remove, info); + if (ret) + return ret; + info->vref_uv = regulator_get_voltage(info->vref); device_property_read_u32_array(dev, "fsl,adck-max-frequency", info->max_adck_rate, 3); @@ -877,54 +887,23 @@ static int vf610_adc_probe(struct platform_device *pdev) indio_dev->info = &vf610_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = vf610_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels); - - ret = clk_prepare_enable(info->clk); - if (ret) { - dev_err(&pdev->dev, - "Could not prepare or enable the clock.\n"); - goto error_adc_clk_enable; - } + indio_dev->num_channels = chip_info->num_channels; vf610_adc_cfg_init(info); vf610_adc_hw_init(info); - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - NULL, &iio_triggered_buffer_setup_ops); - if (ret < 0) { - dev_err(&pdev->dev, "Couldn't initialise the buffer\n"); - goto error_iio_device_register; - } + ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, &iio_pollfunc_store_time, + NULL, &iio_triggered_buffer_setup_ops); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Couldn't initialise the buffer\n"); mutex_init(&info->lock); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "Couldn't register the device.\n"); - goto error_adc_buffer_init; - } + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Couldn't register the device.\n"); return 0; - -error_adc_buffer_init: - iio_triggered_buffer_cleanup(indio_dev); -error_iio_device_register: - clk_disable_unprepare(info->clk); -error_adc_clk_enable: - regulator_disable(info->vref); - - return ret; -} - -static void vf610_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct vf610_adc *info = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(info->vref); - clk_disable_unprepare(info->clk); } static int vf610_adc_suspend(struct device *dev) @@ -972,7 +951,6 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, static struct platform_driver vf610_adc_driver = { .probe = vf610_adc_probe, - .remove = vf610_adc_remove, .driver = { .name = DRIVER_NAME, .of_match_table = vf610_adc_match, diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index cfbfcaefec0f..e257c1b94a5f 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -1186,7 +1186,7 @@ static const struct of_device_id xadc_of_match_table[] = { .compatible = "xlnx,system-management-wiz-1.3", .data = &xadc_us_axi_ops }, - { }, + { } }; MODULE_DEVICE_TABLE(of, xadc_of_match_table); @@ -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/ad74115.c b/drivers/iio/addac/ad74115.c index a7e480f2472d..4d8b64048e4f 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -542,18 +542,16 @@ static int ad74115_gpio_get(struct gpio_chip *gc, unsigned int offset) return FIELD_GET(AD74115_GPIO_CONFIG_GPI_DATA, val); } -static void ad74115_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int ad74115_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct ad74115_state *st = gpiochip_get_data(gc); - struct device *dev = &st->spi->dev; - int ret; - ret = regmap_update_bits(st->regmap, AD74115_GPIO_CONFIG_X_REG(offset), - AD74115_GPIO_CONFIG_GPO_DATA, - FIELD_PREP(AD74115_GPIO_CONFIG_GPO_DATA, value)); - if (ret) - dev_err(dev, "Failed to set GPIO %u output value, err: %d\n", - offset, ret); + return regmap_update_bits(st->regmap, + AD74115_GPIO_CONFIG_X_REG(offset), + AD74115_GPIO_CONFIG_GPO_DATA, + FIELD_PREP(AD74115_GPIO_CONFIG_GPO_DATA, + value)); } static int ad74115_set_comp_debounce(struct ad74115_state *st, unsigned int val) @@ -866,15 +864,14 @@ static int ad74115_get_adc_code(struct iio_dev *indio_dev, struct ad74115_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); ret = _ad74115_get_adc_code(st, channel, val); mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -1580,7 +1577,7 @@ static int ad74115_setup_gpio_chip(struct ad74115_state *st) .direction_input = ad74115_gpio_direction_input, .direction_output = ad74115_gpio_direction_output, .get = ad74115_gpio_get, - .set = ad74115_gpio_set, + .set_rv = ad74115_gpio_set, }; return devm_gpiochip_add_data(dev, &st->gc, st); diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index daea2bde7acf..a0bb1dbcb7ad 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -4,7 +4,6 @@ * Author: Cosmin Tanislav <cosmin.tanislav@analog.com> */ -#include <linux/unaligned.h> #include <linux/bitfield.h> #include <linux/cleanup.h> #include <linux/crc8.h> @@ -24,6 +23,8 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/unaligned.h> #include <dt-bindings/iio/addac/adi,ad74413r.h> @@ -84,7 +85,7 @@ struct ad74413r_state { */ struct { u8 rx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; - s64 timestamp; + aligned_s64 timestamp; } adc_samples_buf __aligned(IIO_DMA_MINALIGN); u8 adc_samples_tx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; @@ -276,8 +277,8 @@ static int ad74413r_set_comp_drive_strength(struct ad74413r_state *st, } -static void ad74413r_gpio_set(struct gpio_chip *chip, - unsigned int offset, int val) +static int ad74413r_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) { struct ad74413r_state *st = gpiochip_get_data(chip); unsigned int real_offset = st->gpo_gpio_offsets[offset]; @@ -286,16 +287,16 @@ static void ad74413r_gpio_set(struct gpio_chip *chip, ret = ad74413r_set_gpo_config(st, real_offset, AD74413R_GPO_CONFIG_LOGIC); if (ret) - return; + return ret; - regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(real_offset), - AD74413R_GPO_CONFIG_DATA_MASK, - val ? AD74413R_GPO_CONFIG_DATA_MASK : 0); + return regmap_update_bits(st->regmap, + AD74413R_REG_GPO_CONFIG_X(real_offset), + AD74413R_GPO_CONFIG_DATA_MASK, + val ? AD74413R_GPO_CONFIG_DATA_MASK : 0); } -static void ad74413r_gpio_set_multiple(struct gpio_chip *chip, - unsigned long *mask, - unsigned long *bits) +static int ad74413r_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) { struct ad74413r_state *st = gpiochip_get_data(chip); unsigned long real_mask = 0; @@ -309,15 +310,15 @@ static void ad74413r_gpio_set_multiple(struct gpio_chip *chip, ret = ad74413r_set_gpo_config(st, real_offset, AD74413R_GPO_CONFIG_LOGIC_PARALLEL); if (ret) - return; + return ret; real_mask |= BIT(real_offset); if (*bits & offset) real_bits |= BIT(real_offset); } - regmap_update_bits(st->regmap, AD74413R_REG_GPO_PAR_DATA, - real_mask, real_bits); + return regmap_update_bits(st->regmap, AD74413R_REG_GPO_PAR_DATA, + real_mask, real_bits); } static int ad74413r_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -826,6 +827,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 +868,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) @@ -1420,8 +1425,8 @@ static int ad74413r_probe(struct spi_device *spi) st->gpo_gpiochip.ngpio = st->num_gpo_gpios; st->gpo_gpiochip.parent = st->dev; st->gpo_gpiochip.can_sleep = true; - st->gpo_gpiochip.set = ad74413r_gpio_set; - st->gpo_gpiochip.set_multiple = ad74413r_gpio_set_multiple; + st->gpo_gpiochip.set_rv = ad74413r_gpio_set; + st->gpo_gpiochip.set_multiple_rv = ad74413r_gpio_set_multiple; st->gpo_gpiochip.set_config = ad74413r_gpio_set_gpo_config; st->gpo_gpiochip.get_direction = ad74413r_gpio_get_gpo_direction; @@ -1501,14 +1506,14 @@ static const struct of_device_id ad74413r_dt_id[] = { .compatible = "adi,ad74413r", .data = &ad74413r_chip_info_data, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ad74413r_dt_id); static const struct spi_device_id ad74413r_spi_id[] = { { .name = "ad74412r", .driver_data = (kernel_ulong_t)&ad74412r_chip_info_data }, { .name = "ad74413r", .driver_data = (kernel_ulong_t)&ad74413r_chip_info_data }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad74413r_spi_id); diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index b6a46036d5ea..ecaf59278c6f 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -514,7 +514,7 @@ static const struct of_device_id rescale_match[] = { .data = &rescale_cfg[TEMP_SENSE_RTD], }, { .compatible = "temperature-transducer", .data = &rescale_cfg[TEMP_TRANSDUCER], }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, rescale_match); diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 31564afb13a2..e73c9b983395 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -330,7 +330,7 @@ static const struct spi_device_id ad8366_id[] = { {"adl5240", ID_ADL5240}, {"hmc792a", ID_HMC792}, {"hmc1119", ID_HMC1119}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad8366_id); diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 566f0e1c98a5..74f8429d652b 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -378,13 +378,13 @@ static int ada4250_probe(struct spi_device *spi) static const struct spi_device_id ada4250_id[] = { { "ada4250", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ada4250_id); static const struct of_device_id ada4250_of_match[] = { { .compatible = "adi,ada4250" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ada4250_of_match); diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index 2ee4c0d70281..4dbf894c7e3b 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; } @@ -271,7 +270,7 @@ static const struct iio_chan_spec_ext_info ltc6373_ext_info[] = { .write = ltc6373_write_powerdown, .shared = IIO_SEPARATE, }, - {} + { } }; #define HMC425A_CHAN(_channel) \ @@ -399,7 +398,6 @@ static int hmc425a_probe(struct platform_device *pdev) return devm_iio_device_register(&pdev->dev, indio_dev); } -/* Match table for of_platform binding */ static const struct of_device_id hmc425a_of_match[] = { { .compatible = "adi,hmc425a", .data = &hmc425a_chip_info_tbl[ID_HMC425A]}, @@ -409,7 +407,7 @@ static const struct of_device_id hmc425a_of_match[] = { .data = &hmc425a_chip_info_tbl[ID_ADRF5740]}, { .compatible = "adi,ltc6373", .data = &hmc425a_chip_info_tbl[ID_LTC6373]}, - {} + { } }; MODULE_DEVICE_TABLE(of, hmc425a_of_match); 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 d2e1529ad8fd..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: Parent device for the buffer - * @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,27 +260,42 @@ 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"); -struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, - struct iio_dev *indio_dev, - const char *channel, - enum iio_buffer_direction dir) +/** + * 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(dev, channel); + buffer = iio_dmaengine_buffer_alloc(chan); if (IS_ERR(buffer)) return ERR_CAST(buffer); @@ -312,16 +311,50 @@ struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, return buffer; } + +/** + * iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device + * @dev: DMA channel consumer device + * @indio_dev: IIO device to which to attach this buffer. + * @channel: DMA channel name, typically "rx". + * @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. + * + * 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, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir) +{ + struct dma_chan *chan; + struct iio_buffer *buffer; + + chan = dma_request_chan(dev, channel); + if (IS_ERR(chan)) + return ERR_CAST(chan); + + 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); } /** * devm_iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device - * @dev: Parent device for the buffer + * @dev: Device for devm ownership and DMA channel consumer device * @indio_dev: IIO device to which to attach this buffer. * @channel: DMA channel name, typically "rx". * @dir: Direction of buffer (in or out) @@ -342,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/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index e64a41bae32c..427d32e398b3 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -631,7 +631,7 @@ static const struct i2c_device_id ad7150_id[] = { { "ad7150", AD7150 }, { "ad7151", AD7151 }, { "ad7156", AD7150 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad7150_id); @@ -640,7 +640,7 @@ static const struct of_device_id ad7150_of_match[] = { { "adi,ad7150" }, { "adi,ad7151" }, { "adi,ad7156" }, - {} + { } }; static struct i2c_driver ad7150_driver = { .driver = { diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c index ba18dbbe0940..8a306d55c72a 100644 --- a/drivers/iio/cdc/ad7746.c +++ b/drivers/iio/cdc/ad7746.c @@ -792,7 +792,7 @@ static const struct i2c_device_id ad7746_id[] = { { "ad7745", 7745 }, { "ad7746", 7746 }, { "ad7747", 7747 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad7746_id); @@ -800,7 +800,7 @@ static const struct of_device_id ad7746_of_match[] = { { .compatible = "adi,ad7745" }, { .compatible = "adi,ad7746" }, { .compatible = "adi,ad7747" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ad7746_of_match); diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 330fe0af946f..b22afa1f6d59 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -108,6 +108,16 @@ config IAQCORE iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds) sensors +config MHZ19B + tristate "Winsen MHZ19B CO2 sensor" + depends on SERIAL_DEV_BUS + help + Say Y here to build Serdev interface support for the Winsen + MHZ19B CO2 sensor. + + To compile this driver as a module, choose M here: the module will + be called mhz19b. + config PMS7003 tristate "Plantower PMS7003 particulate matter sensor" depends on SERIAL_DEV_BUS @@ -166,6 +176,16 @@ config SCD4X To compile this driver as a module, choose M here: the module will be called scd4x. +config SEN0322 + tristate "SEN0322 oxygen sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to build support for the DFRobot SEN0322 oxygen sensor. + + To compile this driver as a module, choose M here: the module will + be called sen0322. + config SENSIRION_SGP30 tristate "Sensirion SGPxx gas sensors" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index 4866db06bdc9..2287a00a6b75 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -15,11 +15,13 @@ obj-$(CONFIG_ENS160) += ens160_core.o obj-$(CONFIG_ENS160_I2C) += ens160_i2c.o obj-$(CONFIG_ENS160_SPI) += ens160_spi.o obj-$(CONFIG_IAQCORE) += ams-iaq-core.o +obj-$(CONFIG_MHZ19B) += mhz19b.o obj-$(CONFIG_PMS7003) += pms7003.o obj-$(CONFIG_SCD30_CORE) += scd30_core.o obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o obj-$(CONFIG_SCD4X) += scd4x.o +obj-$(CONFIG_SEN0322) += sen0322.o obj-$(CONFIG_SENSEAIR_SUNRISE_CO2) += sunrise_co2.o obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o obj-$(CONFIG_SENSIRION_SGP40) += sgp40.o diff --git a/drivers/iio/chemical/ags02ma.c b/drivers/iio/chemical/ags02ma.c index 8fcd80946543..151178d4e8f4 100644 --- a/drivers/iio/chemical/ags02ma.c +++ b/drivers/iio/chemical/ags02ma.c @@ -140,13 +140,13 @@ static int ags02ma_probe(struct i2c_client *client) static const struct i2c_device_id ags02ma_id_table[] = { { "ags02ma" }, - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, ags02ma_id_table); static const struct of_device_id ags02ma_of_table[] = { { .compatible = "aosong,ags02ma" }, - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, ags02ma_of_table); diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c index 761a853a4d17..de0b87edd188 100644 --- a/drivers/iio/chemical/atlas-ezo-sensor.c +++ b/drivers/iio/chemical/atlas-ezo-sensor.c @@ -189,7 +189,7 @@ static const struct i2c_device_id atlas_ezo_id[] = { { "atlas-co2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_CO2_EZO] }, { "atlas-o2-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_O2_EZO] }, { "atlas-hum-ezo", (kernel_ulong_t)&atlas_ezo_devices[ATLAS_HUM_EZO] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, atlas_ezo_id); @@ -197,7 +197,7 @@ static const struct of_device_id atlas_ezo_dt_ids[] = { { .compatible = "atlas,co2-ezo", .data = &atlas_ezo_devices[ATLAS_CO2_EZO], }, { .compatible = "atlas,o2-ezo", .data = &atlas_ezo_devices[ATLAS_O2_EZO], }, { .compatible = "atlas,hum-ezo", .data = &atlas_ezo_devices[ATLAS_HUM_EZO], }, - {} + { } }; MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids); diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index baf93e5e3ca7..cb6662b92137 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -458,8 +458,9 @@ static irqreturn_t atlas_trigger_handler(int irq, void *private) &data->buffer, sizeof(__be32) * channels); if (!ret) - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, data->buffer, + sizeof(data->buffer), + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); @@ -518,13 +519,12 @@ static int atlas_read_raw(struct iio_dev *indio_dev, case IIO_CONCENTRATION: case IIO_ELECTRICALCONDUCTIVITY: case IIO_VOLTAGE: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = atlas_read_measurement(data, chan->address, ®); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); break; default: ret = -EINVAL; @@ -594,7 +594,7 @@ static const struct i2c_device_id atlas_id[] = { { "atlas-orp-sm", (kernel_ulong_t)&atlas_devices[ATLAS_ORP_SM] }, { "atlas-do-sm", (kernel_ulong_t)&atlas_devices[ATLAS_DO_SM] }, { "atlas-rtd-sm", (kernel_ulong_t)&atlas_devices[ATLAS_RTD_SM] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, atlas_id); diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index 00ab89b3138b..7d86ed8b02e6 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -2,6 +2,7 @@ #ifndef BME680_H_ #define BME680_H_ +#include <linux/pm.h> #include <linux/regmap.h> #define BME680_REG_CHIP_ID 0xD0 @@ -80,6 +81,7 @@ #define BME680_CALIB_RANGE_3_LEN 5 extern const struct regmap_config bme680_regmap_config; +extern const struct dev_pm_ops bme680_dev_pm_ops; int bme680_core_probe(struct device *dev, struct regmap *regmap, const char *name); diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index a2949daf9467..3e850562ab00 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -14,7 +14,10 @@ #include <linux/device.h> #include <linux/log2.h> #include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> @@ -111,6 +114,8 @@ enum bme680_scan { BME680_GAS, }; +static const char *const bme680_supply_names[] = { "vdd", "vddio" }; + struct bme680_data { struct regmap *regmap; struct bme680_calib bme680; @@ -817,9 +822,9 @@ static int bme680_read_gas(struct bme680_data *data, int *comp_gas_res) return 0; } -static int bme680_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static int __bme680_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) { struct bme680_data *data = iio_priv(indio_dev); int chan_val, ret; @@ -932,14 +937,33 @@ static int bme680_read_raw(struct iio_dev *indio_dev, } } +static int bme680_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = __bme680_read_raw(indio_dev, chan, val, val2, mask); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static bool bme680_is_valid_oversampling(int rate) { return (rate > 0 && rate <= 16 && is_power_of_2(rate)); } -static int bme680_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __bme680_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct bme680_data *data = iio_priv(indio_dev); @@ -984,6 +1008,25 @@ static int bme680_write_raw(struct iio_dev *indio_dev, } } +static int bme680_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = __bme680_write_raw(indio_dev, chan, val, val2, mask); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16"; static IIO_CONST_ATTR(oversampling_ratio_available, @@ -1077,13 +1120,36 @@ static irqreturn_t bme680_trigger_handler(int irq, void *p) gas_range = FIELD_GET(BME680_GAS_RANGE_MASK, gas_regs_val); data->scan.chan[3] = bme680_compensate_gas(data, adc_gas_res, gas_range); - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } +static int bme680_buffer_preenable(struct iio_dev *indio_dev) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + + return pm_runtime_resume_and_get(dev); +} + +static int bme680_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct bme680_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; +} + +static const struct iio_buffer_setup_ops bme680_buffer_setup_ops = { + .preenable = bme680_buffer_preenable, + .postdisable = bme680_buffer_postdisable, +}; + int bme680_core_probe(struct device *dev, struct regmap *regmap, const char *name) { @@ -1114,6 +1180,14 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, data->heater_dur = 150; /* milliseconds */ data->preheat_curr_mA = 0; + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(bme680_supply_names), + bme680_supply_names); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies.\n"); + + fsleep(BME680_STARTUP_TIME_US); + ret = regmap_write(regmap, BME680_REG_SOFT_RESET, BME680_CMD_SOFTRESET); if (ret < 0) return dev_err_probe(dev, ret, "Failed to reset chip\n"); @@ -1149,15 +1223,47 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, ret = devm_iio_triggered_buffer_setup(dev, indio_dev, iio_pollfunc_store_time, bme680_trigger_handler, - NULL); + &bme680_buffer_setup_ops); if (ret) return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n"); + /* Enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, BME680_STARTUP_TIME_US); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(bme680_core_probe, "IIO_BME680"); +static int bme680_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bme680_data *data = iio_priv(indio_dev); + + return bme680_set_mode(data, BME680_MODE_SLEEP); +} + +static int bme680_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bme680_data *data = iio_priv(indio_dev); + int ret; + + ret = bme680_chip_config(data); + if (ret) + return ret; + + return bme680_gas_config(data); +} + +EXPORT_RUNTIME_DEV_PM_OPS(bme680_dev_pm_ops, bme680_runtime_suspend, + bme680_runtime_resume, NULL); + MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>"); MODULE_DESCRIPTION("Bosch BME680 Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 7a949228b4a6..5560ea708b36 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -37,13 +37,13 @@ static int bme680_i2c_probe(struct i2c_client *client) static const struct i2c_device_id bme680_i2c_id[] = { { "bme680" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bme680_i2c_id); static const struct of_device_id bme680_of_i2c_match[] = { { .compatible = "bosch,bme680", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, bme680_of_i2c_match); @@ -51,6 +51,7 @@ static struct i2c_driver bme680_i2c_driver = { .driver = { .name = "bme680_i2c", .of_match_table = bme680_of_i2c_match, + .pm = pm_ptr(&bme680_dev_pm_ops), }, .probe = bme680_i2c_probe, .id_table = bme680_i2c_id, diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 3916a51ba68e..aa97645ba539 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -112,14 +112,6 @@ static int bme680_spi_probe(struct spi_device *spi) const struct spi_device_id *id = spi_get_device_id(spi); struct bme680_spi_bus_context *bus_context; struct regmap *regmap; - int ret; - - spi->bits_per_word = 8; - ret = spi_setup(spi); - if (ret < 0) { - dev_err(&spi->dev, "spi_setup failed!\n"); - return ret; - } bus_context = devm_kzalloc(&spi->dev, sizeof(*bus_context), GFP_KERNEL); if (!bus_context) @@ -140,13 +132,13 @@ static int bme680_spi_probe(struct spi_device *spi) static const struct spi_device_id bme680_spi_id[] = { {"bme680", 0}, - {}, + { } }; MODULE_DEVICE_TABLE(spi, bme680_spi_id); static const struct of_device_id bme680_of_spi_match[] = { { .compatible = "bosch,bme680", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, bme680_of_spi_match); @@ -154,6 +146,7 @@ static struct spi_driver bme680_spi_driver = { .driver = { .name = "bme680_spi", .of_match_table = bme680_of_spi_match, + .pm = pm_ptr(&bme680_dev_pm_ops), }, .probe = bme680_spi_probe, .id_table = bme680_spi_id, diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 17d1bc518bf2..998c9239c4c7 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -15,6 +15,7 @@ * 4. Read error register and put the information in logs */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -81,7 +82,7 @@ struct ccs811_data { /* Ensures correct alignment of timestamp if present */ struct { s16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -214,6 +215,40 @@ static int ccs811_get_measurement(struct ccs811_data *data) return ret; } +static int ccs811_read_info_raw(struct ccs811_data *data, + struct iio_chan_spec const *chan, + int *val, int mask) +{ + int ret; + + guard(mutex)(&data->lock); + ret = ccs811_get_measurement(data); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_VOLTAGE: + *val = be16_to_cpu(data->buffer.raw_data) & CCS811_VOLTAGE_MASK; + return IIO_VAL_INT; + case IIO_CURRENT: + *val = be16_to_cpu(data->buffer.raw_data) >> 10; + return IIO_VAL_INT; + case IIO_CONCENTRATION: + switch (chan->channel2) { + case IIO_MOD_CO2: + *val = be16_to_cpu(data->buffer.co2); + return IIO_VAL_INT; + case IIO_MOD_VOC: + *val = be16_to_cpu(data->buffer.voc); + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + static int ccs811_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -223,46 +258,12 @@ static int ccs811_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; - mutex_lock(&data->lock); - ret = ccs811_get_measurement(data); - if (ret < 0) { - mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); - return ret; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - switch (chan->type) { - case IIO_VOLTAGE: - *val = be16_to_cpu(data->buffer.raw_data) & - CCS811_VOLTAGE_MASK; - ret = IIO_VAL_INT; - break; - case IIO_CURRENT: - *val = be16_to_cpu(data->buffer.raw_data) >> 10; - ret = IIO_VAL_INT; - break; - case IIO_CONCENTRATION: - switch (chan->channel2) { - case IIO_MOD_CO2: - *val = be16_to_cpu(data->buffer.co2); - ret = IIO_VAL_INT; - break; - case IIO_MOD_VOC: - *val = be16_to_cpu(data->buffer.voc); - ret = IIO_VAL_INT; - break; - default: - ret = -EINVAL; - } - break; - default: - ret = -EINVAL; - } - mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); + ret = ccs811_read_info_raw(data, chan, val, mask); + + iio_device_release_direct(indio_dev); return ret; @@ -342,8 +343,8 @@ static irqreturn_t ccs811_trigger_handler(int irq, void *p) goto err; } - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c index 4a89cd5894d9..6cec60074827 100644 --- a/drivers/iio/chemical/ens160_core.c +++ b/drivers/iio/chemical/ens160_core.c @@ -60,7 +60,7 @@ struct ens160_data { struct mutex mutex; struct { __le16 chans[2]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan __aligned(IIO_DMA_MINALIGN); u8 fw_version[3]; __le16 buf; @@ -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: @@ -257,8 +267,8 @@ static irqreturn_t ens160_trigger_handler(int irq, void *p) if (ret) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/chemical/mhz19b.c b/drivers/iio/chemical/mhz19b.c new file mode 100644 index 000000000000..3c64154918b1 --- /dev/null +++ b/drivers/iio/chemical/mhz19b.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mh-z19b COâ‚‚ sensor driver + * + * Copyright (c) 2025 Gyeyoung Baek <gye976@gmail.com> + * + * Datasheet: + * https://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf + */ + +#include <linux/array_size.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/jiffies.h> +#include <linux/kstrtox.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/serdev.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/unaligned.h> + +/* + * Commands have following format: + * + * +------+------+-----+------+------+------+------+------+-------+ + * | 0xFF | 0x01 | cmd | arg0 | arg1 | 0x00 | 0x00 | 0x00 | cksum | + * +------+------+-----+------+------+------+------+------+-------+ + */ +#define MHZ19B_CMD_SIZE 9 + +/* ABC logic in MHZ19B means auto calibration. */ +#define MHZ19B_ABC_LOGIC_CMD 0x79 +#define MHZ19B_READ_CO2_CMD 0x86 +#define MHZ19B_SPAN_POINT_CMD 0x88 +#define MHZ19B_ZERO_POINT_CMD 0x87 + +#define MHZ19B_SPAN_POINT_PPM_MIN 1000 +#define MHZ19B_SPAN_POINT_PPM_MAX 5000 + +#define MHZ19B_SERDEV_TIMEOUT msecs_to_jiffies(100) + +struct mhz19b_state { + struct serdev_device *serdev; + + /* Must wait until the 'buf' is filled with 9 bytes.*/ + struct completion buf_ready; + + u8 buf_idx; + /* + * Serdev receive buffer. + * When data is received from the MH-Z19B, + * the 'mhz19b_receive_buf' callback function is called and fills this buffer. + */ + u8 buf[MHZ19B_CMD_SIZE] __aligned(IIO_DMA_MINALIGN); +}; + +static u8 mhz19b_get_checksum(u8 *cmd_buf) +{ + u8 i, checksum = 0; + +/* + * +------+------+-----+------+------+------+------+------+-------+ + * | 0xFF | 0x01 | cmd | arg0 | arg1 | 0x00 | 0x00 | 0x00 | cksum | + * +------+------+-----+------+------+------+------+------+-------+ + * i:1 2 3 4 5 6 7 + * + * Sum all cmd_buf elements from index 1 to 7. + */ + for (i = 1; i < 8; i++) + checksum += cmd_buf[i]; + + return -checksum; +} + +static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) +{ + struct mhz19b_state *st = iio_priv(indio_dev); + struct serdev_device *serdev = st->serdev; + struct device *dev = &indio_dev->dev; + int ret; + + /* + * cmd_buf[3,4] : arg0,1 + * cmd_buf[8] : checksum + */ + u8 cmd_buf[MHZ19B_CMD_SIZE] = { + 0xFF, 0x01, cmd, + }; + + switch (cmd) { + case MHZ19B_ABC_LOGIC_CMD: + cmd_buf[3] = arg ? 0xA0 : 0; + break; + case MHZ19B_SPAN_POINT_CMD: + put_unaligned_be16(arg, &cmd_buf[3]); + break; + default: + break; + } + cmd_buf[8] = mhz19b_get_checksum(cmd_buf); + + /* Write buf to uart ctrl synchronously */ + ret = serdev_device_write(serdev, cmd_buf, MHZ19B_CMD_SIZE, 0); + if (ret < 0) + return ret; + if (ret != MHZ19B_CMD_SIZE) + return -EIO; + + switch (cmd) { + case MHZ19B_READ_CO2_CMD: + ret = wait_for_completion_interruptible_timeout(&st->buf_ready, + MHZ19B_SERDEV_TIMEOUT); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + + if (st->buf[8] != mhz19b_get_checksum(st->buf)) { + dev_err(dev, "checksum err"); + return -EINVAL; + } + + return get_unaligned_be16(&st->buf[2]); + default: + /* No response commands. */ + return 0; + } +} + +static int mhz19b_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + + ret = mhz19b_serdev_cmd(indio_dev, MHZ19B_READ_CO2_CMD, 0); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; +} + +/* + * echo 0 > calibration_auto_enable : ABC logic off + * echo 1 > calibration_auto_enable : ABC logic on + */ +static ssize_t calibration_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + ret = mhz19b_serdev_cmd(indio_dev, MHZ19B_ABC_LOGIC_CMD, enable); + if (ret < 0) + return ret; + + return len; +} +static IIO_DEVICE_ATTR_WO(calibration_auto_enable, 0); + +/* + * echo 0 > calibration_forced_value : zero point calibration + * (make sure the sensor has been working under 400ppm for over 20 minutes.) + * echo [1000 1 5000] > calibration_forced_value : span point calibration + * (make sure the sensor has been working under a certain level COâ‚‚ for over 20 minutes.) + */ +static ssize_t calibration_forced_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + u16 ppm; + int cmd, ret; + + ret = kstrtou16(buf, 0, &ppm); + if (ret) + return ret; + + if (ppm) { + if (!in_range(ppm, MHZ19B_SPAN_POINT_PPM_MIN, + MHZ19B_SPAN_POINT_PPM_MAX - MHZ19B_SPAN_POINT_PPM_MIN + 1)) { + dev_dbg(&indio_dev->dev, + "span point ppm should be in a range [%d-%d]\n", + MHZ19B_SPAN_POINT_PPM_MIN, MHZ19B_SPAN_POINT_PPM_MAX); + return -EINVAL; + } + + cmd = MHZ19B_SPAN_POINT_CMD; + } else { + cmd = MHZ19B_ZERO_POINT_CMD; + } + + ret = mhz19b_serdev_cmd(indio_dev, cmd, ppm); + if (ret < 0) + return ret; + + return len; +} +static IIO_DEVICE_ATTR_WO(calibration_forced_value, 0); + +static struct attribute *mhz19b_attrs[] = { + &iio_dev_attr_calibration_auto_enable.dev_attr.attr, + &iio_dev_attr_calibration_forced_value.dev_attr.attr, + NULL +}; + +static const struct attribute_group mhz19b_attr_group = { + .attrs = mhz19b_attrs, +}; + +static const struct iio_info mhz19b_info = { + .attrs = &mhz19b_attr_group, + .read_raw = mhz19b_read_raw, +}; + +static const struct iio_chan_spec mhz19b_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, +}; + +static size_t mhz19b_receive_buf(struct serdev_device *serdev, + const u8 *data, size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); + struct mhz19b_state *st = iio_priv(indio_dev); + + memcpy(st->buf + st->buf_idx, data, len); + st->buf_idx += len; + + if (st->buf_idx == MHZ19B_CMD_SIZE) { + st->buf_idx = 0; + complete(&st->buf_ready); + } + + return len; +} + +static const struct serdev_device_ops mhz19b_ops = { + .receive_buf = mhz19b_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int mhz19b_probe(struct serdev_device *serdev) +{ + int ret; + struct device *dev = &serdev->dev; + struct iio_dev *indio_dev; + struct mhz19b_state *st; + + serdev_device_set_client_ops(serdev, &mhz19b_ops); + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + serdev_device_set_baudrate(serdev, 9600); + serdev_device_set_flow_control(serdev, false); + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) + return ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + serdev_device_set_drvdata(serdev, indio_dev); + + st = iio_priv(indio_dev); + st->serdev = serdev; + + init_completion(&st->buf_ready); + + ret = devm_regulator_get_enable(dev, "vin"); + if (ret) + return ret; + + indio_dev->name = "mh-z19b"; + indio_dev->channels = mhz19b_channels; + indio_dev->num_channels = ARRAY_SIZE(mhz19b_channels); + indio_dev->info = &mhz19b_info; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id mhz19b_of_match[] = { + { .compatible = "winsen,mhz19b", }, + { } +}; +MODULE_DEVICE_TABLE(of, mhz19b_of_match); + +static struct serdev_device_driver mhz19b_driver = { + .driver = { + .name = "mhz19b", + .of_match_table = mhz19b_of_match, + }, + .probe = mhz19b_probe, +}; +module_serdev_device_driver(mhz19b_driver); + +MODULE_AUTHOR("Gyeyoung Baek"); +MODULE_DESCRIPTION("MH-Z19B CO2 sensor driver using serdev interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c index d0bd94912e0a..656d4a12c58f 100644 --- a/drivers/iio/chemical/pms7003.c +++ b/drivers/iio/chemical/pms7003.c @@ -5,7 +5,6 @@ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> */ -#include <linux/unaligned.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/errno.h> @@ -19,6 +18,8 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/serdev.h> +#include <linux/types.h> +#include <linux/unaligned.h> #define PMS7003_DRIVER_NAME "pms7003" @@ -76,7 +77,7 @@ struct pms7003_state { /* Used to construct scan to push to the IIO buffer */ struct { u16 data[3]; /* PM1, PM2P5, PM10 */ - s64 ts; + aligned_s64 ts; } scan; }; @@ -126,8 +127,8 @@ static irqreturn_t pms7003_trigger_handler(int irq, void *p) pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET); mutex_unlock(&state->lock); - iio_push_to_buffers_with_timestamp(indio_dev, &state->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &state->scan, sizeof(state->scan), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index ac3080929f0b..8316720b1fa3 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, @@ -594,7 +586,7 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p) struct scd30_state *state = iio_priv(indio_dev); struct { int data[SCD30_MEAS_COUNT]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int ret; @@ -609,7 +601,8 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p) if (ret) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index 52cad54e8572..2463149519b6 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -358,15 +358,14 @@ static int scd4x_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } - 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); ret = scd4x_read_channel(state, chan->address); mutex_unlock(&state->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -665,7 +664,7 @@ static irqreturn_t scd4x_trigger_handler(int irq, void *p) struct scd4x_state *state = iio_priv(indio_dev); struct { uint16_t data[3]; - int64_t ts __aligned(8); + aligned_s64 ts; } scan; int ret; @@ -676,7 +675,8 @@ static irqreturn_t scd4x_trigger_handler(int irq, void *p) if (ret) goto out; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/chemical/sen0322.c b/drivers/iio/chemical/sen0322.c new file mode 100644 index 000000000000..96c6fc1203ad --- /dev/null +++ b/drivers/iio/chemical/sen0322.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the DFRobot SEN0322 oxygen sensor. + * + * Datasheet: + * https://wiki.dfrobot.com/Gravity_I2C_Oxygen_Sensor_SKU_SEN0322 + * + * Possible I2C slave addresses: + * 0x70 + * 0x71 + * 0x72 + * 0x73 + * + * Copyright (C) 2025 Tóth János <gomba007@gmail.com> + */ + +#include <linux/i2c.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> + +#define SEN0322_REG_DATA 0x03 +#define SEN0322_REG_COEFF 0x0A + +struct sen0322 { + struct regmap *regmap; +}; + +static int sen0322_read_data(struct sen0322 *sen0322) +{ + u8 data[3] = { }; + int ret; + + ret = regmap_bulk_read(sen0322->regmap, SEN0322_REG_DATA, data, + sizeof(data)); + if (ret < 0) + return ret; + + /* + * The actual value in the registers is: + * val = data[0] + data[1] / 10 + data[2] / 100 + * but it is multiplied by 100 here to avoid floating-point math + * and the scale is divided by 100 to compensate this. + */ + return data[0] * 100 + data[1] * 10 + data[2]; +} + +static int sen0322_read_scale(struct sen0322 *sen0322, int *num, int *den) +{ + u32 val; + int ret; + + ret = regmap_read(sen0322->regmap, SEN0322_REG_COEFF, &val); + if (ret < 0) + return ret; + + if (val) { + *num = val; + *den = 100000; /* Coeff is scaled by 1000 at calibration. */ + } else { /* The device is not calibrated, using the factory-defaults. */ + *num = 209; /* Oxygen content in the atmosphere is 20.9%. */ + *den = 120000; /* Output of the sensor at 20.9% is 120 uA. */ + } + + dev_dbg(regmap_get_device(sen0322->regmap), "scale: %d/%d\n", + *num, *den); + + return 0; +} + +static int sen0322_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct sen0322 *sen0322 = iio_priv(iio_dev); + int ret; + + if (chan->type != IIO_CONCENTRATION) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = sen0322_read_data(sen0322); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = sen0322_read_scale(sen0322, val, val2); + if (ret < 0) + return ret; + + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } +} + +static const struct iio_info sen0322_info = { + .read_raw = sen0322_read_raw, +}; + +static const struct regmap_config sen0322_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct iio_chan_spec sen0322_channel = { + .type = IIO_CONCENTRATION, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), +}; + +static int sen0322_probe(struct i2c_client *client) +{ + struct sen0322 *sen0322; + struct iio_dev *iio_dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sen0322)); + if (!iio_dev) + return -ENOMEM; + + sen0322 = iio_priv(iio_dev); + + sen0322->regmap = devm_regmap_init_i2c(client, &sen0322_regmap_conf); + if (IS_ERR(sen0322->regmap)) + return PTR_ERR(sen0322->regmap); + + iio_dev->info = &sen0322_info; + iio_dev->name = "sen0322"; + iio_dev->channels = &sen0322_channel; + iio_dev->num_channels = 1; + iio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&client->dev, iio_dev); +} + +static const struct of_device_id sen0322_of_match[] = { + { .compatible = "dfrobot,sen0322" }, + { } +}; +MODULE_DEVICE_TABLE(of, sen0322_of_match); + +static struct i2c_driver sen0322_driver = { + .driver = { + .name = "sen0322", + .of_match_table = sen0322_of_match, + }, + .probe = sen0322_probe, +}; +module_i2c_driver(sen0322_driver); + +MODULE_AUTHOR("Tóth János <gomba007@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SEN0322 oxygen sensor driver"); diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index 6f4f2ba2c09d..a934bf0298dd 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -108,7 +108,7 @@ static irqreturn_t sps30_trigger_handler(int irq, void *p) int ret; struct { s32 data[4]; /* PM1, PM2P5, PM4, PM10 */ - s64 ts; + aligned_s64 ts; } scan; mutex_lock(&state->lock); @@ -117,8 +117,8 @@ static irqreturn_t sps30_trigger_handler(int irq, void *p) if (ret) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/chemical/sunrise_co2.c b/drivers/iio/chemical/sunrise_co2.c index cdb8696a4e81..af79efde37e8 100644 --- a/drivers/iio/chemical/sunrise_co2.c +++ b/drivers/iio/chemical/sunrise_co2.c @@ -373,7 +373,7 @@ static const struct iio_chan_spec_ext_info sunrise_concentration_ext_info[] = { .read = iio_enum_available_read, .private = (uintptr_t)&sunrise_error_statuses_enum, }, - {} + { } }; static const struct iio_chan_spec sunrise_channels[] = { @@ -519,7 +519,7 @@ static int sunrise_probe(struct i2c_client *client) static const struct of_device_id sunrise_of_match[] = { { .compatible = "senseair,sunrise-006-0-0007" }, - {} + { } }; MODULE_DEVICE_TABLE(of, sunrise_of_match); 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_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c index 119acb078af3..2d3d148b4206 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c @@ -121,7 +121,7 @@ static const struct platform_device_id cros_ec_lid_angle_ids[] = { { .name = DRV_NAME, }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids); diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index 66153b1850f1..82cef4a12442 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -311,7 +311,7 @@ static const struct platform_device_id cros_ec_sensors_ids[] = { { .name = "cros-ec-mag", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids); 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..700ebcd68ff4 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. @@ -32,25 +34,19 @@ static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev, u16 cmd_offset, u16 cmd, u32 *mask) { + DEFINE_RAW_FLEX(struct cros_ec_command, buf, data, + MAX(sizeof(struct ec_response_get_cmd_versions), + sizeof(struct ec_params_get_cmd_versions))); int ret; - struct { - struct cros_ec_command msg; - union { - struct ec_params_get_cmd_versions params; - struct ec_response_get_cmd_versions resp; - }; - } __packed buf = { - .msg = { - .command = EC_CMD_GET_CMD_VERSIONS + cmd_offset, - .insize = sizeof(struct ec_response_get_cmd_versions), - .outsize = sizeof(struct ec_params_get_cmd_versions) - }, - .params = {.cmd = cmd} - }; - - ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg); + + buf->command = EC_CMD_GET_CMD_VERSIONS + cmd_offset; + buf->insize = sizeof(struct ec_response_get_cmd_versions); + buf->outsize = sizeof(struct ec_params_get_cmd_versions); + ((struct ec_params_get_cmd_versions *)buf->data)->cmd = cmd; + + ret = cros_ec_cmd_xfer_status(ec_dev, buf); if (ret >= 0) - *mask = buf.resp.version_mask; + *mask = ((struct ec_response_get_cmd_versions *)buf->data)->version_mask; return ret; } @@ -95,22 +91,6 @@ static void get_default_min_max_freq(enum motionsensor_type type, } } -static int cros_ec_sensor_set_ec_rate(struct cros_ec_sensors_core_state *st, - int rate) -{ - int ret; - - if (rate > U16_MAX) - rate = U16_MAX; - - mutex_lock(&st->cmd_lock); - st->param.cmd = MOTIONSENSE_CMD_EC_RATE; - st->param.ec_rate.data = rate; - ret = cros_ec_motion_send_host_cmd(st, 0); - mutex_unlock(&st->cmd_lock); - return ret; -} - static ssize_t cros_ec_sensor_set_report_latency(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -126,7 +106,25 @@ static ssize_t cros_ec_sensor_set_report_latency(struct device *dev, /* EC rate is in ms. */ latency = integer * 1000 + fract / 1000; - ret = cros_ec_sensor_set_ec_rate(st, latency); + + mutex_lock(&st->cmd_lock); + st->param.cmd = MOTIONSENSE_CMD_EC_RATE; + st->param.ec_rate.data = min(U16_MAX, latency); + ret = cros_ec_motion_send_host_cmd(st, 0); + if (ret < 0) { + mutex_unlock(&st->cmd_lock); + return ret; + } + + /* + * Flush samples currently in the FIFO, especially when the new latency + * is shorter than the old one: new timeout value is only considered when + * there is a new sample available. It can take a while for a slow + * sensor. + */ + st->param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH; + ret = cros_ec_motion_send_host_cmd(st, 0); + mutex_unlock(&st->cmd_lock); if (ret < 0) return ret; @@ -413,6 +411,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 +422,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; } @@ -483,7 +482,7 @@ const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = { .shared = IIO_SHARED_BY_ALL, .read = cros_ec_sensors_id }, - { }, + { } }; EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info); @@ -835,6 +834,18 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, st->param.sensor_odr.roundup = 1; ret = cros_ec_motion_send_host_cmd(st, 0); + if (ret) + break; + + /* Flush the FIFO when a sensor is stopped. + * If the FIFO has just been emptied, pending samples will be + * stuck until new samples are available. It will not happen + * when all the sensors are stopped. + */ + if (frequency == 0) { + st->param.cmd = MOTIONSENSE_CMD_FIFO_FLUSH; + ret = cros_ec_motion_send_host_cmd(st, 0); + } break; default: ret = -EINVAL; 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/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index ad1882f608c0..2055a03cbeb1 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -66,6 +66,10 @@ static struct { {HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0}, {HID_USAGE_SENSOR_HINGE, 0, 0, 17453293}, {HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293}, + + {HID_USAGE_SENSOR_HUMAN_PRESENCE, 0, 1, 0}, + {HID_USAGE_SENSOR_HUMAN_PROXIMITY, 0, 1, 0}, + {HID_USAGE_SENSOR_HUMAN_ATTENTION, 0, 1, 0}, }; static void simple_div(int dividend, int divisor, int *whole, diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index c081b5caa475..97526ba87b93 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -109,8 +109,8 @@ static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) { - const int64_t period_min = ts->min_period * ts->mult; - const int64_t period_max = ts->max_period * ts->mult; + const int64_t period_min = (int64_t)ts->min_period * ts->mult; + const int64_t period_max = (int64_t)ts->max_period * ts->mult; int64_t add_max, sub_max; int64_t delta, jitter; int64_t adjust; diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index ed15dcbf4cf6..da516c46e057 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -351,12 +351,11 @@ static int scmi_iio_read_raw(struct iio_dev *iio_dev, ret = scmi_iio_get_odr_val(iio_dev, val, val2); return ret ? ret : IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; ret = scmi_iio_read_channel_data(iio_dev, ch, val, val2); - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); return ret; default: return -EINVAL; @@ -418,7 +417,7 @@ static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = { .read = scmi_iio_get_raw_available, .shared = IIO_SHARED_BY_TYPE, }, - {}, + { } }; static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan, @@ -705,7 +704,7 @@ static int scmi_iio_dev_probe(struct scmi_device *sdev) static const struct scmi_device_id scmi_id_table[] = { { SCMI_PROTOCOL_SENSOR, "iiodev" }, - {}, + { } }; MODULE_DEVICE_TABLE(scmi, scmi_id_table); diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index 65f8a2b13cfd..1e167dc673ca 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -167,7 +167,7 @@ static void ssp_wdt_work_func(struct work_struct *work) static void ssp_wdt_timer_func(struct timer_list *t) { - struct ssp_data *data = from_timer(data, t, wdt_timer); + struct ssp_data *data = timer_container_of(data, t, wdt_timer); switch (data->fw_dl_state) { case SSP_FW_DL_STATE_FAIL: @@ -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); } @@ -434,7 +434,7 @@ static const struct of_device_id ssp_of_match[] = { .compatible = "samsung,sensorhub-thermostat", .data = &ssp_thermostat_info, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ssp_of_match); @@ -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/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c index caa404edd9d0..78ac689de2fe 100644 --- a/drivers/iio/common/ssp_sensors/ssp_iio.c +++ b/drivers/iio/common/ssp_sensors/ssp_iio.c @@ -8,6 +8,8 @@ #include <linux/iio/kfifo_buf.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/unaligned.h> +#include <linux/units.h> #include "ssp_iio_sensor.h" /** @@ -70,8 +72,7 @@ EXPORT_SYMBOL_NS(ssp_common_buffer_postdisable, "IIO_SSP_SENSORS"); int ssp_common_process_data(struct iio_dev *indio_dev, void *buf, unsigned int len, int64_t timestamp) { - __le32 time; - int64_t calculated_time = 0; + int64_t calculated_time; struct ssp_sensor_data *spd = iio_priv(indio_dev); if (indio_dev->scan_bytes == 0) @@ -82,11 +83,8 @@ int ssp_common_process_data(struct iio_dev *indio_dev, void *buf, */ memcpy(spd->buffer, buf, len); - if (indio_dev->scan_timestamp) { - memcpy(&time, &((char *)buf)[len], SSP_TIME_SIZE); - calculated_time = - timestamp + (int64_t)le32_to_cpu(time) * 1000000; - } + calculated_time = timestamp + + (int64_t)get_unaligned_le32(buf + len) * MEGA; return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer, calculated_time); diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c b/drivers/iio/common/ssp_sensors/ssp_spi.c index f32b04b63ea1..b7f093d7345b 100644 --- a/drivers/iio/common/ssp_sensors/ssp_spi.c +++ b/drivers/iio/common/ssp_sensors/ssp_spi.c @@ -104,7 +104,7 @@ static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data) /* * It is a bit heavy to do it this way but often the function is used to compose * the message from smaller chunks which are placed on the stack. Often the - * chunks are small so memcpy should be optimalized. + * chunks are small so memcpy should be optimized. */ static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset, const void *src, unsigned int len) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index e4f5a7ff7e74..dac593be5695 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -154,7 +154,7 @@ static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs) return err; st_accel_set_fullscale_error: - dev_err(&indio_dev->dev, "failed to set new fullscale.\n"); + dev_err(indio_dev->dev.parent, "failed to set new fullscale.\n"); return err; } @@ -231,8 +231,7 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) ARRAY_SIZE(regulator_names), regulator_names); if (err) - return dev_err_probe(&indio_dev->dev, err, - "unable to enable supplies\n"); + return dev_err_probe(parent, err, "unable to enable supplies\n"); return 0; } @@ -241,13 +240,14 @@ EXPORT_SYMBOL_NS(st_sensors_power_enable, "IIO_ST_SENSORS"); static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) { + struct device *parent = indio_dev->dev.parent; struct st_sensor_data *sdata = iio_priv(indio_dev); /* Sensor does not support interrupts */ if (!sdata->sensor_settings->drdy_irq.int1.addr && !sdata->sensor_settings->drdy_irq.int2.addr) { if (pdata->drdy_int_pin) - dev_info(&indio_dev->dev, + dev_info(parent, "DRDY on pin INT%d specified, but sensor does not support interrupts\n", pdata->drdy_int_pin); return 0; @@ -256,29 +256,27 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, switch (pdata->drdy_int_pin) { case 1: if (!sdata->sensor_settings->drdy_irq.int1.mask) { - dev_err(&indio_dev->dev, - "DRDY on INT1 not available.\n"); + dev_err(parent, "DRDY on INT1 not available.\n"); return -EINVAL; } sdata->drdy_int_pin = 1; break; case 2: if (!sdata->sensor_settings->drdy_irq.int2.mask) { - dev_err(&indio_dev->dev, - "DRDY on INT2 not available.\n"); + dev_err(parent, "DRDY on INT2 not available.\n"); return -EINVAL; } sdata->drdy_int_pin = 2; break; default: - dev_err(&indio_dev->dev, "DRDY on pdata not valid.\n"); + dev_err(parent, "DRDY on pdata not valid.\n"); return -EINVAL; } if (pdata->open_drain) { if (!sdata->sensor_settings->drdy_irq.int1.addr_od && !sdata->sensor_settings->drdy_irq.int2.addr_od) - dev_err(&indio_dev->dev, + dev_err(parent, "open drain requested but unsupported.\n"); else sdata->int_pin_open_drain = true; @@ -336,6 +334,7 @@ EXPORT_SYMBOL_NS(st_sensors_dev_name_probe, "IIO_ST_SENSORS"); int st_sensors_init_sensor(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) { + struct device *parent = indio_dev->dev.parent; struct st_sensor_data *sdata = iio_priv(indio_dev); struct st_sensors_platform_data *of_pdata; int err = 0; @@ -343,7 +342,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, mutex_init(&sdata->odr_lock); /* If OF/DT pdata exists, it will take precedence of anything else */ - of_pdata = st_sensors_dev_probe(indio_dev->dev.parent, pdata); + of_pdata = st_sensors_dev_probe(parent, pdata); if (IS_ERR(of_pdata)) return PTR_ERR(of_pdata); if (of_pdata) @@ -370,7 +369,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, if (err < 0) return err; } else - dev_info(&indio_dev->dev, "Full-scale not possible\n"); + dev_info(parent, "Full-scale not possible\n"); err = st_sensors_set_odr(indio_dev, sdata->odr); if (err < 0) @@ -405,7 +404,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, mask = sdata->sensor_settings->drdy_irq.int2.mask_od; } - dev_info(&indio_dev->dev, + dev_info(parent, "set interrupt line to open drain mode on pin %d\n", sdata->drdy_int_pin); err = st_sensors_write_data_with_mask(indio_dev, addr, @@ -530,9 +529,8 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev, int err; struct st_sensor_data *sdata = iio_priv(indio_dev); - err = iio_device_claim_direct_mode(indio_dev); - if (err) - return err; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&sdata->odr_lock); @@ -551,7 +549,7 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev, out: mutex_unlock(&sdata->odr_lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return err; } @@ -594,21 +592,20 @@ EXPORT_SYMBOL_NS(st_sensors_get_settings_index, "IIO_ST_SENSORS"); int st_sensors_verify_id(struct iio_dev *indio_dev) { struct st_sensor_data *sdata = iio_priv(indio_dev); + struct device *parent = indio_dev->dev.parent; int wai, err; if (sdata->sensor_settings->wai_addr) { err = regmap_read(sdata->regmap, sdata->sensor_settings->wai_addr, &wai); if (err < 0) { - dev_err(&indio_dev->dev, - "failed to read Who-Am-I register.\n"); - return err; + return dev_err_probe(parent, err, + "failed to read Who-Am-I register.\n"); } if (sdata->sensor_settings->wai != wai) { - dev_warn(&indio_dev->dev, - "%s: WhoAmI mismatch (0x%x).\n", - indio_dev->name, wai); + dev_warn(parent, "%s: WhoAmI mismatch (0x%x).\n", + indio_dev->name, wai); } } diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 9d4bf822a15d..8a8ab688d798 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -127,7 +127,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->trig = devm_iio_trigger_alloc(parent, "%s-trigger", indio_dev->name); if (sdata->trig == NULL) { - dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); + dev_err(parent, "failed to allocate iio trigger.\n"); return -ENOMEM; } @@ -143,7 +143,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, case IRQF_TRIGGER_FALLING: case IRQF_TRIGGER_LOW: if (!sdata->sensor_settings->drdy_irq.addr_ihl) { - dev_err(&indio_dev->dev, + dev_err(parent, "falling/low specified for IRQ but hardware supports only rising/high: will request rising/high\n"); if (irq_trig == IRQF_TRIGGER_FALLING) irq_trig = IRQF_TRIGGER_RISING; @@ -156,21 +156,19 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.mask_ihl, 1); if (err < 0) return err; - dev_info(&indio_dev->dev, + dev_info(parent, "interrupts on the falling edge or active low level\n"); } break; case IRQF_TRIGGER_RISING: - dev_info(&indio_dev->dev, - "interrupts on the rising edge\n"); + dev_info(parent, "interrupts on the rising edge\n"); break; case IRQF_TRIGGER_HIGH: - dev_info(&indio_dev->dev, - "interrupts active high level\n"); + dev_info(parent, "interrupts active high level\n"); break; default: /* This is the most preferred mode, if possible */ - dev_err(&indio_dev->dev, + dev_err(parent, "unsupported IRQ trigger specified (%lx), enforce rising edge\n", irq_trig); irq_trig = IRQF_TRIGGER_RISING; } @@ -179,7 +177,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, if (irq_trig == IRQF_TRIGGER_FALLING || irq_trig == IRQF_TRIGGER_RISING) { if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr) { - dev_err(&indio_dev->dev, + dev_err(parent, "edge IRQ not supported w/o stat register.\n"); return -EOPNOTSUPP; } @@ -214,13 +212,13 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->trig->name, sdata->trig); if (err) { - dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n"); + dev_err(parent, "failed to request trigger IRQ.\n"); return err; } err = devm_iio_trigger_register(parent, sdata->trig); if (err < 0) { - dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); + dev_err(parent, "failed to register iio trigger.\n"); return err; } indio_dev->trig = iio_trigger_get(sdata->trig); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 5d01ba4edbf3..e0996dc014a3 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -6,6 +6,17 @@ menu "Digital to analog converters" +config AD3530R + tristate "Analog Devices AD3530R and Similar DACs driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD3530R, AD3531R + Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad3530r. + config AD3552R_HS tristate "Analog Devices AD3552R DAC High Speed driver" select AD3552R_LIB @@ -80,7 +91,7 @@ config AD5421 depends on SPI help Say yes here to build support for Analog Devices AD5421 loop-powered - digital-to-analog convertors (DAC). + digital-to-analog converters (DAC). To compile this driver as module choose M here: the module will be called ad5421. @@ -296,6 +307,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 @@ -348,6 +362,14 @@ config AD8801 To compile this driver as a module choose M here: the module will be called ad8801. +config BD79703 + tristate "ROHM Semiconductor BD79703 DAC driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for ROHM Semiconductor BD79703 Digital + to Analog Converter (DAC). + config CIO_DAC tristate "Measurement Computing CIO-DAC IIO driver" depends on X86 && (ISA_BUS || PC104) diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 414c152be779..3684cd52b7fa 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD3530R) += ad3530r.o obj-$(CONFIG_AD3552R_HS) += ad3552r-hs.o obj-$(CONFIG_AD3552R_LIB) += ad3552r-common.o obj-$(CONFIG_AD3552R) += ad3552r.o @@ -34,6 +35,7 @@ obj-$(CONFIG_AD8460) += ad8460.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_AD9739A) += ad9739a.o obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o +obj-$(CONFIG_BD79703) += rohm-bd79703.o obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_DS4424) += ds4424.o diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c new file mode 100644 index 000000000000..6134613777b8 --- /dev/null +++ b/drivers/iio/dac/ad3530r.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver + * AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/kstrtox.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/units.h> + +#define AD3530R_INTERFACE_CONFIG_A 0x00 +#define AD3530R_OUTPUT_OPERATING_MODE_0 0x20 +#define AD3530R_OUTPUT_OPERATING_MODE_1 0x21 +#define AD3530R_OUTPUT_CONTROL_0 0x2A +#define AD3530R_REFERENCE_CONTROL_0 0x3C +#define AD3530R_SW_LDAC_TRIG_A 0xE5 +#define AD3530R_INPUT_CH 0xEB +#define AD3530R_MAX_REG_ADDR 0xF9 + +#define AD3531R_SW_LDAC_TRIG_A 0xDD +#define AD3531R_INPUT_CH 0xE3 + +#define AD3530R_SLD_TRIG_A BIT(7) +#define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) +#define AD3530R_REFERENCE_CONTROL_SEL BIT(0) +#define AD3530R_REG_VAL_MASK GENMASK(15, 0) +#define AD3530R_OP_MODE_CHAN_MSK(chan) (GENMASK(1, 0) << 2 * (chan)) + +#define AD3530R_SW_RESET (BIT(7) | BIT(0)) +#define AD3530R_INTERNAL_VREF_mV 2500 +#define AD3530R_LDAC_PULSE_US 100 + +#define AD3530R_DAC_MAX_VAL GENMASK(15, 0) +#define AD3530R_MAX_CHANNELS 8 +#define AD3531R_MAX_CHANNELS 4 + +/* Non-constant mask variant of FIELD_PREP() */ +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + +enum ad3530r_mode { + AD3530R_NORMAL_OP, + AD3530R_POWERDOWN_1K, + AD3530R_POWERDOWN_7K7, + AD3530R_POWERDOWN_32K, +}; + +struct ad3530r_chan { + enum ad3530r_mode powerdown_mode; + bool powerdown; +}; + +struct ad3530r_chip_info { + const char *name; + const struct iio_chan_spec *channels; + int (*input_ch_reg)(unsigned int channel); + unsigned int num_channels; + unsigned int sw_ldac_trig_reg; + bool internal_ref_support; +}; + +struct ad3530r_state { + struct regmap *regmap; + /* lock to protect against multiple access to the device and shared data */ + struct mutex lock; + struct ad3530r_chan chan[AD3530R_MAX_CHANNELS]; + const struct ad3530r_chip_info *chip_info; + struct gpio_desc *ldac_gpio; + int vref_mV; + /* + * DMA (thus cache coherency maintenance) may require the transfer + * buffers to live in their own cache lines. + */ + __be16 buf __aligned(IIO_DMA_MINALIGN); +}; + +static int ad3530r_input_ch_reg(unsigned int channel) +{ + return 2 * channel + AD3530R_INPUT_CH; +} + +static int ad3531r_input_ch_reg(unsigned int channel) +{ + return 2 * channel + AD3531R_INPUT_CH; +} + +static const char * const ad3530r_powerdown_modes[] = { + "1kohm_to_gnd", + "7.7kohm_to_gnd", + "32kohm_to_gnd", +}; + +static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + return st->chan[chan->channel].powerdown_mode - 1; +} + +static int ad3530r_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + st->chan[chan->channel].powerdown_mode = mode + 1; + + return 0; +} + +static const struct iio_enum ad3530r_powerdown_mode_enum = { + .items = ad3530r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3530r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + +static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->lock); + return sysfs_emit(buf, "%d\n", st->chan[chan->channel].powerdown); +} + +static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + int ret; + unsigned int reg, pdmode, mask, val; + bool powerdown; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + guard(mutex)(&st->lock); + reg = chan->channel < AD3531R_MAX_CHANNELS ? + AD3530R_OUTPUT_OPERATING_MODE_0 : + AD3530R_OUTPUT_OPERATING_MODE_1; + pdmode = powerdown ? st->chan[chan->channel].powerdown_mode : 0; + mask = chan->channel < AD3531R_MAX_CHANNELS ? + AD3530R_OP_MODE_CHAN_MSK(chan->channel) : + AD3530R_OP_MODE_CHAN_MSK(chan->channel - 4); + val = field_prep(mask, pdmode); + + ret = regmap_update_bits(st->regmap, reg, mask, val); + if (ret) + return ret; + + st->chan[chan->channel].powerdown = powerdown; + + return len; +} + +static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) +{ + gpiod_set_value_cansleep(ldac_gpio, 1); + fsleep(AD3530R_LDAC_PULSE_US); + gpiod_set_value_cansleep(ldac_gpio, 0); + + return 0; +} + +static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, + unsigned int val) +{ + int ret; + + guard(mutex)(&st->lock); + st->buf = cpu_to_be16(val); + + ret = regmap_bulk_write(st->regmap, st->chip_info->input_ch_reg(chan), + &st->buf, sizeof(st->buf)); + if (ret) + return ret; + + if (st->ldac_gpio) + return ad3530r_trigger_hw_ldac(st->ldac_gpio); + + return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg, + AD3530R_SLD_TRIG_A); +} + +static int ad3530r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + int ret; + + guard(mutex)(&st->lock); + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = regmap_bulk_read(st->regmap, + st->chip_info->input_ch_reg(chan->channel), + &st->buf, sizeof(st->buf)); + if (ret) + return ret; + + *val = FIELD_GET(AD3530R_REG_VAL_MASK, be16_to_cpu(st->buf)); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mV; + *val2 = 16; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad3530r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val > AD3530R_DAC_MAX_VAL) + return -EINVAL; + + return ad3530r_dac_write(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad3530r_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad3530r_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3530r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3530r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3530r_powerdown_mode_enum), + { } +}; + +#define AD3530R_CHAN(_chan) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = ad3530r_ext_info, \ +} + +static const struct iio_chan_spec ad3530r_channels[] = { + AD3530R_CHAN(0), + AD3530R_CHAN(1), + AD3530R_CHAN(2), + AD3530R_CHAN(3), + AD3530R_CHAN(4), + AD3530R_CHAN(5), + AD3530R_CHAN(6), + AD3530R_CHAN(7), +}; + +static const struct iio_chan_spec ad3531r_channels[] = { + AD3530R_CHAN(0), + AD3530R_CHAN(1), + AD3530R_CHAN(2), + AD3530R_CHAN(3), +}; + +static const struct ad3530r_chip_info ad3530_chip = { + .name = "ad3530", + .channels = ad3530r_channels, + .num_channels = ARRAY_SIZE(ad3530r_channels), + .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .input_ch_reg = ad3530r_input_ch_reg, + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3530r_chip = { + .name = "ad3530r", + .channels = ad3530r_channels, + .num_channels = ARRAY_SIZE(ad3530r_channels), + .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .input_ch_reg = ad3530r_input_ch_reg, + .internal_ref_support = true, +}; + +static const struct ad3530r_chip_info ad3531_chip = { + .name = "ad3531", + .channels = ad3531r_channels, + .num_channels = ARRAY_SIZE(ad3531r_channels), + .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .input_ch_reg = ad3531r_input_ch_reg, + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3531r_chip = { + .name = "ad3531r", + .channels = ad3531r_channels, + .num_channels = ARRAY_SIZE(ad3531r_channels), + .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .input_ch_reg = ad3531r_input_ch_reg, + .internal_ref_support = true, +}; + +static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) +{ + struct device *dev = regmap_get_device(st->regmap); + struct gpio_desc *reset_gpio; + int i, ret; + u8 range_multiplier, val; + + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to get reset GPIO\n"); + + if (reset_gpio) { + /* Perform hardware reset */ + fsleep(1 * USEC_PER_MSEC); + gpiod_set_value_cansleep(reset_gpio, 0); + } else { + /* Perform software reset */ + ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, + AD3530R_SW_RESET, AD3530R_SW_RESET); + if (ret) + return ret; + } + + fsleep(10 * USEC_PER_MSEC); + + range_multiplier = 1; + if (device_property_read_bool(dev, "adi,range-double")) { + ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, + AD3530R_OUTPUT_CONTROL_RANGE); + if (ret) + return ret; + + range_multiplier = 2; + } + + if (external_vref_uV) { + st->vref_mV = range_multiplier * external_vref_uV / MILLI; + } else { + ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, + AD3530R_REFERENCE_CONTROL_SEL); + if (ret) + return ret; + + st->vref_mV = range_multiplier * AD3530R_INTERNAL_VREF_mV; + } + + /* Set normal operating mode for all channels */ + val = FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(0), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(1), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); + + ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); + if (ret) + return ret; + + if (st->chip_info->num_channels > 4) { + ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, + val); + if (ret) + return ret; + } + + for (i = 0; i < st->chip_info->num_channels; i++) + st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; + + st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); + if (IS_ERR(st->ldac_gpio)) + return dev_err_probe(dev, PTR_ERR(st->ldac_gpio), + "Failed to get ldac GPIO\n"); + + return 0; +} + +static const struct regmap_config ad3530r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD3530R_MAX_REG_ADDR, +}; + +static const struct iio_info ad3530r_info = { + .read_raw = ad3530r_read_raw, + .write_raw = ad3530r_write_raw, + .debugfs_reg_access = ad3530r_reg_access, +}; + +static int ad3530r_probe(struct spi_device *spi) +{ + static const char * const regulators[] = { "vdd", "iovdd" }; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad3530r_state *st; + int ret, external_vref_uV; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->regmap = devm_regmap_init_spi(spi, &ad3530r_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to init regmap"); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), + regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + external_vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (external_vref_uV < 0 && external_vref_uV != -ENODEV) + return external_vref_uV; + + if (external_vref_uV == -ENODEV) + external_vref_uV = 0; + + if (!st->chip_info->internal_ref_support && external_vref_uV == 0) + return -ENODEV; + + ret = ad3530r_setup(st, external_vref_uV); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad3530r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad3530r_id[] = { + { "ad3530", (kernel_ulong_t)&ad3530_chip }, + { "ad3530r", (kernel_ulong_t)&ad3530r_chip }, + { "ad3531", (kernel_ulong_t)&ad3531_chip }, + { "ad3531r", (kernel_ulong_t)&ad3531r_chip }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad3530r_id); + +static const struct of_device_id ad3530r_of_match[] = { + { .compatible = "adi,ad3530", .data = &ad3530_chip }, + { .compatible = "adi,ad3530r", .data = &ad3530r_chip }, + { .compatible = "adi,ad3531", .data = &ad3531_chip }, + { .compatible = "adi,ad3531r", .data = &ad3531r_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, ad3530r_of_match); + +static struct spi_driver ad3530r_driver = { + .driver = { + .name = "ad3530r", + .of_match_table = ad3530r_of_match, + }, + .probe = ad3530r_probe, + .id_table = ad3530r_id, +}; +module_spi_driver(ad3530r_driver); + +MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD3530R and Similar DACs Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c index 03e0864f5084..38baaea0e6c8 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,54 @@ 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, + .max_reg_addr = 0x46, +}; +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, + .max_reg_addr = 0x49, +}; +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, + .max_reg_addr = 0x46, +}; +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, + .max_reg_addr = 0x49, +}; +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..41b96b48ba98 100644 --- a/drivers/iio/dac/ad3552r-hs.c +++ b/drivers/iio/dac/ad3552r-hs.c @@ -7,6 +7,7 @@ */ #include <linux/bitfield.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/iio/backend.h> @@ -19,6 +20,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 +53,52 @@ 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; + /* Protects backend I/O operations from concurrent accesses. */ + struct mutex lock; +}; + +enum ad3552r_sources { + AD3552R_SRC_NORMAL, + AD3552R_SRC_RAMP_16BIT, +}; + +static const char * const dbgfs_attr_source[] = { + [AD3552R_SRC_NORMAL] = "normal", + [AD3552R_SRC_RAMP_16BIT] = "ramp-16bit", }; -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_set_data_source(struct ad3552r_hs_state *st, + enum iio_backend_data_source type) +{ + int i, ret; + + for (i = 0; i < st->model_data->num_hw_channels; ++i) { + ret = iio_backend_data_source_set(st->back, i, type); + if (ret) + return ret; + } + + return 0; +} + +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 +118,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 +156,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 +238,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 +355,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 +378,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 +409,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 +428,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; } @@ -293,6 +491,122 @@ static int ad3552r_hs_setup_custom_gain(struct ad3552r_hs_state *st, gain, 1); } +static int ad3552r_hs_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + + if (reg > st->model_data->max_reg_addr) + return -EINVAL; + + /* + * There are 8, 16 or 24 bit registers, but HDL supports only reading 8 + * or 16 bit data, not 24. So, also to avoid to check any proper read + * alignment, supporting only 8-bit readings here. + */ + if (readval) + return ad3552r_hs_reg_read(st, reg, readval, 1); + + return st->data->bus_reg_write(st->back, reg, writeval, 1); +} + +static ssize_t ad3552r_hs_show_data_source(struct file *f, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad3552r_hs_state *st = file_inode(f)->i_private; + enum iio_backend_data_source type; + int idx, ret; + + guard(mutex)(&st->lock); + + ret = iio_backend_data_source_get(st->back, 0, &type); + if (ret) + return ret; + + switch (type) { + case IIO_BACKEND_INTERNAL_RAMP_16BIT: + idx = AD3552R_SRC_RAMP_16BIT; + break; + case IIO_BACKEND_EXTERNAL: + idx = AD3552R_SRC_NORMAL; + break; + default: + return -EINVAL; + } + + return simple_read_from_buffer(userbuf, count, ppos, + dbgfs_attr_source[idx], + strlen(dbgfs_attr_source[idx])); +} + +static ssize_t ad3552r_hs_write_data_source(struct file *f, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad3552r_hs_state *st = file_inode(f)->i_private; + char buf[64]; + int ret, source; + + guard(mutex)(&st->lock); + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, + count); + if (ret < 0) + return ret; + + buf[count] = '\0'; + + ret = match_string(dbgfs_attr_source, ARRAY_SIZE(dbgfs_attr_source), + buf); + if (ret < 0) + return ret; + + switch (ret) { + case AD3552R_SRC_RAMP_16BIT: + source = IIO_BACKEND_INTERNAL_RAMP_16BIT; + break; + case AD3552R_SRC_NORMAL: + source = IIO_BACKEND_EXTERNAL; + break; + default: + return -EINVAL; + } + + ret = ad3552r_hs_set_data_source(st, source); + if (ret) + return ret; + + return count; +} + +static ssize_t ad3552r_hs_show_data_source_avail(struct file *f, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + char buf[128]; + int i; + + for (i = 0; i < ARRAY_SIZE(dbgfs_attr_source); i++) { + len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", + dbgfs_attr_source[i]); + } + buf[len - 1] = '\n'; + + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations ad3552r_hs_data_source_fops = { + .owner = THIS_MODULE, + .write = ad3552r_hs_write_data_source, + .read = ad3552r_hs_show_data_source, +}; + +static const struct file_operations ad3552r_hs_data_source_avail_fops = { + .owner = THIS_MODULE, + .read = ad3552r_hs_show_data_source_avail, +}; + static int ad3552r_hs_setup(struct ad3552r_hs_state *st) { u16 id; @@ -304,30 +618,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,19 +674,7 @@ 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; - - ret = iio_backend_data_source_set(st->back, 1, IIO_BACKEND_EXTERNAL); + ret = ad3552r_hs_set_data_source(st, IIO_BACKEND_EXTERNAL); if (ret) return ret; @@ -363,19 +684,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; } @@ -455,8 +778,29 @@ static const struct iio_chan_spec ad3552r_hs_channels[] = { static const struct iio_info ad3552r_hs_info = { .read_raw = &ad3552r_hs_read_raw, .write_raw = &ad3552r_hs_write_raw, + .debugfs_reg_access = &ad3552r_hs_reg_access, }; +static void ad3552r_hs_debugfs_init(struct iio_dev *indio_dev) +{ + struct ad3552r_hs_state *st = iio_priv(indio_dev); + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + + d = iio_get_debugfs_dentry(indio_dev); + if (!d) { + dev_warn(st->dev, "can't set debugfs in driver dir\n"); + return; + } + + debugfs_create_file("data_source", 0600, d, st, + &ad3552r_hs_data_source_fops); + debugfs_create_file("data_source_available", 0600, d, st, + &ad3552r_hs_data_source_avail_fops); +} + static int ad3552r_hs_probe(struct platform_device *pdev) { struct ad3552r_hs_state *st; @@ -501,18 +845,23 @@ static int ad3552r_hs_probe(struct platform_device *pdev) if (ret) return ret; - return devm_iio_device_register(&pdev->dev, indio_dev); -} + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return ret; -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), -}; + ret = devm_mutex_init(&pdev->dev, &st->lock); + if (ret) + return ret; + + ad3552r_hs_debugfs_init(indio_dev); + + return ret; +} 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 e7206af53af6..a44b163f3183 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -410,6 +410,12 @@ static int ad3552r_reset(struct ad3552r_desc *dac) return ret; } + /* Clear reset error flag, see ad3552r manual, rev B table 38. */ + ret = ad3552r_write_reg(dac, AD3552R_REG_ADDR_ERR_STATUS, + AD3552R_MASK_RESET_STATUS); + if (ret) + return ret; + return ad3552r_update_reg_field(dac, AD3552R_REG_ADDR_INTERFACE_CONFIG_A, AD3552R_MASK_ADDR_ASCENSION, @@ -649,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..9bb46a9e07a5 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,8 @@ struct ad3552r_model_data { const s32 (*ranges_table)[2]; int num_ranges; bool requires_output_range; + int num_spi_data_lanes; + int max_reg_addr; }; struct ad3552r_ch_data { diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index 905988724f27..84be5174babd 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -378,7 +378,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5064_powerdown_mode_enum), - { }, + { } }; static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = { @@ -390,7 +390,7 @@ static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, <c2617_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, <c2617_powerdown_mode_enum), - { }, + { } }; #define AD5064_CHANNEL(chan, addr, bits, _shift, _ext_info) { \ @@ -936,7 +936,7 @@ static const struct spi_device_id ad5064_spi_ids[] = { {"ad5668-1", ID_AD5668_1}, {"ad5668-2", ID_AD5668_2}, {"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */ - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5064_spi_ids); @@ -1048,7 +1048,7 @@ static const struct i2c_device_id ad5064_i2c_ids[] = { {"ltc2635-h10", ID_LTC2635_H10}, {"ltc2635-l8", ID_LTC2635_L8}, {"ltc2635-h8", ID_LTC2635_H8}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index e0b7f658d611..a57b0a093112 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -542,7 +542,7 @@ static const struct spi_device_id ad5360_ids[] = { { "ad5371", ID_AD5371 }, { "ad5372", ID_AD5372 }, { "ad5373", ID_AD5373 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5360_ids); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 392a1c7aee03..f63af704b77e 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -246,7 +246,7 @@ static const struct iio_chan_spec_ext_info ad5380_ext_info[] = { IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), - { }, + { } }; #define AD5380_CHANNEL(_bits) { \ diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 6ad99f97eed5..ad304b0fec08 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -141,7 +141,7 @@ static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5446_powerdown_mode_enum), - { }, + { } }; #define _AD5446_CHANNEL(bits, storage, _shift, ext) { \ @@ -440,7 +440,7 @@ static const struct spi_device_id ad5446_spi_ids[] = { {"dac101s101", ID_AD5310}, {"dac121s101", ID_AD5320}, {"dac7512", ID_AD5320}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); @@ -543,7 +543,7 @@ static const struct i2c_device_id ad5446_i2c_ids[] = { {"ad5602", ID_AD5602}, {"ad5612", ID_AD5612}, {"ad5622", ID_AD5622}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids); diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index 1c996016756a..d8c325260259 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -337,7 +337,7 @@ static const struct spi_device_id ad5449_spi_ids[] = { { "ad5439", ID_AD5439 }, { "ad5443", ID_AD5443 }, { "ad5449", ID_AD5449 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5449_spi_ids); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index ff0765c8af47..355bcb6a8ba0 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -242,7 +242,7 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5504_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5504_powerdown_mode_enum), - { }, + { } }; #define AD5504_CHANNEL(_chan) { \ @@ -320,7 +320,7 @@ static int ad5504_probe(struct spi_device *spi) static const struct spi_device_id ad5504_id[] = { {"ad5504", ID_AD5504}, {"ad5501", ID_AD5501}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5504_id); diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 50d19304bacb..5f2cd51723f6 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -7,6 +7,7 @@ */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/iio/iio.h> #include <linux/module.h> @@ -24,16 +25,14 @@ static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset) { struct ad5592r_state *st = gpiochip_get_data(chip); int ret = 0; - u8 val; + u8 val = 0; - mutex_lock(&st->gpio_lock); - - if (st->gpio_out & BIT(offset)) - val = st->gpio_val; - else - ret = st->ops->gpio_read(st, &val); - - mutex_unlock(&st->gpio_lock); + scoped_guard(mutex, &st->gpio_lock) { + if (st->gpio_out & BIT(offset)) + val = st->gpio_val; + else + ret = st->ops->gpio_read(st, &val); + } if (ret < 0) return ret; @@ -41,20 +40,19 @@ static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(offset)); } -static void ad5592r_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int ad5592r_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct ad5592r_state *st = gpiochip_get_data(chip); - mutex_lock(&st->gpio_lock); + guard(mutex)(&st->gpio_lock); if (value) st->gpio_val |= BIT(offset); else st->gpio_val &= ~BIT(offset); - st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val); - - mutex_unlock(&st->gpio_lock); + return st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val); } static int ad5592r_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -62,21 +60,16 @@ static int ad5592r_gpio_direction_input(struct gpio_chip *chip, unsigned offset) struct ad5592r_state *st = gpiochip_get_data(chip); int ret; - mutex_lock(&st->gpio_lock); + guard(mutex)(&st->gpio_lock); st->gpio_out &= ~BIT(offset); st->gpio_in |= BIT(offset); ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out); if (ret < 0) - goto err_unlock; - - ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in); - -err_unlock: - mutex_unlock(&st->gpio_lock); + return ret; - return ret; + return st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in); } static int ad5592r_gpio_direction_output(struct gpio_chip *chip, @@ -85,7 +78,7 @@ static int ad5592r_gpio_direction_output(struct gpio_chip *chip, struct ad5592r_state *st = gpiochip_get_data(chip); int ret; - mutex_lock(&st->gpio_lock); + guard(mutex)(&st->gpio_lock); if (value) st->gpio_val |= BIT(offset); @@ -97,18 +90,13 @@ static int ad5592r_gpio_direction_output(struct gpio_chip *chip, ret = st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val); if (ret < 0) - goto err_unlock; + return ret; ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out); if (ret < 0) - goto err_unlock; - - ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in); - -err_unlock: - mutex_unlock(&st->gpio_lock); + return ret; - return ret; + return st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in); } static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset) @@ -141,7 +129,7 @@ static int ad5592r_gpio_init(struct ad5592r_state *st) st->gpiochip.direction_input = ad5592r_gpio_direction_input; st->gpiochip.direction_output = ad5592r_gpio_direction_output; st->gpiochip.get = ad5592r_gpio_get; - st->gpiochip.set = ad5592r_gpio_set; + st->gpiochip.set_rv = ad5592r_gpio_set; st->gpiochip.request = ad5592r_gpio_request; st->gpiochip.owner = THIS_MODULE; st->gpiochip.names = ad5592r_gpio_names; @@ -155,6 +143,8 @@ static void ad5592r_gpio_cleanup(struct ad5592r_state *st) { if (st->gpio_map) gpiochip_remove(&st->gpiochip); + + mutex_destroy(&st->gpio_lock); } static int ad5592r_reset(struct ad5592r_state *st) @@ -169,10 +159,9 @@ static int ad5592r_reset(struct ad5592r_state *st) udelay(1); gpiod_set_value(gpio, 1); } else { - mutex_lock(&st->lock); - /* Writing this magic value resets the device */ - st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac); - mutex_unlock(&st->lock); + scoped_guard(mutex, &st->lock) + /* Writing this magic value resets the device */ + st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac); } udelay(250); @@ -247,46 +236,44 @@ static int ad5592r_set_channel_modes(struct ad5592r_state *st) } } - mutex_lock(&st->lock); + guard(mutex)(&st->lock); /* Pull down unused pins to GND */ ret = ops->reg_write(st, AD5592R_REG_PULLDOWN, pulldown); if (ret) - goto err_unlock; + return ret; ret = ops->reg_write(st, AD5592R_REG_TRISTATE, tristate); if (ret) - goto err_unlock; + return ret; /* Configure pins that we use */ ret = ops->reg_write(st, AD5592R_REG_DAC_EN, dac); if (ret) - goto err_unlock; + return ret; ret = ops->reg_write(st, AD5592R_REG_ADC_EN, adc); if (ret) - goto err_unlock; + return ret; ret = ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val); if (ret) - goto err_unlock; + return ret; ret = ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out); if (ret) - goto err_unlock; + return ret; ret = ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in); if (ret) - goto err_unlock; + return ret; /* Verify that we can read back at least one register */ ret = ops->reg_read(st, AD5592R_REG_ADC_EN, &read_back); if (!ret && (read_back & 0xff) != adc) - ret = -EIO; + return -EIO; -err_unlock: - mutex_unlock(&st->lock); - return ret; + return 0; } static int ad5592r_reset_channel_modes(struct ad5592r_state *st) @@ -303,7 +290,7 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct ad5592r_state *st = iio_priv(iio_dev); - int ret; + int ret = 0; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -314,11 +301,11 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, if (!chan->output) return -EINVAL; - mutex_lock(&st->lock); - ret = st->ops->write_dac(st, chan->channel, val); - if (!ret) - st->cached_dac[chan->channel] = val; - mutex_unlock(&st->lock); + scoped_guard(mutex, &st->lock) { + ret = st->ops->write_dac(st, chan->channel, val); + if (!ret) + st->cached_dac[chan->channel] = val; + } return ret; case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_VOLTAGE) { @@ -333,14 +320,12 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, else return -EINVAL; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); ret = st->ops->reg_read(st, AD5592R_REG_CTRL, &st->cached_gp_ctrl); - if (ret < 0) { - mutex_unlock(&st->lock); + if (ret < 0) return ret; - } if (chan->output) { if (gain) @@ -358,11 +343,8 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, ~AD5592R_REG_CTRL_ADC_RANGE; } - ret = st->ops->reg_write(st, AD5592R_REG_CTRL, - st->cached_gp_ctrl); - mutex_unlock(&st->lock); - - return ret; + return st->ops->reg_write(st, AD5592R_REG_CTRL, + st->cached_gp_ctrl); } break; default: @@ -377,15 +359,15 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, int *val, int *val2, long m) { struct ad5592r_state *st = iio_priv(iio_dev); - u16 read_val; - int ret, mult; + u16 read_val = 0; + int ret = 0, mult = 0; switch (m) { case IIO_CHAN_INFO_RAW: if (!chan->output) { - mutex_lock(&st->lock); - ret = st->ops->read_adc(st, chan->channel, &read_val); - mutex_unlock(&st->lock); + scoped_guard(mutex, &st->lock) + ret = st->ops->read_adc(st, chan->channel, + &read_val); if (ret) return ret; @@ -398,9 +380,8 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, read_val &= GENMASK(11, 0); } else { - mutex_lock(&st->lock); - read_val = st->cached_dac[chan->channel]; - mutex_unlock(&st->lock); + scoped_guard(mutex, &st->lock) + read_val = st->cached_dac[chan->channel]; } dev_dbg(st->dev, "Channel %u read: 0x%04hX\n", @@ -418,35 +399,32 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, return IIO_VAL_INT_PLUS_NANO; } - mutex_lock(&st->lock); - - if (chan->output) - mult = !!(st->cached_gp_ctrl & - AD5592R_REG_CTRL_DAC_RANGE); - else - mult = !!(st->cached_gp_ctrl & - AD5592R_REG_CTRL_ADC_RANGE); - - mutex_unlock(&st->lock); + scoped_guard(mutex, &st->lock) { + if (chan->output) + mult = !!(st->cached_gp_ctrl & + AD5592R_REG_CTRL_DAC_RANGE); + else + mult = !!(st->cached_gp_ctrl & + AD5592R_REG_CTRL_ADC_RANGE); + } *val *= ++mult; *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; - case IIO_CHAN_INFO_OFFSET: + case IIO_CHAN_INFO_OFFSET: { ret = ad5592r_get_vref(st); - mutex_lock(&st->lock); + guard(mutex)(&st->lock); if (st->cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE) *val = (-34365 * 25) / ret; else *val = (-75365 * 25) / ret; - mutex_unlock(&st->lock); - return IIO_VAL_INT; + } default: return -EINVAL; } @@ -490,7 +468,7 @@ static const struct iio_chan_spec_ext_info ad5592r_ext_info[] = { .read = ad5592r_show_scale_available, .shared = IIO_SHARED_BY_TYPE, }, - {}, + { } }; static void ad5592r_setup_channel(struct iio_dev *iio_dev, @@ -606,6 +584,10 @@ int ad5592r_probe(struct device *dev, const char *name, st->num_channels = 8; dev_set_drvdata(dev, iio_dev); + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + st->reg = devm_regulator_get_optional(dev, "vref"); if (IS_ERR(st->reg)) { if ((PTR_ERR(st->reg) != -ENODEV) && dev_fwnode(dev)) @@ -622,8 +604,6 @@ int ad5592r_probe(struct device *dev, const char *name, iio_dev->info = &ad5592r_info; iio_dev->modes = INDIO_DIRECT_MODE; - mutex_init(&st->lock); - ad5592r_init_scales(st, ad5592r_get_vref(st)); ret = ad5592r_reset(st); diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c index fd82d8701322..92d1b629b85d 100644 --- a/drivers/iio/dac/ad5592r.c +++ b/drivers/iio/dac/ad5592r.c @@ -137,19 +137,19 @@ static void ad5592r_spi_remove(struct spi_device *spi) static const struct spi_device_id ad5592r_spi_ids[] = { { .name = "ad5592r", }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5592r_spi_ids); static const struct of_device_id ad5592r_of_match[] = { { .compatible = "adi,ad5592r", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ad5592r_of_match); static const struct acpi_device_id ad5592r_acpi_match[] = { {"ADS5592", }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match); diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index ddd13ad821a7..9a8525c61173 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -116,19 +116,19 @@ static void ad5593r_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id ad5593r_i2c_ids[] = { { .name = "ad5593r", }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, ad5593r_i2c_ids); static const struct of_device_id ad5593r_of_match[] = { { .compatible = "adi,ad5593r", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ad5593r_of_match); static const struct acpi_device_id ad5593r_acpi_match[] = { {"ADS5593", }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, ad5593r_acpi_match); diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h index 14a439b06eb6..098fb5a7683d 100644 --- a/drivers/iio/dac/ad5624r.h +++ b/drivers/iio/dac/ad5624r.h @@ -41,11 +41,9 @@ struct ad5624r_chip_info { }; /** - * struct ad5446_state - driver instance specific data - * @indio_dev: the industrial I/O device + * struct ad5624r_state - driver instance specific data * @us: spi_device * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator * @vref_mv: actual reference voltage used * @pwr_down_mask power down mask * @pwr_down_mode current power down mode diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 2fd38ac8f698..13aefe769bad 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -160,7 +160,7 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5624r_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5624r_powerdown_mode_enum), - { }, + { } }; #define AD5624R_CHANNEL(_chan, _bits) { \ @@ -266,7 +266,7 @@ static const struct spi_device_id ad5624r_id[] = { {"ad5624r5", ID_AD5624R5}, {"ad5644r5", ID_AD5644R5}, {"ad5664r5", ID_AD5664R5}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5624r_id); diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 39b5dad0d6a5..df8619e0c092 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -95,11 +95,6 @@ static int ad5686_spi_probe(struct spi_device *spi) ad5686_spi_write, ad5686_spi_read); } -static void ad5686_spi_remove(struct spi_device *spi) -{ - ad5686_remove(&spi->dev); -} - static const struct spi_device_id ad5686_spi_id[] = { {"ad5310r", ID_AD5310R}, {"ad5672r", ID_AD5672R}, @@ -117,7 +112,7 @@ static const struct spi_device_id ad5686_spi_id[] = { {"ad5685r", ID_AD5685R}, {"ad5686", ID_AD5686}, {"ad5686r", ID_AD5686R}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5686_spi_id); @@ -126,7 +121,6 @@ static struct spi_driver ad5686_spi_driver = { .name = "ad5686", }, .probe = ad5686_spi_probe, - .remove = ad5686_spi_remove, .id_table = ad5686_spi_id, }; diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 8dc578b08784..d9cae9555e5d 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -185,7 +185,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5686_powerdown_mode_enum), - { }, + { } }; #define AD5868_CHANNEL(chan, addr, bits, _shift) { \ @@ -455,39 +455,28 @@ int ad5686_probe(struct device *dev, struct ad5686_state *st; struct iio_dev *indio_dev; unsigned int val, ref_bit_msk; + bool has_external_vref; u8 cmd; - int ret, i, voltage_uv = 0; + int ret, i; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); - dev_set_drvdata(dev, indio_dev); st->dev = dev; st->write = write; st->read = read; - st->reg = devm_regulator_get_optional(dev, "vcc"); - if (!IS_ERR(st->reg)) { - ret = regulator_enable(st->reg); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg); - if (ret < 0) - goto error_disable_reg; - - voltage_uv = ret; - } - st->chip_info = &ad5686_chip_info_tbl[chip_type]; - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = st->chip_info->int_vref_mv; + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + if (ret < 0 && ret != -ENODEV) + return ret; + + has_external_vref = ret != -ENODEV; + st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv; /* Set all the power down mode for all channels to 1K pulldown */ for (i = 0; i < st->chip_info->num_channels; i++) @@ -505,12 +494,12 @@ int ad5686_probe(struct device *dev, case AD5310_REGMAP: cmd = AD5686_CMD_CONTROL_REG; ref_bit_msk = AD5310_REF_BIT_MSK; - st->use_internal_vref = !voltage_uv; + st->use_internal_vref = !has_external_vref; break; case AD5683_REGMAP: cmd = AD5686_CMD_CONTROL_REG; ref_bit_msk = AD5683_REF_BIT_MSK; - st->use_internal_vref = !voltage_uv; + st->use_internal_vref = !has_external_vref; break; case AD5686_REGMAP: cmd = AD5686_CMD_INTERNAL_REFER_SETUP; @@ -519,43 +508,22 @@ int ad5686_probe(struct device *dev, case AD5693_REGMAP: cmd = AD5686_CMD_CONTROL_REG; ref_bit_msk = AD5693_REF_BIT_MSK; - st->use_internal_vref = !voltage_uv; + st->use_internal_vref = !has_external_vref; break; default: - ret = -EINVAL; - goto error_disable_reg; + return -EINVAL; } - val = (voltage_uv | ref_bit_msk); + val = (has_external_vref | ref_bit_msk); ret = st->write(st, cmd, 0, !!val); if (ret) - goto error_disable_reg; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; + return ret; -error_disable_reg: - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); - return ret; + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad5686_probe, "IIO_AD5686"); -void ad5686_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5686_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (!IS_ERR(st->reg)) - regulator_disable(st->reg); -} -EXPORT_SYMBOL_NS_GPL(ad5686_remove, "IIO_AD5686"); - MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 760f852911df..e7d36bae3e59 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -115,10 +115,9 @@ struct ad5686_chip_info { }; /** - * struct ad5446_state - driver instance specific data + * struct ad5686_state - driver instance specific data * @spi: spi_device * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator * @vref_mv: actual reference voltage used * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode @@ -130,7 +129,6 @@ struct ad5686_chip_info { struct ad5686_state { struct device *dev; const struct ad5686_chip_info *chip_info; - struct regulator *reg; unsigned short vref_mv; unsigned int pwr_down_mask; unsigned int pwr_down_mode; @@ -157,7 +155,5 @@ int ad5686_probe(struct device *dev, const char *name, ad5686_write_func write, ad5686_read_func read); -void ad5686_remove(struct device *dev); - #endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index bbcda246c547..d3327bca0e07 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -65,11 +65,6 @@ static int ad5686_i2c_probe(struct i2c_client *i2c) ad5686_i2c_write, ad5686_i2c_read); } -static void ad5686_i2c_remove(struct i2c_client *i2c) -{ - ad5686_remove(&i2c->dev); -} - static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5311r", ID_AD5311R}, {"ad5337r", ID_AD5337R}, @@ -87,7 +82,7 @@ static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5695r", ID_AD5695R}, {"ad5696", ID_AD5696}, {"ad5696r", ID_AD5696R}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); @@ -106,7 +101,7 @@ static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5695r" }, { .compatible = "adi,ad5696" }, { .compatible = "adi,ad5696r" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad5686_of_match); @@ -116,7 +111,6 @@ static struct i2c_driver ad5686_i2c_driver = { .of_match_table = ad5686_of_match, }, .probe = ad5686_i2c_probe, - .remove = ad5686_i2c_remove, .id_table = ad5686_i2c_id, }; diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 05e80b6ae2cc..d0e5f35462b1 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -522,7 +522,7 @@ static const struct iio_chan_spec_ext_info ad5755_ext_info[] = { .write = ad5755_write_powerdown, .shared = IIO_SEPARATE, }, - { }, + { } }; #define AD5755_CHANNEL(_bits) { \ @@ -853,7 +853,7 @@ static const struct spi_device_id ad5755_id[] = { { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5755_id); diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index 98771e37a7b5..4ed4fda76ea9 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -878,7 +878,7 @@ static int ad5758_probe(struct spi_device *spi) static const struct spi_device_id ad5758_id[] = { { "ad5758", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5758_id); diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index 0aa5ba7f4654..b5d20f04f070 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -137,13 +137,11 @@ static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val) struct spi_transfer xfers[] = { { .tx_buf = &st->data[0].d8[1], - .bits_per_word = 8, .len = 3, .cs_change = true, }, { .tx_buf = &st->data[1].d8[1], .rx_buf = &st->data[2].d8[1], - .bits_per_word = 8, .len = 3, }, }; @@ -348,7 +346,7 @@ static const struct spi_device_id ad5761_id[] = { {"ad5721r", ID_AD5721R}, {"ad5761", ID_AD5761}, {"ad5761r", ID_AD5761R}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5761_id); diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index f658ac8086aa..f6a0a0d84fef 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -148,13 +148,11 @@ static int __ad5766_spi_read(struct ad5766_state *st, u8 dac, int *val) struct spi_transfer xfers[] = { { .tx_buf = &st->data[0].d32, - .bits_per_word = 8, .len = 3, .cs_change = 1, }, { .tx_buf = &st->data[1].d32, .rx_buf = &st->data[2].d32, - .bits_per_word = 8, .len = 3, }, }; @@ -437,7 +435,7 @@ static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { IIO_ENUM("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum), IIO_ENUM_AVAILABLE("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum), - {} + { } }; #define AD576x_CHANNEL(_chan, _bits) { \ @@ -648,14 +646,14 @@ static int ad5766_probe(struct spi_device *spi) static const struct of_device_id ad5766_dt_match[] = { { .compatible = "adi,ad5766" }, { .compatible = "adi,ad5767" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad5766_dt_match); static const struct spi_device_id ad5766_spi_ids[] = { { "ad5766", ID_AD5766 }, { "ad5767", ID_AD5767 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad5766_spi_ids); diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 25cf11d0471b..6eb4027a44fb 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -637,13 +637,13 @@ static int ad5770r_probe(struct spi_device *spi) static const struct of_device_id ad5770r_of_id[] = { { .compatible = "adi,ad5770r", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ad5770r_of_id); static const struct spi_device_id ad5770r_id[] = { { "ad5770r", 0 }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, ad5770r_id); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 57374f78f6b8..41582f2b90fb 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; @@ -123,13 +138,11 @@ static int ad5791_spi_read(struct ad5791_state *st, u8 addr, u32 *val) struct spi_transfer xfers[] = { { .tx_buf = &st->data[0].d8[1], - .bits_per_word = 8, .len = 3, .cs_change = 1, }, { .tx_buf = &st->data[1].d8[1], .rx_buf = &st->data[2].d8[1], - .bits_per_word = 8, .len = 3, }, }; @@ -232,6 +245,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 +291,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; } @@ -275,7 +310,7 @@ static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5791_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5791_powerdown_mode_enum), - { }, + { } }; #define AD5791_DEFINE_CHIP_INFO(_name, bits, _shift, _lin_comp) \ @@ -294,7 +329,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 +375,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 +561,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 +612,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/ad7293.c b/drivers/iio/dac/ad7293.c index 1d4032670482..c3797e40cdd9 100644 --- a/drivers/iio/dac/ad7293.c +++ b/drivers/iio/dac/ad7293.c @@ -114,6 +114,7 @@ #define AD7293_REG_DATA_RAW_MSK GENMASK(15, 4) #define AD7293_REG_VINX_RANGE_GET_CH_MSK(x, ch) (((x) >> (ch)) & 0x1) #define AD7293_REG_VINX_RANGE_SET_CH_MSK(x, ch) (((x) & 0x1) << (ch)) +#define AD7293_GENERAL_ADC_REF_MSK BIT(7) #define AD7293_CHIP_ID 0x18 enum ad7293_ch_type { @@ -141,8 +142,7 @@ struct ad7293_state { /* Protect against concurrent accesses to the device, page selection and data content */ struct mutex lock; struct gpio_desc *gpio_reset; - struct regulator *reg_avdd; - struct regulator *reg_vdrive; + bool vrefin_en; u8 page_select; u8 data[3] __aligned(IIO_DMA_MINALIGN); }; @@ -777,6 +777,21 @@ static int ad7293_reset(struct ad7293_state *st) static int ad7293_properties_parse(struct ad7293_state *st) { struct spi_device *spi = st->spi; + int ret; + + ret = devm_regulator_get_enable(&spi->dev, "avdd"); + if (ret) + return dev_err_probe(&spi->dev, ret, "failed to enable AVDD\n"); + + ret = devm_regulator_get_enable(&spi->dev, "vdrive"); + if (ret) + return dev_err_probe(&spi->dev, ret, "failed to enable VDRIVE\n"); + + ret = devm_regulator_get_enable_optional(&spi->dev, "vrefin"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, "failed to enable VREFIN\n"); + + st->vrefin_en = ret != -ENODEV; st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); @@ -784,24 +799,9 @@ static int ad7293_properties_parse(struct ad7293_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_reset), "failed to get the reset GPIO\n"); - st->reg_avdd = devm_regulator_get(&spi->dev, "avdd"); - if (IS_ERR(st->reg_avdd)) - return dev_err_probe(&spi->dev, PTR_ERR(st->reg_avdd), - "failed to get the AVDD voltage\n"); - - st->reg_vdrive = devm_regulator_get(&spi->dev, "vdrive"); - if (IS_ERR(st->reg_vdrive)) - return dev_err_probe(&spi->dev, PTR_ERR(st->reg_vdrive), - "failed to get the VDRIVE voltage\n"); - return 0; } -static void ad7293_reg_disable(void *data) -{ - regulator_disable(data); -} - static int ad7293_init(struct ad7293_state *st) { int ret; @@ -816,48 +816,6 @@ static int ad7293_init(struct ad7293_state *st) if (ret) return ret; - ret = regulator_enable(st->reg_avdd); - if (ret) { - dev_err(&spi->dev, - "Failed to enable specified AVDD Voltage!\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, - st->reg_avdd); - if (ret) - return ret; - - ret = regulator_enable(st->reg_vdrive); - if (ret) { - dev_err(&spi->dev, - "Failed to enable specified VDRIVE Voltage!\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, - st->reg_vdrive); - if (ret) - return ret; - - ret = regulator_get_voltage(st->reg_avdd); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read avdd regulator: %d\n", ret); - return ret; - } - - if (ret > 5500000 || ret < 4500000) - return -EINVAL; - - ret = regulator_get_voltage(st->reg_vdrive); - if (ret < 0) { - dev_err(&spi->dev, - "Failed to read vdrive regulator: %d\n", ret); - return ret; - } - if (ret > 5500000 || ret < 1700000) - return -EINVAL; - /* Check Chip ID */ ret = __ad7293_spi_read(st, AD7293_REG_DEVICE_ID, &chip_id); if (ret) @@ -868,6 +826,11 @@ static int ad7293_init(struct ad7293_state *st) return -EINVAL; } + if (!st->vrefin_en) + return __ad7293_spi_update_bits(st, AD7293_REG_GENERAL, + AD7293_GENERAL_ADC_REF_MSK, + AD7293_GENERAL_ADC_REF_MSK); + return 0; } @@ -909,13 +872,13 @@ static int ad7293_probe(struct spi_device *spi) static const struct spi_device_id ad7293_id[] = { { "ad7293", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7293_id); static const struct of_device_id ad7293_of_match[] = { { .compatible = "adi,ad7293" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad7293_of_match); diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index bff6bf697d9c..a88cc639047d 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -173,7 +173,7 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { .write = ad7303_write_dac_powerdown, .shared = IIO_SEPARATE, }, - { }, + { } }; #define AD7303_CHANNEL(chan) { \ @@ -264,13 +264,13 @@ static int ad7303_probe(struct spi_device *spi) static const struct of_device_id ad7303_spi_of_match[] = { { .compatible = "adi,ad7303", }, - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(of, ad7303_spi_of_match); static const struct spi_device_id ad7303_spi_ids[] = { { "ad7303", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad7303_spi_ids); 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/ad8801.c b/drivers/iio/dac/ad8801.c index 919e8c880697..60e663af1cc1 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -23,8 +23,6 @@ struct ad8801_state { unsigned char dac_cache[8]; /* Value write on each channel */ unsigned int vrefh_mv; unsigned int vrefl_mv; - struct regulator *vrefh_reg; - struct regulator *vrefl_reg; __be16 data __aligned(IIO_DMA_MINALIGN); }; @@ -122,92 +120,40 @@ static int ad8801_probe(struct spi_device *spi) state->spi = spi; id = spi_get_device_id(spi); - state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); - if (IS_ERR(state->vrefh_reg)) - return dev_err_probe(&spi->dev, PTR_ERR(state->vrefh_reg), - "Vrefh regulator not specified\n"); + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefh"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "failed to get Vrefh voltage\n"); - ret = regulator_enable(state->vrefh_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n", - ret); - return ret; - } - - ret = regulator_get_voltage(state->vrefh_reg); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n", - ret); - goto error_disable_vrefh_reg; - } state->vrefh_mv = ret / 1000; if (id->driver_data == ID_AD8803) { - state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); - if (IS_ERR(state->vrefl_reg)) { - ret = dev_err_probe(&spi->dev, PTR_ERR(state->vrefl_reg), - "Vrefl regulator not specified\n"); - goto error_disable_vrefh_reg; - } - - ret = regulator_enable(state->vrefl_reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n", - ret); - goto error_disable_vrefh_reg; - } - - ret = regulator_get_voltage(state->vrefl_reg); - if (ret < 0) { - dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n", - ret); - goto error_disable_vrefl_reg; - } + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vrefl"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "failed to get Vrefl voltage\n"); + state->vrefl_mv = ret / 1000; - } else { - state->vrefl_mv = 0; - state->vrefl_reg = NULL; } - spi_set_drvdata(spi, indio_dev); indio_dev->info = &ad8801_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad8801_channels; indio_dev->num_channels = ARRAY_SIZE(ad8801_channels); indio_dev->name = id->name; - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&spi->dev, "Failed to register iio device: %d\n", - ret); - goto error_disable_vrefl_reg; - } + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Failed to register iio device\n"); return 0; - -error_disable_vrefl_reg: - if (state->vrefl_reg) - regulator_disable(state->vrefl_reg); -error_disable_vrefh_reg: - regulator_disable(state->vrefh_reg); - return ret; -} - -static void ad8801_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad8801_state *state = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (state->vrefl_reg) - regulator_disable(state->vrefl_reg); - regulator_disable(state->vrefh_reg); } static const struct spi_device_id ad8801_ids[] = { {"ad8801", ID_AD8801}, {"ad8803", ID_AD8803}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad8801_ids); @@ -216,7 +162,6 @@ static struct spi_driver ad8801_driver = { .name = "ad8801", }, .probe = ad8801_probe, - .remove = ad8801_remove, .id_table = ad8801_ids, }; module_spi_driver(ad8801_driver); diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c index b6a65359b0b4..d77b46d83bd4 100644 --- a/drivers/iio/dac/ad9739a.c +++ b/drivers/iio/dac/ad9739a.c @@ -442,13 +442,13 @@ static int ad9739a_probe(struct spi_device *spi) static const struct of_device_id ad9739a_of_match[] = { { .compatible = "adi,ad9739a" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad9739a_of_match); static const struct spi_device_id ad9739a_id[] = { {"ad9739a"}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad9739a_id); diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index b143f7ed6847..33faba4b02c2 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) @@ -84,6 +84,7 @@ #define AXI_DAC_CHAN_CNTRL_7_REG(c) (0x0418 + (c) * 0x40) #define AXI_DAC_CHAN_CNTRL_7_DATA_SEL GENMASK(3, 0) +#define AXI_DAC_CHAN_CNTRL_MAX 15 #define AXI_DAC_RD_ADDR(x) (BIT(7) | (x)) /* 360 degrees in rad */ @@ -168,7 +169,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 { @@ -186,6 +187,9 @@ static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan, u32 reg, raw; int ret; + if (chan > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + if (!st->dac_clk) { dev_err(st->dev, "Sampling rate is 0...\n"); return -EINVAL; @@ -230,6 +234,9 @@ static int axi_dac_scale_get(struct axi_dac_state *st, int ret, vals[2]; u32 reg, raw; + if (chan->channel > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + if (tone_2) reg = AXI_DAC_CHAN_CNTRL_3_REG(chan->channel); else @@ -264,6 +271,9 @@ static int axi_dac_phase_get(struct axi_dac_state *st, u32 reg, raw, phase; int ret, vals[2]; + if (chan->channel > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + if (tone_2) reg = AXI_DAC_CHAN_CNTRL_4_REG(chan->channel); else @@ -291,6 +301,9 @@ static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan, u16 raw; int ret; + if (chan > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + if (!sample_rate || freq > sample_rate / 2) { dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n", freq, sample_rate); @@ -342,6 +355,9 @@ static int axi_dac_scale_set(struct axi_dac_state *st, u32 raw = 0, reg; int ret; + if (chan->channel > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac); if (ret) return ret; @@ -385,6 +401,9 @@ static int axi_dac_phase_set(struct axi_dac_state *st, u32 raw, reg; int ret; + if (chan->channel > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac); if (ret) return ret; @@ -469,7 +488,7 @@ static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = { IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2), IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1), IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2), - {} + { } }; static int axi_dac_extend_chan(struct iio_backend *back, @@ -493,6 +512,9 @@ static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan, { struct axi_dac_state *st = iio_backend_get_priv(back); + if (chan > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + switch (data) { case IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE: return regmap_update_bits(st->regmap, @@ -514,6 +536,35 @@ static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan, } } +static int axi_dac_data_source_get(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source *data) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + int ret; + u32 val; + + if (chan > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; + + ret = regmap_read(st->regmap, AXI_DAC_CHAN_CNTRL_7_REG(chan), &val); + if (ret) + return ret; + + switch (val) { + case AXI_DAC_DATA_INTERNAL_TONE: + *data = IIO_BACKEND_INTERNAL_CONTINUOUS_WAVE; + return 0; + case AXI_DAC_DATA_DMA: + *data = IIO_BACKEND_EXTERNAL; + return 0; + case AXI_DAC_DATA_INTERNAL_RAMP_16BIT: + *data = IIO_BACKEND_INTERNAL_RAMP_16BIT; + return 0; + default: + return -EIO; + } +} + static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan, u64 sample_rate) { @@ -521,6 +572,8 @@ static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan, unsigned int freq; int ret, tone; + if (chan > AXI_DAC_CHAN_CNTRL_MAX) + return -EINVAL; if (!sample_rate) return -EINVAL; if (st->reg_config & AXI_DAC_CONFIG_DDS_DISABLE) @@ -585,6 +638,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); @@ -699,6 +760,7 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val, { struct axi_dac_state *st = iio_backend_get_priv(back); int ret; + u32 ival; guard(mutex)(&st->lock); @@ -711,9 +773,38 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val, if (ret) return ret; + ret = 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); + if (ret) + return ret; + 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 +816,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 = { @@ -763,6 +855,7 @@ static const struct iio_backend_ops axi_ad3552r_ops = { .request_buffer = axi_dac_request_buffer, .free_buffer = axi_dac_free_buffer, .data_source_set = axi_dac_data_source_set, + .data_source_get = axi_dac_data_source_get, .ddr_enable = axi_dac_ddr_enable, .ddr_disable = axi_dac_ddr_disable, .data_stream_enable = axi_dac_data_stream_enable, @@ -930,7 +1023,7 @@ static const struct axi_dac_info dac_ad3552r = { static const struct of_device_id axi_dac_of_match[] = { { .compatible = "adi,axi-dac-9.1.b", .data = &dac_generic }, { .compatible = "adi,axi-ad3552r", .data = &dac_ad3552r }, - {} + { } }; MODULE_DEVICE_TABLE(of, axi_dac_of_match); diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index f36f10bfb6be..d1b8441051ae 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -237,7 +237,7 @@ static void dpot_dac_remove(struct platform_device *pdev) static const struct of_device_id dpot_dac_match[] = { { .compatible = "dpot-dac" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, dpot_dac_match); diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index e89e4c054653..a26a99753418 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -301,7 +301,7 @@ MODULE_DEVICE_TABLE(i2c, ds4424_id); static const struct of_device_id ds4424_of_match[] = { { .compatible = "maxim,ds4422" }, { .compatible = "maxim,ds4424" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ds4424_of_match); diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 2332b0c22691..aa1c73f8429d 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -179,7 +179,7 @@ static void lpc18xx_dac_remove(struct platform_device *pdev) static const struct of_device_id lpc18xx_dac_match[] = { { .compatible = "nxp,lpc1850-dac" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, lpc18xx_dac_match); diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c index 2758fc8a5ad5..6e80b49f4665 100644 --- a/drivers/iio/dac/ltc1660.c +++ b/drivers/iio/dac/ltc1660.c @@ -219,14 +219,14 @@ static void ltc1660_remove(struct spi_device *spi) static const struct of_device_id ltc1660_dt_ids[] = { { .compatible = "lltc,ltc1660", .data = (void *)ID_LTC1660 }, { .compatible = "lltc,ltc1665", .data = (void *)ID_LTC1665 }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, ltc1660_dt_ids); static const struct spi_device_id ltc1660_id[] = { {"ltc1660", ID_LTC1660}, {"ltc1665", ID_LTC1665}, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(spi, ltc1660_id); diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index a4fb2509c950..105f939f7e54 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -41,13 +41,11 @@ struct ltc2632_chip_info { * @spi_dev: pointer to the spi_device struct * @powerdown_cache_mask: used to show current channel powerdown state * @vref_mv: used reference voltage (internal or external) - * @vref_reg: regulator for the reference voltage */ struct ltc2632_state { struct spi_device *spi_dev; unsigned int powerdown_cache_mask; int vref_mv; - struct regulator *vref_reg; }; enum ltc2632_supported_device_ids { @@ -178,7 +176,7 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { .write = ltc2632_write_dac_powerdown, .shared = IIO_SEPARATE, }, - { }, + { } }; #define LTC2632_CHANNEL(_chan, _bits) { \ @@ -310,6 +308,7 @@ static int ltc2632_probe(struct spi_device *spi) struct ltc2632_state *st; struct iio_dev *indio_dev; struct ltc2632_chip_info *chip_info; + bool has_external_vref; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -318,49 +317,31 @@ static int ltc2632_probe(struct spi_device *spi) st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->spi_dev = spi; chip_info = (struct ltc2632_chip_info *) spi_get_device_id(spi)->driver_data; - st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); - if (PTR_ERR(st->vref_reg) == -ENODEV) { - /* use internal reference voltage */ - st->vref_reg = NULL; - st->vref_mv = chip_info->vref_mv; + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, + "Failed to get vref regulator voltage\n"); - ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, - 0, 0, 0); - if (ret) { - dev_err(&spi->dev, - "Set internal reference command failed, %d\n", - ret); - return ret; - } - } else if (IS_ERR(st->vref_reg)) { - dev_err(&spi->dev, - "Error getting voltage reference regulator\n"); - return PTR_ERR(st->vref_reg); - } else { - /* use external reference voltage */ - ret = regulator_enable(st->vref_reg); - if (ret) { - dev_err(&spi->dev, - "enable reference regulator failed, %d\n", - ret); - return ret; - } - st->vref_mv = regulator_get_voltage(st->vref_reg) / 1000; + has_external_vref = ret != -ENODEV; + st->vref_mv = has_external_vref ? ret / 1000 : chip_info->vref_mv; + if (has_external_vref) { ret = ltc2632_spi_write(spi, LTC2632_CMD_EXTERNAL_REFER, - 0, 0, 0); - if (ret) { - dev_err(&spi->dev, - "Set external reference command failed, %d\n", - ret); - return ret; - } + 0, 0, 0); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Set external reference command failed\n"); + } else { + ret = ltc2632_spi_write(spi, LTC2632_CMD_INTERNAL_REFER, + 0, 0, 0); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Set internal reference command failed\n"); } indio_dev->name = fwnode_get_name(dev_fwnode(&spi->dev)) ?: spi_get_device_id(spi)->name; @@ -369,18 +350,7 @@ static int ltc2632_probe(struct spi_device *spi) indio_dev->channels = chip_info->channels; indio_dev->num_channels = chip_info->num_channels; - return iio_device_register(indio_dev); -} - -static void ltc2632_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ltc2632_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (st->vref_reg) - regulator_disable(st->vref_reg); + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ltc2632_id[] = { @@ -402,7 +372,7 @@ static const struct spi_device_id ltc2632_id[] = { { "ltc2636-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] }, { "ltc2636-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] }, { "ltc2636-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ltc2632_id); @@ -462,7 +432,7 @@ static const struct of_device_id ltc2632_of_match[] = { .compatible = "lltc,ltc2636-h8", .data = <c2632_chip_info_tbl[ID_LTC2636H8] }, - {} + { } }; MODULE_DEVICE_TABLE(of, ltc2632_of_match); @@ -472,7 +442,6 @@ static struct spi_driver ltc2632_driver = { .of_match_table = ltc2632_of_match, }, .probe = ltc2632_probe, - .remove = ltc2632_remove, .id_table = ltc2632_id, }; module_spi_driver(ltc2632_driver); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index 376dca163c91..1f24f07d1ad2 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -104,13 +104,11 @@ static int ltc2688_spi_read(void *context, const void *reg, size_t reg_size, struct spi_transfer xfers[] = { { .tx_buf = st->tx_data, - .bits_per_word = 8, .len = reg_size + val_size, .cs_change = 1, }, { .tx_buf = st->tx_data + 3, .rx_buf = st->rx_data, - .bits_per_word = 8, .len = reg_size + val_size, }, }; @@ -608,7 +606,7 @@ static const struct iio_chan_spec_ext_info ltc2688_toggle_sym_ext_info[] = { ltc2688_reg_bool_get, ltc2688_reg_bool_set), LTC2688_CHAN_EXT_INFO("symbol", LTC2688_CMD_SW_TOGGLE, IIO_SEPARATE, ltc2688_reg_bool_get, ltc2688_reg_bool_set), - {} + { } }; static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = { @@ -621,7 +619,7 @@ static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = { ltc2688_dither_toggle_set), LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, ltc2688_reg_bool_get, ltc2688_reg_bool_set), - {} + { } }; static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = { @@ -649,13 +647,13 @@ static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = { ltc2688_dither_toggle_set), LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, ltc2688_reg_bool_get, ltc2688_reg_bool_set), - {} + { } }; static const struct iio_chan_spec_ext_info ltc2688_ext_info[] = { LTC2688_CHAN_EXT_INFO("powerdown", LTC2688_CMD_POWERDOWN, IIO_SEPARATE, ltc2688_reg_bool_get, ltc2688_reg_bool_set), - {} + { } }; #define LTC2688_CHANNEL(_chan) { \ @@ -842,7 +840,7 @@ static int ltc2688_channel_config(struct ltc2688_state *st) return 0; } -static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref) +static int ltc2688_setup(struct ltc2688_state *st, bool has_external_vref) { struct device *dev = &st->spi->dev; struct gpio_desc *gpio; @@ -881,18 +879,13 @@ static int ltc2688_setup(struct ltc2688_state *st, struct regulator *vref) if (ret) return ret; - if (!vref) + if (!has_external_vref) return 0; return regmap_set_bits(st->regmap, LTC2688_CMD_CONFIG, LTC2688_CONFIG_EXT_REF); } -static void ltc2688_disable_regulator(void *regulator) -{ - regulator_disable(regulator); -} - static bool ltc2688_reg_readable(struct device *dev, unsigned int reg) { switch (reg) { @@ -947,8 +940,8 @@ static int ltc2688_probe(struct spi_device *spi) static const char * const regulators[] = { "vcc", "iovcc" }; struct ltc2688_state *st; struct iio_dev *indio_dev; - struct regulator *vref_reg; struct device *dev = &spi->dev; + bool has_external_vref; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -973,34 +966,15 @@ static int ltc2688_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "Failed to enable regulators\n"); - vref_reg = devm_regulator_get_optional(dev, "vref"); - if (IS_ERR(vref_reg)) { - if (PTR_ERR(vref_reg) != -ENODEV) - return dev_err_probe(dev, PTR_ERR(vref_reg), - "Failed to get vref regulator"); - - vref_reg = NULL; - /* internal reference */ - st->vref = 4096; - } else { - ret = regulator_enable(vref_reg); - if (ret) - return dev_err_probe(dev, ret, - "Failed to enable vref regulators\n"); - - ret = devm_add_action_or_reset(dev, ltc2688_disable_regulator, - vref_reg); - if (ret) - return ret; - - ret = regulator_get_voltage(vref_reg); - if (ret < 0) - return dev_err_probe(dev, ret, "Failed to get vref\n"); + ret = devm_regulator_get_enable_read_voltage(dev, "vref"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to get vref regulator voltage\n"); - st->vref = ret / 1000; - } + has_external_vref = ret != -ENODEV; + st->vref = has_external_vref ? ret / 1000 : 0; - ret = ltc2688_setup(st, vref_reg); + ret = ltc2688_setup(st, has_external_vref); if (ret) return ret; @@ -1015,13 +989,13 @@ static int ltc2688_probe(struct spi_device *spi) static const struct of_device_id ltc2688_of_id[] = { { .compatible = "adi,ltc2688" }, - {} + { } }; MODULE_DEVICE_TABLE(of, ltc2688_of_id); static const struct spi_device_id ltc2688_id[] = { { "ltc2688" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ltc2688_id); diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c index 9f72155dcbc7..1b8fe6b8d26e 100644 --- a/drivers/iio/dac/max5522.c +++ b/drivers/iio/dac/max5522.c @@ -174,7 +174,7 @@ static int max5522_spi_probe(struct spi_device *spi) static const struct spi_device_id max5522_ids[] = { { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] }, - {} + { } }; MODULE_DEVICE_TABLE(spi, max5522_ids); @@ -183,7 +183,7 @@ static const struct of_device_id max5522_of_match[] = { .compatible = "maxim,max5522", .data = &max5522_chip_info_tbl[ID_MAX5522], }, - {} + { } }; MODULE_DEVICE_TABLE(of, max5522_of_match); diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index 18ba3eaaad75..e7e29359f8fe 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -32,7 +32,6 @@ enum max5821_device_ids { struct max5821_data { struct i2c_client *client; - struct regulator *vref_reg; unsigned short vref_mv; bool powerdown[MAX5821_MAX_DAC_CHANNELS]; u8 powerdown_mode[MAX5821_MAX_DAC_CHANNELS]; @@ -138,7 +137,7 @@ static const struct iio_chan_spec_ext_info max5821_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &max5821_powerdown_mode_enum), - { }, + { } }; #define MAX5821_CHANNEL(chan) { \ @@ -295,11 +294,6 @@ static const struct iio_info max5821_info = { .write_raw = max5821_write_raw, }; -static void max5821_regulator_disable(void *reg) -{ - regulator_disable(reg); -} - static int max5821_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -321,32 +315,10 @@ static int max5821_probe(struct i2c_client *client) data->powerdown_mode[tmp] = MAX5821_100KOHM_TO_GND; } - data->vref_reg = devm_regulator_get(&client->dev, "vref"); - if (IS_ERR(data->vref_reg)) - return dev_err_probe(&client->dev, PTR_ERR(data->vref_reg), - "Failed to get vref regulator\n"); - - ret = regulator_enable(data->vref_reg); - if (ret) { - dev_err(&client->dev, - "Failed to enable vref regulator: %d\n", ret); - return ret; - } - - ret = devm_add_action_or_reset(&client->dev, max5821_regulator_disable, - data->vref_reg); - if (ret) { - dev_err(&client->dev, - "Failed to add action to managed regulator: %d\n", ret); - return ret; - } - - ret = regulator_get_voltage(data->vref_reg); - if (ret < 0) { - dev_err(&client->dev, - "Failed to get voltage on regulator: %d\n", ret); - return ret; - } + ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to get vref regulator voltage\n"); data->vref_mv = ret / 1000; diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 25bb1c0490af..62972494a229 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -241,7 +241,7 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { &mcp472x_powerdown_mode_enum[MCP4725]), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &mcp472x_powerdown_mode_enum[MCP4725]), - { }, + { } }; static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { @@ -255,7 +255,7 @@ static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { &mcp472x_powerdown_mode_enum[MCP4726]), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &mcp472x_powerdown_mode_enum[MCP4726]), - { }, + { } }; static const struct iio_chan_spec mcp472x_channel[] = { @@ -379,7 +379,7 @@ static int mcp4725_probe_dt(struct device *dev, struct mcp4725_platform_data *pdata) { /* check if is the vref-supply defined */ - pdata->use_vref = device_property_read_bool(dev, "vref-supply"); + pdata->use_vref = device_property_present(dev, "vref-supply"); pdata->vref_buffered = device_property_read_bool(dev, "microchip,vref-buffered"); diff --git a/drivers/iio/dac/mcp4728.c b/drivers/iio/dac/mcp4728.c index 192175dc6419..4f30b99110b7 100644 --- a/drivers/iio/dac/mcp4728.c +++ b/drivers/iio/dac/mcp4728.c @@ -286,7 +286,7 @@ static const struct iio_chan_spec_ext_info mcp4728_ext_info[] = { IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &mcp4728_powerdown_mode_enum), - {}, + { } }; static const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = { @@ -573,13 +573,13 @@ static int mcp4728_probe(struct i2c_client *client) static const struct i2c_device_id mcp4728_id[] = { { "mcp4728" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mcp4728_id); static const struct of_device_id mcp4728_of_match[] = { { .compatible = "microchip,mcp4728" }, - {} + { } }; MODULE_DEVICE_TABLE(of, mcp4728_of_match); diff --git a/drivers/iio/dac/mcp4821.c b/drivers/iio/dac/mcp4821.c index c1a59bbbba3c..748bdca9a964 100644 --- a/drivers/iio/dac/mcp4821.c +++ b/drivers/iio/dac/mcp4821.c @@ -206,7 +206,7 @@ static const struct of_device_id mcp4821_of_table[] = { MCP4821_COMPATIBLE("microchip,mcp4812", ID_MCP4812), MCP4821_COMPATIBLE("microchip,mcp4821", ID_MCP4821), MCP4821_COMPATIBLE("microchip,mcp4822", ID_MCP4822), - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, mcp4821_of_table); @@ -217,7 +217,7 @@ static const struct spi_device_id mcp4821_id_table[] = { { "mcp4812", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4812]}, { "mcp4821", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4821]}, { "mcp4822", (kernel_ulong_t)&mcp4821_chip_info_table[ID_MCP4822]}, - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(spi, mcp4821_id_table); diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index 26aa99059813..74f338afcab9 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -161,7 +161,7 @@ static const struct spi_device_id mcp4922_id[] = { {"mcp4912", ID_MCP4912}, {"mcp4921", ID_MCP4921}, {"mcp4922", ID_MCP4922}, - {} + { } }; MODULE_DEVICE_TABLE(spi, mcp4922_id); diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c new file mode 100644 index 000000000000..a35c37d2261d --- /dev/null +++ b/drivers/iio/dac/rohm-bd79703.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BD79703 ROHM Digital to Analog converter + * + * Copyright (c) 2024, ROHM Semiconductor. + */ + +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> + +#define BD79703_MAX_REGISTER 0xf +#define BD79703_DAC_BITS 8 +#define BD79703_REG_OUT_ALL GENMASK(2, 0) + +/* + * The BD79703 uses 12-bit SPI commands. First four bits (high bits) define + * channel(s) which are operated on, and also the mode. The mode can be to set + * a DAC word only, or set DAC word and output. The data-sheet is not very + * specific on how a previously set DAC word can be 'taken in to use'. Thus + * this driver only uses the 'set DAC and output it' -mode. + * + * The BD79703 latches last 12-bits when the chip-select is toggled. Thus we + * can use 16-bit transfers which should be widely supported. To simplify this + * further, we treat the last 8 bits as a value, and first 8 bits as an + * address. This allows us to separate channels/mode by address and treat the + * 8-bit register value as DAC word. The highest 4 bits of address will be + * discarded when the transfer is latched. + */ +static const struct regmap_config bd79703_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = BD79703_MAX_REGISTER, + .cache_type = REGCACHE_RBTREE, +}; + +/* Dynamic driver private data */ +struct bd79703_data { + struct regmap *regmap; + int vfs; +}; + +/* Static, IC type specific data for different variants */ +struct bd7970x_chip_data { + const char *name; + const struct iio_chan_spec *channels; + int num_channels; + bool has_vfs; +}; + +static int bd79703_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct bd79703_data *data = iio_priv(idev); + + if (mask != IIO_CHAN_INFO_SCALE) + return -EINVAL; + + *val = data->vfs / 1000; + *val2 = BD79703_DAC_BITS; + + return IIO_VAL_FRACTIONAL_LOG2; +} + +static int bd79703_write_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct bd79703_data *data = iio_priv(idev); + + if (val < 0 || val >= 1 << BD79703_DAC_BITS) + return -EINVAL; + + return regmap_write(data->regmap, chan->address, val); +}; + +static const struct iio_info bd79703_info = { + .read_raw = bd79703_read_raw, + .write_raw = bd79703_write_raw, +}; + +#define BD79703_CHAN_ADDR(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (_addr), \ +} + +#define BD79703_CHAN(_chan) BD79703_CHAN_ADDR((_chan), (_chan) + 1) + +static const struct iio_chan_spec bd79700_channels[] = { + BD79703_CHAN(0), + BD79703_CHAN(1), +}; + +static const struct iio_chan_spec bd79701_channels[] = { + BD79703_CHAN(0), + BD79703_CHAN(1), + BD79703_CHAN(2), +}; + +/* + * The BD79702 has 4 channels. They aren't mapped to BD79703 channels 0, 1, 2 + * and 3, but to the channels 0, 1, 4, 5. So the addressing used with SPI + * accesses is 1, 2, 5 and 6 for them. Thus, they're not constant offset to + * the channel number as with other IC variants. + */ +static const struct iio_chan_spec bd79702_channels[] = { + BD79703_CHAN_ADDR(0, 1), + BD79703_CHAN_ADDR(1, 2), + BD79703_CHAN_ADDR(2, 5), + BD79703_CHAN_ADDR(3, 6), +}; + +static const struct iio_chan_spec bd79703_channels[] = { + BD79703_CHAN(0), + BD79703_CHAN(1), + BD79703_CHAN(2), + BD79703_CHAN(3), + BD79703_CHAN(4), + BD79703_CHAN(5), +}; + +static const struct bd7970x_chip_data bd79700_chip_data = { + .name = "bd79700", + .channels = bd79700_channels, + .num_channels = ARRAY_SIZE(bd79700_channels), + .has_vfs = false, +}; + +static const struct bd7970x_chip_data bd79701_chip_data = { + .name = "bd79701", + .channels = bd79701_channels, + .num_channels = ARRAY_SIZE(bd79701_channels), + .has_vfs = false, +}; + +static const struct bd7970x_chip_data bd79702_chip_data = { + .name = "bd79702", + .channels = bd79702_channels, + .num_channels = ARRAY_SIZE(bd79702_channels), + .has_vfs = true, +}; + +static const struct bd7970x_chip_data bd79703_chip_data = { + .name = "bd79703", + .channels = bd79703_channels, + .num_channels = ARRAY_SIZE(bd79703_channels), + .has_vfs = true, +}; + +static int bd79703_probe(struct spi_device *spi) +{ + const struct bd7970x_chip_data *cd; + struct device *dev = &spi->dev; + struct bd79703_data *data; + struct iio_dev *idev; + int ret; + + cd = spi_get_device_match_data(spi); + if (!cd) + return -ENODEV; + + idev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!idev) + return -ENOMEM; + + data = iio_priv(idev); + + data->regmap = devm_regmap_init_spi(spi, &bd79703_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "Failed to initialize Regmap\n"); + + /* + * BD79703 has a separate VFS pin, whereas the BD79700 and BD79701 use + * VCC for their full-scale output voltage. + */ + if (cd->has_vfs) { + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable VCC\n"); + + ret = devm_regulator_get_enable_read_voltage(dev, "vfs"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get Vfs\n"); + } else { + ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get VCC\n"); + } + data->vfs = ret; + + idev->channels = cd->channels; + idev->num_channels = cd->num_channels; + idev->modes = INDIO_DIRECT_MODE; + idev->info = &bd79703_info; + idev->name = cd->name; + + /* Initialize all to output zero */ + ret = regmap_write(data->regmap, BD79703_REG_OUT_ALL, 0); + if (ret) + return ret; + + return devm_iio_device_register(dev, idev); +} + +static const struct spi_device_id bd79703_id[] = { + { "bd79700", (kernel_ulong_t)&bd79700_chip_data }, + { "bd79701", (kernel_ulong_t)&bd79701_chip_data }, + { "bd79702", (kernel_ulong_t)&bd79702_chip_data }, + { "bd79703", (kernel_ulong_t)&bd79703_chip_data }, + { } +}; +MODULE_DEVICE_TABLE(spi, bd79703_id); + +static const struct of_device_id bd79703_of_match[] = { + { .compatible = "rohm,bd79700", .data = &bd79700_chip_data }, + { .compatible = "rohm,bd79701", .data = &bd79701_chip_data }, + { .compatible = "rohm,bd79702", .data = &bd79702_chip_data }, + { .compatible = "rohm,bd79703", .data = &bd79703_chip_data }, + { } +}; +MODULE_DEVICE_TABLE(of, bd79703_of_match); + +static struct spi_driver bd79703_driver = { + .driver = { + .name = "bd79703", + .of_match_table = bd79703_of_match, + }, + .probe = bd79703_probe, + .id_table = bd79703_id, +}; +module_spi_driver(bd79703_driver); + +MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); +MODULE_DESCRIPTION("ROHM BD79703 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 95ed5197d16f..8ef702917060 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -239,7 +239,7 @@ static const struct of_device_id stm32_dac_of_match[] = { .compatible = "st,stm32h7-dac-core", .data = (void *)&stm32h7_dac_cfg, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, stm32_dac_of_match); diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index 3bfb368b3a23..344388338d9b 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -250,7 +250,7 @@ static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &stm32_dac_powerdown_mode_en), - {}, + { } }; #define STM32_DAC_CHANNEL(chan, name) { \ @@ -392,7 +392,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dac_pm_ops, stm32_dac_suspend, static const struct of_device_id stm32_dac_of_match[] = { { .compatible = "st,stm32-dac", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, stm32_dac_of_match); diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index 8e1590e3cc8b..715870c8a9c4 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -161,7 +161,7 @@ static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), - { }, + { } }; #define TI_DAC_CHANNEL(chan) { \ diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index c5162b72951a..bdc3f94aef98 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -216,7 +216,7 @@ static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &dac5571_powerdown_mode), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), - {}, + { } }; #define dac5571_CHANNEL(chan, name) { \ @@ -398,7 +398,7 @@ static const struct of_device_id dac5571_of_id[] = { {.compatible = "ti,dac5573", .data = &dac5571_spec[quad_8bit] }, {.compatible = "ti,dac6573", .data = &dac5571_spec[quad_10bit] }, {.compatible = "ti,dac7573", .data = &dac5571_spec[quad_12bit] }, - {} + { } }; MODULE_DEVICE_TABLE(of, dac5571_of_id); @@ -414,7 +414,7 @@ static const struct i2c_device_id dac5571_id[] = { {"dac5573", (kernel_ulong_t)&dac5571_spec[quad_8bit] }, {"dac6573", (kernel_ulong_t)&dac5571_spec[quad_10bit] }, {"dac7573", (kernel_ulong_t)&dac5571_spec[quad_12bit] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, dac5571_id); diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 6f4aa4794a0c..3d2ce61f0db6 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -147,7 +147,7 @@ static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), - { }, + { } }; #define TI_DAC_CHANNEL(chan) { \ diff --git a/drivers/iio/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c index 8195815de26f..c308eca02b88 100644 --- a/drivers/iio/dac/ti-dac7612.c +++ b/drivers/iio/dac/ti-dac7612.c @@ -166,7 +166,7 @@ static int dac7612_probe(struct spi_device *spi) static const struct spi_device_id dac7612_id[] = { {"ti-dac7612"}, - {} + { } }; MODULE_DEVICE_TABLE(spi, dac7612_id); @@ -174,7 +174,7 @@ static const struct of_device_id dac7612_of_match[] = { { .compatible = "ti,dac7612" }, { .compatible = "ti,dac7612u" }, { .compatible = "ti,dac7612ub" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, dac7612_of_match); diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index 82a078fa98ad..b30ff7bb4400 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -99,7 +99,7 @@ static const struct iio_enum vf610_conversion_mode = { static const struct iio_chan_spec_ext_info vf610_ext_info[] = { IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode), - {}, + { } }; #define VF610_DAC_CHAN(_chan_type) { \ @@ -166,7 +166,7 @@ static const struct iio_info vf610_dac_iio_info = { static const struct of_device_id vf610_dac_match[] = { { .compatible = "fsl,vf610-dac", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, vf610_dac_match); 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/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index 288880346707..e35e0596cbfb 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -31,6 +31,11 @@ static const s16 fakedata[] = { [DUMMY_INDEX_ACCELX] = 344, }; +struct dummy_scan { + s16 data[ARRAY_SIZE(fakedata)]; + aligned_s64 timestamp; +}; + /** * iio_simple_dummy_trigger_h() - the trigger handler function * @irq: the interrupt number @@ -45,11 +50,18 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; + struct dummy_scan *scan; int i = 0, j; - u16 *data; - data = kzalloc(indio_dev->scan_bytes, GFP_KERNEL); - if (!data) + /* + * Note that some buses such as SPI require DMA safe buffers which + * cannot be on the stack. Two easy ways to do this: + * - Local kzalloc (as done here) + * - A buffer at the end of the structure accessed via iio_priv() + * that is marked __aligned(IIO_DMA_MINALIGN). + */ + scan = kzalloc(sizeof(*scan), GFP_KERNEL); + if (!scan) goto done; /* @@ -69,13 +81,12 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) * constant table fakedata. */ iio_for_each_active_channel(indio_dev, j) - data[i++] = fakedata[j]; - - iio_push_to_buffers_with_timestamp(indio_dev, data, - iio_get_time_ns(indio_dev)); + scan->data[i++] = fakedata[j]; - kfree(data); + iio_push_to_buffers_with_ts(indio_dev, scan, sizeof(*scan), + iio_get_time_ns(indio_dev)); + kfree(scan); done: /* * Tell the core we are done with this trigger and ready for the diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index 848baa6e3bbf..19f823446cda 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -14,6 +14,7 @@ #include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/notifier.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/spi/spi.h> #include <linux/units.h> @@ -70,6 +71,16 @@ #define ADMV8818_HPF_WR0_MSK GENMASK(7, 4) #define ADMV8818_LPF_WR0_MSK GENMASK(3, 0) +#define ADMV8818_BAND_BYPASS 0 +#define ADMV8818_BAND_MIN 1 +#define ADMV8818_BAND_MAX 4 +#define ADMV8818_BAND_CORNER_LOW 0 +#define ADMV8818_BAND_CORNER_HIGH 1 + +#define ADMV8818_STATE_MIN 0 +#define ADMV8818_STATE_MAX 15 +#define ADMV8818_NUM_STATES 16 + enum { ADMV8818_BW_FREQ, ADMV8818_CENTER_FREQ @@ -90,20 +101,24 @@ struct admv8818_state { struct mutex lock; unsigned int filter_mode; u64 cf_hz; + u64 lpf_margin_hz; + u64 hpf_margin_hz; }; -static const unsigned long long freq_range_hpf[4][2] = { +static const unsigned long long freq_range_hpf[5][2] = { + {0ULL, 0ULL}, /* bypass */ {1750000000ULL, 3550000000ULL}, {3400000000ULL, 7250000000ULL}, {6600000000, 12000000000}, {12500000000, 19900000000} }; -static const unsigned long long freq_range_lpf[4][2] = { +static const unsigned long long freq_range_lpf[5][2] = { + {U64_MAX, U64_MAX}, /* bypass */ {2050000000ULL, 3850000000ULL}, {3350000000ULL, 7250000000ULL}, {7000000000, 13000000000}, - {12550000000, 18500000000} + {12550000000, 18850000000} }; static const struct regmap_config admv8818_regmap_config = { @@ -121,44 +136,59 @@ static const char * const admv8818_modes[] = { static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq) { - unsigned int hpf_step = 0, hpf_band = 0, i, j; - u64 freq_step; - int ret; + int band, state, ret; + unsigned int hpf_state = ADMV8818_STATE_MIN, hpf_band = ADMV8818_BAND_BYPASS; + u64 freq_error, min_freq_error, freq_corner, freq_step; - if (freq < freq_range_hpf[0][0]) + if (freq < freq_range_hpf[ADMV8818_BAND_MIN][ADMV8818_BAND_CORNER_LOW]) goto hpf_write; - if (freq > freq_range_hpf[3][1]) { - hpf_step = 15; - hpf_band = 4; - + if (freq >= freq_range_hpf[ADMV8818_BAND_MAX][ADMV8818_BAND_CORNER_HIGH]) { + hpf_state = ADMV8818_STATE_MAX; + hpf_band = ADMV8818_BAND_MAX; goto hpf_write; } - for (i = 0; i < 4; i++) { - freq_step = div_u64((freq_range_hpf[i][1] - - freq_range_hpf[i][0]), 15); + /* Close HPF frequency gap between 12 and 12.5 GHz */ + if (freq >= 12000ULL * HZ_PER_MHZ && freq < 12500ULL * HZ_PER_MHZ) { + hpf_state = ADMV8818_STATE_MAX; + hpf_band = 3; + goto hpf_write; + } - if (freq > freq_range_hpf[i][0] && - (freq < freq_range_hpf[i][1] + freq_step)) { - hpf_band = i + 1; + min_freq_error = U64_MAX; + for (band = ADMV8818_BAND_MIN; band <= ADMV8818_BAND_MAX; band++) { + /* + * This (and therefore all other ranges) have a corner + * frequency higher than the target frequency. + */ + if (freq_range_hpf[band][ADMV8818_BAND_CORNER_LOW] > freq) + break; - for (j = 1; j <= 16; j++) { - if (freq < (freq_range_hpf[i][0] + (freq_step * j))) { - hpf_step = j - 1; - break; - } + freq_step = freq_range_hpf[band][ADMV8818_BAND_CORNER_HIGH] - + freq_range_hpf[band][ADMV8818_BAND_CORNER_LOW]; + freq_step = div_u64(freq_step, ADMV8818_NUM_STATES - 1); + + for (state = ADMV8818_STATE_MIN; state <= ADMV8818_STATE_MAX; state++) { + freq_corner = freq_range_hpf[band][ADMV8818_BAND_CORNER_LOW] + + freq_step * state; + + /* + * This (and therefore all other states) have a corner + * frequency higher than the target frequency. + */ + if (freq_corner > freq) + break; + + freq_error = freq - freq_corner; + if (freq_error < min_freq_error) { + min_freq_error = freq_error; + hpf_state = state; + hpf_band = band; } - break; } } - /* Close HPF frequency gap between 12 and 12.5 GHz */ - if (freq >= 12000 * HZ_PER_MHZ && freq <= 12500 * HZ_PER_MHZ) { - hpf_band = 3; - hpf_step = 15; - } - hpf_write: ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW, ADMV8818_SW_IN_SET_WR0_MSK | @@ -170,7 +200,7 @@ hpf_write: return regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, ADMV8818_HPF_WR0_MSK, - FIELD_PREP(ADMV8818_HPF_WR0_MSK, hpf_step)); + FIELD_PREP(ADMV8818_HPF_WR0_MSK, hpf_state)); } static int admv8818_hpf_select(struct admv8818_state *st, u64 freq) @@ -186,31 +216,52 @@ static int admv8818_hpf_select(struct admv8818_state *st, u64 freq) static int __admv8818_lpf_select(struct admv8818_state *st, u64 freq) { - unsigned int lpf_step = 0, lpf_band = 0, i, j; - u64 freq_step; - int ret; + int band, state, ret; + unsigned int lpf_state = ADMV8818_STATE_MIN, lpf_band = ADMV8818_BAND_BYPASS; + u64 freq_error, min_freq_error, freq_corner, freq_step; - if (freq > freq_range_lpf[3][1]) + if (freq > freq_range_lpf[ADMV8818_BAND_MAX][ADMV8818_BAND_CORNER_HIGH]) goto lpf_write; - if (freq < freq_range_lpf[0][0]) { - lpf_band = 1; - + if (freq < freq_range_lpf[ADMV8818_BAND_MIN][ADMV8818_BAND_CORNER_LOW]) { + lpf_state = ADMV8818_STATE_MIN; + lpf_band = ADMV8818_BAND_MIN; goto lpf_write; } - for (i = 0; i < 4; i++) { - if (freq > freq_range_lpf[i][0] && freq < freq_range_lpf[i][1]) { - lpf_band = i + 1; - freq_step = div_u64((freq_range_lpf[i][1] - freq_range_lpf[i][0]), 15); + min_freq_error = U64_MAX; + for (band = ADMV8818_BAND_MAX; band >= ADMV8818_BAND_MIN; --band) { + /* + * At this point the highest corner frequency of + * all remaining ranges is below the target. + * LPF corner should be >= the target. + */ + if (freq > freq_range_lpf[band][ADMV8818_BAND_CORNER_HIGH]) + break; + + freq_step = freq_range_lpf[band][ADMV8818_BAND_CORNER_HIGH] - + freq_range_lpf[band][ADMV8818_BAND_CORNER_LOW]; + freq_step = div_u64(freq_step, ADMV8818_NUM_STATES - 1); + + for (state = ADMV8818_STATE_MAX; state >= ADMV8818_STATE_MIN; --state) { + + freq_corner = freq_range_lpf[band][ADMV8818_BAND_CORNER_LOW] + + state * freq_step; - for (j = 0; j <= 15; j++) { - if (freq < (freq_range_lpf[i][0] + (freq_step * j))) { - lpf_step = j; - break; - } + /* + * At this point all other states in range will + * place the corner frequency below the target + * LPF corner should >= the target. + */ + if (freq > freq_corner) + break; + + freq_error = freq_corner - freq; + if (freq_error < min_freq_error) { + min_freq_error = freq_error; + lpf_state = state; + lpf_band = band; } - break; } } @@ -225,7 +276,7 @@ lpf_write: return regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, ADMV8818_LPF_WR0_MSK, - FIELD_PREP(ADMV8818_LPF_WR0_MSK, lpf_step)); + FIELD_PREP(ADMV8818_LPF_WR0_MSK, lpf_state)); } static int admv8818_lpf_select(struct admv8818_state *st, u64 freq) @@ -242,16 +293,28 @@ static int admv8818_lpf_select(struct admv8818_state *st, u64 freq) static int admv8818_rfin_band_select(struct admv8818_state *st) { int ret; + u64 hpf_corner_target, lpf_corner_target; st->cf_hz = clk_get_rate(st->clkin); + /* Check for underflow */ + if (st->cf_hz > st->hpf_margin_hz) + hpf_corner_target = st->cf_hz - st->hpf_margin_hz; + else + hpf_corner_target = 0; + + /* Check for overflow */ + lpf_corner_target = st->cf_hz + st->lpf_margin_hz; + if (lpf_corner_target < st->cf_hz) + lpf_corner_target = U64_MAX; + mutex_lock(&st->lock); - ret = __admv8818_hpf_select(st, st->cf_hz); + ret = __admv8818_hpf_select(st, hpf_corner_target); if (ret) goto exit; - ret = __admv8818_lpf_select(st, st->cf_hz); + ret = __admv8818_lpf_select(st, lpf_corner_target); exit: mutex_unlock(&st->lock); return ret; @@ -278,8 +341,11 @@ static int __admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) hpf_state = FIELD_GET(ADMV8818_HPF_WR0_MSK, data); - *hpf_freq = div_u64(freq_range_hpf[hpf_band - 1][1] - freq_range_hpf[hpf_band - 1][0], 15); - *hpf_freq = freq_range_hpf[hpf_band - 1][0] + (*hpf_freq * hpf_state); + *hpf_freq = freq_range_hpf[hpf_band][ADMV8818_BAND_CORNER_HIGH] - + freq_range_hpf[hpf_band][ADMV8818_BAND_CORNER_LOW]; + *hpf_freq = div_u64(*hpf_freq, ADMV8818_NUM_STATES - 1); + *hpf_freq = freq_range_hpf[hpf_band][ADMV8818_BAND_CORNER_LOW] + + (*hpf_freq * hpf_state); return ret; } @@ -316,8 +382,11 @@ static int __admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) lpf_state = FIELD_GET(ADMV8818_LPF_WR0_MSK, data); - *lpf_freq = div_u64(freq_range_lpf[lpf_band - 1][1] - freq_range_lpf[lpf_band - 1][0], 15); - *lpf_freq = freq_range_lpf[lpf_band - 1][0] + (*lpf_freq * lpf_state); + *lpf_freq = freq_range_lpf[lpf_band][ADMV8818_BAND_CORNER_HIGH] - + freq_range_lpf[lpf_band][ADMV8818_BAND_CORNER_LOW]; + *lpf_freq = div_u64(*lpf_freq, ADMV8818_NUM_STATES - 1); + *lpf_freq = freq_range_lpf[lpf_band][ADMV8818_BAND_CORNER_LOW] + + (*lpf_freq * lpf_state); return ret; } @@ -333,6 +402,19 @@ static int admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) return ret; } +static int admv8818_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + return IIO_VAL_INT_64; + default: + return -EINVAL; + } +} + static int admv8818_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) @@ -341,6 +423,9 @@ static int admv8818_write_raw(struct iio_dev *indio_dev, u64 freq = ((u64)val2 << 32 | (u32)val); + if ((s64)freq < 0) + return -EINVAL; + switch (info) { case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: return admv8818_lpf_select(st, freq); @@ -502,6 +587,7 @@ set_mode: static const struct iio_info admv8818_info = { .write_raw = admv8818_write_raw, + .write_raw_get_fmt = admv8818_write_raw_get_fmt, .read_raw = admv8818_read_raw, .debugfs_reg_access = &admv8818_reg_access, }; @@ -516,7 +602,7 @@ static const struct iio_enum admv8818_mode_enum = { static const struct iio_chan_spec_ext_info admv8818_ext_info[] = { IIO_ENUM("filter_mode", IIO_SHARED_BY_ALL, &admv8818_mode_enum), IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_ALL, &admv8818_mode_enum), - { }, + { } }; #define ADMV8818_CHAN(_channel) { \ @@ -574,21 +660,15 @@ static int admv8818_init(struct admv8818_state *st) struct spi_device *spi = st->spi; unsigned int chip_id; - ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_A, - ADMV8818_SOFTRESET_N_MSK | - ADMV8818_SOFTRESET_MSK, - FIELD_PREP(ADMV8818_SOFTRESET_N_MSK, 1) | - FIELD_PREP(ADMV8818_SOFTRESET_MSK, 1)); + ret = regmap_write(st->regmap, ADMV8818_REG_SPI_CONFIG_A, + ADMV8818_SOFTRESET_N_MSK | ADMV8818_SOFTRESET_MSK); if (ret) { dev_err(&spi->dev, "ADMV8818 Soft Reset failed.\n"); return ret; } - ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_A, - ADMV8818_SDOACTIVE_N_MSK | - ADMV8818_SDOACTIVE_MSK, - FIELD_PREP(ADMV8818_SDOACTIVE_N_MSK, 1) | - FIELD_PREP(ADMV8818_SDOACTIVE_MSK, 1)); + ret = regmap_write(st->regmap, ADMV8818_REG_SPI_CONFIG_A, + ADMV8818_SDOACTIVE_N_MSK | ADMV8818_SDOACTIVE_MSK); if (ret) { dev_err(&spi->dev, "ADMV8818 SDO Enable failed.\n"); return ret; @@ -647,6 +727,32 @@ static int admv8818_clk_setup(struct admv8818_state *st) return devm_add_action_or_reset(&spi->dev, admv8818_clk_notifier_unreg, st); } +static int admv8818_read_properties(struct admv8818_state *st) +{ + struct spi_device *spi = st->spi; + u32 mhz; + int ret; + + ret = device_property_read_u32(&spi->dev, "adi,lpf-margin-mhz", &mhz); + if (ret == 0) + st->lpf_margin_hz = (u64)mhz * HZ_PER_MHZ; + else if (ret == -EINVAL) + st->lpf_margin_hz = 0; + else + return ret; + + + ret = device_property_read_u32(&spi->dev, "adi,hpf-margin-mhz", &mhz); + if (ret == 0) + st->hpf_margin_hz = (u64)mhz * HZ_PER_MHZ; + else if (ret == -EINVAL) + st->hpf_margin_hz = 0; + else if (ret < 0) + return ret; + + return 0; +} + static int admv8818_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -678,6 +784,10 @@ static int admv8818_probe(struct spi_device *spi) mutex_init(&st->lock); + ret = admv8818_read_properties(st); + if (ret) + return ret; + ret = admv8818_init(st); if (ret) return ret; @@ -687,13 +797,13 @@ static int admv8818_probe(struct spi_device *spi) static const struct spi_device_id admv8818_id[] = { { "admv8818", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, admv8818_id); static const struct of_device_id admv8818_of_match[] = { { .compatible = "adi,admv8818" }, - {} + { } }; MODULE_DEVICE_TABLE(of, admv8818_of_match); diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index b1554ced7a26..63c485e9e44c 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -1032,7 +1032,7 @@ static int ad9523_probe(struct spi_device *spi) static const struct spi_device_id ad9523_id[] = { {"ad9523-1", 9523}, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad9523_id); diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 61828e61e275..47f1c7e9efa9 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -373,7 +373,7 @@ static const struct iio_chan_spec_ext_info adf4350_ext_info[] = { _ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION), _ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN), _ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN), - { }, + { } }; static const struct iio_chan_spec adf4350_chan = { @@ -682,14 +682,14 @@ static int adf4350_probe(struct spi_device *spi) static const struct of_device_id adf4350_of_match[] = { { .compatible = "adi,adf4350", }, { .compatible = "adi,adf4351", }, - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(of, adf4350_of_match); static const struct spi_device_id adf4350_id[] = { {"adf4350", 4350}, {"adf4351", 4351}, - {} + { } }; MODULE_DEVICE_TABLE(spi, adf4350_id); diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index d752507e0c98..d6dc7827fb41 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); }; @@ -427,7 +438,7 @@ static const struct iio_chan_spec_ext_info adf4371_ext_info[] = { _ADF4371_EXT_INFO("frequency", ADF4371_FREQ), _ADF4371_EXT_INFO("powerdown", ADF4371_POWER_DOWN), _ADF4371_EXT_INFO("name", ADF4371_CHANNEL_NAME), - { }, + { } }; #define ADF4371_CHANNEL(index) { \ @@ -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); @@ -591,14 +626,14 @@ static int adf4371_probe(struct spi_device *spi) static const struct spi_device_id adf4371_id_table[] = { { "adf4371", (kernel_ulong_t)&adf4371_chip_info }, { "adf4372", (kernel_ulong_t)&adf4372_chip_info }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adf4371_id_table); static const struct of_device_id adf4371_of_match[] = { { .compatible = "adi,adf4371", .data = &adf4371_chip_info }, { .compatible = "adi,adf4372", .data = &adf4372_chip_info}, - { }, + { } }; MODULE_DEVICE_TABLE(of, adf4371_of_match); diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c index 45ceeb828d6b..08833b7035e4 100644 --- a/drivers/iio/frequency/adf4377.c +++ b/drivers/iio/frequency/adf4377.c @@ -985,14 +985,14 @@ static int adf4377_probe(struct spi_device *spi) static const struct spi_device_id adf4377_id[] = { { "adf4377", (kernel_ulong_t)&adf4377_chip_info }, { "adf4378", (kernel_ulong_t)&adf4378_chip_info }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adf4377_id); static const struct of_device_id adf4377_of_match[] = { { .compatible = "adi,adf4377", .data = &adf4377_chip_info }, { .compatible = "adi,adf4378", .data = &adf4378_chip_info }, - {} + { } }; MODULE_DEVICE_TABLE(of, adf4377_of_match); diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index 8ef583680ad0..d8e8d541990f 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -319,7 +319,7 @@ static ssize_t admv1013_write(struct iio_dev *indio_dev, return -EINVAL; } - return ret ? ret : len; + return len; } static int admv1013_update_quad_filters(struct admv1013_state *st) @@ -407,7 +407,7 @@ static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, static const struct iio_chan_spec_ext_info admv1013_ext_info[] = { _ADMV1013_EXT_INFO("i_calibphase", IIO_SEPARATE, ADMV1013_RFMOD_I_CALIBPHASE), _ADMV1013_EXT_INFO("q_calibphase", IIO_SEPARATE, ADMV1013_RFMOD_Q_CALIBPHASE), - { }, + { } }; #define ADMV1013_CHAN_PHASE(_channel, _channel2, _admv1013_ext_info) { \ @@ -615,13 +615,13 @@ static int admv1013_probe(struct spi_device *spi) static const struct spi_device_id admv1013_id[] = { { "admv1013", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, admv1013_id); static const struct of_device_id admv1013_of_match[] = { { .compatible = "adi,admv1013" }, - {}, + { } }; MODULE_DEVICE_TABLE(of, admv1013_of_match); diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c index 986b87a72577..7a8f92ec80a2 100644 --- a/drivers/iio/frequency/admv1014.c +++ b/drivers/iio/frequency/admv1014.c @@ -792,13 +792,13 @@ static int admv1014_probe(struct spi_device *spi) static const struct spi_device_id admv1014_id[] = { { "admv1014", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, admv1014_id); static const struct of_device_id admv1014_of_match[] = { { .compatible = "adi,admv1014" }, - {} + { } }; MODULE_DEVICE_TABLE(of, admv1014_of_match); diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index 57ee908fc747..a7a21f929970 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -487,13 +487,13 @@ static int adrf6780_probe(struct spi_device *spi) static const struct spi_device_id adrf6780_id[] = { { "adrf6780", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adrf6780_id); static const struct of_device_id adrf6780_of_match[] = { { .compatible = "adi,adrf6780" }, - {} + { } }; MODULE_DEVICE_TABLE(of, adrf6780_of_match); diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index 14b3abf6dce9..178bba95a709 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -214,7 +214,7 @@ static int adis16080_probe(struct spi_device *spi) static const struct spi_device_id adis16080_ids[] = { { "adis16080", ID_ADIS16080 }, { "adis16100", ID_ADIS16100 }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, adis16080_ids); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index c151fbb59ffe..586e6cfa14a9 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -414,7 +414,7 @@ static const struct spi_device_id adis16260_id[] = { {"adis16250", ADIS16260}, {"adis16255", ADIS16260}, {"adis16251", ADIS16251}, - {} + { } }; MODULE_DEVICE_TABLE(spi, adis16260_id); diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index 600e9725da78..8fcb41f45baa 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -75,7 +75,7 @@ struct adxrs290_state { /* Ensure correct alignment of timestamp when present */ struct { s16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; }; @@ -290,9 +290,8 @@ static int adxrs290_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; switch (chan->type) { case IIO_ANGL_VEL: @@ -316,7 +315,7 @@ static int adxrs290_read_raw(struct iio_dev *indio_dev, break; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: switch (chan->type) { @@ -366,9 +365,8 @@ static int adxrs290_write_raw(struct iio_dev *indio_dev, struct adxrs290_state *st = iio_priv(indio_dev); int ret, lpf_idx, hpf_idx; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; switch (mask) { case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: @@ -408,7 +406,7 @@ static int adxrs290_write_raw(struct iio_dev *indio_dev, break; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c index f84438e0c42c..a1d8d3cb301b 100644 --- a/drivers/iio/gyro/adxrs450.c +++ b/drivers/iio/gyro/adxrs450.c @@ -95,12 +95,10 @@ static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev, struct spi_transfer xfers[] = { { .tx_buf = &st->tx, - .bits_per_word = 8, .len = sizeof(st->tx), .cs_change = 1, }, { .rx_buf = &st->rx, - .bits_per_word = 8, .len = sizeof(st->rx), }, }; @@ -169,12 +167,10 @@ static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val) struct spi_transfer xfers[] = { { .tx_buf = &st->tx, - .bits_per_word = 8, .len = sizeof(st->tx), .cs_change = 1, }, { .rx_buf = &st->rx, - .bits_per_word = 8, .len = sizeof(st->rx), }, }; @@ -209,7 +205,6 @@ static int adxrs450_spi_initial(struct adxrs450_state *st, struct spi_transfer xfers = { .tx_buf = &st->tx, .rx_buf = &st->rx, - .bits_per_word = 8, .len = sizeof(st->tx), }; @@ -446,7 +441,7 @@ static int adxrs450_probe(struct spi_device *spi) static const struct spi_device_id adxrs450_id[] = { {"adxrs450", ID_ADXRS450}, {"adxrs453", ID_ADXRS453}, - {} + { } }; MODULE_DEVICE_TABLE(spi, adxrs450_id); diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index ba877d067afb..deb3c6459dde 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -99,7 +99,7 @@ struct bmg160_data { /* Ensure naturally aligned timestamp */ struct { s16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u32 dps_range; int ev_enable_state; diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c index 9c5d7e8ee99c..1fb8a7969c25 100644 --- a/drivers/iio/gyro/bmg160_i2c.c +++ b/drivers/iio/gyro/bmg160_i2c.c @@ -41,7 +41,7 @@ static void bmg160_i2c_remove(struct i2c_client *client) static const struct acpi_device_id bmg160_acpi_match[] = { {"BMG0160", 0}, - {}, + { } }; MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match); @@ -50,7 +50,7 @@ static const struct i2c_device_id bmg160_i2c_id[] = { { "bmg160" }, { "bmi055_gyro" }, { "bmi088_gyro" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id); @@ -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..6aecc5eb8347 100644 --- a/drivers/iio/gyro/bmg160_spi.c +++ b/drivers/iio/gyro/bmg160_spi.c @@ -36,14 +36,24 @@ static const struct spi_device_id bmg160_spi_id[] = { {"bmg160", 0}, {"bmi055_gyro", 0}, {"bmi088_gyro", 0}, - {} + { } }; 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/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 54b6f6fbdcaa..c43990c518f7 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -375,7 +375,7 @@ static const struct platform_device_id hid_gyro_3d_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200076", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_gyro_3d_ids); diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index 4cfa0d439560..a624400a239c 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -52,7 +52,7 @@ static irqreturn_t itg3200_trigger_handler(int irq, void *p) */ struct { __be16 buf[ITG3200_SCAN_ELEMENTS]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int ret = itg3200_read_all_channels(st->i2c, scan.buf); diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index b6883e8b2a8b..16553948c5c3 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -474,7 +474,7 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) int ret; struct { __be16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; s64 timestamp; unsigned int datums_from_fifo = 0; @@ -684,7 +684,7 @@ mpu3050_get_mount_matrix(const struct iio_dev *indio_dev, static const struct iio_chan_spec_ext_info mpu3050_ext_info[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mpu3050_get_mount_matrix), - { }, + { } }; #define MPU3050_AXIS_CHANNEL(axis, index) \ diff --git a/drivers/iio/gyro/mpu3050-i2c.c b/drivers/iio/gyro/mpu3050-i2c.c index 29ecfa6fd633..8e284f47242c 100644 --- a/drivers/iio/gyro/mpu3050-i2c.c +++ b/drivers/iio/gyro/mpu3050-i2c.c @@ -95,7 +95,7 @@ static void mpu3050_i2c_remove(struct i2c_client *client) */ static const struct i2c_device_id mpu3050_i2c_id[] = { { "mpu3050" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id); @@ -103,7 +103,7 @@ static const struct of_device_id mpu3050_i2c_of_match[] = { { .compatible = "invensense,mpu3050", .data = "mpu3050" }, /* Deprecated vendor ID from the Input driver */ { .compatible = "invn,mpu3050", .data = "mpu3050" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, mpu3050_i2c_of_match); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index d4b11bdba666..aef5ec8f9dee 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -54,7 +54,7 @@ static const struct of_device_id st_gyro_of_match[] = { .compatible = "st,lsm9ds0-gyro", .data = LSM9DS0_GYRO_DEV_NAME, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_gyro_of_match); @@ -102,7 +102,7 @@ static const struct i2c_device_id st_gyro_id_table[] = { { L3G4IS_GYRO_DEV_NAME }, { LSM330_GYRO_DEV_NAME }, { LSM9DS0_GYRO_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, st_gyro_id_table); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index 811f712711f5..f645da157372 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -59,7 +59,7 @@ static const struct of_device_id st_gyro_of_match[] = { .compatible = "st,lsm9ds0-gyro", .data = LSM9DS0_GYRO_DEV_NAME, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_gyro_of_match); @@ -107,7 +107,7 @@ static const struct spi_device_id st_gyro_id_table[] = { { L3G4IS_GYRO_DEV_NAME }, { LSM330_GYRO_DEV_NAME }, { LSM9DS0_GYRO_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, st_gyro_id_table); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 13e1dd4dd62c..1582cfc03579 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -411,7 +411,7 @@ static const struct regmap_config afe4403_regmap_config = { static const struct of_device_id afe4403_of_match[] = { { .compatible = "ti,afe4403", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, afe4403_of_match); @@ -574,7 +574,7 @@ static int afe4403_probe(struct spi_device *spi) static const struct spi_device_id afe4403_ids[] = { { "afe4403", 0 }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(spi, afe4403_ids); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index d49e1572a439..99ff68aed27c 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -419,7 +419,7 @@ static const struct regmap_config afe4404_regmap_config = { static const struct of_device_id afe4404_of_match[] = { { .compatible = "ti,afe4404", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, afe4404_of_match); @@ -581,7 +581,7 @@ static int afe4404_probe(struct i2c_client *client) static const struct i2c_device_id afe4404_ids[] = { { "afe4404" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, afe4404_ids); diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index e08d143a707c..846664a4ee90 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -483,7 +483,7 @@ static void max30100_remove(struct i2c_client *client) static const struct i2c_device_id max30100_id[] = { { "max30100" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, max30100_id); diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 1d074eb6a8c5..f5f29d2fec57 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -484,11 +484,11 @@ any_mode_retry: * things cannot concurrently change. And we just keep * trying until we get one of the modes... */ - if (iio_device_claim_direct_mode(indio_dev)) + if (!iio_device_claim_direct(indio_dev)) goto any_mode_retry; ret = max30102_get_temp(data, val, true); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); } else { ret = max30102_get_temp(data, val, false); iio_device_release_buffer_mode(indio_dev); @@ -615,7 +615,7 @@ static const struct i2c_device_id max30102_id[] = { { "max30101", max30105 }, { "max30102", max30102 }, { "max30105", max30105 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, max30102_id); diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 6b0aa3a3f025..f021c3e6d886 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -35,7 +35,7 @@ struct am2315_data { /* Ensure timestamp is naturally aligned */ struct { s16 chans[2]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -253,7 +253,7 @@ static int am2315_probe(struct i2c_client *client) static const struct i2c_device_id am2315_i2c_id[] = { { "am2315" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); 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/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index 9b355380c9bf..c2b36e682e06 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -13,6 +13,7 @@ * https://www.ti.com/product/HDC1080/datasheet */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -44,7 +45,7 @@ struct hdc100x_data { /* Ensure natural alignment of timestamp */ struct { __be16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -206,26 +207,20 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: { int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); if (chan->type == IIO_CURRENT) { *val = hdc100x_get_heater_status(data); - ret = IIO_VAL_INT; - } else { - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) { - mutex_unlock(&data->lock); - return ret; - } - - ret = hdc100x_get_measurement(data, chan); - iio_device_release_direct_mode(indio_dev); - if (ret >= 0) { - *val = ret; - ret = IIO_VAL_INT; - } + return IIO_VAL_INT; } - mutex_unlock(&data->lock); - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = hdc100x_get_measurement(data, chan); + iio_device_release_direct(indio_dev); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; } case IIO_CHAN_INFO_INT_TIME: *val = 0; @@ -256,26 +251,23 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct hdc100x_data *data = iio_priv(indio_dev); - int ret = -EINVAL; switch (mask) { - case IIO_CHAN_INFO_INT_TIME: + case IIO_CHAN_INFO_INT_TIME: { if (val != 0) return -EINVAL; - mutex_lock(&data->lock); - ret = hdc100x_set_it_time(data, chan->address, val2); - mutex_unlock(&data->lock); - return ret; - case IIO_CHAN_INFO_RAW: + guard(mutex)(&data->lock); + return hdc100x_set_it_time(data, chan->address, val2); + } + case IIO_CHAN_INFO_RAW: { if (chan->type != IIO_CURRENT || val2 != 0) return -EINVAL; - mutex_lock(&data->lock); - ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, - val ? HDC100X_REG_CONFIG_HEATER_EN : 0); - mutex_unlock(&data->lock); - return ret; + guard(mutex)(&data->lock); + return hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, + val ? HDC100X_REG_CONFIG_HEATER_EN : 0); + } default: return -EINVAL; } @@ -284,27 +276,19 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev, static int hdc100x_buffer_postenable(struct iio_dev *indio_dev) { struct hdc100x_data *data = iio_priv(indio_dev); - int ret; /* Buffer is enabled. First set ACQ Mode, then attach poll func */ - mutex_lock(&data->lock); - ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, - HDC100X_REG_CONFIG_ACQ_MODE); - mutex_unlock(&data->lock); - - return ret; + guard(mutex)(&data->lock); + return hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, + HDC100X_REG_CONFIG_ACQ_MODE); } static int hdc100x_buffer_predisable(struct iio_dev *indio_dev) { struct hdc100x_data *data = iio_priv(indio_dev); - int ret; - mutex_lock(&data->lock); - ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0); - mutex_unlock(&data->lock); - - return ret; + guard(mutex)(&data->lock); + return hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0); } static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = { diff --git a/drivers/iio/humidity/hdc2010.c b/drivers/iio/humidity/hdc2010.c index f5867659e00f..894a8b4ab193 100644 --- a/drivers/iio/humidity/hdc2010.c +++ b/drivers/iio/humidity/hdc2010.c @@ -169,13 +169,12 @@ static int hdc2010_read_raw(struct iio_dev *indio_dev, *val = hdc2010_get_heater_status(data); return IIO_VAL_INT; } - 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 = hdc2010_get_prim_measurement_word(data, chan); mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = ret; @@ -184,13 +183,12 @@ static int hdc2010_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PEAK: { 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(&data->lock); ret = hdc2010_get_peak_measurement_byte(data, chan); mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; /* Scaling up the value so we can use same offset as RAW */ diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index a40e1eb6e98c..be2338d5f407 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -276,7 +276,7 @@ static const struct platform_device_id hid_humidity_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200032", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_humidity_ids); diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index 721359e226cb..0215f11fc35e 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -40,7 +40,7 @@ struct hts221_hw { /* Ensure natural alignment of timestamp */ struct { __le16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index 0be11470730c..bfeb0a60d3af 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -418,31 +418,22 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) return IIO_VAL_INT; } -static int hts221_read_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *ch, - int *val, int *val2, long mask) +static int __hts221_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) { struct hts221_hw *hw = iio_priv(iio_dev); - int ret; - - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - return ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = hts221_read_oneshot(hw, ch->address, val); - break; + return hts221_read_oneshot(hw, ch->address, val); case IIO_CHAN_INFO_SCALE: - ret = hts221_get_sensor_scale(hw, ch->type, val, val2); - break; + return hts221_get_sensor_scale(hw, ch->type, val, val2); case IIO_CHAN_INFO_OFFSET: - ret = hts221_get_sensor_offset(hw, ch->type, val, val2); - break; + return hts221_get_sensor_offset(hw, ch->type, val, val2); case IIO_CHAN_INFO_SAMP_FREQ: *val = hw->odr; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { u8 idx; const struct hts221_avg *avg; @@ -452,64 +443,72 @@ static int hts221_read_raw(struct iio_dev *iio_dev, avg = &hts221_avg_list[HTS221_SENSOR_H]; idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx; *val = avg->avg_avl[idx]; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_TEMP: avg = &hts221_avg_list[HTS221_SENSOR_T]; idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx; *val = avg->avg_avl[idx]; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; default: - ret = -EINVAL; - break; + return -EINVAL; } - break; } default: - ret = -EINVAL; - break; + return -EINVAL; } +} + +static int hts221_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; - iio_device_release_direct_mode(iio_dev); + ret = __hts221_read_raw(iio_dev, ch, val, val2, mask); + + iio_device_release_direct(iio_dev); return ret; } -static int hts221_write_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __hts221_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, long mask) { struct hts221_hw *hw = iio_priv(iio_dev); - int ret; - - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - return ret; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - ret = hts221_update_odr(hw, val); - break; + return hts221_update_odr(hw, val); case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->type) { case IIO_HUMIDITYRELATIVE: - ret = hts221_update_avg(hw, HTS221_SENSOR_H, val); - break; + return hts221_update_avg(hw, HTS221_SENSOR_H, val); case IIO_TEMP: - ret = hts221_update_avg(hw, HTS221_SENSOR_T, val); - break; + return hts221_update_avg(hw, HTS221_SENSOR_T, val); default: - ret = -EINVAL; - break; + return -EINVAL; } - break; default: - ret = -EINVAL; - break; + return -EINVAL; } +} + +static int hts221_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; + + ret = __hts221_write_raw(iio_dev, chan, val, mask); - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); return ret; } diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c index 87a8e3c8d277..cbaa7d1af6c4 100644 --- a/drivers/iio/humidity/hts221_i2c.c +++ b/drivers/iio/humidity/hts221_i2c.c @@ -42,19 +42,19 @@ static int hts221_i2c_probe(struct i2c_client *client) static const struct acpi_device_id hts221_acpi_match[] = { {"SMO9100", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, hts221_acpi_match); static const struct of_device_id hts221_i2c_of_match[] = { { .compatible = "st,hts221", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, hts221_i2c_of_match); static const struct i2c_device_id hts221_i2c_id_table[] = { { HTS221_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table); diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c index 00154b9d66b5..e6fef2acd523 100644 --- a/drivers/iio/humidity/hts221_spi.c +++ b/drivers/iio/humidity/hts221_spi.c @@ -42,13 +42,13 @@ static int hts221_spi_probe(struct spi_device *spi) static const struct of_device_id hts221_spi_of_match[] = { { .compatible = "st,hts221", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, hts221_spi_of_match); static const struct spi_device_id hts221_spi_id_table[] = { { HTS221_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, hts221_spi_id_table); diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index 6402e393edb8..7f1775bd26fd 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -232,14 +232,14 @@ static int htu21_probe(struct i2c_client *client) static const struct i2c_device_id htu21_id[] = { {"htu21", HTU21}, {"ms8607-humidity", MS8607}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, htu21_id); static const struct of_device_id htu21_of_match[] = { { .compatible = "meas,htu21", }, { .compatible = "meas,ms8607-humidity", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, htu21_of_match); 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..d160147cce0b 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -39,34 +39,29 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value, struct spi_transfer xfers[] = { { .tx_buf = adis->tx, - .bits_per_word = 8, .len = 2, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 2, - .bits_per_word = 8, .len = 2, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 4, - .bits_per_word = 8, .len = 2, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 6, - .bits_per_word = 8, .len = 2, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 8, - .bits_per_word = 8, .len = 2, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, @@ -133,14 +128,12 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, struct spi_transfer xfers[] = { { .tx_buf = adis->tx, - .bits_per_word = 8, .len = 2, .cs_change = 1, .delay.value = adis->data->write_delay, .delay.unit = SPI_DELAY_UNIT_USECS, }, { .tx_buf = adis->tx + 2, - .bits_per_word = 8, .len = 2, .cs_change = 1, .delay.value = adis->data->read_delay, @@ -148,14 +141,12 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val, }, { .tx_buf = adis->tx + 4, .rx_buf = adis->rx, - .bits_per_word = 8, .len = 2, .cs_change = 1, .delay.value = adis->data->read_delay, .delay.unit = SPI_DELAY_UNIT_USECS, }, { .rx_buf = adis->rx + 2, - .bits_per_word = 8, .len = 2, .delay.value = adis->data->read_delay, .delay.unit = SPI_DELAY_UNIT_USECS, @@ -223,13 +214,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 +295,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 +317,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 +471,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 +491,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 +526,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/adis16400.c b/drivers/iio/imu/adis16400.c index 3086dd536203..90ed3f9bb39c 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -1212,7 +1212,7 @@ static const struct spi_device_id adis16400_id[] = { {"adis16405", ADIS16400}, {"adis16445", ADIS16445}, {"adis16448", ADIS16448}, - {} + { } }; MODULE_DEVICE_TABLE(spi, adis16400_id); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index ecf74046fde1..ba1887d36577 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -395,13 +395,13 @@ static int adis16460_probe(struct spi_device *spi) static const struct spi_device_id adis16460_ids[] = { { "adis16460", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, adis16460_ids); static const struct of_device_id adis16460_of_match[] = { { .compatible = "adi,adis16460" }, - {} + { } }; MODULE_DEVICE_TABLE(of, adis16460_of_match); diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index df8c6cd91169..924395b7e3b4 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -2058,7 +2058,7 @@ static const struct of_device_id adis16475_of_match[] = { .data = &adis16475_chip_info[ADIS16577_2] }, { .compatible = "adi,adis16577-3", .data = &adis16475_chip_info[ADIS16577_3] }, - { }, + { } }; MODULE_DEVICE_TABLE(of, adis16475_of_match); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 0a5d13d2240e..543d5c4bfb11 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -878,11 +878,32 @@ static const struct iio_chan_spec adis16545_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(17), }; +static const struct iio_chan_spec adis16489_channels[] = { + ADIS16480_GYRO_CHANNEL(X), + ADIS16480_GYRO_CHANNEL(Y), + ADIS16480_GYRO_CHANNEL(Z), + ADIS16480_ACCEL_CHANNEL(X), + ADIS16480_ACCEL_CHANNEL(Y), + ADIS16480_ACCEL_CHANNEL(Z), + ADIS16480_PRESSURE_CHANNEL(), + ADIS16480_TEMP_CHANNEL(), + IIO_CHAN_SOFT_TIMESTAMP(8), + ADIS16480_DELTANG_CHANNEL_NO_SCAN(X), + ADIS16480_DELTANG_CHANNEL_NO_SCAN(Y), + ADIS16480_DELTANG_CHANNEL_NO_SCAN(Z), + ADIS16480_DELTVEL_CHANNEL_NO_SCAN(X), + ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Y), + ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Z), +}; + enum adis16480_variant { ADIS16375, ADIS16480, ADIS16485, + ADIS16486, + ADIS16487, ADIS16488, + ADIS16489, ADIS16490, ADIS16495_1, ADIS16495_2, @@ -1038,6 +1059,38 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .filter_freqs = adis16480_def_filter_freqs, .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0, 0), }, + [ADIS16486] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), + .gyro_max_val = 22500 << 16, + .gyro_max_scale = IIO_DEGREE_TO_RAD(450), + .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), + .accel_max_scale = 18, + .temp_scale = 5650, /* 5.65 milli degree Celsius */ + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 200, + .int_clk = 2460000, + .max_dec_rate = 2048, + .has_sleep_cnt = true, + .filter_freqs = adis16480_def_filter_freqs, + .adis_data = ADIS16480_DATA(16486, &adis16480_timeouts, 0, 0), + }, + [ADIS16487] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), + .gyro_max_val = 22500 << 16, + .gyro_max_scale = IIO_DEGREE_TO_RAD(450), + .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), + .accel_max_scale = 5, + .temp_scale = 5650, /* 5.65 milli degree Celsius */ + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 50, + .int_clk = 2460000, + .max_dec_rate = 2048, + .has_sleep_cnt = true, + .filter_freqs = adis16480_def_filter_freqs, + .adis_data = ADIS16480_DATA(16487, &adis16485_timeouts, 0, 0), + }, [ADIS16488] = { .channels = adis16480_channels, .num_channels = ARRAY_SIZE(adis16480_channels), @@ -1054,6 +1107,22 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .filter_freqs = adis16480_def_filter_freqs, .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0, 0), }, + [ADIS16489] = { + .channels = adis16489_channels, + .num_channels = ARRAY_SIZE(adis16489_channels), + .gyro_max_val = 22500 << 16, + .gyro_max_scale = IIO_DEGREE_TO_RAD(450), + .accel_max_val = IIO_M_S_2_TO_G(20000 << 16), + .accel_max_scale = 18, + .temp_scale = 5650, /* 5.65 milli degree Celsius */ + .deltang_max_val = IIO_DEGREE_TO_RAD(720), + .deltvel_max_val = 200, + .int_clk = 2460000, + .max_dec_rate = 2048, + .has_sleep_cnt = true, + .filter_freqs = adis16480_def_filter_freqs, + .adis_data = ADIS16480_DATA(16489, &adis16480_timeouts, 0, 0), + }, [ADIS16490] = { .channels = adis16485_channels, .num_channels = ARRAY_SIZE(adis16485_channels), @@ -1741,7 +1810,10 @@ static const struct spi_device_id adis16480_ids[] = { { "adis16375", ADIS16375 }, { "adis16480", ADIS16480 }, { "adis16485", ADIS16485 }, + { "adis16486", ADIS16486 }, + { "adis16487", ADIS16487 }, { "adis16488", ADIS16488 }, + { "adis16489", ADIS16489 }, { "adis16490", ADIS16490 }, { "adis16495-1", ADIS16495_1 }, { "adis16495-2", ADIS16495_2 }, @@ -1763,7 +1835,10 @@ static const struct of_device_id adis16480_of_match[] = { { .compatible = "adi,adis16375" }, { .compatible = "adi,adis16480" }, { .compatible = "adi,adis16485" }, + { .compatible = "adi,adis16486" }, + { .compatible = "adi,adis16487" }, { .compatible = "adi,adis16488" }, + { .compatible = "adi,adis16489" }, { .compatible = "adi,adis16490" }, { .compatible = "adi,adis16495-1" }, { .compatible = "adi,adis16495-2" }, @@ -1777,7 +1852,7 @@ static const struct of_device_id adis16480_of_match[] = { { .compatible = "adi,adis16547-1" }, { .compatible = "adi,adis16547-2" }, { .compatible = "adi,adis16547-3" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, adis16480_of_match); diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c new file mode 100644 index 000000000000..28f0dbd0226c --- /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] __aligned(8); + 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/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index fdfc0538734c..cd3db2388164 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -49,12 +49,10 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, tx[1] = 0; adis->xfer[0].tx_buf = tx; - adis->xfer[0].bits_per_word = 8; adis->xfer[0].len = 2; if (adis->data->burst_max_speed_hz) adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz; adis->xfer[1].rx_buf = adis->buffer; - adis->xfer[1].bits_per_word = 8; adis->xfer[1].len = burst_length; if (adis->data->burst_max_speed_hz) adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz; @@ -100,7 +98,6 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, spi_message_init(&adis->msg); for (j = 0; j <= scan_count; j++) { - adis->xfer[j].bits_per_word = 8; if (j != scan_count) adis->xfer[j].cs_change = 1; adis->xfer[j].len = 2; diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c index 214503fa4af5..9fa3a19a8977 100644 --- a/drivers/iio/imu/bmi160/bmi160_i2c.c +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c @@ -39,7 +39,7 @@ static int bmi160_i2c_probe(struct i2c_client *client) static const struct i2c_device_id bmi160_i2c_id[] = { { "bmi120" }, { "bmi160" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id); @@ -55,14 +55,14 @@ static const struct acpi_device_id bmi160_acpi_match[] = { {"10EC5280", 0}, {"BMI0120", 0}, {"BMI0160", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match); static const struct of_device_id bmi160_of_match[] = { { .compatible = "bosch,bmi120" }, { .compatible = "bosch,bmi160" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bmi160_of_match); diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c index 8fbaab22db81..ebb586904215 100644 --- a/drivers/iio/imu/bmi160/bmi160_spi.c +++ b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -36,21 +36,21 @@ static int bmi160_spi_probe(struct spi_device *spi) static const struct spi_device_id bmi160_spi_id[] = { {"bmi120", 0}, {"bmi160", 0}, - {} + { } }; MODULE_DEVICE_TABLE(spi, bmi160_spi_id); static const struct acpi_device_id bmi160_acpi_match[] = { {"BMI0120", 0}, {"BMI0160", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match); static const struct of_device_id bmi160_of_match[] = { { .compatible = "bosch,bmi120" }, { .compatible = "bosch,bmi160" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bmi160_of_match); 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..2e4469f30d53 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"); @@ -654,8 +918,7 @@ static int bmi270_configure_imu(struct bmi270_data *bmi270_device) FIELD_PREP(BMI270_ACC_CONF_ODR_MSK, BMI270_ACC_CONF_ODR_100HZ) | FIELD_PREP(BMI270_ACC_CONF_BWP_MSK, - BMI270_ACC_CONF_BWP_NORMAL_MODE) | - BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); + BMI270_ACC_CONF_BWP_NORMAL_MODE)); if (ret) return dev_err_probe(dev, ret, "Failed to configure accelerometer"); @@ -663,8 +926,7 @@ static int bmi270_configure_imu(struct bmi270_data *bmi270_device) FIELD_PREP(BMI270_GYR_CONF_ODR_MSK, BMI270_GYR_CONF_ODR_200HZ) | FIELD_PREP(BMI270_GYR_CONF_BWP_MSK, - BMI270_GYR_CONF_BWP_NORMAL_MODE) | - BMI270_PWR_CONF_ADV_PWR_SAVE_MSK); + BMI270_GYR_CONF_BWP_NORMAL_MODE)); if (ret) return dev_err_probe(dev, ret, "Failed to configure gyroscope"); @@ -677,38 +939,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 +983,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 f7d7f4442e65..fc54d464a3ae 100644 --- a/drivers/iio/imu/bmi323/bmi323_core.c +++ b/drivers/iio/imu/bmi323/bmi323_core.c @@ -174,7 +174,7 @@ struct bmi323_data { __le16 fifo_buff[BMI323_FIFO_FULL_IN_WORDS] __aligned(IIO_DMA_MINALIGN); struct { __le16 channels[BMI323_CHAN_MAX]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; __le16 steps_count[BMI323_STEP_LEN]; }; @@ -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/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index 0728d38260a1..597c402b98de 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -207,7 +207,7 @@ struct bno055_priv { bool sw_reset; struct { __le16 chans[BNO055_SCAN_CH_COUNT]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } buf; struct dentry *debugfs; }; @@ -1193,7 +1193,7 @@ static ssize_t serialnumber_show(struct device *dev, } static ssize_t calibration_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, + const struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj))); @@ -1348,16 +1348,16 @@ static struct attribute *bno055_attrs[] = { NULL }; -static BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); +static const BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN); -static struct bin_attribute *bno055_bin_attrs[] = { +static const struct bin_attribute *const bno055_bin_attrs[] = { &bin_attr_calibration_data, NULL }; static const struct attribute_group bno055_attrs_group = { .attrs = bno055_attrs, - .bin_attrs = bno055_bin_attrs, + .bin_attrs_new = bno055_bin_attrs, }; static const struct iio_info bno055_info = { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index 18787a43477b..f893dbe69965 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -426,7 +426,7 @@ int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable, int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval); -int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, +int inv_icm42600_core_probe(struct regmap *regmap, int chip, inv_icm42600_bus_setup bus_setup); struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 7968aa27f9fd..e6cd9dcb0687 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -157,7 +157,7 @@ static const struct iio_chan_spec_ext_info inv_icm42600_accel_ext_infos[] = { &inv_icm42600_accel_power_mode_enum), IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE, &inv_icm42600_accel_power_mode_enum), - {}, + { } }; static const struct iio_chan_spec inv_icm42600_accel_channels[] = { @@ -178,7 +178,7 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = { struct inv_icm42600_accel_buffer { struct inv_icm42600_fifo_sensor_data accel; int16_t temp; - int64_t timestamp __aligned(8); + aligned_s64 timestamp; }; #define INV_ICM42600_SCAN_MASK_ACCEL_3AXIS \ @@ -685,11 +685,10 @@ static int inv_icm42600_accel_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 = inv_icm42600_accel_read_sensor(indio_dev, chan, &data); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; *val = data; @@ -747,20 +746,18 @@ static int inv_icm42600_accel_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SCALE: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = inv_icm42600_accel_write_scale(indio_dev, val, val2); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: return inv_icm42600_accel_write_odr(indio_dev, val, val2); case IIO_CHAN_INFO_CALIBBIAS: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = inv_icm42600_accel_write_offset(st, chan, val, val2); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; default: return -EINVAL; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index ef9875d3b79d..63d46619ebfa 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -683,12 +683,13 @@ static void inv_icm42600_disable_pm(void *_data) pm_runtime_disable(dev); } -int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, +int inv_icm42600_core_probe(struct regmap *regmap, int chip, inv_icm42600_bus_setup bus_setup) { struct device *dev = regmap_get_device(regmap); + struct fwnode_handle *fwnode = dev_fwnode(dev); struct inv_icm42600_state *st; - int irq_type; + int irq, irq_type; bool open_drain; int ret; @@ -697,6 +698,15 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, return -ENODEV; } + /* get INT1 only supported interrupt or fallback to first interrupt */ + irq = fwnode_irq_get_byname(fwnode, "INT1"); + if (irq < 0 && irq != -EPROBE_DEFER) { + dev_info(dev, "no INT1 interrupt defined, fallback to first interrupt\n"); + irq = fwnode_irq_get(fwnode, 0); + } + if (irq < 0) + return dev_err_probe(dev, irq, "error missing INT1 interrupt\n"); + irq_type = irq_get_trigger_type(irq); if (!irq_type) irq_type = IRQF_TRIGGER_FALLING; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index c6bb68bf5e14..b4d7ce1432a4 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -57,7 +57,7 @@ enum inv_icm42600_gyro_scan { static const struct iio_chan_spec_ext_info inv_icm42600_gyro_ext_infos[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42600_get_mount_matrix), - {}, + { } }; static const struct iio_chan_spec inv_icm42600_gyro_channels[] = { @@ -78,7 +78,7 @@ static const struct iio_chan_spec inv_icm42600_gyro_channels[] = { struct inv_icm42600_gyro_buffer { struct inv_icm42600_fifo_sensor_data gyro; int16_t temp; - int64_t timestamp __aligned(8); + aligned_s64 timestamp; }; #define INV_ICM42600_SCAN_MASK_GYRO_3AXIS \ @@ -591,11 +591,10 @@ static int inv_icm42600_gyro_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 = inv_icm42600_gyro_read_sensor(st, chan, &data); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; *val = data; @@ -653,20 +652,18 @@ static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SCALE: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = inv_icm42600_gyro_write_scale(indio_dev, val, val2); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: return inv_icm42600_gyro_write_odr(indio_dev, val, val2); case IIO_CHAN_INFO_CALIBBIAS: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = inv_icm42600_gyro_write_offset(st, chan, val, val2); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; default: return -EINVAL; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 04e440fe023a..7e4d3ea68721 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -67,8 +67,7 @@ static int inv_icm42600_probe(struct i2c_client *client) if (IS_ERR(regmap)) return PTR_ERR(regmap); - return inv_icm42600_core_probe(regmap, chip, client->irq, - inv_icm42600_i2c_bus_setup); + return inv_icm42600_core_probe(regmap, chip, inv_icm42600_i2c_bus_setup); } /* @@ -110,7 +109,7 @@ static const struct of_device_id inv_icm42600_of_matches[] = { .compatible = "invensense,icm42631", .data = (void *)INV_CHIP_ICM42631, }, - {} + { } }; MODULE_DEVICE_TABLE(of, inv_icm42600_of_matches); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 2bd2c4c8e50c..13e2e7d38638 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -64,8 +64,7 @@ static int inv_icm42600_probe(struct spi_device *spi) if (IS_ERR(regmap)) return PTR_ERR(regmap); - return inv_icm42600_core_probe(regmap, chip, spi->irq, - inv_icm42600_spi_bus_setup); + return inv_icm42600_core_probe(regmap, chip, inv_icm42600_spi_bus_setup); } /* @@ -107,7 +106,7 @@ static const struct of_device_id inv_icm42600_of_matches[] = { .compatible = "invensense,icm42631", .data = (void *)INV_CHIP_ICM42631, }, - {} + { } }; MODULE_DEVICE_TABLE(of, inv_icm42600_of_matches); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 213cce1c3111..988f227f6563 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -56,27 +56,28 @@ int inv_icm42600_temp_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 = inv_icm42600_temp_read(st, &temp); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; *val = temp; return IIO_VAL_INT; /* * T°C = (temp / 132.48) + 25 - * Tm°C = 1000 * ((temp * 100 / 13248) + 25) + * Tm°C = 1000 * ((temp / 132.48) + 25) + * Tm°C = 7.548309 * temp + 25000 + * Tm°C = (temp + 3312) * 7.548309 * scale: 100000 / 13248 ~= 7.548309 - * offset: 25000 + * offset: 3312 */ case IIO_CHAN_INFO_SCALE: *val = 7; *val2 = 548309; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OFFSET: - *val = 25000; + *val = 3312; return IIO_VAL_INT; default: return -EINVAL; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index 373e59f6d91a..a9bcf02e5b43 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -39,7 +39,7 @@ static const struct dmi_system_id inv_mpu_dev_list[] = { }, }, /* Add more matching tables here..*/ - {} + { } }; static int asus_acpi_get_sensor_info(struct acpi_device *adev, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 844b611b825a..b8656c02354a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -277,6 +277,14 @@ static const struct inv_mpu6050_hw hw_info[] = { .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, .startup_time = {INV_ICM20690_GYRO_STARTUP_TIME, INV_ICM20690_ACCEL_STARTUP_TIME}, }, + { .whoami = INV_IAM20380_WHOAMI_VALUE, + .name = "IAM20380", + .reg = ®_set_6500, + .config = &chip_config_6500, + .fifo_size = 512, + .temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE}, + .startup_time = {INV_MPU6500_GYRO_STARTUP_TIME, INV_MPU6500_ACCEL_STARTUP_TIME}, + }, { .whoami = INV_IAM20680_WHOAMI_VALUE, .name = "IAM20680", @@ -747,13 +755,12 @@ inv_mpu6050_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(&st->lock); ret = inv_mpu6050_read_channel_data(indio_dev, chan, val); mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: switch (chan->type) { @@ -887,9 +894,8 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, * we should only update scale when the chip is disabled, i.e. * not running */ - result = iio_device_claim_direct_mode(indio_dev); - if (result) - return result; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&st->lock); result = pm_runtime_resume_and_get(pdev); @@ -936,7 +942,7 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, pm_runtime_put_autosuspend(pdev); error_write_raw_unlock: mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return result; } @@ -1519,6 +1525,14 @@ static const struct iio_chan_spec inv_mpu6050_channels[] = { INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), }; +static const struct iio_chan_spec inv_iam20380_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), + + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), +}; + static const struct iio_chan_spec inv_mpu6500_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), @@ -1623,6 +1637,10 @@ static const struct iio_chan_spec inv_mpu9250_channels[] = { | BIT(INV_MPU9X50_SCAN_MAGN_Y) \ | BIT(INV_MPU9X50_SCAN_MAGN_Z)) +static const unsigned long inv_iam20380_scan_masks[] = { + INV_MPU6050_SCAN_MASK_3AXIS_GYRO, +}; + static const unsigned long inv_mpu9x50_scan_masks[] = { /* 3-axis accel */ INV_MPU6050_SCAN_MASK_3AXIS_ACCEL, @@ -2026,6 +2044,11 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, indio_dev->num_channels = ARRAY_SIZE(inv_mpu9250_channels); indio_dev->available_scan_masks = inv_mpu9x50_scan_masks; break; + case INV_IAM20380: + indio_dev->channels = inv_iam20380_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_iam20380_channels); + indio_dev->available_scan_masks = inv_iam20380_scan_masks; + break; case INV_ICM20600: case INV_ICM20602: indio_dev->channels = inv_mpu6500_channels; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 307a06f4df2e..8dc61812a8fc 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -34,6 +34,7 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev) case INV_ICM20689: case INV_ICM20600: case INV_ICM20602: + case INV_IAM20380: case INV_IAM20680: /* no i2c auxiliary bus on the chip */ return false; @@ -187,10 +188,11 @@ static const struct i2c_device_id inv_mpu_id[] = { {"icm20600", INV_ICM20600}, {"icm20602", INV_ICM20602}, {"icm20690", INV_ICM20690}, + {"iam20380", INV_IAM20380}, {"iam20680", INV_IAM20680}, {"iam20680hp", INV_IAM20680HP}, {"iam20680ht", INV_IAM20680HT}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, inv_mpu_id); @@ -253,6 +255,10 @@ static const struct of_device_id inv_of_match[] = { .data = (void *)INV_ICM20690 }, { + .compatible = "invensense,iam20380", + .data = (void *)INV_IAM20380 + }, + { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 }, @@ -270,7 +276,7 @@ MODULE_DEVICE_TABLE(of, inv_of_match); static const struct acpi_device_id inv_acpi_match[] = { {"INVN6500", INV_MPU6500}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, inv_acpi_match); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index a6862cf42639..211901f8b8eb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -84,6 +84,7 @@ enum inv_devices { INV_ICM20600, INV_ICM20602, INV_ICM20690, + INV_IAM20380, INV_IAM20680, INV_IAM20680HP, INV_IAM20680HT, @@ -425,6 +426,7 @@ struct inv_mpu6050_state { #define INV_ICM20600_WHOAMI_VALUE 0x11 #define INV_ICM20602_WHOAMI_VALUE 0x12 #define INV_ICM20690_WHOAMI_VALUE 0x20 +#define INV_IAM20380_WHOAMI_VALUE 0xB5 #define INV_IAM20680_WHOAMI_VALUE 0xA9 #define INV_IAM20680HP_WHOAMI_VALUE 0xF8 #define INV_IAM20680HT_WHOAMI_VALUE 0xFA diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 3d3b27f28c9d..273196e647a2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -50,7 +50,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) u16 fifo_count; u32 fifo_period; s64 timestamp; - u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; + u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(8); size_t i, nb; mutex_lock(&st->lock); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index ab415874d699..1f4c62142b60 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -79,10 +79,11 @@ static const struct spi_device_id inv_mpu_id[] = { {"icm20600", INV_ICM20600}, {"icm20602", INV_ICM20602}, {"icm20690", INV_ICM20690}, + {"iam20380", INV_IAM20380}, {"iam20680", INV_IAM20680}, {"iam20680hp", INV_IAM20680HP}, {"iam20680ht", INV_IAM20680HT}, - {} + { } }; MODULE_DEVICE_TABLE(spi, inv_mpu_id); @@ -141,6 +142,10 @@ static const struct of_device_id inv_of_match[] = { .data = (void *)INV_ICM20690 }, { + .compatible = "invensense,iam20380", + .data = (void *)INV_IAM20380 + }, + { .compatible = "invensense,iam20680", .data = (void *)INV_IAM20680 }, @@ -158,7 +163,7 @@ MODULE_DEVICE_TABLE(of, inv_of_match); static const struct acpi_device_id inv_acpi_match[] = { {"INVN6000", INV_MPU6000}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, inv_acpi_match); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index e19c5d3137c6..2bdfb2619137 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1487,7 +1487,7 @@ static const struct dev_pm_ops kmx61_pm_ops = { static const struct i2c_device_id kmx61_id[] = { { "kmx611021" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, kmx61_id); diff --git a/drivers/iio/imu/smi240.c b/drivers/iio/imu/smi240.c index 4492c4d013bd..d159ee59acdd 100644 --- a/drivers/iio/imu/smi240.c +++ b/drivers/iio/imu/smi240.c @@ -414,11 +414,10 @@ static int smi240_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 = smi240_get_data(data, chan->type, chan->channel2, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; return IIO_VAL_INT; diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 89d687ec3099..3cabec3b152d 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -6,9 +6,6 @@ config IIO_ST_LSM6DSX select IIO_BUFFER select IIO_TRIGGERED_BUFFER select IIO_KFIFO_BUF - select IIO_ST_LSM6DSX_I2C if (I2C) - select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) - select IIO_ST_LSM6DSX_I3C if (I3C) help Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. @@ -42,16 +39,19 @@ config IIO_ST_LSM6DSX will be called st_lsm6dsx. config IIO_ST_LSM6DSX_I2C - tristate - depends on IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors I2C Interface" + depends on I2C && IIO_ST_LSM6DSX + default I2C && IIO_ST_LSM6DSX select REGMAP_I2C config IIO_ST_LSM6DSX_SPI - tristate - depends on IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors SPI Interface" + depends on SPI_MASTER && IIO_ST_LSM6DSX + default SPI_MASTER && IIO_ST_LSM6DSX select REGMAP_SPI config IIO_ST_LSM6DSX_I3C - tristate - depends on IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors I3C Interface" + depends on I3C && IIO_ST_LSM6DSX + default I3C && IIO_ST_LSM6DSX select REGMAP_I3C diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 0a7cd8c1aa33..8a9d2593576a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -392,6 +392,9 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK)) return 0; + if (!pattern_len) + pattern_len = ST_LSM6DSX_SAMPLE_SIZE; + fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * ST_LSM6DSX_CHAN_SIZE; fifo_len = (fifo_len / pattern_len) * pattern_len; @@ -623,6 +626,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) if (!fifo_len) return 0; + if (!pattern_len) + pattern_len = ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 4fdcc2acc94e..c65ad49829e7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -1804,12 +1804,11 @@ static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - break; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val); - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); break; case IIO_CHAN_INFO_SAMP_FREQ: *val = sensor->odr / 1000; @@ -1834,11 +1833,10 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, int val, int val2, long mask) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); - int err; + int err = 0; - err = iio_device_claim_direct_mode(iio_dev); - if (err) - return err; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -1860,7 +1858,7 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, break; } - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); return err; } @@ -2719,8 +2717,11 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, } if (device_property_read_bool(dev, "wakeup-source") || - (pdata && pdata->wakeup_source)) - device_init_wakeup(dev, true); + (pdata && pdata->wakeup_source)) { + err = devm_device_init_wakeup(dev); + if (err) + return dev_err_probe(dev, err, "Failed to init wakeup\n"); + } return 0; } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 25e1de89b6e4..7c933218036b 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -138,13 +138,13 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,asm330lhhxg1", .data = (void *)ST_ASM330LHHXG1_ID, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); static const struct acpi_device_id st_lsm6dsx_i2c_acpi_match[] = { { "SMO8B30", ST_LSM6DS3TRC_ID, }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, st_lsm6dsx_i2c_acpi_match); @@ -173,7 +173,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c index 6952d901316f..cb5c5d7e1f3d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c @@ -9,7 +9,6 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/i3c/device.h> -#include <linux/i3c/master.h> #include <linux/slab.h> #include <linux/regmap.h> @@ -18,7 +17,7 @@ static const struct i3c_device_id st_lsm6dsx_i3c_ids[] = { I3C_DEVICE(0x0104, 0x006C, (void *)ST_LSM6DSO_ID), I3C_DEVICE(0x0104, 0x006B, (void *)ST_LSM6DSR_ID), - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(i3c, st_lsm6dsx_i3c_ids); @@ -30,15 +29,16 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev) }; const struct i3c_device_id *id = i3c_device_match_id(i3cdev, st_lsm6dsx_i3c_ids); + struct device *dev = i3cdev_to_dev(i3cdev); struct regmap *regmap; regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config); if (IS_ERR(regmap)) { - dev_err(&i3cdev->dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); + dev_err(dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } - return st_lsm6dsx_probe(&i3cdev->dev, 0, (uintptr_t)id->data, regmap); + return st_lsm6dsx_probe(dev, 0, (uintptr_t)id->data, regmap); } static struct i3c_driver st_lsm6dsx_driver = { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index c1b444520d2a..3c5e65dc0f97 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -558,12 +558,11 @@ st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - break; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val); - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); break; case IIO_CHAN_INFO_SAMP_FREQ: *val = sensor->ext_info.slv_odr / 1000; @@ -614,53 +613,57 @@ st_lsm6dsx_shub_set_full_scale(struct st_lsm6dsx_sensor *sensor, } static int -st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +__st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); int err; - err = iio_device_claim_direct_mode(iio_dev); - if (err) - return err; - switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: { + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *ref_sensor; + u8 odr_val; u16 data; + int odr; val = val * 1000 + val2 / 1000; err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data); - if (!err) { - struct st_lsm6dsx_hw *hw = sensor->hw; - struct st_lsm6dsx_sensor *ref_sensor; - u8 odr_val; - int odr; - - ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - odr = st_lsm6dsx_check_odr(ref_sensor, val, &odr_val); - if (odr < 0) { - err = odr; - goto release; - } - - sensor->ext_info.slv_odr = val; - sensor->odr = odr; - } - break; + if (err) + return err; + + ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + odr = st_lsm6dsx_check_odr(ref_sensor, val, &odr_val); + if (odr < 0) + return odr; + + sensor->ext_info.slv_odr = val; + sensor->odr = odr; + return 0; } case IIO_CHAN_INFO_SCALE: - err = st_lsm6dsx_shub_set_full_scale(sensor, val2); - break; + return st_lsm6dsx_shub_set_full_scale(sensor, val2); default: - err = -EINVAL; - break; + return -EINVAL; } +} + +static int +st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; -release: - iio_device_release_direct_mode(iio_dev); + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; - return err; + ret = __st_lsm6dsx_shub_write_raw(iio_dev, chan, val, val2, mask); + + iio_device_release_direct(iio_dev); + + return ret; } static ssize_t diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 4b4b6d45524f..3389b15df0bc 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -133,7 +133,7 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,asm330lhhxg1", .data = (void *)ST_ASM330LHHXG1_ID, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -162,7 +162,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID }, { ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID }, { ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c index 0732cfa258c4..4232a9d800fc 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> @@ -28,20 +28,20 @@ static const struct of_device_id st_lsm9ds0_of_match[] = { .compatible = "st,lsm9ds0-imu", .data = LSM9DS0_IMU_DEV_NAME, }, - {} + { } }; MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); static const struct i2c_device_id st_lsm9ds0_id_table[] = { { LSM303D_IMU_DEV_NAME }, { LSM9DS0_IMU_DEV_NAME }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, st_lsm9ds0_id_table); static const struct acpi_device_id st_lsm9ds0_acpi_match[] = { {"ACCL0001", (kernel_ulong_t)LSM303D_IMU_DEV_NAME}, - {} + { } }; MODULE_DEVICE_TABLE(acpi, st_lsm9ds0_acpi_match); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 43ec57c1e604..acea8a0757d7 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> @@ -28,14 +28,14 @@ static const struct of_device_id st_lsm9ds0_of_match[] = { .compatible = "st,lsm9ds0-imu", .data = LSM9DS0_IMU_DEV_NAME, }, - {} + { } }; MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); static const struct spi_device_id st_lsm9ds0_id_table[] = { { LSM303D_IMU_DEV_NAME }, { LSM9DS0_IMU_DEV_NAME }, - {} + { } }; MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 363281272035..266e1b29bf91 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -155,10 +155,15 @@ 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); + if (count >= sizeof(buf)) + return -ENOSPC; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count); if (rc < 0) return rc; + buf[rc] = '\0'; + ret = sscanf(buf, "%i %i", &back->cached_reg_addr, &val); switch (ret) { @@ -379,6 +384,34 @@ int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan, EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, "IIO_BACKEND"); /** + * iio_backend_data_source_get - Get current data source + * @back: Backend device + * @chan: Channel number + * @data: Pointer to receive the current source value + * + * A given backend may have different sources to stream/sync data. This allows + * to know what source is in use. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_source_get(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source *data) +{ + int ret; + + ret = iio_backend_op_call(back, data_source_get, chan, data); + if (ret) + return ret; + + if (*data >= IIO_BACKEND_DATA_SOURCE_MAX) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_get, "IIO_BACKEND"); + +/** * iio_backend_set_sampling_freq - Set channel sampling rate * @back: Backend device * @chan: Channel number @@ -637,6 +670,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-buffer.c b/drivers/iio/industrialio-buffer.c index 2708f87df719..a80f7cc25a27 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1137,7 +1137,7 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, int ret; indio_dev->active_scan_mask = config->scan_mask; - indio_dev->scan_timestamp = config->scan_timestamp; + ACCESS_PRIVATE(indio_dev, scan_timestamp) = config->scan_timestamp; indio_dev->scan_bytes = config->scan_bytes; iio_dev_opaque->currentmode = config->mode; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index a2117ad1337d..5ffda104d4b2 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -26,6 +26,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/wait.h> +#include <linux/wordpart.h> #include <linux/iio/buffer.h> #include <linux/iio/buffer_impl.h> @@ -410,11 +411,15 @@ 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; + if (count >= sizeof(buf)) + return -EINVAL; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, + count); + if (ret < 0) + return ret; - buf[count] = 0; + buf[ret] = '\0'; ret = sscanf(buf, "%i %i", ®, &val); @@ -965,8 +970,10 @@ static ssize_t iio_write_channel_info(struct device *dev, struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); int ret, fract_mult = 100000; int integer, fract = 0; + long long integer64; bool is_char = false; bool scale_db = false; + bool is_64bit = false; /* Assumes decimal - precision based on number of digits */ if (!indio_dev->info->write_raw) @@ -990,6 +997,9 @@ static ssize_t iio_write_channel_info(struct device *dev, case IIO_VAL_CHAR: is_char = true; break; + case IIO_VAL_INT_64: + is_64bit = true; + break; default: return -EINVAL; } @@ -1000,6 +1010,13 @@ static ssize_t iio_write_channel_info(struct device *dev, if (sscanf(buf, "%c", &ch) != 1) return -EINVAL; integer = ch; + } else if (is_64bit) { + ret = kstrtoll(buf, 0, &integer64); + if (ret) + return ret; + + fract = upper_32_bits(integer64); + integer = lower_32_bits(integer64); } else { ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract, scale_db); @@ -2143,17 +2160,19 @@ int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, EXPORT_SYMBOL_GPL(__devm_iio_device_register); /** - * iio_device_claim_direct_mode - Keep device in direct mode + * __iio_device_claim_direct - Keep device in direct mode * @indio_dev: the iio_dev associated with the device * * If the device is in direct mode it is guaranteed to stay - * that way until iio_device_release_direct_mode() is called. + * that way until __iio_device_release_direct() is called. * - * Use with iio_device_release_direct_mode() + * Use with __iio_device_release_direct(). * - * Returns: 0 on success, -EBUSY on failure. + * Drivers should only call iio_device_claim_direct(). + * + * Returns: true on success, false on failure. */ -int iio_device_claim_direct_mode(struct iio_dev *indio_dev) +bool __iio_device_claim_direct(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); @@ -2161,26 +2180,28 @@ int iio_device_claim_direct_mode(struct iio_dev *indio_dev) if (iio_buffer_enabled(indio_dev)) { mutex_unlock(&iio_dev_opaque->mlock); - return -EBUSY; + return false; } - return 0; + return true; } -EXPORT_SYMBOL_GPL(iio_device_claim_direct_mode); +EXPORT_SYMBOL_GPL(__iio_device_claim_direct); /** - * iio_device_release_direct_mode - releases claim on direct mode + * __iio_device_release_direct - releases claim on direct mode * @indio_dev: the iio_dev associated with the device * * Release the claim. Device is no longer guaranteed to stay * in direct mode. * - * Use with iio_device_claim_direct_mode() + * Drivers should only call iio_device_release_direct(). + * + * Use with __iio_device_claim_direct() */ -void iio_device_release_direct_mode(struct iio_dev *indio_dev) +void __iio_device_release_direct(struct iio_dev *indio_dev) { mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock); } -EXPORT_SYMBOL_GPL(iio_device_release_direct_mode); +EXPORT_SYMBOL_GPL(__iio_device_release_direct); /** * iio_device_claim_buffer_mode - Keep device in buffer mode 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 3b5a99815062..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; - - per_time_gains = kcalloc(gts->num_itime, sizeof(*per_time_gains), GFP_KERNEL); - if (!per_time_gains) - return ret; + int ret, all_scales_tbl_bytes; + int **per_time_scales; - 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, @@ -915,7 +991,50 @@ int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel } EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, "IIO_GTS_HELPER"); -static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) +/** + * iio_gts_find_gain_time_sel_for_scale - Fetch gain and time selectors for scale + * @gts: Gain time scale descriptor + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain_sel: Pointer to value where gain selector is stored. + * @time_sel: Pointer to value where time selector is stored. + * + * Wrapper around iio_gts_find_gain_for_scale_using_time() to fetch the + * gain and time selectors for a given scale. + * + * Return: 0 on success and -EINVAL on error. + */ +int iio_gts_find_gain_time_sel_for_scale(struct iio_gts *gts, int scale_int, + int scale_nano, int *gain_sel, + int *time_sel) +{ + int i, ret; + + for (i = 0; i < gts->num_itime; i++) { + *time_sel = gts->itime_table[i].sel; + ret = iio_gts_find_gain_sel_for_scale_using_time(gts, *time_sel, + scale_int, + scale_nano, + gain_sel); + if (ret) + continue; + + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_time_sel_for_scale, "IIO_GTS_HELPER"); + +/** + * 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; @@ -931,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) @@ -1086,6 +1206,48 @@ int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, } EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, "IIO_GTS_HELPER"); +/** + * iio_gts_find_new_gain_by_gain_time_min - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time: Selector corresponding previously set time + * @new_time: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * @in_range: Indicate if the @new_gain was in the range of + * supported gains. + * + * Wrapper around iio_gts_find_new_gain_by_old_gain_time() that tries to + * set an optimal value if no exact match was found, defaulting to the + * minimum gain to avoid saturations if the optimal value is not in the + * range of supported gains. + * + * Return: 0 on success and a negative value if no gain was found. + */ +int iio_gts_find_new_gain_by_gain_time_min(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain, bool *in_range) +{ + int ret; + + *in_range = true; + ret = iio_gts_find_new_gain_by_old_gain_time(gts, old_gain, old_time, + new_time, new_gain); + if (*new_gain < 0) + return -EINVAL; + + if (ret) { + *new_gain = iio_find_closest_gain_low(gts, *new_gain, in_range); + if (*new_gain < 0) { + *new_gain = iio_gts_get_min_gain(gts); + if (*new_gain < 0) + return -EINVAL; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_gain_time_min, "IIO_GTS_HELPER"); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9050a59129e6..c174ebb7d5e6 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -7,6 +7,7 @@ #include <linux/err.h> #include <linux/export.h> #include <linux/minmax.h> +#include <linux/mm.h> #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> @@ -989,6 +990,11 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, { const struct iio_chan_spec_ext_info *ext_info; + if (!buf || offset_in_page(buf)) { + pr_err("iio: invalid ext_info read buffer\n"); + return -EINVAL; + } + ext_info = iio_lookup_ext_info(chan, attr); if (!ext_info) return -EINVAL; @@ -1014,6 +1020,11 @@ EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { + if (!buf || offset_in_page(buf)) { + pr_err("iio: invalid label read buffer\n"); + return -EINVAL; + } + return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } EXPORT_SYMBOL_GPL(iio_read_channel_label); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 29ffa8491927..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 @@ -329,22 +350,6 @@ config JSA1212 To compile this driver as a module, choose M here: the module will be called jsa1212. -config ROHM_BU27008 - tristate "ROHM BU27008 color (RGB+C/IR) sensor" - depends on I2C - select REGMAP_I2C - select IIO_GTS_HELPER - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Enable support for the ROHM BU27008 color sensor. - The ROHM BU27008 is a sensor with 5 photodiodes (red, green, - blue, clear and IR) with four configurable channels. Red and - green being always available and two out of the rest three - (blue, clear, IR) can be selected to be simultaneously measured. - Typical application is adjusting LCD backlight of TVs, - mobile phones and tablet PCs. - config ROHM_BU27034 tristate "ROHM BU27034 ambient light sensor" depends on I2C @@ -491,6 +496,19 @@ config OPT4001 If built as a dynamically linked module, it will be called opt4001. +config OPT4060 + tristate "Texas Instruments OPT4060 RGBW Color Sensor" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say Y or M here, you get support for Texas Instruments + OPT4060 RGBW Color Sensor. + + If built as a dynamically linked module, it will be called + opt4060. + config PA12203001 tristate "TXC PA12203001 light and proximity sensor" depends on I2C @@ -672,6 +690,7 @@ config VCNL4035 config VEML3235 tristate "VEML3235 ambient light sensor" select REGMAP_I2C + select IIO_GTS_HELPER depends on I2C help Say Y here if you want to build a driver for the Vishay VEML3235 @@ -683,6 +702,9 @@ config VEML3235 config VEML6030 tristate "VEML6030 and VEML6035 ambient light sensors" 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 f14a37442712..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 @@ -42,8 +44,8 @@ obj-$(CONFIG_MAX44009) += max44009.o obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_OPT4001) += opt4001.o +obj-$(CONFIG_OPT4060) += opt4060.o obj-$(CONFIG_PA12203001) += pa12203001.o -obj-$(CONFIG_ROHM_BU27008) += rohm-bu27008.o obj-$(CONFIG_ROHM_BU27034) += rohm-bu27034.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SI1133) += si1133.o diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 2d91caf24dd0..032e6cae8b80 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -230,7 +230,7 @@ static int acpi_als_add(struct acpi_device *device) static const struct acpi_device_id acpi_als_device_ids[] = { {"ACPI0008", 0}, - {}, + { } }; MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids); diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index c1b43053fbc7..cf96e3dd8bc6 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -56,7 +56,7 @@ struct adjd_s311_data { struct i2c_client *client; struct { s16 chans[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index 593d614b1689..e321f89c5340 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[] = { @@ -821,7 +820,7 @@ static int adux1020_probe(struct i2c_client *client) static const struct i2c_device_id adux1020_id[] = { { "adux1020" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, adux1020_id); diff --git a/drivers/iio/light/al3000a.c b/drivers/iio/light/al3000a.c new file mode 100644 index 000000000000..6f301c067045 --- /dev/null +++ b/drivers/iio/light/al3000a.c @@ -0,0 +1,210 @@ +// 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) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = al3000a_set_pwr_on(data); + if (ret) + return ret; + + 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"); + + 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"); + + 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" }, + { } +}; +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/al3010.c b/drivers/iio/light/al3010.c index 7cbb8b203300..0932fa2b49fa 100644 --- a/drivers/iio/light/al3010.c +++ b/drivers/iio/light/al3010.c @@ -17,13 +17,12 @@ #include <linux/bitfield.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/regmap.h> #include <linux/mod_devicetable.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#define AL3010_DRV_NAME "al3010" - #define AL3010_REG_SYSTEM 0x00 #define AL3010_REG_DATA_LOW 0x0c #define AL3010_REG_CONFIG 0x10 @@ -46,8 +45,14 @@ static const int al3010_scales[][2] = { {0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600} }; +static const struct regmap_config al3010_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AL3010_REG_CONFIG, +}; + struct al3010_data { - struct i2c_client *client; + struct regmap *regmap; }; static const struct iio_chan_spec al3010_channels[] = { @@ -69,40 +74,36 @@ static const struct attribute_group al3010_attribute_group = { .attrs = al3010_attributes, }; -static int al3010_set_pwr(struct i2c_client *client, bool pwr) +static int al3010_set_pwr_on(struct al3010_data *data) { - u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE; - return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val); + return regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_ENABLE); } static void al3010_set_pwr_off(void *_data) { struct al3010_data *data = _data; + struct device *dev = regmap_get_device(data->regmap); + int ret; - al3010_set_pwr(data->client, false); + ret = regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_DISABLE); + if (ret) + dev_err(dev, "failed to write system register\n"); } static int al3010_init(struct al3010_data *data) { + struct device *dev = regmap_get_device(data->regmap); int ret; - ret = al3010_set_pwr(data->client, true); - if (ret < 0) + ret = al3010_set_pwr_on(data); + if (ret) return ret; - ret = devm_add_action_or_reset(&data->client->dev, - al3010_set_pwr_off, - data); - if (ret < 0) + ret = devm_add_action_or_reset(dev, al3010_set_pwr_off, data); + if (ret) return ret; - - ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG, - FIELD_PREP(AL3010_GAIN_MASK, - AL3XXX_RANGE_3)); - if (ret < 0) - return ret; - - return 0; + return regmap_write(data->regmap, AL3010_REG_CONFIG, + FIELD_PREP(AL3010_GAIN_MASK, AL3XXX_RANGE_3)); } static int al3010_read_raw(struct iio_dev *indio_dev, @@ -110,7 +111,7 @@ static int al3010_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3010_data *data = iio_priv(indio_dev); - int ret; + int ret, gain, raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -119,21 +120,21 @@ static int al3010_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3010_REG_DATA_LOW * - high byte of output is stored at AL3010_REG_DATA_LOW + 1 */ - ret = i2c_smbus_read_word_data(data->client, - AL3010_REG_DATA_LOW); - if (ret < 0) + ret = regmap_read(data->regmap, AL3010_REG_DATA_LOW, &raw); + if (ret) return ret; - *val = ret; + + *val = raw; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = i2c_smbus_read_byte_data(data->client, - AL3010_REG_CONFIG); - if (ret < 0) + ret = regmap_read(data->regmap, AL3010_REG_CONFIG, &gain); + if (ret) return ret; - ret = FIELD_GET(AL3010_GAIN_MASK, ret); - *val = al3010_scales[ret][0]; - *val2 = al3010_scales[ret][1]; + gain = FIELD_GET(AL3010_GAIN_MASK, gain); + *val = al3010_scales[gain][0]; + *val2 = al3010_scales[gain][1]; return IIO_VAL_INT_PLUS_MICRO; } @@ -145,7 +146,7 @@ static int al3010_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct al3010_data *data = iio_priv(indio_dev); - int i; + unsigned int i; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -154,9 +155,8 @@ static int al3010_write_raw(struct iio_dev *indio_dev, val2 != al3010_scales[i][1]) continue; - return i2c_smbus_write_byte_data(data->client, - AL3010_REG_CONFIG, - FIELD_PREP(AL3010_GAIN_MASK, i)); + return regmap_write(data->regmap, AL3010_REG_CONFIG, + FIELD_PREP(AL3010_GAIN_MASK, i)); } break; } @@ -172,59 +172,66 @@ static const struct iio_info al3010_info = { static int al3010_probe(struct i2c_client *client) { struct al3010_data *data; + struct device *dev = &client->dev; struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + 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->client = client; + data->regmap = devm_regmap_init_i2c(client, &al3010_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "cannot allocate regmap\n"); indio_dev->info = &al3010_info; - indio_dev->name = AL3010_DRV_NAME; + indio_dev->name = "al3010"; indio_dev->channels = al3010_channels; indio_dev->num_channels = ARRAY_SIZE(al3010_channels); indio_dev->modes = INDIO_DIRECT_MODE; ret = al3010_init(data); - if (ret < 0) { - dev_err(&client->dev, "al3010 chip init failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to init ALS\n"); - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static int al3010_suspend(struct device *dev) { - return al3010_set_pwr(to_i2c_client(dev), false); + struct al3010_data *data = iio_priv(dev_get_drvdata(dev)); + + al3010_set_pwr_off(data); + return 0; } static int al3010_resume(struct device *dev) { - return al3010_set_pwr(to_i2c_client(dev), true); + struct al3010_data *data = iio_priv(dev_get_drvdata(dev)); + + return al3010_set_pwr_on(data); } static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume); static const struct i2c_device_id al3010_id[] = { {"al3010", }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, al3010_id); static const struct of_device_id al3010_of_match[] = { { .compatible = "dynaimage,al3010", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, al3010_of_match); static struct i2c_driver al3010_driver = { .driver = { - .name = AL3010_DRV_NAME, + .name = "al3010", .of_match_table = al3010_of_match, .pm = pm_sleep_ptr(&al3010_pm_ops), }, diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c index 497ea3fe3377..63f5a85912fc 100644 --- a/drivers/iio/light/al3320a.c +++ b/drivers/iio/light/al3320a.c @@ -15,13 +15,12 @@ #include <linux/bitfield.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/regmap.h> #include <linux/mod_devicetable.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#define AL3320A_DRV_NAME "al3320a" - #define AL3320A_REG_CONFIG 0x00 #define AL3320A_REG_STATUS 0x01 #define AL3320A_REG_INT 0x02 @@ -59,8 +58,14 @@ static const int al3320a_scales[][2] = { {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000} }; +static const struct regmap_config al3320a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AL3320A_REG_HIGH_THRESH_HIGH, +}; + struct al3320a_data { - struct i2c_client *client; + struct regmap *regmap; }; static const struct iio_chan_spec al3320a_channels[] = { @@ -82,45 +87,47 @@ static const struct attribute_group al3320a_attribute_group = { .attrs = al3320a_attributes, }; -static int al3320a_set_pwr(struct i2c_client *client, bool pwr) +static int al3320a_set_pwr_on(struct al3320a_data *data) { - u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE; - return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val); + return regmap_write(data->regmap, AL3320A_REG_CONFIG, AL3320A_CONFIG_ENABLE); } static void al3320a_set_pwr_off(void *_data) { struct al3320a_data *data = _data; + struct device *dev = regmap_get_device(data->regmap); + int ret; - al3320a_set_pwr(data->client, false); + ret = regmap_write(data->regmap, AL3320A_REG_CONFIG, AL3320A_CONFIG_DISABLE); + if (ret) + dev_err(dev, "failed to write system register\n"); } static int al3320a_init(struct al3320a_data *data) { + struct device *dev = regmap_get_device(data->regmap); int ret; - ret = al3320a_set_pwr(data->client, true); - - if (ret < 0) + ret = al3320a_set_pwr_on(data); + if (ret) return ret; - ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE, - FIELD_PREP(AL3320A_GAIN_MASK, - AL3320A_RANGE_3)); - if (ret < 0) + ret = devm_add_action_or_reset(dev, al3320a_set_pwr_off, data); + if (ret) return ret; - ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME, - AL3320A_DEFAULT_MEAN_TIME); - if (ret < 0) + ret = regmap_write(data->regmap, AL3320A_REG_CONFIG_RANGE, + FIELD_PREP(AL3320A_GAIN_MASK, AL3320A_RANGE_3)); + if (ret) return ret; - ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT, - AL3320A_DEFAULT_WAIT_TIME); - if (ret < 0) + ret = regmap_write(data->regmap, AL3320A_REG_MEAN_TIME, + AL3320A_DEFAULT_MEAN_TIME); + if (ret) return ret; - return 0; + return regmap_write(data->regmap, AL3320A_REG_WAIT, + AL3320A_DEFAULT_WAIT_TIME); } static int al3320a_read_raw(struct iio_dev *indio_dev, @@ -128,7 +135,7 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct al3320a_data *data = iio_priv(indio_dev); - int ret; + int ret, gain, raw; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -137,21 +144,21 @@ static int al3320a_read_raw(struct iio_dev *indio_dev, * - low byte of output is stored at AL3320A_REG_DATA_LOW * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1 */ - ret = i2c_smbus_read_word_data(data->client, - AL3320A_REG_DATA_LOW); - if (ret < 0) + ret = regmap_read(data->regmap, AL3320A_REG_DATA_LOW, &raw); + if (ret) return ret; - *val = ret; + + *val = raw; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = i2c_smbus_read_byte_data(data->client, - AL3320A_REG_CONFIG_RANGE); - if (ret < 0) + ret = regmap_read(data->regmap, AL3320A_REG_CONFIG_RANGE, &gain); + if (ret) return ret; - ret = FIELD_GET(AL3320A_GAIN_MASK, ret); - *val = al3320a_scales[ret][0]; - *val2 = al3320a_scales[ret][1]; + gain = FIELD_GET(AL3320A_GAIN_MASK, gain); + *val = al3320a_scales[gain][0]; + *val2 = al3320a_scales[gain][1]; return IIO_VAL_INT_PLUS_MICRO; } @@ -163,7 +170,7 @@ static int al3320a_write_raw(struct iio_dev *indio_dev, int val2, long mask) { struct al3320a_data *data = iio_priv(indio_dev); - int i; + unsigned int i; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -172,9 +179,8 @@ static int al3320a_write_raw(struct iio_dev *indio_dev, val2 != al3320a_scales[i][1]) continue; - return i2c_smbus_write_byte_data(data->client, - AL3320A_REG_CONFIG_RANGE, - FIELD_PREP(AL3320A_GAIN_MASK, i)); + return regmap_write(data->regmap, AL3320A_REG_CONFIG_RANGE, + FIELD_PREP(AL3320A_GAIN_MASK, i)); } break; } @@ -190,46 +196,50 @@ static const struct iio_info al3320a_info = { static int al3320a_probe(struct i2c_client *client) { struct al3320a_data *data; + struct device *dev = &client->dev; struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + 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->client = client; + + data->regmap = devm_regmap_init_i2c(client, &al3320a_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "cannot allocate regmap\n"); indio_dev->info = &al3320a_info; - indio_dev->name = AL3320A_DRV_NAME; + indio_dev->name = "al3320a"; indio_dev->channels = al3320a_channels; indio_dev->num_channels = ARRAY_SIZE(al3320a_channels); indio_dev->modes = INDIO_DIRECT_MODE; ret = al3320a_init(data); if (ret < 0) { - dev_err(&client->dev, "al3320a chip init failed\n"); + dev_err(dev, "al3320a chip init failed\n"); return ret; } - ret = devm_add_action_or_reset(&client->dev, - al3320a_set_pwr_off, - data); - if (ret < 0) - return ret; - - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static int al3320a_suspend(struct device *dev) { - return al3320a_set_pwr(to_i2c_client(dev), false); + struct al3320a_data *data = iio_priv(dev_get_drvdata(dev)); + + al3320a_set_pwr_off(data); + return 0; } static int al3320a_resume(struct device *dev) { - return al3320a_set_pwr(to_i2c_client(dev), true); + struct al3320a_data *data = iio_priv(dev_get_drvdata(dev)); + + return al3320a_set_pwr_on(data); } static DEFINE_SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, @@ -237,25 +247,25 @@ static DEFINE_SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, static const struct i2c_device_id al3320a_id[] = { { "al3320a" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, al3320a_id); static const struct of_device_id al3320a_of_match[] = { { .compatible = "dynaimage,al3320a", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, al3320a_of_match); static const struct acpi_device_id al3320a_acpi_match[] = { {"CALS0001"}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, al3320a_acpi_match); static struct i2c_driver al3320a_driver = { .driver = { - .name = AL3320A_DRV_NAME, + .name = "al3320a", .of_match_table = al3320a_of_match, .pm = pm_sleep_ptr(&al3320a_pm_ops), .acpi_match_table = al3320a_acpi_match, 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/apds9306.c b/drivers/iio/light/apds9306.c index 69a0d609cffc..e9b237de180a 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -108,11 +108,11 @@ static const struct part_id_gts_multiplier apds9306_gts_mul[] = { { .part_id = 0xB1, .max_scale_int = 16, - .max_scale_nano = 3264320, + .max_scale_nano = 326432000, }, { .part_id = 0xB3, .max_scale_int = 14, - .max_scale_nano = 9712000, + .max_scale_nano = 97120000, }, }; @@ -831,11 +831,10 @@ static int apds9306_read_raw(struct iio_dev *indio_dev, * Changing device parameters during adc operation, resets * the ADC which has to avoided. */ - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = apds9306_read_data(data, val, reg); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index d30441d33703..0003a29bf264 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1157,7 +1157,7 @@ static const struct dev_pm_ops apds9960_pm_ops = { static const struct i2c_device_id apds9960_id[] = { { "apds9960" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, apds9960_id); diff --git a/drivers/iio/light/as73211.c b/drivers/iio/light/as73211.c index 11fbdcdd26d6..68f60dc3c79d 100644 --- a/drivers/iio/light/as73211.c +++ b/drivers/iio/light/as73211.c @@ -16,6 +16,7 @@ */ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -418,18 +419,17 @@ static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec cons case IIO_CHAN_INFO_RAW: { int ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret < 0) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = as73211_req_data(data); if (ret < 0) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } ret = i2c_smbus_read_word_data(data->client, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -517,6 +517,16 @@ static int _as73211_write_raw(struct iio_dev *indio_dev, struct as73211_data *data = iio_priv(indio_dev); int ret; + /* Need to switch to config mode ... */ + if ((data->osr & AS73211_OSR_DOS_MASK) != AS73211_OSR_DOS_CONFIG) { + data->osr &= ~AS73211_OSR_DOS_MASK; + data->osr |= AS73211_OSR_DOS_CONFIG; + + ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); + if (ret < 0) + return ret; + } + switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: { int reg_bits, freq_kHz = val / HZ_PER_KHZ; /* 1024, 2048, ... */ @@ -601,28 +611,14 @@ static int as73211_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec con struct as73211_data *data = iio_priv(indio_dev); int ret; - mutex_lock(&data->mutex); - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret < 0) - goto error_unlock; - - /* Need to switch to config mode ... */ - if ((data->osr & AS73211_OSR_DOS_MASK) != AS73211_OSR_DOS_CONFIG) { - data->osr &= ~AS73211_OSR_DOS_MASK; - data->osr |= AS73211_OSR_DOS_CONFIG; + guard(mutex)(&data->mutex); - ret = i2c_smbus_write_byte_data(data->client, AS73211_REG_OSR, data->osr); - if (ret < 0) - goto error_release; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = _as73211_write_raw(indio_dev, chan, val, val2, mask); + iio_device_release_direct(indio_dev); -error_release: - iio_device_release_direct_mode(indio_dev); -error_unlock: - mutex_unlock(&data->mutex); return ret; } @@ -642,7 +638,7 @@ static irqreturn_t as73211_trigger_handler(int irq __always_unused, void *p) struct as73211_data *data = iio_priv(indio_dev); struct { __le16 chan[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int data_result, ret; diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 63bf729df517..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); @@ -739,7 +739,7 @@ static irqreturn_t bh1745_trigger_handler(int interrupt, void *p) struct bh1745_data *data = iio_priv(indio_dev); struct { u16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u16 value; int ret; diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index 4b869fa9e5b1..764f88826fcb 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -22,12 +22,16 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/module.h> +#include <linux/gpio/consumer.h> #define BH1750_POWER_DOWN 0x00 #define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */ #define BH1750_CHANGE_INT_TIME_H_BIT 0x40 #define BH1750_CHANGE_INT_TIME_L_BIT 0x60 +/* Define the reset delay time in microseconds */ +#define BH1750_RESET_DELAY_US 10000 /* 10ms */ + enum { BH1710, BH1721, @@ -40,6 +44,7 @@ struct bh1750_data { struct mutex lock; const struct bh1750_chip_info *chip_info; u16 mtreg; + struct gpio_desc *reset_gpio; }; struct bh1750_chip_info { @@ -248,6 +253,25 @@ static int bh1750_probe(struct i2c_client *client) data->client = client; data->chip_info = &bh1750_chip_info_tbl[id->driver_data]; + /* Get reset GPIO from device tree */ + data->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_HIGH); + + if (IS_ERR(data->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio), + "Failed to get reset GPIO\n"); + + /* Perform hardware reset if GPIO is provided */ + if (data->reset_gpio) { + /* Perform reset sequence: low-high */ + gpiod_set_value_cansleep(data->reset_gpio, 1); + fsleep(BH1750_RESET_DELAY_US); + gpiod_set_value_cansleep(data->reset_gpio, 0); + fsleep(BH1750_RESET_DELAY_US); + + dev_dbg(&client->dev, "BH1750 reset completed via GPIO\n"); + } + usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default; ret = bh1750_change_int_time(data, usec); if (ret < 0) diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c index 475f44954f61..c7c877d2fe67 100644 --- a/drivers/iio/light/bh1780.c +++ b/drivers/iio/light/bh1780.c @@ -264,7 +264,7 @@ MODULE_DEVICE_TABLE(i2c, bh1780_id); static const struct of_device_id of_bh1780_match[] = { { .compatible = "rohm,bh1780gli", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, of_bh1780_match); 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/cm3232.c b/drivers/iio/light/cm3232.c index b6288dd25bbf..e864d2ef036e 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -89,6 +89,15 @@ static int cm3232_reg_init(struct cm3232_chip *chip) chip->als_info = &cm3232_als_info_default; + /* Disable and reset device */ + chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; + ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + chip->regs_cmd); + if (ret < 0) { + dev_err(&chip->client->dev, "Error writing reg_cmd\n"); + return ret; + } + /* Identify device */ ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID); if (ret < 0) { @@ -99,15 +108,6 @@ static int cm3232_reg_init(struct cm3232_chip *chip) if ((ret & 0xFF) != chip->als_info->hw_id) return -ENODEV; - /* Disable and reset device */ - chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; - ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, - chip->regs_cmd); - if (ret < 0) { - dev_err(&chip->client->dev, "Error writing reg_cmd\n"); - return ret; - } - /* Register default value */ chip->regs_cmd = chip->als_info->regs_cmd_default; @@ -369,7 +369,7 @@ static void cm3232_remove(struct i2c_client *client) static const struct i2c_device_id cm3232_id[] = { { "cm3232" }, - {} + { } }; static int cm3232_suspend(struct device *dev) @@ -406,7 +406,7 @@ MODULE_DEVICE_TABLE(i2c, cm3232_id); static const struct of_device_id cm3232_of_match[] = { {.compatible = "capella,cm3232"}, - {} + { } }; MODULE_DEVICE_TABLE(of, cm3232_of_match); diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index 79a64e2ff812..79ad6e2209ca 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -251,13 +251,13 @@ static int cm3323_probe(struct i2c_client *client) static const struct i2c_device_id cm3323_id[] = { { "cm3323" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, cm3323_id); static const struct of_device_id cm3323_of_match[] = { { .compatible = "capella,cm3323", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, cm3323_of_match); diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 675c0fd44db4..0c17378e27d1 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -307,7 +307,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(cm3605_dev_pm_ops, cm3605_pm_suspend, static const struct of_device_id cm3605_of_match[] = { {.compatible = "capella,cm3605"}, - { }, + { } }; MODULE_DEVICE_TABLE(of, cm3605_of_match); 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/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index 19e529c84e95..815806ceb5c8 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -249,7 +249,7 @@ static const struct platform_device_id cros_ec_light_prox_ids[] = { { .name = "cros-ec-light", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids); diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index d56ee217fe53..42859e5b1089 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -700,7 +700,7 @@ MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table); static const struct of_device_id gp2ap002_of_match[] = { { .compatible = "sharp,gp2ap002a00f" }, { .compatible = "sharp,gp2ap002s00f" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, gp2ap002_of_match); diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 1a352c88598e..c7df4b258e2c 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1283,12 +1283,11 @@ static int gp2ap020a00f_read_raw(struct iio_dev *indio_dev, int err = -EINVAL; if (mask == IIO_CHAN_INFO_RAW) { - err = iio_device_claim_direct_mode(indio_dev); - if (err) - return err; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; err = gp2ap020a00f_read_channel(data, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); } return err < 0 ? err : IIO_VAL_INT; } diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index aa4c72d4849e..830e5ae7f34a 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -456,7 +456,7 @@ static const struct platform_device_id hid_als_ids[] = { /* Format: HID-SENSOR-custom_sensor_tag-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-LISS-0041", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_als_ids); diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index c83acbd78275..efa904a70d0e 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -34,9 +34,9 @@ struct prox_state { struct iio_chan_spec channels[MAX_CHANNELS]; u32 channel2usage[MAX_CHANNELS]; u32 human_presence[MAX_CHANNELS]; - int scale_pre_decml; - int scale_post_decml; - int scale_precision; + int scale_pre_decml[MAX_CHANNELS]; + int scale_post_decml[MAX_CHANNELS]; + int scale_precision[MAX_CHANNELS]; unsigned long scan_mask[2]; /* One entry plus one terminator. */ int num_channels; }; @@ -49,9 +49,10 @@ static const u32 prox_sensitivity_addresses[] = { #define PROX_CHANNEL(_is_proximity, _channel) \ {\ .type = _is_proximity ? IIO_PROXIMITY : IIO_ATTENTION,\ - .info_mask_separate = _is_proximity ? BIT(IIO_CHAN_INFO_RAW) :\ - BIT(IIO_CHAN_INFO_PROCESSED),\ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |\ + .info_mask_separate = \ + (_is_proximity ? BIT(IIO_CHAN_INFO_RAW) :\ + BIT(IIO_CHAN_INFO_PROCESSED)) |\ + BIT(IIO_CHAN_INFO_OFFSET) |\ BIT(IIO_CHAN_INFO_SCALE) |\ BIT(IIO_CHAN_INFO_SAMP_FREQ) |\ BIT(IIO_CHAN_INFO_HYSTERESIS),\ @@ -94,6 +95,7 @@ static int prox_read_raw(struct iio_dev *indio_dev, *val2 = 0; switch (mask) { case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: if (chan->scan_index >= prox_state->num_channels) return -EINVAL; address = prox_state->channel2usage[chan->scan_index]; @@ -114,13 +116,15 @@ static int prox_read_raw(struct iio_dev *indio_dev, ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = prox_state->scale_pre_decml; - *val2 = prox_state->scale_post_decml; - ret_type = prox_state->scale_precision; + if (chan->scan_index >= prox_state->num_channels) + return -EINVAL; + + *val = prox_state->scale_pre_decml[chan->scan_index]; + *val2 = prox_state->scale_post_decml[chan->scan_index]; + ret_type = prox_state->scale_precision[chan->scan_index]; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - prox_state->prox_attr[chan->scan_index].unit_expo); + *val = 0; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -211,6 +215,9 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, case 1: prox_state->human_presence[chan] = *(u8 *)raw_data * multiplier; return 0; + case 2: + prox_state->human_presence[chan] = *(u16 *)raw_data * multiplier; + return 0; case 4: prox_state->human_presence[chan] = *(u32 *)raw_data * multiplier; return 0; @@ -247,6 +254,10 @@ static int prox_parse_report(struct platform_device *pdev, st->prox_attr[index].size); dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr[index].index, st->prox_attr[index].report_id); + st->scale_precision[index] = + hid_sensor_format_scale(usage_id, &st->prox_attr[index], + &st->scale_pre_decml[index], + &st->scale_post_decml[index]); index++; } @@ -354,7 +365,7 @@ static const struct platform_device_id hid_prox_ids[] = { /* Format: HID-SENSOR-tag-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-LISS-0226", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_prox_ids); diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index 201eae1c4589..1b4c18423048 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -824,7 +824,7 @@ static const struct acpi_device_id isl29018_acpi_match[] = { {"ISL29018", isl29018}, {"ISL29023", isl29023}, {"ISL29035", isl29035}, - {} + { } }; MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match); @@ -832,7 +832,7 @@ static const struct i2c_device_id isl29018_id[] = { {"isl29018", isl29018}, {"isl29023", isl29023}, {"isl29035", isl29035}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, isl29018_id); diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 95bfb3ffa519..609ebf0f7313 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -680,7 +680,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(isl29028_pm_ops, isl29028_suspend, static const struct i2c_device_id isl29028_id[] = { { "isl29028" }, { "isl29030" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, isl29028_id); @@ -688,7 +688,7 @@ static const struct of_device_id isl29028_of_match[] = { { .compatible = "isl,isl29028", }, /* for backward compat., don't use */ { .compatible = "isil,isl29028", }, { .compatible = "isil,isl29030", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, isl29028_of_match); diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index b176bf4c884b..6bc23b164cc5 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -54,7 +54,7 @@ struct isl29125_data { /* Ensure timestamp is naturally aligned */ struct { u16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -131,11 +131,10 @@ static int isl29125_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 = isl29125_read_data(data, chan->scan_index); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = ret; diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index e7ba934c8e69..fa4677c28931 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -424,7 +424,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, static const struct acpi_device_id jsa1212_acpi_match[] = { {"JSA1212", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match); diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c index df664f360903..ee59bbb8aa09 100644 --- a/drivers/iio/light/ltr390.c +++ b/drivers/iio/light/ltr390.c @@ -717,13 +717,13 @@ static DEFINE_SIMPLE_DEV_PM_OPS(ltr390_pm_ops, ltr390_suspend, ltr390_resume); static const struct i2c_device_id ltr390_id[] = { { "ltr390" }, - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, ltr390_id); static const struct of_device_id ltr390_of_table[] = { { .compatible = "liteon,ltr390" }, - { /* Sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, ltr390_of_table); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 604f5f900a2e..8d8051cf6927 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -541,7 +541,7 @@ static const struct iio_chan_spec_ext_info ltr501_ext_info[] = { .shared = IIO_SEPARATE, .read = ltr501_read_near_level, }, - { /* sentinel */ } + { } }; static const struct iio_event_spec ltr501_als_event_spec[] = { @@ -646,6 +646,36 @@ static const struct iio_chan_spec ltr301_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; +static int ltr501_read_info_raw(struct ltr501_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + __le16 buf[2]; + int ret; + + switch (chan->type) { + case IIO_INTENSITY: + mutex_lock(&data->lock_als); + ret = ltr501_read_als(data, buf); + mutex_unlock(&data->lock_als); + if (ret < 0) + return ret; + *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ? + buf[0] : buf[1]); + return IIO_VAL_INT; + case IIO_PROXIMITY: + mutex_lock(&data->lock_ps); + ret = ltr501_read_ps(data); + mutex_unlock(&data->lock_ps); + if (ret < 0) + return ret; + *val = ret & LTR501_PS_DATA_MASK; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + static int ltr501_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -658,14 +688,13 @@ static int ltr501_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: switch (chan->type) { case IIO_LIGHT: - 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_als); ret = ltr501_read_als(data, buf); mutex_unlock(&data->lock_als); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = ltr501_calculate_lux(le16_to_cpu(buf[1]), @@ -675,36 +704,12 @@ static int ltr501_read_raw(struct iio_dev *indio_dev, return -EINVAL; } 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; - switch (chan->type) { - case IIO_INTENSITY: - mutex_lock(&data->lock_als); - ret = ltr501_read_als(data, buf); - mutex_unlock(&data->lock_als); - if (ret < 0) - break; - *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ? - buf[0] : buf[1]); - ret = IIO_VAL_INT; - break; - case IIO_PROXIMITY: - mutex_lock(&data->lock_ps); - ret = ltr501_read_ps(data); - mutex_unlock(&data->lock_ps); - if (ret < 0) - break; - *val = ret & LTR501_PS_DATA_MASK; - ret = IIO_VAL_INT; - break; - default: - ret = -EINVAL; - break; - } + ret = ltr501_read_info_raw(data, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -756,18 +761,14 @@ static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size, return -1; } -static int ltr501_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __ltr501_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct ltr501_data *data = iio_priv(indio_dev); int i, ret, freq_val, freq_val2; const struct ltr501_chip_info *info = data->chip_info; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - switch (mask) { case IIO_CHAN_INFO_SCALE: switch (chan->type) { @@ -775,53 +776,43 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, i = ltr501_get_gain_index(info->als_gain, info->als_gain_tbl_size, val, val2); - if (i < 0) { - ret = -EINVAL; - break; - } + if (i < 0) + return -EINVAL; data->als_contr &= ~info->als_gain_mask; data->als_contr |= i << info->als_gain_shift; - ret = regmap_write(data->regmap, LTR501_ALS_CONTR, - data->als_contr); - break; + return regmap_write(data->regmap, LTR501_ALS_CONTR, + data->als_contr); case IIO_PROXIMITY: i = ltr501_get_gain_index(info->ps_gain, info->ps_gain_tbl_size, val, val2); - if (i < 0) { - ret = -EINVAL; - break; - } + if (i < 0) + return -EINVAL; + data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK; data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT; - ret = regmap_write(data->regmap, LTR501_PS_CONTR, - data->ps_contr); - break; + return regmap_write(data->regmap, LTR501_PS_CONTR, + data->ps_contr); default: - ret = -EINVAL; - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_INT_TIME: switch (chan->type) { case IIO_INTENSITY: - if (val != 0) { - ret = -EINVAL; - break; - } + if (val != 0) + return -EINVAL; + mutex_lock(&data->lock_als); ret = ltr501_set_it_time(data, val2); mutex_unlock(&data->lock_als); - break; + return ret; default: - ret = -EINVAL; - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->type) { @@ -829,50 +820,61 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, ret = ltr501_als_read_samp_freq(data, &freq_val, &freq_val2); if (ret < 0) - break; + return ret; ret = ltr501_als_write_samp_freq(data, val, val2); if (ret < 0) - break; + return ret; /* update persistence count when changing frequency */ ret = ltr501_write_intr_prst(data, chan->type, 0, data->als_period); if (ret < 0) - ret = ltr501_als_write_samp_freq(data, freq_val, - freq_val2); - break; + /* Do not ovewrite error */ + ltr501_als_write_samp_freq(data, freq_val, + freq_val2); + return ret; case IIO_PROXIMITY: ret = ltr501_ps_read_samp_freq(data, &freq_val, &freq_val2); if (ret < 0) - break; + return ret; ret = ltr501_ps_write_samp_freq(data, val, val2); if (ret < 0) - break; + return ret; /* update persistence count when changing frequency */ ret = ltr501_write_intr_prst(data, chan->type, 0, data->ps_period); if (ret < 0) - ret = ltr501_ps_write_samp_freq(data, freq_val, - freq_val2); - break; + /* Do not overwrite error */ + ltr501_ps_write_samp_freq(data, freq_val, + freq_val2); + return ret; default: - ret = -EINVAL; - break; + return -EINVAL; } - break; - default: - ret = -EINVAL; - break; + return -EINVAL; } +} + +static int ltr501_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 = __ltr501_write_raw(indio_dev, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); - iio_device_release_direct_mode(indio_dev); return ret; } @@ -1280,7 +1282,7 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) struct ltr501_data *data = iio_priv(indio_dev); struct { u16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; __le16 als_buf[2]; u8 mask = 0; @@ -1600,7 +1602,7 @@ static const struct acpi_device_id ltr_acpi_match[] = { { "LTER0301", ltr301 }, /* https://www.catalog.update.microsoft.com/Search.aspx?q=lter0303 */ { "LTER0303", ltr303 }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, ltr_acpi_match); @@ -1618,7 +1620,7 @@ static const struct of_device_id ltr501_of_match[] = { { .compatible = "liteon,ltr559", }, { .compatible = "liteon,ltr301", }, { .compatible = "liteon,ltr303", }, - {} + { } }; MODULE_DEVICE_TABLE(of, ltr501_of_match); diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c index dbec1e7cfeb8..61f57a82b872 100644 --- a/drivers/iio/light/ltrf216a.c +++ b/drivers/iio/light/ltrf216a.c @@ -554,7 +554,7 @@ static const struct ltr_chip_info ltrf216a_chip_info = { static const struct i2c_device_id ltrf216a_id[] = { { "ltr308", .driver_data = (kernel_ulong_t)<r308_chip_info }, { "ltrf216a", .driver_data = (kernel_ulong_t)<rf216a_chip_info }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ltrf216a_id); @@ -563,7 +563,7 @@ static const struct of_device_id ltrf216a_of_match[] = { { .compatible = "liteon,ltrf216a", .data = <rf216a_chip_info }, /* For Valve's Steamdeck device, an ACPI platform using PRP0001 */ { .compatible = "ltr,ltrf216a", .data = <rf216a_chip_info }, - {} + { } }; MODULE_DEVICE_TABLE(of, ltrf216a_of_match); diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index b935976871a6..e8b767680133 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -78,7 +78,7 @@ struct max44000_data { /* Ensure naturally aligned timestamp */ struct { u16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 65b295877b41..393a3d2fbe1d 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -788,8 +788,9 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) int ret; bool wake_result_ready_queue = false; enum iio_chan_type chan_type = opt->chip_info->chan_type; + bool ok_to_ignore_lock = opt->ok_to_ignore_lock; - if (!opt->ok_to_ignore_lock) + if (!ok_to_ignore_lock) mutex_lock(&opt->lock); ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION); @@ -826,7 +827,7 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) } out: - if (!opt->ok_to_ignore_lock) + if (!ok_to_ignore_lock) mutex_unlock(&opt->lock); if (wake_result_ready_queue) diff --git a/drivers/iio/light/opt4001.c b/drivers/iio/light/opt4001.c index 6cf60151b3d8..ba4eb82d9bc2 100644 --- a/drivers/iio/light/opt4001.c +++ b/drivers/iio/light/opt4001.c @@ -448,7 +448,7 @@ MODULE_DEVICE_TABLE(i2c, opt4001_id); static const struct of_device_id opt4001_of_match[] = { { .compatible = "ti,opt4001-sot-5x3", .data = &opt4001_sot_5x3_info}, { .compatible = "ti,opt4001-picostar", .data = &opt4001_picostar_info}, - {} + { } }; MODULE_DEVICE_TABLE(of, opt4001_of_match); diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c new file mode 100644 index 000000000000..f4085020e03e --- /dev/null +++ b/drivers/iio/light/opt4060.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Axis Communications AB + * + * Datasheet: https://www.ti.com/lit/gpn/opt4060 + * + * Device driver for the Texas Instruments OPT4060 RGBW Color Sensor. + */ + +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/math64.h> +#include <linux/units.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +/* OPT4060 register set */ +#define OPT4060_RED_MSB 0x00 +#define OPT4060_RED_LSB 0x01 +#define OPT4060_GREEN_MSB 0x02 +#define OPT4060_GREEN_LSB 0x03 +#define OPT4060_BLUE_MSB 0x04 +#define OPT4060_BLUE_LSB 0x05 +#define OPT4060_CLEAR_MSB 0x06 +#define OPT4060_CLEAR_LSB 0x07 +#define OPT4060_THRESHOLD_LOW 0x08 +#define OPT4060_THRESHOLD_HIGH 0x09 +#define OPT4060_CTRL 0x0a +#define OPT4060_INT_CTRL 0x0b +#define OPT4060_RES_CTRL 0x0c +#define OPT4060_DEVICE_ID 0x11 + +/* OPT4060 register mask */ +#define OPT4060_EXPONENT_MASK GENMASK(15, 12) +#define OPT4060_MSB_MASK GENMASK(11, 0) +#define OPT4060_LSB_MASK GENMASK(15, 8) +#define OPT4060_COUNTER_MASK GENMASK(7, 4) +#define OPT4060_CRC_MASK GENMASK(3, 0) + +/* OPT4060 device id mask */ +#define OPT4060_DEVICE_ID_MASK GENMASK(11, 0) + +/* OPT4060 control register masks */ +#define OPT4060_CTRL_QWAKE_MASK BIT(15) +#define OPT4060_CTRL_RANGE_MASK GENMASK(13, 10) +#define OPT4060_CTRL_CONV_TIME_MASK GENMASK(9, 6) +#define OPT4060_CTRL_OPER_MODE_MASK GENMASK(5, 4) +#define OPT4060_CTRL_LATCH_MASK BIT(3) +#define OPT4060_CTRL_INT_POL_MASK BIT(2) +#define OPT4060_CTRL_FAULT_COUNT_MASK GENMASK(1, 0) + +/* OPT4060 interrupt control register masks */ +#define OPT4060_INT_CTRL_THRESH_SEL GENMASK(6, 5) +#define OPT4060_INT_CTRL_OUTPUT BIT(4) +#define OPT4060_INT_CTRL_INT_CFG GENMASK(3, 2) +#define OPT4060_INT_CTRL_THRESHOLD 0x0 +#define OPT4060_INT_CTRL_NEXT_CH 0x1 +#define OPT4060_INT_CTRL_ALL_CH 0x3 + +/* OPT4060 result control register masks */ +#define OPT4060_RES_CTRL_OVERLOAD BIT(3) +#define OPT4060_RES_CTRL_CONV_READY BIT(2) +#define OPT4060_RES_CTRL_FLAG_H BIT(1) +#define OPT4060_RES_CTRL_FLAG_L BIT(0) + +/* OPT4060 constants */ +#define OPT4060_DEVICE_ID_VAL 0x821 + +/* OPT4060 operating modes */ +#define OPT4060_CTRL_OPER_MODE_OFF 0x0 +#define OPT4060_CTRL_OPER_MODE_FORCED 0x1 +#define OPT4060_CTRL_OPER_MODE_ONE_SHOT 0x2 +#define OPT4060_CTRL_OPER_MODE_CONTINUOUS 0x3 + +/* OPT4060 conversion control register definitions */ +#define OPT4060_CTRL_CONVERSION_0_6MS 0x0 +#define OPT4060_CTRL_CONVERSION_1MS 0x1 +#define OPT4060_CTRL_CONVERSION_1_8MS 0x2 +#define OPT4060_CTRL_CONVERSION_3_4MS 0x3 +#define OPT4060_CTRL_CONVERSION_6_5MS 0x4 +#define OPT4060_CTRL_CONVERSION_12_7MS 0x5 +#define OPT4060_CTRL_CONVERSION_25MS 0x6 +#define OPT4060_CTRL_CONVERSION_50MS 0x7 +#define OPT4060_CTRL_CONVERSION_100MS 0x8 +#define OPT4060_CTRL_CONVERSION_200MS 0x9 +#define OPT4060_CTRL_CONVERSION_400MS 0xa +#define OPT4060_CTRL_CONVERSION_800MS 0xb + +/* OPT4060 fault count control register definitions */ +#define OPT4060_CTRL_FAULT_COUNT_1 0x0 +#define OPT4060_CTRL_FAULT_COUNT_2 0x1 +#define OPT4060_CTRL_FAULT_COUNT_4 0x2 +#define OPT4060_CTRL_FAULT_COUNT_8 0x3 + +/* OPT4060 scale light level range definitions */ +#define OPT4060_CTRL_LIGHT_SCALE_AUTO 12 + +/* OPT4060 default values */ +#define OPT4060_DEFAULT_CONVERSION_TIME OPT4060_CTRL_CONVERSION_50MS + +/* + * enum opt4060_chan_type - OPT4060 channel types + * @OPT4060_RED: Red channel. + * @OPT4060_GREEN: Green channel. + * @OPT4060_BLUE: Blue channel. + * @OPT4060_CLEAR: Clear (white) channel. + * @OPT4060_ILLUM: Calculated illuminance channel. + * @OPT4060_NUM_CHANS: Number of channel types. + */ +enum opt4060_chan_type { + OPT4060_RED, + OPT4060_GREEN, + OPT4060_BLUE, + OPT4060_CLEAR, + OPT4060_ILLUM, + OPT4060_NUM_CHANS +}; + +struct opt4060_chip { + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + u8 int_time; + int irq; + /* + * Mutex for protecting sensor irq settings. Switching between interrupt + * on each sample and on thresholds needs to be synchronized. + */ + struct mutex irq_setting_lock; + /* + * Mutex for protecting event enabling. + */ + struct mutex event_enabling_lock; + struct completion completion; + bool thresh_event_lo_active; + bool thresh_event_hi_active; +}; + +struct opt4060_channel_factor { + u32 mul; + u32 div; +}; + +static const int opt4060_int_time_available[][2] = { + { 0, 600 }, + { 0, 1000 }, + { 0, 1800 }, + { 0, 3400 }, + { 0, 6500 }, + { 0, 12700 }, + { 0, 25000 }, + { 0, 50000 }, + { 0, 100000 }, + { 0, 200000 }, + { 0, 400000 }, + { 0, 800000 }, +}; + +/* + * Conversion time is integration time + time to set register + * this is used as integration time. + */ +static const int opt4060_int_time_reg[][2] = { + { 600, OPT4060_CTRL_CONVERSION_0_6MS }, + { 1000, OPT4060_CTRL_CONVERSION_1MS }, + { 1800, OPT4060_CTRL_CONVERSION_1_8MS }, + { 3400, OPT4060_CTRL_CONVERSION_3_4MS }, + { 6500, OPT4060_CTRL_CONVERSION_6_5MS }, + { 12700, OPT4060_CTRL_CONVERSION_12_7MS }, + { 25000, OPT4060_CTRL_CONVERSION_25MS }, + { 50000, OPT4060_CTRL_CONVERSION_50MS }, + { 100000, OPT4060_CTRL_CONVERSION_100MS }, + { 200000, OPT4060_CTRL_CONVERSION_200MS }, + { 400000, OPT4060_CTRL_CONVERSION_400MS }, + { 800000, OPT4060_CTRL_CONVERSION_800MS }, +}; + +static int opt4060_als_time_to_index(const u32 als_integration_time) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(opt4060_int_time_available); i++) { + if (als_integration_time == opt4060_int_time_available[i][1]) + return i; + } + + return -EINVAL; +} + +static u8 opt4060_calculate_crc(u8 exp, u32 mantissa, u8 count) +{ + u8 crc; + + /* + * Calculates a 4-bit CRC from a 20-bit mantissa, 4-bit exponent and a 4-bit counter. + * crc[0] = XOR(mantissa[19:0], exp[3:0], count[3:0]) + * crc[1] = XOR(mantissa[1,3,5,7,9,11,13,15,17,19], exp[1,3], count[1,3]) + * crc[2] = XOR(mantissa[3,7,11,15,19], exp[3], count[3]) + * crc[3] = XOR(mantissa[3,11,19]) + */ + crc = (hweight32(mantissa) + hweight32(exp) + hweight32(count)) % 2; + crc |= ((hweight32(mantissa & 0xAAAAA) + hweight32(exp & 0xA) + + hweight32(count & 0xA)) % 2) << 1; + crc |= ((hweight32(mantissa & 0x88888) + hweight32(exp & 0x8) + + hweight32(count & 0x8)) % 2) << 2; + crc |= (hweight32(mantissa & 0x80808) % 2) << 3; + + return crc; +} + +static int opt4060_set_int_state(struct opt4060_chip *chip, u32 state) +{ + int ret; + unsigned int regval; + + guard(mutex)(&chip->irq_setting_lock); + + regval = FIELD_PREP(OPT4060_INT_CTRL_INT_CFG, state); + ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL, + OPT4060_INT_CTRL_INT_CFG, regval); + if (ret) + dev_err(chip->dev, "Failed to set interrupt config\n"); + return ret; +} + +static int opt4060_set_sampling_mode(struct opt4060_chip *chip, + bool continuous) +{ + unsigned int reg; + int ret; + + ret = regmap_read(chip->regmap, OPT4060_CTRL, ®); + if (ret < 0) { + dev_err(chip->dev, "Failed to read ctrl register\n"); + return ret; + } + reg &= ~OPT4060_CTRL_OPER_MODE_MASK; + if (continuous) + reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK, + OPT4060_CTRL_OPER_MODE_CONTINUOUS); + else + reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK, + OPT4060_CTRL_OPER_MODE_ONE_SHOT); + + /* + * Trigger a new conversions by writing to CRTL register. It is not + * possible to use regmap_update_bits() since that will only write when + * data is modified. + */ + ret = regmap_write(chip->regmap, OPT4060_CTRL, reg); + if (ret) + dev_err(chip->dev, "Failed to set ctrl register\n"); + return ret; +} + +static bool opt4060_event_active(struct opt4060_chip *chip) +{ + return chip->thresh_event_lo_active || chip->thresh_event_hi_active; +} + +static int opt4060_set_state_common(struct opt4060_chip *chip, + bool continuous_sampling, + bool continuous_irq) +{ + int ret = 0; + + /* It is important to setup irq before sampling to avoid missing samples. */ + if (continuous_irq) + ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_ALL_CH); + else + ret = opt4060_set_int_state(chip, OPT4060_INT_CTRL_THRESHOLD); + if (ret) { + dev_err(chip->dev, "Failed to set irq state.\n"); + return ret; + } + + if (continuous_sampling || opt4060_event_active(chip)) + ret = opt4060_set_sampling_mode(chip, true); + else + ret = opt4060_set_sampling_mode(chip, false); + if (ret) + dev_err(chip->dev, "Failed to set sampling state.\n"); + return ret; +} + +/* + * Function for setting the driver state for sampling and irq. Either direct + * mode of buffer mode will be claimed during the transition to prevent races + * between sysfs read, buffer or events. + */ +static int opt4060_set_driver_state(struct iio_dev *indio_dev, + bool continuous_sampling, + bool continuous_irq) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret = 0; +any_mode_retry: + if (iio_device_claim_buffer_mode(indio_dev)) { + /* + * This one is a *bit* hacky. If we cannot claim buffer mode, + * then try direct mode so that we make sure things cannot + * concurrently change. And we just keep trying until we get one + * of the modes... + */ + if (!iio_device_claim_direct(indio_dev)) + goto any_mode_retry; + /* + * This path means that we managed to claim direct mode. In + * this case the buffer isn't enabled and it's okay to leave + * continuous mode for sampling and/or irq. + */ + ret = opt4060_set_state_common(chip, continuous_sampling, + continuous_irq); + iio_device_release_direct(indio_dev); + return ret; + } else { + /* + * This path means that we managed to claim buffer mode. In + * this case the buffer is enabled and irq and sampling must go + * to or remain continuous, but only if the trigger is from this + * device. + */ + if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev)) + ret = opt4060_set_state_common(chip, true, true); + else + ret = opt4060_set_state_common(chip, continuous_sampling, + continuous_irq); + iio_device_release_buffer_mode(indio_dev); + } + return ret; +} + +/* + * This function is called with framework mutex locked. + */ +static int opt4060_trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct opt4060_chip *chip = iio_priv(indio_dev); + + return opt4060_set_state_common(chip, state, state); +} + +static int opt4060_read_raw_value(struct opt4060_chip *chip, + unsigned long address, u32 *raw) +{ + int ret; + u16 result[2]; + u32 mantissa_raw; + u16 msb, lsb; + u8 exp, count, crc, calc_crc; + + ret = regmap_bulk_read(chip->regmap, address, result, 2); + if (ret) { + dev_err(chip->dev, "Reading channel data failed\n"); + return ret; + } + exp = FIELD_GET(OPT4060_EXPONENT_MASK, result[0]); + msb = FIELD_GET(OPT4060_MSB_MASK, result[0]); + count = FIELD_GET(OPT4060_COUNTER_MASK, result[1]); + crc = FIELD_GET(OPT4060_CRC_MASK, result[1]); + lsb = FIELD_GET(OPT4060_LSB_MASK, result[1]); + mantissa_raw = (msb << 8) + lsb; + calc_crc = opt4060_calculate_crc(exp, mantissa_raw, count); + if (calc_crc != crc) + return -EIO; + *raw = mantissa_raw << exp; + return 0; +} + +static int opt4060_trigger_new_samples(struct iio_dev *indio_dev) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + /* + * The conversion time should be 500us startup time plus the integration time + * times the number of channels. An exact timeout isn't critical, it's better + * not to get incorrect errors in the log. Setting the timeout to double the + * theoretical time plus and extra 100ms margin. + */ + unsigned int timeout_us = (500 + OPT4060_NUM_CHANS * + opt4060_int_time_reg[chip->int_time][0]) * 2 + 100000; + + /* Setting the state in one shot mode with irq on each sample. */ + ret = opt4060_set_driver_state(indio_dev, false, true); + if (ret) + return ret; + + if (chip->irq) { + guard(mutex)(&chip->irq_setting_lock); + reinit_completion(&chip->completion); + if (wait_for_completion_timeout(&chip->completion, + usecs_to_jiffies(timeout_us)) == 0) { + dev_err(chip->dev, "Completion timed out.\n"); + return -ETIME; + } + } else { + unsigned int ready; + + ret = regmap_read_poll_timeout(chip->regmap, OPT4060_RES_CTRL, + ready, (ready & OPT4060_RES_CTRL_CONV_READY), + 1000, timeout_us); + if (ret) + dev_err(chip->dev, "Conversion ready did not finish within timeout.\n"); + } + /* Setting the state in one shot mode with irq on thresholds. */ + return opt4060_set_driver_state(indio_dev, false, false); +} + +static int opt4060_read_chan_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + u32 adc_raw; + int ret; + + ret = opt4060_trigger_new_samples(indio_dev); + if (ret) { + dev_err(chip->dev, "Failed to trigger new samples.\n"); + return ret; + } + + ret = opt4060_read_raw_value(chip, chan->address, &adc_raw); + if (ret) { + dev_err(chip->dev, "Reading raw channel data failed.\n"); + return ret; + } + *val = adc_raw; + return IIO_VAL_INT; +} + +/* + * Returns the scale values used for red, green and blue. Scales the raw value + * so that for a particular test light source, typically white, the measurement + * intensity is the same across different color channels. + */ +static int opt4060_get_chan_scale(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + + switch (chan->scan_index) { + case OPT4060_RED: + /* 2.4 */ + *val = 2; + *val2 = 400000; + break; + case OPT4060_GREEN: + /* 1.0 */ + *val = 1; + *val2 = 0; + break; + case OPT4060_BLUE: + /* 1.3 */ + *val = 1; + *val2 = 300000; + break; + default: + dev_err(chip->dev, "Unexpected channel index.\n"); + return -EINVAL; + } + return IIO_VAL_INT_PLUS_MICRO; +} + +static int opt4060_calc_illuminance(struct opt4060_chip *chip, int *val) +{ + u32 lux_raw; + int ret; + + /* The green wide spectral channel is used for illuminance. */ + ret = opt4060_read_raw_value(chip, OPT4060_GREEN_MSB, &lux_raw); + if (ret) { + dev_err(chip->dev, "Reading raw channel data failed\n"); + return ret; + } + + /* Illuminance is calculated by ADC_RAW * 2.15e-3. */ + *val = DIV_U64_ROUND_CLOSEST((u64)(lux_raw * 215), 1000); + return ret; +} + +static int opt4060_read_illuminance(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + ret = opt4060_trigger_new_samples(indio_dev); + if (ret) { + dev_err(chip->dev, "Failed to trigger new samples.\n"); + return ret; + } + ret = opt4060_calc_illuminance(chip, val); + if (ret) { + dev_err(chip->dev, "Failed to calculate illuminance.\n"); + return ret; + } + + return IIO_VAL_INT; +} + +static int opt4060_set_int_time(struct opt4060_chip *chip) +{ + unsigned int regval; + int ret; + + regval = FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time); + ret = regmap_update_bits(chip->regmap, OPT4060_CTRL, + OPT4060_CTRL_CONV_TIME_MASK, regval); + if (ret) + dev_err(chip->dev, "Failed to set integration time.\n"); + + return ret; +} + +static int opt4060_power_down(struct opt4060_chip *chip) +{ + int ret; + + ret = regmap_clear_bits(chip->regmap, OPT4060_CTRL, OPT4060_CTRL_OPER_MODE_MASK); + if (ret) + dev_err(chip->dev, "Failed to power down\n"); + + return ret; +} + +static void opt4060_chip_off_action(void *chip) +{ + opt4060_power_down(chip); +} + +#define _OPT4060_COLOR_CHANNEL(_color, _mask, _ev_spec, _num_ev_spec) \ +{ \ + .type = IIO_INTENSITY, \ + .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_##_color, \ + .info_mask_separate = _mask, \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ + .address = OPT4060_##_color##_MSB, \ + .scan_index = OPT4060_##_color, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = _ev_spec, \ + .num_event_specs = _num_ev_spec, \ +} + +#define OPT4060_COLOR_CHANNEL(_color, _mask) \ + _OPT4060_COLOR_CHANNEL(_color, _mask, opt4060_event_spec, \ + ARRAY_SIZE(opt4060_event_spec)) \ + +#define OPT4060_COLOR_CHANNEL_NO_EVENTS(_color, _mask) \ + _OPT4060_COLOR_CHANNEL(_color, _mask, NULL, 0) \ + +#define OPT4060_LIGHT_CHANNEL(_channel) \ +{ \ + .type = IIO_LIGHT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ + .scan_index = OPT4060_##_channel, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_event_spec opt4060_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, +}; + +static const struct iio_chan_spec opt4060_channels[] = { + OPT4060_COLOR_CHANNEL(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL(CLEAR, BIT(IIO_CHAN_INFO_RAW)), + OPT4060_LIGHT_CHANNEL(ILLUM), + IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS), +}; + +static const struct iio_chan_spec opt4060_channels_no_events[] = { + OPT4060_COLOR_CHANNEL_NO_EVENTS(RED, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL_NO_EVENTS(GREEN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL_NO_EVENTS(BLUE, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)), + OPT4060_COLOR_CHANNEL_NO_EVENTS(CLEAR, BIT(IIO_CHAN_INFO_RAW)), + OPT4060_LIGHT_CHANNEL(ILLUM), + IIO_CHAN_SOFT_TIMESTAMP(OPT4060_NUM_CHANS), +}; + +static int opt4060_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return opt4060_read_chan_raw(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + return opt4060_get_chan_scale(indio_dev, chan, val, val2); + case IIO_CHAN_INFO_PROCESSED: + return opt4060_read_illuminance(indio_dev, chan, val); + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = opt4060_int_time_reg[chip->int_time][0]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int opt4060_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct opt4060_chip *chip = iio_priv(indio_dev); + int int_time; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + int_time = opt4060_als_time_to_index(val2); + if (int_time < 0) + return int_time; + chip->int_time = int_time; + return opt4060_set_int_time(chip); + default: + return -EINVAL; + } +} + +static int opt4060_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_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static u32 opt4060_calc_th_reg(u32 adc_val) +{ + u32 th_val, th_exp, bits; + /* + * The threshold registers take 4 bits of exponent and 12 bits of data + * ADC = TH_VAL << (8 + TH_EXP) + */ + bits = fls(adc_val); + + if (bits > 31) + th_exp = 11; /* Maximum exponent */ + else if (bits > 20) + th_exp = bits - 20; + else + th_exp = 0; + th_val = (adc_val >> (8 + th_exp)) & 0xfff; + + return (th_exp << 12) + th_val; +} + +static u32 opt4060_calc_val_from_th_reg(u32 th_reg) +{ + /* + * The threshold registers take 4 bits of exponent and 12 bits of data + * ADC = TH_VAL << (8 + TH_EXP) + */ + u32 th_val, th_exp; + + th_exp = (th_reg >> 12) & 0xf; + th_val = th_reg & 0xfff; + + return th_val << (8 + th_exp); +} + +static int opt4060_read_available(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_INT_TIME: + *length = ARRAY_SIZE(opt4060_int_time_available) * 2; + *vals = (const int *)opt4060_int_time_available; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +static ssize_t opt4060_read_ev_period(struct opt4060_chip *chip, int *val, + int *val2) +{ + int ret, pers, fault_count, int_time; + u64 uval; + + int_time = opt4060_int_time_reg[chip->int_time][0]; + + ret = regmap_read(chip->regmap, OPT4060_CTRL, &fault_count); + if (ret < 0) + return ret; + + fault_count = fault_count & OPT4060_CTRL_FAULT_COUNT_MASK; + switch (fault_count) { + case OPT4060_CTRL_FAULT_COUNT_2: + pers = 2; + break; + case OPT4060_CTRL_FAULT_COUNT_4: + pers = 4; + break; + case OPT4060_CTRL_FAULT_COUNT_8: + pers = 8; + break; + + default: + pers = 1; + break; + } + + uval = mul_u32_u32(int_time, pers); + *val = div_u64_rem(uval, MICRO, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static ssize_t opt4060_write_ev_period(struct opt4060_chip *chip, int val, + int val2) +{ + u64 uval, int_time; + unsigned int regval, fault_count_val; + + uval = mul_u32_u32(val, MICRO) + val2; + int_time = opt4060_int_time_reg[chip->int_time][0]; + + /* Check if the period is closest to 1, 2, 4 or 8 times integration time.*/ + if (uval <= int_time) + fault_count_val = OPT4060_CTRL_FAULT_COUNT_1; + else if (uval <= int_time * 2) + fault_count_val = OPT4060_CTRL_FAULT_COUNT_2; + else if (uval <= int_time * 4) + fault_count_val = OPT4060_CTRL_FAULT_COUNT_4; + else + fault_count_val = OPT4060_CTRL_FAULT_COUNT_8; + + regval = FIELD_PREP(OPT4060_CTRL_FAULT_COUNT_MASK, fault_count_val); + return regmap_update_bits(chip->regmap, OPT4060_CTRL, + OPT4060_CTRL_FAULT_COUNT_MASK, regval); +} + +static int opt4060_get_channel_sel(struct opt4060_chip *chip, int *ch_sel) +{ + int ret; + u32 regval; + + ret = regmap_read(chip->regmap, OPT4060_INT_CTRL, ®val); + if (ret) { + dev_err(chip->dev, "Failed to get channel selection.\n"); + return ret; + } + *ch_sel = FIELD_GET(OPT4060_INT_CTRL_THRESH_SEL, regval); + return ret; +} + +static int opt4060_set_channel_sel(struct opt4060_chip *chip, int ch_sel) +{ + int ret; + u32 regval; + + regval = FIELD_PREP(OPT4060_INT_CTRL_THRESH_SEL, ch_sel); + ret = regmap_update_bits(chip->regmap, OPT4060_INT_CTRL, + OPT4060_INT_CTRL_THRESH_SEL, regval); + if (ret) + dev_err(chip->dev, "Failed to set channel selection.\n"); + return ret; +} + +static int opt4060_get_thresholds(struct opt4060_chip *chip, u32 *th_lo, u32 *th_hi) +{ + int ret; + u32 regval; + + ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_LOW, ®val); + if (ret) { + dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + return ret; + } + *th_lo = opt4060_calc_val_from_th_reg(regval); + + ret = regmap_read(chip->regmap, OPT4060_THRESHOLD_HIGH, ®val); + if (ret) { + dev_err(chip->dev, "Failed to read THRESHOLD_LOW.\n"); + return ret; + } + *th_hi = opt4060_calc_val_from_th_reg(regval); + + return ret; +} + +static int opt4060_set_thresholds(struct opt4060_chip *chip, u32 th_lo, u32 th_hi) +{ + int ret; + + ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_LOW, opt4060_calc_th_reg(th_lo)); + if (ret) { + dev_err(chip->dev, "Failed to write THRESHOLD_LOW.\n"); + return ret; + } + + ret = regmap_write(chip->regmap, OPT4060_THRESHOLD_HIGH, opt4060_calc_th_reg(th_hi)); + if (ret) + dev_err(chip->dev, "Failed to write THRESHOLD_HIGH.\n"); + + return ret; +} + +static int opt4060_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 opt4060_chip *chip = iio_priv(indio_dev); + u32 th_lo, th_hi; + int ret; + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = opt4060_get_thresholds(chip, &th_lo, &th_hi); + if (ret) + return ret; + if (dir == IIO_EV_DIR_FALLING) { + *val = th_lo; + ret = IIO_VAL_INT; + } else if (dir == IIO_EV_DIR_RISING) { + *val = th_hi; + ret = IIO_VAL_INT; + } + return ret; + case IIO_EV_INFO_PERIOD: + return opt4060_read_ev_period(chip, val, val2); + default: + return -EINVAL; + } +} + +static int opt4060_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 opt4060_chip *chip = iio_priv(indio_dev); + u32 th_lo, th_hi; + int ret; + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = opt4060_get_thresholds(chip, &th_lo, &th_hi); + if (ret) + return ret; + if (dir == IIO_EV_DIR_FALLING) + th_lo = val; + else if (dir == IIO_EV_DIR_RISING) + th_hi = val; + return opt4060_set_thresholds(chip, th_lo, th_hi); + case IIO_EV_INFO_PERIOD: + return opt4060_write_ev_period(chip, val, val2); + default: + return -EINVAL; + } +} + +static int opt4060_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + int ch_sel, ch_idx = chan->scan_index; + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + ret = opt4060_get_channel_sel(chip, &ch_sel); + if (ret) + return ret; + + if (((dir == IIO_EV_DIR_FALLING) && chip->thresh_event_lo_active) || + ((dir == IIO_EV_DIR_RISING) && chip->thresh_event_hi_active)) + return ch_sel == ch_idx; + + return ret; +} + +static int opt4060_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 ch_sel, ch_idx = chan->scan_index; + struct opt4060_chip *chip = iio_priv(indio_dev); + int ret; + + guard(mutex)(&chip->event_enabling_lock); + + if (chan->type != IIO_INTENSITY) + return -EINVAL; + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + ret = opt4060_get_channel_sel(chip, &ch_sel); + if (ret) + return ret; + + if (state) { + /* Only one channel can be active at the same time */ + if ((chip->thresh_event_lo_active || chip->thresh_event_hi_active) && + (ch_idx != ch_sel)) + return -EBUSY; + if (dir == IIO_EV_DIR_FALLING) + chip->thresh_event_lo_active = true; + else if (dir == IIO_EV_DIR_RISING) + chip->thresh_event_hi_active = true; + ret = opt4060_set_channel_sel(chip, ch_idx); + if (ret) + return ret; + } else { + if (ch_idx == ch_sel) { + if (dir == IIO_EV_DIR_FALLING) + chip->thresh_event_lo_active = false; + else if (dir == IIO_EV_DIR_RISING) + chip->thresh_event_hi_active = false; + } + } + + return opt4060_set_driver_state(indio_dev, + chip->thresh_event_hi_active | + chip->thresh_event_lo_active, + false); +} + +static const struct iio_info opt4060_info = { + .read_raw = opt4060_read_raw, + .write_raw = opt4060_write_raw, + .write_raw_get_fmt = opt4060_write_raw_get_fmt, + .read_avail = opt4060_read_available, + .read_event_value = opt4060_read_event, + .write_event_value = opt4060_write_event, + .read_event_config = opt4060_read_event_config, + .write_event_config = opt4060_write_event_config, +}; + +static const struct iio_info opt4060_info_no_irq = { + .read_raw = opt4060_read_raw, + .write_raw = opt4060_write_raw, + .write_raw_get_fmt = opt4060_write_raw_get_fmt, + .read_avail = opt4060_read_available, +}; + +static int opt4060_load_defaults(struct opt4060_chip *chip) +{ + u16 reg; + int ret; + + chip->int_time = OPT4060_DEFAULT_CONVERSION_TIME; + + /* Set initial MIN/MAX thresholds */ + ret = opt4060_set_thresholds(chip, 0, UINT_MAX); + if (ret) + return ret; + + /* + * Setting auto-range, latched window for thresholds, one-shot conversion + * and quick wake-up mode as default. + */ + reg = FIELD_PREP(OPT4060_CTRL_RANGE_MASK, + OPT4060_CTRL_LIGHT_SCALE_AUTO); + reg |= FIELD_PREP(OPT4060_CTRL_CONV_TIME_MASK, chip->int_time); + reg |= FIELD_PREP(OPT4060_CTRL_OPER_MODE_MASK, + OPT4060_CTRL_OPER_MODE_ONE_SHOT); + reg |= OPT4060_CTRL_QWAKE_MASK | OPT4060_CTRL_LATCH_MASK; + + ret = regmap_write(chip->regmap, OPT4060_CTRL, reg); + if (ret) + dev_err(chip->dev, "Failed to set configuration\n"); + + return ret; +} + +static bool opt4060_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg <= OPT4060_CLEAR_LSB || reg == OPT4060_RES_CTRL; +} + +static bool opt4060_writable_reg(struct device *dev, unsigned int reg) +{ + return reg >= OPT4060_THRESHOLD_LOW || reg >= OPT4060_INT_CTRL; +} + +static bool opt4060_readonly_reg(struct device *dev, unsigned int reg) +{ + return reg == OPT4060_DEVICE_ID; +} + +static bool opt4060_readable_reg(struct device *dev, unsigned int reg) +{ + /* Volatile, writable and read-only registers are readable. */ + return opt4060_volatile_reg(dev, reg) || opt4060_writable_reg(dev, reg) || + opt4060_readonly_reg(dev, reg); +} + +static const struct regmap_config opt4060_regmap_config = { + .name = "opt4060", + .reg_bits = 8, + .val_bits = 16, + .cache_type = REGCACHE_RBTREE, + .max_register = OPT4060_DEVICE_ID, + .readable_reg = opt4060_readable_reg, + .writeable_reg = opt4060_writable_reg, + .volatile_reg = opt4060_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static const struct iio_trigger_ops opt4060_trigger_ops = { + .set_trigger_state = opt4060_trigger_set_state, +}; + +static irqreturn_t opt4060_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct opt4060_chip *chip = iio_priv(idev); + struct { + u32 chan[OPT4060_NUM_CHANS]; + aligned_s64 ts; + } raw; + int i = 0; + int chan, ret; + + /* If the trigger is not from this driver, a new sample is needed.*/ + if (iio_trigger_validate_own_device(idev->trig, idev)) + opt4060_trigger_new_samples(idev); + + memset(&raw, 0, sizeof(raw)); + + iio_for_each_active_channel(idev, chan) { + if (chan == OPT4060_ILLUM) + ret = opt4060_calc_illuminance(chip, &raw.chan[i++]); + else + ret = opt4060_read_raw_value(chip, + idev->channels[chan].address, + &raw.chan[i++]); + if (ret) { + dev_err(chip->dev, "Reading channel data failed\n"); + goto err_read; + } + } + + iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp); +err_read: + iio_trigger_notify_done(idev->trig); + return IRQ_HANDLED; +} + +static irqreturn_t opt4060_irq_thread(int irq, void *private) +{ + struct iio_dev *idev = private; + struct opt4060_chip *chip = iio_priv(idev); + int ret, dummy; + unsigned int int_res; + + ret = regmap_read(chip->regmap, OPT4060_RES_CTRL, &int_res); + if (ret < 0) { + dev_err(chip->dev, "Failed to read interrupt reasons.\n"); + return IRQ_NONE; + } + + /* Read OPT4060_CTRL to clear interrupt */ + ret = regmap_read(chip->regmap, OPT4060_CTRL, &dummy); + if (ret < 0) { + dev_err(chip->dev, "Failed to clear interrupt\n"); + return IRQ_NONE; + } + + /* Handle events */ + if (int_res & (OPT4060_RES_CTRL_FLAG_H | OPT4060_RES_CTRL_FLAG_L)) { + u64 code; + int chan = 0; + + ret = opt4060_get_channel_sel(chip, &chan); + if (ret) { + dev_err(chip->dev, "Failed to read threshold channel.\n"); + return IRQ_NONE; + } + + /* Check if the interrupt is from the lower threshold */ + if (int_res & OPT4060_RES_CTRL_FLAG_L) { + code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, + chan, + idev->channels[chan].channel2, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING); + iio_push_event(idev, code, iio_get_time_ns(idev)); + } + /* Check if the interrupt is from the upper threshold */ + if (int_res & OPT4060_RES_CTRL_FLAG_H) { + code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, + chan, + idev->channels[chan].channel2, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING); + iio_push_event(idev, code, iio_get_time_ns(idev)); + } + } + + /* Handle conversion ready */ + if (int_res & OPT4060_RES_CTRL_CONV_READY) { + /* Signal completion for potentially waiting reads */ + complete(&chip->completion); + + /* Handle data ready triggers */ + if (iio_buffer_enabled(idev)) + iio_trigger_poll_nested(chip->trig); + } + return IRQ_HANDLED; +} + +static int opt4060_setup_buffer(struct opt4060_chip *chip, struct iio_dev *idev) +{ + int ret; + + ret = devm_iio_triggered_buffer_setup(chip->dev, idev, + &iio_pollfunc_store_time, + opt4060_trigger_handler, NULL); + if (ret) + return dev_err_probe(chip->dev, ret, + "Buffer setup failed.\n"); + return ret; +} + +static int opt4060_setup_trigger(struct opt4060_chip *chip, struct iio_dev *idev) +{ + struct iio_trigger *data_trigger; + char *name; + int ret; + + data_trigger = devm_iio_trigger_alloc(chip->dev, "%s-data-ready-dev%d", + idev->name, iio_device_id(idev)); + if (!data_trigger) + return -ENOMEM; + + /* + * The data trigger allows for sample capture on each new conversion + * ready interrupt. + */ + chip->trig = data_trigger; + data_trigger->ops = &opt4060_trigger_ops; + iio_trigger_set_drvdata(data_trigger, idev); + ret = devm_iio_trigger_register(chip->dev, data_trigger); + if (ret) + return dev_err_probe(chip->dev, ret, + "Data ready trigger registration failed\n"); + + name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-opt4060", + dev_name(chip->dev)); + if (!name) + return dev_err_probe(chip->dev, -ENOMEM, "Failed to alloc chip name\n"); + + ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, opt4060_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + name, idev); + if (ret) + return dev_err_probe(chip->dev, ret, "Could not request IRQ\n"); + + init_completion(&chip->completion); + + ret = devm_mutex_init(chip->dev, &chip->irq_setting_lock); + if (ret) + return ret; + + ret = devm_mutex_init(chip->dev, &chip->event_enabling_lock); + if (ret) + return ret; + + ret = regmap_write_bits(chip->regmap, OPT4060_INT_CTRL, + OPT4060_INT_CTRL_OUTPUT, + OPT4060_INT_CTRL_OUTPUT); + if (ret) + return dev_err_probe(chip->dev, ret, + "Failed to set interrupt as output\n"); + + return 0; +} + +static int opt4060_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct opt4060_chip *chip; + struct iio_dev *indio_dev; + int ret; + unsigned int regval, dev_id; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable vdd supply\n"); + + chip->regmap = devm_regmap_init_i2c(client, &opt4060_regmap_config); + if (IS_ERR(chip->regmap)) + return dev_err_probe(dev, PTR_ERR(chip->regmap), + "regmap initialization failed\n"); + + chip->dev = dev; + chip->irq = client->irq; + + ret = regmap_reinit_cache(chip->regmap, &opt4060_regmap_config); + if (ret) + return dev_err_probe(dev, ret, + "failed to reinit regmap cache\n"); + + ret = regmap_read(chip->regmap, OPT4060_DEVICE_ID, ®val); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to read the device ID register\n"); + + dev_id = FIELD_GET(OPT4060_DEVICE_ID_MASK, regval); + if (dev_id != OPT4060_DEVICE_ID_VAL) + dev_info(dev, "Device ID: %#04x unknown\n", dev_id); + + if (chip->irq) { + indio_dev->info = &opt4060_info; + indio_dev->channels = opt4060_channels; + indio_dev->num_channels = ARRAY_SIZE(opt4060_channels); + } else { + indio_dev->info = &opt4060_info_no_irq; + indio_dev->channels = opt4060_channels_no_events; + indio_dev->num_channels = ARRAY_SIZE(opt4060_channels_no_events); + } + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = "opt4060"; + + ret = opt4060_load_defaults(chip); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to set sensor defaults\n"); + + ret = devm_add_action_or_reset(dev, opt4060_chip_off_action, chip); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to setup power off action\n"); + + ret = opt4060_setup_buffer(chip, indio_dev); + if (ret) + return ret; + + if (chip->irq) { + ret = opt4060_setup_trigger(chip, indio_dev); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct i2c_device_id opt4060_id[] = { + { "opt4060", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, opt4060_id); + +static const struct of_device_id opt4060_of_match[] = { + { .compatible = "ti,opt4060" }, + { } +}; +MODULE_DEVICE_TABLE(of, opt4060_of_match); + +static struct i2c_driver opt4060_driver = { + .driver = { + .name = "opt4060", + .of_match_table = opt4060_of_match, + }, + .probe = opt4060_probe, + .id_table = opt4060_id, +}; +module_i2c_driver(opt4060_driver); + +MODULE_AUTHOR("Per-Daniel Olsson <perdaniel.olsson@axis.com>"); +MODULE_DESCRIPTION("Texas Instruments OPT4060 RGBW color sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index b920bf82c102..8885852bef22 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -456,14 +456,14 @@ static const struct dev_pm_ops pa12203001_pm_ops = { static const struct acpi_device_id pa12203001_acpi_match[] = { { "TXCPA122", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, pa12203001_acpi_match); static const struct i2c_device_id pa12203001_id[] = { { "txcpa122" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, pa12203001_id); diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c deleted file mode 100644 index fa35dd32700c..000000000000 --- a/drivers/iio/light/rohm-bu27008.c +++ /dev/null @@ -1,1635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * ROHM Colour Sensor driver for - * - BU27008 RGBC sensor - * - BU27010 RGBC + Flickering sensor - * - * Copyright (c) 2023, ROHM Semiconductor. - */ - -#include <linux/bitfield.h> -#include <linux/bitops.h> -#include <linux/device.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/property.h> -#include <linux/regmap.h> -#include <linux/regulator/consumer.h> -#include <linux/units.h> - -#include <linux/iio/iio.h> -#include <linux/iio/iio-gts-helper.h> -#include <linux/iio/trigger.h> -#include <linux/iio/trigger_consumer.h> -#include <linux/iio/triggered_buffer.h> - -/* - * A word about register address and mask definitions. - * - * At a quick glance to the data-sheet register tables, the BU27010 has all the - * registers that the BU27008 has. On top of that the BU27010 adds couple of new - * ones. - * - * So, all definitions BU27008_REG_* are there also for BU27010 but none of the - * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds - * some features (Flicker FIFO, more power control) on top of the BU27008. - * - * Unfortunately, some of the wheel has been re-invented. Even though the names - * of the registers have stayed the same, pretty much all of the functionality - * provided by the registers has changed place. Contents of all MODE_CONTROL - * registers on BU27008 and BU27010 are different. - * - * Chip-specific mapping from register addresses/bits to functionality is done - * in bu27_chip_data structures. - */ -#define BU27008_REG_SYSTEM_CONTROL 0x40 -#define BU27008_MASK_SW_RESET BIT(7) -#define BU27008_MASK_PART_ID GENMASK(5, 0) -#define BU27008_ID 0x1a -#define BU27008_REG_MODE_CONTROL1 0x41 -#define BU27008_MASK_MEAS_MODE GENMASK(2, 0) -#define BU27008_MASK_CHAN_SEL GENMASK(3, 2) - -#define BU27008_REG_MODE_CONTROL2 0x42 -#define BU27008_MASK_RGBC_GAIN GENMASK(7, 3) -#define BU27008_MASK_IR_GAIN_LO GENMASK(2, 0) -#define BU27008_SHIFT_IR_GAIN 3 - -#define BU27008_REG_MODE_CONTROL3 0x43 -#define BU27008_MASK_VALID BIT(7) -#define BU27008_MASK_INT_EN BIT(1) -#define BU27008_INT_EN BU27008_MASK_INT_EN -#define BU27008_INT_DIS 0 -#define BU27008_MASK_MEAS_EN BIT(0) -#define BU27008_MEAS_EN BIT(0) -#define BU27008_MEAS_DIS 0 - -#define BU27008_REG_DATA0_LO 0x50 -#define BU27008_REG_DATA1_LO 0x52 -#define BU27008_REG_DATA2_LO 0x54 -#define BU27008_REG_DATA3_LO 0x56 -#define BU27008_REG_DATA3_HI 0x57 -#define BU27008_REG_MANUFACTURER_ID 0x92 -#define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID - -/* BU27010 specific definitions */ - -#define BU27010_MASK_SW_RESET BIT(7) -#define BU27010_ID 0x1b -#define BU27010_REG_POWER 0x3e -#define BU27010_MASK_POWER BIT(0) - -#define BU27010_REG_RESET 0x3f -#define BU27010_MASK_RESET BIT(0) -#define BU27010_RESET_RELEASE BU27010_MASK_RESET - -#define BU27010_MASK_MEAS_EN BIT(1) - -#define BU27010_MASK_CHAN_SEL GENMASK(7, 6) -#define BU27010_MASK_MEAS_MODE GENMASK(5, 4) -#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0) - -#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6) -#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4) -#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2) -#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0) - -#define BU27010_MASK_FLC_MODE BIT(7) -#define BU27010_MASK_FLC_GAIN GENMASK(4, 0) - -#define BU27010_REG_MODE_CONTROL4 0x44 -/* If flicker is ever to be supported the IRQ must be handled as a field */ -#define BU27010_IRQ_DIS_ALL GENMASK(1, 0) -#define BU27010_DRDY_EN BIT(0) -#define BU27010_MASK_INT_SEL GENMASK(1, 0) - -#define BU27010_REG_MODE_CONTROL5 0x45 -#define BU27010_MASK_RGB_VALID BIT(7) -#define BU27010_MASK_FLC_VALID BIT(6) -#define BU27010_MASK_WAIT_EN BIT(3) -#define BU27010_MASK_FIFO_EN BIT(2) -#define BU27010_MASK_RGB_EN BIT(1) -#define BU27010_MASK_FLC_EN BIT(0) - -#define BU27010_REG_DATA_FLICKER_LO 0x56 -#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0) -#define BU27010_REG_FLICKER_COUNT 0x5a -#define BU27010_REG_FIFO_LEVEL_LO 0x5b -#define BU27010_MASK_FIFO_LEVEL_HI BIT(0) -#define BU27010_REG_FIFO_DATA_LO 0x5d -#define BU27010_REG_FIFO_DATA_HI 0x5e -#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0) -#define BU27010_REG_MANUFACTURER_ID 0x92 -#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID - -/** - * enum bu27008_chan_type - BU27008 channel types - * @BU27008_RED: Red channel. Always via data0. - * @BU27008_GREEN: Green channel. Always via data1. - * @BU27008_BLUE: Blue channel. Via data2 (when used). - * @BU27008_CLEAR: Clear channel. Via data2 or data3 (when used). - * @BU27008_IR: IR channel. Via data3 (when used). - * @BU27008_LUX: Illuminance channel, computed using RGB and IR. - * @BU27008_NUM_CHANS: Number of channel types. - */ -enum bu27008_chan_type { - BU27008_RED, - BU27008_GREEN, - BU27008_BLUE, - BU27008_CLEAR, - BU27008_IR, - BU27008_LUX, - BU27008_NUM_CHANS -}; - -/** - * enum bu27008_chan - BU27008 physical data channel - * @BU27008_DATA0: Always red. - * @BU27008_DATA1: Always green. - * @BU27008_DATA2: Blue or clear. - * @BU27008_DATA3: IR or clear. - * @BU27008_NUM_HW_CHANS: Number of physical channels - */ -enum bu27008_chan { - BU27008_DATA0, - BU27008_DATA1, - BU27008_DATA2, - BU27008_DATA3, - BU27008_NUM_HW_CHANS -}; - -/* We can always measure red and green at same time */ -#define ALWAYS_SCANNABLE (BIT(BU27008_RED) | BIT(BU27008_GREEN)) - -/* We use these data channel configs. Ensure scan_masks below follow them too */ -#define BU27008_BLUE2_CLEAR3 0x0 /* buffer is R, G, B, C */ -#define BU27008_CLEAR2_IR3 0x1 /* buffer is R, G, C, IR */ -#define BU27008_BLUE2_IR3 0x2 /* buffer is R, G, B, IR */ - -static const unsigned long bu27008_scan_masks[] = { - /* buffer is R, G, B, C */ - ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_CLEAR), - /* buffer is R, G, C, IR */ - ALWAYS_SCANNABLE | BIT(BU27008_CLEAR) | BIT(BU27008_IR), - /* buffer is R, G, B, IR */ - ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR), - /* buffer is R, G, B, IR, LUX */ - ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR) | BIT(BU27008_LUX), - 0 -}; - -/* - * Available scales with gain 1x - 1024x, timings 55, 100, 200, 400 mS - * Time impacts to gain: 1x, 2x, 4x, 8x. - * - * => Max total gain is HWGAIN * gain by integration time (8 * 1024) = 8192 - * - * Max amplification is (HWGAIN * MAX integration-time multiplier) 1024 * 8 - * = 8192. With NANO scale we get rid of accuracy loss when we start with the - * scale 16.0 for HWGAIN1, INT-TIME 55 mS. This way the nano scale for MAX - * total gain 8192 will be 1953125 - */ -#define BU27008_SCALE_1X 16 - -/* - * On BU27010 available scales with gain 1x - 4096x, - * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x. - * - * => Max total gain is HWGAIN * gain by integration time (8 * 4096) - * - * Using NANO precision for scale we must use scale 64x corresponding gain 1x - * to avoid precision loss. - */ -#define BU27010_SCALE_1X 64 - -/* See the data sheet for the "Gain Setting" table */ -#define BU27008_GSEL_1X 0x00 -#define BU27008_GSEL_4X 0x08 -#define BU27008_GSEL_8X 0x09 -#define BU27008_GSEL_16X 0x0a -#define BU27008_GSEL_32X 0x0b -#define BU27008_GSEL_64X 0x0c -#define BU27008_GSEL_256X 0x18 -#define BU27008_GSEL_512X 0x19 -#define BU27008_GSEL_1024X 0x1a - -static const struct iio_gain_sel_pair bu27008_gains[] = { - GAIN_SCALE_GAIN(1, BU27008_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27008_GSEL_4X), - GAIN_SCALE_GAIN(8, BU27008_GSEL_8X), - GAIN_SCALE_GAIN(16, BU27008_GSEL_16X), - GAIN_SCALE_GAIN(32, BU27008_GSEL_32X), - GAIN_SCALE_GAIN(64, BU27008_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27008_GSEL_256X), - GAIN_SCALE_GAIN(512, BU27008_GSEL_512X), - GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), -}; - -static const struct iio_gain_sel_pair bu27008_gains_ir[] = { - GAIN_SCALE_GAIN(2, BU27008_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27008_GSEL_4X), - GAIN_SCALE_GAIN(8, BU27008_GSEL_8X), - GAIN_SCALE_GAIN(16, BU27008_GSEL_16X), - GAIN_SCALE_GAIN(32, BU27008_GSEL_32X), - GAIN_SCALE_GAIN(64, BU27008_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27008_GSEL_256X), - GAIN_SCALE_GAIN(512, BU27008_GSEL_512X), - GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), -}; - -#define BU27010_GSEL_1X 0x00 /* 000000 */ -#define BU27010_GSEL_4X 0x08 /* 001000 */ -#define BU27010_GSEL_16X 0x09 /* 001001 */ -#define BU27010_GSEL_64X 0x0e /* 001110 */ -#define BU27010_GSEL_256X 0x1e /* 011110 */ -#define BU27010_GSEL_1024X 0x2e /* 101110 */ -#define BU27010_GSEL_4096X 0x3f /* 111111 */ - -static const struct iio_gain_sel_pair bu27010_gains[] = { - GAIN_SCALE_GAIN(1, BU27010_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), - GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), - GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), - GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), - GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), -}; - -static const struct iio_gain_sel_pair bu27010_gains_ir[] = { - GAIN_SCALE_GAIN(2, BU27010_GSEL_1X), - GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), - GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), - GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), - GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), - GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), - GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), -}; - -#define BU27008_MEAS_MODE_100MS 0x00 -#define BU27008_MEAS_MODE_55MS 0x01 -#define BU27008_MEAS_MODE_200MS 0x02 -#define BU27008_MEAS_MODE_400MS 0x04 - -#define BU27010_MEAS_MODE_100MS 0x00 -#define BU27010_MEAS_MODE_55MS 0x03 -#define BU27010_MEAS_MODE_200MS 0x01 -#define BU27010_MEAS_MODE_400MS 0x02 - -#define BU27008_MEAS_TIME_MAX_MS 400 - -static const struct iio_itime_sel_mul bu27008_itimes[] = { - GAIN_SCALE_ITIME_US(400000, BU27008_MEAS_MODE_400MS, 8), - GAIN_SCALE_ITIME_US(200000, BU27008_MEAS_MODE_200MS, 4), - GAIN_SCALE_ITIME_US(100000, BU27008_MEAS_MODE_100MS, 2), - GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1), -}; - -static const struct iio_itime_sel_mul bu27010_itimes[] = { - GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8), - GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4), - GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2), - GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1), -}; - -/* - * All the RGBC channels share the same gain. - * IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this - * would yield quite complex gain setting. Especially since not all bit - * compinations are supported. And in any case setting GAIN for RGBC will - * always also change the IR-gain. - * - * On top of this, the selector '0' which corresponds to hw-gain 1X on RGBC, - * corresponds to gain 2X on IR. Rest of the selctors correspond to same gains - * though. This, however, makes it not possible to use shared gain for all - * RGBC and IR settings even though they are all changed at the one go. - */ -#define BU27008_CHAN(color, data, separate_avail) \ -{ \ - .type = IIO_INTENSITY, \ - .modified = 1, \ - .channel2 = IIO_MOD_LIGHT_##color, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_separate_available = (separate_avail), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ - .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME), \ - .address = BU27008_REG_##data##_LO, \ - .scan_index = BU27008_##color, \ - .scan_type = { \ - .sign = 'u', \ - .realbits = 16, \ - .storagebits = 16, \ - .endianness = IIO_LE, \ - }, \ -} - -/* For raw reads we always configure DATA3 for CLEAR */ -static const struct iio_chan_spec bu27008_channels[] = { - BU27008_CHAN(RED, DATA0, BIT(IIO_CHAN_INFO_SCALE)), - BU27008_CHAN(GREEN, DATA1, BIT(IIO_CHAN_INFO_SCALE)), - BU27008_CHAN(BLUE, DATA2, BIT(IIO_CHAN_INFO_SCALE)), - BU27008_CHAN(CLEAR, DATA2, BIT(IIO_CHAN_INFO_SCALE)), - /* - * We don't allow setting scale for IR (because of shared gain bits). - * Hence we don't advertise available ones either. - */ - BU27008_CHAN(IR, DATA3, 0), - { - .type = IIO_LIGHT, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), - .channel = BU27008_LUX, - .scan_index = BU27008_LUX, - .scan_type = { - .sign = 'u', - .realbits = 64, - .storagebits = 64, - .endianness = IIO_CPU, - }, - }, - IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS), -}; - -struct bu27008_data; - -struct bu27_chip_data { - const char *name; - int (*chip_init)(struct bu27008_data *data); - int (*get_gain_sel)(struct bu27008_data *data, int *sel); - int (*write_gain_sel)(struct bu27008_data *data, int sel); - const struct regmap_config *regmap_cfg; - const struct iio_gain_sel_pair *gains; - const struct iio_gain_sel_pair *gains_ir; - const struct iio_itime_sel_mul *itimes; - int num_gains; - int num_gains_ir; - int num_itimes; - int scale1x; - - int drdy_en_reg; - int drdy_en_mask; - int meas_en_reg; - int meas_en_mask; - int valid_reg; - int chan_sel_reg; - int chan_sel_mask; - int int_time_mask; - u8 part_id; -}; - -struct bu27008_data { - const struct bu27_chip_data *cd; - struct regmap *regmap; - struct iio_trigger *trig; - struct device *dev; - struct iio_gts gts; - struct iio_gts gts_ir; - int irq; - - /* - * Prevent changing gain/time config when scale is read/written. - * Similarly, protect the integration_time read/change sequence. - * Prevent changing gain/time when data is read. - */ - struct mutex mutex; -}; - -static const struct regmap_range bu27008_volatile_ranges[] = { - { - .range_min = BU27008_REG_SYSTEM_CONTROL, /* SWRESET */ - .range_max = BU27008_REG_SYSTEM_CONTROL, - }, { - .range_min = BU27008_REG_MODE_CONTROL3, /* VALID */ - .range_max = BU27008_REG_MODE_CONTROL3, - }, { - .range_min = BU27008_REG_DATA0_LO, /* DATA */ - .range_max = BU27008_REG_DATA3_HI, - }, -}; - -static const struct regmap_range bu27010_volatile_ranges[] = { - { - .range_min = BU27010_REG_RESET, /* RSTB */ - .range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */ - }, { - .range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */ - .range_max = BU27010_REG_MODE_CONTROL5, - }, { - .range_min = BU27008_REG_DATA0_LO, - .range_max = BU27010_REG_FIFO_DATA_HI, - }, -}; - -static const struct regmap_access_table bu27008_volatile_regs = { - .yes_ranges = &bu27008_volatile_ranges[0], - .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges), -}; - -static const struct regmap_access_table bu27010_volatile_regs = { - .yes_ranges = &bu27010_volatile_ranges[0], - .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges), -}; - -static const struct regmap_range bu27008_read_only_ranges[] = { - { - .range_min = BU27008_REG_DATA0_LO, - .range_max = BU27008_REG_DATA3_HI, - }, { - .range_min = BU27008_REG_MANUFACTURER_ID, - .range_max = BU27008_REG_MANUFACTURER_ID, - }, -}; - -static const struct regmap_range bu27010_read_only_ranges[] = { - { - .range_min = BU27008_REG_DATA0_LO, - .range_max = BU27010_REG_FIFO_DATA_HI, - }, { - .range_min = BU27010_REG_MANUFACTURER_ID, - .range_max = BU27010_REG_MANUFACTURER_ID, - } -}; - -static const struct regmap_access_table bu27008_ro_regs = { - .no_ranges = &bu27008_read_only_ranges[0], - .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges), -}; - -static const struct regmap_access_table bu27010_ro_regs = { - .no_ranges = &bu27010_read_only_ranges[0], - .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges), -}; - -static const struct regmap_config bu27008_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = BU27008_REG_MAX, - .cache_type = REGCACHE_RBTREE, - .volatile_table = &bu27008_volatile_regs, - .wr_table = &bu27008_ro_regs, - /* - * All register writes are serialized by the mutex which protects the - * scale setting/getting. This is needed because scale is combined by - * gain and integration time settings and we need to ensure those are - * not read / written when scale is being computed. - * - * As a result of this serializing, we don't need regmap locking. Note, - * this is not true if we add any configurations which are not - * serialized by the mutex and which may need for example a protected - * read-modify-write cycle (eg. regmap_update_bits()). Please, revise - * this when adding features to the driver. - */ - .disable_locking = true, -}; - -static const struct regmap_config bu27010_regmap = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = BU27010_REG_MAX, - .cache_type = REGCACHE_RBTREE, - .volatile_table = &bu27010_volatile_regs, - .wr_table = &bu27010_ro_regs, - .disable_locking = true, -}; - -static int bu27008_write_gain_sel(struct bu27008_data *data, int sel) -{ - int regval; - - regval = FIELD_PREP(BU27008_MASK_RGBC_GAIN, sel); - - /* - * We do always set also the LOW bits of IR-gain because othervice we - * would risk resulting an invalid GAIN register value. - * - * We could allow setting separate gains for RGBC and IR when the - * values were such that HW could support both gain settings. - * Eg, when the shared bits were same for both gain values. - * - * This, however, has a negligible benefit compared to the increased - * software complexity when we would need to go through the gains - * for both channels separately when the integration time changes. - * This would end up with nasty logic for computing gain values for - * both channels - and rejecting them if shared bits changed. - * - * We should then build the logic by guessing what a user prefers. - * RGBC or IR gains correctly set while other jumps to odd value? - * Maybe look-up a value where both gains are somehow optimized - * <what this somehow is, is ATM unknown to us>. Or maybe user would - * expect us to reject changes when optimal gains can't be set to both - * channels w/given integration time. At best that would result - * solution that works well for a very specific subset of - * configurations but causes unexpected corner-cases. - * - * So, we keep it simple. Always set same selector to IR and RGBC. - * We disallow setting IR (as I expect that most of the users are - * interested in RGBC). This way we can show the user that the scales - * for RGBC and IR channels are different (1X Vs 2X with sel 0) while - * still keeping the operation deterministic. - */ - regval |= FIELD_PREP(BU27008_MASK_IR_GAIN_LO, sel); - - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL2, - BU27008_MASK_RGBC_GAIN, regval); -} - -static int bu27010_write_gain_sel(struct bu27008_data *data, int sel) -{ - unsigned int regval; - int ret, chan_selector; - - /* - * Gain 'selector' is composed of two registers. Selector is 6bit value, - * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and - * two low bits being the channel specific gain in MODE_CONTROL2. - * - * Let's take the 4 high bits of whole 6 bit selector, and prepare - * the MODE_CONTROL1 value (RGBC gain part). - */ - regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2)); - - ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - BU27010_MASK_RGBC_GAIN, regval); - if (ret) - return ret; - - /* - * Two low two bits of the selector must be written for all 4 - * channels in the MODE_CONTROL2 register. Copy these two bits for - * all channels. - */ - chan_selector = sel & GENMASK(1, 0); - - regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector); - regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector); - regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector); - regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector); - - return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval); -} - -static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel) -{ - int ret; - - /* - * If we always "lock" the gain selectors for all channels to prevent - * unsupported configs, then it does not matter which channel is used - * we can just return selector from any of them. - * - * This, however is not true if we decide to support only 4X and 16X - * and then individual gains for channels. Currently this is not the - * case. - * - * If we some day decide to support individual gains, then we need to - * have channel information here. - */ - - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); - if (ret) - return ret; - - *sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel); - - return 0; -} - -static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel) -{ - int ret, tmp; - - /* - * We always "lock" the gain selectors for all channels to prevent - * unsupported configs. It does not matter which channel is used - * we can just return selector from any of them. - * - * Read the channel0 gain. - */ - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); - if (ret) - return ret; - - *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel); - - /* Read the shared gain */ - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp); - if (ret) - return ret; - - /* - * The gain selector is made as a combination of common RGBC gain and - * the channel specific gain. The channel specific gain forms the low - * bits of selector and RGBC gain is appended right after it. - * - * Compose the selector from channel0 gain and shared RGBC gain. - */ - *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN); - - return ret; -} - -static int bu27008_chip_init(struct bu27008_data *data) -{ - int ret; - - ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, - BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); - - /* - * The data-sheet does not tell how long performing the IC reset takes. - * However, the data-sheet says the minimum time it takes the IC to be - * able to take inputs after power is applied, is 100 uS. I'd assume - * > 1 mS is enough. - */ - msleep(1); - - ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg); - if (ret) - dev_err(data->dev, "Failed to reinit reg cache\n"); - - return ret; -} - -static int bu27010_chip_init(struct bu27008_data *data) -{ - int ret; - - ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, - BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); - - msleep(1); - - /* Power ON*/ - ret = regmap_write_bits(data->regmap, BU27010_REG_POWER, - BU27010_MASK_POWER, BU27010_MASK_POWER); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor power-on failed\n"); - - msleep(1); - - /* Release blocks from reset */ - ret = regmap_write_bits(data->regmap, BU27010_REG_RESET, - BU27010_MASK_RESET, BU27010_RESET_RELEASE); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor powering failed\n"); - - msleep(1); - - /* - * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ - * enabling is not a bit mask where individual IRQs could be enabled but - * a field which values are: - * 00 => IRQs disabled - * 01 => Data-ready (RGBC/IR) - * 10 => Data-ready (flicker) - * 11 => Flicker FIFO - * - * So, only one IRQ can be enabled at a time and enabling for example - * flicker FIFO would automagically disable data-ready IRQ. - * - * Currently the driver does not support the flicker. Hence, we can - * just treat the RGBC data-ready as single bit which can be enabled / - * disabled. This works for as long as the second bit in the field - * stays zero. Here we ensure it gets zeroed. - */ - return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4, - BU27010_IRQ_DIS_ALL); -} - -static const struct bu27_chip_data bu27010_chip = { - .name = "bu27010", - .chip_init = bu27010_chip_init, - .get_gain_sel = bu27010_get_gain_sel, - .write_gain_sel = bu27010_write_gain_sel, - .regmap_cfg = &bu27010_regmap, - .gains = &bu27010_gains[0], - .gains_ir = &bu27010_gains_ir[0], - .itimes = &bu27010_itimes[0], - .num_gains = ARRAY_SIZE(bu27010_gains), - .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir), - .num_itimes = ARRAY_SIZE(bu27010_itimes), - .scale1x = BU27010_SCALE_1X, - .drdy_en_reg = BU27010_REG_MODE_CONTROL4, - .drdy_en_mask = BU27010_DRDY_EN, - .meas_en_reg = BU27010_REG_MODE_CONTROL5, - .meas_en_mask = BU27010_MASK_MEAS_EN, - .valid_reg = BU27010_REG_MODE_CONTROL5, - .chan_sel_reg = BU27008_REG_MODE_CONTROL1, - .chan_sel_mask = BU27010_MASK_CHAN_SEL, - .int_time_mask = BU27010_MASK_MEAS_MODE, - .part_id = BU27010_ID, -}; - -static const struct bu27_chip_data bu27008_chip = { - .name = "bu27008", - .chip_init = bu27008_chip_init, - .get_gain_sel = bu27008_get_gain_sel, - .write_gain_sel = bu27008_write_gain_sel, - .regmap_cfg = &bu27008_regmap, - .gains = &bu27008_gains[0], - .gains_ir = &bu27008_gains_ir[0], - .itimes = &bu27008_itimes[0], - .num_gains = ARRAY_SIZE(bu27008_gains), - .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir), - .num_itimes = ARRAY_SIZE(bu27008_itimes), - .scale1x = BU27008_SCALE_1X, - .drdy_en_reg = BU27008_REG_MODE_CONTROL3, - .drdy_en_mask = BU27008_MASK_INT_EN, - .valid_reg = BU27008_REG_MODE_CONTROL3, - .meas_en_reg = BU27008_REG_MODE_CONTROL3, - .meas_en_mask = BU27008_MASK_MEAS_EN, - .chan_sel_reg = BU27008_REG_MODE_CONTROL3, - .chan_sel_mask = BU27008_MASK_CHAN_SEL, - .int_time_mask = BU27008_MASK_MEAS_MODE, - .part_id = BU27008_ID, -}; - -#define BU27008_MAX_VALID_RESULT_WAIT_US 50000 -#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000 - -static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val) -{ - int ret, valid; - __le16 tmp; - - ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg, - valid, (valid & BU27008_MASK_VALID), - BU27008_VALID_RESULT_WAIT_QUANTA_US, - BU27008_MAX_VALID_RESULT_WAIT_US); - if (ret) - return ret; - - ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp)); - if (ret) - dev_err(data->dev, "Reading channel data failed\n"); - - *val = le16_to_cpu(tmp); - - return ret; -} - -static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain) -{ - int ret, sel; - - ret = data->cd->get_gain_sel(data, &sel); - if (ret) - return ret; - - ret = iio_gts_find_gain_by_sel(gts, sel); - if (ret < 0) { - dev_err(data->dev, "unknown gain value 0x%x\n", sel); - return ret; - } - - *gain = ret; - - return 0; -} - -static int bu27008_set_gain(struct bu27008_data *data, int gain) -{ - int ret; - - ret = iio_gts_find_sel_by_gain(&data->gts, gain); - if (ret < 0) - return ret; - - return data->cd->write_gain_sel(data, ret); -} - -static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel) -{ - int ret, val; - - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val); - if (ret) - return ret; - - val &= data->cd->int_time_mask; - val >>= ffs(data->cd->int_time_mask) - 1; - - *sel = val; - - return 0; -} - -static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel) -{ - sel <<= ffs(data->cd->int_time_mask) - 1; - - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - data->cd->int_time_mask, sel); -} - -static int bu27008_get_int_time_us(struct bu27008_data *data) -{ - int ret, sel; - - ret = bu27008_get_int_time_sel(data, &sel); - if (ret) - return ret; - - return iio_gts_find_int_time_by_sel(&data->gts, sel); -} - -static int _bu27008_get_scale(struct bu27008_data *data, bool ir, int *val, - int *val2) -{ - struct iio_gts *gts; - int gain, ret; - - if (ir) - gts = &data->gts_ir; - else - gts = &data->gts; - - ret = bu27008_get_gain(data, gts, &gain); - if (ret) - return ret; - - ret = bu27008_get_int_time_us(data); - if (ret < 0) - return ret; - - return iio_gts_get_scale(gts, gain, ret, val, val2); -} - -static int bu27008_get_scale(struct bu27008_data *data, bool ir, int *val, - int *val2) -{ - int ret; - - mutex_lock(&data->mutex); - ret = _bu27008_get_scale(data, ir, val, val2); - mutex_unlock(&data->mutex); - - return ret; -} - -static int bu27008_set_int_time(struct bu27008_data *data, int time) -{ - int ret; - - ret = iio_gts_find_sel_by_int_time(&data->gts, time); - if (ret < 0) - return ret; - - return bu27008_set_int_time_sel(data, ret); -} - -/* Try to change the time so that the scale is maintained */ -static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new) -{ - int ret, old_time_sel, new_time_sel, old_gain, new_gain; - - mutex_lock(&data->mutex); - - ret = bu27008_get_int_time_sel(data, &old_time_sel); - if (ret < 0) - goto unlock_out; - - if (!iio_gts_valid_time(&data->gts, int_time_new)) { - dev_dbg(data->dev, "Unsupported integration time %u\n", - int_time_new); - - ret = -EINVAL; - goto unlock_out; - } - - /* If we already use requested time, then we're done */ - new_time_sel = iio_gts_find_sel_by_int_time(&data->gts, int_time_new); - if (new_time_sel == old_time_sel) - goto unlock_out; - - ret = bu27008_get_gain(data, &data->gts, &old_gain); - if (ret) - goto unlock_out; - - ret = iio_gts_find_new_gain_sel_by_old_gain_time(&data->gts, old_gain, - old_time_sel, new_time_sel, &new_gain); - if (ret) { - int scale1, scale2; - bool ok; - - _bu27008_get_scale(data, false, &scale1, &scale2); - dev_dbg(data->dev, - "Can't support time %u with current scale %u %u\n", - int_time_new, scale1, scale2); - - if (new_gain < 0) - goto unlock_out; - - /* - * If caller requests for integration time change and we - * can't support the scale - then the caller should be - * prepared to 'pick up the pieces and deal with the - * fact that the scale changed'. - */ - ret = iio_find_closest_gain_low(&data->gts, new_gain, &ok); - if (!ok) - dev_dbg(data->dev, "optimal gain out of range\n"); - - if (ret < 0) { - dev_dbg(data->dev, - "Total gain increase. Risk of saturation"); - ret = iio_gts_get_min_gain(&data->gts); - if (ret < 0) - goto unlock_out; - } - new_gain = ret; - dev_dbg(data->dev, "scale changed, new gain %u\n", new_gain); - } - - ret = bu27008_set_gain(data, new_gain); - if (ret) - goto unlock_out; - - ret = bu27008_set_int_time(data, int_time_new); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; -} - -static int bu27008_meas_set(struct bu27008_data *data, bool enable) -{ - if (enable) - return regmap_set_bits(data->regmap, data->cd->meas_en_reg, - data->cd->meas_en_mask); - return regmap_clear_bits(data->regmap, data->cd->meas_en_reg, - data->cd->meas_en_mask); -} - -static int bu27008_chan_cfg(struct bu27008_data *data, - struct iio_chan_spec const *chan) -{ - int chan_sel; - - if (chan->scan_index == BU27008_BLUE) - chan_sel = BU27008_BLUE2_CLEAR3; - else - chan_sel = BU27008_CLEAR2_IR3; - - /* - * prepare bitfield for channel sel. The FIELD_PREP works only when - * mask is constant. In our case the mask is assigned based on the - * chip type. Hence the open-coded FIELD_PREP here. We don't bother - * zeroing the irrelevant bits though - update_bits takes care of that. - */ - chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - - return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, - BU27008_MASK_CHAN_SEL, chan_sel); -} - -static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, - struct iio_chan_spec const *chan, int *val, int *val2) -{ - int ret, int_time; - - ret = bu27008_chan_cfg(data, chan); - if (ret) - return ret; - - ret = bu27008_meas_set(data, true); - if (ret) - return ret; - - ret = bu27008_get_int_time_us(data); - if (ret < 0) - int_time = BU27008_MEAS_TIME_MAX_MS; - else - int_time = ret / USEC_PER_MSEC; - - msleep(int_time); - - ret = bu27008_chan_read_data(data, chan->address, val); - if (!ret) - ret = IIO_VAL_INT; - - if (bu27008_meas_set(data, false)) - dev_warn(data->dev, "measurement disabling failed\n"); - - return ret; -} - -#define BU27008_LUX_DATA_RED 0 -#define BU27008_LUX_DATA_GREEN 1 -#define BU27008_LUX_DATA_BLUE 2 -#define BU27008_LUX_DATA_IR 3 -#define LUX_DATA_SIZE (BU27008_NUM_HW_CHANS * sizeof(__le16)) - -static int bu27008_read_lux_chans(struct bu27008_data *data, unsigned int time, - __le16 *chan_data) -{ - int ret, chan_sel, tmpret, valid; - - chan_sel = BU27008_BLUE2_IR3 << (ffs(data->cd->chan_sel_mask) - 1); - - ret = regmap_update_bits(data->regmap, data->cd->chan_sel_reg, - data->cd->chan_sel_mask, chan_sel); - if (ret) - return ret; - - ret = bu27008_meas_set(data, true); - if (ret) - return ret; - - msleep(time / USEC_PER_MSEC); - - ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg, - valid, (valid & BU27008_MASK_VALID), - BU27008_VALID_RESULT_WAIT_QUANTA_US, - BU27008_MAX_VALID_RESULT_WAIT_US); - if (ret) - goto out; - - ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, chan_data, - LUX_DATA_SIZE); - if (ret) - goto out; -out: - tmpret = bu27008_meas_set(data, false); - if (tmpret) - dev_warn(data->dev, "Stopping measurement failed\n"); - - return ret; -} - -/* - * Following equation for computing lux out of register values was given by - * ROHM HW colleagues; - * - * Red = RedData*1024 / Gain * 20 / meas_mode - * Green = GreenData* 1024 / Gain * 20 / meas_mode - * Blue = BlueData* 1024 / Gain * 20 / meas_mode - * IR = IrData* 1024 / Gain * 20 / meas_mode - * - * where meas_mode is the integration time in mS / 10 - * - * IRratio = (IR > 0.18 * Green) ? 0 : 1 - * - * Lx = max(c1*Red + c2*Green + c3*Blue,0) - * - * for - * IRratio 0: c1 = -0.00002237, c2 = 0.0003219, c3 = -0.000120371 - * IRratio 1: c1 = -0.00001074, c2 = 0.000305415, c3 = -0.000129367 - */ - -/* - * The max chan data is 0xffff. When we multiply it by 1024 * 20, we'll get - * 0x4FFFB000 which still fits in 32-bit integer. This won't overflow. - */ -#define NORM_CHAN_DATA_FOR_LX_CALC(chan, gain, time) (le16_to_cpu(chan) * \ - 1024 * 20 / (gain) / (time)) -static u64 bu27008_calc_nlux(struct bu27008_data *data, __le16 *lux_data, - unsigned int gain, unsigned int gain_ir, unsigned int time) -{ - unsigned int red, green, blue, ir; - s64 c1, c2, c3, nlux; - - time /= 10000; - ir = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_IR], gain_ir, time); - red = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_RED], gain, time); - green = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_GREEN], gain, time); - blue = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_BLUE], gain, time); - - if ((u64)ir * 100LLU > (u64)green * 18LLU) { - c1 = -22370; - c2 = 321900; - c3 = -120371; - } else { - c1 = -10740; - c2 = 305415; - c3 = -129367; - } - nlux = c1 * red + c2 * green + c3 * blue; - - return max_t(s64, 0, nlux); -} - -static int bu27008_get_time_n_gains(struct bu27008_data *data, - unsigned int *gain, unsigned int *gain_ir, unsigned int *time) -{ - int ret; - - ret = bu27008_get_gain(data, &data->gts, gain); - if (ret < 0) - return ret; - - ret = bu27008_get_gain(data, &data->gts_ir, gain_ir); - if (ret < 0) - return ret; - - ret = bu27008_get_int_time_us(data); - if (ret < 0) - return ret; - - /* Max integration time is 400000. Fits in signed int. */ - *time = ret; - - return 0; -} - -struct bu27008_buf { - __le16 chan[BU27008_NUM_HW_CHANS]; - u64 lux __aligned(8); - s64 ts __aligned(8); -}; - -static int bu27008_buffer_fill_lux(struct bu27008_data *data, - struct bu27008_buf *raw) -{ - unsigned int gain, gain_ir, time; - int ret; - - ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time); - if (ret) - return ret; - - raw->lux = bu27008_calc_nlux(data, raw->chan, gain, gain_ir, time); - - return 0; -} - -static int bu27008_read_lux(struct bu27008_data *data, struct iio_dev *idev, - struct iio_chan_spec const *chan, - int *val, int *val2) -{ - __le16 lux_data[BU27008_NUM_HW_CHANS]; - unsigned int gain, gain_ir, time; - u64 nlux; - int ret; - - ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time); - if (ret) - return ret; - - ret = bu27008_read_lux_chans(data, time, lux_data); - if (ret) - return ret; - - nlux = bu27008_calc_nlux(data, lux_data, gain, gain_ir, time); - *val = (int)nlux; - *val2 = nlux >> 32LLU; - - return IIO_VAL_INT_64; -} - -static int bu27008_read_raw(struct iio_dev *idev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct bu27008_data *data = iio_priv(idev); - int busy, ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - busy = iio_device_claim_direct_mode(idev); - if (busy) - return -EBUSY; - - mutex_lock(&data->mutex); - if (chan->type == IIO_LIGHT) - ret = bu27008_read_lux(data, idev, chan, val, val2); - else - ret = bu27008_read_one(data, idev, chan, val, val2); - mutex_unlock(&data->mutex); - - iio_device_release_direct_mode(idev); - - return ret; - - case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_LIGHT) { - *val = 0; - *val2 = 1; - return IIO_VAL_INT_PLUS_NANO; - } - ret = bu27008_get_scale(data, chan->scan_index == BU27008_IR, - val, val2); - if (ret) - return ret; - - return IIO_VAL_INT_PLUS_NANO; - - case IIO_CHAN_INFO_INT_TIME: - ret = bu27008_get_int_time_us(data); - if (ret < 0) - return ret; - - *val = 0; - *val2 = ret; - - return IIO_VAL_INT_PLUS_MICRO; - - default: - return -EINVAL; - } -} - -/* Called if the new scale could not be supported with existing int-time */ -static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val, - int val2, int *gain_sel) -{ - int i, ret, new_time_sel; - - for (i = 0; i < data->gts.num_itime; i++) { - new_time_sel = data->gts.itime_table[i].sel; - ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, - new_time_sel, val, val2, gain_sel); - if (!ret) - break; - } - if (i == data->gts.num_itime) { - dev_err(data->dev, "Can't support scale %u %u\n", val, val2); - - return -EINVAL; - } - - return bu27008_set_int_time_sel(data, new_time_sel); -} - -static int bu27008_set_scale(struct bu27008_data *data, - struct iio_chan_spec const *chan, - int val, int val2) -{ - int ret, gain_sel, time_sel; - - if (chan->scan_index == BU27008_IR) - return -EINVAL; - - mutex_lock(&data->mutex); - - ret = bu27008_get_int_time_sel(data, &time_sel); - if (ret < 0) - goto unlock_out; - - ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, - val, val2, &gain_sel); - if (ret) { - ret = bu27008_try_find_new_time_gain(data, val, val2, &gain_sel); - if (ret) - goto unlock_out; - - } - ret = data->cd->write_gain_sel(data, gain_sel); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; -} - -static int bu27008_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; - } -} - -static int bu27008_write_raw(struct iio_dev *idev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct bu27008_data *data = iio_priv(idev); - int ret; - - /* - * Do not allow changing scale when measurement is ongoing as doing so - * could make values in the buffer inconsistent. - */ - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; - - switch (mask) { - case IIO_CHAN_INFO_SCALE: - ret = bu27008_set_scale(data, chan, val, val2); - break; - case IIO_CHAN_INFO_INT_TIME: - if (val) { - ret = -EINVAL; - break; - } - ret = bu27008_try_set_int_time(data, val2); - break; - default: - ret = -EINVAL; - break; - } - iio_device_release_direct_mode(idev); - - return ret; -} - -static int bu27008_read_avail(struct iio_dev *idev, - struct iio_chan_spec const *chan, const int **vals, - int *type, int *length, long mask) -{ - struct bu27008_data *data = iio_priv(idev); - - switch (mask) { - case IIO_CHAN_INFO_INT_TIME: - return iio_gts_avail_times(&data->gts, vals, type, length); - case IIO_CHAN_INFO_SCALE: - if (chan->channel2 == IIO_MOD_LIGHT_IR) - return iio_gts_all_avail_scales(&data->gts_ir, vals, - type, length); - return iio_gts_all_avail_scales(&data->gts, vals, type, length); - default: - return -EINVAL; - } -} - -static int bu27008_update_scan_mode(struct iio_dev *idev, - const unsigned long *scan_mask) -{ - struct bu27008_data *data = iio_priv(idev); - int chan_sel; - - /* Configure channel selection */ - if (test_bit(BU27008_BLUE, idev->active_scan_mask)) { - if (test_bit(BU27008_CLEAR, idev->active_scan_mask)) - chan_sel = BU27008_BLUE2_CLEAR3; - else - chan_sel = BU27008_BLUE2_IR3; - } else { - chan_sel = BU27008_CLEAR2_IR3; - } - - chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - - return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, - data->cd->chan_sel_mask, chan_sel); -} - -static const struct iio_info bu27008_info = { - .read_raw = &bu27008_read_raw, - .write_raw = &bu27008_write_raw, - .write_raw_get_fmt = &bu27008_write_raw_get_fmt, - .read_avail = &bu27008_read_avail, - .update_scan_mode = bu27008_update_scan_mode, - .validate_trigger = iio_validate_own_trigger, -}; - -static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state) -{ - struct bu27008_data *data = iio_trigger_get_drvdata(trig); - int ret; - - - if (state) - ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg, - data->cd->drdy_en_mask); - else - ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg, - data->cd->drdy_en_mask); - if (ret) - dev_err(data->dev, "Failed to set trigger state\n"); - - return ret; -} - -static void bu27008_trigger_reenable(struct iio_trigger *trig) -{ - struct bu27008_data *data = iio_trigger_get_drvdata(trig); - - enable_irq(data->irq); -} - -static const struct iio_trigger_ops bu27008_trigger_ops = { - .set_trigger_state = bu27008_trigger_set_state, - .reenable = bu27008_trigger_reenable, -}; - -static irqreturn_t bu27008_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *idev = pf->indio_dev; - struct bu27008_data *data = iio_priv(idev); - struct bu27008_buf raw; - int ret, dummy; - - memset(&raw, 0, sizeof(raw)); - - /* - * After some measurements, it seems reading the - * BU27008_REG_MODE_CONTROL3 debounces the IRQ line - */ - ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy); - if (ret < 0) - goto err_read; - - ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, &raw.chan, - sizeof(raw.chan)); - if (ret < 0) - goto err_read; - - if (test_bit(BU27008_LUX, idev->active_scan_mask)) { - ret = bu27008_buffer_fill_lux(data, &raw); - if (ret) - goto err_read; - } - - iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp); -err_read: - iio_trigger_notify_done(idev->trig); - - return IRQ_HANDLED; -} - -static int bu27008_buffer_preenable(struct iio_dev *idev) -{ - struct bu27008_data *data = iio_priv(idev); - - return bu27008_meas_set(data, true); -} - -static int bu27008_buffer_postdisable(struct iio_dev *idev) -{ - struct bu27008_data *data = iio_priv(idev); - - return bu27008_meas_set(data, false); -} - -static const struct iio_buffer_setup_ops bu27008_buffer_ops = { - .preenable = bu27008_buffer_preenable, - .postdisable = bu27008_buffer_postdisable, -}; - -static irqreturn_t bu27008_data_rdy_poll(int irq, void *private) -{ - /* - * The BU27008 keeps IRQ asserted until we read the VALID bit from - * a register. We need to keep the IRQ disabled until then. - */ - disable_irq_nosync(irq); - iio_trigger_poll(private); - - return IRQ_HANDLED; -} - -static int bu27008_setup_trigger(struct bu27008_data *data, struct iio_dev *idev) -{ - struct iio_trigger *itrig; - char *name; - int ret; - - ret = devm_iio_triggered_buffer_setup(data->dev, idev, - &iio_pollfunc_store_time, - bu27008_trigger_handler, - &bu27008_buffer_ops); - if (ret) - return dev_err_probe(data->dev, ret, - "iio_triggered_buffer_setup_ext FAIL\n"); - - itrig = devm_iio_trigger_alloc(data->dev, "%sdata-rdy-dev%d", - idev->name, iio_device_id(idev)); - if (!itrig) - return -ENOMEM; - - data->trig = itrig; - - itrig->ops = &bu27008_trigger_ops; - iio_trigger_set_drvdata(itrig, data); - - name = devm_kasprintf(data->dev, GFP_KERNEL, "%s-bu27008", - dev_name(data->dev)); - - ret = devm_request_irq(data->dev, data->irq, - &bu27008_data_rdy_poll, - 0, name, itrig); - if (ret) - return dev_err_probe(data->dev, ret, "Could not request IRQ\n"); - - ret = devm_iio_trigger_register(data->dev, itrig); - if (ret) - return dev_err_probe(data->dev, ret, - "Trigger registration failed\n"); - - /* set default trigger */ - idev->trig = iio_trigger_get(itrig); - - return 0; -} - -static int bu27008_probe(struct i2c_client *i2c) -{ - struct device *dev = &i2c->dev; - struct bu27008_data *data; - struct regmap *regmap; - unsigned int part_id, reg; - struct iio_dev *idev; - int ret; - - idev = devm_iio_device_alloc(dev, sizeof(*data)); - if (!idev) - return -ENOMEM; - - ret = devm_regulator_get_enable(dev, "vdd"); - if (ret) - return dev_err_probe(dev, ret, "Failed to get regulator\n"); - - data = iio_priv(idev); - - data->cd = device_get_match_data(&i2c->dev); - if (!data->cd) - return -ENODEV; - - regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg); - if (IS_ERR(regmap)) - return dev_err_probe(dev, PTR_ERR(regmap), - "Failed to initialize Regmap\n"); - - - ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, ®); - if (ret) - return dev_err_probe(dev, ret, "Failed to access sensor\n"); - - part_id = FIELD_GET(BU27008_MASK_PART_ID, reg); - - if (part_id != data->cd->part_id) - dev_warn(dev, "unknown device 0x%x\n", part_id); - - ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains, - data->cd->num_gains, data->cd->itimes, - data->cd->num_itimes, &data->gts); - if (ret) - return ret; - - ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir, - data->cd->num_gains_ir, data->cd->itimes, - data->cd->num_itimes, &data->gts_ir); - if (ret) - return ret; - - mutex_init(&data->mutex); - data->regmap = regmap; - data->dev = dev; - data->irq = i2c->irq; - - idev->channels = bu27008_channels; - idev->num_channels = ARRAY_SIZE(bu27008_channels); - idev->name = data->cd->name; - idev->info = &bu27008_info; - idev->modes = INDIO_DIRECT_MODE; - idev->available_scan_masks = bu27008_scan_masks; - - ret = data->cd->chip_init(data); - if (ret) - return ret; - - if (i2c->irq) { - ret = bu27008_setup_trigger(data, idev); - if (ret) - return ret; - } else { - dev_info(dev, "No IRQ, buffered mode disabled\n"); - } - - ret = devm_iio_device_register(dev, idev); - if (ret) - return dev_err_probe(dev, ret, - "Unable to register iio device\n"); - - return 0; -} - -static const struct of_device_id bu27008_of_match[] = { - { .compatible = "rohm,bu27008", .data = &bu27008_chip }, - { .compatible = "rohm,bu27010", .data = &bu27010_chip }, - { } -}; -MODULE_DEVICE_TABLE(of, bu27008_of_match); - -static struct i2c_driver bu27008_i2c_driver = { - .driver = { - .name = "bu27008", - .of_match_table = bu27008_of_match, - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, - .probe = bu27008_probe, -}; -module_i2c_driver(bu27008_i2c_driver); - -MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver"); -MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); -MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS("IIO_GTS_HELPER"); diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c index 4f591c2278f2..7cec5e943373 100644 --- a/drivers/iio/light/rohm-bu27034.c +++ b/drivers/iio/light/rohm-bu27034.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/module.h> @@ -205,7 +206,7 @@ struct bu27034_data { struct { u32 mlux; __le16 channels[BU27034_NUM_HW_DATA_CHANS]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -395,30 +396,26 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) int numg = ARRAY_SIZE(gains); int ret, int_time_old, i; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bu27034_get_int_time(data); if (ret < 0) - goto unlock_out; + return ret; int_time_old = ret; if (!iio_gts_valid_time(&data->gts, time_us)) { dev_err(data->dev, "Unsupported integration time %u\n", time_us); - ret = -EINVAL; - - goto unlock_out; + return -EINVAL; } - if (time_us == int_time_old) { - ret = 0; - goto unlock_out; - } + if (time_us == int_time_old) + return 0; for (i = 0; i < numg; i++) { ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain); if (ret) - goto unlock_out; + return 0; ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, gains[i].old_gain, @@ -434,7 +431,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) gains[i].chan, time_us, scale1, scale2); if (gains[i].new_gain < 0) - goto unlock_out; + return ret; /* * If caller requests for integration time change and we @@ -455,7 +452,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) "Total gain increase. Risk of saturation"); ret = iio_gts_get_min_gain(&data->gts); if (ret < 0) - goto unlock_out; + return ret; } dev_dbg(data->dev, "chan %u scale changed\n", gains[i].chan); @@ -468,15 +465,10 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us) for (i = 0; i < numg; i++) { ret = bu27034_set_gain(data, gains[i].chan, gains[i].new_gain); if (ret) - goto unlock_out; + return ret; } - ret = bu27034_set_int_time(data, time_us); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bu27034_set_int_time(data, time_us); } static int bu27034_set_scale(struct bu27034_data *data, int chan, @@ -492,10 +484,10 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, return -EINVAL; } - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = regmap_read(data->regmap, BU27034_REG_MODE_CONTROL1, &time_sel); if (ret) - goto unlock_out; + return ret; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, val, val2, &gain_sel); @@ -518,7 +510,7 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, ret = bu27034_get_gain(data, gain.chan, &gain.old_gain); if (ret) - goto unlock_out; + return ret; /* * Iterate through all the times to see if we find one which @@ -551,26 +543,20 @@ static int bu27034_set_scale(struct bu27034_data *data, int chan, if (!found) { dev_dbg(data->dev, "Can't set scale maintaining other channel\n"); - ret = -EINVAL; - - goto unlock_out; + return -EINVAL; } ret = bu27034_set_gain(data, gain.chan, gain.new_gain); if (ret) - goto unlock_out; + return ret; ret = regmap_update_bits(data->regmap, BU27034_REG_MODE_CONTROL1, BU27034_MASK_MEAS_MODE, new_time_sel); if (ret) - goto unlock_out; + return ret; } - ret = bu27034_write_gain_sel(data, chan, gain_sel); -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bu27034_write_gain_sel(data, chan, gain_sel); } /* @@ -1012,9 +998,8 @@ static int bu27034_read_raw(struct iio_dev *idev, return -EINVAL; /* Don't mess with measurement enabling while buffering */ - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; + if (!iio_device_claim_direct(idev)) + return -EBUSY; mutex_lock(&data->mutex); /* @@ -1025,7 +1010,7 @@ static int bu27034_read_raw(struct iio_dev *idev, ret = result_get(data, chan->channel, val); mutex_unlock(&data->mutex); - iio_device_release_direct_mode(idev); + iio_device_release_direct(idev); if (ret) return ret; @@ -1064,9 +1049,8 @@ static int bu27034_write_raw(struct iio_dev *idev, struct bu27034_data *data = iio_priv(idev); int ret; - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; + if (!iio_device_claim_direct(idev)) + return -EBUSY; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -1083,7 +1067,7 @@ static int bu27034_write_raw(struct iio_dev *idev, break; } - iio_device_release_direct_mode(idev); + iio_device_release_direct(idev); return ret; } @@ -1221,42 +1205,33 @@ static int bu27034_buffer_enable(struct iio_dev *idev) struct task_struct *task; int ret; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bu27034_meas_set(data, true); if (ret) - goto unlock_out; + return ret; task = kthread_run(bu27034_buffer_thread, idev, "bu27034-buffering-%u", iio_device_id(idev)); - if (IS_ERR(task)) { - ret = PTR_ERR(task); - goto unlock_out; - } + if (IS_ERR(task)) + return PTR_ERR(task); data->task = task; -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return 0; } static int bu27034_buffer_disable(struct iio_dev *idev) { struct bu27034_data *data = iio_priv(idev); - int ret; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->task) { kthread_stop(data->task); data->task = NULL; } - ret = bu27034_meas_set(data, false); - mutex_unlock(&data->mutex); - - return ret; + return bu27034_meas_set(data, false); } static const struct iio_buffer_setup_ops bu27034_buffer_ops = { diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 56f5fbbf79ac..92e7552f3e39 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/mod_devicetable.h> +#include <linux/cleanup.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/regmap.h> @@ -203,7 +204,7 @@ struct rpr0521_data { struct { __le16 channels[3]; u8 garbage; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -704,50 +705,58 @@ static int rpr0521_write_ps_offset(struct rpr0521_data *data, int offset) return ret; } +static int rpr0521_read_info_raw(struct rpr0521_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + u8 device_mask; + __le16 raw_data; + int ret; + + device_mask = rpr0521_data_reg[chan->address].device_mask; + + guard(mutex)(&data->lock); + ret = rpr0521_set_power_state(data, true, device_mask); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, + rpr0521_data_reg[chan->address].address, + &raw_data, sizeof(raw_data)); + if (ret < 0) { + rpr0521_set_power_state(data, false, device_mask); + return ret; + } + + ret = rpr0521_set_power_state(data, false, device_mask); + if (ret < 0) + return ret; + + *val = le16_to_cpu(raw_data); + + return 0; +} + static int rpr0521_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct rpr0521_data *data = iio_priv(indio_dev); int ret; - int busy; - u8 device_mask; - __le16 raw_data; switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY) return -EINVAL; - busy = iio_device_claim_direct_mode(indio_dev); - if (busy) + if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - device_mask = rpr0521_data_reg[chan->address].device_mask; - - mutex_lock(&data->lock); - ret = rpr0521_set_power_state(data, true, device_mask); - if (ret < 0) - goto rpr0521_read_raw_out; - - ret = regmap_bulk_read(data->regmap, - rpr0521_data_reg[chan->address].address, - &raw_data, sizeof(raw_data)); - if (ret < 0) { - rpr0521_set_power_state(data, false, device_mask); - goto rpr0521_read_raw_out; - } - - ret = rpr0521_set_power_state(data, false, device_mask); - -rpr0521_read_raw_out: - mutex_unlock(&data->lock); - iio_device_release_direct_mode(indio_dev); + ret = rpr0521_read_info_raw(data, chan, val); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; - *val = le16_to_cpu(raw_data); - return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 66abda021696..4aa02afd853e 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -633,11 +633,10 @@ static int si1145_read_raw(struct iio_dev *indio_dev, case IIO_VOLTAGE: case IIO_TEMP: case IIO_UVINDEX: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = si1145_measure(indio_dev, chan); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; @@ -750,18 +749,17 @@ static int si1145_write_raw(struct iio_dev *indio_dev, return -EINVAL; } - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = si1145_param_set(data, reg1, val); if (ret < 0) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } /* Set recovery period to one's complement of gain */ ret = si1145_param_set(data, reg2, (~val & 0x07) << 4); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_RAW: if (chan->type != IIO_CURRENT) @@ -773,19 +771,18 @@ static int si1145_write_raw(struct iio_dev *indio_dev, reg1 = SI1145_PS_LED_REG(chan->channel); shift = SI1145_PS_LED_SHIFT(chan->channel); - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = i2c_smbus_read_byte_data(data->client, reg1); if (ret < 0) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } ret = i2c_smbus_write_byte_data(data->client, reg1, (ret & ~(0x0f << shift)) | ((val & 0x0f) << shift)); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: return si1145_store_samp_freq(data, val); diff --git a/drivers/iio/light/st_uvis25.h b/drivers/iio/light/st_uvis25.h index 283086887caf..1f93e3dc45c2 100644 --- a/drivers/iio/light/st_uvis25.h +++ b/drivers/iio/light/st_uvis25.h @@ -30,7 +30,7 @@ struct st_uvis25_hw { /* Ensure timestamp is naturally aligned */ struct { u8 chan; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 40a810000df0..124a8f9204a9 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -117,9 +117,8 @@ static int st_uvis25_read_raw(struct iio_dev *iio_dev, { int ret; - ret = iio_device_claim_direct_mode(iio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(iio_dev)) + return -EBUSY; switch (mask) { case IIO_CHAN_INFO_PROCESSED: { @@ -144,7 +143,7 @@ static int st_uvis25_read_raw(struct iio_dev *iio_dev, break; } - iio_device_release_direct_mode(iio_dev); + iio_device_release_direct(iio_dev); return ret; } diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c index f54282476d11..5d9bb4d9be63 100644 --- a/drivers/iio/light/st_uvis25_i2c.c +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -41,13 +41,13 @@ static int st_uvis25_i2c_probe(struct i2c_client *client) static const struct of_device_id st_uvis25_i2c_of_match[] = { { .compatible = "st,uvis25", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_uvis25_i2c_of_match); static const struct i2c_device_id st_uvis25_i2c_id_table[] = { { ST_UVIS25_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table); diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c index 18edc6a5a4a4..a5aad74ce73e 100644 --- a/drivers/iio/light/st_uvis25_spi.c +++ b/drivers/iio/light/st_uvis25_spi.c @@ -42,13 +42,13 @@ static int st_uvis25_spi_probe(struct spi_device *spi) static const struct of_device_id st_uvis25_spi_of_match[] = { { .compatible = "st,uvis25", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match); static const struct spi_device_id st_uvis25_spi_id_table[] = { { ST_UVIS25_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index b81cc44db43c..deada9ba4748 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -165,7 +165,7 @@ static const struct iio_chan_spec_ext_info stk3310_ext_info[] = { .shared = IIO_SEPARATE, .read = stk3310_read_near_level, }, - { /* sentinel */ } + { } }; static const struct iio_chan_spec stk3310_channels[] = { @@ -703,7 +703,7 @@ static const struct i2c_device_id stk3310_i2c_id[] = { { "STK3310" }, { "STK3311" }, { "STK3335" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); @@ -711,7 +711,7 @@ static const struct acpi_device_id stk3310_acpi_id[] = { {"STK3013", 0}, {"STK3310", 0}, {"STK3311", 0}, - {} + { } }; MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id); @@ -721,7 +721,7 @@ static const struct of_device_id stk3310_of_match[] = { { .compatible = "sensortek,stk3310", }, { .compatible = "sensortek,stk3311", }, { .compatible = "sensortek,stk3335", }, - {} + { } }; MODULE_DEVICE_TABLE(of, stk3310_of_match); diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 4fecdf10aeb1..39268f855c77 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -56,7 +56,7 @@ struct tcs3414_data { /* Ensure timestamp is naturally aligned */ struct { u16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -134,16 +134,15 @@ static int tcs3414_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 = tcs3414_req_data(data); if (ret < 0) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } ret = i2c_smbus_read_word_data(data->client, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = ret; diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 4186aac04902..0f8bf8503edd 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -67,7 +67,7 @@ struct tcs3472_data { /* Ensure timestamp is naturally aligned */ struct { u16 chans[4]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -148,16 +148,15 @@ static int tcs3472_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 = tcs3472_req_data(data); if (ret < 0) { - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } ret = i2c_smbus_read_word_data(data->client, chan->address); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = ret; diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index f1fe7640fce6..f2af1cd7c2d1 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -843,7 +843,7 @@ static const struct i2c_device_id tsl2563_id[] = { { "tsl2561", 1 }, { "tsl2562", 2 }, { "tsl2563", 3 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, tsl2563_id); @@ -852,7 +852,7 @@ static const struct of_device_id tsl2563_of_match[] = { { .compatible = "amstaos,tsl2561" }, { .compatible = "amstaos,tsl2562" }, { .compatible = "amstaos,tsl2563" }, - {} + { } }; MODULE_DEVICE_TABLE(of, tsl2563_of_match); diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index 02ad11611b9c..fc3b0c4226be 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -922,7 +922,7 @@ static const struct i2c_device_id tsl2583_idtable[] = { { "tsl2580", 0 }, { "tsl2581", 1 }, { "tsl2583", 2 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, tsl2583_idtable); @@ -930,7 +930,7 @@ static const struct of_device_id tsl2583_of_match[] = { { .compatible = "amstaos,tsl2580", }, { .compatible = "amstaos,tsl2581", }, { .compatible = "amstaos,tsl2583", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, tsl2583_of_match); diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c index b81ca6f73f92..08476f193a44 100644 --- a/drivers/iio/light/tsl2591.c +++ b/drivers/iio/light/tsl2591.c @@ -1204,7 +1204,7 @@ static int tsl2591_probe(struct i2c_client *client) static const struct of_device_id tsl2591_of_match[] = { { .compatible = "amstaos,tsl2591"}, - {} + { } }; MODULE_DEVICE_TABLE(of, tsl2591_of_match); diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 349afdcbe30d..0b171106441a 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -1899,7 +1899,7 @@ static const struct i2c_device_id tsl2772_idtable[] = { { "tsl2772", tsl2772 }, { "tmd2772", tmd2772 }, { "apds9930", apds9930 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, tsl2772_idtable); @@ -1916,7 +1916,7 @@ static const struct of_device_id tsl2772_of_match[] = { { .compatible = "amstaos,tsl2772" }, { .compatible = "amstaos,tmd2772" }, { .compatible = "avago,apds9930" }, - {} + { } }; MODULE_DEVICE_TABLE(of, tsl2772_of_match); diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index c83114aed6b2..61a0957317a1 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -949,21 +949,21 @@ static const struct dev_pm_ops us5182d_pm_ops = { static const struct acpi_device_id us5182d_acpi_match[] = { { "USD5182", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match); static const struct i2c_device_id us5182d_id[] = { { "usd5182" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, us5182d_id); static const struct of_device_id us5182d_of_match[] = { { .compatible = "upisemi,usd5182" }, - {} + { } }; MODULE_DEVICE_TABLE(of, us5182d_of_match); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index e19199b17f2e..90e7d4421abf 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1084,9 +1084,8 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_SCALE: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; /* Protect against event capture. */ if (vcnl4010_is_in_periodic_mode(data)) { @@ -1096,7 +1095,7 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev, mask); } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->type) { @@ -1157,9 +1156,8 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev, int ret; struct vcnl4000_data *data = iio_priv(indio_dev); - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; /* Protect against event capture. */ if (vcnl4010_is_in_periodic_mode(data)) { @@ -1183,7 +1181,7 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev, } end: - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -1410,46 +1408,52 @@ static int vcnl4010_read_event_config(struct iio_dev *indio_dev, } } -static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) +static int vcnl4010_config_threshold_enable(struct vcnl4000_data *data) { - struct vcnl4000_data *data = iio_priv(indio_dev); int ret; - int icr; - int command; - if (state) { - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + /* Enable periodic measurement of proximity data. */ + ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, + VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN); + if (ret < 0) + return ret; - /* Enable periodic measurement of proximity data. */ - command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; + /* + * Enable interrupts on threshold, for proximity data by + * default. + */ + return i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, + VCNL4010_INT_THR_EN); +} - /* - * Enable interrupts on threshold, for proximity data by - * default. - */ - icr = VCNL4010_INT_THR_EN; - } else { - if (!vcnl4010_is_thr_enabled(data)) - return 0; +static int vcnl4010_config_threshold_disable(struct vcnl4000_data *data) +{ + int ret; - command = 0; - icr = 0; - } + if (!vcnl4010_is_thr_enabled(data)) + return 0; - ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, - command); + ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); if (ret < 0) - goto end; + return ret; - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); + return i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); +} -end: - if (state) - iio_device_release_direct_mode(indio_dev); +static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) +{ + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; - return ret; + if (state) { + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = vcnl4010_config_threshold_enable(data); + iio_device_release_direct(indio_dev); + return ret; + } else { + return vcnl4010_config_threshold_disable(data); + } } static int vcnl4010_write_event_config(struct iio_dev *indio_dev, @@ -1741,7 +1745,7 @@ static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { .shared = IIO_SEPARATE, .read = vcnl4000_read_near_level, }, - { /* sentinel */ } + { } }; static const struct iio_event_spec vcnl4000_event_spec[] = { @@ -2064,7 +2068,7 @@ static const struct of_device_id vcnl_4000_of_match[] = { .compatible = "vishay,vcnl4200", .data = (void *)VCNL4200, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 67c94be02018..b2bede9d3daa 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -156,6 +156,31 @@ static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) return ret; } +static int vcnl4035_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct vcnl4035_data *data = iio_priv(indio_dev); + int ret; + int raw_data; + unsigned int reg; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (chan->channel) + reg = VCNL4035_ALS_DATA; + else + reg = VCNL4035_WHITE_DATA; + ret = regmap_read(data->regmap, reg, &raw_data); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + *val = raw_data; + + return IIO_VAL_INT; +} + /* * Device IT INT Time (ms) Scale (lux/step) * 000 50 0.064 @@ -175,28 +200,13 @@ static int vcnl4035_read_raw(struct iio_dev *indio_dev, { struct vcnl4035_data *data = iio_priv(indio_dev); int ret; - int raw_data; - unsigned int reg; switch (mask) { case IIO_CHAN_INFO_RAW: ret = vcnl4035_set_pm_runtime_state(data, true); if (ret < 0) return ret; - - ret = iio_device_claim_direct_mode(indio_dev); - if (!ret) { - if (chan->channel) - reg = VCNL4035_ALS_DATA; - else - reg = VCNL4035_WHITE_DATA; - ret = regmap_read(data->regmap, reg, &raw_data); - iio_device_release_direct_mode(indio_dev); - if (!ret) { - *val = raw_data; - ret = IIO_VAL_INT; - } - } + ret = vcnl4035_read_info_raw(indio_dev, chan, val); vcnl4035_set_pm_runtime_state(data, false); return ret; case IIO_CHAN_INFO_INT_TIME: diff --git a/drivers/iio/light/veml3235.c b/drivers/iio/light/veml3235.c index 66361c3012a3..77c9ae17ed47 100644 --- a/drivers/iio/light/veml3235.c +++ b/drivers/iio/light/veml3235.c @@ -11,6 +11,7 @@ #include <linux/err.h> #include <linux/i2c.h> #include <linux/iio/iio.h> +#include <linux/iio/iio-gts-helper.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -35,17 +36,33 @@ struct veml3235_data { struct device *dev; struct regmap *regmap; struct veml3235_rf rf; + struct iio_gts gts; }; -static const int veml3235_it_times[][2] = { - { 0, 50000 }, - { 0, 100000 }, - { 0, 200000 }, - { 0, 400000 }, - { 0, 800000 }, +static const struct iio_itime_sel_mul veml3235_it_sel[] = { + GAIN_SCALE_ITIME_US(50000, 0, 1), + GAIN_SCALE_ITIME_US(100000, 1, 2), + GAIN_SCALE_ITIME_US(200000, 2, 4), + GAIN_SCALE_ITIME_US(400000, 3, 8), + GAIN_SCALE_ITIME_US(800000, 4, 16), }; -static const int veml3235_scale_vals[] = { 1, 2, 4, 8 }; +/* + * The MSB (DG) doubles the value of the rest of the field, which leads to + * two possible combinations to obtain gain = 2 and gain = 4. The gain + * handling can be simplified by restricting DG = 1 to the only gain that + * really requires it, gain = 8. Note that "X10" is a reserved value. + */ +#define VEML3235_SEL_GAIN_X1 0 +#define VEML3235_SEL_GAIN_X2 1 +#define VEML3235_SEL_GAIN_X4 3 +#define VEML3235_SEL_GAIN_X8 7 +static const struct iio_gain_sel_pair veml3235_gain_sel[] = { + GAIN_SCALE_GAIN(1, VEML3235_SEL_GAIN_X1), + GAIN_SCALE_GAIN(2, VEML3235_SEL_GAIN_X2), + GAIN_SCALE_GAIN(4, VEML3235_SEL_GAIN_X4), + GAIN_SCALE_GAIN(8, VEML3235_SEL_GAIN_X8), +}; static int veml3235_power_on(struct veml3235_data *data) { @@ -101,42 +118,58 @@ static const struct iio_chan_spec veml3235_channels[] = { }, }; +static const struct regmap_range veml3235_readable_ranges[] = { + regmap_reg_range(VEML3235_REG_CONF, VEML3235_REG_ID), +}; + +static const struct regmap_access_table veml3235_readable_table = { + .yes_ranges = veml3235_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml3235_readable_ranges), +}; + +static const struct regmap_range veml3235_writable_ranges[] = { + regmap_reg_range(VEML3235_REG_CONF, VEML3235_REG_CONF), +}; + +static const struct regmap_access_table veml3235_writable_table = { + .yes_ranges = veml3235_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(veml3235_writable_ranges), +}; + +static const struct regmap_range veml3235_volatile_ranges[] = { + regmap_reg_range(VEML3235_REG_WH_DATA, VEML3235_REG_ALS_DATA), +}; + +static const struct regmap_access_table veml3235_volatile_table = { + .yes_ranges = veml3235_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(veml3235_volatile_ranges), +}; + static const struct regmap_config veml3235_regmap_config = { .name = "veml3235_regmap", .reg_bits = 8, .val_bits = 16, .max_register = VEML3235_REG_ID, .val_format_endian = REGMAP_ENDIAN_LITTLE, + .rd_table = &veml3235_readable_table, + .wr_table = &veml3235_writable_table, + .volatile_table = &veml3235_volatile_table, + .cache_type = REGCACHE_RBTREE, }; static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2) { - int ret, reg; + int ret, it_idx; - ret = regmap_field_read(data->rf.it, ®); + ret = regmap_field_read(data->rf.it, &it_idx); if (ret) return ret; - switch (reg) { - case 0: - *val2 = 50000; - break; - case 1: - *val2 = 100000; - break; - case 2: - *val2 = 200000; - break; - case 3: - *val2 = 400000; - break; - case 4: - *val2 = 800000; - 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; @@ -145,78 +178,78 @@ static int veml3235_get_it(struct veml3235_data *data, int *val, int *val2) static int veml3235_set_it(struct iio_dev *indio_dev, int val, int val2) { struct veml3235_data *data = iio_priv(indio_dev); - int ret, new_it; + 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 50000: - new_it = 0x00; - break; - case 100000: - new_it = 0x01; - break; - case 200000: - new_it = 0x02; - break; - case 400000: - new_it = 0x03; - break; - case 800000: - new_it = 0x04; - break; - default: - return -EINVAL; - } + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; - ret = regmap_field_write(data->rf.it, new_it); - if (ret) { - dev_err(data->dev, - "failed to update integration time: %d\n", ret); + ret = regmap_field_read(data->rf.gain, &gain_idx); + if (ret) return ret; - } - return 0; + 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; + + prev_gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx); + if (prev_gain < 0) + return prev_gain; + + 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->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 veml3235_set_gain(struct iio_dev *indio_dev, int val, int val2) +static int veml3235_set_scale(struct iio_dev *indio_dev, int val, int val2) { struct veml3235_data *data = iio_priv(indio_dev); - int ret, new_gain; + int ret, it_idx, gain_sel, time_sel; - if (val2 != 0) - return -EINVAL; + ret = regmap_field_read(data->rf.it, &it_idx); + if (ret) + return ret; - switch (val) { - case 1: - new_gain = 0x00; - break; - case 2: - new_gain = 0x01; - break; - case 4: - new_gain = 0x03; - break; - case 8: - new_gain = 0x07; - break; - default: - return -EINVAL; - } + ret = iio_gts_find_gain_time_sel_for_scale(&data->gts, val, val2, + &gain_sel, &time_sel); + if (ret) + return ret; - ret = regmap_field_write(data->rf.gain, new_gain); - if (ret) { - dev_err(data->dev, "failed to set gain: %d\n", ret); + ret = regmap_field_write(data->rf.it, time_sel); + if (ret) return ret; - } - return 0; + return regmap_field_write(data->rf.gain, gain_sel); } -static int veml3235_get_gain(struct veml3235_data *data, int *val) +static int veml3235_get_scale(struct veml3235_data *data, int *val, int *val2) { - int ret, reg; + int gain, it, reg, ret; ret = regmap_field_read(data->rf.gain, ®); if (ret) { @@ -224,25 +257,25 @@ static int veml3235_get_gain(struct veml3235_data *data, int *val) return ret; } - switch (reg & 0x03) { - case 0: - *val = 1; - break; - case 1: - *val = 2; - break; - case 3: - *val = 4; - break; - default: - return -EINVAL; + gain = iio_gts_find_gain_by_sel(&data->gts, reg); + if (gain < 0) + return gain; + + ret = regmap_field_read(data->rf.it, ®); + if (ret) { + dev_err(data->dev, "failed to read integration time %d\n", ret); + return ret; } - /* Double gain */ - if (reg & 0x04) - *val *= 2; + 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; + return IIO_VAL_INT_PLUS_NANO; } static int veml3235_read_raw(struct iio_dev *indio_dev, @@ -276,7 +309,7 @@ static int veml3235_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: return veml3235_get_it(data, val, val2); case IIO_CHAN_INFO_SCALE: - return veml3235_get_gain(data, val); + return veml3235_get_scale(data, val, val2); default: return -EINVAL; } @@ -287,17 +320,27 @@ static int veml3235_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + struct veml3235_data *data = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_INT_TIME: - *vals = (int *)&veml3235_it_times; - *length = 2 * ARRAY_SIZE(veml3235_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: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + default: + return -EINVAL; + } +} + +static int veml3235_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { case IIO_CHAN_INFO_SCALE: - *vals = (int *)&veml3235_scale_vals; - *length = ARRAY_SIZE(veml3235_scale_vals); - *type = IIO_VAL_INT; - return IIO_AVAIL_LIST; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -311,7 +354,7 @@ static int veml3235_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: return veml3235_set_it(indio_dev, val, val2); case IIO_CHAN_INFO_SCALE: - return veml3235_set_gain(indio_dev, val, val2); + return veml3235_set_scale(indio_dev, val, val2); } return -EINVAL; @@ -321,7 +364,7 @@ static void veml3235_read_id(struct veml3235_data *data) { int ret, reg; - ret = regmap_field_read(data->rf.id, ®); + ret = regmap_field_read(data->rf.id, ®); if (ret) { dev_info(data->dev, "failed to read ID\n"); return; @@ -371,6 +414,13 @@ static int veml3235_hw_init(struct iio_dev *indio_dev) struct device *dev = data->dev; int ret; + ret = devm_iio_init_iio_gts(data->dev, 0, 272640000, + veml3235_gain_sel, ARRAY_SIZE(veml3235_gain_sel), + veml3235_it_sel, ARRAY_SIZE(veml3235_it_sel), + &data->gts); + if (ret) + return dev_err_probe(data->dev, ret, "failed to init iio gts\n"); + /* Set gain to 1 and integration time to 100 ms */ ret = regmap_field_write(data->rf.gain, 0x00); if (ret) @@ -389,9 +439,10 @@ static int veml3235_hw_init(struct iio_dev *indio_dev) } static const struct iio_info veml3235_info = { - .read_raw = veml3235_read_raw, - .read_avail = veml3235_read_avail, + .read_raw = veml3235_read_raw, + .read_avail = veml3235_read_avail, .write_raw = veml3235_write_raw, + .write_raw_get_fmt = veml3235_write_raw_get_fmt, }; static int veml3235_probe(struct i2c_client *client) @@ -493,3 +544,4 @@ module_i2c_driver(veml3235_driver); MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>"); MODULE_DESCRIPTION("VEML3235 Ambient Light Sensor"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_GTS_HELPER"); diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index ccb43dfd5cf7..473a9c3e32a3 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -24,10 +24,14 @@ #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> /* Device registers */ #define VEML6030_REG_ALS_CONF 0x00 @@ -37,6 +41,7 @@ #define VEML6030_REG_ALS_DATA 0x04 #define VEML6030_REG_WH_DATA 0x05 #define VEML6030_REG_ALS_INT 0x06 +#define VEML6030_REG_DATA(ch) (VEML6030_REG_ALS_DATA + (ch)) /* Bit masks for specific functionality */ #define VEML6030_ALS_IT GENMASK(9, 6) @@ -56,16 +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); }; /* @@ -82,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), }; /* @@ -242,6 +283,13 @@ static const struct iio_chan_spec veml6030_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .event_spec = veml6030_event_spec, .num_event_specs = ARRAY_SIZE(veml6030_event_spec), + .scan_index = VEML6030_SCAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, { .type = IIO_INTENSITY, @@ -253,7 +301,15 @@ static const struct iio_chan_spec veml6030_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6030_SCAN_WH, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(VEML6030_SCAN_TIMESTAMP), }; static const struct iio_chan_spec veml7700_channels[] = { @@ -266,6 +322,13 @@ static const struct iio_chan_spec veml7700_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6030_SCAN_ALS, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, { .type = IIO_INTENSITY, @@ -277,7 +340,42 @@ static const struct iio_chan_spec veml7700_channels[] = { BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = VEML6030_SCAN_WH, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, }, + 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 = { @@ -286,107 +384,79 @@ static const struct regmap_config veml6030_regmap_config = { .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, @@ -395,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; @@ -420,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; @@ -449,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) +static int veml6030_set_scale(struct iio_dev *indio_dev, int val, int val2) { - 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) -{ - 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, @@ -666,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 @@ -689,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: @@ -708,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; } @@ -725,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; @@ -743,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; } @@ -847,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, @@ -858,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) @@ -889,6 +882,37 @@ static irqreturn_t veml6030_event_handler(int irq, void *private) return IRQ_HANDLED; } +static irqreturn_t veml6030_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct veml6030_data *data = iio_priv(iio); + unsigned int reg; + int ch, ret, i = 0; + struct { + u16 chans[2]; + aligned_s64 timestamp; + } scan; + + memset(&scan, 0, sizeof(scan)); + + iio_for_each_active_channel(iio, ch) { + ret = regmap_read(data->regmap, VEML6030_REG_DATA(ch), + ®); + if (ret) + goto done; + + scan.chans[i++] = reg; + } + + iio_push_to_buffers_with_timestamp(iio, &scan, pf->timestamp); + +done: + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + static int veml6030_set_info(struct iio_dev *indio_dev) { struct veml6030_data *data = iio_priv(indio_dev); @@ -920,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 @@ -931,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"); @@ -966,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; } @@ -986,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"); @@ -1022,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; } @@ -1073,10 +1122,21 @@ 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; + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + veml6030_trigger_handler, NULL); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to register triggered buffer"); + return devm_iio_device_register(&client->dev, indio_dev); } @@ -1111,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[] = { @@ -1184,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/veml6040.c b/drivers/iio/light/veml6040.c index 216e271001a8..71a594b2ec85 100644 --- a/drivers/iio/light/veml6040.c +++ b/drivers/iio/light/veml6040.c @@ -256,13 +256,13 @@ static int veml6040_probe(struct i2c_client *client) static const struct i2c_device_id veml6040_id_table[] = { {"veml6040"}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, veml6040_id_table); static const struct of_device_id veml6040_of_match[] = { {.compatible = "vishay,veml6040"}, - {} + { } }; MODULE_DEVICE_TABLE(of, veml6040_of_match); diff --git a/drivers/iio/light/veml6075.c b/drivers/iio/light/veml6075.c index 05d4c0e9015d..edbb43407054 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) @@ -454,7 +458,7 @@ MODULE_DEVICE_TABLE(i2c, veml6075_id); static const struct of_device_id veml6075_of_match[] = { { .compatible = "vishay,veml6075" }, - {} + { } }; MODULE_DEVICE_TABLE(of, veml6075_of_match); diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 6e2183a4243e..cc4f2e5404aa 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -745,7 +745,7 @@ static int vl6180_probe(struct i2c_client *client) static const struct of_device_id vl6180_of_match[] = { { .compatible = "st,vl6180", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, vl6180_of_match); diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c index 604be60e92ac..1e5e9bf2935f 100644 --- a/drivers/iio/light/zopt2201.c +++ b/drivers/iio/light/zopt2201.c @@ -113,11 +113,13 @@ static const struct { { 13, 3125 }, }; -static const struct { +struct zopt2201_scale { unsigned int scale, uscale; /* scale factor as integer + micro */ u8 gain; /* gain register value */ u8 res; /* resolution register value */ -} zopt2201_scale_als[] = { +}; + +static struct zopt2201_scale zopt2201_scale_als[] = { { 19, 200000, 0, 5 }, { 6, 400000, 1, 5 }, { 3, 200000, 2, 5 }, @@ -142,11 +144,7 @@ static const struct { { 0, 8333, 4, 0 }, }; -static const struct { - unsigned int scale, uscale; /* scale factor as integer + micro */ - u8 gain; /* gain register value */ - u8 res; /* resolution register value */ -} zopt2201_scale_uvb[] = { +static struct zopt2201_scale zopt2201_scale_uvb[] = { { 0, 460800, 0, 5 }, { 0, 153600, 1, 5 }, { 0, 76800, 2, 5 }, @@ -348,16 +346,17 @@ static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain) return 0; } -static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx) +static int zopt2201_write_scale_by_idx(struct zopt2201_data *data, int idx, + struct zopt2201_scale *zopt2201_scale_array) { int ret; mutex_lock(&data->lock); - ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res); + ret = zopt2201_set_resolution(data, zopt2201_scale_array[idx].res); if (ret < 0) goto unlock; - ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain); + ret = zopt2201_set_gain(data, zopt2201_scale_array[idx].gain); unlock: mutex_unlock(&data->lock); @@ -371,29 +370,12 @@ static int zopt2201_write_scale_als(struct zopt2201_data *data, for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++) if (val == zopt2201_scale_als[i].scale && - val2 == zopt2201_scale_als[i].uscale) { - return zopt2201_write_scale_als_by_idx(data, i); - } + val2 == zopt2201_scale_als[i].uscale) + return zopt2201_write_scale_by_idx(data, i, zopt2201_scale_als); return -EINVAL; } -static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx) -{ - int ret; - - mutex_lock(&data->lock); - ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res); - if (ret < 0) - goto unlock; - - ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain); - -unlock: - mutex_unlock(&data->lock); - return ret; -} - static int zopt2201_write_scale_uvb(struct zopt2201_data *data, int val, int val2) { @@ -402,7 +384,7 @@ static int zopt2201_write_scale_uvb(struct zopt2201_data *data, for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++) if (val == zopt2201_scale_uvb[i].scale && val2 == zopt2201_scale_uvb[i].uscale) - return zopt2201_write_scale_uvb_by_idx(data, i); + return zopt2201_write_scale_by_idx(data, i, zopt2201_scale_uvb); return -EINVAL; } 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 acd291f3e792..192ba2da94e2 100644 --- a/drivers/iio/magnetometer/af8133j.c +++ b/drivers/iio/magnetometer/af8133j.c @@ -360,7 +360,7 @@ static irqreturn_t af8133j_trigger_handler(int irq, void *p) s64 timestamp = iio_get_time_ns(indio_dev); struct { __le16 values[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } sample; int ret; @@ -370,7 +370,8 @@ static irqreturn_t af8133j_trigger_handler(int irq, void *p) if (ret) goto out_done; - iio_push_to_buffers_with_timestamp(indio_dev, &sample, timestamp); + iio_push_to_buffers_with_ts(indio_dev, &sample, sizeof(sample), + timestamp); out_done: iio_trigger_notify_done(indio_dev->trig); @@ -383,7 +384,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 8306a18706ac..947fe8a475f2 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -197,7 +197,7 @@ struct ak8974 { /* Ensure timestamp is naturally aligned */ struct { __le16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -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]); @@ -673,8 +673,8 @@ static void ak8974_fill_buffer(struct iio_dev *indio_dev) goto out_unlock; } - iio_push_to_buffers_with_timestamp(indio_dev, &ak8974->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &ak8974->scan, sizeof(ak8974->scan), + iio_get_time_ns(indio_dev)); out_unlock: mutex_unlock(&ak8974->lock); @@ -704,7 +704,7 @@ ak8974_get_mount_matrix(const struct iio_dev *indio_dev, static const struct iio_chan_spec_ext_info ak8974_ext_info[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix), - { }, + { } }; #define AK8974_AXIS_CHANNEL(axis, index, bits) \ @@ -1023,14 +1023,14 @@ static const struct i2c_device_id ak8974_id[] = { { "ami306" }, { "ak8974" }, { "hscdtd008a" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ak8974_id); static const struct of_device_id ak8974_of_match[] = { { .compatible = "asahi-kasei,ak8974", }, { .compatible = "alps,hscdtd008a", }, - {} + { } }; MODULE_DEVICE_TABLE(of, ak8974_of_match); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 18077fb463a9..a1e92b2abffd 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -426,7 +426,7 @@ struct ak8975_data { /* Ensure natural alignment of timestamp */ struct { s16 channels[3]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -882,8 +882,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) data->scan.channels[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range); data->scan.channels[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range); - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); return; @@ -1107,7 +1107,7 @@ static const struct i2c_device_id ak8975_id[] = { {"ak09912", (kernel_ulong_t)&ak_def_array[AK09912] }, {"ak09916", (kernel_ulong_t)&ak_def_array[AK09916] }, {"ak09918", (kernel_ulong_t)&ak_def_array[AK09918] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ak8975_id); @@ -1122,7 +1122,7 @@ static const struct of_device_id ak8975_of_match[] = { { .compatible = "ak09912", .data = &ak_def_array[AK09912] }, { .compatible = "asahi-kasei,ak09916", .data = &ak_def_array[AK09916] }, { .compatible = "asahi-kasei,ak09918", .data = &ak_def_array[AK09918] }, - {} + { } }; MODULE_DEVICE_TABLE(of, ak8975_of_match); diff --git a/drivers/iio/magnetometer/als31300.c b/drivers/iio/magnetometer/als31300.c index 87b60c4e81fa..f72af829715f 100644 --- a/drivers/iio/magnetometer/als31300.c +++ b/drivers/iio/magnetometer/als31300.c @@ -245,8 +245,7 @@ static irqreturn_t als31300_trigger_handler(int irq, void *p) scan.channels[0] = x; scan.channels[1] = y; scan.channels[2] = z; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), pf->timestamp); trigger_out: iio_trigger_notify_done(indio_dev->trig); @@ -457,7 +456,7 @@ static const struct i2c_device_id als31300_id[] = { .name = "als31300-2000", .driver_data = (kernel_ulong_t)&al31300_variant_2000, }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, als31300_id); @@ -474,7 +473,7 @@ static const struct of_device_id als31300_of_match[] = { .compatible = "allegromicro,als31300-2000", .data = &al31300_variant_2000, }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, als31300_of_match); diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 7f545740178e..f9c51ceae011 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -140,7 +140,7 @@ struct bmc150_magn_data { /* Ensure timestamp is naturally aligned */ struct { s32 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; struct iio_trigger *dready_trig; bool dready_trigger_on; @@ -678,8 +678,8 @@ static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: mutex_unlock(&data->mutex); diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c index 8cbeda924bda..b110791f688a 100644 --- a/drivers/iio/magnetometer/bmc150_magn_i2c.c +++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c @@ -42,7 +42,7 @@ static const struct i2c_device_id bmc150_magn_i2c_id[] = { { "bmc150_magn" }, { "bmc156_magn" }, { "bmm150_magn" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id); diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index 2d4b8cba32f1..896b1d280731 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -37,7 +37,7 @@ static const struct spi_device_id bmc150_magn_spi_id[] = { {"bmc150_magn", 0}, {"bmc156_magn", 0}, {"bmm150_magn", 0}, - {} + { } }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 97ddaa2a03f6..c673f9323e47 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -563,7 +563,7 @@ static const struct platform_device_id hid_magn_3d_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200083", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_magn_3d_ids); diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 60fbb5431c88..7a3faf7ffed4 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -34,7 +34,7 @@ enum hmc5843_ids { * @regmap: hardware access register maps * @variant: describe chip variants * @scan: buffer to pack data for passing to - * iio_push_to_buffers_with_timestamp() + * iio_push_to_buffers_with_ts() */ struct hmc5843_data { struct device *dev; @@ -44,7 +44,7 @@ struct hmc5843_data { struct iio_mount_matrix orientation; struct { __be16 chans[3]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 2fc84310e2cc..fc16ebd314f7 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -452,8 +452,8 @@ static irqreturn_t hmc5843_trigger_handler(int irq, void *p) if (ret < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c index 657a309e2bd5..b41709959e2b 100644 --- a/drivers/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -84,7 +84,7 @@ static const struct of_device_id hmc5843_of_match[] = { { .compatible = "honeywell,hmc5883", .data = (void *)HMC5883_ID }, { .compatible = "honeywell,hmc5883l", .data = (void *)HMC5883L_ID }, { .compatible = "honeywell,hmc5983", .data = (void *)HMC5983_ID }, - {} + { } }; MODULE_DEVICE_TABLE(of, hmc5843_of_match); diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index b7fde331069d..6a55c1559b0d 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -60,7 +60,6 @@ static int hmc5843_spi_probe(struct spi_device *spi) spi->mode = SPI_MODE_3; spi->max_speed_hz = 8000000; - spi->bits_per_word = 8; ret = spi_setup(spi); if (ret) return ret; diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index 5295dc0100e4..ff09250a06e7 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -9,6 +9,7 @@ * TODO: irq, user offset, oversampling, continuous mode */ +#include <linux/cleanup.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/iio/iio.h> @@ -60,7 +61,7 @@ struct mag3110_data { struct { __be16 channels[3]; u8 temperature; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -102,17 +103,12 @@ static int mag3110_read(struct mag3110_data *data, __be16 buf[3]) { int ret; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); ret = mag3110_request(data); - if (ret < 0) { - mutex_unlock(&data->lock); + if (ret < 0) return ret; - } - ret = i2c_smbus_read_i2c_block_data(data->client, - MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf); - mutex_unlock(&data->lock); - - return ret; + return i2c_smbus_read_i2c_block_data(data->client, MAG3110_OUT_X, + 3 * sizeof(__be16), (u8 *) buf); } static ssize_t mag3110_show_int_plus_micros(char *buf, @@ -231,19 +227,17 @@ static int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val) int ret; int is_active; - mutex_lock(&data->lock); + guard(mutex)(&data->lock); is_active = mag3110_is_active(data); - if (is_active < 0) { - ret = is_active; - goto fail; - } + if (is_active < 0) + return is_active; /* config can only be changed when in standby */ if (is_active > 0) { ret = mag3110_standby(data); if (ret < 0) - goto fail; + return ret; } /* @@ -252,23 +246,52 @@ static int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val) */ ret = mag3110_wait_standby(data); if (ret < 0) - goto fail; + return ret; ret = i2c_smbus_write_byte_data(data->client, reg, val); if (ret < 0) - goto fail; + return ret; if (is_active > 0) { ret = mag3110_active(data); if (ret < 0) - goto fail; + return ret; } - ret = 0; -fail: - mutex_unlock(&data->lock); + return 0; +} - return ret; +static int __mag3110_read_info_raw(struct mag3110_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + __be16 buffer[3]; + int ret; + + switch (chan->type) { + case IIO_MAGN: /* in 0.1 uT / LSB */ + ret = mag3110_read(data, buffer); + if (ret < 0) + return ret; + *val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]), + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + + case IIO_TEMP: { /* in 1 C / LSB */ + guard(mutex)(&data->lock); + ret = mag3110_request(data); + if (ret < 0) + return ret; + ret = i2c_smbus_read_byte_data(data->client, + MAG3110_DIE_TEMP); + if (ret < 0) + return ret; + *val = sign_extend32(ret, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + } + default: + return -EINVAL; + } } static int mag3110_read_raw(struct iio_dev *indio_dev, @@ -276,46 +299,14 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct mag3110_data *data = iio_priv(indio_dev); - __be16 buffer[3]; int i, ret; switch (mask) { case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - switch (chan->type) { - case IIO_MAGN: /* in 0.1 uT / LSB */ - ret = mag3110_read(data, buffer); - if (ret < 0) - goto release; - *val = sign_extend32( - be16_to_cpu(buffer[chan->scan_index]), - chan->scan_type.realbits - 1); - ret = IIO_VAL_INT; - break; - case IIO_TEMP: /* in 1 C / LSB */ - mutex_lock(&data->lock); - ret = mag3110_request(data); - if (ret < 0) { - mutex_unlock(&data->lock); - goto release; - } - ret = i2c_smbus_read_byte_data(data->client, - MAG3110_DIE_TEMP); - mutex_unlock(&data->lock); - if (ret < 0) - goto release; - *val = sign_extend32(ret, - chan->scan_type.realbits - 1); - ret = IIO_VAL_INT; - break; - default: - ret = -EINVAL; - } -release: - iio_device_release_direct_mode(indio_dev); + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __mag3110_read_info_raw(data, chan, val); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -346,24 +337,18 @@ release: return -EINVAL; } -static int mag3110_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __mag3110_write_raw(struct mag3110_data *data, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { - struct mag3110_data *data = iio_priv(indio_dev); - int rate, ret; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + int rate; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: rate = mag3110_get_samp_freq_index(data, val, val2); - if (rate < 0) { - ret = -EINVAL; - break; - } + if (rate < 0) + return -EINVAL; + data->ctrl_reg1 &= 0xff & ~MAG3110_CTRL_DR_MASK & ~MAG3110_CTRL_AC; data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; @@ -371,22 +356,32 @@ static int mag3110_write_raw(struct iio_dev *indio_dev, if (data->sleep_val < 40) data->ctrl_reg1 |= MAG3110_CTRL_AC; - ret = mag3110_change_config(data, MAG3110_CTRL_REG1, - data->ctrl_reg1); - break; + return mag3110_change_config(data, MAG3110_CTRL_REG1, + data->ctrl_reg1); + case IIO_CHAN_INFO_CALIBBIAS: - if (val < -10000 || val > 10000) { - ret = -EINVAL; - break; - } - ret = i2c_smbus_write_word_swapped(data->client, + if (val < -10000 || val > 10000) + return -EINVAL; + + return i2c_smbus_write_word_swapped(data->client, MAG3110_OFF_X + 2 * chan->scan_index, val << 1); - break; default: - ret = -EINVAL; - break; + return -EINVAL; } - iio_device_release_direct_mode(indio_dev); +} + +static int mag3110_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mag3110_data *data = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = __mag3110_write_raw(data, chan, val, val2, mask); + iio_device_release_direct(indio_dev); + return ret; } @@ -409,8 +404,8 @@ static irqreturn_t mag3110_trigger_handler(int irq, void *p) data->scan.temperature = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index dd480a4a5f98..e08a57cd6de2 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -556,13 +556,13 @@ MODULE_DEVICE_TABLE(of, mmc35240_of_match); static const struct acpi_device_id mmc35240_acpi_match[] = { {"MMC35240", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match); static const struct i2c_device_id mmc35240_id[] = { { "mmc35240" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mmc35240_id); diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index c99694a77a14..2b2884425746 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -399,12 +399,11 @@ static int rm3100_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 = rm3100_read_mag(data, chan->scan_index, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -516,8 +515,8 @@ static irqreturn_t rm3100_trigger_handler(int irq, void *p) * Always using the same buffer so that we wouldn't need to set the * paddings to 0 in case of leaking any data. */ - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, data->buffer, sizeof(data->buffer), + pf->timestamp); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c index dd6d48043740..2f60a41c07f7 100644 --- a/drivers/iio/magnetometer/rm3100-spi.c +++ b/drivers/iio/magnetometer/rm3100-spi.c @@ -32,7 +32,6 @@ static int rm3100_probe(struct spi_device *spi) spi->mode = SPI_MODE_0; /* Data rates cannot exceed 1Mbits. */ spi->max_speed_hz = 1000000; - spi->bits_per_word = 8; ret = spi_setup(spi); if (ret) return ret; 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/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 1672b274768d..ed70e782af5e 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -54,7 +54,7 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lsm303c-magn", .data = LSM303C_MAGN_DEV_NAME, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -102,7 +102,7 @@ static const struct i2c_device_id st_magn_id_table[] = { { LSM9DS1_MAGN_DEV_NAME }, { IIS2MDC_MAGN_DEV_NAME }, { LSM303C_MAGN_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index fe4d0e63133c..68816362bb95 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -49,7 +49,7 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lsm303c-magn", .data = LSM303C_MAGN_DEV_NAME, }, - {} + { } }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -94,7 +94,7 @@ static const struct spi_device_id st_magn_id_table[] = { { LSM9DS1_MAGN_DEV_NAME }, { IIS2MDC_MAGN_DEV_NAME }, { LSM303C_MAGN_DEV_NAME }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/magnetometer/tmag5273.c b/drivers/iio/magnetometer/tmag5273.c index 4187abe12784..2ca5c26f0091 100644 --- a/drivers/iio/magnetometer/tmag5273.c +++ b/drivers/iio/magnetometer/tmag5273.c @@ -712,13 +712,13 @@ static DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops, static const struct i2c_device_id tmag5273_id[] = { { "tmag5273" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, tmag5273_id); static const struct of_device_id tmag5273_of_match[] = { { .compatible = "ti,tmag5273" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, tmag5273_of_match); diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index c55a38650c0d..340607111d9a 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -236,7 +236,7 @@ struct yas5xx { */ struct { s32 channels[4]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -674,8 +674,8 @@ static void yas5xx_fill_buffer(struct iio_dev *indio_dev) yas5xx->scan.channels[1] = x; yas5xx->scan.channels[2] = y; yas5xx->scan.channels[3] = z; - iio_push_to_buffers_with_timestamp(indio_dev, &yas5xx->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &yas5xx->scan, sizeof(yas5xx->scan), + iio_get_time_ns(indio_dev)); } static irqreturn_t yas5xx_handle_trigger(int irq, void *p) @@ -1585,7 +1585,7 @@ static const struct i2c_device_id yas5xx_id[] = { {"yas532", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas532] }, {"yas533", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas533] }, {"yas537", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas537] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, yas5xx_id); @@ -1594,7 +1594,7 @@ static const struct of_device_id yas5xx_of_match[] = { { .compatible = "yamaha,yas532", &yas5xx_chip_info_tbl[yas532] }, { .compatible = "yamaha,yas533", &yas5xx_chip_info_tbl[yas533] }, { .compatible = "yamaha,yas537", &yas5xx_chip_info_tbl[yas537] }, - {} + { } }; MODULE_DEVICE_TABLE(of, yas5xx_of_match); diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index 2953403bef53..b742ca9a99d1 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -7,6 +7,7 @@ * Author: Peter Rosin <peda@axentia.se> */ +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/iio/consumer.h> #include <linux/iio/iio.h> @@ -237,49 +238,18 @@ static ssize_t mux_write_ext_info(struct iio_dev *indio_dev, uintptr_t private, return ret; } -static int mux_configure_channel(struct device *dev, struct mux *mux, - u32 state, const char *label, int idx) +static int mux_configure_chan_ext_info(struct device *dev, struct mux *mux, + int idx, int num_ext_info) { struct mux_child *child = &mux->child[idx]; - struct iio_chan_spec *chan = &mux->chan[idx]; struct iio_chan_spec const *pchan = mux->parent->channel; - char *page = NULL; - int num_ext_info; int i; int ret; - chan->indexed = 1; - chan->output = pchan->output; - chan->datasheet_name = label; - chan->ext_info = mux->ext_info; - - ret = iio_get_channel_type(mux->parent, &chan->type); - if (ret < 0) { - dev_err(dev, "failed to get parent channel type\n"); - return ret; - } - - if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW)) - chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); - if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE)) - chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); - - if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW)) - chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); - - if (state >= mux_control_states(mux->control)) { - dev_err(dev, "too many channels\n"); - return -EINVAL; - } - - chan->channel = state; + char *page __free(kfree) = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!page) + return -ENOMEM; - num_ext_info = iio_get_channel_ext_info_count(mux->parent); - if (num_ext_info) { - page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL); - if (!page) - return -ENOMEM; - } child->ext_info_cache = devm_kcalloc(dev, num_ext_info, sizeof(*child->ext_info_cache), @@ -318,8 +288,46 @@ static int mux_configure_channel(struct device *dev, struct mux *mux, child->ext_info_cache[i].size = ret; } - if (page) - devm_kfree(dev, page); + return 0; +} + +static int mux_configure_channel(struct device *dev, struct mux *mux, u32 state, + const char *label, int idx) +{ + struct iio_chan_spec *chan = &mux->chan[idx]; + struct iio_chan_spec const *pchan = mux->parent->channel; + int num_ext_info; + int ret; + + chan->indexed = 1; + chan->output = pchan->output; + chan->datasheet_name = label; + chan->ext_info = mux->ext_info; + + ret = iio_get_channel_type(mux->parent, &chan->type); + if (ret < 0) { + dev_err(dev, "failed to get parent channel type\n"); + return ret; + } + + if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW)) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); + if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE)) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); + + if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW)) + chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); + + if (state >= mux_control_states(mux->control)) { + dev_err(dev, "too many channels\n"); + return -EINVAL; + } + + chan->channel = state; + + num_ext_info = iio_get_channel_ext_info_count(mux->parent); + if (num_ext_info) + return mux_configure_chan_ext_info(dev, mux, idx, num_ext_info); return 0; } @@ -440,7 +448,7 @@ static int mux_probe(struct platform_device *pdev) static const struct of_device_id mux_match[] = { { .compatible = "io-channel-mux" }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, mux_match); diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 429035b65c65..4e23a598a3fb 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -399,7 +399,7 @@ static const struct platform_device_id hid_incl_3d_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200086", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_incl_3d_ids); diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 96f03988640c..e759f91a710a 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -19,7 +19,7 @@ struct dev_rot_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info quaternion; struct { - s32 sampled_vals[4] __aligned(16); + s32 sampled_vals[4]; aligned_s64 timestamp; } scan; int scale_pre_decml; @@ -351,7 +351,7 @@ static const struct platform_device_id hid_dev_rot_ids[] = { /* Geomagnetic orientation(AM) sensor */ .name = "HID-SENSOR-2000c1", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_dev_rot_ids); diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 423bbb8a3b38..bff7039690ac 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -358,7 +358,7 @@ static const struct platform_device_id hid_hinge_ids[] = { /* Format: HID-SENSOR-INT-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-INT-020b", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_hinge_ids); diff --git a/drivers/iio/potentiometer/ad5272.c b/drivers/iio/potentiometer/ad5272.c index b17941e4c2f7..672b1ca3a920 100644 --- a/drivers/iio/potentiometer/ad5272.c +++ b/drivers/iio/potentiometer/ad5272.c @@ -199,7 +199,7 @@ static const struct of_device_id ad5272_dt_ids[] = { { .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 }, { .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 }, { .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad5272_dt_ids); @@ -209,7 +209,7 @@ static const struct i2c_device_id ad5272_id[] = { { "ad5272-100", AD5272_100 }, { "ad5274-020", AD5274_020 }, { "ad5274-100", AD5274_100 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ad5272_id); diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c index e0526dd0e3cb..5761f69c538a 100644 --- a/drivers/iio/potentiometer/ds1803.c +++ b/drivers/iio/potentiometer/ds1803.c @@ -231,7 +231,7 @@ static const struct of_device_id ds1803_dt_ids[] = { { .compatible = "maxim,ds1803-050", .data = &ds1803_cfg[DS1803_050] }, { .compatible = "maxim,ds1803-100", .data = &ds1803_cfg[DS1803_100] }, { .compatible = "maxim,ds3502", .data = &ds1803_cfg[DS3502] }, - {} + { } }; MODULE_DEVICE_TABLE(of, ds1803_dt_ids); @@ -240,7 +240,7 @@ static const struct i2c_device_id ds1803_id[] = { { "ds1803-050", (kernel_ulong_t)&ds1803_cfg[DS1803_050] }, { "ds1803-100", (kernel_ulong_t)&ds1803_cfg[DS1803_100] }, { "ds3502", (kernel_ulong_t)&ds1803_cfg[DS3502] }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ds1803_id); diff --git a/drivers/iio/potentiometer/max5432.c b/drivers/iio/potentiometer/max5432.c index c8e2481dadb5..26390be79d02 100644 --- a/drivers/iio/potentiometer/max5432.c +++ b/drivers/iio/potentiometer/max5432.c @@ -114,7 +114,7 @@ static const struct of_device_id max5432_dt_ids[] = { { .compatible = "maxim,max5433", .data = (void *)MAX5432_OHM_100K }, { .compatible = "maxim,max5434", .data = (void *)MAX5432_OHM_50K }, { .compatible = "maxim,max5435", .data = (void *)MAX5432_OHM_100K }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, max5432_dt_ids); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 4838d2e72f53..3b11b991940b 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -137,7 +137,7 @@ static const struct acpi_device_id max5487_acpi_match[] = { { "MAX5487", 10 }, { "MAX5488", 50 }, { "MAX5489", 100 }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c index 44678d372126..a88bb2231850 100644 --- a/drivers/iio/potentiometer/mcp4018.c +++ b/drivers/iio/potentiometer/mcp4018.c @@ -117,7 +117,7 @@ static const struct i2c_device_id mcp4018_id[] = { MCP4018_ID_TABLE("mcp4019-103", MCP4018_103), MCP4018_ID_TABLE("mcp4019-503", MCP4018_503), MCP4018_ID_TABLE("mcp4019-104", MCP4018_104), - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, mcp4018_id); @@ -139,7 +139,7 @@ static const struct of_device_id mcp4018_of_match[] = { MCP4018_COMPATIBLE("microchip,mcp4019-103", MCP4018_103), MCP4018_COMPATIBLE("microchip,mcp4019-503", MCP4018_503), MCP4018_COMPATIBLE("microchip,mcp4019-104", MCP4018_104), - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, mcp4018_of_match); diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c index 2b73c7540209..f35fc4a6c55b 100644 --- a/drivers/iio/potentiometer/mcp41010.c +++ b/drivers/iio/potentiometer/mcp41010.c @@ -171,7 +171,7 @@ static const struct of_device_id mcp41010_match[] = { { .compatible = "microchip,mcp42010", .data = &mcp41010_cfg[MCP42010] }, { .compatible = "microchip,mcp42050", .data = &mcp41010_cfg[MCP42050] }, { .compatible = "microchip,mcp42100", .data = &mcp41010_cfg[MCP42100] }, - {} + { } }; MODULE_DEVICE_TABLE(of, mcp41010_match); @@ -182,7 +182,7 @@ static const struct spi_device_id mcp41010_id[] = { { "mcp42010", MCP42010 }, { "mcp42050", MCP42050 }, { "mcp42100", MCP42100 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, mcp41010_id); diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c index 7890c0993ec4..9082b559d029 100644 --- a/drivers/iio/potentiometer/mcp4131.c +++ b/drivers/iio/potentiometer/mcp4131.c @@ -403,7 +403,7 @@ static const struct of_device_id mcp4131_dt_ids[] = { .data = &mcp4131_cfg[MCP426x_503] }, { .compatible = "microchip,mcp4262-104", .data = &mcp4131_cfg[MCP426x_104] }, - {} + { } }; MODULE_DEVICE_TABLE(of, mcp4131_dt_ids); @@ -472,7 +472,7 @@ static const struct spi_device_id mcp4131_id[] = { { "mcp4262-103", MCP426x_103 }, { "mcp4262-503", MCP426x_503 }, { "mcp4262-104", MCP426x_104 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, mcp4131_id); diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index f28880ebd758..9912e91ff7b4 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -276,7 +276,7 @@ static const struct i2c_device_id mcp4531_id[] = { MCP4531_ID_TABLE("mcp4662-103", MCP466x_103), MCP4531_ID_TABLE("mcp4662-503", MCP466x_503), MCP4531_ID_TABLE("mcp4662-104", MCP466x_104), - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(i2c, mcp4531_id); @@ -350,7 +350,7 @@ static const struct of_device_id mcp4531_of_match[] = { MCP4531_COMPATIBLE("microchip,mcp4662-103", MCP466x_103), MCP4531_COMPATIBLE("microchip,mcp4662-503", MCP466x_503), MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104), - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(of, mcp4531_of_match); diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c index 8923ccb0fc4f..a42b57733363 100644 --- a/drivers/iio/potentiometer/tpl0102.c +++ b/drivers/iio/potentiometer/tpl0102.c @@ -153,7 +153,7 @@ static const struct i2c_device_id tpl0102_id[] = { { "cat5140-104", CAT5140_104 }, { "tpl0102-104", TPL0102_104 }, { "tpl0401-103", TPL0401_103 }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, tpl0102_id); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index c2c6b2b29867..030498d0b763 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -400,14 +400,14 @@ static void lmp91000_remove(struct i2c_client *client) static const struct of_device_id lmp91000_of_match[] = { { .compatible = "ti,lmp91000", }, { .compatible = "ti,lmp91002", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, lmp91000_of_match); static const struct i2c_device_id lmp91000_id[] = { { "lmp91000" }, { "lmp91002" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, lmp91000_id); diff --git a/drivers/iio/pressure/abp060mg.c b/drivers/iio/pressure/abp060mg.c index 752a63c06b44..a0d956c3e254 100644 --- a/drivers/iio/pressure/abp060mg.c +++ b/drivers/iio/pressure/abp060mg.c @@ -247,7 +247,7 @@ static const struct i2c_device_id abp060mg_id_table[] = { { "abp015pd", ABP015PD }, { "abp030pd", ABP030PD }, { "abp060pd", ABP060PD }, - { /* empty */ }, + { } }; MODULE_DEVICE_TABLE(i2c, abp060mg_id_table); diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 5376605b69b4..f37f20776c89 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -46,6 +46,7 @@ #include <linux/random.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/types.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> @@ -1002,7 +1003,7 @@ static int bmp280_preinit(struct bmp280_data *data) * after resetting, the device uses the complete power-on sequence so * it needs to wait for the defined start-up time. */ - fsleep(data->start_up_time); + fsleep(data->start_up_time_us); ret = regmap_read(data->regmap, BMP280_REG_STATUS, ®); if (ret) @@ -1105,9 +1106,13 @@ static irqreturn_t bmp280_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - u32 adc_temp, adc_press, comp_press; - s32 t_fine, comp_temp; - s32 *chans = (s32 *)data->sensor_data; + u32 adc_temp, adc_press; + s32 t_fine; + struct { + u32 comp_press; + s32 comp_temp; + aligned_s64 timestamp; + } buffer; int ret; guard(mutex)(&data->lock); @@ -1127,7 +1132,7 @@ static irqreturn_t bmp280_trigger_handler(int irq, void *p) goto out; } - comp_temp = bmp280_compensate_temp(data, adc_temp); + buffer.comp_temp = bmp280_compensate_temp(data, adc_temp); /* Pressure calculations */ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0])); @@ -1137,13 +1142,10 @@ static irqreturn_t bmp280_trigger_handler(int irq, void *p) } t_fine = bmp280_calc_t_fine(data, adc_temp); - comp_press = bmp280_compensate_press(data, adc_press, t_fine); - - chans[0] = comp_press; - chans[1] = comp_temp; + buffer.comp_press = bmp280_compensate_press(data, adc_press, t_fine); - iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -1161,7 +1163,7 @@ const struct bmp280_chip_info bmp280_chip_info = { .chip_id = bmp280_chip_ids, .num_chip_id = ARRAY_SIZE(bmp280_chip_ids), .regmap_config = &bmp280_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp280_channels, .num_channels = ARRAY_SIZE(bmp280_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -1225,11 +1227,19 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - u32 adc_temp, adc_press, adc_humidity, comp_press, comp_humidity; - s32 t_fine, comp_temp; - s32 *chans = (s32 *)data->sensor_data; + u32 adc_temp, adc_press, adc_humidity; + s32 t_fine; + struct { + u32 comp_press; + s32 comp_temp; + u32 comp_humidity; + aligned_s64 timestamp; + } buffer; int ret; + /* Don't leak uninitialized stack to userspace. */ + memset(&buffer, 0, sizeof(buffer)); + guard(mutex)(&data->lock); /* Burst read data registers */ @@ -1247,7 +1257,7 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) goto out; } - comp_temp = bmp280_compensate_temp(data, adc_temp); + buffer.comp_temp = bmp280_compensate_temp(data, adc_temp); /* Pressure calculations */ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0])); @@ -1257,7 +1267,7 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) } t_fine = bmp280_calc_t_fine(data, adc_temp); - comp_press = bmp280_compensate_press(data, adc_press, t_fine); + buffer.comp_press = bmp280_compensate_press(data, adc_press, t_fine); /* Humidity calculations */ adc_humidity = get_unaligned_be16(&data->buf[6]); @@ -1267,14 +1277,11 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) goto out; } - comp_humidity = bme280_compensate_humidity(data, adc_humidity, t_fine); + buffer.comp_humidity = bme280_compensate_humidity(data, adc_humidity, + t_fine); - chans[0] = comp_press; - chans[1] = comp_temp; - chans[2] = comp_humidity; - - iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -1347,7 +1354,7 @@ const struct bmp280_chip_info bme280_chip_info = { .chip_id = bme280_chip_ids, .num_chip_id = ARRAY_SIZE(bme280_chip_ids), .regmap_config = &bme280_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bme280_channels, .num_channels = ARRAY_SIZE(bme280_channels), .avail_scan_masks = bme280_avail_scan_masks, @@ -1414,7 +1421,7 @@ static int bmp380_cmd(struct bmp280_data *data, u8 cmd) return ret; } /* Wait for 2ms for command to be processed */ - usleep_range(data->start_up_time, data->start_up_time + 100); + fsleep(data->start_up_time_us); /* Check for command processing error */ ret = regmap_read(data->regmap, BMP380_REG_ERROR, ®); if (ret) { @@ -1806,7 +1813,7 @@ static int bmp380_chip_config(struct bmp280_data *data) * formula in datasheet section 3.9.2 with an offset of ~+15% * as it seen as well in table 3.9.1. */ - msleep(150); + fsleep(150 * USEC_PER_MSEC); /* Check config error flag */ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp); @@ -1899,9 +1906,13 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - u32 adc_temp, adc_press, comp_press; - s32 t_fine, comp_temp; - s32 *chans = (s32 *)data->sensor_data; + u32 adc_temp, adc_press; + s32 t_fine; + struct { + u32 comp_press; + s32 comp_temp; + aligned_s64 timestamp; + } buffer; int ret; guard(mutex)(&data->lock); @@ -1921,7 +1932,7 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) goto out; } - comp_temp = bmp380_compensate_temp(data, adc_temp); + buffer.comp_temp = bmp380_compensate_temp(data, adc_temp); /* Pressure calculations */ adc_press = get_unaligned_le24(&data->buf[0]); @@ -1931,13 +1942,10 @@ static irqreturn_t bmp380_trigger_handler(int irq, void *p) } t_fine = bmp380_calc_t_fine(data, adc_temp); - comp_press = bmp380_compensate_press(data, adc_press, t_fine); - - chans[0] = comp_press; - chans[1] = comp_temp; + buffer.comp_press = bmp380_compensate_press(data, adc_press, t_fine); - iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -1957,7 +1965,7 @@ const struct bmp280_chip_info bmp380_chip_info = { .num_chip_id = ARRAY_SIZE(bmp380_chip_ids), .regmap_config = &bmp380_regmap_config, .spi_read_extra_byte = true, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp380_channels, .num_channels = ARRAY_SIZE(bmp380_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -2006,7 +2014,8 @@ static int bmp580_soft_reset(struct bmp280_data *data) dev_err(data->dev, "failed to send reset command to device\n"); return ret; } - usleep_range(2000, 2500); + /* From datasheet's table 4: electrical characteristics */ + fsleep(2000); /* Dummy read of chip_id */ ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, ®); @@ -2208,7 +2217,7 @@ static int bmp580_nvmem_read_impl(void *priv, unsigned int offset, void *val, goto exit; } /* Wait standby transition time */ - usleep_range(2500, 3000); + fsleep(2500); while (bytes >= sizeof(*dst)) { addr = bmp580_nvmem_addrs[offset / sizeof(*dst)]; @@ -2274,7 +2283,7 @@ static int bmp580_nvmem_write_impl(void *priv, unsigned int offset, void *val, goto exit; } /* Wait standby transition time */ - usleep_range(2500, 3000); + fsleep(2500); while (bytes >= sizeof(*buf)) { addr = bmp580_nvmem_addrs[offset / sizeof(*buf)]; @@ -2458,7 +2467,7 @@ static int bmp580_chip_config(struct bmp280_data *data) return ret; } /* From datasheet's table 4: electrical characteristics */ - usleep_range(2500, 3000); + fsleep(2500); /* Set default DSP mode settings */ reg_val = FIELD_PREP(BMP580_DSP_COMP_MASK, BMP580_DSP_PRESS_TEMP_COMP_EN) | @@ -2607,7 +2616,12 @@ static irqreturn_t bmp580_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - int ret, offset; + struct { + __le32 comp_temp; + __le32 comp_press; + aligned_s64 timestamp; + } buffer; + int ret; guard(mutex)(&data->lock); @@ -2619,18 +2633,14 @@ static irqreturn_t bmp580_trigger_handler(int irq, void *p) goto out; } - offset = 0; - /* Pressure calculations */ - memcpy(&data->sensor_data[offset], &data->buf[3], 3); - - offset += sizeof(s32); + memcpy(&buffer.comp_press, &data->buf[3], 3); /* Temperature calculations */ - memcpy(&data->sensor_data[offset], &data->buf[0], 3); + memcpy(&buffer.comp_temp, &data->buf[0], 3); - iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -2649,7 +2659,7 @@ const struct bmp280_chip_info bmp580_chip_info = { .chip_id = bmp580_chip_ids, .num_chip_id = ARRAY_SIZE(bmp580_chip_ids), .regmap_config = &bmp580_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp580_channels, .num_channels = ARRAY_SIZE(bmp580_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -2720,7 +2730,7 @@ static int bmp180_wait_for_eoc(struct bmp280_data *data, u8 ctrl_meas) delay_us = conversion_time_max[data->oversampling_press]; - usleep_range(delay_us, delay_us + 1000); + fsleep(delay_us); } ret = regmap_read(data->regmap, BMP280_REG_CTRL_MEAS, &ctrl); @@ -2951,25 +2961,26 @@ static irqreturn_t bmp180_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bmp280_data *data = iio_priv(indio_dev); - int ret, comp_temp, comp_press; - s32 *chans = (s32 *)data->sensor_data; + struct { + u32 comp_press; + s32 comp_temp; + aligned_s64 timestamp; + } buffer; + int ret; guard(mutex)(&data->lock); - ret = bmp180_read_temp(data, &comp_temp); + ret = bmp180_read_temp(data, &buffer.comp_temp); if (ret) goto out; - ret = bmp180_read_press(data, &comp_press); + ret = bmp180_read_press(data, &buffer.comp_press); if (ret) goto out; - chans[0] = comp_press; - chans[1] = comp_temp; - - iio_push_to_buffers_with_timestamp(indio_dev, data->sensor_data, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -2988,7 +2999,7 @@ const struct bmp280_chip_info bmp180_chip_info = { .chip_id = bmp180_chip_ids, .num_chip_id = ARRAY_SIZE(bmp180_chip_ids), .regmap_config = &bmp180_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp280_channels, .num_channels = ARRAY_SIZE(bmp280_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -3066,7 +3077,7 @@ const struct bmp280_chip_info bmp085_chip_info = { .chip_id = bmp180_chip_ids, .num_chip_id = ARRAY_SIZE(bmp180_chip_ids), .regmap_config = &bmp180_regmap_config, - .start_up_time = 2000, + .start_up_time_us = 2000, .channels = bmp280_channels, .num_channels = ARRAY_SIZE(bmp280_channels), .avail_scan_masks = bmp280_avail_scan_masks, @@ -3175,7 +3186,7 @@ int bmp280_common_probe(struct device *dev, data->oversampling_temp = chip_info->oversampling_temp_default; data->iir_filter_coeff = chip_info->iir_filter_coeff_default; data->sampling_freq = chip_info->sampling_freq_default; - data->start_up_time = chip_info->start_up_time; + data->start_up_time_us = chip_info->start_up_time_us; /* Bring up regulators */ regulator_bulk_set_supply_names(data->supplies, @@ -3201,7 +3212,7 @@ int bmp280_common_probe(struct device *dev, return ret; /* Wait to make sure we started up properly */ - usleep_range(data->start_up_time, data->start_up_time + 100); + fsleep(data->start_up_time_us); /* Bring chip out of reset if there is an assigned GPIO line */ gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); @@ -3287,7 +3298,7 @@ int bmp280_common_probe(struct device *dev, * Set autosuspend to two orders of magnitude larger than the * start-up time. */ - pm_runtime_set_autosuspend_delay(dev, data->start_up_time / 10); + pm_runtime_set_autosuspend_delay(dev, data->start_up_time_us / 10); pm_runtime_use_autosuspend(dev); pm_runtime_put(dev); @@ -3306,7 +3317,7 @@ static int bmp280_runtime_suspend(struct device *dev) data->chip_info->set_mode(data, BMP280_SLEEP); - fsleep(data->start_up_time); + fsleep(data->start_up_time_us); return regulator_bulk_disable(BMP280_NUM_SUPPLIES, data->supplies); } @@ -3320,7 +3331,7 @@ static int bmp280_runtime_resume(struct device *dev) if (ret) return ret; - usleep_range(data->start_up_time, data->start_up_time + 100); + fsleep(data->start_up_time_us); ret = data->chip_info->chip_config(data); if (ret) diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c index 868e1b2ec711..8e459b6c97ff 100644 --- a/drivers/iio/pressure/bmp280-i2c.c +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -33,7 +33,7 @@ static const struct of_device_id bmp280_of_i2c_match[] = { { .compatible = "bosch,bme280", .data = &bme280_chip_info }, { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, { .compatible = "bosch,bmp580", .data = &bmp580_chip_info }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); @@ -44,7 +44,7 @@ static const struct i2c_device_id bmp280_i2c_id[] = { {"bme280", (kernel_ulong_t)&bme280_chip_info }, {"bmp380", (kernel_ulong_t)&bmp380_chip_info }, {"bmp580", (kernel_ulong_t)&bmp580_chip_info }, - { }, + { } }; MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c index 0e6e27892f99..3b90384f17d7 100644 --- a/drivers/iio/pressure/bmp280-spi.c +++ b/drivers/iio/pressure/bmp280-spi.c @@ -81,14 +81,6 @@ static int bmp280_spi_probe(struct spi_device *spi) const struct bmp280_chip_info *chip_info; struct regmap_bus const *bmp_regmap_bus; struct regmap *regmap; - int ret; - - spi->bits_per_word = 8; - ret = spi_setup(spi); - if (ret < 0) { - dev_err(&spi->dev, "spi_setup failed!\n"); - return ret; - } chip_info = spi_get_device_match_data(spi); @@ -121,7 +113,7 @@ static const struct of_device_id bmp280_of_spi_match[] = { { .compatible = "bosch,bme280", .data = &bme280_chip_info }, { .compatible = "bosch,bmp380", .data = &bmp380_chip_info }, { .compatible = "bosch,bmp580", .data = &bmp580_chip_info }, - { }, + { } }; MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 2df1175b6b85..25bb9c743a05 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -349,7 +349,6 @@ BMP280_NUM_TEMP_BYTES + \ BME280_NUM_HUMIDITY_BYTES) -#define BME280_NUM_MAX_CHANNELS 3 /* Core exported structs */ static const char *const bmp280_supply_names[] = { @@ -434,7 +433,7 @@ struct bmp280_data { struct bmp380_calib bmp380; } calib; struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; - unsigned int start_up_time; /* in microseconds */ + unsigned int start_up_time_us; /* log of base 2 of oversampling rate */ u8 oversampling_press; @@ -452,13 +451,6 @@ struct bmp280_data { */ int sampling_freq; - /* - * Data to push to userspace triggered buffer. Up to 3 channels and - * s64 timestamp, aligned. - */ - u8 sensor_data[ALIGN(sizeof(s32) * BME280_NUM_MAX_CHANNELS, sizeof(s64)) - + sizeof(s64)] __aligned(sizeof(s64)); - /* Value to hold the current operation mode of the device */ enum bmp280_op_mode op_mode; @@ -470,8 +462,8 @@ struct bmp280_data { /* Sensor data buffer */ u8 buf[BME280_BURST_READ_BYTES]; /* Calibration data buffers */ - __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; - __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; + __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / sizeof(__le16)]; + __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / sizeof(__be16)]; u8 bme280_humid_cal_buf[BME280_CONTIGUOUS_CALIB_REGS]; u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; /* Miscellaneous, endianness-aware data buffers */ @@ -490,7 +482,7 @@ struct bmp280_chip_info { const struct iio_chan_spec *channels; int num_channels; - unsigned int start_up_time; + unsigned int start_up_time_us; const unsigned long *avail_scan_masks; const int *oversampling_temp_avail; diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c index 2649c2f89e89..c6b950c596c1 100644 --- a/drivers/iio/pressure/cros_ec_baro.c +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -192,7 +192,7 @@ static const struct platform_device_id cros_ec_baro_ids[] = { { .name = "cros-ec-baro", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids); diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index e99e97ea6300..48afe5c94000 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -147,12 +147,11 @@ static int dlh_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 = dlh_read_direct(st, &pressure, &temperature); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret) return ret; @@ -344,14 +343,14 @@ static int dlh_probe(struct i2c_client *client) static const struct of_device_id dlh_of_match[] = { { .compatible = "asc,dlhl60d" }, { .compatible = "asc,dlhl60g" }, - {} + { } }; MODULE_DEVICE_TABLE(of, dlh_of_match); static const struct i2c_device_id dlh_id[] = { { "dlhl60d", dlhl60d }, { "dlhl60g", dlhl60g }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, dlh_id); diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index c6f44f0f4d2e..8edaa4d10a70 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -888,13 +888,13 @@ static int dps310_probe(struct i2c_client *client) static const struct i2c_device_id dps310_id[] = { { DPS310_DEV_NAME }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, dps310_id); static const struct acpi_device_id dps310_acpi_match[] = { { "IFX3100" }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, dps310_acpi_match); diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index f7273d30c5f0..5f1d6abda3e4 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -176,8 +176,9 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev, if (!press_state->timestamp) press_state->timestamp = iio_get_time_ns(indio_dev); - iio_push_to_buffers_with_timestamp( - indio_dev, &press_state->scan, press_state->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &press_state->scan, + sizeof(press_state->scan), + press_state->timestamp); } return 0; @@ -339,7 +340,7 @@ static const struct platform_device_id hid_press_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200031", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_press_ids); diff --git a/drivers/iio/pressure/hp03.c b/drivers/iio/pressure/hp03.c index 6f7a16787143..cbb4aaf45e2c 100644 --- a/drivers/iio/pressure/hp03.c +++ b/drivers/iio/pressure/hp03.c @@ -273,7 +273,7 @@ MODULE_DEVICE_TABLE(i2c, hp03_id); static const struct of_device_id hp03_of_match[] = { { .compatible = "hoperf,hp03" }, - { }, + { } }; MODULE_DEVICE_TABLE(of, hp03_of_match); diff --git a/drivers/iio/pressure/hp206c.c b/drivers/iio/pressure/hp206c.c index 442740941933..abe10ccb6770 100644 --- a/drivers/iio/pressure/hp206c.c +++ b/drivers/iio/pressure/hp206c.c @@ -396,13 +396,13 @@ static int hp206c_probe(struct i2c_client *client) static const struct i2c_device_id hp206c_id[] = { {"hp206c"}, - {} + { } }; MODULE_DEVICE_TABLE(i2c, hp206c_id); static const struct acpi_device_id hp206c_acpi_match[] = { {"HOP206C", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, hp206c_acpi_match); diff --git a/drivers/iio/pressure/hsc030pa.c b/drivers/iio/pressure/hsc030pa.c index 168245818cfe..2d00c0656259 100644 --- a/drivers/iio/pressure/hsc030pa.c +++ b/drivers/iio/pressure/hsc030pa.c @@ -314,8 +314,8 @@ static irqreturn_t hsc_trigger_handler(int irq, void *private) memcpy(&data->scan.chan[0], &data->buffer[0], 2); memcpy(&data->scan.chan[1], &data->buffer[2], 2); - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); error: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/pressure/hsc030pa.h b/drivers/iio/pressure/hsc030pa.h index 9b40f46f575f..5db46784f4c6 100644 --- a/drivers/iio/pressure/hsc030pa.h +++ b/drivers/iio/pressure/hsc030pa.h @@ -58,7 +58,7 @@ struct hsc_data { s32 p_offset_dec; struct { __be16 chan[2]; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/pressure/hsc030pa_i2c.c b/drivers/iio/pressure/hsc030pa_i2c.c index 7f2398aa8155..a34ef4653f34 100644 --- a/drivers/iio/pressure/hsc030pa_i2c.c +++ b/drivers/iio/pressure/hsc030pa_i2c.c @@ -48,13 +48,13 @@ static int hsc_i2c_probe(struct i2c_client *client) static const struct of_device_id hsc_i2c_match[] = { { .compatible = "honeywell,hsc030pa" }, - {} + { } }; MODULE_DEVICE_TABLE(of, hsc_i2c_match); static const struct i2c_device_id hsc_i2c_id[] = { { "hsc030pa" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, hsc_i2c_id); diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c index 60768726e9ad..5d331b3b6da8 100644 --- a/drivers/iio/pressure/hsc030pa_spi.c +++ b/drivers/iio/pressure/hsc030pa_spi.c @@ -35,13 +35,13 @@ static int hsc_spi_probe(struct spi_device *spi) static const struct of_device_id hsc_spi_match[] = { { .compatible = "honeywell,hsc030pa" }, - {} + { } }; MODULE_DEVICE_TABLE(of, hsc_spi_match); static const struct spi_device_id hsc_spi_id[] = { { "hsc030pa" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, hsc_spi_id); diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c index 3e0bf5d31ad7..1951c1cc84cf 100644 --- a/drivers/iio/pressure/icp10100.c +++ b/drivers/iio/pressure/icp10100.c @@ -343,9 +343,8 @@ static int icp10100_read_raw_measures(struct iio_dev *indio_dev, uint32_t pressure_mPa; int ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = icp10100_get_measures(st, &raw_pressure, &raw_temp); if (ret) @@ -370,7 +369,7 @@ static int icp10100_read_raw_measures(struct iio_dev *indio_dev, } error_release: - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -439,7 +438,6 @@ static int icp10100_write_raw(struct iio_dev *indio_dev, { struct icp10100_state *st = iio_priv(indio_dev); unsigned int mode; - int ret; switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -449,13 +447,12 @@ static int icp10100_write_raw(struct iio_dev *indio_dev, mode = ilog2(val); if (mode >= ICP10100_MODE_NB) 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); st->mode = mode; 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/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c index 888cfa666238..4e1d24beff94 100644 --- a/drivers/iio/pressure/mpl115_spi.c +++ b/drivers/iio/pressure/mpl115_spi.c @@ -85,7 +85,7 @@ static int mpl115_spi_probe(struct spi_device *spi) static const struct spi_device_id mpl115_spi_ids[] = { { "mpl115", 0 }, - {} + { } }; MODULE_DEVICE_TABLE(spi, mpl115_spi_ids); diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 71ded2eee060..d6715997f137 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -69,6 +69,52 @@ static int mpl3115_request(struct mpl3115_data *data) return 0; } +static int mpl3115_read_info_raw(struct mpl3115_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + switch (chan->type) { + case IIO_PRESSURE: { /* in 0.25 pascal / LSB */ + __be32 tmp = 0; + + guard(mutex)(&data->lock); + ret = mpl3115_request(data); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_i2c_block_data(data->client, + MPL3115_OUT_PRESS, + 3, (u8 *) &tmp); + if (ret < 0) + return ret; + + *val = be32_to_cpu(tmp) >> chan->scan_type.shift; + return IIO_VAL_INT; + } + case IIO_TEMP: { /* in 0.0625 celsius / LSB */ + __be16 tmp; + + guard(mutex)(&data->lock); + ret = mpl3115_request(data); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_i2c_block_data(data->client, + MPL3115_OUT_TEMP, + 2, (u8 *) &tmp); + if (ret < 0) + return ret; + + *val = sign_extend32(be16_to_cpu(tmp) >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + } + default: + return -EINVAL; + } +} + static int mpl3115_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -78,54 +124,11 @@ static int mpl3115_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; - - switch (chan->type) { - case IIO_PRESSURE: { /* in 0.25 pascal / LSB */ - __be32 tmp = 0; - - mutex_lock(&data->lock); - ret = mpl3115_request(data); - if (ret < 0) { - mutex_unlock(&data->lock); - break; - } - ret = i2c_smbus_read_i2c_block_data(data->client, - MPL3115_OUT_PRESS, 3, (u8 *) &tmp); - mutex_unlock(&data->lock); - if (ret < 0) - break; - *val = be32_to_cpu(tmp) >> chan->scan_type.shift; - ret = IIO_VAL_INT; - break; - } - case IIO_TEMP: { /* in 0.0625 celsius / LSB */ - __be16 tmp; - - mutex_lock(&data->lock); - ret = mpl3115_request(data); - if (ret < 0) { - mutex_unlock(&data->lock); - break; - } - ret = i2c_smbus_read_i2c_block_data(data->client, - MPL3115_OUT_TEMP, 2, (u8 *) &tmp); - mutex_unlock(&data->lock); - if (ret < 0) - break; - *val = sign_extend32(be16_to_cpu(tmp) >> chan->scan_type.shift, - chan->scan_type.realbits - 1); - ret = IIO_VAL_INT; - break; - } - default: - ret = -EINVAL; - break; - } + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - iio_device_release_direct_mode(indio_dev); + ret = mpl3115_read_info_raw(data, chan, val); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -188,8 +191,8 @@ static irqreturn_t mpl3115_trigger_handler(int irq, void *p) } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, buffer, sizeof(buffer), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/pressure/mprls0025pa.h b/drivers/iio/pressure/mprls0025pa.h index 9d5c30afa9d6..d62a018eaff3 100644 --- a/drivers/iio/pressure/mprls0025pa.h +++ b/drivers/iio/pressure/mprls0025pa.h @@ -34,16 +34,6 @@ struct iio_dev; struct mpr_data; struct mpr_ops; -/** - * struct mpr_chan - * @pres: pressure value - * @ts: timestamp - */ -struct mpr_chan { - s32 pres; - s64 ts; -}; - enum mpr_func_id { MPR_FUNCTION_A, MPR_FUNCTION_B, @@ -69,6 +59,8 @@ enum mpr_func_id { * reading in a loop until data is ready * @completion: handshake from irq to read * @chan: channel values for buffered mode + * @chan.pres: pressure value + * @chan.ts: timestamp * @buffer: raw conversion data */ struct mpr_data { @@ -87,7 +79,10 @@ struct mpr_data { struct gpio_desc *gpiod_reset; int irq; struct completion completion; - struct mpr_chan chan; + struct { + s32 pres; + aligned_s64 ts; + } chan; u8 buffer[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN); }; diff --git a/drivers/iio/pressure/mprls0025pa_i2c.c b/drivers/iio/pressure/mprls0025pa_i2c.c index 48b23a4256ce..1a48f8d43d71 100644 --- a/drivers/iio/pressure/mprls0025pa_i2c.c +++ b/drivers/iio/pressure/mprls0025pa_i2c.c @@ -74,13 +74,13 @@ static int mpr_i2c_probe(struct i2c_client *client) static const struct of_device_id mpr_i2c_match[] = { { .compatible = "honeywell,mprls0025pa" }, - {} + { } }; MODULE_DEVICE_TABLE(of, mpr_i2c_match); static const struct i2c_device_id mpr_i2c_id[] = { { "mprls0025pa" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mpr_i2c_id); diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c index 09f724c76d70..d04102f8a4a0 100644 --- a/drivers/iio/pressure/mprls0025pa_spi.c +++ b/drivers/iio/pressure/mprls0025pa_spi.c @@ -66,13 +66,13 @@ static int mpr_spi_probe(struct spi_device *spi) static const struct of_device_id mpr_spi_match[] = { { .compatible = "honeywell,mprls0025pa" }, - {} + { } }; MODULE_DEVICE_TABLE(of, mpr_spi_match); static const struct spi_device_id mpr_spi_id[] = { { "mprls0025pa" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, mpr_spi_id); diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 056c8271c49d..bdac27bd5a5d 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -213,7 +213,7 @@ static irqreturn_t ms5611_trigger_handler(int irq, void *p) /* Ensure buffer elements are naturally aligned */ struct { s32 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; int ret; @@ -308,7 +308,6 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, { struct ms5611_state *st = iio_priv(indio_dev); const struct ms5611_osr *osr = NULL; - int ret; if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) return -EINVAL; @@ -322,9 +321,8 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, if (!osr) 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); @@ -334,7 +332,7 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, st->pressure_osr = osr; mutex_unlock(&st->lock); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return 0; } diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index b5a91e885793..25c7bd2d8fdf 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -92,7 +92,6 @@ static int ms5611_spi_probe(struct spi_device *spi) spi->mode = SPI_MODE_0; spi->max_speed_hz = min(spi->max_speed_hz, 20000000U); - spi->bits_per_word = 8; ret = spi_setup(spi); if (ret < 0) return ret; diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index a1767a17fdce..59705a666979 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -219,7 +219,7 @@ static const struct i2c_device_id ms5637_id[] = { {"ms5805", (kernel_ulong_t)&ms5805_data }, {"ms5837", (kernel_ulong_t)&ms5837_data }, {"ms8607-temppressure", (kernel_ulong_t)&ms8607_data }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, ms5637_id); @@ -229,7 +229,7 @@ static const struct of_device_id ms5637_of_match[] = { { .compatible = "meas,ms5805", .data = &ms5805_data }, { .compatible = "meas,ms5837", .data = &ms5837_data }, { .compatible = "meas,ms8607-temppressure", .data = &ms8607_data }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ms5637_of_match); diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c index f24d9f927681..dac27fd359ad 100644 --- a/drivers/iio/pressure/rohm-bm1390.c +++ b/drivers/iio/pressure/rohm-bm1390.c @@ -8,6 +8,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/i2c.h> #include <linux/module.h> @@ -138,7 +139,7 @@ enum { struct bm1390_data_buf { u32 pressure; __be16 temp; - s64 ts __aligned(8); + aligned_s64 ts; }; /* BM1390 has FIFO for 4 pressure samples */ @@ -263,14 +264,14 @@ static int bm1390_read_data(struct bm1390_data *data, { int ret, warn; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); /* * We use 'continuous mode' even for raw read because according to the * data-sheet an one-shot mode can't be used with IIR filter. */ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); if (ret) - goto unlock_out; + return ret; switch (chan->type) { case IIO_PRESSURE: @@ -287,10 +288,8 @@ static int bm1390_read_data(struct bm1390_data *data, warn = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); if (warn) dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn); -unlock_out: - mutex_unlock(&data->mutex); - return ret; + return 0; } static int bm1390_read_raw(struct iio_dev *idev, @@ -320,12 +319,11 @@ static int bm1390_read_raw(struct iio_dev *idev, return -EINVAL; case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(idev); - if (ret) - return ret; + if (!iio_device_claim_direct(idev)) + return -EBUSY; ret = bm1390_read_data(data, chan, val, val2); - iio_device_release_direct_mode(idev); + iio_device_release_direct(idev); if (ret) return ret; @@ -543,38 +541,33 @@ static int bm1390_fifo_enable(struct iio_dev *idev) if (data->irq <= 0) return -EINVAL; - mutex_lock(&data->mutex); - if (data->trigger_enabled) { - ret = -EBUSY; - goto unlock_out; - } + guard(mutex)(&data->mutex); + + if (data->trigger_enabled) + return -EBUSY; /* Update watermark to HW */ ret = bm1390_fifo_set_wmi(data); if (ret) - goto unlock_out; + return ret; /* Enable WMI_IRQ */ ret = regmap_set_bits(data->regmap, BM1390_REG_MODE_CTRL, BM1390_MASK_WMI_EN); if (ret) - goto unlock_out; + return ret; /* Enable FIFO */ ret = regmap_set_bits(data->regmap, BM1390_REG_FIFO_CTRL, BM1390_MASK_FIFO_EN); if (ret) - goto unlock_out; + return ret; data->state = BM1390_STATE_FIFO; data->old_timestamp = iio_get_time_ns(idev); - ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); - -unlock_out: - mutex_unlock(&data->mutex); - return ret; + return bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); } static int bm1390_fifo_disable(struct iio_dev *idev) @@ -584,27 +577,22 @@ static int bm1390_fifo_disable(struct iio_dev *idev) msleep(1); - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); if (ret) - goto unlock_out; + return ret; /* Disable FIFO */ ret = regmap_clear_bits(data->regmap, BM1390_REG_FIFO_CTRL, BM1390_MASK_FIFO_EN); if (ret) - goto unlock_out; + return ret; data->state = BM1390_STATE_SAMPLE; /* Disable WMI_IRQ */ - ret = regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL, + return regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL, BM1390_MASK_WMI_EN); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; } static int bm1390_buffer_postenable(struct iio_dev *idev) @@ -664,7 +652,8 @@ static irqreturn_t bm1390_trigger_handler(int irq, void *p) } } - iio_push_to_buffers_with_timestamp(idev, &data->buf, data->timestamp); + iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf), + data->timestamp); iio_trigger_notify_done(idev->trig); return IRQ_HANDLED; @@ -688,25 +677,24 @@ static irqreturn_t bm1390_irq_thread_handler(int irq, void *private) { struct iio_dev *idev = private; struct bm1390_data *data = iio_priv(idev); - int ret = IRQ_NONE; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled) { iio_trigger_poll_nested(data->trig); - ret = IRQ_HANDLED; - } else if (data->state == BM1390_STATE_FIFO) { + return IRQ_HANDLED; + } + + if (data->state == BM1390_STATE_FIFO) { int ok; ok = __bm1390_fifo_flush(idev, BM1390_FIFO_LENGTH, data->timestamp); if (ok > 0) - ret = IRQ_HANDLED; + return IRQ_HANDLED; } - mutex_unlock(&data->mutex); - - return ret; + return IRQ_NONE; } static int bm1390_set_drdy_irq(struct bm1390_data *data, bool en) @@ -722,17 +710,16 @@ static int bm1390_trigger_set_state(struct iio_trigger *trig, bool state) { struct bm1390_data *data = iio_trigger_get_drvdata(trig); - int ret = 0; + int ret; - mutex_lock(&data->mutex); + guard(mutex)(&data->mutex); if (data->trigger_enabled == state) - goto unlock_out; + return 0; if (data->state == BM1390_STATE_FIFO) { dev_warn(data->dev, "Can't set trigger when FIFO enabled\n"); - ret = -EBUSY; - goto unlock_out; + return -EBUSY; } data->trigger_enabled = state; @@ -740,13 +727,13 @@ static int bm1390_trigger_set_state(struct iio_trigger *trig, if (state) { ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS); if (ret) - goto unlock_out; + return ret; } else { int dummy; ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP); if (ret) - goto unlock_out; + return ret; /* * We need to read the status register in order to ACK the @@ -758,12 +745,7 @@ static int bm1390_trigger_set_state(struct iio_trigger *trig, dev_warn(data->dev, "status read failed\n"); } - ret = bm1390_set_drdy_irq(data, state); - -unlock_out: - mutex_unlock(&data->mutex); - - return ret; + return bm1390_set_drdy_irq(data, state); } static const struct iio_trigger_ops bm1390_trigger_ops = { @@ -901,13 +883,13 @@ static int bm1390_probe(struct i2c_client *i2c) static const struct of_device_id bm1390_of_match[] = { { .compatible = "rohm,bm1390glv-z" }, - {} + { } }; MODULE_DEVICE_TABLE(of, bm1390_of_match); static const struct i2c_device_id bm1390_id[] = { { "bm1390glv-z", }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, bm1390_id); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index b7b66ddc3a73..0f50bac1fb4d 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -50,13 +50,13 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps22df", .data = LPS22DF_PRESS_DEV_NAME, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_press_of_match); static const struct acpi_device_id st_press_acpi_match[] = { {"SNO9210", LPS22HB}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, st_press_acpi_match); @@ -69,7 +69,7 @@ static const struct i2c_device_id st_press_id_table[] = { { LPS35HW_PRESS_DEV_NAME, LPS35HW }, { LPS22HH_PRESS_DEV_NAME, LPS22HH }, { LPS22DF_PRESS_DEV_NAME, LPS22DF }, - {}, + { } }; MODULE_DEVICE_TABLE(i2c, st_press_id_table); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 1a4bd1a0f787..39827e6841ca 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -55,7 +55,7 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps22df", .data = LPS22DF_PRESS_DEV_NAME, }, - {}, + { } }; MODULE_DEVICE_TABLE(of, st_press_of_match); @@ -106,7 +106,7 @@ static const struct spi_device_id st_press_id_table[] = { { "lps25h-press", }, { "lps331ap-press" }, { "lps22hb-press" }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, st_press_id_table); diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 9db1c94dfc18..1640aa3717ed 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -582,7 +582,7 @@ static int zpa2326_fill_sample_buffer(struct iio_dev *indio_dev, struct { u32 pressure; u16 temperature; - u64 timestamp; + aligned_s64 timestamp; } sample; int err; @@ -618,8 +618,8 @@ static int zpa2326_fill_sample_buffer(struct iio_dev *indio_dev, */ zpa2326_dbg(indio_dev, "filling raw samples buffer"); - iio_push_to_buffers_with_timestamp(indio_dev, &sample, - private->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &sample, sizeof(sample), + private->timestamp); return 0; } @@ -1062,9 +1062,8 @@ static int zpa2326_sample_oneshot(struct iio_dev *indio_dev, int ret; struct zpa2326_private *priv; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; ret = zpa2326_resume(indio_dev); if (ret < 0) @@ -1120,7 +1119,7 @@ static int zpa2326_sample_oneshot(struct iio_dev *indio_dev, suspend: zpa2326_suspend(indio_dev); release: - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; } @@ -1438,7 +1437,6 @@ static int zpa2326_set_frequency(struct iio_dev *indio_dev, int hz) { struct zpa2326_private *priv = iio_priv(indio_dev); int freq; - int err; /* Check if requested frequency is supported. */ for (freq = 0; freq < ARRAY_SIZE(zpa2326_sampling_frequencies); freq++) @@ -1448,13 +1446,12 @@ static int zpa2326_set_frequency(struct iio_dev *indio_dev, int hz) return -EINVAL; /* Don't allow changing frequency if buffered sampling is ongoing. */ - err = iio_device_claim_direct_mode(indio_dev); - if (err) - return err; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; priv->frequency = &zpa2326_sampling_frequencies[freq]; - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return 0; } 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..af756e2b0f31 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) @@ -48,7 +47,6 @@ static int zpa2326_probe_spi(struct spi_device *spi) */ spi->mode = SPI_MODE_3; spi->max_speed_hz = min(spi->max_speed_hz, 1000000U); - spi->bits_per_word = 8; err = spi_setup(spi); if (err < 0) return err; @@ -64,7 +62,7 @@ static void zpa2326_remove_spi(struct spi_device *spi) static const struct spi_device_id zpa2326_spi_ids[] = { { "zpa2326", 0 }, - { }, + { } }; MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids); diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 96fa97451cbf..f1018b14aecf 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -63,7 +63,7 @@ struct as3935_state { /* Ensure timestamp is naturally aligned */ struct { u8 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; u8 buf[2] __aligned(IIO_DMA_MINALIGN); }; @@ -231,8 +231,8 @@ static irqreturn_t as3935_trigger_handler(int irq, void *private) goto err_read; st->scan.chan = val & AS3935_DATA_MASK; - iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + iio_get_time_ns(indio_dev)); err_read: iio_trigger_notify_done(indio_dev->trig); @@ -444,13 +444,13 @@ static int as3935_probe(struct spi_device *spi) static const struct of_device_id as3935_of_match[] = { { .compatible = "ams,as3935", }, - { /* sentinel */ }, + { } }; MODULE_DEVICE_TABLE(of, as3935_of_match); static const struct spi_device_id as3935_id[] = { {"as3935", 0}, - {}, + { } }; MODULE_DEVICE_TABLE(spi, as3935_id); diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c index cdd254da9e50..3472a2c36e44 100644 --- a/drivers/iio/proximity/aw96103.c +++ b/drivers/iio/proximity/aw96103.c @@ -433,7 +433,7 @@ static int aw96103_write_event_config(struct iio_dev *indio_dev, state ? BIT(chan->channel) : 0); } -static struct iio_info iio_info = { +static const struct iio_info iio_info = { .read_raw = aw96103_read_raw, .read_event_value = aw96103_read_event_val, .write_event_value = aw96103_write_event_val, diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c index 667369be0555..1f9de7066ebf 100644 --- a/drivers/iio/proximity/cros_ec_mkbp_proximity.c +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -59,16 +59,11 @@ static int cros_ec_mkbp_proximity_parse_state(const void *data) static int cros_ec_mkbp_proximity_query(struct cros_ec_device *ec_dev, int *state) { - struct { - struct cros_ec_command msg; - union { - struct ec_params_mkbp_info params; - u32 switches; - }; - } __packed buf = { }; - struct ec_params_mkbp_info *params = &buf.params; - struct cros_ec_command *msg = &buf.msg; - u32 *switches = &buf.switches; + DEFINE_RAW_FLEX(struct cros_ec_command, buf, data, + MAX(sizeof(u32), sizeof(struct ec_params_mkbp_info))); + struct ec_params_mkbp_info *params = (struct ec_params_mkbp_info *)buf->data; + struct cros_ec_command *msg = buf; + u32 *switches = (u32 *)buf->data; size_t insize = sizeof(*switches); int ret; @@ -250,7 +245,7 @@ static void cros_ec_mkbp_proximity_remove(struct platform_device *pdev) static const struct of_device_id cros_ec_mkbp_proximity_of_match[] = { { .compatible = "google,cros-ec-mkbp-proximity" }, - {} + { } }; MODULE_DEVICE_TABLE(of, cros_ec_mkbp_proximity_of_match); diff --git a/drivers/iio/proximity/hx9023s.c b/drivers/iio/proximity/hx9023s.c index 4021feb7a7ac..33781c314728 100644 --- a/drivers/iio/proximity/hx9023s.c +++ b/drivers/iio/proximity/hx9023s.c @@ -14,6 +14,7 @@ #include <linux/cleanup.h> #include <linux/device.h> #include <linux/errno.h> +#include <linux/firmware.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/irqreturn.h> @@ -100,6 +101,17 @@ #define HX9023S_INTERRUPT_MASK GENMASK(9, 0) #define HX9023S_PROX_DEBOUNCE_MASK GENMASK(3, 0) +#define FW_VER_OFFSET 2 +#define FW_REG_CNT_OFFSET 3 +#define FW_DATA_OFFSET 16 + +struct hx9023s_bin { + u16 reg_count; + u16 fw_size; + u8 fw_ver; + u8 data[] __counted_by(fw_size); +}; + struct hx9023s_ch_data { s16 raw; /* Raw Data*/ s16 lp; /* Low Pass Filter Data*/ @@ -134,7 +146,7 @@ struct hx9023s_data { struct { __le16 channels[HX9023S_CH_NUM]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; /* @@ -689,12 +701,11 @@ static int hx9023s_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 = hx9023s_get_proximity(data, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: return hx9023s_get_samp_freq(data, val, val2); @@ -942,8 +953,8 @@ static irqreturn_t hx9023s_trigger_handler(int irq, void *private) data->buffer.channels[i++] = cpu_to_le16(data->ch_data[index].diff); } - iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->buffer, + sizeof(data->buffer), pf->timestamp); out: iio_trigger_notify_done(indio_dev->trig); @@ -998,6 +1009,78 @@ static int hx9023s_id_check(struct iio_dev *indio_dev) return 0; } +static int hx9023s_bin_load(struct hx9023s_data *data, struct hx9023s_bin *bin) +{ + u8 *cfg_start = bin->data + FW_DATA_OFFSET; + u8 addr, val; + u16 i; + int ret; + + for (i = 0; i < bin->reg_count; i++) { + addr = cfg_start[i * 2]; + val = cfg_start[i * 2 + 1]; + ret = regmap_write(data->regmap, addr, val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int hx9023s_send_cfg(const struct firmware *fw, struct hx9023s_data *data) +{ + struct hx9023s_bin *bin __free(kfree) = + kzalloc(fw->size + sizeof(*bin), GFP_KERNEL); + if (!bin) + return -ENOMEM; + + memcpy(bin->data, fw->data, fw->size); + + bin->fw_size = fw->size; + bin->fw_ver = bin->data[FW_VER_OFFSET]; + bin->reg_count = get_unaligned_le16(bin->data + FW_REG_CNT_OFFSET); + + release_firmware(fw); + + return hx9023s_bin_load(data, bin); +} + +static void hx9023s_cfg_update(const struct firmware *fw, void *context) +{ + struct hx9023s_data *data = context; + struct device *dev = regmap_get_device(data->regmap); + int ret; + + if (!fw || !fw->data) { + dev_warn(dev, "No firmware\n"); + goto no_fw; + } + + ret = hx9023s_send_cfg(fw, data); + if (ret) { + dev_warn(dev, "Firmware update failed: %d\n", ret); + goto no_fw; + } + + ret = regcache_sync(data->regmap); + if (ret) + dev_err(dev, "regcache sync failed\n"); + + return; + +no_fw: + ret = regmap_multi_reg_write(data->regmap, hx9023s_reg_init_list, + ARRAY_SIZE(hx9023s_reg_init_list)); + if (ret) { + dev_err(dev, "Error loading default configuration\n"); + return; + } + + ret = regcache_sync(data->regmap); + if (ret) + dev_err(dev, "regcache sync failed\n"); +} + static int hx9023s_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1036,18 +1119,14 @@ static int hx9023s_probe(struct i2c_client *client) indio_dev->modes = INDIO_DIRECT_MODE; i2c_set_clientdata(client, indio_dev); - ret = regmap_multi_reg_write(data->regmap, hx9023s_reg_init_list, - ARRAY_SIZE(hx9023s_reg_init_list)); - if (ret) - return dev_err_probe(dev, ret, "device init failed\n"); - ret = hx9023s_ch_cfg(data); if (ret) return dev_err_probe(dev, ret, "channel config failed\n"); - ret = regcache_sync(data->regmap); + ret = request_firmware_nowait(THIS_MODULE, true, "hx9023s.bin", dev, + GFP_KERNEL, data, hx9023s_cfg_update); if (ret) - return dev_err_probe(dev, ret, "regcache sync failed\n"); + return dev_err_probe(dev, ret, "reg config failed\n"); if (client->irq) { ret = devm_request_threaded_irq(dev, client->irq, @@ -1111,13 +1190,13 @@ static DEFINE_SIMPLE_DEV_PM_OPS(hx9023s_pm_ops, hx9023s_suspend, static const struct of_device_id hx9023s_of_match[] = { { .compatible = "tyhx,hx9023s" }, - {} + { } }; MODULE_DEVICE_TABLE(of, hx9023s_of_match); static const struct i2c_device_id hx9023s_id[] = { { "hx9023s" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, hx9023s_id); diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index b09d15230111..0d30b91dbcbc 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> @@ -759,15 +760,19 @@ static irqreturn_t irsd200_trigger_handler(int irq, void *pollf) { struct iio_dev *indio_dev = ((struct iio_poll_func *)pollf)->indio_dev; struct irsd200_data *data = iio_priv(indio_dev); - s64 buf[2] = {}; + struct { + s16 channel; + aligned_s64 ts; + } scan; int ret; - ret = irsd200_read_data(data, (s16 *)buf); + memset(&scan, 0, sizeof(scan)); + ret = irsd200_read_data(data, &scan.channel); if (ret) goto end; - iio_push_to_buffers_with_timestamp(indio_dev, buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); end: iio_trigger_notify_done(indio_dev->trig); @@ -783,7 +788,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; @@ -940,7 +945,7 @@ static const struct of_device_id irsd200_of_match[] = { { .compatible = "murata,irsd200", }, - {} + { } }; MODULE_DEVICE_TABLE(of, irsd200_of_match); diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index dc66ca9bba6b..d1510fe24050 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -481,7 +481,7 @@ static const struct iio_chan_spec_ext_info isl29501_ext_info[] = { _ISL29501_EXT_INFO("calib_phase_temp_b", REG_CALIB_PHASE_TEMP_B), _ISL29501_EXT_INFO("calib_phase_light_a", REG_CALIB_PHASE_LIGHT_A), _ISL29501_EXT_INFO("calib_phase_light_b", REG_CALIB_PHASE_LIGHT_B), - { }, + { } }; #define ISL29501_DISTANCE_SCAN_INDEX 0 @@ -990,7 +990,7 @@ static int isl29501_probe(struct i2c_client *client) static const struct i2c_device_id isl29501_id[] = { { "isl29501" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, isl29501_id); diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index 614e65cb9d42..01783486bc7d 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -45,7 +45,7 @@ struct mb1232_data { /* Ensure correct alignment of data to push to IIO buffer */ struct { s16 distance; - s64 ts __aligned(8); + aligned_s64 ts; } scan; }; @@ -125,8 +125,8 @@ static irqreturn_t mb1232_trigger_handler(int irq, void *p) if (data->scan.distance < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -239,7 +239,7 @@ static const struct of_device_id of_mb1232_match[] = { { .compatible = "maxbotix,mb1242", }, { .compatible = "maxbotix,mb7040", }, { .compatible = "maxbotix,mb7137", }, - {}, + { } }; MODULE_DEVICE_TABLE(of, of_mb1232_match); diff --git a/drivers/iio/proximity/ping.c b/drivers/iio/proximity/ping.c index 2ad69b150902..c5b4e1378b7d 100644 --- a/drivers/iio/proximity/ping.c +++ b/drivers/iio/proximity/ping.c @@ -268,7 +268,7 @@ static const struct iio_chan_spec ping_chan_spec[] = { static const struct of_device_id of_ping_match[] = { { .compatible = "parallax,ping", .data = &pa_ping_cfg }, { .compatible = "parallax,laserping", .data = &pa_laser_ping_cfg }, - {}, + { } }; MODULE_DEVICE_TABLE(of, of_ping_match); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 5c959730aecd..1deaf70e92ce 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -47,7 +47,7 @@ struct lidar_data { /* Ensure timestamp is naturally aligned */ struct { u16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; }; @@ -208,7 +208,7 @@ static int lidar_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: { u16 reg; - if (iio_device_claim_direct_mode(indio_dev)) + if (!iio_device_claim_direct(indio_dev)) return -EBUSY; ret = lidar_get_measurement(data, ®); @@ -216,7 +216,7 @@ static int lidar_read_raw(struct iio_dev *indio_dev, *val = reg; ret = IIO_VAL_INT; } - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); break; } case IIO_CHAN_INFO_SCALE: @@ -238,8 +238,9 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private) ret = lidar_get_measurement(data, &data->scan.chan); if (!ret) { - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, + sizeof(data->scan), + iio_get_time_ns(indio_dev)); } else if (ret != -EINVAL) { dev_err(&data->client->dev, "cannot read LIDAR measurement"); } diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 71ad29e441b2..b059bac1078b 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -240,7 +240,7 @@ static const struct of_device_id of_srf04_match[] = { { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg }, { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg }, { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg }, - {}, + { } }; MODULE_DEVICE_TABLE(of, of_srf04_match); diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index a75ea5042876..6e32fdfd161b 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -66,7 +66,7 @@ struct srf08_data { /* Ensure timestamp is naturally aligned */ struct { s16 chan; - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; /* Sensor-Type */ @@ -191,8 +191,8 @@ static irqreturn_t srf08_trigger_handler(int irq, void *p) mutex_lock(&data->lock); data->scan.chan = sensor_data; - iio_push_to_buffers_with_timestamp(indio_dev, - &data->scan, pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + pf->timestamp); mutex_unlock(&data->lock); err: @@ -531,7 +531,7 @@ static const struct of_device_id of_srf08_match[] = { { .compatible = "devantech,srf02", (void *)SRF02 }, { .compatible = "devantech,srf08", (void *)SRF08 }, { .compatible = "devantech,srf10", (void *)SRF10 }, - {}, + { } }; MODULE_DEVICE_TABLE(of, of_srf08_match); diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 0d7f0518d4fb..fb02eac78ed4 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: @@ -988,21 +995,21 @@ static const struct sx931x_info sx9311_info = { static const struct acpi_device_id sx9310_acpi_match[] = { { "STH9310", (kernel_ulong_t)&sx9310_info }, { "STH9311", (kernel_ulong_t)&sx9311_info }, - {} + { } }; MODULE_DEVICE_TABLE(acpi, sx9310_acpi_match); static const struct of_device_id sx9310_of_match[] = { { .compatible = "semtech,sx9310", &sx9310_info }, { .compatible = "semtech,sx9311", &sx9311_info }, - {} + { } }; MODULE_DEVICE_TABLE(of, sx9310_of_match); static const struct i2c_device_id sx9310_id[] = { { "sx9310", (kernel_ulong_t)&sx9310_info }, { "sx9311", (kernel_ulong_t)&sx9311_info }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, sx9310_id); diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c index f7819dd2775c..c7b2d03c23bc 100644 --- a/drivers/iio/proximity/sx9324.c +++ b/drivers/iio/proximity/sx9324.c @@ -202,7 +202,7 @@ static const struct iio_chan_spec_ext_info sx9324_channel_ext_info[] = { .shared = IIO_SEPARATE, .read = sx9324_phase_configuration_show, }, - {} + { } }; #define SX9324_CHANNEL(idx) \ @@ -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/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index c4e94d0fb163..8913da59dc73 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -387,11 +387,10 @@ static int sx9500_read_raw(struct iio_dev *indio_dev, case IIO_PROXIMITY: 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 = sx9500_read_proximity(data, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: return sx9500_read_samp_freq(data, val, val2); @@ -866,7 +865,7 @@ static const struct acpi_gpio_mapping acpi_sx9500_gpios[] = { * GPIO to be output only. Ask the GPIO core to ignore this limit. */ { "interrupt-gpios", &interrupt_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION }, - { }, + { } }; static void sx9500_gpio_probe(struct i2c_client *client, @@ -1031,7 +1030,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(sx9500_pm_ops, sx9500_suspend, sx9500_resume); static const struct acpi_device_id sx9500_acpi_match[] = { {"SSX9500", 0}, {"SASX9500", 0}, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match); diff --git a/drivers/iio/proximity/sx_common.c b/drivers/iio/proximity/sx_common.c index f70198a1f0d1..59b35e40739b 100644 --- a/drivers/iio/proximity/sx_common.c +++ b/drivers/iio/proximity/sx_common.c @@ -379,8 +379,8 @@ static irqreturn_t sx_common_trigger_handler(int irq, void *private) data->buffer.channels[i++] = val; } - iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, - pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &data->buffer, + sizeof(data->buffer), pf->timestamp); out: mutex_unlock(&data->mutex); diff --git a/drivers/iio/proximity/sx_common.h b/drivers/iio/proximity/sx_common.h index fb14e6f06a6d..259b5c695233 100644 --- a/drivers/iio/proximity/sx_common.h +++ b/drivers/iio/proximity/sx_common.h @@ -125,7 +125,7 @@ struct sx_common_data { /* Ensure correct alignment of timestamp when present. */ struct { __be16 channels[SX_COMMON_MAX_NUM_CHANNELS]; - s64 ts __aligned(8); + aligned_s64 ts; } buffer; unsigned int suspend_ctrl; diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index bb6c9cc88b35..31e77d9e0c90 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -653,7 +653,7 @@ static const struct of_device_id vcnl3020_of_match[] = { { .compatible = "vishay,vcnl3020", }, - {} + { } }; MODULE_DEVICE_TABLE(of, vcnl3020_of_match); diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c index 87d10faaff9b..ef4aa7b2835e 100644 --- a/drivers/iio/proximity/vl53l0x-i2c.c +++ b/drivers/iio/proximity/vl53l0x-i2c.c @@ -94,8 +94,8 @@ static irqreturn_t vl53l0x_trigger_handler(int irq, void *priv) return -EREMOTEIO; data->scan.chan = get_unaligned_be16(&buffer[10]); - iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); vl53l0x_clear_irq(data); diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index 9d95241bdf8f..0bc84f12cb34 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -186,7 +186,7 @@ MODULE_DEVICE_TABLE(of, ad2s1200_of_match); static const struct spi_device_id ad2s1200_id[] = { { "ad2s1200" }, { "ad2s1205" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad2s1200_id); diff --git a/drivers/iio/resolver/ad2s1210.c b/drivers/iio/resolver/ad2s1210.c index a414eef12e5e..9b028c8bb1db 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> @@ -164,7 +165,7 @@ struct ad2s1210_state { struct { __be16 chan[2]; /* Ensure timestamp is naturally aligned. */ - s64 timestamp __aligned(8); + aligned_s64 timestamp; } scan; /** SPI transmit buffer. */ u8 rx[2]; @@ -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); } /* @@ -1340,7 +1340,8 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p) } ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp); - iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp); + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan), + pf->timestamp); error_ret: iio_trigger_notify_done(indio_dev->trig); @@ -1427,7 +1428,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 +1472,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"); @@ -1600,7 +1598,7 @@ MODULE_DEVICE_TABLE(of, ad2s1210_of_match); static const struct spi_device_id ad2s1210_id[] = { { "ad2s1210" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad2s1210_id); diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c index be6836e55376..18f1c905eeac 100644 --- a/drivers/iio/resolver/ad2s90.c +++ b/drivers/iio/resolver/ad2s90.c @@ -105,13 +105,13 @@ static int ad2s90_probe(struct spi_device *spi) static const struct of_device_id ad2s90_of_match[] = { { .compatible = "adi,ad2s90", }, - {} + { } }; MODULE_DEVICE_TABLE(of, ad2s90_of_match); static const struct spi_device_id ad2s90_id[] = { { "ad2s90" }, - {} + { } }; MODULE_DEVICE_TABLE(spi, ad2s90_id); diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 692520e1c497..9f628a8e5cfb 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -131,8 +131,9 @@ static int temperature_proc_event(struct hid_sensor_hub_device *hsdev, struct temperature_state *temp_st = iio_priv(indio_dev); if (atomic_read(&temp_st->common_attributes.data_ready)) - iio_push_to_buffers_with_timestamp(indio_dev, &temp_st->scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &temp_st->scan, + sizeof(temp_st->scan), + iio_get_time_ns(indio_dev)); return 0; } @@ -272,7 +273,7 @@ static const struct platform_device_id hid_temperature_ids[] = { /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ .name = "HID-SENSOR-200033", }, - { /* sentinel */ } + { } }; MODULE_DEVICE_TABLE(platform, hid_temperature_ids); diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index f8ea2219ab48..7dd40d69cce6 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1664,7 +1664,7 @@ static const struct spi_device_id ltc2983_id_table[] = { { "ltc2984", (kernel_ulong_t)<c2984_chip_info_data }, { "ltc2986", (kernel_ulong_t)<c2986_chip_info_data }, { "ltm2985", (kernel_ulong_t)<m2985_chip_info_data }, - {}, + { } }; MODULE_DEVICE_TABLE(spi, ltc2983_id_table); @@ -1673,7 +1673,7 @@ static const struct of_device_id ltc2983_of_match[] = { { .compatible = "adi,ltc2984", .data = <c2984_chip_info_data }, { .compatible = "adi,ltc2986", .data = <c2986_chip_info_data }, { .compatible = "adi,ltm2985", .data = <m2985_chip_info_data }, - {}, + { } }; MODULE_DEVICE_TABLE(of, ltc2983_of_match); diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index c28a7a6dea5f..cae8e84821d7 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -9,7 +9,6 @@ #include <linux/init.h> #include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/err.h> #include <linux/spi/spi.h> #include <linux/iio/iio.h> @@ -121,9 +120,9 @@ static const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = { struct maxim_thermocouple_data { struct spi_device *spi; const struct maxim_thermocouple_chip *chip; + char tc_type; u8 buffer[16] __aligned(IIO_DMA_MINALIGN); - char tc_type; }; static int maxim_thermocouple_read(struct maxim_thermocouple_data *data, @@ -169,8 +168,9 @@ static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private) ret = spi_read(data->spi, data->buffer, data->chip->read_size); if (!ret) { - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, data->buffer, + sizeof(data->buffer), + iio_get_time_ns(indio_dev)); } iio_trigger_notify_done(indio_dev->trig); @@ -183,40 +183,35 @@ static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct maxim_thermocouple_data *data = iio_priv(indio_dev); - int ret = -EINVAL; + int ret; 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 = maxim_thermocouple_read(data, chan, val); - iio_device_release_direct_mode(indio_dev); - - if (!ret) - return IIO_VAL_INT; + iio_device_release_direct(indio_dev); + if (ret) + return ret; - break; + return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (chan->channel2) { case IIO_MOD_TEMP_AMBIENT: *val = 62; *val2 = 500000; /* 1000 * 0.0625 */ - ret = IIO_VAL_INT_PLUS_MICRO; - break; + return IIO_VAL_INT_PLUS_MICRO; default: *val = 250; /* 1000 * 0.25 */ - ret = IIO_VAL_INT; + return IIO_VAL_INT; } - break; case IIO_CHAN_INFO_THERMOCOUPLE_TYPE: *val = data->tc_type; - ret = IIO_VAL_CHAR; - break; + return IIO_VAL_CHAR; + default: + return -EINVAL; } - - return ret; } static const struct iio_info maxim_thermocouple_info = { @@ -271,7 +266,7 @@ static const struct spi_device_id maxim_thermocouple_id[] = { {"max31855t", MAX31855T}, {"max31855e", MAX31855E}, {"max31855r", MAX31855R}, - {}, + { } }; MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); diff --git a/drivers/iio/temperature/mcp9600.c b/drivers/iio/temperature/mcp9600.c index c2447860adfd..6e9108d5cf75 100644 --- a/drivers/iio/temperature/mcp9600.c +++ b/drivers/iio/temperature/mcp9600.c @@ -449,13 +449,13 @@ static int mcp9600_probe(struct i2c_client *client) static const struct i2c_device_id mcp9600_id[] = { { "mcp9600" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, mcp9600_id); static const struct of_device_id mcp9600_of_match[] = { { .compatible = "microchip,mcp9600" }, - {} + { } }; MODULE_DEVICE_TABLE(of, mcp9600_of_match); diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 02b27f471baa..29bff9d8859d 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; @@ -248,7 +253,7 @@ static irqreturn_t tmp006_trigger_handler(int irq, void *p) struct tmp006_data *data = iio_priv(indio_dev); struct { s16 channels[2]; - s64 ts __aligned(8); + aligned_s64 ts; } scan; s32 ret; @@ -264,8 +269,8 @@ static irqreturn_t tmp006_trigger_handler(int irq, void *p) goto err; scan.channels[1] = ret; - iio_push_to_buffers_with_timestamp(indio_dev, &scan, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index fd4d389ce1df..043283b02c4d 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -558,7 +558,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume); static const struct of_device_id tmp007_of_match[] = { { .compatible = "ti,tmp007", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, tmp007_of_match); diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index cfaa16f46a3f..334bba6fdae6 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -207,13 +207,13 @@ static int tsys01_i2c_probe(struct i2c_client *client) static const struct i2c_device_id tsys01_id[] = { { "tsys01" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, tsys01_id); static const struct of_device_id tsys01_of_match[] = { { .compatible = "meas,tsys01", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, tsys01_of_match); diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index ef34b3c58f26..0cad27205667 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -169,7 +169,7 @@ static int tsys02d_probe(struct i2c_client *client) static const struct i2c_device_id tsys02d_id[] = { { "tsys02d" }, - {} + { } }; MODULE_DEVICE_TABLE(i2c, tsys02d_id); diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index fc67e6b73df7..872dd8582003 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -257,7 +257,7 @@ static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_fractional_log2), KUNIT_CASE(iio_test_iio_format_value_multiple), KUNIT_CASE(iio_test_iio_format_value_integer_64), - {} + { } }; static struct kunit_suite iio_format_test_suite = { diff --git a/drivers/iio/test/iio-test-gts.c b/drivers/iio/test/iio-test-gts.c index 1eceec9d477f..11250bc905c9 100644 --- a/drivers/iio/test/iio-test-gts.c +++ b/drivers/iio/test/iio-test-gts.c @@ -499,7 +499,7 @@ static struct kunit_case iio_gts_test_cases[] = { KUNIT_CASE(test_iio_find_closest_gain_low), KUNIT_CASE(test_iio_gts_total_gain_to_scale), KUNIT_CASE(test_iio_gts_avail_test), - {} + { } }; static struct kunit_suite iio_gts_test_suite = { diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c index bbc6a2e1c2c1..ac6942cf1e44 100644 --- a/drivers/iio/test/iio-test-rescale.c +++ b/drivers/iio/test/iio-test-rescale.c @@ -704,7 +704,7 @@ static void iio_rescale_test_offset(struct kunit *test) static struct kunit_case iio_rescale_test_cases[] = { KUNIT_CASE_PARAM(iio_rescale_test_scale, iio_rescale_scale_gen_params), KUNIT_CASE_PARAM(iio_rescale_test_offset, iio_rescale_offset_gen_params), - {} + { } }; static struct kunit_suite iio_rescale_test_suite = { 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]; diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c index f1e18913236a..2505ace440b4 100644 --- a/drivers/iio/trigger/stm32-lptimer-trigger.c +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c @@ -16,16 +16,43 @@ #include <linux/platform_device.h> #include <linux/property.h> -/* List Low-Power Timer triggers */ -static const char * const stm32_lptim_triggers[] = { - LPTIM1_OUT, - LPTIM2_OUT, - LPTIM3_OUT, +/* Maximum triggers + one trailing null entry to indicate the end of array */ +#define MAX_TRIGGERS 3 + +struct stm32_lptim_cfg { + const char * const (*triggers)[MAX_TRIGGERS]; + unsigned int nb_triggers; +}; + +/* List Low-Power Timer triggers for H7, MP13, MP15 */ +static const char * const stm32_lptim_triggers[][MAX_TRIGGERS] = { + { LPTIM1_OUT,}, + { LPTIM2_OUT,}, + { LPTIM3_OUT,}, +}; + +/* List Low-Power Timer triggers for STM32MP25 */ +static const char * const stm32mp25_lptim_triggers[][MAX_TRIGGERS] = { + { LPTIM1_CH1, LPTIM1_CH2, }, + { LPTIM2_CH1, LPTIM2_CH2, }, + { LPTIM3_CH1,}, + { LPTIM4_CH1,}, + { LPTIM5_OUT,}, +}; + +static const struct stm32_lptim_cfg stm32mp15_lptim_cfg = { + .triggers = stm32_lptim_triggers, + .nb_triggers = ARRAY_SIZE(stm32_lptim_triggers), +}; + +static const struct stm32_lptim_cfg stm32mp25_lptim_cfg = { + .triggers = stm32mp25_lptim_triggers, + .nb_triggers = ARRAY_SIZE(stm32mp25_lptim_triggers), }; struct stm32_lptim_trigger { struct device *dev; - const char *trg; + const char * const *triggers; }; static int stm32_lptim_validate_device(struct iio_trigger *trig, @@ -56,22 +83,33 @@ EXPORT_SYMBOL(is_stm32_lptim_trigger); static int stm32_lptim_setup_trig(struct stm32_lptim_trigger *priv) { - struct iio_trigger *trig; + const char * const *cur = priv->triggers; + int ret; - trig = devm_iio_trigger_alloc(priv->dev, "%s", priv->trg); - if (!trig) - return -ENOMEM; + while (cur && *cur) { + struct iio_trigger *trig; - trig->dev.parent = priv->dev->parent; - trig->ops = &stm32_lptim_trigger_ops; - iio_trigger_set_drvdata(trig, priv); + trig = devm_iio_trigger_alloc(priv->dev, "%s", *cur); + if (!trig) + return -ENOMEM; - return devm_iio_trigger_register(priv->dev, trig); + trig->dev.parent = priv->dev->parent; + trig->ops = &stm32_lptim_trigger_ops; + iio_trigger_set_drvdata(trig, priv); + + ret = devm_iio_trigger_register(priv->dev, trig); + if (ret) + return ret; + cur++; + } + + return 0; } static int stm32_lptim_trigger_probe(struct platform_device *pdev) { struct stm32_lptim_trigger *priv; + struct stm32_lptim_cfg const *lptim_cfg; u32 index; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -81,18 +119,21 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev) if (device_property_read_u32(&pdev->dev, "reg", &index)) return -EINVAL; - if (index >= ARRAY_SIZE(stm32_lptim_triggers)) + lptim_cfg = device_get_match_data(&pdev->dev); + + if (index >= lptim_cfg->nb_triggers) return -EINVAL; priv->dev = &pdev->dev; - priv->trg = stm32_lptim_triggers[index]; + priv->triggers = lptim_cfg->triggers[index]; return stm32_lptim_setup_trig(priv); } static const struct of_device_id stm32_lptim_trig_of_match[] = { - { .compatible = "st,stm32-lptimer-trigger", }, - {}, + { .compatible = "st,stm32-lptimer-trigger", .data = &stm32mp15_lptim_cfg }, + { .compatible = "st,stm32mp25-lptimer-trigger", .data = &stm32mp25_lptim_cfg}, + { } }; MODULE_DEVICE_TABLE(of, stm32_lptim_trig_of_match); diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index bb60b2d7b2ec..925b864facca 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -38,6 +38,9 @@ static const void *triggers_table[][MAX_TRIGGERS] = { { TIM15_TRGO,}, { TIM16_OC1,}, { TIM17_OC1,}, + { }, /* timer 18 */ + { }, /* timer 19 */ + { TIM20_TRGO, TIM20_TRGO2, TIM20_OC1, TIM20_OC2, TIM20_OC3, }, }; /* List the triggers accepted by each timer */ @@ -119,7 +122,7 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, unsigned int frequency) { unsigned long long prd, div; - int prescaler = 0; + int prescaler = 0, ret; u32 ccer; /* Period and prescaler values depends of clock rate */ @@ -150,10 +153,12 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, if (ccer & TIM_CCER_CCXE) return -EBUSY; - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); if (!priv->enabled) { priv->enabled = true; - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + return ret; } regmap_write(priv->regmap, TIM_PSC, prescaler); @@ -173,7 +178,6 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv, /* Enable controller */ regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); - mutex_unlock(&priv->lock); return 0; } @@ -307,7 +311,7 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, struct stm32_timer_trigger *priv = dev_get_drvdata(dev); struct iio_trigger *trig = to_iio_trigger(dev); u32 mask, shift, master_mode_max; - int i; + int i, ret; if (stm32_timer_is_trgo2_name(trig->name)) { mask = TIM_CR2_MMS2; @@ -322,15 +326,16 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev, for (i = 0; i <= master_mode_max; i++) { if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { - mutex_lock(&priv->lock); + guard(mutex)(&priv->lock); if (!priv->enabled) { /* Clock should be enabled first */ priv->enabled = true; - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + return ret; } regmap_update_bits(priv->regmap, TIM_CR2, mask, i << shift); - mutex_unlock(&priv->lock); return len; } } @@ -482,6 +487,7 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -491,12 +497,14 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, /* fixed scale */ return -EINVAL; - case IIO_CHAN_INFO_ENABLE: - mutex_lock(&priv->lock); + case IIO_CHAN_INFO_ENABLE: { + guard(mutex)(&priv->lock); if (val) { if (!priv->enabled) { priv->enabled = true; - clk_enable(priv->clk); + ret = clk_enable(priv->clk); + if (ret) + return ret; } regmap_set_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN); } else { @@ -506,11 +514,12 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, clk_disable(priv->clk); } } - mutex_unlock(&priv->lock); + return 0; } - - return -EINVAL; + default: + return -EINVAL; + } } static int stm32_counter_validate_trigger(struct iio_dev *indio_dev, @@ -602,6 +611,7 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); int sms = stm32_enable_mode2sms(mode); + int ret; if (sms < 0) return sms; @@ -609,12 +619,15 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, * Triggered mode sets CEN bit automatically by hardware. So, first * enable counter clock, so it can use it. Keeps it in sync with CEN. */ - mutex_lock(&priv->lock); - if (sms == 6 && !priv->enabled) { - clk_enable(priv->clk); - priv->enabled = true; + scoped_guard(mutex, &priv->lock) { + if (sms == 6 && !priv->enabled) { + ret = clk_enable(priv->clk); + if (ret) + return ret; + + priv->enabled = true; + } } - mutex_unlock(&priv->lock); regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); @@ -698,7 +711,7 @@ static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { IIO_ENUM_AVAILABLE("enable_mode", IIO_SHARED_BY_TYPE, &stm32_enable_mode_enum), IIO_ENUM("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum), IIO_ENUM_AVAILABLE("trigger_mode", IIO_SHARED_BY_TYPE, &stm32_trigger_mode_enum), - {} + { } }; static const struct iio_chan_spec stm32_trigger_channel = { @@ -781,7 +794,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) return -EINVAL; /* Create an IIO device only if we have triggers to be validated */ - if (*cfg->valids_table[index]) + if (cfg->valids_table && *cfg->valids_table[index]) priv = stm32_setup_counter_device(dev); else priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -794,7 +807,8 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; - priv->valids = cfg->valids_table[index]; + if (cfg->valids_table && *cfg->valids_table[index]) + priv->valids = cfg->valids_table[index]; stm32_timer_detect_trgo2(priv); mutex_init(&priv->lock); @@ -886,6 +900,16 @@ static const struct stm32_timer_trigger_cfg stm32h7_timer_trg_cfg = { .num_valids_table = ARRAY_SIZE(stm32h7_valids_table), }; +static const struct stm32_timer_trigger_cfg stm32mp25_timer_trg_cfg = { + /* + * valids_table not used: counter framework is now superseding the deprecated IIO + * counter interface (IIO_COUNT), so don't support it. num_valids_table is only + * kept here to register the IIO HW triggers. valids_table should be moved at some + * point to the stm32-timer-cnt driver instead. + */ + .num_valids_table = ARRAY_SIZE(triggers_table), +}; + static const struct of_device_id stm32_trig_of_match[] = { { .compatible = "st,stm32-timer-trigger", @@ -893,8 +917,11 @@ static const struct of_device_id stm32_trig_of_match[] = { }, { .compatible = "st,stm32h7-timer-trigger", .data = (void *)&stm32h7_timer_trg_cfg, + }, { + .compatible = "st,stm32mp25-timer-trigger", + .data = (void *)&stm32mp25_timer_trg_cfg, }, - { /* end node */ }, + { } }; MODULE_DEVICE_TABLE(of, stm32_trig_of_match); |