From 2dbbe4d513ad17f6ce125234c7710626268b8932 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 9 Mar 2020 11:28:47 +0100 Subject: iio: xilinx-xadc: Fix typo in author's name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It appears the author of the xilinx-xadc driver can't even spell his own name correctly. Fix that. Reported-by: Lars Möllendorf Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 2 +- drivers/iio/adc/xilinx-xadc-events.c | 2 +- drivers/iio/adc/xilinx-xadc.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index ec227b358cd6..2d6505a66511 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -3,7 +3,7 @@ * Xilinx XADC driver * * Copyright 2013-2014 Analog Devices Inc. - * Author: Lars-Peter Clauen + * Author: Lars-Peter Clausen * * Documentation for the parts can be found at: * - XADC hardmacro: Xilinx UG480 diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c index dbfd5da290a4..2357f585720a 100644 --- a/drivers/iio/adc/xilinx-xadc-events.c +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -3,7 +3,7 @@ * Xilinx XADC driver * * Copyright 2013 Analog Devices Inc. - * Author: Lars-Peter Clauen + * Author: Lars-Peter Clausen */ #include diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h index 4017f18b0a4f..25abed9c0285 100644 --- a/drivers/iio/adc/xilinx-xadc.h +++ b/drivers/iio/adc/xilinx-xadc.h @@ -3,7 +3,7 @@ * Xilinx XADC driver * * Copyright 2013 Analog Devices Inc. - * Author: Lars-Peter Clauen + * Author: Lars-Peter Clausen */ #ifndef __IIO_XILINX_XADC__ -- cgit v1.2.3 From 4be590e3f6ec402abe563db90afff6ce3c44737f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Mar 2020 11:22:23 +0200 Subject: iio: adc: intel_mrfld_adc: Use be16_to_cpu() instead of get_unaligned_be16() There is no need to call unaligned helpers on stack placed variables because compiler will align them correctly, accordingly to architectural ABI. Moreover, using bitwise type makes it explicit to see what we are reading in bulk transfer. On top of that, use sizeof() instead of magic value. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/intel_mrfld_adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index c35a1beb817c..a6d2e1f27e76 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -75,7 +75,7 @@ static int mrfld_adc_single_conv(struct iio_dev *indio_dev, struct regmap *regmap = adc->regmap; unsigned int req; long timeout; - u8 buf[2]; + __be16 value; int ret; reinit_completion(&adc->completion); @@ -105,11 +105,11 @@ static int mrfld_adc_single_conv(struct iio_dev *indio_dev, goto done; } - ret = regmap_bulk_read(regmap, chan->address, buf, 2); + ret = regmap_bulk_read(regmap, chan->address, &value, sizeof(value)); if (ret) goto done; - *result = get_unaligned_be16(buf); + *result = be16_to_cpu(value); ret = IIO_VAL_INT; done: -- cgit v1.2.3 From af37e4703d00cea66e467ffe69afe2540fa32f75 Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Wed, 11 Mar 2020 10:43:25 +0200 Subject: iio: adc: ad7476: Generate CONVST signal internally Compared to the other supported parts, AD7091R are dependent of a CONVST signal that initiates the conversion. At this moment, only sampling in buffered mode is supported for AD7091R and the only option until now was to generate this signal externally using an IIO trigger. This patch adds the option of generating it internally, more compatible triggers being available in this case. Also, it is an intermediate step of adding support more devices. Signed-off-by: Dragos Bogdan Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 76747488044b..32e857dfec9c 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -34,6 +36,7 @@ struct ad7476_state { struct spi_device *spi; const struct ad7476_chip_info *chip_info; struct regulator *reg; + struct gpio_desc *convst_gpio; struct spi_transfer xfer; struct spi_message msg; /* @@ -64,6 +67,17 @@ enum ad7476_supported_device_ids { ID_ADS7868, }; +static void ad7091_convst(struct ad7476_state *st) +{ + if (!st->convst_gpio) + return; + + gpiod_set_value(st->convst_gpio, 0); + udelay(1); /* CONVST pulse width: 10 ns min */ + gpiod_set_value(st->convst_gpio, 1); + udelay(1); /* Conversion time: 650 ns max */ +} + static irqreturn_t ad7476_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -71,6 +85,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) struct ad7476_state *st = iio_priv(indio_dev); int b_sent; + ad7091_convst(st); + b_sent = spi_sync(st->spi, &st->msg); if (b_sent < 0) goto done; @@ -254,6 +270,12 @@ static int ad7476_probe(struct spi_device *spi) if (ret) return ret; + st->convst_gpio = devm_gpiod_get_optional(&spi->dev, + "adi,conversion-start", + GPIOD_OUT_LOW); + if (IS_ERR(st->convst_gpio)) + return PTR_ERR(st->convst_gpio); + spi_set_drvdata(spi, indio_dev); st->spi = spi; -- cgit v1.2.3 From 3a6af93dd66eba90cee3eb778b83bdccb2202d4f Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Wed, 11 Mar 2020 10:43:26 +0200 Subject: iio: adc: ad7476: Add IIO_CHAN_INFO_RAW for AD7091R When CONVST signal is generated internally, IIO_CHAN_INFO_RAW can be made available for AD7091R for single reads. This patch enables it and makes supporting more devices by this driver easier. Signed-off-by: Dragos Bogdan Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 32e857dfec9c..3b48073dd62d 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -29,6 +29,8 @@ struct ad7476_state; struct ad7476_chip_info { unsigned int int_vref_uv; struct iio_chan_spec channel[2]; + /* channels used when convst gpio is defined */ + struct iio_chan_spec convst_channel[2]; void (*reset)(struct ad7476_state *); }; @@ -109,6 +111,8 @@ static int ad7476_scan_direct(struct ad7476_state *st) { int ret; + ad7091_convst(st); + ret = spi_sync(st->spi, &st->msg); if (ret) return ret; @@ -176,6 +180,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, #define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \ BIT(IIO_CHAN_INFO_RAW)) #define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0) +#define AD7091R_CONVST_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) #define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ BIT(IIO_CHAN_INFO_RAW)) @@ -183,6 +189,8 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { [ID_AD7091R] = { .channel[0] = AD7091R_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .convst_channel[0] = AD7091R_CONVST_CHAN(12), + .convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .reset = ad7091_reset, }, [ID_AD7276] = { @@ -288,6 +296,9 @@ static int ad7476_probe(struct spi_device *spi) indio_dev->channels = st->chip_info->channel; indio_dev->num_channels = 2; indio_dev->info = &ad7476_info; + + if (st->convst_gpio && st->chip_info->convst_channel) + indio_dev->channels = st->chip_info->convst_channel; /* Setup default message */ st->xfer.rx_buf = &st->data; -- cgit v1.2.3 From 0267a3166644037f0de66b496165a5801a99a5ae Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Wed, 11 Mar 2020 10:43:27 +0200 Subject: iio: adc: ad7476: Add AD7091 support AD7091R is already supported by this driver. While AD7091R allows the choice of an internal or an external voltage reference, for AD7091 the reference is only provided by VDD. Since this information is anyway obtained through the "vcc" regulator, no other driver changes are required for adding AD7091 support as well. Signed-off-by: Dragos Bogdan Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 3b48073dd62d..cd7d42df2033 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -341,6 +341,7 @@ static int ad7476_remove(struct spi_device *spi) } static const struct spi_device_id ad7476_id[] = { + {"ad7091", ID_AD7091R}, {"ad7091r", ID_AD7091R}, {"ad7273", ID_AD7277}, {"ad7274", ID_AD7276}, -- cgit v1.2.3 From 4bb2b8f94ace32c1e0d94a7dc9f881c44c3a71b8 Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Wed, 11 Mar 2020 10:43:28 +0200 Subject: iio: adc: ad7476: implement devm_add_action_or_reset Use devm_add_action_or_reset to automatically disable the device when it is removed or an error occurs during probe routine. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index cd7d42df2033..e9984a38fc4c 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -256,6 +256,13 @@ static const struct iio_info ad7476_info = { .read_raw = &ad7476_read_raw, }; +static void ad7476_reg_disable(void *data) +{ + struct ad7476_state *st = data; + + regulator_disable(st->reg); +} + static int ad7476_probe(struct spi_device *spi) { struct ad7476_state *st; @@ -278,6 +285,11 @@ static int ad7476_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, + st); + if (ret) + return ret; + st->convst_gpio = devm_gpiod_get_optional(&spi->dev, "adi,conversion-start", GPIOD_OUT_LOW); @@ -328,18 +340,6 @@ error_disable_reg: return ret; } -static int ad7476_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7476_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(st->reg); - - return 0; -} - static const struct spi_device_id ad7476_id[] = { {"ad7091", ID_AD7091R}, {"ad7091r", ID_AD7091R}, @@ -377,7 +377,6 @@ static struct spi_driver ad7476_driver = { .name = "ad7476", }, .probe = ad7476_probe, - .remove = ad7476_remove, .id_table = ad7476_id, }; module_spi_driver(ad7476_driver); -- cgit v1.2.3 From 5924dab2412b42144c93ab6ab72ad489617b0121 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sat, 21 Mar 2020 11:07:58 +0200 Subject: iio: adc: ad7780: define/use own IIO channel macros This change gets rid of the AD_SD_*_CHANNEL macros in favor of defining it's own. The ad7780 is quite simpler than it's other Sigma-Delta brothers. It turned out that centralizing the AD_SD_*_CHANNEL macros doesn't scale too well, especially with some more complicated drivers. Some of the variations in the more complicated drivers require new macros, and that way things can become harder to maintain. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7780.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 291c1a898129..f47606ebbbbe 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -206,10 +206,29 @@ static const struct ad_sigma_delta_info ad7780_sigma_delta_info = { .irq_flags = IRQF_TRIGGER_LOW, }; -#define AD7780_CHANNEL(bits, wordsize) \ - AD_SD_CHANNEL(1, 0, 0, bits, 32, (wordsize) - (bits)) -#define AD7170_CHANNEL(bits, wordsize) \ - AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, (wordsize) - (bits)) +#define _AD7780_CHANNEL(_bits, _wordsize, _mask_all) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 32, \ + .shift = (_wordsize) - (_bits), \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7780_CHANNEL(_bits, _wordsize) \ + _AD7780_CHANNEL(_bits, _wordsize, BIT(IIO_CHAN_INFO_SAMP_FREQ)) +#define AD7170_CHANNEL(_bits, _wordsize) \ + _AD7780_CHANNEL(_bits, _wordsize, 0) static const struct ad7780_chip_info ad7780_chip_info_tbl[] = { [ID_AD7170] = { -- cgit v1.2.3 From ab3449943b1da697301c3aa814b7c5f3bfe3065b Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sat, 21 Mar 2020 11:07:59 +0200 Subject: iio: adc: ad7791: define/use own IIO channel macros This driver seems to use most of the AD_SD_*_CHANNEL. This change will move them in the driver. The intent is that if a new part comes along which would require tweaks per IIO channel, these should be doable in the driver. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7791.c | 62 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index abb239392631..ba22808507d0 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -64,25 +64,73 @@ #define AD7791_MODE_SEL_MASK (0x3 << 6) #define AD7791_MODE_SEL(x) ((x) << 6) +#define __AD7991_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type, _mask_all) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD7991_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD7991_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + #define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \ const struct iio_chan_spec name[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ (bits), (storagebits), 0), \ - AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ - AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ + AD7991_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ + AD7991_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ (bits), (storagebits), 0), \ - AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ + AD7991_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ (bits), (storagebits), 0), \ IIO_CHAN_SOFT_TIMESTAMP(4), \ } #define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \ const struct iio_chan_spec name[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ (bits), (storagebits), 0), \ - AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ + AD7991_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ (bits), (storagebits), 0), \ - AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ + AD7991_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ (bits), (storagebits), 0), \ IIO_CHAN_SOFT_TIMESTAMP(3), \ } -- cgit v1.2.3 From aebefb444fe889bd7d92741a3dcc1c20c308f875 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sat, 21 Mar 2020 11:08:00 +0200 Subject: iio: adc: ad7793: define/use own IIO channel macros This driver seems to use most of the AD_SD_*_CHANNEL. This change will move them in the driver. The intent is that if a new part comes along which would require tweaks per IIO channel, these should be doable in the driver. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 95 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 24 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index b747db97f78a..5592ae573e6b 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -546,47 +546,94 @@ static const struct iio_info ad7797_info = { .validate_trigger = ad_sd_validate_trigger, }; +#define __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type, _mask_all) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD7793_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \ + __AD7793_CHANNEL(_si, 0, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD7793_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + #define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ const struct iio_chan_spec _name##_channels[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ - AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ - AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ - AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ - AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ - AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ + AD7793_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ + AD7793_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ + AD7793_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ IIO_CHAN_SOFT_TIMESTAMP(6), \ } #define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \ const struct iio_chan_spec _name##_channels[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ - AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ - AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ - AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ IIO_CHAN_SOFT_TIMESTAMP(9), \ } #define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \ const struct iio_chan_spec _name##_channels[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ - AD_SD_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ - AD_SD_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ - AD_SD_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ IIO_CHAN_SOFT_TIMESTAMP(4), \ } #define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \ const struct iio_chan_spec _name##_channels[] = { \ - AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ - AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ - AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ - AD_SD_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ IIO_CHAN_SOFT_TIMESTAMP(5), \ } -- cgit v1.2.3 From bf09cddb2cd4028681a83426649f18b90c662e16 Mon Sep 17 00:00:00 2001 From: Rohit Sarkar Date: Sun, 15 Mar 2020 21:34:58 +0530 Subject: iio: adc: max1363: replace uses of mlock Replace usage indio_dev's mlock with either local lock or iio_device_claim_direct_mode. Signed-off-by: Rohit Sarkar Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 5c2cc61b666e..9d92017c79b2 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -150,6 +150,7 @@ struct max1363_chip_info { * @current_mode: the scan mode of this chip * @requestedmask: a valid requested set of channels * @reg: supply regulator + * @lock lock to ensure state is consistent * @monitor_on: whether monitor mode is enabled * @monitor_speed: parameter corresponding to device monitor speed setting * @mask_high: bitmask for enabled high thresholds @@ -169,6 +170,7 @@ struct max1363_state { const struct max1363_mode *current_mode; u32 requestedmask; struct regulator *reg; + struct mutex lock; /* Using monitor modes and buffer at the same time is currently not supported */ @@ -364,7 +366,11 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, struct max1363_state *st = iio_priv(indio_dev); struct i2c_client *client = st->client; - mutex_lock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + /* * If monitor mode is enabled, the method for reading a single * channel will have to be rather different and has not yet @@ -372,7 +378,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, * * Also, cannot read directly if buffered capture enabled. */ - if (st->monitor_on || iio_buffer_enabled(indio_dev)) { + if (st->monitor_on) { ret = -EBUSY; goto error_ret; } @@ -404,8 +410,10 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, data = rxbuf[0]; } *val = data; + error_ret: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return ret; } @@ -705,9 +713,9 @@ static ssize_t max1363_monitor_store_freq(struct device *dev, if (!found) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->monitor_speed = i; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return 0; } @@ -810,12 +818,12 @@ static int max1363_read_event_config(struct iio_dev *indio_dev, int val; int number = chan->channel; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if (dir == IIO_EV_DIR_FALLING) val = (1 << number) & st->mask_low; else val = (1 << number) & st->mask_high; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return val; } @@ -962,7 +970,11 @@ static int max1363_write_event_config(struct iio_dev *indio_dev, u16 unifiedmask; int number = chan->channel; - mutex_lock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + unifiedmask = st->mask_low | st->mask_high; if (dir == IIO_EV_DIR_FALLING) { @@ -989,7 +1001,8 @@ static int max1363_write_event_config(struct iio_dev *indio_dev, max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); error_ret: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return ret; } @@ -1587,6 +1600,7 @@ static int max1363_probe(struct i2c_client *client, st = iio_priv(indio_dev); + mutex_init(&st->lock); st->reg = devm_regulator_get(&client->dev, "vcc"); if (IS_ERR(st->reg)) { ret = PTR_ERR(st->reg); -- cgit v1.2.3 From 1cfe38fc40e6d3d86e8fc88f9e2c080b9b38df3d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 22 Mar 2020 17:26:56 +0200 Subject: iio: adc: ad7793: use read_avail iio hook for scale available This change uses the read_avail and '.info_mask_shared_by_type_available' modifier to set the available scale. Essentially, nothing changes to the driver's ABI. The main idea for this patch is to remove the AD7793 driver from checkpatch's radar. There have been about ~3 attempts to fix/break the 'in_voltage-voltage_scale_available' attribute, because checkpatch assumed it to be an arithmetic operation and people were trying to change that. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 55 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 5592ae573e6b..7005bde50a76 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -354,29 +354,28 @@ static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797, sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4"); -static ssize_t ad7793_show_scale_available(struct device *dev, - struct device_attribute *attr, char *buf) +static int ad7793_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7793_state *st = iio_priv(indio_dev); - int i, len = 0; - for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) - len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0], - st->scale_avail[i][1]); - - len += sprintf(buf + len, "\n"); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->scale_avail) * 2; - return len; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } } -static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, - in_voltage-voltage_scale_available, S_IRUGO, - ad7793_show_scale_available, NULL, 0); - static struct attribute *ad7793_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_in_m_in_scale_available.dev_attr.attr, NULL }; @@ -534,6 +533,7 @@ static const struct iio_info ad7793_info = { .read_raw = &ad7793_read_raw, .write_raw = &ad7793_write_raw, .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .read_avail = ad7793_read_avail, .attrs = &ad7793_attribute_group, .validate_trigger = ad_sd_validate_trigger, }; @@ -547,7 +547,7 @@ static const struct iio_info ad7797_info = { }; #define __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ - _storagebits, _shift, _extend_name, _type, _mask_all) \ + _storagebits, _shift, _extend_name, _type, _mask_type_av, _mask_all) \ { \ .type = (_type), \ .differential = (_channel2 == -1 ? 0 : 1), \ @@ -559,6 +559,7 @@ static const struct iio_info ad7797_info = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = (_mask_type_av), \ .info_mask_shared_by_all = _mask_all, \ .scan_index = (_si), \ .scan_type = { \ @@ -574,23 +575,41 @@ static const struct iio_info ad7797_info = { _storagebits, _shift) \ __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), \ BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define AD7793_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ _storagebits, _shift) \ __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), \ BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define AD7793_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \ __AD7793_CHANNEL(_si, 0, -1, _address, _bits, \ _storagebits, _shift, NULL, IIO_TEMP, \ + 0, \ BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define AD7793_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ _shift) \ __AD7793_CHANNEL(_si, _channel, -1, _address, _bits, \ _storagebits, _shift, "supply", IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7797_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7797_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + 0, \ BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ @@ -620,8 +639,8 @@ const struct iio_chan_spec _name##_channels[] = { \ #define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \ const struct iio_chan_spec _name##_channels[] = { \ - AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ - AD7793_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7797_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7797_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ AD7793_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ AD7793_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ IIO_CHAN_SOFT_TIMESTAMP(4), \ -- cgit v1.2.3 From ef04070692a21633ec6a60f80c19b6af44b3cf47 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 24 Mar 2020 15:46:33 +0200 Subject: iio: adc: adi-axi-adc: add support for AXI ADC IP core This change adds support for the Analog Devices Generic AXI ADC IP core. The IP core is used for interfacing with analog-to-digital (ADC) converters that require either a high-speed serial interface (JESD204B/C) or a source synchronous parallel interface (LVDS/CMOS). Usually, some other interface type (i.e SPI) is used as a control interface for the actual ADC, while the IP core (controlled via this driver), will interface to the data-lines of the ADC and handle the streaming of data into memory via DMA. Because of this, the AXI ADC driver needs the other SPI-ADC driver to register with it. The SPI-ADC needs to be register via the SPI framework, while the AXI ADC registers as a platform driver. The two cannot be ordered in a hierarchy as both drivers have their own registers, and trying to organize this [in a hierarchy becomes] problematic when trying to map memory/registers. There are some modes where the AXI ADC can operate as standalone ADC, but those will be implemented at a later point in time. DocLink: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip Signed-off-by: Michael Hennerich Signed-off-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 20 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/adi-axi-adc.c | 482 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 503 insertions(+) create mode 100644 drivers/iio/adc/adi-axi-adc.c (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index f4da821c4022..445070abf376 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -246,6 +246,26 @@ config AD799X To compile this driver as a module, choose M here: the module will be called ad799x. +config ADI_AXI_ADC + tristate "Analog Devices Generic AXI ADC IP core driver" + select IIO_BUFFER + select IIO_BUFFER_HW_CONSUMER + select IIO_BUFFER_DMAENGINE + help + Say yes here to build support for Analog Devices Generic + AXI ADC IP core. The IP core is used for interfacing with + analog-to-digital (ADC) converters that require either a high-speed + serial interface (JESD204B/C) or a source synchronous parallel + interface (LVDS/CMOS). + Typically (for such devices) SPI will be used for configuration only, + while this IP core handles the streaming of data into memory via DMA. + + Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called adi-axi-adc. + config ASPEED_ADC tristate "Aspeed ADC" depends on ARCH_ASPEED || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 8462455b4228..7c6594d049f9 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD7949) += ad7949.o obj-$(CONFIG_AD799X) += ad799x.o +obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c new file mode 100644 index 000000000000..c24c8da99eb4 --- /dev/null +++ b/drivers/iio/adc/adi-axi-adc.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices Generic AXI ADC IP core + * Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + * + * Copyright 2012-2020 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/** + * Register definitions: + * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map + */ + +/* ADC controls */ + +#define ADI_AXI_REG_RSTN 0x0040 +#define ADI_AXI_REG_RSTN_CE_N BIT(2) +#define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1) +#define ADI_AXI_REG_RSTN_RSTN BIT(0) + +/* ADC Channel controls */ + +#define ADI_AXI_REG_CHAN_CTRL(c) (0x0400 + (c) * 0x40) +#define ADI_AXI_REG_CHAN_CTRL_LB_OWR BIT(11) +#define ADI_AXI_REG_CHAN_CTRL_PN_SEL_OWR BIT(10) +#define ADI_AXI_REG_CHAN_CTRL_IQCOR_EN BIT(9) +#define ADI_AXI_REG_CHAN_CTRL_DCFILT_EN BIT(8) +#define ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT BIT(6) +#define ADI_AXI_REG_CHAN_CTRL_FMT_TYPE BIT(5) +#define ADI_AXI_REG_CHAN_CTRL_FMT_EN BIT(4) +#define ADI_AXI_REG_CHAN_CTRL_PN_TYPE_OWR BIT(1) +#define ADI_AXI_REG_CHAN_CTRL_ENABLE BIT(0) + +#define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \ + (ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT | \ + ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ + ADI_AXI_REG_CHAN_CTRL_ENABLE) + +struct adi_axi_adc_core_info { + unsigned int version; +}; + +struct adi_axi_adc_state { + struct mutex lock; + + struct adi_axi_adc_client *client; + void __iomem *regs; +}; + +struct adi_axi_adc_client { + struct list_head entry; + struct adi_axi_adc_conv conv; + struct adi_axi_adc_state *state; + struct device *dev; + const struct adi_axi_adc_core_info *info; +}; + +static LIST_HEAD(registered_clients); +static DEFINE_MUTEX(registered_clients_lock); + +static struct adi_axi_adc_client *conv_to_client(struct adi_axi_adc_conv *conv) +{ + return container_of(conv, struct adi_axi_adc_client, conv); +} + +void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv) +{ + struct adi_axi_adc_client *cl = conv_to_client(conv); + + return (char *)cl + ALIGN(sizeof(struct adi_axi_adc_client), IIO_ALIGN); +} +EXPORT_SYMBOL_GPL(adi_axi_adc_conv_priv); + +static void adi_axi_adc_write(struct adi_axi_adc_state *st, + unsigned int reg, + unsigned int val) +{ + iowrite32(val, st->regs + reg); +} + +static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st, + unsigned int reg) +{ + return ioread32(st->regs + reg); +} + +static int adi_axi_adc_config_dma_buffer(struct device *dev, + struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + const char *dma_name; + + if (!device_property_present(dev, "dmas")) + return 0; + + if (device_property_read_string(dev, "dma-names", &dma_name)) + dma_name = "rx"; + + buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, + dma_name); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + iio_device_attach_buffer(indio_dev, buffer); + + return 0; +} + +static int adi_axi_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + if (!conv->read_raw) + return -EOPNOTSUPP; + + return conv->read_raw(conv, chan, val, val2, mask); +} + +static int adi_axi_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + if (!conv->write_raw) + return -EOPNOTSUPP; + + return conv->write_raw(conv, chan, val, val2, mask); +} + +static int adi_axi_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + unsigned int i, ctrl; + + for (i = 0; i < conv->chip_info->num_channels; i++) { + ctrl = adi_axi_adc_read(st, ADI_AXI_REG_CHAN_CTRL(i)); + + if (test_bit(i, scan_mask)) + ctrl |= ADI_AXI_REG_CHAN_CTRL_ENABLE; + else + ctrl &= ~ADI_AXI_REG_CHAN_CTRL_ENABLE; + + adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), ctrl); + } + + return 0; +} + +static struct adi_axi_adc_conv *adi_axi_adc_conv_register(struct device *dev, + size_t sizeof_priv) +{ + struct adi_axi_adc_client *cl; + size_t alloc_size; + + alloc_size = ALIGN(sizeof(struct adi_axi_adc_client), IIO_ALIGN); + if (sizeof_priv) + alloc_size += ALIGN(sizeof_priv, IIO_ALIGN); + + cl = kzalloc(alloc_size, GFP_KERNEL); + if (!cl) + return ERR_PTR(-ENOMEM); + + mutex_lock(®istered_clients_lock); + + cl->dev = get_device(dev); + + list_add_tail(&cl->entry, ®istered_clients); + + mutex_unlock(®istered_clients_lock); + + return &cl->conv; +} + +static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv) +{ + struct adi_axi_adc_client *cl = conv_to_client(conv); + + mutex_lock(®istered_clients_lock); + + list_del(&cl->entry); + put_device(cl->dev); + + mutex_unlock(®istered_clients_lock); + + kfree(cl); +} + +static void devm_adi_axi_adc_conv_release(struct device *dev, void *res) +{ + adi_axi_adc_conv_unregister(*(struct adi_axi_adc_conv **)res); +} + +struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, + size_t sizeof_priv) +{ + struct adi_axi_adc_conv **ptr, *conv; + + ptr = devres_alloc(devm_adi_axi_adc_conv_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + conv = adi_axi_adc_conv_register(dev, sizeof_priv); + if (IS_ERR(conv)) { + devres_free(ptr); + return ERR_CAST(conv); + } + + *ptr = conv; + devres_add(dev, ptr); + + return conv; +} +EXPORT_SYMBOL_GPL(devm_adi_axi_adc_conv_register); + +static ssize_t in_voltage_scale_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + size_t len = 0; + int i; + + for (i = 0; i < conv->chip_info->num_scales; i++) { + const unsigned int *s = conv->chip_info->scale_table[i]; + + len += scnprintf(buf + len, PAGE_SIZE - len, + "%u.%06u ", s[0], s[1]); + } + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); + +enum { + ADI_AXI_ATTR_SCALE_AVAIL, +}; + +#define ADI_AXI_ATTR(_en_, _file_) \ + [ADI_AXI_ATTR_##_en_] = &iio_dev_attr_##_file_.dev_attr.attr + +static struct attribute *adi_axi_adc_attributes[] = { + ADI_AXI_ATTR(SCALE_AVAIL, in_voltage_scale_available), + NULL +}; + +static umode_t axi_adc_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + switch (n) { + case ADI_AXI_ATTR_SCALE_AVAIL: + if (!conv->chip_info->num_scales) + return 0; + return attr->mode; + default: + return attr->mode; + } +} + +static const struct attribute_group adi_axi_adc_attribute_group = { + .attrs = adi_axi_adc_attributes, + .is_visible = axi_adc_attr_is_visible, +}; + +static const struct iio_info adi_axi_adc_info = { + .read_raw = &adi_axi_adc_read_raw, + .write_raw = &adi_axi_adc_write_raw, + .attrs = &adi_axi_adc_attribute_group, + .update_scan_mode = &adi_axi_adc_update_scan_mode, +}; + +static const struct adi_axi_adc_core_info adi_axi_adc_10_0_a_info = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), +}; + +static struct adi_axi_adc_client *adi_axi_adc_attach_client(struct device *dev) +{ + const struct adi_axi_adc_core_info *info; + struct adi_axi_adc_client *cl; + struct device_node *cln; + + info = of_device_get_match_data(dev); + if (!info) + return ERR_PTR(-ENODEV); + + cln = of_parse_phandle(dev->of_node, "adi,adc-dev", 0); + if (!cln) { + dev_err(dev, "No 'adi,adc-dev' node defined\n"); + return ERR_PTR(-ENODEV); + } + + mutex_lock(®istered_clients_lock); + + list_for_each_entry(cl, ®istered_clients, entry) { + if (!cl->dev) + continue; + + if (cl->dev->of_node != cln) + continue; + + if (!try_module_get(dev->driver->owner)) { + mutex_unlock(®istered_clients_lock); + return ERR_PTR(-ENODEV); + } + + get_device(dev); + cl->info = info; + mutex_unlock(®istered_clients_lock); + return cl; + } + + mutex_unlock(®istered_clients_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + +static int adi_axi_adc_setup_channels(struct device *dev, + struct adi_axi_adc_state *st) +{ + struct adi_axi_adc_conv *conv = &st->client->conv; + int i, ret; + + if (conv->preenable_setup) { + ret = conv->preenable_setup(conv); + if (ret) + return ret; + } + + for (i = 0; i < conv->chip_info->num_channels; i++) { + adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), + ADI_AXI_REG_CHAN_CTRL_DEFAULTS); + } + + return 0; +} + +static void axi_adc_reset(struct adi_axi_adc_state *st) +{ + adi_axi_adc_write(st, ADI_AXI_REG_RSTN, 0); + mdelay(10); + adi_axi_adc_write(st, ADI_AXI_REG_RSTN, ADI_AXI_REG_RSTN_MMCM_RSTN); + mdelay(10); + adi_axi_adc_write(st, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); +} + +static void adi_axi_adc_cleanup(void *data) +{ + struct adi_axi_adc_client *cl = data; + + put_device(cl->dev); + module_put(cl->dev->driver->owner); +} + +static int adi_axi_adc_probe(struct platform_device *pdev) +{ + struct adi_axi_adc_conv *conv; + struct iio_dev *indio_dev; + struct adi_axi_adc_client *cl; + struct adi_axi_adc_state *st; + unsigned int ver; + int ret; + + cl = adi_axi_adc_attach_client(&pdev->dev); + if (IS_ERR(cl)) + return PTR_ERR(cl); + + ret = devm_add_action_or_reset(&pdev->dev, adi_axi_adc_cleanup, cl); + if (ret) + return ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->client = cl; + cl->state = st; + mutex_init(&st->lock); + + st->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(st->regs)) + return PTR_ERR(st->regs); + + conv = &st->client->conv; + + axi_adc_reset(st); + + ver = adi_axi_adc_read(st, ADI_AXI_REG_VERSION); + + if (cl->info->version > ver) { + dev_err(&pdev->dev, + "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", + ADI_AXI_PCORE_VER_MAJOR(cl->info->version), + ADI_AXI_PCORE_VER_MINOR(cl->info->version), + ADI_AXI_PCORE_VER_PATCH(cl->info->version), + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + return -ENODEV; + } + + indio_dev->info = &adi_axi_adc_info; + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = "adi-axi-adc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = conv->chip_info->num_channels; + indio_dev->channels = conv->chip_info->channels; + + ret = adi_axi_adc_config_dma_buffer(&pdev->dev, indio_dev); + if (ret) + return ret; + + ret = adi_axi_adc_setup_channels(&pdev->dev, st); + if (ret) + return ret; + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return ret; + + 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), + ADI_AXI_PCORE_VER_PATCH(ver)); + + return 0; +} + +/* 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 */ } +}; +MODULE_DEVICE_TABLE(of, adi_axi_adc_of_match); + +static struct platform_driver adi_axi_adc_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = adi_axi_adc_of_match, + }, + .probe = adi_axi_adc_probe, +}; +module_platform_driver(adi_axi_adc_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ad67971202381c9b7289b018151f0b12c0ee5314 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 24 Mar 2020 15:46:35 +0200 Subject: iio: adc: ad9467: add support AD9467 ADC The AD9467 is a 16-bit, monolithic, IF sampling analog-to-digital converter (ADC). It is optimized for high performanceover wide bandwidths and ease of use. The product operates at a 250 MSPS conversion rate and is designed for wireless receivers, instrumentation, and test equipment that require a high dynamic range. The ADC requires 1.8 V and 3.3 V power supplies and a low voltage differential input clock for full performance operation. No external reference or driver components are required for many applications. Data outputs are LVDS compatible (ANSI-644 compatible) and include the means to reduce the overall current needed for short trace distances. Since the chip can operate at such high sample-rates (much higher than classical interfaces), it requires that a DMA controller be used to interface directly to the chip and push data into memory. Typically, the AXI ADC IP core is used to interface with it. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/AD9467.pdf Signed-off-by: Lars-Peter Clausen Signed-off-by: Michael Hennerich Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 15 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad9467.c | 422 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 438 insertions(+) create mode 100644 drivers/iio/adc/ad9467.c (limited to 'drivers/iio/adc') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 445070abf376..a0796510f9d4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -246,6 +246,21 @@ config AD799X To compile this driver as a module, choose M here: the module will be called ad799x. +config AD9467 + tristate "Analog Devices AD9467 High Speed ADC driver" + depends on SPI + select ADI_AXI_ADC + help + Say yes here to build support for Analog Devices: + * AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter + + The driver requires the assistance of the AXI ADC IP core to operate, + since SPI is used for configuration only, while data has to be + streamed into memory via DMA. + + To compile this driver as a module, choose M here: the module will be + called ad9467. + config ADI_AXI_ADC tristate "Analog Devices Generic AXI ADC IP core driver" select IIO_BUFFER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 7c6594d049f9..59722770a654 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD7949) += ad7949.o obj-$(CONFIG_AD799X) += ad799x.o +obj-$(CONFIG_AD9467) += ad9467.o obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c new file mode 100644 index 000000000000..1e8fd83b9bc2 --- /dev/null +++ b/drivers/iio/adc/ad9467.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD9467 SPI ADC driver + * + * Copyright 2012-2020 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include + +#include + +/* + * ADI High-Speed ADC common spi interface registers + * See Application-Note AN-877: + * https://www.analog.com/media/en/technical-documentation/application-notes/AN-877.pdf + */ + +#define AN877_ADC_REG_CHIP_PORT_CONF 0x00 +#define AN877_ADC_REG_CHIP_ID 0x01 +#define AN877_ADC_REG_CHIP_GRADE 0x02 +#define AN877_ADC_REG_CHAN_INDEX 0x05 +#define AN877_ADC_REG_TRANSFER 0xFF +#define AN877_ADC_REG_MODES 0x08 +#define AN877_ADC_REG_TEST_IO 0x0D +#define AN877_ADC_REG_ADC_INPUT 0x0F +#define AN877_ADC_REG_OFFSET 0x10 +#define AN877_ADC_REG_OUTPUT_MODE 0x14 +#define AN877_ADC_REG_OUTPUT_ADJUST 0x15 +#define AN877_ADC_REG_OUTPUT_PHASE 0x16 +#define AN877_ADC_REG_OUTPUT_DELAY 0x17 +#define AN877_ADC_REG_VREF 0x18 +#define AN877_ADC_REG_ANALOG_INPUT 0x2C + +/* AN877_ADC_REG_TEST_IO */ +#define AN877_ADC_TESTMODE_OFF 0x0 +#define AN877_ADC_TESTMODE_MIDSCALE_SHORT 0x1 +#define AN877_ADC_TESTMODE_POS_FULLSCALE 0x2 +#define AN877_ADC_TESTMODE_NEG_FULLSCALE 0x3 +#define AN877_ADC_TESTMODE_ALT_CHECKERBOARD 0x4 +#define AN877_ADC_TESTMODE_PN23_SEQ 0x5 +#define AN877_ADC_TESTMODE_PN9_SEQ 0x6 +#define AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE 0x7 +#define AN877_ADC_TESTMODE_USER 0x8 +#define AN877_ADC_TESTMODE_BIT_TOGGLE 0x9 +#define AN877_ADC_TESTMODE_SYNC 0xA +#define AN877_ADC_TESTMODE_ONE_BIT_HIGH 0xB +#define AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY 0xC +#define AN877_ADC_TESTMODE_RAMP 0xF + +/* AN877_ADC_REG_TRANSFER */ +#define AN877_ADC_TRANSFER_SYNC 0x1 + +/* AN877_ADC_REG_OUTPUT_MODE */ +#define AN877_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0 +#define AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1 +#define AN877_ADC_OUTPUT_MODE_GRAY_CODE 0x2 + +/* AN877_ADC_REG_OUTPUT_PHASE */ +#define AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20 +#define AN877_ADC_INVERT_DCO_CLK 0x80 + +/* AN877_ADC_REG_OUTPUT_DELAY */ +#define AN877_ADC_DCO_DELAY_ENABLE 0x80 + +/* + * Analog Devices AD9467 16-Bit, 200/250 MSPS ADC + */ + +#define CHIPID_AD9467 0x50 +#define AD9467_DEF_OUTPUT_MODE 0x08 +#define AD9467_REG_VREF_MASK 0x0F + +enum { + ID_AD9467, +}; + +struct ad9467_state { + struct spi_device *spi; + struct clk *clk; + unsigned int output_mode; + + struct gpio_desc *pwrdown_gpio; + struct gpio_desc *reset_gpio; +}; + +static int ad9467_spi_read(struct spi_device *spi, unsigned int reg) +{ + unsigned char tbuf[2], rbuf[1]; + int ret; + + tbuf[0] = 0x80 | (reg >> 8); + tbuf[1] = reg & 0xFF; + + ret = spi_write_then_read(spi, + tbuf, ARRAY_SIZE(tbuf), + rbuf, ARRAY_SIZE(rbuf)); + + if (ret < 0) + return ret; + + return rbuf[0]; +} + +static int ad9467_spi_write(struct spi_device *spi, unsigned int reg, + unsigned int val) +{ + unsigned char buf[3]; + + buf[0] = reg >> 8; + buf[1] = reg & 0xFF; + buf[2] = val; + + return spi_write(spi, buf, ARRAY_SIZE(buf)); +} + +static int ad9467_reg_access(struct adi_axi_adc_conv *conv, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + struct spi_device *spi = st->spi; + int ret; + + if (readval == NULL) { + ret = ad9467_spi_write(spi, reg, writeval); + ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + return ret; + } + + ret = ad9467_spi_read(spi, reg); + if (ret < 0) + return ret; + *readval = ret; + + return 0; +} + +static const unsigned int ad9467_scale_table[][2] = { + {2000, 0}, {2100, 6}, {2200, 7}, + {2300, 8}, {2400, 9}, {2500, 10}, +}; + +static void __ad9467_get_scale(struct adi_axi_adc_conv *conv, int index, + unsigned int *val, unsigned int *val2) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + const struct iio_chan_spec *chan = &info->channels[0]; + unsigned int tmp; + + tmp = (info->scale_table[index][0] * 1000000ULL) >> + chan->scan_type.realbits; + *val = tmp / 1000000; + *val2 = tmp % 1000000; +} + +#define AD9467_CHAN(_chan, _si, _bits, _sign) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _si, \ + .scan_type = { \ + .sign = _sign, \ + .realbits = _bits, \ + .storagebits = 16, \ + }, \ +} + +static const struct iio_chan_spec ad9467_channels[] = { + AD9467_CHAN(0, 0, 16, 'S'), +}; + +static const struct adi_axi_adc_chip_info ad9467_chip_tbl[] = { + [ID_AD9467] = { + .id = CHIPID_AD9467, + .max_rate = 250000000UL, + .scale_table = ad9467_scale_table, + .num_scales = ARRAY_SIZE(ad9467_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), + }, +}; + +static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + unsigned int i, vref_val, vref_mask; + + vref_val = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF); + + switch (info->id) { + case CHIPID_AD9467: + vref_mask = AD9467_REG_VREF_MASK; + break; + default: + vref_mask = 0xFFFF; + break; + } + + vref_val &= vref_mask; + + for (i = 0; i < info->num_scales; i++) { + if (vref_val == info->scale_table[i][1]) + break; + } + + if (i == info->num_scales) + return -ERANGE; + + __ad9467_get_scale(conv, i, val, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + unsigned int scale_val[2]; + unsigned int i; + + if (val != 0) + return -EINVAL; + + for (i = 0; i < info->num_scales; i++) { + __ad9467_get_scale(conv, i, &scale_val[0], &scale_val[1]); + if (scale_val[0] != val || scale_val[1] != val2) + continue; + + ad9467_spi_write(st->spi, AN877_ADC_REG_VREF, + info->scale_table[i][1]); + ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + return 0; + } + + return -EINVAL; +} + +static int ad9467_read_raw(struct adi_axi_adc_conv *conv, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + + switch (m) { + case IIO_CHAN_INFO_SCALE: + return ad9467_get_scale(conv, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(st->clk); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad9467_write_raw(struct adi_axi_adc_conv *conv, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + long r_clk; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return ad9467_set_scale(conv, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + r_clk = clk_round_rate(st->clk, val); + if (r_clk < 0 || r_clk > info->max_rate) { + dev_warn(&st->spi->dev, + "Error setting ADC sample rate %ld", r_clk); + return -EINVAL; + } + + return clk_set_rate(st->clk, r_clk); + default: + return -EINVAL; + } +} + +static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) +{ + int ret; + + ret = ad9467_spi_write(spi, AN877_ADC_REG_OUTPUT_MODE, mode); + if (ret < 0) + return ret; + + return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv) +{ + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + + return ad9467_outputmode_set(st->spi, st->output_mode); +} + +static int ad9467_setup(struct ad9467_state *st, unsigned int chip_id) +{ + switch (chip_id) { + case CHIPID_AD9467: + st->output_mode = AD9467_DEF_OUTPUT_MODE | + AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; + return 0; + default: + return -EINVAL; + } +} + +static void ad9467_clk_disable(void *data) +{ + struct ad9467_state *st = data; + + clk_disable_unprepare(st->clk); +} + +static int ad9467_probe(struct spi_device *spi) +{ + const struct adi_axi_adc_chip_info *info; + struct adi_axi_adc_conv *conv; + struct ad9467_state *st; + unsigned int id; + int ret; + + info = of_device_get_match_data(&spi->dev); + if (!info) + return -ENODEV; + + conv = devm_adi_axi_adc_conv_register(&spi->dev, sizeof(*st)); + if (IS_ERR(conv)) + return PTR_ERR(conv); + + st = adi_axi_adc_conv_priv(conv); + st->spi = spi; + + st->clk = devm_clk_get(&spi->dev, "adc-clk"); + if (IS_ERR(st->clk)) + return PTR_ERR(st->clk); + + ret = clk_prepare_enable(st->clk); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad9467_clk_disable, st); + if (ret) + return ret; + + st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(st->pwrdown_gpio)) + return PTR_ERR(st->pwrdown_gpio); + + st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(st->reset_gpio)) + return PTR_ERR(st->reset_gpio); + + if (st->reset_gpio) { + udelay(1); + ret = gpiod_direction_output(st->reset_gpio, 1); + if (ret) + return ret; + mdelay(10); + } + + spi_set_drvdata(spi, st); + + conv->chip_info = info; + + id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID); + if (id != conv->chip_info->id) { + dev_err(&spi->dev, "Unrecognized CHIP_ID 0x%X\n", id); + return -ENODEV; + } + + conv->reg_access = ad9467_reg_access; + conv->write_raw = ad9467_write_raw; + conv->read_raw = ad9467_read_raw; + conv->preenable_setup = ad9467_preenable_setup; + + return ad9467_setup(st, id); +} + +static const struct of_device_id ad9467_of_match[] = { + { .compatible = "adi,ad9467", .data = &ad9467_chip_tbl[ID_AD9467], }, + {} +}; +MODULE_DEVICE_TABLE(of, ad9467_of_match); + +static struct spi_driver ad9467_driver = { + .driver = { + .name = "ad9467", + .of_match_table = ad9467_of_match, + }, + .probe = ad9467_probe, +}; +module_spi_driver(ad9467_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8a80a71d9020b0066cb035e7cedfc26334a2aa44 Mon Sep 17 00:00:00 2001 From: Alexandru Lazar Date: Fri, 3 Apr 2020 15:13:23 +0300 Subject: iio: adc: Add MAX1241 d