diff --git a/drivers/iio/adc/ad4630.c b/drivers/iio/adc/ad4630.c index 540866a64fea0c..43a3b48d237aa0 100644 --- a/drivers/iio/adc/ad4630.c +++ b/drivers/iio/adc/ad4630.c @@ -152,6 +152,12 @@ enum { AD4630_MAX_PGA, }; +enum ad4630_scan_type { + AD4630_SCAN_TYPE_NORMAL, + AD4630_SCAN_TYPE_COMMON_MODE, + AD4630_SCAN_TYPE_NORMAL_AND_COMMON_MODE, +}; + /* * Gains computed as fractions of 1000 so they can be expressed by integers. */ @@ -202,6 +208,7 @@ struct ad4630_state { int scale_tbl[ARRAY_SIZE(ad4630_gains)][2]; unsigned int out_data; unsigned int max_rate; + enum ad4630_scan_type current_scan_type; /* offload sampling spi message */ struct spi_transfer offload_xfer; @@ -317,6 +324,7 @@ static int ad4630_read_raw(struct iio_dev *indio_dev, int *val2, long info) { struct ad4630_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type = iio_get_current_scan_type(indio_dev, chan); unsigned int temp; int ret; @@ -331,7 +339,7 @@ static int ad4630_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_NANO; } *val = (st->vref * 2) / 1000; - *val2 = chan->scan_type.realbits; + *val2 = scan_type->realbits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_CALIBSCALE: ret = ad4630_get_chan_gain(indio_dev, chan->channel, &temp); @@ -456,12 +464,17 @@ static int ad4630_set_chan_offset(struct iio_dev *indio_dev, int ch, int offset) return ret; } -static void ad4630_fill_scale_tbl(struct ad4630_state *st) +static void ad4630_fill_scale_tbl(struct iio_dev *indio_dev, + struct ad4630_state *st) { + const struct iio_scan_type *scan_type; int val, val2, tmp0, tmp1, i; u64 tmp2; - val2 = st->chip->modes[st->out_data].channels->scan_type.realbits; + scan_type = iio_get_current_scan_type(indio_dev, + st->chip->modes[st->out_data].channels); + + val2 = scan_type->realbits; for (i = 0; i < ARRAY_SIZE(ad4630_gains); i++) { val = (st->vref * 2) / 1000; /* Multiply by MILLI here to avoid losing precision */ @@ -571,14 +584,27 @@ static int ad4630_write_raw(struct iio_dev *indio_dev, int val2, long info) { struct ad4630_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; + enum ad4630_scan_type cur_scan_type; int gain_idx; switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: return ad4630_set_sampling_freq(indio_dev, val); case IIO_CHAN_INFO_SCALE: + /* + * Common mode and normal + common mode scan types declare + * different amount of realbits. Though, to get to the scale + * factor for converting ADC output codes to voltage units only + * the ADC precision bits matter. Thus, use normal scan_type + * realbits to calcalte scale related parameters. + */ + cur_scan_type = st->current_scan_type; + st->current_scan_type = AD4630_SCAN_TYPE_NORMAL; + scan_type = iio_get_current_scan_type(indio_dev, chan); gain_idx = ad4630_calc_pga_gain(val, val2, st->vref, - chan->scan_type.realbits); + scan_type->realbits); + st->current_scan_type = cur_scan_type; return ad4630_set_pga_gain(indio_dev, gain_idx); case IIO_CHAN_INFO_CALIBSCALE: return ad4630_set_chan_gain(indio_dev, chan->channel, val, @@ -759,6 +785,32 @@ static int ad4630_buffer_postdisable(struct iio_dev *indio_dev) return ret; } +static int ad4630_set_scan_type(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int scan_type) +{ + struct ad4630_state *st = iio_priv(dev); + int ret; + + ret = iio_device_claim_direct_mode(dev); + if (ret) + return ret; + + if (chan->has_ext_scan_type) + st->current_scan_type = scan_type; + + iio_device_release_direct_mode(dev); + return 0; +} + +static int ad4630_get_scan_type(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + const struct ad4630_state *st = iio_priv(dev); + + return st->current_scan_type; +} + static const char *const ad4630_average_modes[] = { "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", "65536" @@ -779,6 +831,25 @@ static const struct iio_chan_spec_ext_info ad4630_ext_info[] = { {} }; +static const char *const ad4630_scan_types[] = { + "conversion_data", "common_mode_data", "conversion_plus_common_mode_data" +}; + +static const struct iio_enum ad4630_scan_type_enum = { + .items = ad4630_scan_types, + .num_items = ARRAY_SIZE(ad4630_scan_types), + .set = ad4630_set_scan_type, + .get = ad4630_get_scan_type, +}; + +static const struct iio_chan_spec_ext_info ad4630_com_ext_info[] = { + IIO_ENUM("scan_type", IIO_SHARED_BY_TYPE, + &ad4630_scan_type_enum), + IIO_ENUM_AVAILABLE("scan_type", IIO_SHARED_BY_TYPE, + &ad4630_scan_type_enum), + {} +}; + #define AD4630_CHAN(_idx, _msk_avail, _storage, _real, _shift, _info) { \ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ @@ -798,6 +869,114 @@ static const struct iio_chan_spec_ext_info ad4630_ext_info[] = { }, \ } +#define AD4630_COM_CHAN(_idx, _msk_avail, _storage, _real, _shift, _count, _info) {\ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_separate_available = _msk_avail, \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _idx, \ + .scan_index = _idx, \ + .ext_info = ad4630_com_ext_info, \ + .has_ext_scan_type = 1, \ + .ext_scan_type = ad4630_scan_type_##_real##_count, \ + .num_ext_scan_type = ARRAY_SIZE(ad4630_scan_type_##_real##_count),\ +} + +/* + * IIO correctly sign-extend buffer data elements to a twos-complement + * signed number if their scan_type is set to signed. However, if common mode + * data bits are together with ADC conversion bits, sign extend will also affect + * common mode bits. To avoid that, set AD4630_SCAN_TYPE_NORMAL_AND_COMMON_MODE + * scan_types to unsigned. This will have the side effect of applications having + * to sign-extend ADC conversion data themselves, though. + */ +static const struct iio_scan_type ad4630_scan_type_16_dual[] = { + [AD4630_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 64, + .realbits = 16, + .shift = 8, + }, + [AD4630_SCAN_TYPE_COMMON_MODE] = { + .sign = 'u', + .storagebits = 64, + .realbits = 8, + .shift = 0, + }, + [AD4630_SCAN_TYPE_NORMAL_AND_COMMON_MODE] = { + .sign = 'u', + .storagebits = 64, + .realbits = 32, + .shift = 0, + }, +}; + +static const struct iio_scan_type ad4630_scan_type_24_dual[] = { + [AD4630_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 64, + .realbits = 24, + .shift = 8, + }, + [AD4630_SCAN_TYPE_COMMON_MODE] = { + .sign = 'u', + .storagebits = 64, + .realbits = 8, + .shift = 0, + }, + [AD4630_SCAN_TYPE_NORMAL_AND_COMMON_MODE] = { + .sign = 'u', + .storagebits = 64, + .realbits = 32, + .shift = 0, + }, +}; + +static const struct iio_scan_type ad4630_scan_type_16_single[] = { + [AD4630_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 32, + .realbits = 16, + .shift = 8, + }, + [AD4630_SCAN_TYPE_COMMON_MODE] = { + .sign = 'u', + .storagebits = 32, + .realbits = 8, + .shift = 0, + }, + [AD4630_SCAN_TYPE_NORMAL_AND_COMMON_MODE] = { + .sign = 'u', + .storagebits = 32, + .realbits = 24, + .shift = 0, + }, +}; + +static const struct iio_scan_type ad4630_scan_type_24_single[] = { + [AD4630_SCAN_TYPE_NORMAL] = { + .sign = 's', + .storagebits = 32, + .realbits = 24, + .shift = 8, + }, + [AD4630_SCAN_TYPE_COMMON_MODE] = { + .sign = 'u', + .storagebits = 32, + .realbits = 8, + .shift = 0, + }, + [AD4630_SCAN_TYPE_NORMAL_AND_COMMON_MODE] = { + .sign = 'u', + .storagebits = 32, + .realbits = 32, + .shift = 0, + }, +}; + /* * We need the sample size to be 64 bytes when both channels are enabled as the * HW will always fill in the DMA bus which is 64bits. If we had just 16 bits @@ -815,13 +994,15 @@ static const struct ad4630_out_mode ad4030_24_modes[] = { }, [AD4630_16_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, AD4630_CHAN_INFO_NONE, 64, 16, 8, NULL), + AD4630_COM_CHAN(0, AD4630_CHAN_INFO_NONE, 64, 16, 8, + _dual, NULL), }, .data_width = 24, }, [AD4630_24_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, AD4630_CHAN_INFO_NONE, 64, 24, 8, NULL), + AD4630_COM_CHAN(0, AD4630_CHAN_INFO_NONE, 64, 24, 8, + _dual, NULL), }, .data_width = 32, }, @@ -844,8 +1025,10 @@ static const struct ad4630_out_mode ad4630_16_modes[] = { }, [AD4630_16_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, AD4630_CHAN_INFO_NONE, 32, 16, 8, NULL), - AD4630_CHAN(1, AD4630_CHAN_INFO_NONE, 32, 16, 8, NULL), + AD4630_COM_CHAN(0, AD4630_CHAN_INFO_NONE, 32, 16, 8, + _single, NULL), + AD4630_COM_CHAN(1, AD4630_CHAN_INFO_NONE, 32, 16, 8, + _single, NULL), }, .data_width = 24, }, @@ -870,15 +1053,19 @@ static const struct ad4630_out_mode ad4630_24_modes[] = { }, [AD4630_16_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, AD4630_CHAN_INFO_NONE, 32, 16, 8, NULL), - AD4630_CHAN(1, AD4630_CHAN_INFO_NONE, 32, 16, 8, NULL), + AD4630_COM_CHAN(0, AD4630_CHAN_INFO_NONE, 32, 16, 8, + _single, NULL), + AD4630_COM_CHAN(1, AD4630_CHAN_INFO_NONE, 32, 16, 8, + _single, NULL), }, .data_width = 24, }, [AD4630_24_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, AD4630_CHAN_INFO_NONE, 32, 24, 8, NULL), - AD4630_CHAN(1, AD4630_CHAN_INFO_NONE, 32, 24, 8, NULL), + AD4630_COM_CHAN(0, AD4630_CHAN_INFO_NONE, 32, 24, 8, + _single, NULL), + AD4630_COM_CHAN(1, AD4630_CHAN_INFO_NONE, 32, 24, 8, + _single, NULL), }, .data_width = 32, }, @@ -902,7 +1089,8 @@ static const struct ad4630_out_mode adaq4216_modes[] = { }, [AD4630_16_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 16, 8, NULL), + AD4630_COM_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 16, 8, + _dual, NULL), }, .data_width = 24, }, @@ -923,7 +1111,8 @@ static const struct ad4630_out_mode adaq4220_modes[] = { }, [AD4630_16_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 16, 8, NULL), + AD4630_COM_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 16, 8, + _dual, NULL), }, .data_width = 24, }, @@ -944,13 +1133,15 @@ static const struct ad4630_out_mode adaq4224_modes[] = { }, [AD4630_16_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 16, 8, NULL), + AD4630_COM_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 16, 8, + _dual, NULL), }, .data_width = 24, }, [AD4630_24_DIFF_8_COM] = { .channels = { - AD4630_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 24, 8, NULL), + AD4630_COM_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 64, 24, 8, + _dual, NULL), }, .data_width = 32, }, @@ -1323,12 +1514,21 @@ static int ad4630_config(struct ad4630_state *st) return ret; st->max_rate = AD4630_MAX_RATE; + st->current_scan_type = AD4630_SCAN_TYPE_NORMAL; ad4630_prepare_spi_sampling_msg(st, clock_mode, lane_mode, data_rate); return 0; } +static int ad4630_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad4630_state *st = iio_priv(indio_dev); + + return st->current_scan_type; +} + static const struct iio_buffer_setup_ops ad4630_buffer_setup_ops = { .preenable = &ad4630_buffer_preenable, .postdisable = &ad4630_buffer_postdisable, @@ -1339,6 +1539,7 @@ static const struct iio_info ad4630_info = { .read_avail = &ad4630_read_avail, .write_raw = &ad4630_write_raw, .write_raw_get_fmt = &ad4630_write_raw_get_fmt, + .get_current_scan_type = &ad4630_get_current_scan_type, .debugfs_reg_access = &ad4630_reg_access, }; @@ -1520,11 +1721,6 @@ static int ad4630_probe(struct spi_device *spi) return dev_err_probe(&spi->dev, ret, "Config failed: %d\n", ret); - if (st->pga_gpios) { - ad4630_fill_scale_tbl(st); - ad4630_set_pga_gain(indio_dev, 0); - } - /* * Due to a hardware bug in some chips when using average mode zero * (no averaging), set default averaging mode to 2 samples. @@ -1546,6 +1742,11 @@ static int ad4630_probe(struct spi_device *spi) indio_dev->available_scan_masks = st->chip->available_masks; indio_dev->setup_ops = &ad4630_buffer_setup_ops; + if (st->pga_gpios) { + ad4630_fill_scale_tbl(indio_dev, st); + ad4630_set_pga_gain(indio_dev, 0); + } + ret = devm_iio_dmaengine_buffer_setup(dev, indio_dev, "rx", IIO_BUFFER_DIRECTION_IN); if (ret) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 5d656c131f6a78..8eb442bce68160 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -399,8 +399,16 @@ static ssize_t iio_show_fixed_type(struct device *dev, struct device_attribute *attr, char *buf) { + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - u8 type = this_attr->c->scan_type.endianness; + const struct iio_scan_type *scan_type; + u8 type; + + scan_type = iio_get_current_scan_type(indio_dev, this_attr->c); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + type = scan_type->endianness; if (type == IIO_CPU) { #ifdef __LITTLE_ENDIAN @@ -409,21 +417,21 @@ static ssize_t iio_show_fixed_type(struct device *dev, type = IIO_BE; #endif } - if (this_attr->c->scan_type.repeat > 1) + if (scan_type->repeat > 1) return sysfs_emit(buf, "%s:%c%d/%dX%d>>%u\n", iio_endian_prefix[type], - this_attr->c->scan_type.sign, - this_attr->c->scan_type.realbits, - this_attr->c->scan_type.storagebits, - this_attr->c->scan_type.repeat, - this_attr->c->scan_type.shift); + scan_type->sign, + scan_type->realbits, + scan_type->storagebits, + scan_type->repeat, + scan_type->shift); else return sysfs_emit(buf, "%s:%c%d/%d>>%u\n", iio_endian_prefix[type], - this_attr->c->scan_type.sign, - this_attr->c->scan_type.realbits, - this_attr->c->scan_type.storagebits, - this_attr->c->scan_type.shift); + scan_type->sign, + scan_type->realbits, + scan_type->storagebits, + scan_type->shift); } static ssize_t iio_scan_el_show(struct device *dev, @@ -731,20 +739,27 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%d\n", iio_buffer_is_active(buffer)); } -static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev, - unsigned int scan_index) +static int iio_storage_bytes_for_si(struct iio_dev *indio_dev, + unsigned int scan_index) { const struct iio_chan_spec *ch; + const struct iio_scan_type *scan_type; unsigned int bytes; ch = iio_find_channel_from_si(indio_dev, scan_index); - bytes = ch->scan_type.storagebits / 8; - if (ch->scan_type.repeat > 1) - bytes *= ch->scan_type.repeat; + scan_type = iio_get_current_scan_type(indio_dev, ch); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); + + bytes = scan_type->storagebits / 8; + + if (scan_type->repeat > 1) + bytes *= scan_type->repeat; + return bytes; } -static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) +static int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); @@ -762,6 +777,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, for_each_set_bit(i, mask, indio_dev->masklength) { length = iio_storage_bytes_for_si(indio_dev, i); + if (length < 0) + return length; + bytes = ALIGN(bytes, length); bytes += length; largest = max(largest, length); @@ -769,6 +787,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, if (timestamp) { length = iio_storage_bytes_for_timestamp(indio_dev); + if (length < 0) + return length; + bytes = ALIGN(bytes, length); bytes += length; largest = max(largest, length); @@ -1047,14 +1068,22 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, indio_dev->masklength, in_ind + 1); while (in_ind != out_ind) { - length = iio_storage_bytes_for_si(indio_dev, in_ind); + ret = iio_storage_bytes_for_si(indio_dev, in_ind); + if (ret < 0) + goto error_clear_mux_table; + + length = ret; /* Make sure we are aligned */ in_loc = roundup(in_loc, length) + length; in_ind = find_next_bit(indio_dev->active_scan_mask, indio_dev->masklength, in_ind + 1); } - length = iio_storage_bytes_for_si(indio_dev, in_ind); + ret = iio_storage_bytes_for_si(indio_dev, in_ind); + if (ret < 0) + goto error_clear_mux_table; + + length = ret; out_loc = roundup(out_loc, length); in_loc = roundup(in_loc, length); ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length); @@ -1065,7 +1094,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, } /* Relies on scan_timestamp being last */ if (buffer->scan_timestamp) { - length = iio_storage_bytes_for_timestamp(indio_dev); + ret = iio_storage_bytes_for_timestamp(indio_dev); + if (ret < 0) + goto error_clear_mux_table; + + length = ret; out_loc = roundup(out_loc, length); in_loc = roundup(in_loc, length); ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length); @@ -1867,6 +1900,22 @@ static long iio_device_buffer_ioctl(struct iio_dev *indio_dev, struct file *filp } } +static int iio_channel_validate_scan_type(struct device *dev, int ch, + const struct iio_scan_type *scan_type) +{ + /* Verify that sample bits fit into storage */ + if (scan_type->storagebits < scan_type->realbits + scan_type->shift) { + dev_err(dev, + "Channel %d storagebits (%d) < shifted realbits (%d + %d)\n", + ch, scan_type->storagebits, + scan_type->realbits, + scan_type->shift); + return -EINVAL; + } + + return 0; +} + static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, struct iio_dev *indio_dev, int index) @@ -1889,20 +1938,38 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, if (channels) { /* new magic */ for (i = 0; i < indio_dev->num_channels; i++) { + const struct iio_scan_type *scan_type; + if (channels[i].scan_index < 0) continue; - /* Verify that sample bits fit into storage */ - if (channels[i].scan_type.storagebits < - channels[i].scan_type.realbits + - channels[i].scan_type.shift) { - dev_err(&indio_dev->dev, - "Channel %d storagebits (%d) < shifted realbits (%d + %d)\n", - i, channels[i].scan_type.storagebits, - channels[i].scan_type.realbits, - channels[i].scan_type.shift); - ret = -EINVAL; - goto error_cleanup_dynamic; + if (channels[i].has_ext_scan_type) { + int j; + + /* + * get_current_scan_type is required when using + * extended scan types. + */ + if (!indio_dev->info->get_current_scan_type) { + ret = -EINVAL; + goto error_cleanup_dynamic; + } + + for (j = 0; j < channels[i].num_ext_scan_type; j++) { + scan_type = &channels[i].ext_scan_type[j]; + + ret = iio_channel_validate_scan_type( + &indio_dev->dev, i, scan_type); + if (ret) + goto error_cleanup_dynamic; + } + } else { + scan_type = &channels[i].scan_type; + + ret = iio_channel_validate_scan_type( + &indio_dev->dev, i, scan_type); + if (ret) + goto error_cleanup_dynamic; } ret = iio_buffer_add_channel_sysfs(indio_dev, buffer, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index e7da2aa6f1c3d5..50547953a13a8f 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -172,6 +172,27 @@ struct iio_event_spec { unsigned long mask_shared_by_all; }; +/** + * struct iio_scan_type - specification for channel data format in buffer + * @sign: 's' or 'u' to specify signed or unsigned + * @realbits: Number of valid bits of data + * @storagebits: Realbits + padding + * @shift: Shift right by this before masking out realbits. + * @repeat: Number of times real/storage bits repeats. When the + * repeat element is more than 1, then the type element in + * sysfs will show a repeat value. Otherwise, the number + * of repetitions is omitted. + * @endianness: little or big endian + */ +struct iio_scan_type { + char sign; + u8 realbits; + u8 storagebits; + u8 shift; + u8 repeat; + enum iio_endian endianness; +}; + /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. @@ -182,18 +203,13 @@ struct iio_event_spec { * @address: Driver specific identifier. * @scan_index: Monotonic index to give ordering in scans when read * from a buffer. - * @scan_type: struct describing the scan type - * @scan_type.sign: 's' or 'u' to specify signed or unsigned - * @scan_type.realbits: Number of valid bits of data - * @scan_type.storagebits: Realbits + padding - * @scan_type.shift: Shift right by this before masking out - * realbits. - * @scan_type.repeat: Number of times real/storage bits repeats. - * When the repeat element is more than 1, then - * the type element in sysfs will show a repeat - * value. Otherwise, the number of repetitions - * is omitted. - * @scan_type.endianness: little or big endian + * @scan_type: struct describing the scan type - mutually exclusive + * with ext_scan_type. + * @ext_scan_type: Used in rare cases where there is more than one scan + * format for a channel. When this is used, the flag + * has_ext_scan_type must be set and the driver must + * implement get_current_scan_type in struct iio_info. + * @num_ext_scan_type: Number of elements in ext_scan_type. * @info_mask_separate: What information is to be exported that is specific to * this channel. * @info_mask_separate_available: What availability information is to be @@ -234,6 +250,7 @@ struct iio_event_spec { * attributes but not for event codes. * @output: Channel is output. * @differential: Channel is differential. + * @has_ext_scan_type: True if ext_scan_type is used instead of scan_type. */ struct iio_chan_spec { enum iio_chan_type type; @@ -241,14 +258,13 @@ struct iio_chan_spec { int channel2; unsigned long address; int scan_index; - struct { - char sign; - u8 realbits; - u8 storagebits; - u8 shift; - u8 repeat; - enum iio_endian endianness; - } scan_type; + union { + struct iio_scan_type scan_type; + struct { + const struct iio_scan_type *ext_scan_type; + unsigned int num_ext_scan_type; + }; + }; long info_mask_separate; long info_mask_separate_available; long info_mask_shared_by_type; @@ -266,6 +282,7 @@ struct iio_chan_spec { unsigned indexed:1; unsigned output:1; unsigned differential:1; + unsigned has_ext_scan_type:1; }; @@ -423,6 +440,9 @@ struct iio_trigger; /* forward declaration */ * for better event identification. * @validate_trigger: function to validate the trigger when the * current trigger gets changed. + * @get_current_scan_type: must be implemented by drivers that use ext_scan_type + * in the channel spec to return the index of the currently + * active ext_scan type for a channel. * @update_scan_mode: function to configure device and scan buffer when * channels have changed * @debugfs_reg_access: function to read or write register value of device @@ -513,6 +533,8 @@ struct iio_info { int (*validate_trigger)(struct iio_dev *indio_dev, struct iio_trigger *trig); + int (*get_current_scan_type)(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan); int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask); int (*debugfs_reg_access)(struct iio_dev *indio_dev, @@ -757,6 +779,38 @@ static inline struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev) } #endif +/** + * iio_get_current_scan_type - Get the current scan type for a channel + * @indio_dev: the IIO device to get the scan type for + * @chan: the channel to get the scan type for + * + * Most devices only have one scan type per channel and can just access it + * directly without calling this function. Core IIO code and drivers that + * implement ext_scan_type in the channel spec should use this function to + * get the current scan type for a channel. + * + * Returns: the current scan type for the channel or error. + */ +static inline const struct iio_scan_type +*iio_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + int ret; + + if (chan->has_ext_scan_type) { + ret = indio_dev->info->get_current_scan_type(indio_dev, chan); + if (ret < 0) + return ERR_PTR(ret); + + if (ret >= chan->num_ext_scan_type) + return ERR_PTR(-EINVAL); + + return &chan->ext_scan_type[ret]; + } + + return &chan->scan_type; +} + ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,