summaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/iio/adc
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--drivers/iio/adc/Kconfig1341
-rw-r--r--drivers/iio/adc/Makefile122
-rw-r--r--drivers/iio/adc/ab8500-gpadc.c1209
-rw-r--r--drivers/iio/adc/ad7091r-base.c466
-rw-r--r--drivers/iio/adc/ad7091r-base.h34
-rw-r--r--drivers/iio/adc/ad7091r5.c94
-rw-r--r--drivers/iio/adc/ad7124.c1036
-rw-r--r--drivers/iio/adc/ad7192.c1119
-rw-r--r--drivers/iio/adc/ad7266.c496
-rw-r--r--drivers/iio/adc/ad7280a.c1111
-rw-r--r--drivers/iio/adc/ad7291.c563
-rw-r--r--drivers/iio/adc/ad7292.c350
-rw-r--r--drivers/iio/adc/ad7298.c374
-rw-r--r--drivers/iio/adc/ad7476.c464
-rw-r--r--drivers/iio/adc/ad7606.c735
-rw-r--r--drivers/iio/adc/ad7606.h172
-rw-r--r--drivers/iio/adc/ad7606_par.c105
-rw-r--r--drivers/iio/adc/ad7606_spi.c365
-rw-r--r--drivers/iio/adc/ad7766.c315
-rw-r--r--drivers/iio/adc/ad7768-1.c677
-rw-r--r--drivers/iio/adc/ad7780.c378
-rw-r--r--drivers/iio/adc/ad7791.c477
-rw-r--r--drivers/iio/adc/ad7793.c870
-rw-r--r--drivers/iio/adc/ad7887.c348
-rw-r--r--drivers/iio/adc/ad7923.c400
-rw-r--r--drivers/iio/adc/ad7949.c442
-rw-r--r--drivers/iio/adc/ad799x.c971
-rw-r--r--drivers/iio/adc/ad9467.c521
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c679
-rw-r--r--drivers/iio/adc/adi-axi-adc.c429
-rw-r--r--drivers/iio/adc/aspeed_adc.c728
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c2659
-rw-r--r--drivers/iio/adc/at91_adc.c1398
-rw-r--r--drivers/iio/adc/axp20x_adc.c777
-rw-r--r--drivers/iio/adc/axp288_adc.c314
-rw-r--r--drivers/iio/adc/bcm_iproc_adc.c627
-rw-r--r--drivers/iio/adc/berlin2-adc.c371
-rw-r--r--drivers/iio/adc/cc10001_adc.c439
-rw-r--r--drivers/iio/adc/cpcap-adc.c1031
-rw-r--r--drivers/iio/adc/da9150-gpadc.c375
-rw-r--r--drivers/iio/adc/dln2-adc.c714
-rw-r--r--drivers/iio/adc/envelope-detector.c408
-rw-r--r--drivers/iio/adc/ep93xx_adc.c242
-rw-r--r--drivers/iio/adc/exynos_adc.c1025
-rw-r--r--drivers/iio/adc/fsl-imx25-gcq.c422
-rw-r--r--drivers/iio/adc/hi8435.c549
-rw-r--r--drivers/iio/adc/hx711.c622
-rw-r--r--drivers/iio/adc/imx7d_adc.c559
-rw-r--r--drivers/iio/adc/imx8qxp-adc.c503
-rw-r--r--drivers/iio/adc/ina2xx-adc.c1104
-rw-r--r--drivers/iio/adc/ingenic-adc.c923
-rw-r--r--drivers/iio/adc/intel_mrfld_adc.c242
-rw-r--r--drivers/iio/adc/lp8788_adc.c227
-rw-r--r--drivers/iio/adc/lpc18xx_adc.c206
-rw-r--r--drivers/iio/adc/lpc32xx_adc.c231
-rw-r--r--drivers/iio/adc/ltc2471.c157
-rw-r--r--drivers/iio/adc/ltc2485.c143
-rw-r--r--drivers/iio/adc/ltc2496.c113
-rw-r--r--drivers/iio/adc/ltc2497-core.c246
-rw-r--r--drivers/iio/adc/ltc2497.c174
-rw-r--r--drivers/iio/adc/ltc2497.h24
-rw-r--r--drivers/iio/adc/max1027.c649
-rw-r--r--drivers/iio/adc/max11100.c163
-rw-r--r--drivers/iio/adc/max1118.c288
-rw-r--r--drivers/iio/adc/max11205.c183
-rw-r--r--drivers/iio/adc/max1241.c223
-rw-r--r--drivers/iio/adc/max1363.c1738
-rw-r--r--drivers/iio/adc/max9611.c566
-rw-r--r--drivers/iio/adc/mcp320x.c530
-rw-r--r--drivers/iio/adc/mcp3422.c427
-rw-r--r--drivers/iio/adc/mcp3911.c554
-rw-r--r--drivers/iio/adc/men_z188_adc.c176
-rw-r--r--drivers/iio/adc/meson_saradc.c1315
-rw-r--r--drivers/iio/adc/mp2629_adc.c209
-rw-r--r--drivers/iio/adc/mt6360-adc.c373
-rw-r--r--drivers/iio/adc/mt6577_auxadc.c361
-rw-r--r--drivers/iio/adc/mxs-lradc-adc.c835
-rw-r--r--drivers/iio/adc/nau7802.c560
-rw-r--r--drivers/iio/adc/npcm_adc.c353
-rw-r--r--drivers/iio/adc/palmas_gpadc.c849
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c1028
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c942
-rw-r--r--drivers/iio/adc/qcom-spmi-iadc.c585
-rw-r--r--drivers/iio/adc/qcom-spmi-rradc.c1022
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c937
-rw-r--r--drivers/iio/adc/qcom-vadc-common.c772
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c618
-rw-r--r--drivers/iio/adc/rn5t618-adc.c262
-rw-r--r--drivers/iio/adc/rockchip_saradc.c534
-rw-r--r--drivers/iio/adc/rtq6056.c661
-rw-r--r--drivers/iio/adc/rzg2l_adc.c602
-rw-r--r--drivers/iio/adc/sc27xx_adc.c965
-rw-r--r--drivers/iio/adc/sd_adc_modulator.c67
-rw-r--r--drivers/iio/adc/spear_adc.c401
-rw-r--r--drivers/iio/adc/stm32-adc-core.c905
-rw-r--r--drivers/iio/adc/stm32-adc-core.h231
-rw-r--r--drivers/iio/adc/stm32-adc.c2485
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c1685
-rw-r--r--drivers/iio/adc/stm32-dfsdm-core.c454
-rw-r--r--drivers/iio/adc/stm32-dfsdm.h326
-rw-r--r--drivers/iio/adc/stmpe-adc.c367
-rw-r--r--drivers/iio/adc/sun4i-gpadc-iio.c714
-rw-r--r--drivers/iio/adc/ti-adc081c.c245
-rw-r--r--drivers/iio/adc/ti-adc0832.c338
-rw-r--r--drivers/iio/adc/ti-adc084s021.c269
-rw-r--r--drivers/iio/adc/ti-adc108s102.c323
-rw-r--r--drivers/iio/adc/ti-adc12138.c548
-rw-r--r--drivers/iio/adc/ti-adc128s052.c228
-rw-r--r--drivers/iio/adc/ti-adc161s626.c256
-rw-r--r--drivers/iio/adc/ti-ads1015.c1207
-rw-r--r--drivers/iio/adc/ti-ads124s08.c377
-rw-r--r--drivers/iio/adc/ti-ads131e08.c940
-rw-r--r--drivers/iio/adc/ti-ads7950.c725
-rw-r--r--drivers/iio/adc/ti-ads8344.c193
-rw-r--r--drivers/iio/adc/ti-ads8688.c521
-rw-r--r--drivers/iio/adc/ti-tlc4541.c264
-rw-r--r--drivers/iio/adc/ti-tsc2046.c912
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c759
-rw-r--r--drivers/iio/adc/twl4030-madc.c931
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c1025
-rw-r--r--drivers/iio/adc/vf610_adc.c969
-rw-r--r--drivers/iio/adc/viperboard_adc.c150
-rw-r--r--drivers/iio/adc/xilinx-ams.c1440
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c1457
-rw-r--r--drivers/iio/adc/xilinx-xadc-events.c244
-rw-r--r--drivers/iio/adc/xilinx-xadc.h214
126 files changed, 75666 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
new file mode 100644
index 000000000..791612ca6
--- /dev/null
+++ b/drivers/iio/adc/Kconfig
@@ -0,0 +1,1341 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ADC drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Analog to digital converters"
+
+config AB8500_GPADC
+ bool "ST-Ericsson AB8500 GPADC driver"
+ depends on AB8500_CORE && REGULATOR_AB8500
+ default y
+ help
+ AB8500 Analog Baseband, mixed signal integrated circuit GPADC
+ (General Purpose Analog to Digital Converter) driver used to monitor
+ internal voltages, convert accessory and battery, AC (charger, mains)
+ and USB voltages integral to the U8500 platform.
+
+config AD_SIGMA_DELTA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config AD7091R5
+ tristate "Analog Devices AD7091R5 ADC Driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to build support for Analog Devices AD7091R-5 ADC.
+
+config AD7124
+ tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
+ depends on SPI_MASTER
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7124-4 and AD7124-8
+ SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7124.
+
+config AD7192
+ tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7190,
+ AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC).
+ 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 ad7192.
+
+config AD7266
+ tristate "Analog Devices AD7265/AD7266 ADC driver"
+ depends on SPI_MASTER
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7265 and AD7266
+ ADCs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7266.
+
+config AD7280
+ tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
+ depends on SPI
+ select CRC8
+ help
+ Say yes here to build support for Analog Devices AD7280A
+ Lithium Ion Battery Monitoring System.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7280a
+
+config AD7291
+ tristate "Analog Devices AD7291 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Analog Devices AD7291
+ 8 Channel ADC with temperature sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7291.
+
+config AD7292
+ tristate "Analog Devices AD7292 ADC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD7292
+ 8 Channel ADC with temperature sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7292.
+
+config AD7298
+ tristate "Analog Devices AD7298 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7298
+ 8 Channel ADC with temperature sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7298.
+
+config AD7476
+ tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD and TI"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the following SPI analog to
+ digital converters (ADCs):
+ Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475,
+ AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910,
+ AD7920.
+ Texas Instruments: ADS7866, ADS7867, ADS7868.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7476.
+
+config AD7606
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config AD7606_IFACE_PARALLEL
+ tristate "Analog Devices AD7606 ADC driver with parallel interface support"
+ depends on HAS_IOMEM
+ select AD7606
+ help
+ Say yes here to build parallel interface support for Analog Devices:
+ ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_parallel.
+
+config AD7606_IFACE_SPI
+ tristate "Analog Devices AD7606 ADC driver with spi interface support"
+ depends on SPI
+ select AD7606
+ help
+ Say yes here to build spi interface support for Analog Devices:
+ ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_spi.
+
+config AD7766
+ tristate "Analog Devices AD7766/AD7767 ADC driver"
+ depends on SPI_MASTER
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7766, AD7766-1,
+ AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7766.
+
+config AD7768_1
+ tristate "Analog Devices AD7768-1 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7768-1 SPI
+ simultaneously sampling sigma-delta analog to digital converter (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7768-1.
+
+config AD7780
+ tristate "Analog Devices AD7780 and similar ADCs driver"
+ depends on SPI
+ depends on GPIOLIB || COMPILE_TEST
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7170, AD7171,
+ AD7780 and AD7781 SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7780.
+
+config AD7791
+ tristate "Analog Devices AD7791 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
+ AD7790 and AD7791 SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7791.
+
+config AD7793
+ tristate "Analog Devices AD7793 and similar ADCs driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7785, AD7792, AD7793,
+ AD7794 and AD7795 SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called AD7793.
+
+config AD7887
+ tristate "Analog Devices AD7887 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices
+ AD7887 SPI analog to digital converter (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7887.
+
+config AD7923
+ tristate "Analog Devices AD7923 and similar ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices
+ AD7904, AD7914, AD7923, AD7924 4 Channel ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7923.
+
+config AD7949
+ tristate "Analog Devices AD7949 and similar ADCs driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices
+ AD7949, AD7682, AD7689 8 Channel ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7949.
+
+config AD799X
+ tristate "Analog Devices AD799x ADC driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices:
+ ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998
+ i2c analog to digital converters (ADC). Provides direct access
+ via sysfs.
+
+ 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
+ depends on 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
+ select IIO_BUFFER_HW_CONSUMER
+ select IIO_BUFFER_DMAENGINE
+ depends on HAS_IOMEM
+ depends on OF
+ 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
+ depends on COMMON_CLK
+ help
+ If you say yes here you get support for the ADC included in Aspeed
+ BMC SoCs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called aspeed_adc.
+
+config AT91_ADC
+ tristate "Atmel AT91 ADC"
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on INPUT && SYSFS && OF
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Atmel AT91 ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called at91_adc.
+
+config AT91_SAMA5D2_ADC
+ tristate "Atmel AT91 SAMA5D2 ADC"
+ depends on ARCH_AT91 || COMPILE_TEST
+ depends on HAS_IOMEM
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Atmel SAMA5D2 ADC which is
+ available on SAMA5D2 SoC family.
+
+ To compile this driver as a module, choose M here: the module will be
+ called at91-sama5d2_adc.
+
+config AXP20X_ADC
+ tristate "X-Powers AXP20X and AXP22X ADC driver"
+ depends on MFD_AXP20X
+ help
+ Say yes here to have support for X-Powers power management IC (PMIC)
+ AXP20X and AXP22X ADC devices.
+
+ To compile this driver as a module, choose M here: the module will be
+ called axp20x_adc.
+
+config AXP288_ADC
+ tristate "X-Powers AXP288 ADC driver"
+ depends on MFD_AXP20X
+ help
+ Say yes here to have support for X-Powers power management IC (PMIC) ADC
+ device. Depending on platform configuration, this general purpose ADC can
+ be used for sampling sensors such as thermal resistors.
+
+ To compile this driver as a module, choose M here: the module will be
+ called axp288_adc.
+
+config BCM_IPROC_ADC
+ tristate "Broadcom IPROC ADC driver"
+ depends on (ARCH_BCM_IPROC && OF) || COMPILE_TEST
+ depends on MFD_SYSCON
+ default ARCH_BCM_CYGNUS
+ help
+ Say Y here if you want to add support for the Broadcom static
+ ADC driver.
+
+ Broadcom iProc ADC driver. Broadcom iProc ADC controller has 8
+ channels. The driver allows the user to read voltage values.
+
+config BERLIN2_ADC
+ tristate "Marvell Berlin2 ADC driver"
+ depends on ARCH_BERLIN || COMPILE_TEST
+ help
+ Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for
+ temperature measurement.
+
+config CC10001_ADC
+ tristate "Cosmic Circuits 10001 ADC driver"
+ depends on HAS_IOMEM && HAVE_CLK && REGULATOR
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Cosmic Circuits 10001 ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called cc10001_adc.
+
+config CPCAP_ADC
+ tristate "Motorola CPCAP PMIC ADC driver"
+ depends on MFD_CPCAP
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Motorola CPCAP PMIC ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called cpcap-adc.
+
+config DA9150_GPADC
+ tristate "Dialog DA9150 GPADC driver support"
+ depends on MFD_DA9150
+ help
+ Say yes here to build support for Dialog DA9150 GPADC.
+
+ This driver can also be built as a module. If chosen, the module name
+ will be da9150-gpadc.
+
+ To compile this driver as a module, choose M here: the module will be
+ called berlin2-adc.
+
+config DLN2_ADC
+ tristate "Diolan DLN-2 ADC driver support"
+ depends on MFD_DLN2
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Diolan DLN-2 ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called adc_dln2.
+
+config ENVELOPE_DETECTOR
+ tristate "Envelope detector using a DAC and a comparator"
+ help
+ Say yes here to build support for an envelope detector using a DAC
+ and a comparator.
+
+ To compile this driver as a module, choose M here: the module will be
+ called envelope-detector.
+
+config EP93XX_ADC
+ tristate "Cirrus Logic EP93XX ADC driver"
+ depends on ARCH_EP93XX
+ help
+ Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic.
+ It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this
+ case driver will reduce its CPU usage by 90% in some use cases.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ep93xx_adc.
+
+config EXYNOS_ADC
+ tristate "Exynos ADC driver support"
+ depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 || (OF && COMPILE_TEST)
+ depends on HAS_IOMEM
+ help
+ Driver for the ADC block found in the Samsung S3C (S3C2410, S3C2416,
+ S3C2440, S3C2443, S3C6410), S5Pv210 and Exynos SoCs.
+ Choose Y here only if you build for such Samsung SoC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called exynos_adc.
+
+config MXS_LRADC_ADC
+ tristate "Freescale i.MX23/i.MX28 LRADC ADC"
+ depends on MFD_MXS_LRADC
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the ADC functions of the
+ i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings,
+ battery voltage measurement, and die temperature measurement.
+
+ This driver can also be built as a module. If so, the module will be
+ called mxs-lradc-adc.
+
+config FSL_MX25_ADC
+ tristate "Freescale MX25 ADC driver"
+ depends on MFD_MX25_TSADC
+ help
+ Generic Conversion Queue driver used for general purpose ADC in the
+ MX25. This driver supports single measurements using the MX25 ADC.
+
+config HI8435
+ tristate "Holt Integrated Circuits HI-8435 threshold detector"
+ select IIO_TRIGGERED_EVENT
+ depends on SPI
+ help
+ If you say yes here you get support for Holt Integrated Circuits
+ HI-8435 chip.
+
+ This driver can also be built as a module. If so, the module will be
+ called hi8435.
+
+config HX711
+ tristate "AVIA HX711 ADC for weight cells"
+ depends on GPIOLIB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for AVIA HX711 ADC which is used
+ for weigh cells
+
+ This driver uses two GPIOs, one acts as the clock and controls the
+ channel selection and gain, the other one is used for the measurement
+ data
+
+ Currently the raw value is read from the chip and delivered.
+ To get an actual weight one needs to subtract the
+ zero offset and multiply by a scale factor.
+ This should be done in userspace.
+
+ This driver can also be built as a module. If so, the module will be
+ called hx711.
+
+config INA2XX_ADC
+ tristate "Texas Instruments INA2xx Power Monitors IIO driver"
+ depends on I2C && !SENSORS_INA2XX
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to build support for TI INA2xx family of Power Monitors.
+ This driver is mutually exclusive with the HWMON version.
+
+config INGENIC_ADC
+ tristate "Ingenic JZ47xx SoCs ADC driver"
+ depends on MIPS || COMPILE_TEST
+ select IIO_BUFFER
+ help
+ Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit.
+
+ This driver can also be built as a module. If so, the module will be
+ called ingenic_adc.
+
+config INTEL_MRFLD_ADC
+ tristate "Intel Merrifield Basin Cove ADC driver"
+ depends on INTEL_SOC_PMIC_MRFLD
+ help
+ Say yes here to have support for Basin Cove power management IC (PMIC) ADC
+ device. Depending on platform configuration, this general purpose ADC can
+ be used for sampling sensors such as thermal resistors.
+
+ To compile this driver as a module, choose M here: the module will be
+ called intel_mrfld_adc.
+
+config IMX7D_ADC
+ tristate "Freescale IMX7D ADC driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for IMX7D ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called imx7d_adc.
+
+config IMX8QXP_ADC
+ tristate "NXP IMX8QXP ADC driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for IMX8QXP ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called imx8qxp-adc.
+
+config LP8788_ADC
+ tristate "LP8788 ADC driver"
+ depends on MFD_LP8788
+ help
+ Say yes here to build support for TI LP8788 ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called lp8788_adc.
+
+config LPC18XX_ADC
+ tristate "NXP LPC18xx ADC driver"
+ depends on ARCH_LPC18XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for NXP LPC18XX ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called lpc18xx_adc.
+
+config LPC32XX_ADC
+ tristate "NXP LPC32XX ADC"
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for the integrated ADC inside the
+ LPC32XX SoC. Note that this feature uses the same hardware as the
+ touchscreen driver, so you should either select only one of the two
+ drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case,
+ activate only one via device tree selection. Provides direct access
+ via sysfs.
+
+config LTC2471
+ tristate "Linear Technology LTC2471 and LTC2473 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Linear Technology LTC2471 and
+ LTC2473 16-bit I2C ADC.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2471.
+
+config LTC2485
+ tristate "Linear Technology LTC2485 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Linear Technology LTC2485 ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ltc2485.
+
+config LTC2496
+ tristate "Linear Technology LTC2496 ADC driver"
+ depends on SPI
+ help
+ Say yes here to build support for Linear Technology LTC2496
+ 16-Bit 8-/16-Channel Delta Sigma ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ltc2496.
+
+config LTC2497
+ tristate "Linear Technology LTC2497 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Linear Technology LTC2497
+ 16-Bit 8-/16-Channel Delta Sigma ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ltc2497.
+
+config MAX1027
+ tristate "Maxim max1027 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Maxim SPI {10,12}-bit ADC models:
+ max1027, max1029, max1031, max1227, max1229 and max1231.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max1027.
+
+config MAX11100
+ tristate "Maxim max11100 ADC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Maxim max11100 SPI ADC
+
+ To compile this driver as a module, choose M here: the module will be
+ called max11100.
+
+config MAX1118
+ tristate "Maxim max1117/max1118/max1119 ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Maxim max1117/max1118/max1119
+ 8-bit, dual-channel ADCs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max1118.
+
+config MAX11205
+ tristate "Maxim max11205 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+ help
+ Say yes here to build support for Maxim max11205 16-bit, single-channel
+ ultra-low power delta-sigma ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max11205.
+
+config MAX1241
+ tristate "Maxim max1241 ADC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Maxim max1241 12-bit, single-channel
+ ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max1241.
+
+config MAX1363
+ tristate "Maxim max1363 ADC driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for many Maxim i2c analog to digital
+ converters (ADC). (max1361, max1362, max1363, max1364, max1036,
+ max1037, max1038, max1039, max1136, max1136, max1137, max1138,
+ max1139, max1236, max1237, max11238, max1239, max11600, max11601,
+ max11602, max11603, max11604, max11605, max11606, max11607,
+ max11608, max11609, max11610, max11611, max11612, max11613,
+ max11614, max11615, max11616, max11617, max11644, max11645,
+ max11646, max11647) Provides direct access via sysfs and buffered
+ data via the iio dev interface.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max1363.
+
+config MAX9611
+ tristate "Maxim max9611/max9612 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Maxim max9611/max9612 current sense
+ amplifier with 12-bits ADC interface.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max9611.
+
+config MCP320X
+ tristate "Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3"
+ depends on SPI
+ help
+ Say yes here to build support for Microchip Technology's
+ MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204,
+ MCP3208, MCP3301, MCP3550, MCP3551 and MCP3553 analog to digital
+ converters.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp320x.
+
+config MCP3422
+ tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver"
+ depends on I2C
+ help
+ Say yes here to build support for Microchip Technology's MCP3421
+ MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428
+ analog to digital converters.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp3422.
+
+config MCP3911
+ tristate "Microchip Technology MCP3911 driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Microchip Technology's MCP3911
+ analog to digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp3911.
+
+config MEDIATEK_MT6360_ADC
+ tristate "Mediatek MT6360 ADC driver"
+ depends on MFD_MT6360
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here to enable MT6360 ADC support.
+ Integrated for System Monitoring includes
+ is used in smartphones and tablets and supports a 11 channel
+ general purpose ADC.
+
+config MEDIATEK_MT6577_AUXADC
+ tristate "MediaTek AUXADC driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to enable support for MediaTek mt65xx AUXADC.
+
+ The driver supports immediate mode operation to read from one of sixteen
+ channels (external or internal).
+
+ This driver can also be built as a module. If so, the module will be
+ called mt6577_auxadc.
+
+config MEN_Z188_ADC
+ tristate "MEN 16z188 ADC IP Core support"
+ depends on MCB
+ help
+ Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB
+ carrier.
+
+ This driver can also be built as a module. If so, the module will be
+ called men_z188_adc.
+
+config MESON_SARADC
+ tristate "Amlogic Meson SAR ADC driver"
+ default ARCH_MESON
+ depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST)
+ select REGMAP_MMIO
+ help
+ Say yes here to build support for the SAR ADC found in Amlogic Meson
+ SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called meson_saradc.
+
+config MP2629_ADC
+ tristate "Monolithic MP2629 ADC driver"
+ depends on MFD_MP2629
+ help
+ Say yes to have support for battery charger IC MP2629 ADC device
+ accessed over I2C.
+
+ This driver provides ADC conversion of system, input power supply
+ and battery voltage & current information.
+
+config NAU7802
+ tristate "Nuvoton NAU7802 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Nuvoton NAU7802 ADC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called nau7802.
+
+config NPCM_ADC
+ tristate "Nuvoton NPCM ADC driver"
+ depends on ARCH_NPCM || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for Nuvoton NPCM ADC.
+
+ This driver can also be built as a module. If so, the module
+ will be called npcm_adc.
+
+config PALMAS_GPADC
+ tristate "TI Palmas General Purpose ADC"
+ depends on MFD_PALMAS
+ help
+ Palmas series pmic chip by Texas Instruments (twl6035/6037)
+ is used in smartphones and tablets and supports a 16 channel
+ general purpose ADC.
+
+config QCOM_VADC_COMMON
+ tristate
+
+config QCOM_PM8XXX_XOADC
+ tristate "Qualcomm SSBI PM8xxx PMIC XOADCs"
+ depends on MFD_PM8XXX
+ select QCOM_VADC_COMMON
+ help
+ ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs
+ using SSBI transport: PM8018, PM8038, PM8058, PM8921.
+
+ To compile this driver as a module, choose M here: the module
+ will be called qcom-pm8xxx-xoadc.
+
+config QCOM_SPMI_RRADC
+ tristate "Qualcomm SPMI RRADC"
+ depends on MFD_SPMI_PMIC
+ help
+ This is for the PMIC Round Robin ADC driver.
+
+ This driver exposes the battery ID resistor, battery thermal, PMIC die
+ temperature, charger USB in and DC in voltage and current.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-qpmi-rradc.
+
+config QCOM_SPMI_IADC
+ tristate "Qualcomm SPMI PMIC current ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ This is the IIO Current ADC driver for Qualcomm QPNP IADC Chip.
+
+ The driver supports single mode operation to read from one of two
+ channels (external or internal). Hardware have additional
+ channels internally used for gain and offset calibration.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-iadc.
+
+config QCOM_SPMI_VADC
+ tristate "Qualcomm SPMI PMIC voltage ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ select QCOM_VADC_COMMON
+ help
+ This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
+
+ The driver supports multiple channels read. The VADC is a 15-bit
+ sigma-delta ADC. Some of the channels are internally used for
+ calibration.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-vadc.
+
+config QCOM_SPMI_ADC5
+ tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ select QCOM_VADC_COMMON
+ help
+ This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc.
+
+ The driver supports multiple channels read. The ADC is a 16-bit
+ sigma-delta ADC. The hardware supports calibrated results for
+ conversion requests and clients include reading voltage phone
+ power, on board system thermistors connected to the PMIC ADC,
+ PMIC die temperature, charger temperature, battery current, USB voltage
+ input, voltage signals connected to supported PMIC GPIO inputs. The
+ hardware supports internal pull-up for thermistors and can choose between
+ a 100k, 30k and 400k pull up using the ADC channels.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-adc5.
+
+config RCAR_GYRO_ADC
+ tristate "Renesas R-Car GyroADC driver"
+ depends on ARCH_RCAR_GEN2 || COMPILE_TEST
+ help
+ Say yes here to build support for the GyroADC found in Renesas
+ R-Car Gen2 SoCs. This block is a simple SPI offload engine for
+ reading data out of attached compatible ADCs in a round-robin
+ fashion. Up to 4 or 8 ADC channels are supported by this block,
+ depending on which ADCs are attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rcar-gyroadc.
+
+config RN5T618_ADC
+ tristate "ADC for the RN5T618/RC5T619 family of chips"
+ depends on MFD_RN5T618
+ help
+ Say yes here to build support for the integrated ADC inside the
+ RN5T618/619 series PMICs:
+
+ This driver can also be built as a module. If so, the module
+ will be called rn5t618-adc.
+
+config ROCKCHIP_SARADC
+ tristate "Rockchip SARADC driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on RESET_CONTROLLER
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the SARADC found in SoCs from
+ Rockchip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rockchip_saradc.
+
+config RICHTEK_RTQ6056
+ tristate "Richtek RTQ6056 Current and Power Monitor ADC"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to enable RQT6056 ADC support.
+ RTQ6056 is a high accuracy current-sense monitor with I2C and SMBus
+ compatible interface, and the device provides full information for
+ system by reading out the load current and power.
+
+ This driver can also be built as a module. If so, the module will be
+ called rtq6056.
+
+config RZG2L_ADC
+ tristate "Renesas RZ/G2L ADC driver"
+ depends on ARCH_RZG2L || COMPILE_TEST
+ help
+ Say yes here to build support for the ADC found in Renesas
+ RZ/G2L family.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rzg2l_adc.
+
+config SC27XX_ADC
+ tristate "Spreadtrum SC27xx series PMICs ADC"
+ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ help
+ Say yes here to build support for the integrated ADC inside the
+ Spreadtrum SC27xx series PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called sc27xx_adc.
+
+config SPEAR_ADC
+ tristate "ST SPEAr ADC"
+ depends on PLAT_SPEAR || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for the integrated ADC inside the
+ ST SPEAr SoC. Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spear_adc.
+
+config SD_ADC_MODULATOR
+ tristate "Generic sigma delta modulator"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Select this option to enables sigma delta modulator. This driver can
+ support generic sigma delta modulators.
+
+ This driver can also be built as a module. If so, the module
+ will be called sd_adc_modulator.
+
+config STM32_ADC_CORE
+ tristate "STMicroelectronics STM32 adc core"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on OF
+ depends on REGULATOR
+ depends on HAS_IOMEM
+ select IIO_BUFFER
+ select MFD_STM32_TIMERS
+ select IIO_STM32_TIMER_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Select this option to enable the core driver for STMicroelectronics
+ STM32 analog-to-digital converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc-core.
+
+config STM32_ADC
+ tristate "STMicroelectronics STM32 adc"
+ depends on STM32_ADC_CORE
+ help
+ Say yes here to build support for STMicroelectronics stm32 Analog
+ to Digital Converter (ADC).
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc.
+
+config STM32_DFSDM_CORE
+ tristate "STMicroelectronics STM32 DFSDM core"
+ depends on (ARCH_STM32 && OF) || COMPILE_TEST
+ select REGMAP
+ select REGMAP_MMIO
+ help
+ Select this option to enable the driver for STMicroelectronics
+ STM32 digital filter for sigma delta converter.
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-dfsdm-core.
+
+config STM32_DFSDM_ADC
+ tristate "STMicroelectronics STM32 dfsdm adc"
+ depends on (ARCH_STM32 && OF) || COMPILE_TEST
+ select STM32_DFSDM_CORE
+ select REGMAP_MMIO
+ select IIO_BUFFER
+ select IIO_BUFFER_HW_CONSUMER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Select this option to support ADCSigma delta modulator for
+ STMicroelectronics STM32 digital filter for sigma delta converter.
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-dfsdm-adc.
+
+config STMPE_ADC
+ tristate "STMicroelectronics STMPE ADC driver"
+ depends on OF && MFD_STMPE
+ help
+ Say yes here to build support for ST Microelectronics STMPE
+ built-in ADC block (stmpe811).
+
+config SUN4I_GPADC
+ tristate "Support for the Allwinner SoCs GPADC"
+ depends on IIO
+ depends on MFD_SUN4I_GPADC || MACH_SUN8I
+ depends on THERMAL || !THERMAL_OF
+ select REGMAP_IRQ
+ help
+ Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
+ GPADC. This ADC provides 4 channels which can be used as an ADC or as
+ a touchscreen input and one channel for thermal sensor.
+
+ The thermal sensor slows down ADC readings and can be disabled by
+ disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
+ enabled by default since the SoC temperature is usually more critical
+ than ADC readings.
+
+ To compile this driver as a module, choose M here: the module will be
+ called sun4i-gpadc-iio.
+
+config TI_ADC081C
+ tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADC081C,
+ ADC101C and ADC121C ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc081c.
+
+config TI_ADC0832
+ tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADC0831,
+ ADC0832, ADC0834, ADC0838 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc0832.
+
+config TI_ADC084S021
+ tristate "Texas Instruments ADC084S021"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADC084S021
+ chips.
+
+ 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
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Texas Instruments ADC108S102 and
+ ADC128S102 ADC.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ti-adc108s102.
+
+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.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc128s052.
+
+config TI_ADC161S626
+ tristate "Texas Instruments ADC161S626 1-channel differential ADC"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADC141S626,
+ and ADC161S626 chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc161s626.
+
+config TI_ADS1015
+ tristate "Texas Instruments ADS1015 ADC"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADS1015
+ ADC chip.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads1015.
+
+config TI_ADS7950
+ tristate "Texas Instruments ADS7950 ADC driver"
+ depends on SPI && GPIOLIB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Texas Instruments ADS7950, ADS7951,
+ ADS7952, ADS7953, ADS7954, ADS7955, ADS7956, ADS7957, ADS7958, ADS7959.
+ ADS7960, ADS7961.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ti-ads7950.
+
+config TI_ADS8344
+ tristate "Texas Instruments ADS8344"
+ depends on SPI
+ help
+ If you say yes here you get support for Texas Instruments ADS8344
+ ADC chips
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads8344.
+
+config TI_ADS8688
+ tristate "Texas Instruments ADS8688"
+ depends on SPI
+ help
+ If you say yes here you get support for Texas Instruments ADS8684 and
+ and ADS8688 ADC chips
+
+ 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
+ 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
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to build support for Texas Instruments ADC
+ driver which is also a MFD client.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ti_am335x_adc.
+
+config TI_TLC4541
+ tristate "Texas Instruments TLC4541 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Texas Instruments TLC4541 / TLC3541
+ ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-tlc4541.
+
+config TI_TSC2046
+ tristate "Texas Instruments TSC2046 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for ADC functionality of Texas
+ Instruments TSC2046 touch screen controller.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-tsc2046.
+
+config TWL4030_MADC
+ tristate "TWL4030 MADC (Monitoring A/D Converter)"
+ depends on TWL4030_CORE
+ help
+ This driver provides support for Triton TWL4030-MADC. The
+ driver supports both RT and SW conversion methods.
+
+ This driver can also be built as a module. If so, the module will be
+ called twl4030-madc.
+
+config TWL6030_GPADC
+ tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
+ depends on TWL4030_CORE
+ default n
+ help
+ Say yes here if you want support for the TWL6030/TWL6032 General
+ Purpose A/D Converter. This will add support for battery type
+ detection, battery voltage and temperature measurement, die
+ temperature measurement, system supply voltage, audio accessory,
+ USB ID detection.
+
+ This driver can also be built as a module. If so, the module will be
+ called twl6030-gpadc.
+
+config VF610_ADC
+ tristate "Freescale vf610 ADC driver"
+ depends on HAS_IOMEM
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to support for Vybrid board analog-to-digital converter.
+ Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
+
+ This driver can also be built as a module. If so, the module will be
+ called vf610_adc.
+
+config VIPERBOARD_ADC
+ tristate "Viperboard ADC support"
+ depends on MFD_VIPERBOARD && USB
+ help
+ Say yes here to access the ADC part of the Nano River
+ Technologies Viperboard.
+
+ To compile this driver as a module, choose M here: the module will be
+ called viperboard_adc.
+
+config XILINX_XADC
+ tristate "Xilinx XADC driver"
+ depends on HAS_IOMEM
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to have support for the Xilinx 7 Series XADC or
+ UltraScale/UltraScale+ System Management Wizard.
+
+ For the 7 Series the driver does support both the ZYNQ interface
+ to the XADC as well as the AXI-XADC interface.
+
+ The driver also support the Xilinx System Management Wizard IP core
+ that can be used to access the System Monitor ADC on the Xilinx
+ UltraScale and UltraScale+ FPGAs.
+
+ The driver can also be build as a module. If so, the module will be called
+ xilinx-xadc.
+
+config XILINX_AMS
+ tristate "Xilinx AMS driver"
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to have support for the Xilinx AMS for Ultrascale/Ultrascale+
+ System Monitor. With this you can measure and monitor the Voltages and
+ Temperature values on the SOC.
+
+ The driver supports Voltage and Temperature monitoring on Xilinx Ultrascale
+ devices.
+
+ The driver can also be built as a module. If so, the module will be called
+ xilinx-ams.
+
+endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
new file mode 100644
index 000000000..46caba7a0
--- /dev/null
+++ b/drivers/iio/adc/Makefile
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for IIO ADC drivers
+#
+
+# 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_AD7091R5) += ad7091r5.o ad7091r-base.o
+obj-$(CONFIG_AD7124) += ad7124.o
+obj-$(CONFIG_AD7192) += ad7192.o
+obj-$(CONFIG_AD7266) += ad7266.o
+obj-$(CONFIG_AD7280) += ad7280a.o
+obj-$(CONFIG_AD7291) += ad7291.o
+obj-$(CONFIG_AD7292) += ad7292.o
+obj-$(CONFIG_AD7298) += ad7298.o
+obj-$(CONFIG_AD7923) += ad7923.o
+obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
+obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
+obj-$(CONFIG_AD7606) += ad7606.o
+obj-$(CONFIG_AD7766) += ad7766.o
+obj-$(CONFIG_AD7768_1) += ad7768-1.o
+obj-$(CONFIG_AD7780) += ad7780.o
+obj-$(CONFIG_AD7791) += ad7791.o
+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
+obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
+obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
+obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
+obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
+obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
+obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
+obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_DLN2_ADC) += dln2-adc.o
+obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
+obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o
+obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
+obj-$(CONFIG_HI8435) += hi8435.o
+obj-$(CONFIG_HX711) += hx711.o
+obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
+obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
+obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
+obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
+obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o
+obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
+obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
+obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
+obj-$(CONFIG_LTC2471) += ltc2471.o
+obj-$(CONFIG_LTC2485) += ltc2485.o
+obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
+obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o
+obj-$(CONFIG_MAX1027) += max1027.o
+obj-$(CONFIG_MAX11100) += max11100.o
+obj-$(CONFIG_MAX1118) += max1118.o
+obj-$(CONFIG_MAX11205) += max11205.o
+obj-$(CONFIG_MAX1241) += max1241.o
+obj-$(CONFIG_MAX1363) += max1363.o
+obj-$(CONFIG_MAX9611) += max9611.o
+obj-$(CONFIG_MCP320X) += mcp320x.o
+obj-$(CONFIG_MCP3422) += mcp3422.o
+obj-$(CONFIG_MCP3911) += mcp3911.o
+obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
+obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
+obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
+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_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
+obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
+obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
+obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o
+obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
+obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
+obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
+obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
+obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
+obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o
+obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
+obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
+obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
+obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
+obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
+obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o
+obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
+obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
+obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
+obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
+obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o
+obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
+obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
+obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
+obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
+obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
+obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
+obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
+obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
+obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
+obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o
+obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
+obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
+obj-$(CONFIG_VF610_ADC) += vf610_adc.o
+obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
+xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
+obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
+obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
+obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c
new file mode 100644
index 000000000..4fa2126a3
--- /dev/null
+++ b/drivers/iio/adc/ab8500-gpadc.c
@@ -0,0 +1,1209 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>
+ * Author: Daniel Willerud <daniel.willerud@stericsson.com>
+ * Author: Johan Palsson <johan.palsson@stericsson.com>
+ * Author: M'boumba Cedric Madianga
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * AB8500 General Purpose ADC driver. The AB8500 uses reference voltages:
+ * VinVADC, and VADC relative to GND to do its job. It monitors main and backup
+ * battery voltages, AC (mains) voltage, USB cable voltage, as well as voltages
+ * representing the temperature of the chip die and battery, accessory
+ * detection by resistance measurements using relative voltages and GSM burst
+ * information.
+ *
+ * Some of the voltages are measured on external pins on the IC, such as
+ * battery temperature or "ADC aux" 1 and 2. Other voltages are internal rails
+ * from other parts of the ASIC such as main charger voltage, main and battery
+ * backup voltage or USB VBUS voltage. For this reason drivers for other
+ * parts of the system are required to obtain handles to the ADC to do work
+ * for them and the IIO driver provides arbitration among these consumers.
+ */
+#include <linux/init.h>
+#include <linux/bits.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/random.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+
+/* GPADC register offsets and bit definitions */
+
+#define AB8500_GPADC_CTRL1_REG 0x00
+/* GPADC control register 1 bits */
+#define AB8500_GPADC_CTRL1_DISABLE 0x00
+#define AB8500_GPADC_CTRL1_ENABLE BIT(0)
+#define AB8500_GPADC_CTRL1_TRIG_ENA BIT(1)
+#define AB8500_GPADC_CTRL1_START_SW_CONV BIT(2)
+#define AB8500_GPADC_CTRL1_BTEMP_PULL_UP BIT(3)
+/* 0 = use rising edge, 1 = use falling edge */
+#define AB8500_GPADC_CTRL1_TRIG_EDGE BIT(4)
+/* 0 = use VTVOUT, 1 = use VRTC as pull-up supply for battery temp NTC */
+#define AB8500_GPADC_CTRL1_PUPSUPSEL BIT(5)
+#define AB8500_GPADC_CTRL1_BUF_ENA BIT(6)
+#define AB8500_GPADC_CTRL1_ICHAR_ENA BIT(7)
+
+#define AB8500_GPADC_CTRL2_REG 0x01
+#define AB8500_GPADC_CTRL3_REG 0x02
+/*
+ * GPADC control register 2 and 3 bits
+ * the bit layout is the same for SW and HW conversion set-up
+ */
+#define AB8500_GPADC_CTRL2_AVG_1 0x00
+#define AB8500_GPADC_CTRL2_AVG_4 BIT(5)
+#define AB8500_GPADC_CTRL2_AVG_8 BIT(6)
+#define AB8500_GPADC_CTRL2_AVG_16 (BIT(5) | BIT(6))
+
+enum ab8500_gpadc_channel {
+ AB8500_GPADC_CHAN_UNUSED = 0x00,
+ AB8500_GPADC_CHAN_BAT_CTRL = 0x01,
+ AB8500_GPADC_CHAN_BAT_TEMP = 0x02,
+ /* This is not used on AB8505 */
+ AB8500_GPADC_CHAN_MAIN_CHARGER = 0x03,
+ AB8500_GPADC_CHAN_ACC_DET_1 = 0x04,
+ AB8500_GPADC_CHAN_ACC_DET_2 = 0x05,
+ AB8500_GPADC_CHAN_ADC_AUX_1 = 0x06,
+ AB8500_GPADC_CHAN_ADC_AUX_2 = 0x07,
+ AB8500_GPADC_CHAN_VBAT_A = 0x08,
+ AB8500_GPADC_CHAN_VBUS = 0x09,
+ AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT = 0x0a,
+ AB8500_GPADC_CHAN_USB_CHARGER_CURRENT = 0x0b,
+ AB8500_GPADC_CHAN_BACKUP_BAT = 0x0c,
+ /* Only on AB8505 */
+ AB8505_GPADC_CHAN_DIE_TEMP = 0x0d,
+ AB8500_GPADC_CHAN_ID = 0x0e,
+ AB8500_GPADC_CHAN_INTERNAL_TEST_1 = 0x0f,
+ AB8500_GPADC_CHAN_INTERNAL_TEST_2 = 0x10,
+ AB8500_GPADC_CHAN_INTERNAL_TEST_3 = 0x11,
+ /* FIXME: Applicable to all ASIC variants? */
+ AB8500_GPADC_CHAN_XTAL_TEMP = 0x12,
+ AB8500_GPADC_CHAN_VBAT_TRUE_MEAS = 0x13,
+ /* FIXME: Doesn't seem to work with pure AB8500 */
+ AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT = 0x1c,
+ AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT = 0x1d,
+ AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT = 0x1e,
+ AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT = 0x1f,
+ /*
+ * Virtual channel used only for ibat conversion to ampere.
+ * Battery current conversion (ibat) cannot be requested as a
+ * single conversion but it is always requested in combination
+ * with other input requests.
+ */
+ AB8500_GPADC_CHAN_IBAT_VIRTUAL = 0xFF,
+};
+
+#define AB8500_GPADC_AUTO_TIMER_REG 0x03
+
+#define AB8500_GPADC_STAT_REG 0x04
+#define AB8500_GPADC_STAT_BUSY BIT(0)
+
+#define AB8500_GPADC_MANDATAL_REG 0x05
+#define AB8500_GPADC_MANDATAH_REG 0x06
+#define AB8500_GPADC_AUTODATAL_REG 0x07
+#define AB8500_GPADC_AUTODATAH_REG 0x08
+#define AB8500_GPADC_MUX_CTRL_REG 0x09
+#define AB8540_GPADC_MANDATA2L_REG 0x09
+#define AB8540_GPADC_MANDATA2H_REG 0x0A
+#define AB8540_GPADC_APEAAX_REG 0x10
+#define AB8540_GPADC_APEAAT_REG 0x11
+#define AB8540_GPADC_APEAAM_REG 0x12
+#define AB8540_GPADC_APEAAH_REG 0x13
+#define AB8540_GPADC_APEAAL_REG 0x14
+
+/*
+ * OTP register offsets
+ * Bank : 0x15
+ */
+#define AB8500_GPADC_CAL_1 0x0F
+#define AB8500_GPADC_CAL_2 0x10
+#define AB8500_GPADC_CAL_3 0x11
+#define AB8500_GPADC_CAL_4 0x12
+#define AB8500_GPADC_CAL_5 0x13
+#define AB8500_GPADC_CAL_6 0x14
+#define AB8500_GPADC_CAL_7 0x15
+/* New calibration for 8540 */
+#define AB8540_GPADC_OTP4_REG_7 0x38
+#define AB8540_GPADC_OTP4_REG_6 0x39
+#define AB8540_GPADC_OTP4_REG_5 0x3A
+
+#define AB8540_GPADC_DIS_ZERO 0x00
+#define AB8540_GPADC_EN_VBIAS_XTAL_TEMP 0x02
+
+/* GPADC constants from AB8500 spec, UM0836 */
+#define AB8500_ADC_RESOLUTION 1024
+#define AB8500_ADC_CH_BTEMP_MIN 0
+#define AB8500_ADC_CH_BTEMP_MAX 1350
+#define AB8500_ADC_CH_DIETEMP_MIN 0
+#define AB8500_ADC_CH_DIETEMP_MAX 1350
+#define AB8500_ADC_CH_CHG_V_MIN 0
+#define AB8500_ADC_CH_CHG_V_MAX 20030
+#define AB8500_ADC_CH_ACCDET2_MIN 0
+#define AB8500_ADC_CH_ACCDET2_MAX 2500
+#define AB8500_ADC_CH_VBAT_MIN 2300
+#define AB8500_ADC_CH_VBAT_MAX 4800
+#define AB8500_ADC_CH_CHG_I_MIN 0
+#define AB8500_ADC_CH_CHG_I_MAX 1500
+#define AB8500_ADC_CH_BKBAT_MIN 0
+#define AB8500_ADC_CH_BKBAT_MAX 3200
+
+/* GPADC constants from AB8540 spec */
+#define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */
+#define AB8500_ADC_CH_IBAT_MAX 6000
+#define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */
+#define AB8500_ADC_CH_IBAT_MAX_V 60
+#define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */
+#define AB8500_GPADC_IBAT_VDROP_H 56
+
+/* This is used to not lose precision when dividing to get gain and offset */
+#define AB8500_GPADC_CALIB_SCALE 1000
+/*
+ * Number of bits shift used to not lose precision
+ * when dividing to get ibat gain.
+ */
+#define AB8500_GPADC_CALIB_SHIFT_IBAT 20
+
+/* Time in ms before disabling regulator */
+#define AB8500_GPADC_AUTOSUSPEND_DELAY 1
+
+#define AB8500_GPADC_CONVERSION_TIME 500 /* ms */
+
+enum ab8500_cal_channels {
+ AB8500_CAL_VMAIN = 0,
+ AB8500_CAL_BTEMP,
+ AB8500_CAL_VBAT,
+ AB8500_CAL_IBAT,
+ AB8500_CAL_NR,
+};
+
+/**
+ * struct ab8500_adc_cal_data - Table for storing gain and offset for the
+ * calibrated ADC channels
+ * @gain: Gain of the ADC channel
+ * @offset: Offset of the ADC channel
+ * @otp_calib_hi: Calibration from OTP
+ * @otp_calib_lo: Calibration from OTP
+ */
+struct ab8500_adc_cal_data {
+ s64 gain;
+ s64 offset;
+ u16 otp_calib_hi;
+ u16 otp_calib_lo;
+};
+
+/**
+ * struct ab8500_gpadc_chan_info - per-channel GPADC info
+ * @name: name of the channel
+ * @id: the internal AB8500 ID number for the channel
+ * @hardware_control: indicate that we want to use hardware ADC control
+ * on this channel, the default is software ADC control. Hardware control
+ * is normally only used to test the battery voltage during GSM bursts
+ * and needs a hardware trigger on the GPADCTrig pin of the ASIC.
+ * @falling_edge: indicate that we want to trigger on falling edge
+ * rather than rising edge, rising edge is the default
+ * @avg_sample: how many samples to average: must be 1, 4, 8 or 16.
+ * @trig_timer: how long to wait for the trigger, in 32kHz periods:
+ * 0 .. 255 periods
+ */
+struct ab8500_gpadc_chan_info {
+ const char *name;
+ u8 id;
+ bool hardware_control;
+ bool falling_edge;
+ u8 avg_sample;
+ u8 trig_timer;
+};
+
+/**
+ * struct ab8500_gpadc - AB8500 GPADC device information
+ * @dev: pointer to the containing device
+ * @ab8500: pointer to the parent AB8500 device
+ * @chans: internal per-channel information container
+ * @nchans: number of channels
+ * @complete: pointer to the completion that indicates
+ * the completion of an gpadc conversion cycle
+ * @vddadc: pointer to the regulator supplying VDDADC
+ * @irq_sw: interrupt number that is used by gpadc for software ADC conversion
+ * @irq_hw: interrupt number that is used by gpadc for hardware ADC conversion
+ * @cal_data: array of ADC calibration data structs
+ */
+struct ab8500_gpadc {
+ struct device *dev;
+ struct ab8500 *ab8500;
+ struct ab8500_gpadc_chan_info *chans;
+ unsigned int nchans;
+ struct completion complete;
+ struct regulator *vddadc;
+ int irq_sw;
+ int irq_hw;
+ struct ab8500_adc_cal_data cal_data[AB8500_CAL_NR];
+};
+
+static struct ab8500_gpadc_chan_info *
+ab8500_gpadc_get_channel(struct ab8500_gpadc *gpadc, u8 chan)
+{
+ struct ab8500_gpadc_chan_info *ch;
+ int i;
+
+ for (i = 0; i < gpadc->nchans; i++) {
+ ch = &gpadc->chans[i];
+ if (ch->id == chan)
+ break;
+ }
+ if (i == gpadc->nchans)
+ return NULL;
+
+ return ch;
+}
+
+/**
+ * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage
+ * @gpadc: GPADC instance
+ * @ch: the sampled channel this raw value is coming from
+ * @ad_value: the raw value
+ */
+static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc,
+ enum ab8500_gpadc_channel ch,
+ int ad_value)
+{
+ int res;
+
+ switch (ch) {
+ case AB8500_GPADC_CHAN_MAIN_CHARGER:
+ /* No calibration data available: just interpolate */
+ if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) {
+ res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX -
+ AB8500_ADC_CH_CHG_V_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use calibration */
+ res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain +
+ gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE;
+ break;
+
+ case AB8500_GPADC_CHAN_BAT_CTRL:
+ case AB8500_GPADC_CHAN_BAT_TEMP:
+ case AB8500_GPADC_CHAN_ACC_DET_1:
+ case AB8500_GPADC_CHAN_ADC_AUX_1:
+ case AB8500_GPADC_CHAN_ADC_AUX_2:
+ case AB8500_GPADC_CHAN_XTAL_TEMP:
+ /* No calibration data available: just interpolate */
+ if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) {
+ res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX -
+ AB8500_ADC_CH_BTEMP_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use calibration */
+ res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain +
+ gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE;
+ break;
+
+ case AB8500_GPADC_CHAN_VBAT_A:
+ case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS:
+ /* No calibration data available: just interpolate */
+ if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) {
+ res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX -
+ AB8500_ADC_CH_VBAT_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use calibration */
+ res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain +
+ gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE;
+ break;
+
+ case AB8505_GPADC_CHAN_DIE_TEMP:
+ res = AB8500_ADC_CH_DIETEMP_MIN +
+ (AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+
+ case AB8500_GPADC_CHAN_ACC_DET_2:
+ res = AB8500_ADC_CH_ACCDET2_MIN +
+ (AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+
+ case AB8500_GPADC_CHAN_VBUS:
+ res = AB8500_ADC_CH_CHG_V_MIN +
+ (AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+
+ case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT:
+ case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT:
+ res = AB8500_ADC_CH_CHG_I_MIN +
+ (AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+
+ case AB8500_GPADC_CHAN_BACKUP_BAT:
+ res = AB8500_ADC_CH_BKBAT_MIN +
+ (AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+
+ case AB8500_GPADC_CHAN_IBAT_VIRTUAL:
+ /* No calibration data available: just interpolate */
+ if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) {
+ res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX -
+ AB8500_ADC_CH_IBAT_MIN) * ad_value /
+ AB8500_ADC_RESOLUTION;
+ break;
+ }
+ /* Here we can use calibration */
+ res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain +
+ gpadc->cal_data[AB8500_CAL_IBAT].offset)
+ >> AB8500_GPADC_CALIB_SHIFT_IBAT;
+ break;
+
+ default:
+ dev_err(gpadc->dev,
+ "unknown channel ID: %d, not possible to convert\n",
+ ch);
+ res = -EINVAL;
+ break;
+
+ }
+
+ return res;
+}
+
+static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc,
+ const struct ab8500_gpadc_chan_info *ch,
+ int *ibat)
+{
+ int ret;
+ int looplimit = 0;
+ unsigned long completion_timeout;
+ u8 val;
+ u8 low_data, high_data, low_data2, high_data2;
+ u8 ctrl1;
+ u8 ctrl23;
+ unsigned int delay_min = 0;
+ unsigned int delay_max = 0;
+ u8 data_low_addr, data_high_addr;
+
+ if (!gpadc)
+ return -ENODEV;
+
+ /* check if conversion is supported */
+ if ((gpadc->irq_sw <= 0) && !ch->hardware_control)
+ return -ENOTSUPP;
+ if ((gpadc->irq_hw <= 0) && ch->hardware_control)
+ return -ENOTSUPP;
+
+ /* Enable vddadc by grabbing PM runtime */
+ pm_runtime_get_sync(gpadc->dev);
+
+ /* Check if ADC is not busy, lock and proceed */
+ do {
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_STAT_REG, &val);
+ if (ret < 0)
+ goto out;
+ if (!(val & AB8500_GPADC_STAT_BUSY))
+ break;
+ msleep(20);
+ } while (++looplimit < 10);
+ if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) {
+ dev_err(gpadc->dev, "gpadc_conversion: GPADC busy");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Enable GPADC */
+ ctrl1 = AB8500_GPADC_CTRL1_ENABLE;
+
+ /* Select the channel source and set average samples */
+ switch (ch->avg_sample) {
+ case 1:
+ ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1;
+ break;
+ case 4:
+ ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4;
+ break;
+ case 8:
+ ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8;
+ break;
+ default:
+ ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16;
+ break;
+ }
+
+ if (ch->hardware_control) {
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23);
+ ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA;
+ if (ch->falling_edge)
+ ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE;
+ } else {
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23);
+ }
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: set avg samples failed\n");
+ goto out;
+ }
+
+ /*
+ * Enable ADC, buffering, select rising edge and enable ADC path
+ * charging current sense if it needed, ABB 3.0 needs some special
+ * treatment too.
+ */
+ switch (ch->id) {
+ case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT:
+ case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT:
+ ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA |
+ AB8500_GPADC_CTRL1_ICHAR_ENA;
+ break;
+ case AB8500_GPADC_CHAN_BAT_TEMP:
+ if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) {
+ ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA |
+ AB8500_GPADC_CTRL1_BTEMP_PULL_UP;
+ /*
+ * Delay might be needed for ABB8500 cut 3.0, if not,
+ * remove when hardware will be available
+ */
+ delay_min = 1000; /* Delay in micro seconds */
+ delay_max = 10000; /* large range optimises sleepmode */
+ break;
+ }
+ fallthrough;
+ default:
+ ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA;
+ break;
+ }
+
+ /* Write configuration to control register 1 */
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: set Control register failed\n");
+ goto out;
+ }
+
+ if (delay_min != 0)
+ usleep_range(delay_min, delay_max);
+
+ if (ch->hardware_control) {
+ /* Set trigger delay timer */
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG,
+ ch->trig_timer);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: trig timer failed\n");
+ goto out;
+ }
+ completion_timeout = 2 * HZ;
+ data_low_addr = AB8500_GPADC_AUTODATAL_REG;
+ data_high_addr = AB8500_GPADC_AUTODATAH_REG;
+ } else {
+ /* Start SW conversion */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
+ AB8500_GPADC_CTRL1_START_SW_CONV,
+ AB8500_GPADC_CTRL1_START_SW_CONV);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: start s/w conv failed\n");
+ goto out;
+ }
+ completion_timeout = msecs_to_jiffies(AB8500_GPADC_CONVERSION_TIME);
+ data_low_addr = AB8500_GPADC_MANDATAL_REG;
+ data_high_addr = AB8500_GPADC_MANDATAH_REG;
+ }
+
+ /* Wait for completion of conversion */
+ if (!wait_for_completion_timeout(&gpadc->complete,
+ completion_timeout)) {
+ dev_err(gpadc->dev,
+ "timeout didn't receive GPADC conv interrupt\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Read the converted RAW data */
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, data_low_addr, &low_data);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read low data failed\n");
+ goto out;
+ }
+
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, data_high_addr, &high_data);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read high data failed\n");
+ goto out;
+ }
+
+ /* Check if double conversion is required */
+ if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) ||
+ (ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) ||
+ (ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) ||
+ (ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) {
+
+ if (ch->hardware_control) {
+ /* not supported */
+ ret = -ENOTSUPP;
+ dev_err(gpadc->dev,
+ "gpadc_conversion: only SW double conversion supported\n");
+ goto out;
+ } else {
+ /* Read the converted RAW data 2 */
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG,
+ &low_data2);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read sw low data 2 failed\n");
+ goto out;
+ }
+
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG,
+ &high_data2);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc_conversion: read sw high data 2 failed\n");
+ goto out;
+ }
+ if (ibat != NULL) {
+ *ibat = (high_data2 << 8) | low_data2;
+ } else {
+ dev_warn(gpadc->dev,
+ "gpadc_conversion: ibat not stored\n");
+ }
+
+ }
+ }
+
+ /* Disable GPADC */
+ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
+ goto out;
+ }
+
+ /* This eventually drops the regulator */
+ pm_runtime_mark_last_busy(gpadc->dev);
+ pm_runtime_put_autosuspend(gpadc->dev);
+
+ return (high_data << 8) | low_data;
+
+out:
+ /*
+ * It has shown to be needed to turn off the GPADC if an error occurs,
+ * otherwise we might have problem when waiting for the busy bit in the
+ * GPADC status register to go low. In V1.1 there wait_for_completion
+ * seems to timeout when waiting for an interrupt.. Not seen in V2.0
+ */
+ (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
+ AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE);
+ pm_runtime_put(gpadc->dev);
+ dev_err(gpadc->dev,
+ "gpadc_conversion: Failed to AD convert channel %d\n", ch->id);
+
+ return ret;
+}
+
+/**
+ * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
+ * @irq: irq number
+ * @data: pointer to the data passed during request irq
+ *
+ * This is a interrupt service routine for gpadc conversion completion.
+ * Notifies the gpadc completion is completed and the converted raw value
+ * can be read from the registers.
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *data)
+{
+ struct ab8500_gpadc *gpadc = data;
+
+ complete(&gpadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int otp_cal_regs[] = {
+ AB8500_GPADC_CAL_1,
+ AB8500_GPADC_CAL_2,
+ AB8500_GPADC_CAL_3,
+ AB8500_GPADC_CAL_4,
+ AB8500_GPADC_CAL_5,
+ AB8500_GPADC_CAL_6,
+ AB8500_GPADC_CAL_7,
+};
+
+static int otp4_cal_regs[] = {
+ AB8540_GPADC_OTP4_REG_7,
+ AB8540_GPADC_OTP4_REG_6,
+ AB8540_GPADC_OTP4_REG_5,
+};
+
+static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
+{
+ int i;
+ int ret[ARRAY_SIZE(otp_cal_regs)];
+ u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)];
+ int ret_otp4[ARRAY_SIZE(otp4_cal_regs)];
+ u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)];
+ int vmain_high, vmain_low;
+ int btemp_high, btemp_low;
+ int vbat_high, vbat_low;
+ int ibat_high, ibat_low;
+ s64 V_gain, V_offset, V2A_gain, V2A_offset;
+
+ /* First we read all OTP registers and store the error code */
+ for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) {
+ ret[i] = abx500_get_register_interruptible(gpadc->dev,
+ AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]);
+ if (ret[i] < 0) {
+ /* Continue anyway: maybe the other registers are OK */
+ dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n",
+ __func__, otp_cal_regs[i]);
+ } else {
+ /* Put this in the entropy pool as device-unique */
+ add_device_randomness(&ret[i], sizeof(ret[i]));
+ }
+ }
+
+ /*
+ * The ADC calibration data is stored in OTP registers.
+ * The layout of the calibration data is outlined below and a more
+ * detailed description can be found in UM0836
+ *
+ * vm_h/l = vmain_high/low
+ * bt_h/l = btemp_high/low
+ * vb_h/l = vbat_high/low
+ *
+ * Data bits 8500/9540:
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | vm_h9 | vm_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ *
+ * Data bits 8540:
+ * OTP2
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 |
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ *
+ * Data bits 8540:
+ * OTP4
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | | ib_h9 | ib_h8 | ib_h7
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5
+ * |.......|.......|.......|.......|.......|.......|.......|.......
+ * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 |
+ *
+ *
+ * Ideal output ADC codes corresponding to injected input voltages
+ * during manufacturing is:
+ *
+ * vmain_high: Vin = 19500mV / ADC ideal code = 997
+ * vmain_low: Vin = 315mV / ADC ideal code = 16
+ * btemp_high: Vin = 1300mV / ADC ideal code = 985
+ * btemp_low: Vin = 21mV / ADC ideal code = 16
+ * vbat_high: Vin = 4700mV / ADC ideal code = 982
+ * vbat_low: Vin = 2380mV / ADC ideal code = 33
+ */
+
+ if (is_ab8540(gpadc->ab8500)) {
+ /* Calculate gain and offset for VMAIN if all reads succeeded*/
+ if (!(ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[1] & 0xFF) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi =
+ (u16)vmain_high;
+ gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo =
+ (u16)vmain_low;
+
+ gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+ gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE *
+ 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0;
+ }
+
+ /* Read IBAT calibration Data */
+ for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) {
+ ret_otp4[i] = abx500_get_register_interruptible(
+ gpadc->dev, AB8500_OTP_EMUL,
+ otp4_cal_regs[i], &gpadc_otp4[i]);
+ if (ret_otp4[i] < 0)
+ dev_err(gpadc->dev,
+ "%s: read otp4 reg 0x%02x failed\n",
+ __func__, otp4_cal_regs[i]);
+ }
+
+ /* Calculate gain and offset for IBAT if all reads succeeded */
+ if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) {
+ ibat_high = (((gpadc_otp4[0] & 0x07) << 7) |
+ ((gpadc_otp4[1] & 0xFE) >> 1));
+ ibat_low = (((gpadc_otp4[1] & 0x01) << 5) |
+ ((gpadc_otp4[2] & 0xF8) >> 3));
+
+ gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_hi =
+ (u16)ibat_high;
+ gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_lo =
+ (u16)ibat_low;
+
+ V_gain = ((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L)
+ << AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low);
+
+ V_offset = (AB8500_GPADC_IBAT_VDROP_H << AB8500_GPADC_CALIB_SHIFT_IBAT) -
+ (((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) <<
+ AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low))
+ * ibat_high;
+ /*
+ * Result obtained is in mV (at a scale factor),
+ * we need to calculate gain and offset to get mA
+ */
+ V2A_gain = (AB8500_ADC_CH_IBAT_MAX - AB8500_ADC_CH_IBAT_MIN)/
+ (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V);
+ V2A_offset = ((AB8500_ADC_CH_IBAT_MAX_V * AB8500_ADC_CH_IBAT_MIN -
+ AB8500_ADC_CH_IBAT_MAX * AB8500_ADC_CH_IBAT_MIN_V)
+ << AB8500_GPADC_CALIB_SHIFT_IBAT)
+ / (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V);
+
+ gpadc->cal_data[AB8500_CAL_IBAT].gain =
+ V_gain * V2A_gain;
+ gpadc->cal_data[AB8500_CAL_IBAT].offset =
+ V_offset * V2A_gain + V2A_offset;
+ } else {
+ gpadc->cal_data[AB8500_CAL_IBAT].gain = 0;
+ }
+ } else {
+ /* Calculate gain and offset for VMAIN if all reads succeeded */
+ if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) {
+ vmain_high = (((gpadc_cal[0] & 0x03) << 8) |
+ ((gpadc_cal[1] & 0x3F) << 2) |
+ ((gpadc_cal[2] & 0xC0) >> 6));
+ vmain_low = ((gpadc_cal[2] & 0x3E) >> 1);
+
+ gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi =
+ (u16)vmain_high;
+ gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo =
+ (u16)vmain_low;
+
+ gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE *
+ (19500 - 315) / (vmain_high - vmain_low);
+
+ gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE *
+ 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) /
+ (vmain_high - vmain_low)) * vmain_high;
+ } else {
+ gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0;
+ }
+ }
+
+ /* Calculate gain and offset for BTEMP if all reads succeeded */
+ if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) {
+ btemp_high = (((gpadc_cal[2] & 0x01) << 9) |
+ (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7));
+ btemp_low = ((gpadc_cal[4] & 0x7C) >> 2);
+
+ gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_hi = (u16)btemp_high;
+ gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_lo = (u16)btemp_low;
+
+ gpadc->cal_data[AB8500_CAL_BTEMP].gain =
+ AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low);
+ gpadc->cal_data[AB8500_CAL_BTEMP].offset = AB8500_GPADC_CALIB_SCALE * 1300 -
+ (AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low))
+ * btemp_high;
+ } else {
+ gpadc->cal_data[AB8500_CAL_BTEMP].gain = 0;
+ }
+
+ /* Calculate gain and offset for VBAT if all reads succeeded */
+ if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) {
+ vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]);
+ vbat_low = ((gpadc_cal[6] & 0xFC) >> 2);
+
+ gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_hi = (u16)vbat_high;
+ gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_lo = (u16)vbat_low;
+
+ gpadc->cal_data[AB8500_CAL_VBAT].gain = AB8500_GPADC_CALIB_SCALE *
+ (4700 - 2380) / (vbat_high - vbat_low);
+ gpadc->cal_data[AB8500_CAL_VBAT].offset = AB8500_GPADC_CALIB_SCALE * 4700 -
+ (AB8500_GPADC_CALIB_SCALE * (4700 - 2380) /
+ (vbat_high - vbat_low)) * vbat_high;
+ } else {
+ gpadc->cal_data[AB8500_CAL_VBAT].gain = 0;
+ }
+}
+
+static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ab8500_gpadc *gpadc = iio_priv(indio_dev);
+ const struct ab8500_gpadc_chan_info *ch;
+ int raw_val;
+ int processed;
+
+ ch = ab8500_gpadc_get_channel(gpadc, chan->address);
+ if (!ch) {
+ dev_err(gpadc->dev, "no such channel %lu\n",
+ chan->address);
+ return -EINVAL;
+ }
+
+ raw_val = ab8500_gpadc_read(gpadc, ch, NULL);
+ if (raw_val < 0)
+ return raw_val;
+
+ if (mask == IIO_CHAN_INFO_RAW) {
+ *val = raw_val;
+ return IIO_VAL_INT;
+ }
+
+ if (mask == IIO_CHAN_INFO_PROCESSED) {
+ processed = ab8500_gpadc_ad_to_voltage(gpadc, ch->id, raw_val);
+ if (processed < 0)
+ return processed;
+
+ /* Return millivolt or milliamps or millicentigrades */
+ *val = processed;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ab8500_gpadc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info ab8500_gpadc_info = {
+ .fwnode_xlate = ab8500_gpadc_fwnode_xlate,
+ .read_raw = ab8500_gpadc_read_raw,
+};
+
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ab8500_gpadc *gpadc = iio_priv(indio_dev);
+
+ regulator_disable(gpadc->vddadc);
+
+ return 0;
+}
+
+static int ab8500_gpadc_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ab8500_gpadc *gpadc = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(gpadc->vddadc);
+ if (ret)
+ dev_err(dev, "Failed to enable vddadc: %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * ab8500_gpadc_parse_channel() - process devicetree channel configuration
+ * @dev: pointer to containing device
+ * @fwnode: fw node for the channel to configure
+ * @ch: channel info to fill in
+ * @iio_chan: IIO channel specification to fill in
+ *
+ * The devicetree will set up the channel for use with the specific device,
+ * and define usage for things like AUX GPADC inputs more precisely.
+ */
+static int ab8500_gpadc_parse_channel(struct device *dev,
+ struct fwnode_handle *fwnode,
+ struct ab8500_gpadc_chan_info *ch,
+ struct iio_chan_spec *iio_chan)
+{
+ const char *name = fwnode_get_name(fwnode);
+ u32 chan;
+ int ret;
+
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+ if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) {
+ dev_err(dev, "%s channel number out of range %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ iio_chan->channel = chan;
+ iio_chan->datasheet_name = name;
+ iio_chan->indexed = 1;
+ iio_chan->address = chan;
+ iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED);
+ /* Most are voltages (also temperatures), some are currents */
+ if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) ||
+ (chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT))
+ iio_chan->type = IIO_CURRENT;
+ else
+ iio_chan->type = IIO_VOLTAGE;
+
+ ch->id = chan;
+
+ /* Sensible defaults */
+ ch->avg_sample = 16;
+ ch->hardware_control = false;
+ ch->falling_edge = false;
+ ch->trig_timer = 0;
+
+ return 0;
+}
+
+/**
+ * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT
+ * @gpadc: the GPADC to configure the channels for
+ * @chans: the IIO channels we parsed
+ * @nchans: the number of IIO channels we parsed
+ */
+static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
+ struct iio_chan_spec **chans_parsed,
+ unsigned int *nchans_parsed)
+{
+ struct fwnode_handle *child;
+ struct ab8500_gpadc_chan_info *ch;
+ struct iio_chan_spec *iio_chans;
+ unsigned int nchans;
+ int i;
+
+ nchans = device_get_child_node_count(gpadc->dev);
+ if (!nchans) {
+ dev_err(gpadc->dev, "no channel children\n");
+ return -ENODEV;
+ }
+ dev_info(gpadc->dev, "found %d ADC channels\n", nchans);
+
+ iio_chans = devm_kcalloc(gpadc->dev, nchans,
+ sizeof(*iio_chans), GFP_KERNEL);
+ if (!iio_chans)
+ return -ENOMEM;
+
+ gpadc->chans = devm_kcalloc(gpadc->dev, nchans,
+ sizeof(*gpadc->chans), GFP_KERNEL);
+ if (!gpadc->chans)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_child_node(gpadc->dev, child) {
+ struct iio_chan_spec *iio_chan;
+ int ret;
+
+ ch = &gpadc->chans[i];
+ iio_chan = &iio_chans[i];
+
+ ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch,
+ iio_chan);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+ i++;
+ }
+ gpadc->nchans = nchans;
+ *chans_parsed = iio_chans;
+ *nchans_parsed = nchans;
+
+ return 0;
+}
+
+static int ab8500_gpadc_probe(struct platform_device *pdev)
+{
+ struct ab8500_gpadc *gpadc;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ struct iio_chan_spec *iio_chans;
+ unsigned int n_iio_chans;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ gpadc = iio_priv(indio_dev);
+
+ gpadc->dev = dev;
+ gpadc->ab8500 = dev_get_drvdata(dev->parent);
+
+ ret = ab8500_gpadc_parse_channels(gpadc, &iio_chans, &n_iio_chans);
+ if (ret)
+ return ret;
+
+ gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
+ if (gpadc->irq_sw < 0)
+ return dev_err_probe(dev, gpadc->irq_sw,
+ "failed to get platform sw_conv_end irq\n");
+
+ if (is_ab8500(gpadc->ab8500)) {
+ gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
+ if (gpadc->irq_hw < 0)
+ return dev_err_probe(dev, gpadc->irq_hw,
+ "failed to get platform hw_conv_end irq\n");
+ } else {
+ gpadc->irq_hw = 0;
+ }
+
+ /* Initialize completion used to notify completion of conversion */
+ init_completion(&gpadc->complete);
+
+ /* Request interrupts */
+ ret = devm_request_threaded_irq(dev, gpadc->irq_sw, NULL,
+ ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "ab8500-gpadc-sw", gpadc);
+ if (ret < 0) {
+ dev_err(dev,
+ "failed to request sw conversion irq %d\n",
+ gpadc->irq_sw);
+ return ret;
+ }
+
+ if (gpadc->irq_hw) {
+ ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL,
+ ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "ab8500-gpadc-hw", gpadc);
+ if (ret < 0) {
+ dev_err(dev,
+ "Failed to request hw conversion irq: %d\n",
+ gpadc->irq_hw);
+ return ret;
+ }
+ }
+
+ /* The VTVout LDO used to power the AB8500 GPADC */
+ gpadc->vddadc = devm_regulator_get(dev, "vddadc");
+ if (IS_ERR(gpadc->vddadc))
+ return dev_err_probe(dev, PTR_ERR(gpadc->vddadc),
+ "failed to get vddadc\n");
+
+ ret = regulator_enable(gpadc->vddadc);
+ if (ret) {
+ dev_err(dev, "failed to enable vddadc: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable runtime PM */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, AB8500_GPADC_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(dev);
+
+ ab8500_gpadc_read_calibration_data(gpadc);
+
+ pm_runtime_put(dev);
+
+ indio_dev->name = "ab8500-gpadc";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ab8500_gpadc_info;
+ indio_dev->channels = iio_chans;
+ indio_dev->num_channels = n_iio_chans;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ goto out_dis_pm;
+
+ return 0;
+
+out_dis_pm:
+ pm_runtime_get_sync(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ regulator_disable(gpadc->vddadc);
+
+ return ret;
+}
+
+static int ab8500_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct ab8500_gpadc *gpadc = iio_priv(indio_dev);
+
+ pm_runtime_get_sync(gpadc->dev);
+ pm_runtime_put_noidle(gpadc->dev);
+ pm_runtime_disable(gpadc->dev);
+ regulator_disable(gpadc->vddadc);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ab8500_gpadc_pm_ops,
+ ab8500_gpadc_runtime_suspend,
+ ab8500_gpadc_runtime_resume, NULL);
+
+static struct platform_driver ab8500_gpadc_driver = {
+ .probe = ab8500_gpadc_probe,
+ .remove = ab8500_gpadc_remove,
+ .driver = {
+ .name = "ab8500-gpadc",
+ .pm = pm_ptr(&ab8500_gpadc_pm_ops),
+ },
+};
+builtin_platform_driver(ab8500_gpadc_driver);
diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c
new file mode 100644
index 000000000..76002b91c
--- /dev/null
+++ b/drivers/iio/adc/ad7091r-base.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7091RX Analog to Digital converter driver
+ *
+ * Copyright 2014-2019 Analog Devices Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include "ad7091r-base.h"
+
+#define AD7091R_REG_RESULT 0
+#define AD7091R_REG_CHANNEL 1
+#define AD7091R_REG_CONF 2
+#define AD7091R_REG_ALERT 3
+#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4)
+#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5)
+#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6)
+
+/* AD7091R_REG_RESULT */
+#define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3)
+#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff)
+
+/* AD7091R_REG_CONF */
+#define AD7091R_REG_CONF_ALERT_EN BIT(4)
+#define AD7091R_REG_CONF_AUTO BIT(8)
+#define AD7091R_REG_CONF_CMD BIT(10)
+
+#define AD7091R_REG_CONF_MODE_MASK \
+ (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD)
+
+enum ad7091r_mode {
+ AD7091R_MODE_SAMPLE,
+ AD7091R_MODE_COMMAND,
+ AD7091R_MODE_AUTOCYCLE,
+};
+
+struct ad7091r_state {
+ struct device *dev;
+ struct regmap *map;
+ struct regulator *vref;
+ const struct ad7091r_chip_info *chip_info;
+ enum ad7091r_mode mode;
+ struct mutex lock; /*lock to prevent concurent reads */
+};
+
+const struct iio_event_spec ad7091r_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),
+ },
+};
+EXPORT_SYMBOL_NS_GPL(ad7091r_events, IIO_AD7091R);
+
+static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode)
+{
+ int ret, conf;
+
+ switch (mode) {
+ case AD7091R_MODE_SAMPLE:
+ conf = 0;
+ break;
+ case AD7091R_MODE_COMMAND:
+ conf = AD7091R_REG_CONF_CMD;
+ break;
+ case AD7091R_MODE_AUTOCYCLE:
+ conf = AD7091R_REG_CONF_AUTO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(st->map, AD7091R_REG_CONF,
+ AD7091R_REG_CONF_MODE_MASK, conf);
+ if (ret)
+ return ret;
+
+ st->mode = mode;
+
+ return 0;
+}
+
+static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel)
+{
+ unsigned int dummy;
+ int ret;
+
+ /* AD7091R_REG_CHANNEL specified which channels to be converted */
+ ret = regmap_write(st->map, AD7091R_REG_CHANNEL,
+ BIT(channel) | (BIT(channel) << 8));
+ if (ret)
+ return ret;
+
+ /*
+ * There is a latency of one conversion before the channel conversion
+ * sequence is updated
+ */
+ return regmap_read(st->map, AD7091R_REG_RESULT, &dummy);
+}
+
+static int ad7091r_read_one(struct iio_dev *iio_dev,
+ unsigned int channel, unsigned int *read_val)
+{
+ struct ad7091r_state *st = iio_priv(iio_dev);
+ unsigned int val;
+ int ret;
+
+ ret = ad7091r_set_channel(st, channel);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->map, AD7091R_REG_RESULT, &val);
+ if (ret)
+ return ret;
+
+ if (AD7091R_REG_RESULT_CH_ID(val) != channel)
+ return -EIO;
+
+ *read_val = AD7091R_REG_RESULT_CONV_RESULT(val);
+
+ return 0;
+}
+
+static int ad7091r_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ad7091r_state *st = iio_priv(iio_dev);
+ unsigned int read_val;
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ if (st->mode != AD7091R_MODE_COMMAND) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ ret = ad7091r_read_one(iio_dev, chan->channel, &read_val);
+ if (ret)
+ goto unlock;
+
+ *val = read_val;
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (st->vref) {
+ ret = regulator_get_voltage(st->vref);
+ if (ret < 0)
+ goto unlock;
+
+ *val = ret / 1000;
+ } else {
+ *val = st->chip_info->vref_mV;
+ }
+
+ *val2 = chan->scan_type.realbits;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_read(st->map,
+ AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
+ &val);
+ if (ret)
+ return ret;
+ return val != AD7091R_HIGH_LIMIT;
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_read(st->map,
+ AD7091R_REG_CH_LOW_LIMIT(chan->channel),
+ &val);
+ if (ret)
+ return ret;
+ return val != AD7091R_LOW_LIMIT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7091r_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct ad7091r_state *st = iio_priv(indio_dev);
+
+ if (state) {
+ return regmap_set_bits(st->map, AD7091R_REG_CONF,
+ AD7091R_REG_CONF_ALERT_EN);
+ } else {
+ /*
+ * Set thresholds either to 0 or to 2^12 - 1 as appropriate to
+ * prevent alerts and thus disable event generation.
+ */
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return regmap_write(st->map,
+ AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
+ AD7091R_HIGH_LIMIT);
+ case IIO_EV_DIR_FALLING:
+ return regmap_write(st->map,
+ AD7091R_REG_CH_LOW_LIMIT(chan->channel),
+ AD7091R_LOW_LIMIT);
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_read(st->map,
+ AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
+ val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_read(st->map,
+ AD7091R_REG_CH_LOW_LIMIT(chan->channel),
+ val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_HYSTERESIS:
+ ret = regmap_read(st->map,
+ AD7091R_REG_CH_HYSTERESIS(chan->channel),
+ val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7091r_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 ad7091r_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return regmap_write(st->map,
+ AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
+ val);
+ case IIO_EV_DIR_FALLING:
+ return regmap_write(st->map,
+ AD7091R_REG_CH_LOW_LIMIT(chan->channel),
+ val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_HYSTERESIS:
+ return regmap_write(st->map,
+ AD7091R_REG_CH_HYSTERESIS(chan->channel),
+ val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7091r_info = {
+ .read_raw = ad7091r_read_raw,
+ .read_event_config = &ad7091r_read_event_config,
+ .write_event_config = &ad7091r_write_event_config,
+ .read_event_value = &ad7091r_read_event_value,
+ .write_event_value = &ad7091r_write_event_value,
+};
+
+static irqreturn_t ad7091r_event_handler(int irq, void *private)
+{
+ struct iio_dev *iio_dev = private;
+ struct ad7091r_state *st = iio_priv(iio_dev);
+ unsigned int i, read_val;
+ int ret;
+ s64 timestamp = iio_get_time_ns(iio_dev);
+
+ ret = regmap_read(st->map, AD7091R_REG_ALERT, &read_val);
+ if (ret)
+ return IRQ_HANDLED;
+
+ for (i = 0; i < st->chip_info->num_channels; i++) {
+ if (read_val & BIT(i * 2))
+ iio_push_event(iio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING), timestamp);
+ if (read_val & BIT(i * 2 + 1))
+ iio_push_event(iio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING), timestamp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void ad7091r_remove(void *data)
+{
+ struct ad7091r_state *st = data;
+
+ regulator_disable(st->vref);
+}
+
+int ad7091r_probe(struct device *dev, const char *name,
+ const struct ad7091r_chip_info *chip_info,
+ struct regmap *map, int irq)
+{
+ struct iio_dev *iio_dev;
+ struct ad7091r_state *st;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(iio_dev);
+ st->dev = dev;
+ st->chip_info = chip_info;
+ st->map = map;
+
+ iio_dev->name = name;
+ iio_dev->info = &ad7091r_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+
+ iio_dev->num_channels = chip_info->num_channels;
+ iio_dev->channels = chip_info->channels;
+
+ if (irq) {
+ ret = regmap_update_bits(st->map, AD7091R_REG_CONF,
+ AD7091R_REG_CONF_ALERT_EN, BIT(4));
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ ad7091r_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, iio_dev);
+ if (ret)
+ return ret;
+ }
+
+ st->vref = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(st->vref)) {
+ if (PTR_ERR(st->vref) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ st->vref = NULL;
+ /* Enable internal vref */
+ ret = regmap_set_bits(st->map, AD7091R_REG_CONF,
+ AD7091R_REG_CONF_INT_VREF);
+ if (ret)
+ return dev_err_probe(st->dev, ret,
+ "Error on enable internal reference\n");
+ } else {
+ ret = regulator_enable(st->vref);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(dev, ad7091r_remove, st);
+ if (ret)
+ return ret;
+ }
+
+ /* Use command mode by default to convert only desired channels*/
+ ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, iio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(ad7091r_probe, IIO_AD7091R);
+
+static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AD7091R_REG_RESULT:
+ case AD7091R_REG_ALERT:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static 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;
+ }
+}
+
+const struct regmap_config ad7091r_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .writeable_reg = ad7091r_writeable_reg,
+ .volatile_reg = ad7091r_volatile_reg,
+};
+EXPORT_SYMBOL_NS_GPL(ad7091r_regmap_config, IIO_AD7091R);
+
+MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h
new file mode 100644
index 000000000..b9e1c8bf3
--- /dev/null
+++ b/drivers/iio/adc/ad7091r-base.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AD7091RX Analog to Digital converter driver
+ *
+ * Copyright 2014-2019 Analog Devices Inc.
+ */
+
+#ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__
+#define __DRIVERS_IIO_ADC_AD7091R_BASE_H__
+
+#define AD7091R_REG_CONF_INT_VREF BIT(0)
+
+/* AD7091R_REG_CH_LIMIT */
+#define AD7091R_HIGH_LIMIT 0xFFF
+#define AD7091R_LOW_LIMIT 0x0
+
+struct device;
+struct ad7091r_state;
+
+struct ad7091r_chip_info {
+ unsigned int num_channels;
+ const struct iio_chan_spec *channels;
+ unsigned int vref_mV;
+};
+
+extern const struct iio_event_spec ad7091r_events[3];
+
+extern const struct regmap_config ad7091r_regmap_config;
+
+int ad7091r_probe(struct device *dev, const char *name,
+ const struct ad7091r_chip_info *chip_info,
+ struct regmap *map, int irq);
+
+#endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */
diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c
new file mode 100644
index 000000000..12d475463
--- /dev/null
+++ b/drivers/iio/adc/ad7091r5.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7091R5 Analog to Digital converter driver
+ *
+ * Copyright 2014-2019 Analog Devices Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "ad7091r-base.h"
+
+#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .indexed = 1, \
+ .channel = idx, \
+ .event_spec = ev, \
+ .num_event_specs = num_ev, \
+ .scan_type.storagebits = 16, \
+ .scan_type.realbits = bits, \
+}
+static const struct iio_chan_spec ad7091r5_channels_irq[] = {
+ AD7091R_CHANNEL(0, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+ AD7091R_CHANNEL(1, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+ AD7091R_CHANNEL(2, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+ AD7091R_CHANNEL(3, 12, ad7091r_events, ARRAY_SIZE(ad7091r_events)),
+};
+
+static const struct iio_chan_spec ad7091r5_channels_noirq[] = {
+ AD7091R_CHANNEL(0, 12, NULL, 0),
+ AD7091R_CHANNEL(1, 12, NULL, 0),
+ AD7091R_CHANNEL(2, 12, NULL, 0),
+ AD7091R_CHANNEL(3, 12, NULL, 0),
+};
+
+static const struct ad7091r_chip_info ad7091r5_chip_info_irq = {
+ .channels = ad7091r5_channels_irq,
+ .num_channels = ARRAY_SIZE(ad7091r5_channels_irq),
+ .vref_mV = 2500,
+};
+
+static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = {
+ .channels = ad7091r5_channels_noirq,
+ .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq),
+ .vref_mV = 2500,
+};
+
+static int ad7091r5_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ const struct ad7091r_chip_info *chip_info;
+ struct regmap *map = devm_regmap_init_i2c(i2c, &ad7091r_regmap_config);
+
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ if (i2c->irq)
+ chip_info = &ad7091r5_chip_info_irq;
+ else
+ chip_info = &ad7091r5_chip_info_noirq;
+
+ return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq);
+}
+
+static const struct of_device_id ad7091r5_dt_ids[] = {
+ { .compatible = "adi,ad7091r5" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids);
+
+static const struct i2c_device_id ad7091r5_i2c_ids[] = {
+ {"ad7091r5", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids);
+
+static struct i2c_driver ad7091r5_driver = {
+ .driver = {
+ .name = "ad7091r5",
+ .of_match_table = ad7091r5_dt_ids,
+ },
+ .probe = ad7091r5_i2c_probe,
+ .id_table = ad7091r5_i2c_ids,
+};
+module_i2c_driver(ad7091r5_driver);
+
+MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD7091R);
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
new file mode 100644
index 000000000..4088786e1
--- /dev/null
+++ b/drivers/iio/adc/ad7124.c
@@ -0,0 +1,1036 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD7124 SPI ADC driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kfifo.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+#include <linux/iio/sysfs.h>
+
+/* AD7124 registers */
+#define AD7124_COMMS 0x00
+#define AD7124_STATUS 0x00
+#define AD7124_ADC_CONTROL 0x01
+#define AD7124_DATA 0x02
+#define AD7124_IO_CONTROL_1 0x03
+#define AD7124_IO_CONTROL_2 0x04
+#define AD7124_ID 0x05
+#define AD7124_ERROR 0x06
+#define AD7124_ERROR_EN 0x07
+#define AD7124_MCLK_COUNT 0x08
+#define AD7124_CHANNEL(x) (0x09 + (x))
+#define AD7124_CONFIG(x) (0x19 + (x))
+#define AD7124_FILTER(x) (0x21 + (x))
+#define AD7124_OFFSET(x) (0x29 + (x))
+#define AD7124_GAIN(x) (0x31 + (x))
+
+/* AD7124_STATUS */
+#define AD7124_STATUS_POR_FLAG_MSK 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
+
+/* 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)
+
+/* 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)
+
+/* 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_SINC3_FILTER 2
+#define AD7124_SINC4_FILTER 0
+
+#define AD7124_CONF_ADDR_OFFSET 20
+#define AD7124_MAX_CONFIGS 8
+#define AD7124_MAX_CHANNELS 16
+
+enum ad7124_ids {
+ ID_AD7124_4,
+ ID_AD7124_8,
+};
+
+enum ad7124_ref_sel {
+ AD7124_REFIN1,
+ AD7124_REFIN2,
+ AD7124_INT_REF,
+ AD7124_AVDD_REF,
+};
+
+enum ad7124_power_mode {
+ AD7124_LOW_POWER,
+ AD7124_MID_POWER,
+ AD7124_FULL_POWER,
+};
+
+static const unsigned int ad7124_gain[8] = {
+ 1, 2, 4, 8, 16, 32, 64, 128
+};
+
+static const unsigned int ad7124_reg_size[] = {
+ 1, 2, 3, 3, 2, 1, 3, 3, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3
+};
+
+static const int ad7124_master_clk_freq_hz[3] = {
+ [AD7124_LOW_POWER] = 76800,
+ [AD7124_MID_POWER] = 153600,
+ [AD7124_FULL_POWER] = 614400,
+};
+
+static const char * const ad7124_ref_names[] = {
+ [AD7124_REFIN1] = "refin1",
+ [AD7124_REFIN2] = "refin2",
+ [AD7124_INT_REF] = "int",
+ [AD7124_AVDD_REF] = "avdd",
+};
+
+struct ad7124_chip_info {
+ const char *name;
+ unsigned int chip_id;
+ unsigned int num_inputs;
+};
+
+struct ad7124_channel_config {
+ bool live;
+ unsigned int cfg_slot;
+ 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;
+};
+
+struct ad7124_channel {
+ unsigned int nr;
+ struct ad7124_channel_config cfg;
+ unsigned int ain;
+ unsigned int slot;
+};
+
+struct ad7124_state {
+ const struct ad7124_chip_info *chip_info;
+ struct ad_sigma_delta sd;
+ struct ad7124_channel *channels;
+ struct regulator *vref[4];
+ struct clk *mclk;
+ unsigned int adc_control;
+ 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,
+ },
+};
+
+static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
+ [ID_AD7124_4] = {
+ .name = "ad7124-4",
+ .chip_id = CHIPID_AD7124_4,
+ .num_inputs = 8,
+ },
+ [ID_AD7124_8] = {
+ .name = "ad7124-8",
+ .chip_id = CHIPID_AD7124_8,
+ .num_inputs = 16,
+ },
+};
+
+static int ad7124_find_closest_match(const int *array,
+ unsigned int size, int val)
+{
+ int i, idx;
+ unsigned int diff_new, diff_old;
+
+ diff_old = U32_MAX;
+ idx = 0;
+
+ for (i = 0; i < size; i++) {
+ diff_new = abs(val - array[i]);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ return idx;
+}
+
+static int ad7124_spi_write_mask(struct ad7124_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val,
+ unsigned int bytes)
+{
+ unsigned int readval;
+ int ret;
+
+ ret = ad_sd_read_reg(&st->sd, addr, bytes, &readval);
+ if (ret < 0)
+ return ret;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return ad_sd_write_reg(&st->sd, addr, bytes, readval);
+}
+
+static int ad7124_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ 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);
+
+ return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+}
+
+static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel, unsigned int odr)
+{
+ unsigned int fclk, odr_sel_bits;
+
+ fclk = clk_get_rate(st->mclk);
+ /*
+ * FS[10:0] = fCLK / (fADC x 32) where:
+ * fADC is the output data rate
+ * fCLK is the master clock frequency
+ * FS[10:0] are the bits in the filter register
+ * FS[10:0] can have a value from 1 to 2047
+ */
+ odr_sel_bits = DIV_ROUND_CLOSEST(fclk, odr * 32);
+ if (odr_sel_bits < 1)
+ odr_sel_bits = 1;
+ else if (odr_sel_bits > 2047)
+ odr_sel_bits = 2047;
+
+ if (odr_sel_bits != st->channels[channel].cfg.odr_sel_bits)
+ st->channels[channel].cfg.live = false;
+
+ /* fADC = fCLK / (FS[10:0] x 32) */
+ st->channels[channel].cfg.odr = DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
+ st->channels[channel].cfg.odr_sel_bits = odr_sel_bits;
+}
+
+static int ad7124_get_3db_filter_freq(struct ad7124_state *st,
+ unsigned int channel)
+{
+ unsigned int fadc;
+
+ fadc = st->channels[channel].cfg.odr;
+
+ switch (st->channels[channel].cfg.filter_type) {
+ case AD7124_SINC3_FILTER:
+ 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 = (u8 *)&cfg->live - (u8 *)cfg;
+ for (i = 0; i < st->num_channels; i++) {
+ cfg_aux = &st->channels[i].cfg;
+
+ if (cfg_aux->live && !memcmp(cfg, cfg_aux, cmp_size))
+ return cfg_aux;
+ }
+
+ return NULL;
+}
+
+static int ad7124_find_free_config_slot(struct ad7124_state *st)
+{
+ unsigned int free_cfg_slot;
+
+ free_cfg_slot = find_first_zero_bit(&st->cfg_slots_status, AD7124_MAX_CONFIGS);
+ if (free_cfg_slot == AD7124_MAX_CONFIGS)
+ return -1;
+
+ return free_cfg_slot;
+}
+
+static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg)
+{
+ 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]);
+ }
+ 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);
+ return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL,
+ 2, st->adc_control);
+ default:
+ dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
+ return -EINVAL;
+ }
+}
+
+static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_config *cfg,
+ unsigned int cfg_slot)
+{
+ unsigned int tmp;
+ unsigned int val;
+ int ret;
+
+ 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);
+ 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);
+ ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_TYPE_MSK,
+ tmp, 3);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_FS_MSK,
+ AD7124_FILTER_FS(cfg->odr_sel_bits), 3);
+ if (ret < 0)
+ return ret;
+
+ return ad7124_spi_write_mask(st, AD7124_CONFIG(cfg->cfg_slot), AD7124_CONFIG_PGA_MSK,
+ AD7124_CONFIG_PGA(cfg->pga_bits), 2);
+}
+
+static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st)
+{
+ struct ad7124_channel_config *lru_cfg;
+ struct ad7124_channel_config *cfg;
+ int ret;
+ int i;
+
+ /*
+ * Pop least recently used config from the fifo
+ * in order to make room for the new one
+ */
+ ret = kfifo_get(&st->live_cfgs_fifo, &lru_cfg);
+ if (ret <= 0)
+ return NULL;
+
+ lru_cfg->live = false;
+
+ /* mark slot as free */
+ assign_bit(lru_cfg->cfg_slot, &st->cfg_slots_status, 0);
+
+ /* invalidate all other configs that pointed to this one */
+ for (i = 0; i < st->num_channels; i++) {
+ cfg = &st->channels[i].cfg;
+
+ if (cfg->cfg_slot == lru_cfg->cfg_slot)
+ cfg->live = false;
+ }
+
+ return lru_cfg;
+}
+
+static int ad7124_push_config(struct ad7124_state *st, struct ad7124_channel_config *cfg)
+{
+ struct ad7124_channel_config *lru_cfg;
+ int free_cfg_slot;
+
+ free_cfg_slot = ad7124_find_free_config_slot(st);
+ if (free_cfg_slot >= 0) {
+ /* push the new config in configs queue */
+ kfifo_put(&st->live_cfgs_fifo, cfg);
+ } else {
+ /* pop one config to make room for the new one */
+ lru_cfg = ad7124_pop_config(st);
+ if (!lru_cfg)
+ return -EINVAL;
+
+ /* push the new config in configs queue */
+ free_cfg_slot = lru_cfg->cfg_slot;
+ kfifo_put(&st->live_cfgs_fifo, cfg);
+ }
+
+ /* mark slot as used */
+ assign_bit(free_cfg_slot, &st->cfg_slots_status, 1);
+
+ return ad7124_write_config(st, cfg, free_cfg_slot);
+}
+
+static int ad7124_enable_channel(struct ad7124_state *st, struct ad7124_channel *ch)
+{
+ 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));
+}
+
+static int ad7124_prepare_read(struct ad7124_state *st, int address)
+{
+ struct ad7124_channel_config *cfg = &st->channels[address].cfg;
+ struct ad7124_channel_config *live_cfg;
+
+ /*
+ * Before doing any reads assign the channel a configuration.
+ * Check if channel's config is on the device
+ */
+ if (!cfg->live) {
+ /* check if config matches another one */
+ live_cfg = ad7124_find_similar_live_cfg(st, cfg);
+ if (!live_cfg)
+ ad7124_push_config(st, cfg);
+ else
+ cfg->cfg_slot = live_cfg->cfg_slot;
+ }
+
+ /* point channel to the config slot and enable */
+ return ad7124_enable_channel(st, &st->channels[address]);
+}
+
+static int __ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+
+ return ad7124_prepare_read(st, channel);
+}
+
+static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+ int ret;
+
+ mutex_lock(&st->cfgs_lock);
+ ret = __ad7124_set_channel(sd, channel);
+ mutex_unlock(&st->cfgs_lock);
+
+ return ret;
+}
+
+static int ad7124_append_status(struct ad_sigma_delta *sd, bool append)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+ unsigned int adc_control = st->adc_control;
+ int ret;
+
+ adc_control &= ~AD7124_ADC_STATUS_EN_MSK;
+ adc_control |= AD7124_ADC_STATUS_EN(append);
+
+ ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, adc_control);
+ if (ret < 0)
+ return ret;
+
+ st->adc_control = adc_control;
+
+ return 0;
+}
+
+static int ad7124_disable_all(struct ad_sigma_delta *sd)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, 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);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
+ .set_channel = ad7124_set_channel,
+ .append_status = ad7124_append_status,
+ .disable_all = ad7124_disable_all,
+ .set_mode = ad7124_set_mode,
+ .has_registers = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .status_ch_mask = GENMASK(3, 0),
+ .data_reg = AD7124_DATA,
+ .num_slots = 8,
+ .irq_flags = IRQF_TRIGGER_FALLING,
+};
+
+static int ad7124_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ int idx, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ /* After the conversion is performed, disable the channel */
+ ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan->address), 2,
+ st->channels[chan->address].ain | AD7124_CHANNEL_EN(0));
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ 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;
+
+ 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;
+
+ 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;
+ mutex_unlock(&st->cfgs_lock);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ mutex_lock(&st->cfgs_lock);
+ *val = ad7124_get_3db_filter_freq(st, chan->scan_index);
+ mutex_unlock(&st->cfgs_lock);
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7124_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ unsigned int res, gain, full_scale, vref;
+ int ret = 0;
+
+ mutex_lock(&st->cfgs_lock);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2 != 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ad7124_set_channel_odr(st, chan->address, val);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (st->channels[chan->address].cfg.bipolar)
+ full_scale = 1 << (chan->scan_type.realbits - 1);
+ else
+ full_scale = 1 << chan->scan_type.realbits;
+
+ vref = st->channels[chan->address].cfg.vref_mv * 1000000LL;
+ res = DIV_ROUND_CLOSEST(vref, full_scale);
+ gain = DIV_ROUND_CLOSEST(res, val2);
+ res = ad7124_find_closest_match(ad7124_gain, ARRAY_SIZE(ad7124_gain), gain);
+
+ if (st->channels[chan->address].cfg.pga_bits != res)
+ st->channels[chan->address].cfg.live = false;
+
+ 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;
+ }
+
+ mutex_unlock(&st->cfgs_lock);
+ return ret;
+}
+
+static int ad7124_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (reg >= ARRAY_SIZE(ad7124_reg_size))
+ return -EINVAL;
+
+ if (readval)
+ ret = ad_sd_read_reg(&st->sd, reg, ad7124_reg_size[reg],
+ readval);
+ else
+ ret = ad_sd_write_reg(&st->sd, reg, ad7124_reg_size[reg],
+ writeval);
+
+ return ret;
+}
+
+static IIO_CONST_ATTR(in_voltage_scale_available,
+ "0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023");
+
+static struct attribute *ad7124_attributes[] = {
+ &iio_const_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7124_attrs_group = {
+ .attrs = ad7124_attributes,
+};
+
+static int ad7124_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ bool bit_set;
+ int ret;
+ int i;
+
+ mutex_lock(&st->cfgs_lock);
+ for (i = 0; i < st->num_channels; i++) {
+ bit_set = test_bit(i, scan_mask);
+ if (bit_set)
+ ret = __ad7124_set_channel(&st->sd, i);
+ else
+ ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_EN_MSK,
+ 0, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->cfgs_lock);
+
+ return ret;
+ }
+ }
+
+ mutex_unlock(&st->cfgs_lock);
+
+ return 0;
+}
+
+static const struct iio_info ad7124_info = {
+ .read_raw = ad7124_read_raw,
+ .write_raw = ad7124_write_raw,
+ .debugfs_reg_access = &ad7124_reg_access,
+ .validate_trigger = ad_sd_validate_trigger,
+ .update_scan_mode = ad7124_update_scan_mode,
+ .attrs = &ad7124_attrs_group,
+};
+
+static int ad7124_soft_reset(struct ad7124_state *st)
+{
+ unsigned int readval, timeout;
+ int ret;
+
+ ret = ad_sd_reset(&st->sd, 64);
+ if (ret < 0)
+ return ret;
+
+ timeout = 100;
+ do {
+ ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval);
+ if (ret < 0)
+ return ret;
+
+ if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
+ return 0;
+
+ /* 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");
+
+ return -EIO;
+}
+
+static int ad7124_check_chip_id(struct ad7124_state *st)
+{
+ 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;
+
+ chip_id = AD7124_DEVICE_ID_GET(readval);
+ silicon_rev = AD7124_SILICON_REV_GET(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 (silicon_rev == 0) {
+ dev_err(&st->sd.spi->dev,
+ "Silicon revision empty. Chip may not be present\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
+ struct device_node *np)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ struct ad7124_channel_config *cfg;
+ struct ad7124_channel *channels;
+ struct device_node *child;
+ struct iio_chan_spec *chan;
+ unsigned int ain[2], channel = 0, tmp;
+ int ret;
+
+ st->num_channels = of_get_available_child_count(np);
+ if (!st->num_channels) {
+ dev_err(indio_dev->dev.parent, "no channel children\n");
+ return -ENODEV;
+ }
+
+ chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
+ sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ channels = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*channels),
+ GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ indio_dev->channels = chan;
+ indio_dev->num_channels = st->num_channels;
+ st->channels = channels;
+
+ for_each_available_child_of_node(np, child) {
+ cfg = &st->channels[channel].cfg;
+
+ ret = of_property_read_u32(child, "reg", &channel);
+ if (ret)
+ goto err;
+
+ if (channel >= indio_dev->num_channels) {
+ dev_err(indio_dev->dev.parent,
+ "Channel index >= number of channels\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = of_property_read_u32_array(child, "diff-channels",
+ ain, 2);
+ if (ret)
+ goto err;
+
+ st->channels[channel].nr = channel;
+ st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
+ AD7124_CHANNEL_AINM(ain[1]);
+
+ cfg->bipolar = of_property_read_bool(child, "bipolar");
+
+ ret = of_property_read_u32(child, "adi,reference-select", &tmp);
+ if (ret)
+ cfg->refsel = AD7124_INT_REF;
+ else
+ cfg->refsel = tmp;
+
+ cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive");
+ cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative");
+
+ chan[channel] = ad7124_channel_template;
+ chan[channel].address = channel;
+ chan[channel].scan_index = channel;
+ chan[channel].channel = ain[0];
+ chan[channel].channel2 = ain[1];
+ }
+
+ return 0;
+err:
+ of_node_put(child);
+
+ return ret;
+}
+
+static int ad7124_setup(struct ad7124_state *st)
+{
+ unsigned int fclk, power_mode;
+ int i, ret;
+
+ fclk = clk_get_rate(st->mclk);
+ if (!fclk)
+ return -EINVAL;
+
+ /* The power mode changes the master clock frequency */
+ power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz,
+ ARRAY_SIZE(ad7124_master_clk_freq_hz),
+ fclk);
+ if (fclk != ad7124_master_clk_freq_hz[power_mode]) {
+ ret = clk_set_rate(st->mclk, fclk);
+ if (ret)
+ return ret;
+ }
+
+ /* Set the power mode */
+ st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode);
+ ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&st->cfgs_lock);
+ INIT_KFIFO(st->live_cfgs_fifo);
+ for (i = 0; i < st->num_channels; i++) {
+
+ ret = ad7124_init_config_vref(st, &st->channels[i].cfg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * 9.38 SPS is the minimum output data rate supported
+ * regardless of the selected power mode. Round it up to 10 and
+ * set all channels to this default value.
+ */
+ ad7124_set_channel_odr(st, i, 10);
+ }
+
+ return ret;
+}
+
+static void ad7124_reg_disable(void *r)
+{
+ regulator_disable(r);
+}
+
+static int ad7124_probe(struct spi_device *spi)
+{
+ const struct ad7124_chip_info *info;
+ struct ad7124_state *st;
+ struct iio_dev *indio_dev;
+ int i, ret;
+
+ info = of_device_get_match_data(&spi->dev);
+ if (!info)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->chip_info = info;
+
+ indio_dev->name = st->chip_info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7124_info;
+
+ ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(st->vref); i++) {
+ if (i == AD7124_INT_REF)
+ continue;
+
+ st->vref[i] = devm_regulator_get_optional(&spi->dev,
+ ad7124_ref_names[i]);
+ if (PTR_ERR(st->vref[i]) == -ENODEV)
+ continue;
+ else if (IS_ERR(st->vref[i]))
+ return PTR_ERR(st->vref[i]);
+
+ ret = regulator_enable(st->vref[i]);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable,
+ st->vref[i]);
+ if (ret)
+ return ret;
+ }
+
+ st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
+ if (IS_ERR(st->mclk))
+ return PTR_ERR(st->mclk);
+
+ ret = ad7124_soft_reset(st);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7124_check_chip_id(st);
+ if (ret)
+ return ret;
+
+ ret = ad7124_setup(st);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+
+}
+
+static const struct of_device_id ad7124_of_match[] = {
+ { .compatible = "adi,ad7124-4",
+ .data = &ad7124_chip_info_tbl[ID_AD7124_4], },
+ { .compatible = "adi,ad7124-8",
+ .data = &ad7124_chip_info_tbl[ID_AD7124_8], },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7124_of_match);
+
+static struct spi_driver ad71124_driver = {
+ .driver = {
+ .name = "ad7124",
+ .of_match_table = ad7124_of_match,
+ },
+ .probe = ad7124_probe,
+};
+module_spi_driver(ad71124_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
new file mode 100644
index 000000000..faf680140
--- /dev/null
+++ b/drivers/iio/adc/ad7192.c
@@ -0,0 +1,1119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7190 AD7192 AD7193 AD7195 SPI ADC driver
+ *
+ * Copyright 2011-2015 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+/* Registers */
+#define AD7192_REG_COMM 0 /* Communications Register (WO, 8-bit) */
+#define AD7192_REG_STAT 0 /* Status Register (RO, 8-bit) */
+#define AD7192_REG_MODE 1 /* Mode Register (RW, 24-bit */
+#define AD7192_REG_CONF 2 /* Configuration Register (RW, 24-bit) */
+#define AD7192_REG_DATA 3 /* Data Register (RO, 24/32-bit) */
+#define AD7192_REG_ID 4 /* ID Register (RO, 8-bit) */
+#define AD7192_REG_GPOCON 5 /* GPOCON Register (RO, 8-bit) */
+#define AD7192_REG_OFFSET 6 /* Offset Register (RW, 16-bit */
+ /* (AD7792)/24-bit (AD7192)) */
+#define AD7192_REG_FULLSALE 7 /* Full-Scale Register */
+ /* (RW, 16-bit (AD7792)/24-bit (AD7192)) */
+
+/* Communications Register Bit Designations (AD7192_REG_COMM) */
+#define AD7192_COMM_WEN BIT(7) /* Write Enable */
+#define AD7192_COMM_WRITE 0 /* Write Operation */
+#define AD7192_COMM_READ BIT(6) /* Read Operation */
+#define AD7192_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */
+#define AD7192_COMM_CREAD BIT(2) /* Continuous Read of Data Register */
+
+/* Status Register Bit Designations (AD7192_REG_STAT) */
+#define AD7192_STAT_RDY BIT(7) /* Ready */
+#define AD7192_STAT_ERR BIT(6) /* Error (Overrange, Underrange) */
+#define AD7192_STAT_NOREF BIT(5) /* Error no external reference */
+#define AD7192_STAT_PARITY BIT(4) /* Parity */
+#define AD7192_STAT_CH3 BIT(2) /* Channel 3 */
+#define AD7192_STAT_CH2 BIT(1) /* Channel 2 */
+#define AD7192_STAT_CH1 BIT(0) /* Channel 1 */
+
+/* Mode Register Bit Designations (AD7192_REG_MODE) */
+#define AD7192_MODE_SEL(x) (((x) & 0x7) << 21) /* Operation Mode Select */
+#define AD7192_MODE_SEL_MASK (0x7 << 21) /* Operation Mode Select Mask */
+#define AD7192_MODE_STA(x) (((x) & 0x1) << 20) /* Status Register transmission */
+#define AD7192_MODE_STA_MASK BIT(20) /* Status Register transmission Mask */
+#define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */
+#define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */
+#define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */
+#define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/
+#define AD7192_MODE_SCYCLE BIT(11) /* Single cycle conversion */
+#define AD7192_MODE_REJ60 BIT(10) /* 50/60Hz notch filter */
+#define AD7192_MODE_RATE(x) ((x) & 0x3FF) /* Filter Update Rate Select */
+
+/* Mode Register: AD7192_MODE_SEL options */
+#define AD7192_MODE_CONT 0 /* Continuous Conversion Mode */
+#define AD7192_MODE_SINGLE 1 /* Single Conversion Mode */
+#define AD7192_MODE_IDLE 2 /* Idle Mode */
+#define AD7192_MODE_PWRDN 3 /* Power-Down Mode */
+#define AD7192_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */
+#define AD7192_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */
+#define AD7192_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */
+#define AD7192_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */
+
+/* Mode Register: AD7192_MODE_CLKSRC options */
+#define AD7192_CLK_EXT_MCLK1_2 0 /* External 4.92 MHz Clock connected*/
+ /* from MCLK1 to MCLK2 */
+#define AD7192_CLK_EXT_MCLK2 1 /* External Clock applied to MCLK2 */
+#define AD7192_CLK_INT 2 /* Internal 4.92 MHz Clock not */
+ /* available at the MCLK2 pin */
+#define AD7192_CLK_INT_CO 3 /* Internal 4.92 MHz Clock available*/
+ /* at the MCLK2 pin */
+
+/* Configuration Register Bit Designations (AD7192_REG_CONF) */
+
+#define AD7192_CONF_CHOP BIT(23) /* CHOP enable */
+#define AD7192_CONF_ACX BIT(22) /* AC excitation enable(AD7195 only) */
+#define AD7192_CONF_REFSEL BIT(20) /* REFIN1/REFIN2 Reference Select */
+#define AD7192_CONF_CHAN(x) ((x) << 8) /* Channel select */
+#define AD7192_CONF_CHAN_MASK (0x7FF << 8) /* Channel select mask */
+#define AD7192_CONF_BURN BIT(7) /* Burnout current enable */
+#define AD7192_CONF_REFDET BIT(6) /* Reference detect enable */
+#define AD7192_CONF_BUF BIT(4) /* Buffered Mode Enable */
+#define AD7192_CONF_UNIPOLAR BIT(3) /* Unipolar/Bipolar Enable */
+#define AD7192_CONF_GAIN(x) ((x) & 0x7) /* Gain Select */
+
+#define AD7192_CH_AIN1P_AIN2M BIT(0) /* AIN1(+) - AIN2(-) */
+#define AD7192_CH_AIN3P_AIN4M BIT(1) /* AIN3(+) - AIN4(-) */
+#define AD7192_CH_TEMP BIT(2) /* Temp Sensor */
+#define AD7192_CH_AIN2P_AIN2M BIT(3) /* AIN2(+) - AIN2(-) */
+#define AD7192_CH_AIN1 BIT(4) /* AIN1 - AINCOM */
+#define AD7192_CH_AIN2 BIT(5) /* AIN2 - AINCOM */
+#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */
+#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */
+
+#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */
+#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */
+#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */
+#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */
+#define AD7193_CH_TEMP 0x100 /* Temp senseor */
+#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */
+#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */
+#define AD7193_CH_AIN2 0x402 /* AIN2 - AINCOM */
+#define AD7193_CH_AIN3 0x404 /* AIN3 - AINCOM */
+#define AD7193_CH_AIN4 0x408 /* AIN4 - AINCOM */
+#define AD7193_CH_AIN5 0x410 /* AIN5 - AINCOM */
+#define AD7193_CH_AIN6 0x420 /* AIN6 - AINCOM */
+#define AD7193_CH_AIN7 0x440 /* AIN7 - AINCOM */
+#define AD7193_CH_AIN8 0x480 /* AIN7 - AINCOM */
+#define AD7193_CH_AINCOM 0x600 /* AINCOM - AINCOM */
+
+/* ID Register Bit Designations (AD7192_REG_ID) */
+#define CHIPID_AD7190 0x4
+#define CHIPID_AD7192 0x0
+#define CHIPID_AD7193 0x2
+#define CHIPID_AD7195 0x6
+#define AD7192_ID_MASK 0x0F
+
+/* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */
+#define AD7192_GPOCON_BPDSW BIT(6) /* Bridge power-down switch enable */
+#define AD7192_GPOCON_GP32EN BIT(5) /* Digital Output P3 and P2 enable */
+#define AD7192_GPOCON_GP10EN BIT(4) /* Digital Output P1 and P0 enable */
+#define AD7192_GPOCON_P3DAT BIT(3) /* P3 state */
+#define AD7192_GPOCON_P2DAT BIT(2) /* P2 state */
+#define AD7192_GPOCON_P1DAT BIT(1) /* P1 state */
+#define AD7192_GPOCON_P0DAT BIT(0) /* P0 state */
+
+#define AD7192_EXT_FREQ_MHZ_MIN 2457600
+#define AD7192_EXT_FREQ_MHZ_MAX 5120000
+#define AD7192_INT_FREQ_MHZ 4915200
+
+#define AD7192_NO_SYNC_FILTER 1
+#define AD7192_SYNC3_FILTER 3
+#define AD7192_SYNC4_FILTER 4
+
+/* NOTE:
+ * The AD7190/2/5 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.
+ */
+
+enum {
+ AD7192_SYSCALIB_ZERO_SCALE,
+ AD7192_SYSCALIB_FULL_SCALE,
+};
+
+enum {
+ ID_AD7190,
+ ID_AD7192,
+ ID_AD7193,
+ ID_AD7195,
+};
+
+struct ad7192_chip_info {
+ unsigned int chip_id;
+ const char *name;
+};
+
+struct ad7192_state {
+ const struct ad7192_chip_info *chip_info;
+ struct regulator *avdd;
+ struct regulator *vref;
+ struct clk *mclk;
+ u16 int_vref_mv;
+ u32 fclk;
+ u32 f_order;
+ u32 mode;
+ u32 conf;
+ u32 scale_avail[8][2];
+ u8 gpocon;
+ u8 clock_sel;
+ struct mutex lock; /* protect sensor state */
+ u8 syscalib_mode[8];
+
+ struct ad_sigma_delta sd;
+};
+
+static const char * const ad7192_syscalib_modes[] = {
+ [AD7192_SYSCALIB_ZERO_SCALE] = "zero_scale",
+ [AD7192_SYSCALIB_FULL_SCALE] = "full_scale",
+};
+
+static int ad7192_set_syscalib_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ st->syscalib_mode[chan->channel] = mode;
+
+ return 0;
+}
+
+static int ad7192_get_syscalib_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ return st->syscalib_mode[chan->channel];
+}
+
+static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+ bool sys_calib;
+ int ret, temp;
+
+ ret = kstrtobool(buf, &sys_calib);
+ if (ret)
+ return ret;
+
+ temp = st->syscalib_mode[chan->channel];
+ if (sys_calib) {
+ if (temp == AD7192_SYSCALIB_ZERO_SCALE)
+ ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_ZERO,
+ chan->address);
+ else
+ ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_FULL,
+ chan->address);
+ }
+
+ return ret ? ret : len;
+}
+
+static const struct iio_enum ad7192_syscalib_mode_enum = {
+ .items = ad7192_syscalib_modes,
+ .num_items = ARRAY_SIZE(ad7192_syscalib_modes),
+ .set = ad7192_set_syscalib_mode,
+ .get = ad7192_get_syscalib_mode
+};
+
+static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = {
+ {
+ .name = "sys_calibration",
+ .write = ad7192_write_syscalib,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("sys_calibration_mode", IIO_SEPARATE,
+ &ad7192_syscalib_mode_enum),
+ IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
+ &ad7192_syscalib_mode_enum),
+ {}
+};
+
+static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7192_state, sd);
+}
+
+static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
+
+ st->conf &= ~AD7192_CONF_CHAN_MASK;
+ st->conf |= AD7192_CONF_CHAN(channel);
+
+ return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+}
+
+static int ad7192_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
+
+ st->mode &= ~AD7192_MODE_SEL_MASK;
+ st->mode |= AD7192_MODE_SEL(mode);
+
+ return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+}
+
+static int ad7192_append_status(struct ad_sigma_delta *sd, bool append)
+{
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
+ unsigned int mode = st->mode;
+ int ret;
+
+ mode &= ~AD7192_MODE_STA_MASK;
+ mode |= AD7192_MODE_STA(append);
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, mode);
+ if (ret < 0)
+ return ret;
+
+ st->mode = mode;
+
+ return 0;
+}
+
+static int ad7192_disable_all(struct ad_sigma_delta *sd)
+{
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
+ u32 conf = st->conf;
+ int ret;
+
+ conf &= ~AD7192_CONF_CHAN_MASK;
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, conf);
+ if (ret < 0)
+ return ret;
+
+ st->conf = conf;
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7192_sigma_delta_info = {
+ .set_channel = ad7192_set_channel,
+ .append_status = ad7192_append_status,
+ .disable_all = ad7192_disable_all,
+ .set_mode = ad7192_set_mode,
+ .has_registers = true,
+ .addr_shift = 3,
+ .read_mask = BIT(6),
+ .status_ch_mask = GENMASK(3, 0),
+ .num_slots = 4,
+ .irq_flags = IRQF_TRIGGER_FALLING,
+};
+
+static const struct ad_sd_calib_data ad7192_calib_arr[8] = {
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1},
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN2},
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN3},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN3},
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN4},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN4}
+};
+
+static int ad7192_calibrate_all(struct ad7192_state *st)
+{
+ return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr,
+ ARRAY_SIZE(ad7192_calib_arr));
+}
+
+static inline bool ad7192_valid_external_frequency(u32 freq)
+{
+ return (freq >= AD7192_EXT_FREQ_MHZ_MIN &&
+ freq <= AD7192_EXT_FREQ_MHZ_MAX);
+}
+
+static int ad7192_of_clock_select(struct ad7192_state *st)
+{
+ struct device_node *np = st->sd.spi->dev.of_node;
+ unsigned int clock_sel;
+
+ clock_sel = AD7192_CLK_INT;
+
+ /* use internal clock */
+ if (!st->mclk) {
+ if (of_property_read_bool(np, "adi,int-clock-output-enable"))
+ clock_sel = AD7192_CLK_INT_CO;
+ } else {
+ if (of_property_read_bool(np, "adi,clock-xtal"))
+ clock_sel = AD7192_CLK_EXT_MCLK1_2;
+ else
+ clock_sel = AD7192_CLK_EXT_MCLK2;
+ }
+
+ return clock_sel;
+}
+
+static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+ bool rej60_en, refin2_en;
+ bool buf_en, bipolar, burnout_curr_en;
+ unsigned long long scale_uv;
+ int i, ret, id;
+
+ /* reset the serial interface */
+ ret = ad_sd_reset(&st->sd, 48);
+ if (ret < 0)
+ return ret;
+ usleep_range(500, 1000); /* Wait for at least 500us */
+
+ /* write/read test for device presence */
+ ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id);
+ if (ret)
+ return ret;
+
+ id &= AD7192_ID_MASK;
+
+ if (id != st->chip_info->chip_id)
+ dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
+ id);
+
+ st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
+ AD7192_MODE_CLKSRC(st->clock_sel) |
+ AD7192_MODE_RATE(480);
+
+ st->conf = AD7192_CONF_GAIN(0);
+
+ rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable");
+ if (rej60_en)
+ st->mode |= AD7192_MODE_REJ60;
+
+ refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable");
+ if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195)
+ st->conf |= AD7192_CONF_REFSEL;
+
+ st->conf &= ~AD7192_CONF_CHOP;
+ st->f_order = AD7192_NO_SYNC_FILTER;
+
+ buf_en = of_property_read_bool(np, "adi,buffer-enable");
+ if (buf_en)
+ st->conf |= AD7192_CONF_BUF;
+
+ bipolar = of_property_read_bool(np, "bipolar");
+ if (!bipolar)
+ st->conf |= AD7192_CONF_UNIPOLAR;
+
+ burnout_curr_en = of_property_read_bool(np,
+ "adi,burnout-currents-enable");
+ if (burnout_curr_en && buf_en) {
+ st->conf |= AD7192_CONF_BURN;
+ } else if (burnout_curr_en) {
+ dev_warn(&st->sd.spi->dev,
+ "Can't enable burnout currents: see CHOP or buffer\n");
+ }
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ if (ret)
+ return ret;
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+ if (ret)
+ return ret;
+
+ ret = ad7192_calibrate_all(st);
+ if (ret)
+ return ret;
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
+ scale_uv = ((u64)st->int_vref_mv * 100000000)
+ >> (indio_dev->channels[0].scan_type.realbits -
+ ((st->conf & AD7192_CONF_UNIPOLAR) ? 0 : 1));
+ scale_uv >>= i;
+
+ st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10;
+ st->scale_avail[i][0] = scale_uv;
+ }
+
+ return 0;
+}
+
+static ssize_t ad7192_show_ac_excitation(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ return sysfs_emit(buf, "%d\n", !!(st->conf & AD7192_CONF_ACX));
+}
+
+static ssize_t ad7192_show_bridge_switch(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ return sysfs_emit(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW));
+}
+
+static ssize_t ad7192_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ bool val;
+
+ ret = kstrtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ switch ((u32)this_attr->address) {
+ case AD7192_REG_GPOCON:
+ if (val)
+ st->gpocon |= AD7192_GPOCON_BPDSW;
+ else
+ st->gpocon &= ~AD7192_GPOCON_BPDSW;
+
+ ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon);
+ break;
+ case AD7192_REG_CONF:
+ if (val)
+ st->conf |= AD7192_CONF_ACX;
+ else
+ st->conf &= ~AD7192_CONF_ACX;
+
+ ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret ? ret : len;
+}
+
+static void ad7192_get_available_filter_freq(struct ad7192_state *st,
+ int *freq)
+{
+ unsigned int fadc;
+
+ /* Formulas for filter at page 25 of the datasheet */
+ fadc = DIV_ROUND_CLOSEST(st->fclk,
+ AD7192_SYNC4_FILTER * AD7192_MODE_RATE(st->mode));
+ freq[0] = DIV_ROUND_CLOSEST(fadc * 240, 1024);
+
+ fadc = DIV_ROUND_CLOSEST(st->fclk,
+ AD7192_SYNC3_FILTER * AD7192_MODE_RATE(st->mode));
+ freq[1] = DIV_ROUND_CLOSEST(fadc * 240, 1024);
+
+ fadc = DIV_ROUND_CLOSEST(st->fclk, AD7192_MODE_RATE(st->mode));
+ freq[2] = DIV_ROUND_CLOSEST(fadc * 230, 1024);
+ freq[3] = DIV_ROUND_CLOSEST(fadc * 272, 1024);
+}
+
+static ssize_t ad7192_show_filter_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+ unsigned int freq_avail[4], i;
+ size_t len = 0;
+
+ ad7192_get_available_filter_freq(st, freq_avail);
+
+ for (i = 0; i < ARRAY_SIZE(freq_avail); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%d.%d ", freq_avail[i] / 1000,
+ freq_avail[i] % 1000);
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(filter_low_pass_3db_frequency_available,
+ 0444, ad7192_show_filter_avail, NULL, 0);
+
+static IIO_DEVICE_ATTR(bridge_switch_en, 0644,
+ ad7192_show_bridge_switch, ad7192_set,
+ AD7192_REG_GPOCON);
+
+static IIO_DEVICE_ATTR(ac_excitation_en, 0644,
+ ad7192_show_ac_excitation, ad7192_set,
+ AD7192_REG_CONF);
+
+static struct attribute *ad7192_attributes[] = {
+ &iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr,
+ &iio_dev_attr_bridge_switch_en.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7192_attribute_group = {
+ .attrs = ad7192_attributes,
+};
+
+static struct attribute *ad7195_attributes[] = {
+ &iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr,
+ &iio_dev_attr_bridge_switch_en.dev_attr.attr,
+ &iio_dev_attr_ac_excitation_en.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7195_attribute_group = {
+ .attrs = ad7195_attributes,
+};
+
+static unsigned int ad7192_get_temp_scale(bool unipolar)
+{
+ return unipolar ? 2815 * 2 : 2815;
+}
+
+static int ad7192_set_3db_filter_freq(struct ad7192_state *st,
+ int val, int val2)
+{
+ int freq_avail[4], i, ret, freq;
+ unsigned int diff_new, diff_old;
+ int idx = 0;
+
+ diff_old = U32_MAX;
+ freq = val * 1000 + val2;
+
+ ad7192_get_available_filter_freq(st, freq_avail);
+
+ for (i = 0; i < ARRAY_SIZE(freq_avail); i++) {
+ diff_new = abs(freq - freq_avail[i]);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ switch (idx) {
+ case 0:
+ st->f_order = AD7192_SYNC4_FILTER;
+ st->mode &= ~AD7192_MODE_SINC3;
+
+ st->conf |= AD7192_CONF_CHOP;
+ break;
+ case 1:
+ st->f_order = AD7192_SYNC3_FILTER;
+ st->mode |= AD7192_MODE_SINC3;
+
+ st->conf |= AD7192_CONF_CHOP;
+ break;
+ case 2:
+ st->f_order = AD7192_NO_SYNC_FILTER;
+ st->mode &= ~AD7192_MODE_SINC3;
+
+ st->conf &= ~AD7192_CONF_CHOP;
+ break;
+ case 3:
+ st->f_order = AD7192_NO_SYNC_FILTER;
+ st->mode |= AD7192_MODE_SINC3;
+
+ st->conf &= ~AD7192_CONF_CHOP;
+ break;
+ }
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ if (ret < 0)
+ return ret;
+
+ return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+}
+
+static int ad7192_get_3db_filter_freq(struct ad7192_state *st)
+{
+ unsigned int fadc;
+
+ fadc = DIV_ROUND_CLOSEST(st->fclk,
+ st->f_order * AD7192_MODE_RATE(st->mode));
+
+ if (st->conf & AD7192_CONF_CHOP)
+ return DIV_ROUND_CLOSEST(fadc * 240, 1024);
+ if (st->mode & AD7192_MODE_SINC3)
+ return DIV_ROUND_CLOSEST(fadc * 272, 1024);
+ else
+ return DIV_ROUND_CLOSEST(fadc * 230, 1024);
+}
+
+static int ad7192_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+ bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR);
+
+ 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:
+ mutex_lock(&st->lock);
+ *val = st->scale_avail[AD7192_CONF_GAIN(st->conf)][0];
+ *val2 = st->scale_avail[AD7192_CONF_GAIN(st->conf)][1];
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 1000000000 / ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP)
+ *val -= 273 * ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->fclk /
+ (st->f_order * 1024 * AD7192_MODE_RATE(st->mode));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *val = ad7192_get_3db_filter_freq(st);
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+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;
+ unsigned int tmp;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = -EINVAL;
+ mutex_lock(&st->lock);
+ 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(-1);
+ st->conf |= AD7192_CONF_GAIN(i);
+ if (tmp == st->conf)
+ break;
+ ad_sd_write_reg(&st->sd, AD7192_REG_CONF,
+ 3, st->conf);
+ ad7192_calibrate_all(st);
+ break;
+ }
+ mutex_unlock(&st->lock);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!val) {
+ ret = -EINVAL;
+ break;
+ }
+
+ div = st->fclk / (val * st->f_order * 1024);
+ if (div < 1 || div > 1023) {
+ ret = -EINVAL;
+ break;
+ }
+
+ st->mode &= ~AD7192_MODE_RATE(-1);
+ st->mode |= AD7192_MODE_RATE(div);
+ ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ break;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static int ad7192_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;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7192_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ 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 IIO_AVAIL_LIST;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7192_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+ u32 conf = st->conf;
+ int ret;
+ int i;
+
+ conf &= ~AD7192_CONF_CHAN_MASK;
+ for_each_set_bit(i, scan_mask, 8)
+ conf |= AD7192_CONF_CHAN(i);
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, conf);
+ if (ret < 0)
+ return ret;
+
+ st->conf = conf;
+
+ return 0;
+}
+
+static const struct iio_info ad7192_info = {
+ .read_raw = ad7192_read_raw,
+ .write_raw = ad7192_write_raw,
+ .write_raw_get_fmt = ad7192_write_raw_get_fmt,
+ .read_avail = ad7192_read_avail,
+ .attrs = &ad7192_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .update_scan_mode = ad7192_update_scan_mode,
+};
+
+static const struct iio_info ad7195_info = {
+ .read_raw = ad7192_read_raw,
+ .write_raw = ad7192_write_raw,
+ .write_raw_get_fmt = ad7192_write_raw_get_fmt,
+ .read_avail = ad7192_read_avail,
+ .attrs = &ad7195_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .update_scan_mode = ad7192_update_scan_mode,
+};
+
+#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _extend_name, \
+ _type, _mask_type_av, _ext_info) \
+ { \
+ .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 = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .info_mask_shared_by_type_available = (_mask_type_av), \
+ .ext_info = (_ext_info), \
+ .scan_index = (_si), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 24, \
+ .storagebits = 32, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \
+ __AD719x_CHANNEL(_si, _channel1, _channel2, _address, NULL, \
+ IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE), \
+ ad7192_calibsys_ext_info)
+
+#define AD719x_CHANNEL(_si, _channel1, _address) \
+ __AD719x_CHANNEL(_si, _channel1, -1, _address, NULL, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info)
+
+#define AD719x_TEMP_CHANNEL(_si, _address) \
+ __AD719x_CHANNEL(_si, 0, -1, _address, NULL, IIO_TEMP, 0, NULL)
+
+static const struct iio_chan_spec ad7192_channels[] = {
+ AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M),
+ AD719x_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M),
+ AD719x_TEMP_CHANNEL(2, AD7192_CH_TEMP),
+ AD719x_DIFF_CHANNEL(3, 2, 2, AD7192_CH_AIN2P_AIN2M),
+ AD719x_CHANNEL(4, 1, AD7192_CH_AIN1),
+ AD719x_CHANNEL(5, 2, AD7192_CH_AIN2),
+ AD719x_CHANNEL(6, 3, AD7192_CH_AIN3),
+ AD719x_CHANNEL(7, 4, AD7192_CH_AIN4),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const struct iio_chan_spec ad7193_channels[] = {
+ AD719x_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M),
+ AD719x_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M),
+ AD719x_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M),
+ AD719x_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M),
+ AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP),
+ AD719x_DIFF_CHANNEL(5, 2, 2, AD7193_CH_AIN2P_AIN2M),
+ AD719x_CHANNEL(6, 1, AD7193_CH_AIN1),
+ AD719x_CHANNEL(7, 2, AD7193_CH_AIN2),
+ AD719x_CHANNEL(8, 3, AD7193_CH_AIN3),
+ AD719x_CHANNEL(9, 4, AD7193_CH_AIN4),
+ AD719x_CHANNEL(10, 5, AD7193_CH_AIN5),
+ AD719x_CHANNEL(11, 6, AD7193_CH_AIN6),
+ AD719x_CHANNEL(12, 7, AD7193_CH_AIN7),
+ AD719x_CHANNEL(13, 8, AD7193_CH_AIN8),
+ IIO_CHAN_SOFT_TIMESTAMP(14),
+};
+
+static const struct ad7192_chip_info ad7192_chip_info_tbl[] = {
+ [ID_AD7190] = {
+ .chip_id = CHIPID_AD7190,
+ .name = "ad7190",
+ },
+ [ID_AD7192] = {
+ .chip_id = CHIPID_AD7192,
+ .name = "ad7192",
+ },
+ [ID_AD7193] = {
+ .chip_id = CHIPID_AD7193,
+ .name = "ad7193",
+ },
+ [ID_AD7195] = {
+ .chip_id = CHIPID_AD7195,
+ .name = "ad7195",
+ },
+};
+
+static int ad7192_channels_config(struct iio_dev *indio_dev)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ switch (st->chip_info->chip_id) {
+ case CHIPID_AD7193:
+ indio_dev->channels = ad7193_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7193_channels);
+ break;
+ default:
+ indio_dev->channels = ad7192_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7192_channels);
+ break;
+ }
+
+ return 0;
+}
+
+static void ad7192_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static void ad7192_clk_disable(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static int ad7192_probe(struct spi_device *spi)
+{
+ struct ad7192_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ mutex_init(&st->lock);
+
+ st->avdd = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->avdd))
+ return PTR_ERR(st->avdd);
+
+ ret = regulator_enable(st->avdd);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7192_reg_disable, st->avdd);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable(&spi->dev, "dvdd");
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to enable specified DVdd supply\n");
+
+ st->vref = devm_regulator_get_optional(&spi->dev, "vref");
+ if (IS_ERR(st->vref)) {
+ if (PTR_ERR(st->vref) != -ENODEV)
+ return PTR_ERR(st->vref);
+
+ ret = regulator_get_voltage(st->avdd);
+ if (ret < 0)
+ return dev_err_probe(&spi->dev, ret,
+ "Device tree error, AVdd voltage undefined\n");
+ } else {
+ ret = regulator_enable(st->vref);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified Vref supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7192_reg_disable, st->vref);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->vref);
+ if (ret < 0)
+ return dev_err_probe(&spi->dev, ret,
+ "Device tree error, Vref voltage undefined\n");
+ }
+ st->int_vref_mv = ret / 1000;
+
+ st->chip_info = of_device_get_match_data(&spi->dev);
+ indio_dev->name = st->chip_info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad7192_channels_config(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ if (st->chip_info->chip_id == CHIPID_AD7195)
+ indio_dev->info = &ad7195_info;
+ else
+ indio_dev->info = &ad7192_info;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ st->fclk = AD7192_INT_FREQ_MHZ;
+
+ st->mclk = devm_clk_get_optional(&spi->dev, "mclk");
+ if (IS_ERR(st->mclk))
+ return PTR_ERR(st->mclk);
+
+ st->clock_sel = ad7192_of_clock_select(st);
+
+ if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
+ st->clock_sel == AD7192_CLK_EXT_MCLK2) {
+ ret = clk_prepare_enable(st->mclk);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7192_clk_disable,
+ st->mclk);
+ if (ret)
+ return ret;
+
+ st->fclk = clk_get_rate(st->mclk);
+ if (!ad7192_valid_external_frequency(st->fclk)) {
+ dev_err(&spi->dev,
+ "External clock frequency out of bounds\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = ad7192_setup(indio_dev, spi->dev.of_node);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad7192_of_match[] = {
+ { .compatible = "adi,ad7190", .data = &ad7192_chip_info_tbl[ID_AD7190] },
+ { .compatible = "adi,ad7192", .data = &ad7192_chip_info_tbl[ID_AD7192] },
+ { .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] },
+ { .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad7192_of_match);
+
+static struct spi_driver ad7192_driver = {
+ .driver = {
+ .name = "ad7192",
+ .of_match_table = ad7192_of_match,
+ },
+ .probe = ad7192_probe,
+};
+module_spi_driver(ad7192_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7193, AD7195 ADC");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
new file mode 100644
index 000000000..468c2656d
--- /dev/null
+++ b/drivers/iio/adc/ad7266.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7266/65 SPI ADC driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+
+#include <linux/interrupt.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/platform_data/ad7266.h>
+
+struct ad7266_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned long vref_mv;
+
+ struct spi_transfer single_xfer[3];
+ struct spi_message single_msg;
+
+ enum ad7266_range range;
+ enum ad7266_mode mode;
+ bool fixed_addr;
+ struct gpio_desc *gpios[3];
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * The buffer needs to be large enough to hold two samples (4 bytes) and
+ * the naturally aligned timestamp (8 bytes).
+ */
+ struct {
+ __be16 sample[2];
+ s64 timestamp;
+ } data __aligned(IIO_DMA_MINALIGN);
+};
+
+static int ad7266_wakeup(struct ad7266_state *st)
+{
+ /* Any read with >= 2 bytes will wake the device */
+ return spi_read(st->spi, &st->data.sample[0], 2);
+}
+
+static int ad7266_powerdown(struct ad7266_state *st)
+{
+ /* Any read with < 2 bytes will powerdown the device */
+ return spi_read(st->spi, &st->data.sample[0], 1);
+}
+
+static int ad7266_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ return ad7266_wakeup(st);
+}
+
+static int ad7266_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ return ad7266_powerdown(st);
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .preenable = &ad7266_preenable,
+ .postdisable = &ad7266_postdisable,
+};
+
+static irqreturn_t ad7266_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7266_state *st = iio_priv(indio_dev);
+ 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);
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7266_select_input(struct ad7266_state *st, unsigned int nr)
+{
+ unsigned int i;
+
+ if (st->fixed_addr)
+ return;
+
+ switch (st->mode) {
+ case AD7266_MODE_SINGLE_ENDED:
+ nr >>= 1;
+ break;
+ case AD7266_MODE_PSEUDO_DIFF:
+ nr |= 1;
+ break;
+ case AD7266_MODE_DIFF:
+ nr &= ~1;
+ break;
+ }
+
+ for (i = 0; i < 3; ++i)
+ gpiod_set_value(st->gpios[i], (bool)(nr & BIT(i)));
+}
+
+static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength);
+
+ ad7266_select_input(st, nr);
+
+ return 0;
+}
+
+static int ad7266_read_single(struct ad7266_state *st, int *val,
+ unsigned int address)
+{
+ int ret;
+
+ ad7266_select_input(st, address);
+
+ ret = spi_sync(st->spi, &st->single_msg);
+ *val = be16_to_cpu(st->data.sample[address % 2]);
+
+ return ret;
+}
+
+static int ad7266_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long m)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ unsigned long scale_mv;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = ad7266_read_single(st, val, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+
+ *val = (*val >> 2) & 0xfff;
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(*val,
+ chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_mv = st->vref_mv;
+ if (st->mode == AD7266_MODE_DIFF)
+ scale_mv *= 2;
+ if (st->range == AD7266_RANGE_2VREF)
+ scale_mv *= 2;
+
+ *val = scale_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (st->range == AD7266_RANGE_2VREF &&
+ st->mode != AD7266_MODE_DIFF)
+ *val = 2048;
+ else
+ *val = 0;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+#define AD7266_CHAN(_chan, _sign) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan), \
+ .address = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = (_chan), \
+ .scan_type = { \
+ .sign = (_sign), \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 2, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_##_name[] = { \
+ AD7266_CHAN(0, (_sign)), \
+ AD7266_CHAN(1, (_sign)), \
+ AD7266_CHAN(2, (_sign)), \
+ AD7266_CHAN(3, (_sign)), \
+ AD7266_CHAN(4, (_sign)), \
+ AD7266_CHAN(5, (_sign)), \
+ AD7266_CHAN(6, (_sign)), \
+ AD7266_CHAN(7, (_sign)), \
+ AD7266_CHAN(8, (_sign)), \
+ AD7266_CHAN(9, (_sign)), \
+ AD7266_CHAN(10, (_sign)), \
+ AD7266_CHAN(11, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(13), \
+}
+
+#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \
+ AD7266_CHAN(0, (_sign)), \
+ AD7266_CHAN(1, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(2), \
+}
+
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u');
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's');
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u');
+static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's');
+
+#define AD7266_CHAN_DIFF(_chan, _sign) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan) * 2, \
+ .channel2 = (_chan) * 2 + 1, \
+ .address = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = (_chan), \
+ .scan_type = { \
+ .sign = _sign, \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 2, \
+ .endianness = IIO_BE, \
+ }, \
+ .differential = 1, \
+}
+
+#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \
+ AD7266_CHAN_DIFF(0, (_sign)), \
+ AD7266_CHAN_DIFF(1, (_sign)), \
+ AD7266_CHAN_DIFF(2, (_sign)), \
+ AD7266_CHAN_DIFF(3, (_sign)), \
+ AD7266_CHAN_DIFF(4, (_sign)), \
+ AD7266_CHAN_DIFF(5, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(6), \
+}
+
+static AD7266_DECLARE_DIFF_CHANNELS(s, 's');
+static AD7266_DECLARE_DIFF_CHANNELS(u, 'u');
+
+#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \
+const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \
+ AD7266_CHAN_DIFF(0, (_sign)), \
+ AD7266_CHAN_DIFF(1, (_sign)), \
+ IIO_CHAN_SOFT_TIMESTAMP(2), \
+}
+
+static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's');
+static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u');
+
+static const struct iio_info ad7266_info = {
+ .read_raw = &ad7266_read_raw,
+ .update_scan_mode = &ad7266_update_scan_mode,
+};
+
+static const unsigned long ad7266_available_scan_masks[] = {
+ 0x003,
+ 0x00c,
+ 0x030,
+ 0x0c0,
+ 0x300,
+ 0xc00,
+ 0x000,
+};
+
+static const unsigned long ad7266_available_scan_masks_diff[] = {
+ 0x003,
+ 0x00c,
+ 0x030,
+ 0x000,
+};
+
+static const unsigned long ad7266_available_scan_masks_fixed[] = {
+ 0x003,
+ 0x000,
+};
+
+struct ad7266_chan_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const unsigned long *scan_masks;
+};
+
+#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \
+ (((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0))
+
+static const struct ad7266_chan_info ad7266_chan_infos[] = {
+ [AD7266_CHAN_INFO_INDEX(0, 0, 0)] = {
+ .channels = ad7266_channels_u,
+ .num_channels = ARRAY_SIZE(ad7266_channels_u),
+ .scan_masks = ad7266_available_scan_masks,
+ },
+ [AD7266_CHAN_INFO_INDEX(0, 0, 1)] = {
+ .channels = ad7266_channels_u_fixed,
+ .num_channels = ARRAY_SIZE(ad7266_channels_u_fixed),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+ [AD7266_CHAN_INFO_INDEX(0, 1, 0)] = {
+ .channels = ad7266_channels_s,
+ .num_channels = ARRAY_SIZE(ad7266_channels_s),
+ .scan_masks = ad7266_available_scan_masks,
+ },
+ [AD7266_CHAN_INFO_INDEX(0, 1, 1)] = {
+ .channels = ad7266_channels_s_fixed,
+ .num_channels = ARRAY_SIZE(ad7266_channels_s_fixed),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 0, 0)] = {
+ .channels = ad7266_channels_diff_u,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_u),
+ .scan_masks = ad7266_available_scan_masks_diff,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 0, 1)] = {
+ .channels = ad7266_channels_diff_fixed_u,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 1, 0)] = {
+ .channels = ad7266_channels_diff_s,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_s),
+ .scan_masks = ad7266_available_scan_masks_diff,
+ },
+ [AD7266_CHAN_INFO_INDEX(1, 1, 1)] = {
+ .channels = ad7266_channels_diff_fixed_s,
+ .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s),
+ .scan_masks = ad7266_available_scan_masks_fixed,
+ },
+};
+
+static void ad7266_init_channels(struct iio_dev *indio_dev)
+{
+ struct ad7266_state *st = iio_priv(indio_dev);
+ bool is_differential, is_signed;
+ const struct ad7266_chan_info *chan_info;
+ int i;
+
+ is_differential = st->mode != AD7266_MODE_SINGLE_ENDED;
+ is_signed = (st->range == AD7266_RANGE_2VREF) |
+ (st->mode == AD7266_MODE_DIFF);
+
+ i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr);
+ chan_info = &ad7266_chan_infos[i];
+
+ indio_dev->channels = chan_info->channels;
+ indio_dev->num_channels = chan_info->num_channels;
+ indio_dev->available_scan_masks = chan_info->scan_masks;
+ indio_dev->masklength = chan_info->num_channels - 1;
+}
+
+static const char * const ad7266_gpio_labels[] = {
+ "ad0", "ad1", "ad2",
+};
+
+static void ad7266_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int ad7266_probe(struct spi_device *spi)
+{
+ struct ad7266_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad7266_state *st;
+ unsigned int i;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7266_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ return ret;
+
+ st->vref_mv = ret / 1000;
+ } else {
+ /* Any other error indicates that the regulator does exist */
+ if (PTR_ERR(st->reg) != -ENODEV)
+ return PTR_ERR(st->reg);
+ /* Use internal reference */
+ st->vref_mv = 2500;
+ }
+
+ if (pdata) {
+ st->fixed_addr = pdata->fixed_addr;
+ st->mode = pdata->mode;
+ st->range = pdata->range;
+
+ if (!st->fixed_addr) {
+ for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
+ st->gpios[i] = devm_gpiod_get(&spi->dev,
+ ad7266_gpio_labels[i],
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpios[i])) {
+ ret = PTR_ERR(st->gpios[i]);
+ return ret;
+ }
+ }
+ }
+ } else {
+ st->fixed_addr = true;
+ st->range = AD7266_RANGE_VREF;
+ st->mode = AD7266_MODE_DIFF;
+ }
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7266_info;
+
+ ad7266_init_channels(indio_dev);
+
+ /* wakeup */
+ st->single_xfer[0].rx_buf = &st->data.sample[0];
+ st->single_xfer[0].len = 2;
+ st->single_xfer[0].cs_change = 1;
+ /* conversion */
+ st->single_xfer[1].rx_buf = st->data.sample;
+ st->single_xfer[1].len = 4;
+ st->single_xfer[1].cs_change = 1;
+ /* powerdown */
+ st->single_xfer[2].tx_buf = &st->data.sample[0];
+ st->single_xfer[2].len = 1;
+
+ spi_message_init(&st->single_msg);
+ spi_message_add_tail(&st->single_xfer[0], &st->single_msg);
+ spi_message_add_tail(&st->single_xfer[1], &st->single_msg);
+ spi_message_add_tail(&st->single_xfer[2], &st->single_msg);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, &iio_pollfunc_store_time,
+ &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7266_id[] = {
+ {"ad7265", 0},
+ {"ad7266", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7266_id);
+
+static struct spi_driver ad7266_driver = {
+ .driver = {
+ .name = "ad7266",
+ },
+ .probe = ad7266_probe,
+ .id_table = ad7266_id,
+};
+module_spi_driver(ad7266_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
new file mode 100644
index 000000000..d4a4e15c8
--- /dev/null
+++ b/drivers/iio/adc/ad7280a.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7280A Lithium Ion Battery Monitoring System
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+
+/* Registers */
+
+#define AD7280A_CELL_VOLTAGE_1_REG 0x0 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_2_REG 0x1 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_3_REG 0x2 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_4_REG 0x3 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_5_REG 0x4 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_6_REG 0x5 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_1_REG 0x6 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_2_REG 0x7 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_3_REG 0x8 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_4_REG 0x9 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_5_REG 0xA /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_6_REG 0xB /* D11 to D0, Read only */
+#define AD7280A_SELF_TEST_REG 0xC /* D11 to D0, Read only */
+
+#define AD7280A_CTRL_HB_REG 0xD /* D15 to D8, Read/write */
+#define AD7280A_CTRL_HB_CONV_INPUT_MSK GENMASK(7, 6)
+#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_5 1
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL 2
+#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST 3
+#define AD7280A_CTRL_HB_CONV_RREAD_MSK GENMASK(5, 4)
+#define AD7280A_CTRL_HB_CONV_RREAD_ALL 0
+#define AD7280A_CTRL_HB_CONV_RREAD_6CELL_AUX1_3_5 1
+#define AD7280A_CTRL_HB_CONV_RREAD_6CELL 2
+#define AD7280A_CTRL_HB_CONV_RREAD_NO 3
+#define AD7280A_CTRL_HB_CONV_START_MSK BIT(3)
+#define AD7280A_CTRL_HB_CONV_START_CNVST 0
+#define AD7280A_CTRL_HB_CONV_START_CS 1
+#define AD7280A_CTRL_HB_CONV_AVG_MSK GENMASK(2, 1)
+#define AD7280A_CTRL_HB_CONV_AVG_DIS 0
+#define AD7280A_CTRL_HB_CONV_AVG_2 1
+#define AD7280A_CTRL_HB_CONV_AVG_4 2
+#define AD7280A_CTRL_HB_CONV_AVG_8 3
+#define AD7280A_CTRL_HB_PWRDN_SW BIT(0)
+
+#define AD7280A_CTRL_LB_REG 0xE /* D7 to D0, Read/write */
+#define AD7280A_CTRL_LB_SWRST_MSK BIT(7)
+#define AD7280A_CTRL_LB_ACQ_TIME_MSK GENMASK(6, 5)
+#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0
+#define AD7280A_CTRL_LB_ACQ_TIME_800ns 1
+#define AD7280A_CTRL_LB_ACQ_TIME_1200ns 2
+#define AD7280A_CTRL_LB_ACQ_TIME_1600ns 3
+#define AD7280A_CTRL_LB_MUST_SET BIT(4)
+#define AD7280A_CTRL_LB_THERMISTOR_MSK BIT(3)
+#define AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK BIT(2)
+#define AD7280A_CTRL_LB_INC_DEV_ADDR_MSK BIT(1)
+#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK BIT(0)
+
+#define AD7280A_CELL_OVERVOLTAGE_REG 0xF /* D7 to D0, Read/write */
+#define AD7280A_CELL_UNDERVOLTAGE_REG 0x10 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_OVERVOLTAGE_REG 0x11 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_UNDERVOLTAGE_REG 0x12 /* D7 to D0, Read/write */
+
+#define AD7280A_ALERT_REG 0x13 /* D7 to D0, Read/write */
+#define AD7280A_ALERT_REMOVE_MSK GENMASK(3, 0)
+#define AD7280A_ALERT_REMOVE_AUX5 BIT(0)
+#define AD7280A_ALERT_REMOVE_AUX3_AUX5 BIT(1)
+#define AD7280A_ALERT_REMOVE_VIN5 BIT(2)
+#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3)
+#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6)
+#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6))
+
+#define AD7280A_CELL_BALANCE_REG 0x14 /* D7 to D0, Read/write */
+#define AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK GENMASK(7, 2)
+#define AD7280A_CB1_TIMER_REG 0x15 /* D7 to D0, Read/write */
+#define AD7280A_CB_TIMER_VAL_MSK GENMASK(7, 3)
+#define AD7280A_CB2_TIMER_REG 0x16 /* D7 to D0, Read/write */
+#define AD7280A_CB3_TIMER_REG 0x17 /* D7 to D0, Read/write */
+#define AD7280A_CB4_TIMER_REG 0x18 /* D7 to D0, Read/write */
+#define AD7280A_CB5_TIMER_REG 0x19 /* D7 to D0, Read/write */
+#define AD7280A_CB6_TIMER_REG 0x1A /* D7 to D0, Read/write */
+#define AD7280A_PD_TIMER_REG 0x1B /* D7 to D0, Read/write */
+#define AD7280A_READ_REG 0x1C /* D7 to D0, Read/write */
+#define AD7280A_READ_ADDR_MSK GENMASK(7, 2)
+#define AD7280A_CNVST_CTRL_REG 0x1D /* D7 to D0, Read/write */
+
+/* Transfer fields */
+#define AD7280A_TRANS_WRITE_DEVADDR_MSK GENMASK(31, 27)
+#define AD7280A_TRANS_WRITE_ADDR_MSK GENMASK(26, 21)
+#define AD7280A_TRANS_WRITE_VAL_MSK GENMASK(20, 13)
+#define AD7280A_TRANS_WRITE_ALL_MSK BIT(12)
+#define AD7280A_TRANS_WRITE_CRC_MSK GENMASK(10, 3)
+#define AD7280A_TRANS_WRITE_RES_PATTERN 0x2
+
+/* Layouts differ for channel vs other registers */
+#define AD7280A_TRANS_READ_DEVADDR_MSK GENMASK(31, 27)
+#define AD7280A_TRANS_READ_CONV_CHANADDR_MSK GENMASK(26, 23)
+#define AD7280A_TRANS_READ_CONV_DATA_MSK GENMASK(22, 11)
+#define AD7280A_TRANS_READ_REG_REGADDR_MSK GENMASK(26, 21)
+#define AD7280A_TRANS_READ_REG_DATA_MSK GENMASK(20, 13)
+#define AD7280A_TRANS_READ_WRITE_ACK_MSK BIT(10)
+#define AD7280A_TRANS_READ_CRC_MSK GENMASK(9, 2)
+
+/* Magic value used to indicate this special case */
+#define AD7280A_ALL_CELLS (0xAD << 16)
+
+#define AD7280A_MAX_SPI_CLK_HZ 700000 /* < 1MHz */
+#define AD7280A_MAX_CHAIN 8
+#define AD7280A_CELLS_PER_DEV 6
+#define AD7280A_BITS 12
+#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6_REG - \
+ AD7280A_CELL_VOLTAGE_1_REG + 1)
+
+#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
+ (c))
+#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) (((d) * AD7280A_CELLS_PER_DEV) + \
+ (c) - AD7280A_CELLS_PER_DEV)
+
+#define AD7280A_DEVADDR_MASTER 0
+#define AD7280A_DEVADDR_ALL 0x1F
+
+static const unsigned short ad7280a_n_avg[4] = {1, 2, 4, 8};
+static const unsigned short ad7280a_t_acq_ns[4] = {470, 1030, 1510, 1945};
+
+/* 5-bit device address is sent LSB first */
+static unsigned int ad7280a_devaddr(unsigned int addr)
+{
+ return ((addr & 0x1) << 4) |
+ ((addr & 0x2) << 2) |
+ (addr & 0x4) |
+ ((addr & 0x8) >> 2) |
+ ((addr & 0x10) >> 4);
+}
+
+/*
+ * During a read a valid write is mandatory.
+ * So writing to the highest available address (Address 0x1F) and setting the
+ * address all parts bit to 0 is recommended.
+ * So the TXVAL is AD7280A_DEVADDR_ALL + CRC
+ */
+#define AD7280A_READ_TXVAL 0xF800030A
+
+/*
+ * AD7280 CRC
+ *
+ * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F
+ */
+#define POLYNOM 0x2F
+
+struct ad7280_state {
+ struct spi_device *spi;
+ struct iio_chan_spec *channels;
+ unsigned int chain_last_alert_ignore;
+ bool thermistor_term_en;
+ int slave_num;
+ int scan_cnt;
+ int readback_delay_us;
+ unsigned char crc_tab[CRC8_TABLE_SIZE];
+ u8 oversampling_ratio;
+ u8 acquisition_time;
+ unsigned char ctrl_lb;
+ unsigned char cell_threshhigh;
+ unsigned char cell_threshlow;
+ unsigned char aux_threshhigh;
+ unsigned char aux_threshlow;
+ unsigned char cb_mask[AD7280A_MAX_CHAIN];
+ struct mutex lock; /* protect sensor state */
+
+ __be32 tx __aligned(IIO_DMA_MINALIGN);
+ __be32 rx;
+};
+
+static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned int val)
+{
+ unsigned char crc;
+
+ crc = crc_tab[val >> 16 & 0xFF];
+ crc = crc_tab[crc ^ (val >> 8 & 0xFF)];
+
+ return crc ^ (val & 0xFF);
+}
+
+static int ad7280_check_crc(struct ad7280_state *st, unsigned int val)
+{
+ unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10);
+
+ if (crc != ((val >> 2) & 0xFF))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * After initiating a conversion sequence we need to wait until the conversion
+ * is done. The delay is typically in the range of 15..30us however depending on
+ * the number of devices in the daisy chain, the number of averages taken,
+ * conversion delays and acquisition time options it may take up to 250us, in
+ * this case we better sleep instead of busy wait.
+ */
+
+static void ad7280_delay(struct ad7280_state *st)
+{
+ if (st->readback_delay_us < 50)
+ udelay(st->readback_delay_us);
+ else
+ usleep_range(250, 500);
+}
+
+static int __ad7280_read32(struct ad7280_state *st, unsigned int *val)
+{
+ int ret;
+ struct spi_transfer t = {
+ .tx_buf = &st->tx,
+ .rx_buf = &st->rx,
+ .len = sizeof(st->tx),
+ };
+
+ st->tx = cpu_to_be32(AD7280A_READ_TXVAL);
+
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(st->rx);
+
+ return 0;
+}
+
+static int ad7280_write(struct ad7280_state *st, unsigned int devaddr,
+ unsigned int addr, bool all, unsigned int val)
+{
+ unsigned int reg = FIELD_PREP(AD7280A_TRANS_WRITE_DEVADDR_MSK, devaddr) |
+ FIELD_PREP(AD7280A_TRANS_WRITE_ADDR_MSK, addr) |
+ FIELD_PREP(AD7280A_TRANS_WRITE_VAL_MSK, val) |
+ FIELD_PREP(AD7280A_TRANS_WRITE_ALL_MSK, all);
+
+ reg |= FIELD_PREP(AD7280A_TRANS_WRITE_CRC_MSK,
+ ad7280_calc_crc8(st->crc_tab, reg >> 11));
+ /* Reserved b010 pattern not included crc calc */
+ reg |= AD7280A_TRANS_WRITE_RES_PATTERN;
+
+ st->tx = cpu_to_be32(reg);
+
+ return spi_write(st->spi, &st->tx, sizeof(st->tx));
+}
+
+static int ad7280_read_reg(struct ad7280_state *st, unsigned int devaddr,
+ unsigned int addr)
+{
+ int ret;
+ unsigned int tmp;
+
+ /* turns off the read operation on all parts */
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_NO) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ /* turns on the read operation on the addressed part */
+ ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ /* Set register address on the part to be read from */
+ ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, addr));
+ if (ret)
+ return ret;
+
+ ret = __ad7280_read32(st, &tmp);
+ if (ret)
+ return ret;
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) ||
+ (FIELD_GET(AD7280A_TRANS_READ_REG_REGADDR_MSK, tmp) != addr))
+ return -EFAULT;
+
+ return FIELD_GET(AD7280A_TRANS_READ_REG_DATA_MSK, tmp);
+}
+
+static int ad7280_read_channel(struct ad7280_state *st, unsigned int devaddr,
+ unsigned int addr)
+{
+ int ret;
+ unsigned int tmp;
+
+ ret = ad7280_write(st, devaddr, AD7280A_READ_REG, 0,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, addr));
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_NO) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, devaddr, AD7280A_CTRL_HB_REG, 0,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK,
+ AD7280A_CTRL_HB_CONV_START_CS) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ ad7280_delay(st);
+
+ ret = __ad7280_read32(st, &tmp);
+ if (ret)
+ return ret;
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if ((FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, tmp) != devaddr) ||
+ (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) != addr))
+ return -EFAULT;
+
+ return FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp);
+}
+
+static int ad7280_read_all_channels(struct ad7280_state *st, unsigned int cnt,
+ unsigned int *array)
+{
+ int i, ret;
+ unsigned int tmp, sum = 0;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1,
+ AD7280A_CELL_VOLTAGE_1_REG << 2);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_INPUT_MSK,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_RREAD_MSK,
+ AD7280A_CTRL_HB_CONV_RREAD_ALL) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_START_MSK,
+ AD7280A_CTRL_HB_CONV_START_CS) |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK,
+ st->oversampling_ratio));
+ if (ret)
+ return ret;
+
+ ad7280_delay(st);
+
+ for (i = 0; i < cnt; i++) {
+ ret = __ad7280_read32(st, &tmp);
+ if (ret)
+ return ret;
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if (array)
+ array[i] = tmp;
+ /* only sum cell voltages */
+ if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, tmp) <=
+ AD7280A_CELL_VOLTAGE_6_REG)
+ sum += FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, tmp);
+ }
+
+ return sum;
+}
+
+static void ad7280_sw_power_down(void *data)
+{
+ struct ad7280_state *st = data;
+
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ AD7280A_CTRL_HB_PWRDN_SW |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio));
+}
+
+static int ad7280_chain_setup(struct ad7280_state *st)
+{
+ unsigned int val, n;
+ int ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) |
+ FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) |
+ AD7280A_CTRL_LB_MUST_SET |
+ FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 1) |
+ st->ctrl_lb);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_LB_REG, 1,
+ FIELD_PREP(AD7280A_CTRL_LB_DAISY_CHAIN_RB_MSK, 1) |
+ FIELD_PREP(AD7280A_CTRL_LB_LOCK_DEV_ADDR_MSK, 1) |
+ AD7280A_CTRL_LB_MUST_SET |
+ FIELD_PREP(AD7280A_CTRL_LB_SWRST_MSK, 0) |
+ st->ctrl_lb);
+ if (ret)
+ goto error_power_down;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ_REG, 1,
+ FIELD_PREP(AD7280A_READ_ADDR_MSK, AD7280A_CTRL_LB_REG));
+ if (ret)
+ goto error_power_down;
+
+ for (n = 0; n <= AD7280A_MAX_CHAIN; n++) {
+ ret = __ad7280_read32(st, &val);
+ if (ret)
+ goto error_power_down;
+
+ if (val == 0)
+ return n - 1;
+
+ if (ad7280_check_crc(st, val)) {
+ ret = -EIO;
+ goto error_power_down;
+ }
+
+ if (n != ad7280a_devaddr(FIELD_GET(AD7280A_TRANS_READ_DEVADDR_MSK, val))) {
+ ret = -EIO;
+ goto error_power_down;
+ }
+ }
+ ret = -EFAULT;
+
+error_power_down:
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CTRL_HB_REG, 1,
+ AD7280A_CTRL_HB_PWRDN_SW |
+ FIELD_PREP(AD7280A_CTRL_HB_CONV_AVG_MSK, st->oversampling_ratio));
+
+ return ret;
+}
+
+static ssize_t ad7280_show_balance_sw(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+
+ return sysfs_emit(buf, "%d\n",
+ !!(st->cb_mask[chan->address >> 8] &
+ BIT(chan->address & 0xFF)));
+}
+
+static ssize_t ad7280_store_balance_sw(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned int devaddr, ch;
+ bool readin;
+ int ret;
+
+ ret = kstrtobool(buf, &readin);
+ if (ret)
+ return ret;
+
+ devaddr = chan->address >> 8;
+ ch = chan->address & 0xFF;
+
+ mutex_lock(&st->lock);
+ if (readin)
+ st->cb_mask[devaddr] |= BIT(ch);
+ else
+ st->cb_mask[devaddr] &= ~BIT(ch);
+
+ ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE_REG, 0,
+ FIELD_PREP(AD7280A_CELL_BALANCE_CHAN_BITMAP_MSK,
+ st->cb_mask[devaddr]));
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static ssize_t ad7280_show_balance_timer(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned int msecs;
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad7280_read_reg(st, chan->address >> 8,
+ (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG);
+ mutex_unlock(&st->lock);
+
+ if (ret < 0)
+ return ret;
+
+ msecs = FIELD_GET(AD7280A_CB_TIMER_VAL_MSK, ret) * 71500;
+
+ return sysfs_emit(buf, "%u.%u\n", msecs / 1000, msecs % 1000);
+}
+
+static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int val, val2;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 1000, &val, &val2);
+ if (ret)
+ return ret;
+
+ val = val * 1000 + val2;
+ val /= 71500;
+
+ if (val > 31)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = ad7280_write(st, chan->address >> 8,
+ (chan->address & 0xFF) + AD7280A_CB1_TIMER_REG, 0,
+ FIELD_PREP(AD7280A_CB_TIMER_VAL_MSK, val));
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ad7280_cell_ext_info[] = {
+ {
+ .name = "balance_switch_en",
+ .read = ad7280_show_balance_sw,
+ .write = ad7280_store_balance_sw,
+ .shared = IIO_SEPARATE,
+ }, {
+ .name = "balance_switch_timer",
+ .read = ad7280_show_balance_timer,
+ .write = ad7280_store_balance_timer,
+ .shared = IIO_SEPARATE,
+ },
+ {}
+};
+
+static const struct iio_event_spec ad7280_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i,
+ bool irq_present)
+{
+ chan->type = IIO_VOLTAGE;
+ chan->differential = 1;
+ chan->channel = i;
+ chan->channel2 = chan->channel + 1;
+ if (irq_present) {
+ chan->event_spec = ad7280_events;
+ chan->num_event_specs = ARRAY_SIZE(ad7280_events);
+ }
+ chan->ext_info = ad7280_cell_ext_info;
+}
+
+static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i,
+ bool irq_present)
+{
+ chan->type = IIO_TEMP;
+ chan->channel = i;
+ if (irq_present) {
+ chan->event_spec = ad7280_events;
+ chan->num_event_specs = ARRAY_SIZE(ad7280_events);
+ }
+}
+
+static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr,
+ int cnt)
+{
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ chan->address = addr;
+ chan->scan_index = cnt;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 12;
+ chan->scan_type.storagebits = 32;
+}
+
+static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan,
+ int cnt, int dev)
+{
+ chan->type = IIO_VOLTAGE;
+ chan->differential = 1;
+ chan->channel = 0;
+ chan->channel2 = dev * AD7280A_CELLS_PER_DEV;
+ chan->address = AD7280A_ALL_CELLS;
+ chan->indexed = 1;
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->scan_index = cnt;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 32;
+ chan->scan_type.storagebits = 32;
+}
+
+static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt,
+ bool irq_present)
+{
+ int addr, ch, i;
+ struct iio_chan_spec *chan;
+
+ for (ch = AD7280A_CELL_VOLTAGE_1_REG; ch <= AD7280A_AUX_ADC_6_REG; ch++) {
+ chan = &st->channels[*cnt];
+
+ if (ch < AD7280A_AUX_ADC_1_REG) {
+ i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch);
+ ad7280_voltage_channel_init(chan, i, irq_present);
+ } else {
+ i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch);
+ ad7280_temp_channel_init(chan, i, irq_present);
+ }
+
+ addr = ad7280a_devaddr(dev) << 8 | ch;
+ ad7280_common_fields_init(chan, addr, *cnt);
+
+ (*cnt)++;
+ }
+}
+
+static int ad7280_channel_init(struct ad7280_state *st, bool irq_present)
+{
+ int dev, cnt = 0;
+
+ st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 1,
+ sizeof(*st->channels), GFP_KERNEL);
+ if (!st->channels)
+ return -ENOMEM;
+
+ for (dev = 0; dev <= st->slave_num; dev++)
+ ad7280_init_dev_channels(st, dev, &cnt, irq_present);
+
+ ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev);
+
+ return cnt + 1;
+}
+
+static int ad7280a_read_thresh(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 ad7280_state *st = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = 1000 + (st->cell_threshhigh * 1568L) / 100;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ *val = 1000 + (st->cell_threshlow * 1568L) / 100;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_TEMP:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = ((st->aux_threshhigh) * 196L) / 10;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ *val = (st->aux_threshlow * 196L) / 10;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7280a_write_thresh(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 ad7280_state *st = iio_priv(indio_dev);
+ unsigned int addr;
+ long value;
+ int ret;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ value = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */
+ value = clamp(value, 0L, 0xFFL);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ addr = AD7280A_CELL_OVERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, value);
+ if (ret)
+ break;
+ st->cell_threshhigh = value;
+ break;
+ case IIO_EV_DIR_FALLING:
+ addr = AD7280A_CELL_UNDERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, value);
+ if (ret)
+ break;
+ st->cell_threshlow = value;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ break;
+ case IIO_TEMP:
+ value = (val * 10) / 196; /* LSB 19.6mV */
+ value = clamp(value, 0L, 0xFFL);
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ addr = AD7280A_AUX_ADC_OVERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, value);
+ if (ret)
+ break;
+ st->aux_threshhigh = value;
+ break;
+ case IIO_EV_DIR_FALLING:
+ addr = AD7280A_AUX_ADC_UNDERVOLTAGE_REG;
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, addr,
+ 1, value);
+ if (ret)
+ break;
+ st->aux_threshlow = value;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static irqreturn_t ad7280_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned int *channels;
+ int i, ret;
+
+ channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return IRQ_HANDLED;
+
+ ret = ad7280_read_all_channels(st, st->scan_cnt, channels);
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < st->scan_cnt; i++) {
+ unsigned int val;
+
+ val = FIELD_GET(AD7280A_TRANS_READ_CONV_DATA_MSK, channels[i]);
+ if (FIELD_GET(AD7280A_TRANS_READ_CONV_CHANADDR_MSK, channels[i]) <=
+ AD7280A_CELL_VOLTAGE_6_REG) {
+ if (val >= st->cell_threshhigh) {
+ u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ } else if (val <= st->cell_threshlow) {
+ u64 tmp = IIO_EVENT_CODE(IIO_VOLTAGE, 1, 0,
+ IIO_EV_DIR_FALLING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ }
+ } else {
+ if (val >= st->aux_threshhigh) {
+ u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ } else if (val <= st->aux_threshlow) {
+ u64 tmp = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING);
+ iio_push_event(indio_dev, tmp,
+ iio_get_time_ns(indio_dev));
+ }
+ }
+ }
+
+out:
+ kfree(channels);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7280_update_delay(struct ad7280_state *st)
+{
+ /*
+ * Total Conversion Time = ((tACQ + tCONV) *
+ * (Number of Conversions per Part)) −
+ * tACQ + ((N - 1) * tDELAY)
+ *
+ * Readback Delay = Total Conversion Time + tWAIT
+ */
+
+ st->readback_delay_us =
+ ((ad7280a_t_acq_ns[st->acquisition_time & 0x3] + 720) *
+ (AD7280A_NUM_CH * ad7280a_n_avg[st->oversampling_ratio & 0x3])) -
+ ad7280a_t_acq_ns[st->acquisition_time & 0x3] + st->slave_num * 250;
+
+ /* Convert to usecs */
+ st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000);
+ st->readback_delay_us += 5; /* Add tWAIT */
+}
+
+static int ad7280_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ if (chan->address == AD7280A_ALL_CELLS)
+ ret = ad7280_read_all_channels(st, st->scan_cnt, NULL);
+ else
+ ret = ad7280_read_channel(st, chan->address >> 8,
+ chan->address & 0xFF);
+ mutex_unlock(&st->lock);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6_REG)
+ *val = 4000;
+ else
+ *val = 5000;
+
+ *val2 = AD7280A_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = ad7280a_n_avg[st->oversampling_ratio];
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int ad7280_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (val2 != 0)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(ad7280a_n_avg); i++) {
+ if (val == ad7280a_n_avg[i]) {
+ st->oversampling_ratio = i;
+ ad7280_update_delay(st);
+ return 0;
+ }
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7280_info = {
+ .read_raw = ad7280_read_raw,
+ .write_raw = ad7280_write_raw,
+ .read_event_value = &ad7280a_read_thresh,
+ .write_event_value = &ad7280a_write_thresh,
+};
+
+static const struct iio_info ad7280_info_no_irq = {
+ .read_raw = ad7280_read_raw,
+ .write_raw = ad7280_write_raw,
+};
+
+static int ad7280_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ad7280_state *st;
+ int ret;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+ mutex_init(&st->lock);
+
+ st->thermistor_term_en =
+ device_property_read_bool(dev, "adi,thermistor-termination");
+
+ if (device_property_present(dev, "adi,acquisition-time-ns")) {
+ u32 val;
+
+ ret = device_property_read_u32(dev, "adi,acquisition-time-ns", &val);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 400:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns;
+ break;
+ case 800:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_800ns;
+ break;
+ case 1200:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1200ns;
+ break;
+ case 1600:
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_1600ns;
+ break;
+ default:
+ dev_err(dev, "Firmware provided acquisition time is invalid\n");
+ return -EINVAL;
+ }
+ } else {
+ st->acquisition_time = AD7280A_CTRL_LB_ACQ_TIME_400ns;
+ }
+
+ /* Alert masks are intended for when particular inputs are not wired up */
+ if (device_property_present(dev, "adi,voltage-alert-last-chan")) {
+ u32 val;
+
+ ret = device_property_read_u32(dev, "adi,voltage-alert-last-chan", &val);
+ if (ret)
+ return ret;
+
+ switch (val) {
+ case 3:
+ st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN4_VIN5;
+ break;
+ case 4:
+ st->chain_last_alert_ignore |= AD7280A_ALERT_REMOVE_VIN5;
+ break;
+ case 5:
+ break;
+ default:
+ dev_err(dev,
+ "Firmware provided last voltage alert channel invalid\n");
+ break;
+ }
+ }
+ crc8_populate_msb(st->crc_tab, POLYNOM);
+
+ st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_HZ;
+ st->spi->mode = SPI_MODE_1;
+ spi_setup(st->spi);
+
+ st->ctrl_lb = FIELD_PREP(AD7280A_CTRL_LB_ACQ_TIME_MSK, st->acquisition_time) |
+ FIELD_PREP(AD7280A_CTRL_LB_THERMISTOR_MSK, st->thermistor_term_en);
+ st->oversampling_ratio = 0; /* No oversampling */
+
+ ret = ad7280_chain_setup(st);
+ if (ret < 0)
+ return ret;
+
+ st->slave_num = ret;
+ st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH;
+ st->cell_threshhigh = 0xFF;
+ st->aux_threshhigh = 0xFF;
+
+ ret = devm_add_action_or_reset(dev, ad7280_sw_power_down, st);
+ if (ret)
+ return ret;
+
+ ad7280_update_delay(st);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad7280_channel_init(st, spi->irq > 0);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->num_channels = ret;
+ indio_dev->channels = st->channels;
+ if (spi->irq > 0) {
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
+ AD7280A_ALERT_REG, 1,
+ AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, ad7280a_devaddr(st->slave_num),
+ AD7280A_ALERT_REG, 0,
+ AD7280A_ALERT_GEN_STATIC_HIGH |
+ FIELD_PREP(AD7280A_ALERT_REMOVE_MSK,
+ st->chain_last_alert_ignore));
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(dev, spi->irq,
+ NULL,
+ ad7280_event_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &ad7280_info;
+ } else {
+ indio_dev->info = &ad7280_info_no_irq;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad7280_id[] = {
+ {"ad7280a", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7280_id);
+
+static struct spi_driver ad7280_driver = {
+ .driver = {
+ .name = "ad7280",
+ },
+ .probe = ad7280_probe,
+ .id_table = ad7280_id,
+};
+module_spi_driver(ad7280_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7280A");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c
new file mode 100644
index 000000000..e9129dac7
--- /dev/null
+++ b/drivers/iio/adc/ad7291.c
@@ -0,0 +1,563 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+/*
+ * Simplified handling
+ *
+ * If no events enabled - single polled channel read
+ * If event enabled direct reads disable unless channel
+ * is in the read mask.
+ *
+ * The noise-delayed bit as per datasheet suggestion is always enabled.
+ */
+
+/*
+ * AD7291 registers definition
+ */
+#define AD7291_COMMAND 0x00
+#define AD7291_VOLTAGE 0x01
+#define AD7291_T_SENSE 0x02
+#define AD7291_T_AVERAGE 0x03
+#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4)
+#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5)
+#define AD7291_HYST(x) ((x) * 3 + 0x6)
+#define AD7291_VOLTAGE_ALERT_STATUS 0x1F
+#define AD7291_T_ALERT_STATUS 0x20
+
+#define AD7291_BITS 12
+#define AD7291_VOLTAGE_LIMIT_COUNT 8
+
+
+/*
+ * AD7291 command
+ */
+#define AD7291_AUTOCYCLE BIT(0)
+#define AD7291_RESET BIT(1)
+#define AD7291_ALERT_CLEAR BIT(2)
+#define AD7291_ALERT_POLARITY BIT(3)
+#define AD7291_EXT_REF BIT(4)
+#define AD7291_NOISE_DELAY BIT(5)
+#define AD7291_T_SENSE_MASK BIT(7)
+#define AD7291_VOLTAGE_MASK GENMASK(15, 8)
+#define AD7291_VOLTAGE_OFFSET 8
+
+/*
+ * AD7291 value masks
+ */
+#define AD7291_VALUE_MASK GENMASK(11, 0)
+
+/*
+ * AD7291 alert register bits
+ */
+#define AD7291_T_LOW BIT(0)
+#define AD7291_T_HIGH BIT(1)
+#define AD7291_T_AVG_LOW BIT(2)
+#define AD7291_T_AVG_HIGH BIT(3)
+#define AD7291_V_LOW(x) BIT((x) * 2)
+#define AD7291_V_HIGH(x) BIT((x) * 2 + 1)
+
+
+struct ad7291_chip_info {
+ struct i2c_client *client;
+ struct regulator *reg;
+ u16 command;
+ u16 c_mask; /* Active voltage channels for events */
+ struct mutex state_lock;
+};
+
+static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data)
+{
+ struct i2c_client *client = chip->client;
+ int ret = 0;
+
+ ret = i2c_smbus_read_word_swapped(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C read error\n");
+ return ret;
+ }
+
+ *data = ret;
+
+ return 0;
+}
+
+static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_swapped(chip->client, reg, data);
+}
+
+static irqreturn_t ad7291_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad7291_chip_info *chip = iio_priv(private);
+ u16 t_status, v_status;
+ u16 command;
+ int i;
+ s64 timestamp = iio_get_time_ns(indio_dev);
+
+ if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status))
+ return IRQ_HANDLED;
+
+ if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status))
+ return IRQ_HANDLED;
+
+ if (!(t_status || v_status))
+ return IRQ_HANDLED;
+
+ command = chip->command | AD7291_ALERT_CLEAR;
+ ad7291_i2c_write(chip, AD7291_COMMAND, command);
+
+ command = chip->command & ~AD7291_ALERT_CLEAR;
+ ad7291_i2c_write(chip, AD7291_COMMAND, command);
+
+ /* For now treat t_sense and t_sense_average the same */
+ if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+ if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
+ if (v_status & AD7291_V_LOW(i))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+ i,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+ if (v_status & AD7291_V_HIGH(i))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+ i,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan,
+ enum iio_event_direction dir,
+ enum iio_event_info info)
+{
+ unsigned int offset;
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ offset = chan->channel;
+ break;
+ case IIO_TEMP:
+ offset = AD7291_VOLTAGE_OFFSET;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (dir == IIO_EV_DIR_FALLING)
+ return AD7291_DATA_HIGH(offset);
+ else
+ return AD7291_DATA_LOW(offset);
+ case IIO_EV_INFO_HYSTERESIS:
+ return AD7291_HYST(offset);
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int ad7291_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 ad7291_chip_info *chip = iio_priv(indio_dev);
+ int ret;
+ u16 uval;
+
+ ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info),
+ &uval);
+ if (ret < 0)
+ return ret;
+
+ if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE)
+ *val = uval & AD7291_VALUE_MASK;
+
+ else
+ *val = sign_extend32(uval, 11);
+
+ return IIO_VAL_INT;
+}
+
+static int ad7291_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 ad7291_chip_info *chip = iio_priv(indio_dev);
+
+ if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) {
+ if (val > AD7291_VALUE_MASK || val < 0)
+ return -EINVAL;
+ } else {
+ if (val > 2047 || val < -2048)
+ return -EINVAL;
+ }
+
+ return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info),
+ val);
+}
+
+static int ad7291_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 ad7291_chip_info *chip = iio_priv(indio_dev);
+ /*
+ * To be enabled the channel must simply be on. If any are enabled
+ * we are in continuous sampling mode
+ */
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return !!(chip->c_mask & BIT(15 - chan->channel));
+ case IIO_TEMP:
+ /* always on */
+ return 1;
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static int ad7291_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ int ret = 0;
+ struct ad7291_chip_info *chip = iio_priv(indio_dev);
+ unsigned int mask;
+ u16 regval;
+
+ mutex_lock(&chip->state_lock);
+ regval = chip->command;
+ /*
+ * To be enabled the channel must simply be on. If any are enabled
+ * use continuous sampling mode.
+ * Possible to disable temp as well but that makes single read tricky.
+ */
+
+ mask = BIT(15 - chan->channel);
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if ((!state) && (chip->c_mask & mask))
+ chip->c_mask &= ~mask;
+ else if (state && (!(chip->c_mask & mask)))
+ chip->c_mask |= mask;
+ else
+ break;
+
+ regval &= ~AD7291_AUTOCYCLE;
+ regval |= chip->c_mask;
+ if (chip->c_mask) /* Enable autocycle? */
+ regval |= AD7291_AUTOCYCLE;
+
+ ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval);
+ if (ret < 0)
+ goto error_ret;
+
+ chip->command = regval;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+error_ret:
+ mutex_unlock(&chip->state_lock);
+ return ret;
+}
+
+static int ad7291_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret;
+ struct ad7291_chip_info *chip = iio_priv(indio_dev);
+ u16 regval;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ mutex_lock(&chip->state_lock);
+ /* If in autocycle mode drop through */
+ if (chip->command & AD7291_AUTOCYCLE) {
+ mutex_unlock(&chip->state_lock);
+ return -EBUSY;
+ }
+ /* Enable this channel alone */
+ regval = chip->command & (~AD7291_VOLTAGE_MASK);
+ regval |= BIT(15 - chan->channel);
+ ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval);
+ if (ret < 0) {
+ mutex_unlock(&chip->state_lock);
+ return ret;
+ }
+ /* Read voltage */
+ ret = i2c_smbus_read_word_swapped(chip->client,
+ AD7291_VOLTAGE);
+ if (ret < 0) {
+ mutex_unlock(&chip->state_lock);
+ return ret;
+ }
+ *val = ret & AD7291_VALUE_MASK;
+ mutex_unlock(&chip->state_lock);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ /* Assumes tsense bit of command register always set */
+ ret = i2c_smbus_read_word_swapped(chip->client,
+ AD7291_T_SENSE);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(ret, 11);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_AVERAGE_RAW:
+ ret = i2c_smbus_read_word_swapped(chip->client,
+ AD7291_T_AVERAGE);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(ret, 11);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chip->reg) {
+ int vref;
+
+ vref = regulator_get_voltage(chip->reg);
+ if (vref < 0)
+ return vref;
+ *val = vref / 1000;
+ } else {
+ *val = 2500;
+ }
+ *val2 = AD7291_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ /*
+ * One LSB of the ADC corresponds to 0.25 deg C.
+ * The temperature reading is in 12-bit twos
+ * complement format
+ */
+ *val = 250;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_event_spec ad7291_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),
+ },
+};
+
+#define AD7291_VOLTAGE_CHAN(_chan) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .indexed = 1, \
+ .channel = _chan, \
+ .event_spec = ad7291_events, \
+ .num_event_specs = ARRAY_SIZE(ad7291_events), \
+}
+
+static const struct iio_chan_spec ad7291_channels[] = {
+ AD7291_VOLTAGE_CHAN(0),
+ AD7291_VOLTAGE_CHAN(1),
+ AD7291_VOLTAGE_CHAN(2),
+ AD7291_VOLTAGE_CHAN(3),
+ AD7291_VOLTAGE_CHAN(4),
+ AD7291_VOLTAGE_CHAN(5),
+ AD7291_VOLTAGE_CHAN(6),
+ AD7291_VOLTAGE_CHAN(7),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = 0,
+ .event_spec = ad7291_events,
+ .num_event_specs = ARRAY_SIZE(ad7291_events),
+ }
+};
+
+static const struct iio_info ad7291_info = {
+ .read_raw = &ad7291_read_raw,
+ .read_event_config = &ad7291_read_event_config,
+ .write_event_config = &ad7291_write_event_config,
+ .read_event_value = &ad7291_read_event_value,
+ .write_event_value = &ad7291_write_event_value,
+};
+
+static void ad7291_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int ad7291_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ad7291_chip_info *chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+
+ mutex_init(&chip->state_lock);
+
+ chip->client = client;
+
+ chip->command = AD7291_NOISE_DELAY |
+ AD7291_T_SENSE_MASK | /* Tsense always enabled */
+ AD7291_ALERT_POLARITY; /* set irq polarity low level */
+
+ chip->reg = devm_regulator_get_optional(&client->dev, "vref");
+ if (IS_ERR(chip->reg)) {
+ if (PTR_ERR(chip->reg) != -ENODEV)
+ return PTR_ERR(chip->reg);
+
+ chip->reg = NULL;
+ }
+
+ if (chip->reg) {
+ ret = regulator_enable(chip->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev, ad7291_reg_disable,
+ chip->reg);
+ if (ret)
+ return ret;
+
+ chip->command |= AD7291_EXT_REF;
+ }
+
+ indio_dev->name = id->name;
+ indio_dev->channels = ad7291_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7291_channels);
+
+ indio_dev->info = &ad7291_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET);
+ if (ret)
+ return -EIO;
+
+ ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command);
+ if (ret)
+ return -EIO;
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL,
+ &ad7291_event_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ id->name,
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ad7291_id[] = {
+ { "ad7291", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7291_id);
+
+static const struct of_device_id ad7291_of_match[] = {
+ { .compatible = "adi,ad7291" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad7291_of_match);
+
+static struct i2c_driver ad7291_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ad7291_of_match,
+ },
+ .probe = ad7291_probe,
+ .id_table = ad7291_id,
+};
+module_i2c_driver(ad7291_driver);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c
new file mode 100644
index 000000000..a2f9fda25
--- /dev/null
+++ b/drivers/iio/adc/ad7292.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7292 SPI ADC driver
+ *
+ * Copyright 2019 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+
+#define ADI_VENDOR_ID 0x0018
+
+/* AD7292 registers definition */
+#define AD7292_REG_VENDOR_ID 0x00
+#define AD7292_REG_CONF_BANK 0x05
+#define AD7292_REG_CONV_COMM 0x0E
+#define AD7292_REG_ADC_CH(x) (0x10 + (x))
+
+/* AD7292 configuration bank subregisters definition */
+#define AD7292_BANK_REG_VIN_RNG0 0x10
+#define AD7292_BANK_REG_VIN_RNG1 0x11
+#define AD7292_BANK_REG_SAMP_MODE 0x12
+
+#define AD7292_RD_FLAG_MSK(x) (BIT(7) | ((x) & 0x3F))
+
+/* AD7292_REG_ADC_CONVERSION */
+#define AD7292_ADC_DATA_MASK GENMASK(15, 6)
+#define AD7292_ADC_DATA(x) FIELD_GET(AD7292_ADC_DATA_MASK, x)
+
+/* AD7292_CHANNEL_SAMPLING_MODE */
+#define AD7292_CH_SAMP_MODE(reg, ch) (((reg) >> 8) & BIT(ch))
+
+/* AD7292_CHANNEL_VIN_RANGE */
+#define AD7292_CH_VIN_RANGE(reg, ch) ((reg) & BIT(ch))
+
+#define AD7292_VOLTAGE_CHAN(_chan) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .indexed = 1, \
+ .channel = _chan, \
+}
+
+static const struct iio_chan_spec ad7292_channels[] = {
+ AD7292_VOLTAGE_CHAN(0),
+ AD7292_VOLTAGE_CHAN(1),
+ AD7292_VOLTAGE_CHAN(2),
+ AD7292_VOLTAGE_CHAN(3),
+ AD7292_VOLTAGE_CHAN(4),
+ AD7292_VOLTAGE_CHAN(5),
+ AD7292_VOLTAGE_CHAN(6),
+ AD7292_VOLTAGE_CHAN(7)
+};
+
+static const struct iio_chan_spec ad7292_channels_diff[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .indexed = 1,
+ .differential = 1,
+ .channel = 0,
+ .channel2 = 1,
+ },
+ AD7292_VOLTAGE_CHAN(2),
+ AD7292_VOLTAGE_CHAN(3),
+ AD7292_VOLTAGE_CHAN(4),
+ AD7292_VOLTAGE_CHAN(5),
+ AD7292_VOLTAGE_CHAN(6),
+ AD7292_VOLTAGE_CHAN(7)
+};
+
+struct ad7292_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned short vref_mv;
+
+ __be16 d16 __aligned(IIO_DMA_MINALIGN);
+ u8 d8[2];
+};
+
+static int ad7292_spi_reg_read(struct ad7292_state *st, unsigned int addr)
+{
+ int ret;
+
+ st->d8[0] = AD7292_RD_FLAG_MSK(addr);
+
+ ret = spi_write_then_read(st->spi, st->d8, 1, &st->d16, 2);
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->d16);
+}
+
+static int ad7292_spi_subreg_read(struct ad7292_state *st, unsigned int addr,
+ unsigned int sub_addr, unsigned int len)
+{
+ unsigned int shift = 16 - (8 * len);
+ int ret;
+
+ st->d8[0] = AD7292_RD_FLAG_MSK(addr);
+ st->d8[1] = sub_addr;
+
+ ret = spi_write_then_read(st->spi, st->d8, 2, &st->d16, len);
+ if (ret < 0)
+ return ret;
+
+ return (be16_to_cpu(st->d16) >> shift);
+}
+
+static int ad7292_single_conversion(struct ad7292_state *st,
+ unsigned int chan_addr)
+{
+ int ret;
+
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d8,
+ .len = 4,
+ .delay = {
+ .value = 6,
+ .unit = SPI_DELAY_UNIT_USECS
+ },
+ }, {
+ .rx_buf = &st->d16,
+ .len = 2,
+ },
+ };
+
+ st->d8[0] = chan_addr;
+ st->d8[1] = AD7292_RD_FLAG_MSK(AD7292_REG_CONV_COMM);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->d16);
+}
+
+static int ad7292_vin_range_multiplier(struct ad7292_state *st, int channel)
+{
+ int samp_mode, range0, range1, factor = 1;
+
+ /*
+ * Every AD7292 ADC channel may have its input range adjusted according
+ * to the settings at the ADC sampling mode and VIN range subregisters.
+ * For a given channel, the minimum input range is equal to Vref, and it
+ * may be increased by a multiplier factor of 2 or 4 according to the
+ * following rule:
+ * If channel is being sampled with respect to AGND:
+ * factor = 4 if VIN range0 and VIN range1 equal 0
+ * factor = 2 if only one of VIN ranges equal 1
+ * factor = 1 if both VIN range0 and VIN range1 equal 1
+ * If channel is being sampled with respect to AVDD:
+ * factor = 4 if VIN range0 and VIN range1 equal 0
+ * Behavior is undefined if any of VIN range doesn't equal 0
+ */
+
+ samp_mode = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK,
+ AD7292_BANK_REG_SAMP_MODE, 2);
+
+ if (samp_mode < 0)
+ return samp_mode;
+
+ range0 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK,
+ AD7292_BANK_REG_VIN_RNG0, 2);
+
+ if (range0 < 0)
+ return range0;
+
+ range1 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK,
+ AD7292_BANK_REG_VIN_RNG1, 2);
+
+ if (range1 < 0)
+ return range1;
+
+ if (AD7292_CH_SAMP_MODE(samp_mode, channel)) {
+ /* Sampling with respect to AGND */
+ if (!AD7292_CH_VIN_RANGE(range0, channel))
+ factor *= 2;
+
+ if (!AD7292_CH_VIN_RANGE(range1, channel))
+ factor *= 2;
+
+ } else {
+ /* Sampling with respect to AVDD */
+ if (AD7292_CH_VIN_RANGE(range0, channel) ||
+ AD7292_CH_VIN_RANGE(range1, channel))
+ return -EPERM;
+
+ factor = 4;
+ }
+
+ return factor;
+}
+
+static int ad7292_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7292_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ch_addr = AD7292_REG_ADC_CH(chan->channel);
+ ret = ad7292_single_conversion(st, ch_addr);
+ if (ret < 0)
+ return ret;
+
+ *val = AD7292_ADC_DATA(ret);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * To convert a raw value to standard units, the IIO defines
+ * this formula: Scaled value = (raw + offset) * scale.
+ * For the scale to be a correct multiplier for (raw + offset),
+ * it must be calculated as the input range divided by the
+ * number of possible distinct input values. Given the ADC data
+ * is 10 bit long, it may assume 2^10 distinct values.
+ * Hence, scale = range / 2^10. The IIO_VAL_FRACTIONAL_LOG2
+ * return type indicates to the IIO API to divide *val by 2 to
+ * the power of *val2 when returning from read_raw.
+ */
+
+ ret = ad7292_vin_range_multiplier(st, chan->channel);
+ if (ret < 0)
+ return ret;
+
+ *val = st->vref_mv * ret;
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad7292_info = {
+ .read_raw = ad7292_read_raw,
+};
+
+static void ad7292_regulator_disable(void *data)
+{
+ struct ad7292_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+static int ad7292_probe(struct spi_device *spi)
+{
+ struct ad7292_state *st;
+ struct iio_dev *indio_dev;
+ struct device_node *child;
+ bool diff_channels = false;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ ret = ad7292_spi_reg_read(st, AD7292_REG_VENDOR_ID);
+ if (ret != ADI_VENDOR_ID) {
+ dev_err(&spi->dev, "Wrong vendor id 0x%x\n", ret);
+ return -EINVAL;
+ }
+
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev,
+ "Failed to enable external vref supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev,
+ ad7292_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ return ret;
+
+ st->vref_mv = ret / 1000;
+ } else {
+ /* Use the internal voltage reference. */
+ st->vref_mv = 1250;
+ }
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7292_info;
+
+ for_each_available_child_of_node(spi->dev.of_node, child) {
+ diff_channels = of_property_read_bool(child, "diff-channels");
+ if (diff_channels) {
+ of_node_put(child);
+ break;
+ }
+ }
+
+ if (diff_channels) {
+ indio_dev->num_channels = ARRAY_SIZE(ad7292_channels_diff);
+ indio_dev->channels = ad7292_channels_diff;
+ } else {
+ indio_dev->num_channels = ARRAY_SIZE(ad7292_channels);
+ indio_dev->channels = ad7292_channels;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7292_id_table[] = {
+ { "ad7292", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7292_id_table);
+
+static const struct of_device_id ad7292_of_match[] = {
+ { .compatible = "adi,ad7292" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7292_of_match);
+
+static struct spi_driver ad7292_driver = {
+ .driver = {
+ .name = "ad7292",
+ .of_match_table = ad7292_of_match,
+ },
+ .probe = ad7292_probe,
+ .id_table = ad7292_id_table,
+};
+module_spi_driver(ad7292_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt1@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD7292 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
new file mode 100644
index 000000000..c0430f71f
--- /dev/null
+++ b/drivers/iio/adc/ad7298.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7298 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define AD7298_WRITE BIT(15) /* write to the control register */
+#define AD7298_REPEAT BIT(14) /* repeated conversion enable */
+#define AD7298_CH(x) BIT(13 - (x)) /* channel select */
+#define AD7298_TSENSE BIT(5) /* temperature conversion enable */
+#define AD7298_EXTREF BIT(2) /* external reference enable */
+#define AD7298_TAVG BIT(1) /* temperature sensor averaging enable */
+#define AD7298_PDD BIT(0) /* partial power down enable */
+
+#define AD7298_MAX_CHAN 8
+#define AD7298_INTREF_mV 2500
+
+#define AD7298_CH_TEMP 9
+
+struct ad7298_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned ext_ref;
+ struct spi_transfer ring_xfer[10];
+ struct spi_transfer scan_single_xfer[3];
+ struct spi_message ring_msg;
+ struct spi_message scan_single_msg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 rx_buf[12] __aligned(IIO_DMA_MINALIGN);
+ __be16 tx_buf[2];
+};
+
+#define AD7298_V_CHAN(index) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec ad7298_channels[] = {
+ {
+ .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),
+ .address = AD7298_CH_TEMP,
+ .scan_index = -1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ },
+ },
+ AD7298_V_CHAN(0),
+ AD7298_V_CHAN(1),
+ AD7298_V_CHAN(2),
+ AD7298_V_CHAN(3),
+ AD7298_V_CHAN(4),
+ AD7298_V_CHAN(5),
+ AD7298_V_CHAN(6),
+ AD7298_V_CHAN(7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+/*
+ * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask
+ */
+static int ad7298_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct ad7298_state *st = iio_priv(indio_dev);
+ int i, m;
+ unsigned short command;
+ int scan_count;
+
+ /* Now compute overall size */
+ scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
+
+ command = AD7298_WRITE | st->ext_ref;
+
+ for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1)
+ if (test_bit(i, active_scan_mask))
+ command |= m;
+
+ st->tx_buf[0] = cpu_to_be16(command);
+
+ /* build spi ring message */
+ st->ring_xfer[0].tx_buf = &st->tx_buf[0];
+ st->ring_xfer[0].len = 2;
+ st->ring_xfer[0].cs_change = 1;
+ st->ring_xfer[1].tx_buf = &st->tx_buf[1];
+ st->ring_xfer[1].len = 2;
+ st->ring_xfer[1].cs_change = 1;
+
+ spi_message_init(&st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg);
+
+ for (i = 0; i < scan_count; i++) {
+ st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i];
+ st->ring_xfer[i + 2].len = 2;
+ st->ring_xfer[i + 2].cs_change = 1;
+ spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg);
+ }
+ /* make sure last transfer cs_change is not set */
+ st->ring_xfer[i + 1].cs_change = 0;
+
+ return 0;
+}
+
+static irqreturn_t ad7298_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7298_state *st = iio_priv(indio_dev);
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, &st->ring_msg);
+ if (b_sent)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch)
+{
+ int ret;
+ st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref |
+ (AD7298_CH(0) >> ch));
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+
+ return be16_to_cpu(st->rx_buf[0]);
+}
+
+static int ad7298_scan_temp(struct ad7298_state *st, int *val)
+{
+ int ret;
+ __be16 buf;
+
+ buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE |
+ AD7298_TAVG | st->ext_ref);
+
+ ret = spi_write(st->spi, (u8 *)&buf, 2);
+ if (ret)
+ return ret;
+
+ buf = cpu_to_be16(0);
+
+ ret = spi_write(st->spi, (u8 *)&buf, 2);
+ if (ret)
+ return ret;
+
+ usleep_range(101, 1000); /* sleep > 100us */
+
+ ret = spi_read(st->spi, (u8 *)&buf, 2);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(be16_to_cpu(buf), 11);
+
+ return 0;
+}
+
+static int ad7298_get_ref_voltage(struct ad7298_state *st)
+{
+ int vref;
+
+ if (st->reg) {
+ vref = regulator_get_voltage(st->reg);
+ if (vref < 0)
+ return vref;
+
+ return vref / 1000;
+ } else {
+ return AD7298_INTREF_mV;
+ }
+}
+
+static int ad7298_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7298_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ 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);
+
+ if (ret < 0)
+ return ret;
+
+ if (chan->address != AD7298_CH_TEMP)
+ *val = ret & GENMASK(chan->scan_type.realbits - 1, 0);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = ad7298_get_ref_voltage(st);
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ *val = ad7298_get_ref_voltage(st);
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 1093 - 2732500 / ad7298_get_ref_voltage(st);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad7298_info = {
+ .read_raw = &ad7298_read_raw,
+ .update_scan_mode = ad7298_update_scan_mode,
+};
+
+static void ad7298_reg_disable(void *data)
+{
+ struct regulator *reg = data;
+
+ regulator_disable(reg);
+}
+
+static int ad7298_probe(struct spi_device *spi)
+{
+ struct ad7298_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
+ st->ext_ref = AD7298_EXTREF;
+ } else {
+ ret = PTR_ERR(st->reg);
+ if (ret != -ENODEV)
+ return ret;
+
+ st->reg = NULL;
+ }
+
+ if (st->reg) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7298_reg_disable,
+ st->reg);
+ if (ret)
+ return ret;
+ }
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7298_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7298_channels);
+ indio_dev->info = &ad7298_info;
+
+ /* Setup default message */
+
+ st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
+ st->scan_single_xfer[0].len = 2;
+ st->scan_single_xfer[0].cs_change = 1;
+ st->scan_single_xfer[1].tx_buf = &st->tx_buf[1];
+ st->scan_single_xfer[1].len = 2;
+ st->scan_single_xfer[1].cs_change = 1;
+ st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
+ st->scan_single_xfer[2].len = 2;
+
+ spi_message_init(&st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ &ad7298_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct acpi_device_id ad7298_acpi_ids[] = {
+ { "INT3494", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids);
+
+static const struct spi_device_id ad7298_id[] = {
+ {"ad7298", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7298_id);
+
+static struct spi_driver ad7298_driver = {
+ .driver = {
+ .name = "ad7298",
+ .acpi_match_table = ad7298_acpi_ids,
+ },
+ .probe = ad7298_probe,
+ .id_table = ad7298_id,
+};
+module_spi_driver(ad7298_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
new file mode 100644
index 000000000..94776f696
--- /dev/null
+++ b/drivers/iio/adc/ad7476.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
+ * TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+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 *);
+ bool has_vref;
+ bool has_vdrive;
+};
+
+struct ad7476_state {
+ struct spi_device *spi;
+ const struct ad7476_chip_info *chip_info;
+ struct regulator *ref_reg;
+ struct gpio_desc *convst_gpio;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * Make the buffer large enough for one 16 bit sample and one 64 bit
+ * aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
+};
+
+enum ad7476_supported_device_ids {
+ ID_AD7091,
+ ID_AD7091R,
+ ID_AD7273,
+ ID_AD7274,
+ ID_AD7276,
+ ID_AD7277,
+ ID_AD7278,
+ ID_AD7466,
+ ID_AD7467,
+ ID_AD7468,
+ ID_AD7475,
+ ID_AD7495,
+ ID_AD7940,
+ ID_ADC081S,
+ ID_ADC101S,
+ ID_ADC121S,
+ ID_ADS7866,
+ ID_ADS7867,
+ ID_ADS7868,
+ ID_LTC2314_14,
+};
+
+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;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ 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;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7091_reset(struct ad7476_state *st)
+{
+ /* Any transfers with 8 scl cycles will reset the device */
+ spi_read(st->spi, st->data, 1);
+}
+
+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;
+
+ return be16_to_cpup((__be16 *)st->data);
+}
+
+static int ad7476_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7476_state *st = iio_priv(indio_dev);
+ int scale_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = ad7476_scan_direct(st);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+ *val = (ret >> st->chip_info->channel[0].scan_type.shift) &
+ GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->ref_reg) {
+ scale_uv = regulator_get_voltage(st->ref_reg);
+ if (scale_uv < 0)
+ return scale_uv;
+ } else {
+ scale_uv = st->chip_info->int_vref_uv;
+ }
+ *val = scale_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .info_mask_separate = _info_mask_sep, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = (_shift), \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
+ BIT(IIO_CHAN_INFO_RAW))
+#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
+ BIT(IIO_CHAN_INFO_RAW))
+#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))
+
+static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
+ [ID_AD7091] = {
+ .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_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),
+ .int_vref_uv = 2500000,
+ .has_vref = true,
+ .reset = ad7091_reset,
+ },
+ [ID_AD7273] = {
+ .channel[0] = AD7940_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .has_vref = true,
+ },
+ [ID_AD7274] = {
+ .channel[0] = AD7940_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .has_vref = true,
+ },
+ [ID_AD7276] = {
+ .channel[0] = AD7940_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7277] = {
+ .channel[0] = AD7940_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7278] = {
+ .channel[0] = AD7940_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7466] = {
+ .channel[0] = AD7476_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7467] = {
+ .channel[0] = AD7476_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7468] = {
+ .channel[0] = AD7476_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_AD7475] = {
+ .channel[0] = AD7476_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .has_vref = true,
+ .has_vdrive = true,
+ },
+ [ID_AD7495] = {
+ .channel[0] = AD7476_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .int_vref_uv = 2500000,
+ .has_vdrive = true,
+ },
+ [ID_AD7940] = {
+ .channel[0] = AD7940_CHAN(14),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADC081S] = {
+ .channel[0] = ADC081S_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADC101S] = {
+ .channel[0] = ADC081S_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADC121S] = {
+ .channel[0] = ADC081S_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADS7866] = {
+ .channel[0] = ADS786X_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADS7867] = {
+ .channel[0] = ADS786X_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADS7868] = {
+ .channel[0] = ADS786X_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_LTC2314_14] = {
+ .channel[0] = AD7940_CHAN(14),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .has_vref = true,
+ },
+};
+
+static const struct iio_info ad7476_info = {
+ .read_raw = &ad7476_read_raw,
+};
+
+static void ad7476_reg_disable(void *data)
+{
+ struct regulator *reg = data;
+
+ regulator_disable(reg);
+}
+
+static int ad7476_probe(struct spi_device *spi)
+{
+ struct ad7476_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *reg;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->chip_info =
+ &ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ reg = devm_regulator_get(&spi->dev, "vcc");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg);
+ if (ret)
+ return ret;
+
+ /* Either vcc or vref (below) as appropriate */
+ if (!st->chip_info->int_vref_uv)
+ st->ref_reg = reg;
+
+ if (st->chip_info->has_vref) {
+
+ /* If a device has an internal reference vref is optional */
+ if (st->chip_info->int_vref_uv) {
+ reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (IS_ERR(reg) && (PTR_ERR(reg) != -ENODEV))
+ return PTR_ERR(reg);
+ } else {
+ reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ }
+
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev,
+ ad7476_reg_disable,
+ reg);
+ if (ret)
+ return ret;
+ st->ref_reg = reg;
+ } else {
+ /*
+ * Can only get here if device supports both internal
+ * and external reference, but the regulator connected
+ * to the external reference is not connected.
+ * Set the reference regulator pointer to NULL to
+ * indicate this.
+ */
+ st->ref_reg = NULL;
+ }
+ }
+
+ if (st->chip_info->has_vdrive) {
+ reg = devm_regulator_get(&spi->dev, "vdrive");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable,
+ reg);
+ 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);
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channel;
+ indio_dev->num_channels = 2;
+ indio_dev->info = &ad7476_info;
+
+ if (st->convst_gpio)
+ indio_dev->channels = st->chip_info->convst_channel;
+ /* Setup default message */
+
+ st->xfer.rx_buf = &st->data;
+ st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
+
+ spi_message_init(&st->msg);
+ spi_message_add_tail(&st->xfer, &st->msg);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ &ad7476_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ if (st->chip_info->reset)
+ st->chip_info->reset(st);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7476_id[] = {
+ {"ad7091", ID_AD7091},
+ {"ad7091r", ID_AD7091R},
+ {"ad7273", ID_AD7273},
+ {"ad7274", ID_AD7274},
+ {"ad7276", ID_AD7276},
+ {"ad7277", ID_AD7277},
+ {"ad7278", ID_AD7278},
+ {"ad7466", ID_AD7466},
+ {"ad7467", ID_AD7467},
+ {"ad7468", ID_AD7468},
+ {"ad7475", ID_AD7475},
+ {"ad7476", ID_AD7466},
+ {"ad7476a", ID_AD7466},
+ {"ad7477", ID_AD7467},
+ {"ad7477a", ID_AD7467},
+ {"ad7478", ID_AD7468},
+ {"ad7478a", ID_AD7468},
+ {"ad7495", ID_AD7495},
+ {"ad7910", ID_AD7467},
+ {"ad7920", ID_AD7466},
+ {"ad7940", ID_AD7940},
+ {"adc081s", ID_ADC081S},
+ {"adc101s", ID_ADC101S},
+ {"adc121s", ID_ADC121S},
+ {"ads7866", ID_ADS7866},
+ {"ads7867", ID_ADS7867},
+ {"ads7868", ID_ADS7868},
+ {"ltc2314-14", ID_LTC2314_14},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7476_id);
+
+static struct spi_driver ad7476_driver = {
+ .driver = {
+ .name = "ad7476",
+ },
+ .probe = ad7476_probe,
+ .id_table = ad7476_id,
+};
+module_spi_driver(ad7476_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
new file mode 100644
index 000000000..ba24f9952
--- /dev/null
+++ b/drivers/iio/adc/ad7606.c
@@ -0,0 +1,735 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "ad7606.h"
+
+/*
+ * Scales are computed as 5000/32768 and 10000/32768 respectively,
+ * so that when applied to the raw values they provide mV values
+ */
+static const unsigned int ad7606_scale_avail[2] = {
+ 152588, 305176
+};
+
+
+static const unsigned int ad7616_sw_scale_avail[3] = {
+ 76293, 152588, 305176
+};
+
+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 int ad7606_reset(struct ad7606_state *st)
+{
+ if (st->gpio_reset) {
+ gpiod_set_value(st->gpio_reset, 1);
+ ndelay(100); /* t_reset >= 100ns */
+ gpiod_set_value(st->gpio_reset, 0);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int ad7606_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = st->bops->reg_read(st, reg);
+ if (ret < 0)
+ goto err_unlock;
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = st->bops->reg_write(st, reg, writeval);
+ }
+err_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ad7606_read_samples(struct ad7606_state *st)
+{
+ unsigned int num = st->chip_info->num_channels - 1;
+ u16 *data = st->data;
+ int ret;
+
+ /*
+ * The frstdata signal is set to high while and after reading the sample
+ * of the first channel and low for all other channels. This can be used
+ * to check that the incoming data is correctly aligned. During normal
+ * operation the data should never become unaligned, but some glitch or
+ * electrostatic discharge might cause an extra read or clock cycle.
+ * Monitoring the frstdata signal allows to recover from such failure
+ * situations.
+ */
+
+ if (st->gpio_frstdata) {
+ ret = st->bops->read_block(st->dev, 1, data);
+ if (ret)
+ return ret;
+
+ if (!gpiod_get_value(st->gpio_frstdata)) {
+ ad7606_reset(st);
+ return -EIO;
+ }
+
+ data++;
+ num--;
+ }
+
+ return st->bops->read_block(st->dev, num, data);
+}
+
+static irqreturn_t ad7606_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = ad7606_read_samples(st);
+ if (ret == 0)
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+ /* The rising edge of the CONVST signal starts a new conversion. */
+ gpiod_set_value(st->gpio_convst, 1);
+
+ mutex_unlock(&st->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ gpiod_set_value(st->gpio_convst, 1);
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto error_ret;
+ }
+
+ ret = ad7606_read_samples(st);
+ if (ret == 0)
+ ret = st->data[ch];
+
+error_ret:
+ gpiod_set_value(st->gpio_convst, 0);
+
+ return ret;
+}
+
+static int ad7606_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret, ch = 0;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7606_scan_direct(indio_dev, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+ *val = (short)ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->sw_mode_en)
+ ch = chan->address;
+ *val = 0;
+ *val2 = st->scale_avail[st->range[ch]];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = st->oversampling;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals,
+ unsigned int n, bool micros)
+{
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ micros ? "0.%06u " : "%u ", vals[i]);
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+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 ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true);
+}
+
+static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
+
+static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_range, val);
+
+ return 0;
+}
+
+static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ DECLARE_BITMAP(values, 3);
+
+ values[0] = val;
+
+ gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
+ st->gpio_os->info, values);
+
+ /* AD7616 requires a reset to update value */
+ if (st->chip_info->os_req_reset)
+ ad7606_reset(st);
+
+ return 0;
+}
+
+static int ad7606_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int i, ret, ch = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ mutex_lock(&st->lock);
+ i = find_closest(val2, st->scale_avail, st->num_scales);
+ if (st->sw_mode_en)
+ ch = chan->address;
+ ret = st->write_scale(indio_dev, ch, i);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ st->range[ch] = i;
+ mutex_unlock(&st->lock);
+
+ return 0;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (val2)
+ return -EINVAL;
+ i = find_closest(val, st->oversampling_avail,
+ st->num_os_ratios);
+ mutex_lock(&st->lock);
+ ret = st->write_os(indio_dev, i);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ st->oversampling = st->oversampling_avail[i];
+ mutex_unlock(&st->lock);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ad7606_oversampling_ratio_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_show_avail(buf, st->oversampling_avail,
+ st->num_os_ratios, false);
+}
+
+static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444,
+ ad7606_oversampling_ratio_avail, NULL, 0);
+
+static struct attribute *ad7606_attributes_os_and_range[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os_and_range = {
+ .attrs = ad7606_attributes_os_and_range,
+};
+
+static struct attribute *ad7606_attributes_os[] = {
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os = {
+ .attrs = ad7606_attributes_os,
+};
+
+static struct attribute *ad7606_attributes_range[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_range = {
+ .attrs = ad7606_attributes_range,
+};
+
+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[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+};
+
+/*
+ * 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),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+ AD7606_CHANNEL(8),
+ AD7606_CHANNEL(9),
+ AD7606_CHANNEL(10),
+ AD7606_CHANNEL(11),
+ AD7606_CHANNEL(12),
+ AD7606_CHANNEL(13),
+ AD7606_CHANNEL(14),
+ AD7606_CHANNEL(15),
+};
+
+static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
+ /* More devices added in future */
+ [ID_AD7605_4] = {
+ .channels = ad7605_channels,
+ .num_channels = 5,
+ },
+ [ID_AD7606_8] = {
+ .channels = ad7606_channels,
+ .num_channels = 9,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606_6] = {
+ .channels = ad7606_channels,
+ .num_channels = 7,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606_4] = {
+ .channels = ad7606_channels,
+ .num_channels = 5,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606B] = {
+ .channels = ad7606_channels,
+ .num_channels = 9,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7616] = {
+ .channels = ad7616_channels,
+ .num_channels = 17,
+ .oversampling_avail = ad7616_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
+ .os_req_reset = true,
+ .init_delay_ms = 15,
+ },
+};
+
+static int ad7606_request_gpios(struct ad7606_state *st)
+{
+ struct device *dev = st->dev;
+
+ st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_convst))
+ return PTR_ERR(st->gpio_convst);
+
+ st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
+
+ st->gpio_range = devm_gpiod_get_optional(dev, "adi,range",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_range))
+ return PTR_ERR(st->gpio_range);
+
+ st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_standby))
+ return PTR_ERR(st->gpio_standby);
+
+ st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data",
+ GPIOD_IN);
+ if (IS_ERR(st->gpio_frstdata))
+ return PTR_ERR(st->gpio_frstdata);
+
+ if (!st->chip_info->oversampling_num)
+ return 0;
+
+ st->gpio_os = devm_gpiod_get_array_optional(dev,
+ "adi,oversampling-ratio",
+ GPIOD_OUT_LOW);
+ return PTR_ERR_OR_ZERO(st->gpio_os);
+}
+
+/*
+ * The BUSY signal indicates when conversions are in progress, so when a rising
+ * edge of CONVST is applied, BUSY goes logic high and transitions low at the
+ * end of the entire conversion process. The falling edge of the BUSY signal
+ * triggers this interrupt.
+ */
+static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev)) {
+ gpiod_set_value(st->gpio_convst, 0);
+ iio_trigger_poll_chained(st->trig);
+ } else {
+ complete(&st->completion);
+ }
+
+ return IRQ_HANDLED;
+};
+
+static int ad7606_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ad7606_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_convst, 1);
+
+ return 0;
+}
+
+static int ad7606_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_convst, 0);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad7606_buffer_ops = {
+ .postenable = &ad7606_buffer_postenable,
+ .predisable = &ad7606_buffer_predisable,
+};
+
+static const struct iio_info ad7606_info_no_os_or_range = {
+ .read_raw = &ad7606_read_raw,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os_and_range = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_os_and_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os_range_and_debug = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .debugfs_reg_access = &ad7606_reg_access,
+ .attrs = &ad7606_attribute_group_os_and_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_os,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_range = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_trigger_ops ad7606_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static void ad7606_regulator_disable(void *data)
+{
+ struct ad7606_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
+ const char *name, unsigned int id,
+ const struct ad7606_bus_ops *bops)
+{
+ struct ad7606_state *st;
+ int ret;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->dev = dev;
+ mutex_init(&st->lock);
+ st->bops = bops;
+ st->base_address = base_address;
+ /* tied to logic low, analog input range is +/- 5V */
+ st->range[0] = 0;
+ st->oversampling = 1;
+ st->scale_avail = ad7606_scale_avail;
+ st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
+
+ st->reg = devm_regulator_get(dev, "avcc");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(dev, "Failed to enable specified AVcc supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ st->chip_info = &ad7606_chip_info_tbl[id];
+
+ if (st->chip_info->oversampling_num) {
+ st->oversampling_avail = st->chip_info->oversampling_avail;
+ st->num_os_ratios = st->chip_info->oversampling_num;
+ }
+
+ ret = ad7606_request_gpios(st);
+ if (ret)
+ return ret;
+
+ if (st->gpio_os) {
+ if (st->gpio_range)
+ indio_dev->info = &ad7606_info_os_and_range;
+ else
+ indio_dev->info = &ad7606_info_os;
+ } else {
+ if (st->gpio_range)
+ indio_dev->info = &ad7606_info_range;
+ else
+ indio_dev->info = &ad7606_info_no_os_or_range;
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ init_completion(&st->completion);
+
+ ret = ad7606_reset(st);
+ if (ret)
+ dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
+
+ /* AD7616 requires al least 15ms to reconfigure after a reset */
+ if (st->chip_info->init_delay_ms) {
+ if (msleep_interruptible(st->chip_info->init_delay_ms))
+ return -ERESTARTSYS;
+ }
+
+ st->write_scale = ad7606_write_scale_hw;
+ st->write_os = ad7606_write_os_hw;
+
+ if (st->bops->sw_mode_config)
+ st->sw_mode_en = device_property_present(st->dev,
+ "adi,sw-mode");
+
+ if (st->sw_mode_en) {
+ /* Scale of 0.076293 is only available in sw mode */
+ st->scale_avail = ad7616_sw_scale_avail;
+ st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+
+ /* After reset, in software mode, ±10 V is set by default */
+ memset32(st->range, 2, ARRAY_SIZE(st->range));
+ indio_dev->info = &ad7606_info_os_range_and_debug;
+
+ ret = st->bops->sw_mode_config(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7606_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ ret = devm_request_threaded_irq(dev, irq,
+ NULL,
+ &ad7606_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7606_trigger_handler,
+ &ad7606_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(ad7606_probe, IIO_AD7606);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int ad7606_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->gpio_standby) {
+ gpiod_set_value(st->gpio_range, 1);
+ gpiod_set_value(st->gpio_standby, 0);
+ }
+
+ return 0;
+}
+
+static int ad7606_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->gpio_standby) {
+ gpiod_set_value(st->gpio_range, st->range[0]);
+ gpiod_set_value(st->gpio_standby, 1);
+ ad7606_reset(st);
+ }
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume);
+EXPORT_SYMBOL_NS_GPL(ad7606_pm_ops, IIO_AD7606);
+
+#endif
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
new file mode 100644
index 000000000..2dc4f599f
--- /dev/null
+++ b/drivers/iio/adc/ad7606.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AD7606 ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#ifndef IIO_ADC_AD7606_H_
+#define IIO_ADC_AD7606_H_
+
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) { \
+ .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 = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define AD7605_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
+ BIT(IIO_CHAN_INFO_SCALE), 0)
+
+#define AD7606_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+#define AD7616_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
+ 0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+/**
+ * struct ad7606_chip_info - chip specific information
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @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 miliseconds for initialization
+ * after a restart
+ */
+struct ad7606_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const unsigned int *oversampling_avail;
+ unsigned int oversampling_num;
+ bool os_req_reset;
+ unsigned long init_delay_ms;
+};
+
+/**
+ * struct ad7606_state - driver instance specific data
+ * @dev pointer to kernel device
+ * @chip_info entry in the table of chips that describes this device
+ * @reg regulator info for the power supply of the device
+ * @bops bus operations (SPI or parallel)
+ * @range voltage range selection, selects which scale to apply
+ * @oversampling oversampling selection
+ * @base_address address from where to read data in parallel operation
+ * @sw_mode_en software mode enabled
+ * @scale_avail pointer to the array which stores the available scales
+ * @num_scales number of elements stored in the scale_avail array
+ * @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),
+ * controls power-down mode of device
+ * @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
+ */
+struct ad7606_state {
+ struct device *dev;
+ const struct ad7606_chip_info *chip_info;
+ struct regulator *reg;
+ const struct ad7606_bus_ops *bops;
+ unsigned int range[16];
+ unsigned int oversampling;
+ void __iomem *base_address;
+ bool sw_mode_en;
+ const unsigned int *scale_avail;
+ unsigned int num_scales;
+ const unsigned int *oversampling_avail;
+ unsigned int num_os_ratios;
+ int (*write_scale)(struct iio_dev *indio_dev, int ch, int val);
+ int (*write_os)(struct iio_dev *indio_dev, int val);
+
+ struct mutex lock; /* protect sensor state */
+ struct gpio_desc *gpio_convst;
+ struct gpio_desc *gpio_reset;
+ struct gpio_desc *gpio_range;
+ struct gpio_desc *gpio_standby;
+ struct gpio_desc *gpio_frstdata;
+ struct gpio_descs *gpio_os;
+ struct iio_trigger *trig;
+ struct completion completion;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * 16 * 16-bit samples + 64-bit timestamp
+ */
+ unsigned short data[20] __aligned(IIO_DMA_MINALIGN);
+ __be16 d16[2];
+};
+
+/**
+ * struct ad7606_bus_ops - driver bus operations
+ * @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
+ * @rd_wr_cmd pointer to the function which calculates the spi address
+ */
+struct ad7606_bus_ops {
+ /* more methods added in future? */
+ 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);
+ u16 (*rd_wr_cmd)(int addr, char isWriteOp);
+};
+
+int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
+ const char *name, unsigned int id,
+ const struct ad7606_bus_ops *bops);
+
+enum ad7606_supported_device_ids {
+ ID_AD7605_4,
+ ID_AD7606_8,
+ ID_AD7606_6,
+ ID_AD7606_4,
+ ID_AD7606B,
+ ID_AD7616,
+};
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops ad7606_pm_ops;
+#define AD7606_PM_OPS (&ad7606_pm_ops)
+#else
+#define AD7606_PM_OPS NULL
+#endif
+
+#endif /* IIO_ADC_AD7606_H_ */
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
new file mode 100644
index 000000000..b912b4df9
--- /dev/null
+++ b/drivers/iio/adc/ad7606_par.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 Parallel Interface ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+static int ad7606_par16_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insw((unsigned long)st->base_address, buf, count);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par16_bops = {
+ .read_block = ad7606_par16_read_block,
+};
+
+static int ad7606_par8_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insb((unsigned long)st->base_address, buf, count * 2);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par8_bops = {
+ .read_block = ad7606_par8_read_block,
+};
+
+static int ad7606_par_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct resource *res;
+ void __iomem *addr;
+ resource_size_t remap_size;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ remap_size = resource_size(res);
+
+ return ad7606_probe(&pdev->dev, irq, addr,
+ id->name, id->driver_data,
+ remap_size > 1 ? &ad7606_par16_bops :
+ &ad7606_par8_bops);
+}
+
+static const struct platform_device_id ad7606_driver_ids[] = {
+ { .name = "ad7605-4", .driver_data = ID_AD7605_4, },
+ { .name = "ad7606-4", .driver_data = ID_AD7606_4, },
+ { .name = "ad7606-6", .driver_data = ID_AD7606_6, },
+ { .name = "ad7606-8", .driver_data = ID_AD7606_8, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
+
+static const struct of_device_id ad7606_of_match[] = {
+ { .compatible = "adi,ad7605-4" },
+ { .compatible = "adi,ad7606-4" },
+ { .compatible = "adi,ad7606-6" },
+ { .compatible = "adi,ad7606-8" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7606_of_match);
+
+static struct platform_driver ad7606_driver = {
+ .probe = ad7606_par_probe,
+ .id_table = ad7606_driver_ids,
+ .driver = {
+ .name = "ad7606",
+ .pm = AD7606_PM_OPS,
+ .of_match_table = ad7606_of_match,
+ },
+};
+module_platform_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD7606);
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
new file mode 100644
index 000000000..263a778bc
--- /dev/null
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/err.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),
+};
+
+static const struct iio_chan_spec ad7606b_sw_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ AD7616_CHANNEL(0),
+ AD7616_CHANNEL(1),
+ AD7616_CHANNEL(2),
+ AD7616_CHANNEL(3),
+ AD7616_CHANNEL(4),
+ AD7616_CHANNEL(5),
+ AD7616_CHANNEL(6),
+ AD7616_CHANNEL(7),
+};
+
+static const unsigned int ad7606B_oversampling_avail[9] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256
+};
+
+static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
+{
+ /*
+ * 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);
+}
+
+static u16 ad7606B_spi_rd_wr_cmd(int addr, char is_write_op)
+{
+ /*
+ * The address of register consists of one bit which
+ * specifies a read command placed in bit 6, followed by
+ * 6 bits of address.
+ */
+ return (addr & 0x3F) | (((~is_write_op) & 0x1) << 6);
+}
+
+static int ad7606_spi_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int i, ret;
+ unsigned short *data = buf;
+ __be16 *bdata = buf;
+
+ ret = spi_read(spi, buf, count * 2);
+ if (ret < 0) {
+ dev_err(&spi->dev, "SPI read error\n");
+ return ret;
+ }
+
+ for (i = 0; i < count; i++)
+ data[i] = be16_to_cpu(bdata[i]);
+
+ return 0;
+}
+
+static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d16[0],
+ .len = 2,
+ .cs_change = 0,
+ }, {
+ .rx_buf = &st->d16[1],
+ .len = 2,
+ },
+ };
+ int ret;
+
+ st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
+
+ ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->d16[1]);
+}
+
+static int ad7606_spi_reg_write(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ 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));
+
+ return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
+}
+
+static int ad7606_spi_write_mask(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val)
+{
+ int readval;
+
+ readval = st->bops->reg_read(st, addr);
+ if (readval < 0)
+ return readval;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr, mode, ch_index;
+
+
+ /*
+ * Ad7616 has 16 channels divided in group A and group B.
+ * The range of channels from A are stored in registers with address 4
+ * while channels from B are stored in register with address 6.
+ * The last bit from channels determines if it is from group A or B
+ * because the order of channels in iio is 0A, 0B, 1A, 1B...
+ */
+ ch_index = ch >> 1;
+
+ ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+ if ((ch & 0x1) == 0) /* channel A */
+ ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+ else /* channel B */
+ ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
+
+ /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+ mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+ return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+ mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_OS_MASK, val << 2);
+}
+
+static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_spi_write_mask(st,
+ AD7606_RANGE_CH_ADDR(ch),
+ AD7606_RANGE_CH_MSK(ch),
+ AD7606_RANGE_CH_MODE(ch, val));
+}
+
+static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_spi_reg_write(st, AD7606_OS_MODE, val);
+}
+
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+ indio_dev->channels = ad7616_sw_channels;
+
+ st->write_scale = ad7616_write_scale_sw;
+ st->write_os = &ad7616_write_os_sw;
+
+ /* Activate Burst mode and SEQEN MODE */
+ return st->bops->write_mask(st,
+ AD7616_CONFIGURATION_REGISTER,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
+static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned long os[3] = {1};
+
+ /*
+ * Software mode is enabled when all three oversampling
+ * pins are set to high. If oversampling gpios are defined
+ * in the device tree, then they need to be set to high,
+ * otherwise, they must be hardwired to VDD
+ */
+ if (st->gpio_os) {
+ gpiod_set_array_value(ARRAY_SIZE(os),
+ st->gpio_os->desc, st->gpio_os->info, os);
+ }
+ /* OS of 128 and 256 are available only in software mode */
+ st->oversampling_avail = ad7606B_oversampling_avail;
+ st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail);
+
+ st->write_scale = ad7606_write_scale_sw;
+ st->write_os = &ad7606_write_os_sw;
+
+ /* Configure device spi to output on a single channel */
+ st->bops->reg_write(st,
+ AD7606_CONFIGURATION_REGISTER,
+ AD7606_SINGLE_DOUT);
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+ indio_dev->channels = ad7606b_sw_channels;
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+};
+
+static const struct ad7606_bus_ops ad7616_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+ .reg_read = ad7606_spi_reg_read,
+ .reg_write = ad7606_spi_reg_write,
+ .write_mask = ad7606_spi_write_mask,
+ .rd_wr_cmd = ad7616_spi_rd_wr_cmd,
+ .sw_mode_config = ad7616_sw_mode_config,
+};
+
+static const struct ad7606_bus_ops ad7606B_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+ .reg_read = ad7606_spi_reg_read,
+ .reg_write = ad7606_spi_reg_write,
+ .write_mask = ad7606_spi_write_mask,
+ .rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
+ .sw_mode_config = ad7606B_sw_mode_config,
+};
+
+static int ad7606_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ const struct ad7606_bus_ops *bops;
+
+ switch (id->driver_data) {
+ case ID_AD7616:
+ bops = &ad7616_spi_bops;
+ break;
+ case ID_AD7606B:
+ bops = &ad7606B_spi_bops;
+ break;
+ default:
+ bops = &ad7606_spi_bops;
+ break;
+ }
+
+ return ad7606_probe(&spi->dev, spi->irq, NULL,
+ id->name, id->driver_data,
+ bops);
+}
+
+static const struct spi_device_id ad7606_id_table[] = {
+ { "ad7605-4", ID_AD7605_4 },
+ { "ad7606-4", ID_AD7606_4 },
+ { "ad7606-6", ID_AD7606_6 },
+ { "ad7606-8", ID_AD7606_8 },
+ { "ad7606b", ID_AD7606B },
+ { "ad7616", ID_AD7616 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7606_id_table);
+
+static const struct of_device_id ad7606_of_match[] = {
+ { .compatible = "adi,ad7605-4" },
+ { .compatible = "adi,ad7606-4" },
+ { .compatible = "adi,ad7606-6" },
+ { .compatible = "adi,ad7606-8" },
+ { .compatible = "adi,ad7606b" },
+ { .compatible = "adi,ad7616" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7606_of_match);
+
+static struct spi_driver ad7606_driver = {
+ .driver = {
+ .name = "ad7606",
+ .of_match_table = ad7606_of_match,
+ .pm = AD7606_PM_OPS,
+ },
+ .probe = ad7606_spi_probe,
+ .id_table = ad7606_id_table,
+};
+module_spi_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD7606);
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
new file mode 100644
index 000000000..3079a0872
--- /dev/null
+++ b/drivers/iio/adc/ad7766.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AD7766/AD7767 SPI ADC driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+struct ad7766_chip_info {
+ unsigned int decimation_factor;
+};
+
+enum {
+ AD7766_SUPPLY_AVDD = 0,
+ AD7766_SUPPLY_DVDD = 1,
+ AD7766_SUPPLY_VREF = 2,
+ AD7766_NUM_SUPPLIES = 3
+};
+
+struct ad7766 {
+ const struct ad7766_chip_info *chip_info;
+ struct spi_device *spi;
+ struct clk *mclk;
+ struct gpio_desc *pd_gpio;
+ struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES];
+
+ struct iio_trigger *trig;
+
+ struct spi_transfer xfer;
+ struct spi_message msg;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * Make the buffer large enough for one 24 bit sample and one 64 bit
+ * aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
+};
+
+/*
+ * AD7766 and AD7767 variations are interface compatible, the main difference is
+ * analog performance. Both parts will use the same ID.
+ */
+enum ad7766_device_ids {
+ ID_AD7766,
+ ID_AD7766_1,
+ ID_AD7766_2,
+};
+
+static irqreturn_t ad7766_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_sync(ad7766->spi, &ad7766->msg);
+ if (ret < 0)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data,
+ pf->timestamp);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7766_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+ if (ret < 0) {
+ dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ad7766->mclk);
+ if (ret < 0) {
+ dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret);
+ regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+ return ret;
+ }
+
+ gpiod_set_value(ad7766->pd_gpio, 0);
+
+ return 0;
+}
+
+static int ad7766_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+
+ gpiod_set_value(ad7766->pd_gpio, 1);
+
+ /*
+ * The PD pin is synchronous to the clock, so give it some time to
+ * notice the change before we disable the clock.
+ */
+ msleep(20);
+
+ clk_disable_unprepare(ad7766->mclk);
+ regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+
+ return 0;
+}
+
+static int ad7766_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+ struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer;
+ int scale_uv;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = regulator_get_voltage(vref);
+ if (scale_uv < 0)
+ return scale_uv;
+ *val = scale_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = clk_get_rate(ad7766->mclk) /
+ ad7766->chip_info->decimation_factor;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec ad7766_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7766_chip_info ad7766_chip_info[] = {
+ [ID_AD7766] = {
+ .decimation_factor = 8,
+ },
+ [ID_AD7766_1] = {
+ .decimation_factor = 16,
+ },
+ [ID_AD7766_2] = {
+ .decimation_factor = 32,
+ },
+};
+
+static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = {
+ .preenable = &ad7766_preenable,
+ .postdisable = &ad7766_postdisable,
+};
+
+static const struct iio_info ad7766_info = {
+ .read_raw = &ad7766_read_raw,
+};
+
+static irqreturn_t ad7766_irq(int irq, void *private)
+{
+ iio_trigger_poll(private);
+ return IRQ_HANDLED;
+}
+
+static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
+
+ if (enable)
+ enable_irq(ad7766->spi->irq);
+ else
+ disable_irq(ad7766->spi->irq);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops ad7766_trigger_ops = {
+ .set_trigger_state = ad7766_set_trigger_state,
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int ad7766_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct ad7766 *ad7766;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ad7766 = iio_priv(indio_dev);
+ ad7766->chip_info = &ad7766_chip_info[id->driver_data];
+
+ ad7766->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(ad7766->mclk))
+ return PTR_ERR(ad7766->mclk);
+
+ ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd";
+ ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd";
+ ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref";
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg),
+ ad7766->reg);
+ if (ret)
+ return ret;
+
+ ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ad7766->pd_gpio))
+ return PTR_ERR(ad7766->pd_gpio);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7766_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7766_channels);
+ indio_dev->info = &ad7766_info;
+
+ if (spi->irq > 0) {
+ ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!ad7766->trig)
+ return -ENOMEM;
+
+ ad7766->trig->ops = &ad7766_trigger_ops;
+ iio_trigger_set_drvdata(ad7766->trig, ad7766);
+
+ /*
+ * The device generates interrupts as long as it is powered up.
+ * Some platforms might not allow the option to power it down so
+ * don't enable the interrupt to avoid extra load on the system
+ */
+ ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
+ IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
+ dev_name(&spi->dev),
+ ad7766->trig);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_iio_trigger_register(&spi->dev, ad7766->trig);
+ if (ret)
+ return ret;
+ }
+
+ ad7766->spi = spi;
+
+ /* First byte always 0 */
+ ad7766->xfer.rx_buf = &ad7766->data[1];
+ ad7766->xfer.len = 3;
+
+ spi_message_init(&ad7766->msg);
+ spi_message_add_tail(&ad7766->xfer, &ad7766->msg);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time, &ad7766_trigger_handler,
+ &ad7766_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7766_id[] = {
+ {"ad7766", ID_AD7766},
+ {"ad7766-1", ID_AD7766_1},
+ {"ad7766-2", ID_AD7766_2},
+ {"ad7767", ID_AD7766},
+ {"ad7767-1", ID_AD7766_1},
+ {"ad7767-2", ID_AD7766_2},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7766_id);
+
+static struct spi_driver ad7766_driver = {
+ .driver = {
+ .name = "ad7766",
+ },
+ .probe = ad7766_probe,
+ .id_table = ad7766_id,
+};
+module_spi_driver(ad7766_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
new file mode 100644
index 000000000..70a259491
--- /dev/null
+++ b/drivers/iio/adc/ad7768-1.c
@@ -0,0 +1,677 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7768-1 SPI ADC driver
+ *
+ * Copyright 2017 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/buffer.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>
+
+/* AD7768 registers definition */
+#define AD7768_REG_CHIP_TYPE 0x3
+#define AD7768_REG_PROD_ID_L 0x4
+#define AD7768_REG_PROD_ID_H 0x5
+#define AD7768_REG_CHIP_GRADE 0x6
+#define AD7768_REG_SCRATCH_PAD 0x0A
+#define AD7768_REG_VENDOR_L 0x0C
+#define AD7768_REG_VENDOR_H 0x0D
+#define AD7768_REG_INTERFACE_FORMAT 0x14
+#define AD7768_REG_POWER_CLOCK 0x15
+#define AD7768_REG_ANALOG 0x16
+#define AD7768_REG_ANALOG2 0x17
+#define AD7768_REG_CONVERSION 0x18
+#define AD7768_REG_DIGITAL_FILTER 0x19
+#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A
+#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B
+#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C
+#define AD7768_REG_SYNC_RESET 0x1D
+#define AD7768_REG_GPIO_CONTROL 0x1E
+#define AD7768_REG_GPIO_WRITE 0x1F
+#define AD7768_REG_GPIO_READ 0x20
+#define AD7768_REG_OFFSET_HI 0x21
+#define AD7768_REG_OFFSET_MID 0x22
+#define AD7768_REG_OFFSET_LO 0x23
+#define AD7768_REG_GAIN_HI 0x24
+#define AD7768_REG_GAIN_MID 0x25
+#define AD7768_REG_GAIN_LO 0x26
+#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_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
+
+/* AD7768_REG_POWER_CLOCK */
+#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4)
+#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x)
+#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0)
+#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x)
+
+/* AD7768_REG_DIGITAL_FILTER */
+#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4)
+#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x)
+#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
+#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
+
+/* AD7768_REG_CONVERSION */
+#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,
+ AD7768_SINGLE,
+ AD7768_PERIODIC,
+ AD7768_STANDBY
+};
+
+enum ad7768_pwrmode {
+ AD7768_ECO_MODE = 0,
+ AD7768_MED_MODE = 2,
+ AD7768_FAST_MODE = 3
+};
+
+enum ad7768_mclk_div {
+ AD7768_MCLK_DIV_16,
+ AD7768_MCLK_DIV_8,
+ AD7768_MCLK_DIV_4,
+ AD7768_MCLK_DIV_2
+};
+
+enum ad7768_dec_rate {
+ AD7768_DEC_RATE_32 = 0,
+ AD7768_DEC_RATE_64 = 1,
+ AD7768_DEC_RATE_128 = 2,
+ AD7768_DEC_RATE_256 = 3,
+ AD7768_DEC_RATE_512 = 4,
+ AD7768_DEC_RATE_1024 = 5,
+ AD7768_DEC_RATE_8 = 9,
+ AD7768_DEC_RATE_16 = 10
+};
+
+struct ad7768_clk_configuration {
+ enum ad7768_mclk_div mclk_div;
+ enum ad7768_dec_rate dec_rate;
+ unsigned int clk_div;
+ enum ad7768_pwrmode pwrmode;
+};
+
+static const struct ad7768_clk_configuration ad7768_clk_config[] = {
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
+};
+
+static const struct iio_chan_spec ad7768_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .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),
+ .indexed = 1,
+ .channel = 0,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ },
+};
+
+struct ad7768_state {
+ struct spi_device *spi;
+ 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;
+ const char *labels[ARRAY_SIZE(ad7768_channels)];
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ struct {
+ __be32 chan;
+ 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;
+
+ shift = 32 - (8 * len);
+ st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
+
+ ret = spi_write_then_read(st->spi, st->data.d8, 1,
+ &st->data.d32, len);
+ if (ret < 0)
+ return ret;
+
+ return (be32_to_cpu(st->data.d32) >> shift);
+}
+
+static int ad7768_spi_reg_write(struct ad7768_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
+ st->data.d8[1] = val & 0xFF;
+
+ return spi_write(st->spi, st->data.d8, 2);
+}
+
+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);
+}
+
+static int ad7768_scan_direct(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int readval, ret;
+
+ reinit_completion(&st->completion);
+
+ ret = ad7768_set_mode(st, AD7768_ONE_SHOT);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
+ if (readval < 0)
+ return readval;
+ /*
+ * Any SPI configuration of the AD7768-1 can only be
+ * performed in continuous conversion mode.
+ */
+ ret = ad7768_set_mode(st, AD7768_CONTINUOUS);
+ if (ret < 0)
+ return ret;
+
+ return readval;
+}
+
+static int ad7768_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = ad7768_spi_reg_read(st, reg, 1);
+ if (ret < 0)
+ goto err_unlock;
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = ad7768_spi_reg_write(st, reg, writeval);
+ }
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad7768_set_dig_fil(struct ad7768_state *st,
+ enum ad7768_dec_rate dec_rate)
+{
+ unsigned int mode;
+ int ret;
+
+ if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
+ mode = AD7768_DIG_FIL_FIL(dec_rate);
+ else
+ mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
+
+ ret = ad7768_spi_reg_write(st, 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;
+}
+
+static int ad7768_set_freq(struct ad7768_state *st,
+ unsigned int freq)
+{
+ unsigned int diff_new, diff_old, pwr_mode, i, idx;
+ int res, ret;
+
+ diff_old = U32_MAX;
+ idx = 0;
+
+ res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
+
+ /* Find the closest match for the desired sampling frequency */
+ for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
+ diff_new = abs(res - ad7768_clk_config[i].clk_div);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ /*
+ * Set both the mclk_div and pwrmode with a single write to the
+ * POWER_CLOCK register
+ */
+ 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);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
+ if (ret < 0)
+ return ret;
+
+ st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
+ ad7768_clk_config[idx].clk_div);
+
+ return 0;
+}
+
+static ssize_t ad7768_sampling_freq_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int freq;
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
+ freq = DIV_ROUND_CLOSEST(st->mclk_freq,
+ ad7768_clk_config[i].clk_div);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
+
+static int ad7768_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int scale_uv, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7768_scan_direct(indio_dev);
+ if (ret >= 0)
+ *val = ret;
+
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = regulator_get_voltage(st->vref);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ *val = (scale_uv * 2) / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->samp_freq;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad7768_set_freq(st, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7768_read_label(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, char *label)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ return sprintf(label, "%s\n", st->labels[chan->channel]);
+}
+
+static struct attribute *ad7768_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7768_group = {
+ .attrs = ad7768_attributes,
+};
+
+static const struct iio_info ad7768_info = {
+ .attrs = &ad7768_group,
+ .read_raw = &ad7768_read_raw,
+ .write_raw = &ad7768_write_raw,
+ .read_label = ad7768_read_label,
+ .debugfs_reg_access = &ad7768_reg_access,
+};
+
+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;
+
+ ret = ad7768_spi_reg_write(st, 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);
+ if (IS_ERR(st->gpio_sync_in))
+ return PTR_ERR(st->gpio_sync_in);
+
+ /* Set the default sampling frequency to 32000 kSPS */
+ return ad7768_set_freq(st, 32000);
+}
+
+static irqreturn_t ad7768_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ 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;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan,
+ iio_get_time_ns(indio_dev));
+
+err_unlock:
+ iio_trigger_notify_done(indio_dev->trig);
+ mutex_unlock(&st->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ad7768_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev))
+ iio_trigger_poll(st->trig);
+ else
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+};
+
+static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ /*
+ * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter
+ * 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);
+}
+
+static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ /*
+ * 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);
+}
+
+static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
+ .postenable = &ad7768_buffer_postenable,
+ .predisable = &ad7768_buffer_predisable,
+};
+
+static const struct iio_trigger_ops ad7768_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static void ad7768_regulator_disable(void *data)
+{
+ struct ad7768_state *st = data;
+
+ regulator_disable(st->vref);
+}
+
+static int ad7768_set_channel_label(struct iio_dev *indio_dev,
+ int num_channels)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ struct device *device = indio_dev->dev.parent;
+ struct fwnode_handle *fwnode;
+ struct fwnode_handle *child;
+ const char *label;
+ int crt_ch = 0;
+
+ fwnode = dev_fwnode(device);
+ fwnode_for_each_child_node(fwnode, child) {
+ if (fwnode_property_read_u32(child, "reg", &crt_ch))
+ continue;
+
+ if (crt_ch >= num_channels)
+ continue;
+
+ if (fwnode_property_read_string(child, "label", &label))
+ continue;
+
+ st->labels[crt_ch] = label;
+ }
+
+ return 0;
+}
+
+static int ad7768_probe(struct spi_device *spi)
+{
+ struct ad7768_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ st->vref = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = regulator_enable(st->vref);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified vref supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
+ if (IS_ERR(st->mclk))
+ return PTR_ERR(st->mclk);
+
+ 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;
+ indio_dev->info = &ad7768_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad7768_setup(st);
+ if (ret < 0) {
+ dev_err(&spi->dev, "AD7768 setup failed\n");
+ 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));
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ &ad7768_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7768_trigger_handler,
+ &ad7768_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7768_id_table[] = {
+ { "ad7768-1", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7768_id_table);
+
+static const struct of_device_id ad7768_of_match[] = {
+ { .compatible = "adi,ad7768-1" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7768_of_match);
+
+static struct spi_driver ad7768_driver = {
+ .driver = {
+ .name = "ad7768-1",
+ .of_match_table = ad7768_of_match,
+ },
+ .probe = ad7768_probe,
+ .id_table = ad7768_id_table,
+};
+module_spi_driver(ad7768_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
new file mode 100644
index 000000000..a813fe047
--- /dev/null
+++ b/drivers/iio/adc/ad7780.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Copyright 2019 Renato Lui Geh
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/bits.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define AD7780_RDY BIT(7)
+#define AD7780_FILTER BIT(6)
+#define AD7780_ERR BIT(5)
+#define AD7780_ID1 BIT(4)
+#define AD7780_ID0 BIT(3)
+#define AD7780_GAIN BIT(2)
+
+#define AD7170_ID 0
+#define AD7171_ID 1
+#define AD7780_ID 1
+#define AD7781_ID 0
+
+#define AD7780_ID_MASK (AD7780_ID0 | AD7780_ID1)
+
+#define AD7780_PATTERN_GOOD 1
+#define AD7780_PATTERN_MASK GENMASK(1, 0)
+
+#define AD7170_PATTERN_GOOD 5
+#define AD7170_PATTERN_MASK GENMASK(2, 0)
+
+#define AD7780_GAIN_MIDPOINT 64
+#define AD7780_FILTER_MIDPOINT 13350
+
+static const unsigned int ad778x_gain[2] = { 1, 128 };
+static const unsigned int ad778x_odr_avail[2] = { 10000, 16700 };
+
+struct ad7780_chip_info {
+ struct iio_chan_spec channel;
+ unsigned int pattern_mask;
+ unsigned int pattern;
+ bool is_ad778x;
+};
+
+struct ad7780_state {
+ const struct ad7780_chip_info *chip_info;
+ struct regulator *reg;
+ struct gpio_desc *powerdown_gpio;
+ struct gpio_desc *gain_gpio;
+ struct gpio_desc *filter_gpio;
+ unsigned int gain;
+ unsigned int odr;
+ unsigned int int_vref_mv;
+
+ struct ad_sigma_delta sd;
+};
+
+enum ad7780_supported_device_ids {
+ ID_AD7170,
+ ID_AD7171,
+ ID_AD7780,
+ ID_AD7781,
+};
+
+static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7780_state, sd);
+}
+
+static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ unsigned int val;
+
+ switch (mode) {
+ case AD_SD_MODE_SINGLE:
+ case AD_SD_MODE_CONTINUOUS:
+ val = 1;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ gpiod_set_value(st->powerdown_gpio, val);
+
+ return 0;
+}
+
+static int ad7780_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+ int voltage_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ voltage_uv = regulator_get_voltage(st->reg);
+ if (voltage_uv < 0)
+ return voltage_uv;
+ voltage_uv /= 1000;
+ *val = voltage_uv * st->gain;
+ *val2 = chan->scan_type.realbits - 1;
+ st->int_vref_mv = voltage_uv;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->odr;
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7780_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+ unsigned long long vref;
+ unsigned int full_scale, gain;
+
+ if (!chip_info->is_ad778x)
+ return -EINVAL;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ vref = st->int_vref_mv * 1000000LL;
+ full_scale = 1 << (chip_info->channel.scan_type.realbits - 1);
+ gain = DIV_ROUND_CLOSEST_ULL(vref, full_scale);
+ gain = DIV_ROUND_CLOSEST(gain, val2);
+ st->gain = gain;
+ if (gain < AD7780_GAIN_MIDPOINT)
+ gain = 0;
+ else
+ gain = 1;
+ gpiod_set_value(st->gain_gpio, gain);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (1000*val + val2/1000 < AD7780_FILTER_MIDPOINT)
+ val = 0;
+ else
+ val = 1;
+ st->odr = ad778x_odr_avail[val];
+ gpiod_set_value(st->filter_gpio, val);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
+ unsigned int raw_sample)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+
+ if ((raw_sample & AD7780_ERR) ||
+ ((raw_sample & chip_info->pattern_mask) != chip_info->pattern))
+ return -EIO;
+
+ if (chip_info->is_ad778x) {
+ st->gain = ad778x_gain[raw_sample & AD7780_GAIN];
+ st->odr = ad778x_odr_avail[raw_sample & AD7780_FILTER];
+ }
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
+ .set_mode = ad7780_set_mode,
+ .postprocess_sample = ad7780_postprocess_sample,
+ .has_registers = false,
+ .irq_flags = IRQF_TRIGGER_FALLING,
+};
+
+#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] = {
+ .channel = AD7170_CHANNEL(12, 24),
+ .pattern = AD7170_PATTERN_GOOD,
+ .pattern_mask = AD7170_PATTERN_MASK,
+ .is_ad778x = false,
+ },
+ [ID_AD7171] = {
+ .channel = AD7170_CHANNEL(16, 24),
+ .pattern = AD7170_PATTERN_GOOD,
+ .pattern_mask = AD7170_PATTERN_MASK,
+ .is_ad778x = false,
+ },
+ [ID_AD7780] = {
+ .channel = AD7780_CHANNEL(24, 32),
+ .pattern = AD7780_PATTERN_GOOD,
+ .pattern_mask = AD7780_PATTERN_MASK,
+ .is_ad778x = true,
+ },
+ [ID_AD7781] = {
+ .channel = AD7780_CHANNEL(20, 32),
+ .pattern = AD7780_PATTERN_GOOD,
+ .pattern_mask = AD7780_PATTERN_MASK,
+ .is_ad778x = true,
+ },
+};
+
+static const struct iio_info ad7780_info = {
+ .read_raw = ad7780_read_raw,
+ .write_raw = ad7780_write_raw,
+};
+
+static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st)
+{
+ int ret;
+
+ st->powerdown_gpio = devm_gpiod_get_optional(dev,
+ "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->powerdown_gpio)) {
+ ret = PTR_ERR(st->powerdown_gpio);
+ dev_err(dev, "Failed to request powerdown GPIO: %d\n", ret);
+ return ret;
+ }
+
+ if (!st->chip_info->is_ad778x)
+ return 0;
+
+
+ st->gain_gpio = devm_gpiod_get_optional(dev,
+ "adi,gain",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gain_gpio)) {
+ ret = PTR_ERR(st->gain_gpio);
+ dev_err(dev, "Failed to request gain GPIO: %d\n", ret);
+ return ret;
+ }
+
+ st->filter_gpio = devm_gpiod_get_optional(dev,
+ "adi,filter",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->filter_gpio)) {
+ ret = PTR_ERR(st->filter_gpio);
+ dev_err(dev, "Failed to request filter GPIO: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ad7780_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int ad7780_probe(struct spi_device *spi)
+{
+ struct ad7780_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->gain = 1;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
+
+ st->chip_info =
+ &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &st->chip_info->channel;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &ad7780_info;
+
+ ret = ad7780_init_gpios(&spi->dev, st);
+ if (ret)
+ return ret;
+
+ st->reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7780_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7780_id[] = {
+ {"ad7170", ID_AD7170},
+ {"ad7171", ID_AD7171},
+ {"ad7780", ID_AD7780},
+ {"ad7781", ID_AD7781},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7780_id);
+
+static struct spi_driver ad7780_driver = {
+ .driver = {
+ .name = "ad7780",
+ },
+ .probe = ad7780_probe,
+ .id_table = ad7780_id,
+};
+module_spi_driver(ad7780_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
new file mode 100644
index 000000000..86effe850
--- /dev/null
+++ b/drivers/iio/adc/ad7791.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include <linux/platform_data/ad7791.h>
+
+#define AD7791_REG_COMM 0x0 /* For writes */
+#define AD7791_REG_STATUS 0x0 /* For reads */
+#define AD7791_REG_MODE 0x1
+#define AD7791_REG_FILTER 0x2
+#define AD7791_REG_DATA 0x3
+
+#define AD7791_MODE_CONTINUOUS 0x00
+#define AD7791_MODE_SINGLE 0x02
+#define AD7791_MODE_POWERDOWN 0x03
+
+#define AD7791_CH_AIN1P_AIN1N 0x00
+#define AD7791_CH_AIN2 0x01
+#define AD7791_CH_AIN1N_AIN1N 0x02
+#define AD7791_CH_AVDD_MONITOR 0x03
+
+#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4)
+#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4)
+#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4)
+#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4)
+#define AD7791_FILTER_CLK_MASK (0x3 << 4)
+#define AD7791_FILTER_RATE_120 0x0
+#define AD7791_FILTER_RATE_100 0x1
+#define AD7791_FILTER_RATE_33_3 0x2
+#define AD7791_FILTER_RATE_20 0x3
+#define AD7791_FILTER_RATE_16_6 0x4
+#define AD7791_FILTER_RATE_16_7 0x5
+#define AD7791_FILTER_RATE_13_3 0x6
+#define AD7791_FILTER_RATE_9_5 0x7
+#define AD7791_FILTER_RATE_MASK 0x7
+
+#define AD7791_MODE_BUFFER BIT(1)
+#define AD7791_MODE_UNIPOLAR BIT(2)
+#define AD7791_MODE_BURNOUT BIT(3)
+#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[] = { \
+ AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD7991_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
+ AD7991_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
+ (bits), (storagebits), 0), \
+ 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[] = { \
+ AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD7991_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD7991_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
+ (bits), (storagebits), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(3), \
+}
+
+static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
+static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
+static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
+
+enum {
+ AD7787,
+ AD7788,
+ AD7789,
+ AD7790,
+ AD7791,
+};
+
+enum ad7791_chip_info_flags {
+ AD7791_FLAG_HAS_FILTER = (1 << 0),
+ AD7791_FLAG_HAS_BUFFER = (1 << 1),
+ AD7791_FLAG_HAS_UNIPOLAR = (1 << 2),
+ AD7791_FLAG_HAS_BURNOUT = (1 << 3),
+};
+
+struct ad7791_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ enum ad7791_chip_info_flags flags;
+};
+
+static const struct ad7791_chip_info ad7791_chip_infos[] = {
+ [AD7787] = {
+ .channels = ad7787_channels,
+ .num_channels = ARRAY_SIZE(ad7787_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+ },
+ [AD7788] = {
+ .channels = ad7790_channels,
+ .num_channels = ARRAY_SIZE(ad7790_channels),
+ .flags = AD7791_FLAG_HAS_UNIPOLAR,
+ },
+ [AD7789] = {
+ .channels = ad7791_channels,
+ .num_channels = ARRAY_SIZE(ad7791_channels),
+ .flags = AD7791_FLAG_HAS_UNIPOLAR,
+ },
+ [AD7790] = {
+ .channels = ad7790_channels,
+ .num_channels = ARRAY_SIZE(ad7790_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_BURNOUT,
+ },
+ [AD7791] = {
+ .channels = ad7791_channels,
+ .num_channels = ARRAY_SIZE(ad7791_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+ },
+};
+
+struct ad7791_state {
+ struct ad_sigma_delta sd;
+ uint8_t mode;
+ uint8_t filter;
+
+ struct regulator *reg;
+ const struct ad7791_chip_info *info;
+};
+
+static const int ad7791_sample_freq_avail[8][2] = {
+ [AD7791_FILTER_RATE_120] = { 120, 0 },
+ [AD7791_FILTER_RATE_100] = { 100, 0 },
+ [AD7791_FILTER_RATE_33_3] = { 33, 300000 },
+ [AD7791_FILTER_RATE_20] = { 20, 0 },
+ [AD7791_FILTER_RATE_16_6] = { 16, 600000 },
+ [AD7791_FILTER_RATE_16_7] = { 16, 700000 },
+ [AD7791_FILTER_RATE_13_3] = { 13, 300000 },
+ [AD7791_FILTER_RATE_9_5] = { 9, 500000 },
+};
+
+static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7791_state, sd);
+}
+
+static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ ad_sd_set_comm(sd, channel);
+
+ return 0;
+}
+
+static int ad7791_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
+
+ switch (mode) {
+ case AD_SD_MODE_CONTINUOUS:
+ mode = AD7791_MODE_CONTINUOUS;
+ break;
+ case AD_SD_MODE_SINGLE:
+ mode = AD7791_MODE_SINGLE;
+ break;
+ case AD_SD_MODE_IDLE:
+ case AD_SD_MODE_POWERDOWN:
+ mode = AD7791_MODE_POWERDOWN;
+ break;
+ }
+
+ st->mode &= ~AD7791_MODE_SEL_MASK;
+ st->mode |= AD7791_MODE_SEL(mode);
+
+ return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
+ .set_channel = ad7791_set_channel,
+ .set_mode = ad7791_set_mode,
+ .has_registers = true,
+ .addr_shift = 4,
+ .read_mask = BIT(3),
+ .irq_flags = IRQF_TRIGGER_FALLING,
+};
+
+static int ad7791_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad7791_state *st = iio_priv(indio_dev);
+ bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
+ unsigned int rate;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_OFFSET:
+ /**
+ * Unipolar: 0 to VREF
+ * Bipolar -VREF to VREF
+ **/
+ if (unipolar)
+ *val = 0;
+ else
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* The monitor channel uses an internal reference. */
+ if (chan->address == AD7791_CH_AVDD_MONITOR) {
+ /*
+ * The signal is attenuated by a factor of 5 and
+ * compared against a 1.17V internal reference.
+ */
+ *val = 1170 * 5;
+ } else {
+ int voltage_uv;
+
+ voltage_uv = regulator_get_voltage(st->reg);
+ if (voltage_uv < 0)
+ return voltage_uv;
+
+ *val = voltage_uv / 1000;
+ }
+ if (unipolar)
+ *val2 = chan->scan_type.realbits;
+ else
+ *val2 = chan->scan_type.realbits - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ rate = st->filter & AD7791_FILTER_RATE_MASK;
+ *val = ad7791_sample_freq_avail[rate][0];
+ *val2 = ad7791_sample_freq_avail[rate][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+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;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
+ if (ad7791_sample_freq_avail[i][0] == val &&
+ ad7791_sample_freq_avail[i][1] == val2)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ st->filter &= ~AD7791_FILTER_RATE_MASK;
+ st->filter |= i;
+ ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
+ sizeof(st->filter),
+ st->filter);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
+
+static struct attribute *ad7791_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7791_attribute_group = {
+ .attrs = ad7791_attributes,
+};
+
+static const struct iio_info ad7791_info = {
+ .read_raw = &ad7791_read_raw,
+ .write_raw = &ad7791_write_raw,
+ .attrs = &ad7791_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+};
+
+static const struct iio_info ad7791_no_filter_info = {
+ .read_raw = &ad7791_read_raw,
+ .write_raw = &ad7791_write_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+};
+
+static int ad7791_setup(struct ad7791_state *st,
+ struct ad7791_platform_data *pdata)
+{
+ /* Set to poweron-reset default values */
+ st->mode = AD7791_MODE_BUFFER;
+ st->filter = AD7791_FILTER_RATE_16_6;
+
+ if (!pdata)
+ return 0;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
+ st->mode &= ~AD7791_MODE_BUFFER;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
+ pdata->burnout_current)
+ st->mode |= AD7791_MODE_BURNOUT;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
+ st->mode |= AD7791_MODE_UNIPOLAR;
+
+ return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
+ st->mode);
+}
+
+static void ad7791_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int ad7791_probe(struct spi_device *spi)
+{
+ struct ad7791_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad7791_state *st;
+ int ret;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "Missing IRQ.\n");
+ return -ENXIO;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7791_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->info->channels;
+ indio_dev->num_channels = st->info->num_channels;
+ if (st->info->flags & AD7791_FLAG_HAS_FILTER)
+ indio_dev->info = &ad7791_info;
+ else
+ indio_dev->info = &ad7791_no_filter_info;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7791_setup(st, pdata);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7791_spi_ids[] = {
+ { "ad7787", AD7787 },
+ { "ad7788", AD7788 },
+ { "ad7789", AD7789 },
+ { "ad7790", AD7790 },
+ { "ad7791", AD7791 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
+
+static struct spi_driver ad7791_driver = {
+ .driver = {
+ .name = "ad7791",
+ },
+ .probe = ad7791_probe,
+ .id_table = ad7791_spi_ids,
+};
+module_spi_driver(ad7791_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
new file mode 100644
index 000000000..5f8cb9aaa
--- /dev/null
+++ b/drivers/iio/adc/ad7793.c
@@ -0,0 +1,870 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver
+ *
+ * Copyright 2011-2012 Analog Devices Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+#include <linux/platform_data/ad7793.h>
+
+/* Registers */
+#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */
+#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */
+#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */
+#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */
+#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */
+#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */
+#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */
+#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit
+ * (AD7792)/24-bit (AD7793)) */
+#define AD7793_REG_FULLSALE 7 /* Full-Scale Register
+ * (RW, 16-bit (AD7792)/24-bit (AD7793)) */
+
+/* Communications Register Bit Designations (AD7793_REG_COMM) */
+#define AD7793_COMM_WEN (1 << 7) /* Write Enable */
+#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */
+#define AD7793_COMM_READ (1 << 6) /* Read Operation */
+#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */
+#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */
+
+/* Status Register Bit Designations (AD7793_REG_STAT) */
+#define AD7793_STAT_RDY (1 << 7) /* Ready */
+#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */
+#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */
+#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */
+#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */
+
+/* Mode Register Bit Designations (AD7793_REG_MODE) */
+#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */
+#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */
+#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */
+#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */
+
+#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */
+#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */
+#define AD7793_MODE_IDLE 2 /* Idle Mode */
+#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */
+#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */
+#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */
+#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */
+#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */
+
+#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not
+ * available at the CLK pin */
+#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available
+ * at the CLK pin */
+#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */
+#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */
+
+/* Configuration Register Bit Designations (AD7793_REG_CONF) */
+#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage
+ * Generator Enable */
+#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */
+#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */
+#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */
+#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */
+#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */
+#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */
+#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */
+#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */
+
+#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */
+#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */
+#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */
+#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */
+#define AD7793_CH_TEMP 6 /* Temp Sensor */
+#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */
+
+#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */
+#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */
+#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */
+#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */
+
+/* ID Register Bit Designations (AD7793_REG_ID) */
+#define AD7785_ID 0x3
+#define AD7792_ID 0xA
+#define AD7793_ID 0xB
+#define AD7794_ID 0xF
+#define AD7795_ID 0xF
+#define AD7796_ID 0xA
+#define AD7797_ID 0xB
+#define AD7798_ID 0x8
+#define AD7799_ID 0x9
+#define AD7793_ID_MASK 0xF
+
+/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */
+#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1,
+ * IEXC2 connect to IOUT2 */
+#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2,
+ * IEXC2 connect to IOUT1 */
+#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources
+ * IEXC1,2 connect to IOUT1 */
+#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources
+ * IEXC1,2 connect to IOUT2 */
+
+#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */
+#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */
+#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */
+
+/* NOTE:
+ * The AD7792/AD7793 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.
+ */
+
+#define AD7793_FLAG_HAS_CLKSEL BIT(0)
+#define AD7793_FLAG_HAS_REFSEL BIT(1)
+#define AD7793_FLAG_HAS_VBIAS BIT(2)
+#define AD7793_HAS_EXITATION_CURRENT BIT(3)
+#define AD7793_FLAG_HAS_GAIN BIT(4)
+#define AD7793_FLAG_HAS_BUFFER BIT(5)
+
+struct ad7793_chip_info {
+ unsigned int id;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int flags;
+
+ const struct iio_info *iio_info;
+ const u16 *sample_freq_avail;
+};
+
+struct ad7793_state {
+ const struct ad7793_chip_info *chip_info;
+ struct regulator *reg;
+ u16 int_vref_mv;
+ u16 mode;
+ u16 conf;
+ u32 scale_avail[8][2];
+
+ struct ad_sigma_delta sd;
+
+};
+
+enum ad7793_supported_device_ids {
+ ID_AD7785,
+ ID_AD7792,
+ ID_AD7793,
+ ID_AD7794,
+ ID_AD7795,
+ ID_AD7796,
+ ID_AD7797,
+ ID_AD7798,
+ ID_AD7799,
+};
+
+static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7793_state, sd);
+}
+
+static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
+
+ st->conf &= ~AD7793_CONF_CHAN_MASK;
+ st->conf |= AD7793_CONF_CHAN(channel);
+
+ return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf);
+}
+
+static int ad7793_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
+
+ st->mode &= ~AD7793_MODE_SEL_MASK;
+ st->mode |= AD7793_MODE_SEL(mode);
+
+ return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7793_sigma_delta_info = {
+ .set_channel = ad7793_set_channel,
+ .set_mode = ad7793_set_mode,
+ .has_registers = true,
+ .addr_shift = 3,
+ .read_mask = BIT(6),
+ .irq_flags = IRQF_TRIGGER_FALLING,
+};
+
+static const struct ad_sd_calib_data ad7793_calib_arr[6] = {
+ {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M},
+ {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M},
+ {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M},
+ {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M},
+ {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M},
+ {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M}
+};
+
+static int ad7793_calibrate_all(struct ad7793_state *st)
+{
+ return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr,
+ ARRAY_SIZE(ad7793_calib_arr));
+}
+
+static int ad7793_check_platform_data(struct ad7793_state *st,
+ const struct ad7793_platform_data *pdata)
+{
+ if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 ||
+ pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) &&
+ ((pdata->exitation_current != AD7793_IX_10uA) &&
+ (pdata->exitation_current != AD7793_IX_210uA)))
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) &&
+ pdata->clock_src != AD7793_CLK_SRC_INT)
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) &&
+ pdata->refsel != AD7793_REFSEL_REFIN1)
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) &&
+ pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED)
+ return -EINVAL;
+
+ if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) &&
+ pdata->exitation_current != AD7793_IX_DISABLED)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ad7793_setup(struct iio_dev *indio_dev,
+ const struct ad7793_platform_data *pdata,
+ unsigned int vref_mv)
+{
+ struct ad7793_state *st = iio_priv(indio_dev);
+ int i, ret;
+ unsigned long long scale_uv;
+ u32 id;
+
+ ret = ad7793_check_platform_data(st, pdata);
+ if (ret)
+ return ret;
+
+ /* reset the serial interface */
+ ret = ad_sd_reset(&st->sd, 32);
+ if (ret < 0)
+ goto out;
+ usleep_range(500, 2000); /* Wait for at least 500us */
+
+ /* write/read test for device presence */
+ ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id);
+ if (ret)
+ goto out;
+
+ id &= AD7793_ID_MASK;
+
+ if (id != st->chip_info->id) {
+ ret = -ENODEV;
+ dev_err(&st->sd.spi->dev, "device ID query failed\n");
+ goto out;
+ }
+
+ st->mode = AD7793_MODE_RATE(1);
+ st->conf = 0;
+
+ if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL)
+ st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src);
+ if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL)
+ st->conf |= AD7793_CONF_REFSEL(pdata->refsel);
+ if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)
+ st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage);
+ if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER))
+ st->conf |= AD7793_CONF_BUF;
+ if (pdata->boost_enable &&
+ (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS))
+ st->conf |= AD7793_CONF_BOOST;
+ if (pdata->burnout_current)
+ st->conf |= AD7793_CONF_BO_EN;
+ if (pdata->unipolar)
+ st->conf |= AD7793_CONF_UNIPOLAR;
+
+ if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN))
+ st->conf |= AD7793_CONF_GAIN(7);
+
+ ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE);
+ if (ret)
+ goto out;
+
+ ret = ad7793_set_channel(&st->sd, 0);
+ if (ret)
+ goto out;
+
+ if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) {
+ ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1,
+ pdata->exitation_current |
+ (pdata->current_source_direction << 2));
+ if (ret)
+ goto out;
+ }
+
+ ret = ad7793_calibrate_all(st);
+ if (ret)
+ goto out;
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
+ scale_uv = ((u64)vref_mv * 100000000)
+ >> (st->chip_info->channels[0].scan_type.realbits -
+ (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1));
+ scale_uv >>= i;
+
+ st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10;
+ st->scale_avail[i][0] = scale_uv;
+ }
+
+ return 0;
+out:
+ dev_err(&st->sd.spi->dev, "setup failed\n");
+ return ret;
+}
+
+static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39,
+ 33, 19, 17, 16, 12, 10, 8, 6, 4};
+
+static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0,
+ 33, 0, 17, 16, 12, 10, 8, 6, 4};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4");
+
+static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797,
+ sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4");
+
+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 ad7793_state *st = iio_priv(indio_dev);
+
+ 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 IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct attribute *ad7793_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7793_attribute_group = {
+ .attrs = ad7793_attributes,
+};
+
+static struct attribute *ad7797_attributes[] = {
+ &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7797_attribute_group = {
+ .attrs = ad7797_attributes,
+};
+
+static int ad7793_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7793_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned long long scale_uv;
+ bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->differential) {
+ *val = st->
+ scale_avail[(st->conf >> 8) & 0x7][0];
+ *val2 = st->
+ scale_avail[(st->conf >> 8) & 0x7][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+ /* 1170mV / 2^23 * 6 */
+ scale_uv = (1170ULL * 1000000000ULL * 6ULL);
+ break;
+ case IIO_TEMP:
+ /* 1170mV / 0.81 mV/C / 2^23 */
+ scale_uv = 1444444444444444ULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1));
+ *val = 0;
+ *val2 = scale_uv;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP) {
+ unsigned long long offset;
+ unsigned int shift;
+
+ shift = chan->scan_type.realbits - (unipolar ? 0 : 1);
+ offset = 273ULL << shift;
+ do_div(offset, 1444);
+ *val -= offset;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->chip_info
+ ->sample_freq_avail[AD7793_MODE_RATE(st->mode)];
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+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;
+ 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 < 16; i++)
+ if (val == st->chip_info->sample_freq_avail[i])
+ break;
+
+ if (i == 16) {
+ ret = -EINVAL;
+ break;
+ }
+
+ 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;
+ default:
+ ret = -EINVAL;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+}
+
+static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+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,
+};
+
+static const struct iio_info ad7797_info = {
+ .read_raw = &ad7793_read_raw,
+ .write_raw = &ad7793_write_raw,
+ .write_raw_get_fmt = &ad7793_write_raw_get_fmt,
+ .attrs = &ad7797_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+};
+
+#define __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \
+ _storagebits, _shift, _extend_name, _type, _mask_type_av, _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_type_available = (_mask_type_av), \
+ .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_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) \
+const struct iio_chan_spec _name##_channels[] = { \
+ 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[] = { \
+ 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[] = { \
+ 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), \
+}
+
+#define DECLARE_AD7799_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_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), \
+}
+
+static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4);
+static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0);
+static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0);
+static DECLARE_AD7795_CHANNELS(ad7794, 16, 32);
+static DECLARE_AD7795_CHANNELS(ad7795, 24, 32);
+static DECLARE_AD7797_CHANNELS(ad7796, 16, 16);
+static DECLARE_AD7797_CHANNELS(ad7797, 24, 32);
+static DECLARE_AD7799_CHANNELS(ad7798, 16, 16);
+static DECLARE_AD7799_CHANNELS(ad7799, 24, 32);
+
+static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
+ [ID_AD7785] = {
+ .id = AD7785_ID,
+ .channels = ad7785_channels,
+ .num_channels = ARRAY_SIZE(ad7785_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7792] = {
+ .id = AD7792_ID,
+ .channels = ad7792_channels,
+ .num_channels = ARRAY_SIZE(ad7792_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7793] = {
+ .id = AD7793_ID,
+ .channels = ad7793_channels,
+ .num_channels = ARRAY_SIZE(ad7793_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7794] = {
+ .id = AD7794_ID,
+ .channels = ad7794_channels,
+ .num_channels = ARRAY_SIZE(ad7794_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7795] = {
+ .id = AD7795_ID,
+ .channels = ad7795_channels,
+ .num_channels = ARRAY_SIZE(ad7795_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL |
+ AD7793_FLAG_HAS_REFSEL |
+ AD7793_FLAG_HAS_VBIAS |
+ AD7793_HAS_EXITATION_CURRENT |
+ AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7796] = {
+ .id = AD7796_ID,
+ .channels = ad7796_channels,
+ .num_channels = ARRAY_SIZE(ad7796_channels),
+ .iio_info = &ad7797_info,
+ .sample_freq_avail = ad7797_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL,
+ },
+ [ID_AD7797] = {
+ .id = AD7797_ID,
+ .channels = ad7797_channels,
+ .num_channels = ARRAY_SIZE(ad7797_channels),
+ .iio_info = &ad7797_info,
+ .sample_freq_avail = ad7797_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_CLKSEL,
+ },
+ [ID_AD7798] = {
+ .id = AD7798_ID,
+ .channels = ad7798_channels,
+ .num_channels = ARRAY_SIZE(ad7798_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+ [ID_AD7799] = {
+ .id = AD7799_ID,
+ .channels = ad7799_channels,
+ .num_channels = ARRAY_SIZE(ad7799_channels),
+ .iio_info = &ad7793_info,
+ .sample_freq_avail = ad7793_sample_freq_avail,
+ .flags = AD7793_FLAG_HAS_GAIN |
+ AD7793_FLAG_HAS_BUFFER,
+ },
+};
+
+static void ad7793_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int ad7793_probe(struct spi_device *spi)
+{
+ const struct ad7793_platform_data *pdata = spi->dev.platform_data;
+ struct ad7793_state *st;
+ struct iio_dev *indio_dev;
+ int ret, vref_mv = 0;
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info);
+
+ if (pdata->refsel != AD7793_REFSEL_INTERNAL) {
+ st->reg = devm_regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7793_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ vref_mv = regulator_get_voltage(st->reg);
+ if (vref_mv < 0)
+ return vref_mv;
+
+ vref_mv /= 1000;
+ } else {
+ vref_mv = 1170; /* Build-in ref */
+ }
+
+ st->chip_info =
+ &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = st->chip_info->iio_info;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7793_setup(indio_dev, pdata, vref_mv);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7793_id[] = {
+ {"ad7785", ID_AD7785},
+ {"ad7792", ID_AD7792},
+ {"ad7793", ID_AD7793},
+ {"ad7794", ID_AD7794},
+ {"ad7795", ID_AD7795},
+ {"ad7796", ID_AD7796},
+ {"ad7797", ID_AD7797},
+ {"ad7798", ID_AD7798},
+ {"ad7799", ID_AD7799},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7793_id);
+
+static struct spi_driver ad7793_driver = {
+ .driver = {
+ .name = "ad7793",
+ },
+ .probe = ad7793_probe,
+ .id_table = ad7793_id,
+};
+module_spi_driver(ad7793_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
new file mode 100644
index 000000000..965bdc8aa
--- /dev/null
+++ b/drivers/iio/adc/ad7887.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7887 SPI ADC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/platform_data/ad7887.h>
+
+#define AD7887_REF_DIS BIT(5) /* on-chip reference disable */
+#define AD7887_DUAL BIT(4) /* dual-channel mode */
+#define AD7887_CH_AIN1 BIT(3) /* convert on channel 1, DUAL=1 */
+#define AD7887_CH_AIN0 0 /* convert on channel 0, DUAL=0,1 */
+#define AD7887_PM_MODE1 0 /* CS based shutdown */
+#define AD7887_PM_MODE2 1 /* full on */
+#define AD7887_PM_MODE3 2 /* auto shutdown after conversion */
+#define AD7887_PM_MODE4 3 /* standby mode */
+
+enum ad7887_channels {
+ AD7887_CH0,
+ AD7887_CH0_CH1,
+ AD7887_CH1,
+};
+
+/**
+ * struct ad7887_chip_info - chip specifc information
+ * @int_vref_mv: the internal reference voltage
+ * @channels: channels specification
+ * @num_channels: number of channels
+ * @dual_channels: channels specification in dual mode
+ * @num_dual_channels: number of channels in dual mode
+ */
+struct ad7887_chip_info {
+ u16 int_vref_mv;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const struct iio_chan_spec *dual_channels;
+ unsigned int num_dual_channels;
+};
+
+struct ad7887_state {
+ struct spi_device *spi;
+ const struct ad7887_chip_info *chip_info;
+ struct regulator *reg;
+ struct spi_transfer xfer[4];
+ struct spi_message msg[3];
+ struct spi_message *ring_msg;
+ unsigned char tx_cmd_buf[4];
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * Buffer needs to be large enough to hold two 16 bit samples and a
+ * 64 bit aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
+};
+
+enum ad7887_supported_device_ids {
+ ID_AD7887
+};
+
+static int ad7887_ring_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7887_state *st = iio_priv(indio_dev);
+
+ /* We know this is a single long so can 'cheat' */
+ switch (*indio_dev->active_scan_mask) {
+ case (1 << 0):
+ st->ring_msg = &st->msg[AD7887_CH0];
+ break;
+ case (1 << 1):
+ st->ring_msg = &st->msg[AD7887_CH1];
+ /* Dummy read: push CH1 setting down to hardware */
+ spi_sync(st->spi, st->ring_msg);
+ break;
+ case ((1 << 1) | (1 << 0)):
+ st->ring_msg = &st->msg[AD7887_CH0_CH1];
+ break;
+ }
+
+ return 0;
+}
+
+static int ad7887_ring_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7887_state *st = iio_priv(indio_dev);
+
+ /* dummy read: restore default CH0 settin */
+ return spi_sync(st->spi, &st->msg[AD7887_CH0]);
+}
+
+static irqreturn_t ad7887_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7887_state *st = iio_priv(indio_dev);
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, st->ring_msg);
+ if (b_sent)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = {
+ .preenable = &ad7887_ring_preenable,
+ .postdisable = &ad7887_ring_postdisable,
+};
+
+static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch)
+{
+ int ret = spi_sync(st->spi, &st->msg[ch]);
+ if (ret)
+ return ret;
+
+ return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1];
+}
+
+static int ad7887_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7887_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = ad7887_scan_direct(st, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+ *val = ret >> chan->scan_type.shift;
+ *val &= GENMASK(chan->scan_type.realbits - 1, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->reg) {
+ *val = regulator_get_voltage(st->reg);
+ if (*val < 0)
+ return *val;
+ *val /= 1000;
+ } else {
+ *val = st->chip_info->int_vref_mv;
+ }
+
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+#define AD7887_CHANNEL(x) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (x), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = (x), \
+ .scan_index = (x), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 0, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec ad7887_channels[] = {
+ AD7887_CHANNEL(0),
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_chan_spec ad7887_dual_channels[] = {
+ AD7887_CHANNEL(0),
+ AD7887_CHANNEL(1),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
+ /*
+ * More devices added in future
+ */
+ [ID_AD7887] = {
+ .channels = ad7887_channels,
+ .num_channels = ARRAY_SIZE(ad7887_channels),
+ .dual_channels = ad7887_dual_channels,
+ .num_dual_channels = ARRAY_SIZE(ad7887_dual_channels),
+ .int_vref_mv = 2500,
+ },
+};
+
+static const struct iio_info ad7887_info = {
+ .read_raw = &ad7887_read_raw,
+};
+
+static void ad7887_reg_disable(void *data)
+{
+ struct regulator *reg = data;
+
+ regulator_disable(reg);
+}
+
+static int ad7887_probe(struct spi_device *spi)
+{
+ struct ad7887_platform_data *pdata = spi->dev.platform_data;
+ struct ad7887_state *st;
+ struct iio_dev *indio_dev;
+ uint8_t mode;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (IS_ERR(st->reg)) {
+ if (PTR_ERR(st->reg) != -ENODEV)
+ return PTR_ERR(st->reg);
+
+ st->reg = NULL;
+ }
+
+ if (st->reg) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7887_reg_disable, st->reg);
+ if (ret)
+ return ret;
+ }
+
+ st->chip_info =
+ &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad7887_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Setup default message */
+
+ mode = AD7887_PM_MODE4;
+ if (!st->reg)
+ mode |= AD7887_REF_DIS;
+ if (pdata && pdata->en_dual)
+ mode |= AD7887_DUAL;
+
+ st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode;
+
+ st->xfer[0].rx_buf = &st->data[0];
+ st->xfer[0].tx_buf = &st->tx_cmd_buf[0];
+ st->xfer[0].len = 2;
+
+ spi_message_init(&st->msg[AD7887_CH0]);
+ spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]);
+
+ if (pdata && pdata->en_dual) {
+ st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode;
+
+ st->xfer[1].rx_buf = &st->data[0];
+ st->xfer[1].tx_buf = &st->tx_cmd_buf[2];
+ st->xfer[1].len = 2;
+
+ st->xfer[2].rx_buf = &st->data[2];
+ st->xfer[2].tx_buf = &st->tx_cmd_buf[0];
+ st->xfer[2].len = 2;
+
+ spi_message_init(&st->msg[AD7887_CH0_CH1]);
+ spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]);
+ spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]);
+
+ st->xfer[3].rx_buf = &st->data[2];
+ st->xfer[3].tx_buf = &st->tx_cmd_buf[2];
+ st->xfer[3].len = 2;
+
+ spi_message_init(&st->msg[AD7887_CH1]);
+ spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
+
+ indio_dev->channels = st->chip_info->dual_channels;
+ indio_dev->num_channels = st->chip_info->num_dual_channels;
+ } else {
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7887_trigger_handler, &ad7887_ring_setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7887_id[] = {
+ {"ad7887", ID_AD7887},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7887_id);
+
+static struct spi_driver ad7887_driver = {
+ .driver = {
+ .name = "ad7887",
+ },
+ .probe = ad7887_probe,
+ .id_table = ad7887_id,
+};
+module_spi_driver(ad7887_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
new file mode 100644
index 000000000..9d6bf6d09
--- /dev/null
+++ b/drivers/iio/adc/ad7923.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD7904/AD7914/AD7923/AD7924/AD7908/AD7918/AD7928 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc (from AD7923 Driver)
+ * Copyright 2012 CS Systemes d'Information
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define AD7923_WRITE_CR BIT(11) /* write control register */
+#define AD7923_RANGE BIT(1) /* range to REFin */
+#define AD7923_CODING BIT(0) /* coding is straight binary */
+#define AD7923_PM_MODE_AS (1) /* auto shutdown */
+#define AD7923_PM_MODE_FS (2) /* full shutdown */
+#define AD7923_PM_MODE_OPS (3) /* normal operation */
+#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */
+#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
+#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
+
+
+#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
+#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
+#define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \
+ + (((sequence) & 2) << 9))
+ /* write sequence fonction */
+/* left shift for CR : bit 11 transmit in first */
+#define AD7923_SHIFT_REGISTER 4
+
+/* val = value, dec = left shift, bits = number of bits of the mask */
+#define EXTRACT(val, dec, bits) (((val) >> (dec)) & ((1 << (bits)) - 1))
+
+struct ad7923_state {
+ struct spi_device *spi;
+ struct spi_transfer ring_xfer[5];
+ struct spi_transfer scan_single_xfer[2];
+ struct spi_message ring_msg;
+ struct spi_message scan_single_msg;
+
+ struct regulator *reg;
+
+ unsigned int settings;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * Ensure rx_buf can be directly used in iio_push_to_buffers_with_timetamp
+ * Length = 8 channels + 4 extra for 8 byte timestamp
+ */
+ __be16 rx_buf[12] __aligned(IIO_DMA_MINALIGN);
+ __be16 tx_buf[4];
+};
+
+struct ad7923_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+enum ad7923_id {
+ AD7904,
+ AD7914,
+ AD7924,
+ AD7908,
+ AD7918,
+ AD7928
+};
+
+#define AD7923_V_CHAN(index, bits) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 12 - (bits), \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define DECLARE_AD7923_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ AD7923_V_CHAN(0, bits), \
+ AD7923_V_CHAN(1, bits), \
+ AD7923_V_CHAN(2, bits), \
+ AD7923_V_CHAN(3, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DECLARE_AD7908_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ AD7923_V_CHAN(0, bits), \
+ AD7923_V_CHAN(1, bits), \
+ AD7923_V_CHAN(2, bits), \
+ AD7923_V_CHAN(3, bits), \
+ AD7923_V_CHAN(4, bits), \
+ AD7923_V_CHAN(5, bits), \
+ AD7923_V_CHAN(6, bits), \
+ AD7923_V_CHAN(7, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(8), \
+}
+
+static DECLARE_AD7923_CHANNELS(ad7904, 8);
+static DECLARE_AD7923_CHANNELS(ad7914, 10);
+static DECLARE_AD7923_CHANNELS(ad7924, 12);
+static DECLARE_AD7908_CHANNELS(ad7908, 8);
+static DECLARE_AD7908_CHANNELS(ad7918, 10);
+static DECLARE_AD7908_CHANNELS(ad7928, 12);
+
+static const struct ad7923_chip_info ad7923_chip_info[] = {
+ [AD7904] = {
+ .channels = ad7904_channels,
+ .num_channels = ARRAY_SIZE(ad7904_channels),
+ },
+ [AD7914] = {
+ .channels = ad7914_channels,
+ .num_channels = ARRAY_SIZE(ad7914_channels),
+ },
+ [AD7924] = {
+ .channels = ad7924_channels,
+ .num_channels = ARRAY_SIZE(ad7924_channels),
+ },
+ [AD7908] = {
+ .channels = ad7908_channels,
+ .num_channels = ARRAY_SIZE(ad7908_channels),
+ },
+ [AD7918] = {
+ .channels = ad7918_channels,
+ .num_channels = ARRAY_SIZE(ad7918_channels),
+ },
+ [AD7928] = {
+ .channels = ad7928_channels,
+ .num_channels = ARRAY_SIZE(ad7928_channels),
+ },
+};
+
+/*
+ * ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
+ */
+static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct ad7923_state *st = iio_priv(indio_dev);
+ int i, cmd, len;
+
+ len = 0;
+ /*
+ * For this driver the last channel is always the software timestamp so
+ * skip that one.
+ */
+ for_each_set_bit(i, active_scan_mask, indio_dev->num_channels - 1) {
+ cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
+ AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
+ st->settings;
+ cmd <<= AD7923_SHIFT_REGISTER;
+ st->tx_buf[len++] = cpu_to_be16(cmd);
+ }
+ /* build spi ring message */
+ st->ring_xfer[0].tx_buf = &st->tx_buf[0];
+ st->ring_xfer[0].len = len;
+ st->ring_xfer[0].cs_change = 1;
+
+ spi_message_init(&st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
+
+ for (i = 0; i < len; i++) {
+ st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i];
+ st->ring_xfer[i + 1].len = 2;
+ st->ring_xfer[i + 1].cs_change = 1;
+ spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg);
+ }
+ /* make sure last transfer cs_change is not set */
+ st->ring_xfer[i + 1].cs_change = 0;
+
+ return 0;
+}
+
+static irqreturn_t ad7923_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7923_state *st = iio_priv(indio_dev);
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, &st->ring_msg);
+ if (b_sent)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7923_scan_direct(struct ad7923_state *st, unsigned int ch)
+{
+ int ret, cmd;
+
+ cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) |
+ AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
+ st->settings;
+ cmd <<= AD7923_SHIFT_REGISTER;
+ st->tx_buf[0] = cpu_to_be16(cmd);
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+
+ return be16_to_cpu(st->rx_buf[0]);
+}
+
+static int ad7923_get_range(struct ad7923_state *st)
+{
+ int vref;
+
+ vref = regulator_get_voltage(st->reg);
+ if (vref < 0)
+ return vref;
+
+ vref /= 1000;
+
+ if (!(st->settings & AD7923_RANGE))
+ vref *= 2;
+
+ return vref;
+}
+
+static int ad7923_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7923_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = ad7923_scan_direct(st, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+
+ if (chan->address == EXTRACT(ret, 12, 4))
+ *val = EXTRACT(ret, chan->scan_type.shift,
+ chan->scan_type.realbits);
+ else
+ return -EIO;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = ad7923_get_range(st);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad7923_info = {
+ .read_raw = &ad7923_read_raw,
+ .update_scan_mode = ad7923_update_scan_mode,
+};
+
+static void ad7923_regulator_disable(void *data)
+{
+ struct ad7923_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+static int ad7923_probe(struct spi_device *spi)
+{
+ u32 ad7923_range = AD7923_RANGE;
+ struct ad7923_state *st;
+ struct iio_dev *indio_dev;
+ const struct ad7923_chip_info *info;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ if (device_property_read_bool(&spi->dev, "adi,range-double"))
+ ad7923_range = 0;
+
+ st->spi = spi;
+ st->settings = AD7923_CODING | ad7923_range |
+ AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS);
+
+ info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = info->channels;
+ indio_dev->num_channels = info->num_channels;
+ indio_dev->info = &ad7923_info;
+
+ /* Setup default message */
+
+ st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
+ st->scan_single_xfer[0].len = 2;
+ st->scan_single_xfer[0].cs_change = 1;
+ st->scan_single_xfer[1].rx_buf = &st->rx_buf[0];
+ st->scan_single_xfer[1].len = 2;
+
+ spi_message_init(&st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
+ spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
+
+ st->reg = devm_regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7923_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ &ad7923_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7923_id[] = {
+ {"ad7904", AD7904},
+ {"ad7914", AD7914},
+ {"ad7923", AD7924},
+ {"ad7924", AD7924},
+ {"ad7908", AD7908},
+ {"ad7918", AD7918},
+ {"ad7928", AD7928},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7923_id);
+
+static const struct of_device_id ad7923_of_match[] = {
+ { .compatible = "adi,ad7904", },
+ { .compatible = "adi,ad7914", },
+ { .compatible = "adi,ad7923", },
+ { .compatible = "adi,ad7924", },
+ { .compatible = "adi,ad7908", },
+ { .compatible = "adi,ad7918", },
+ { .compatible = "adi,ad7928", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7923_of_match);
+
+static struct spi_driver ad7923_driver = {
+ .driver = {
+ .name = "ad7923",
+ .of_match_table = ad7923_of_match,
+ },
+ .probe = ad7923_probe,
+ .id_table = ad7923_id,
+};
+module_spi_driver(ad7923_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
+MODULE_DESCRIPTION("Analog Devices AD7923 and similar ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c
new file mode 100644
index 000000000..edd0c3a35
--- /dev/null
+++ b/drivers/iio/adc/ad7949.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels
+ *
+ * Copyright (C) 2018 CMC NV
+ *
+ * https://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/bitfield.h>
+
+#define AD7949_CFG_MASK_TOTAL GENMASK(13, 0)
+
+/* CFG: Configuration Update */
+#define AD7949_CFG_MASK_OVERWRITE BIT(13)
+
+/* INCC: Input Channel Configuration */
+#define AD7949_CFG_MASK_INCC GENMASK(12, 10)
+#define AD7949_CFG_VAL_INCC_UNIPOLAR_GND 7
+#define AD7949_CFG_VAL_INCC_UNIPOLAR_COMM 6
+#define AD7949_CFG_VAL_INCC_UNIPOLAR_DIFF 4
+#define AD7949_CFG_VAL_INCC_TEMP 3
+#define AD7949_CFG_VAL_INCC_BIPOLAR 2
+#define AD7949_CFG_VAL_INCC_BIPOLAR_DIFF 0
+
+/* INX: Input channel Selection in a binary fashion */
+#define AD7949_CFG_MASK_INX GENMASK(9, 7)
+
+/* BW: select bandwidth for low-pass filter. Full or Quarter */
+#define AD7949_CFG_MASK_BW_FULL BIT(6)
+
+/* REF: reference/buffer selection */
+#define AD7949_CFG_MASK_REF GENMASK(5, 3)
+#define AD7949_CFG_VAL_REF_EXT_TEMP_BUF 3
+#define AD7949_CFG_VAL_REF_EXT_TEMP 2
+#define AD7949_CFG_VAL_REF_INT_4096 1
+#define AD7949_CFG_VAL_REF_INT_2500 0
+#define AD7949_CFG_VAL_REF_EXTERNAL BIT(1)
+
+/* SEQ: channel sequencer. Allows for scanning channels */
+#define AD7949_CFG_MASK_SEQ GENMASK(2, 1)
+
+/* RB: Read back the CFG register */
+#define AD7949_CFG_MASK_RBN BIT(0)
+
+enum {
+ ID_AD7949 = 0,
+ ID_AD7682,
+ ID_AD7689,
+};
+
+struct ad7949_adc_spec {
+ u8 num_channels;
+ u8 resolution;
+};
+
+static const struct ad7949_adc_spec ad7949_adc_spec[] = {
+ [ID_AD7949] = { .num_channels = 8, .resolution = 14 },
+ [ID_AD7682] = { .num_channels = 4, .resolution = 16 },
+ [ID_AD7689] = { .num_channels = 8, .resolution = 16 },
+};
+
+/**
+ * struct ad7949_adc_chip - AD ADC chip
+ * @lock: protects write sequences
+ * @vref: regulator generating Vref
+ * @indio_dev: reference to iio structure
+ * @spi: reference to spi structure
+ * @refsel: reference selection
+ * @resolution: resolution of the chip
+ * @cfg: copy of the configuration register
+ * @current_channel: current channel in use
+ * @buffer: buffer to send / receive data to / from device
+ * @buf8b: be16 buffer to exchange data with the device in 8-bit transfers
+ */
+struct ad7949_adc_chip {
+ struct mutex lock;
+ struct regulator *vref;
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ u32 refsel;
+ u8 resolution;
+ u16 cfg;
+ unsigned int current_channel;
+ u16 buffer __aligned(IIO_DMA_MINALIGN);
+ __be16 buf8b;
+};
+
+static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val,
+ u16 mask)
+{
+ int ret;
+
+ ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask);
+
+ switch (ad7949_adc->spi->bits_per_word) {
+ case 16:
+ ad7949_adc->buffer = ad7949_adc->cfg << 2;
+ ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2);
+ break;
+ case 14:
+ ad7949_adc->buffer = ad7949_adc->cfg;
+ ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2);
+ break;
+ case 8:
+ /* Here, type is big endian as it must be sent in two transfers */
+ ad7949_adc->buf8b = cpu_to_be16(ad7949_adc->cfg << 2);
+ ret = spi_write(ad7949_adc->spi, &ad7949_adc->buf8b, 2);
+ break;
+ default:
+ dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n");
+ return -EINVAL;
+ }
+
+ /*
+ * This delay is to avoid a new request before the required time to
+ * send a new command to the device
+ */
+ udelay(2);
+ return ret;
+}
+
+static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
+ unsigned int channel)
+{
+ int ret;
+ int i;
+
+ /*
+ * 1: write CFG for sample N and read old data (sample N-2)
+ * 2: if CFG was not changed since sample N-1 then we'll get good data
+ * at the next xfer, so we bail out now, otherwise we write something
+ * and we read garbage (sample N-1 configuration).
+ */
+ for (i = 0; i < 2; i++) {
+ ret = ad7949_spi_write_cfg(ad7949_adc,
+ FIELD_PREP(AD7949_CFG_MASK_INX, channel),
+ AD7949_CFG_MASK_INX);
+ if (ret)
+ return ret;
+ if (channel == ad7949_adc->current_channel)
+ break;
+ }
+
+ /* 3: write something and read actual data */
+ if (ad7949_adc->spi->bits_per_word == 8)
+ ret = spi_read(ad7949_adc->spi, &ad7949_adc->buf8b, 2);
+ else
+ ret = spi_read(ad7949_adc->spi, &ad7949_adc->buffer, 2);
+
+ if (ret)
+ return ret;
+
+ /*
+ * This delay is to avoid a new request before the required time to
+ * send a new command to the device
+ */
+ udelay(2);
+
+ ad7949_adc->current_channel = channel;
+
+ switch (ad7949_adc->spi->bits_per_word) {
+ case 16:
+ *val = ad7949_adc->buffer;
+ /* Shift-out padding bits */
+ *val >>= 16 - ad7949_adc->resolution;
+ break;
+ case 14:
+ *val = ad7949_adc->buffer & GENMASK(13, 0);
+ break;
+ case 8:
+ /* Here, type is big endian as data was sent in two transfers */
+ *val = be16_to_cpu(ad7949_adc->buf8b);
+ /* Shift-out padding bits */
+ *val >>= 16 - ad7949_adc->resolution;
+ break;
+ default:
+ dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define AD7949_ADC_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec ad7949_adc_channels[] = {
+ AD7949_ADC_CHANNEL(0),
+ AD7949_ADC_CHANNEL(1),
+ AD7949_ADC_CHANNEL(2),
+ AD7949_ADC_CHANNEL(3),
+ AD7949_ADC_CHANNEL(4),
+ AD7949_ADC_CHANNEL(5),
+ AD7949_ADC_CHANNEL(6),
+ AD7949_ADC_CHANNEL(7),
+};
+
+static int ad7949_spi_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+ int ret;
+
+ if (!val)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&ad7949_adc->lock);
+ ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel);
+ mutex_unlock(&ad7949_adc->lock);
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (ad7949_adc->refsel) {
+ case AD7949_CFG_VAL_REF_INT_2500:
+ *val = 2500;
+ break;
+ case AD7949_CFG_VAL_REF_INT_4096:
+ *val = 4096;
+ break;
+ case AD7949_CFG_VAL_REF_EXT_TEMP:
+ case AD7949_CFG_VAL_REF_EXT_TEMP_BUF:
+ ret = regulator_get_voltage(ad7949_adc->vref);
+ if (ret < 0)
+ return ret;
+
+ /* convert value back to mV */
+ *val = ret / 1000;
+ break;
+ }
+
+ *val2 = (1 << ad7949_adc->resolution) - 1;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7949_spi_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (readval)
+ *readval = ad7949_adc->cfg;
+ else
+ ret = ad7949_spi_write_cfg(ad7949_adc, writeval,
+ AD7949_CFG_MASK_TOTAL);
+
+ return ret;
+}
+
+static const struct iio_info ad7949_spi_info = {
+ .read_raw = ad7949_spi_read_raw,
+ .debugfs_reg_access = ad7949_spi_reg_access,
+};
+
+static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc)
+{
+ int ret;
+ int val;
+ u16 cfg;
+
+ ad7949_adc->current_channel = 0;
+
+ cfg = FIELD_PREP(AD7949_CFG_MASK_OVERWRITE, 1) |
+ FIELD_PREP(AD7949_CFG_MASK_INCC, AD7949_CFG_VAL_INCC_UNIPOLAR_GND) |
+ FIELD_PREP(AD7949_CFG_MASK_INX, ad7949_adc->current_channel) |
+ FIELD_PREP(AD7949_CFG_MASK_BW_FULL, 1) |
+ FIELD_PREP(AD7949_CFG_MASK_REF, ad7949_adc->refsel) |
+ FIELD_PREP(AD7949_CFG_MASK_SEQ, 0x0) |
+ FIELD_PREP(AD7949_CFG_MASK_RBN, 1);
+
+ ret = ad7949_spi_write_cfg(ad7949_adc, cfg, AD7949_CFG_MASK_TOTAL);
+
+ /*
+ * Do two dummy conversions to apply the first configuration setting.
+ * Required only after the start up of the device.
+ */
+ ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
+ ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
+
+ return ret;
+}
+
+static void ad7949_disable_reg(void *reg)
+{
+ regulator_disable(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;
+ struct iio_dev *indio_dev;
+ u32 tmp;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc));
+ if (!indio_dev) {
+ dev_err(dev, "can not allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ indio_dev->info = &ad7949_spi_info;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7949_adc_channels;
+ spi_set_drvdata(spi, indio_dev);
+
+ ad7949_adc = iio_priv(indio_dev);
+ ad7949_adc->indio_dev = indio_dev;
+ ad7949_adc->spi = spi;
+
+ spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = spec->num_channels;
+ ad7949_adc->resolution = spec->resolution;
+
+ /* Set SPI bits per word */
+ if (spi_ctrl_mask & SPI_BPW_MASK(ad7949_adc->resolution)) {
+ spi->bits_per_word = ad7949_adc->resolution;
+ } else if (spi_ctrl_mask == SPI_BPW_MASK(16)) {
+ spi->bits_per_word = 16;
+ } else if (spi_ctrl_mask == SPI_BPW_MASK(8)) {
+ spi->bits_per_word = 8;
+ } else {
+ dev_err(dev, "unable to find common BPW with spi controller\n");
+ return -EINVAL;
+ }
+
+ /* Setup internal voltage reference */
+ tmp = 4096000;
+ device_property_read_u32(dev, "adi,internal-ref-microvolt", &tmp);
+
+ switch (tmp) {
+ case 2500000:
+ ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_2500;
+ break;
+ case 4096000:
+ ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_4096;
+ break;
+ default:
+ dev_err(dev, "unsupported internal voltage reference\n");
+ return -EINVAL;
+ }
+
+ /* Setup external voltage reference, buffered? */
+ ad7949_adc->vref = devm_regulator_get_optional(dev, "vrefin");
+ if (IS_ERR(ad7949_adc->vref)) {
+ ret = PTR_ERR(ad7949_adc->vref);
+ if (ret != -ENODEV)
+ return ret;
+ /* unbuffered? */
+ ad7949_adc->vref = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(ad7949_adc->vref)) {
+ ret = PTR_ERR(ad7949_adc->vref);
+ if (ret != -ENODEV)
+ return ret;
+ } else {
+ ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP;
+ }
+ } else {
+ ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP_BUF;
+ }
+
+ if (ad7949_adc->refsel & AD7949_CFG_VAL_REF_EXTERNAL) {
+ ret = regulator_enable(ad7949_adc->vref);
+ if (ret < 0) {
+ dev_err(dev, "fail to enable regulator\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, ad7949_disable_reg,
+ ad7949_adc->vref);
+ if (ret)
+ return ret;
+ }
+
+ mutex_init(&ad7949_adc->lock);
+
+ ret = ad7949_spi_init(ad7949_adc);
+ if (ret) {
+ dev_err(dev, "fail to init this device: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ dev_err(dev, "fail to register iio device: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id ad7949_spi_of_id[] = {
+ { .compatible = "adi,ad7949" },
+ { .compatible = "adi,ad7682" },
+ { .compatible = "adi,ad7689" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7949_spi_of_id);
+
+static const struct spi_device_id ad7949_spi_id[] = {
+ { "ad7949", ID_AD7949 },
+ { "ad7682", ID_AD7682 },
+ { "ad7689", ID_AD7689 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7949_spi_id);
+
+static struct spi_driver ad7949_spi_driver = {
+ .driver = {
+ .name = "ad7949",
+ .of_match_table = ad7949_spi_of_id,
+ },
+ .probe = ad7949_spi_probe,
+ .id_table = ad7949_spi_id,
+};
+module_spi_driver(ad7949_spi_driver);
+
+MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
+MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
new file mode 100644
index 000000000..6dbe9d5e0
--- /dev/null
+++ b/drivers/iio/adc/ad799x.c
@@ -0,0 +1,971 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * iio/adc/ad799x.c
+ * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
+ *
+ * based on iio/adc/max1363
+ * Copyright (C) 2008-2010 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * ad799x.c
+ *
+ * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
+ * ad7998 and similar chips.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define AD799X_CHANNEL_SHIFT 4
+
+/*
+ * AD7991, AD7995 and AD7999 defines
+ */
+
+#define AD7991_REF_SEL 0x08
+#define AD7991_FLTR 0x04
+#define AD7991_BIT_TRIAL_DELAY 0x02
+#define AD7991_SAMPLE_DELAY 0x01
+
+/*
+ * AD7992, AD7993, AD7994, AD7997 and AD7998 defines
+ */
+
+#define AD7998_FLTR BIT(3)
+#define AD7998_ALERT_EN BIT(2)
+#define AD7998_BUSY_ALERT BIT(1)
+#define AD7998_BUSY_ALERT_POL BIT(0)
+
+#define AD7998_CONV_RES_REG 0x0
+#define AD7998_ALERT_STAT_REG 0x1
+#define AD7998_CONF_REG 0x2
+#define AD7998_CYCLE_TMR_REG 0x3
+
+#define AD7998_DATALOW_REG(x) ((x) * 3 + 0x4)
+#define AD7998_DATAHIGH_REG(x) ((x) * 3 + 0x5)
+#define AD7998_HYST_REG(x) ((x) * 3 + 0x6)
+
+#define AD7998_CYC_MASK GENMASK(2, 0)
+#define AD7998_CYC_DIS 0x0
+#define AD7998_CYC_TCONF_32 0x1
+#define AD7998_CYC_TCONF_64 0x2
+#define AD7998_CYC_TCONF_128 0x3
+#define AD7998_CYC_TCONF_256 0x4
+#define AD7998_CYC_TCONF_512 0x5
+#define AD7998_CYC_TCONF_1024 0x6
+#define AD7998_CYC_TCONF_2048 0x7
+
+#define AD7998_ALERT_STAT_CLEAR 0xFF
+
+/*
+ * AD7997 and AD7997 defines
+ */
+
+#define AD7997_8_READ_SINGLE BIT(7)
+#define AD7997_8_READ_SEQUENCE (BIT(6) | BIT(5) | BIT(4))
+
+enum {
+ ad7991,
+ ad7995,
+ ad7999,
+ ad7992,
+ ad7993,
+ ad7994,
+ ad7997,
+ ad7998
+};
+
+/**
+ * struct ad799x_chip_config - chip specific information
+ * @channel: channel specification
+ * @default_config: device default configuration
+ * @info: pointer to iio_info struct
+ */
+struct ad799x_chip_config {
+ const struct iio_chan_spec channel[9];
+ u16 default_config;
+ const struct iio_info *info;
+};
+
+/**
+ * struct ad799x_chip_info - chip specific information
+ * @num_channels: number of channels
+ * @noirq_config: device configuration w/o IRQ
+ * @irq_config: device configuration w/IRQ
+ */
+struct ad799x_chip_info {
+ int num_channels;
+ const struct ad799x_chip_config noirq_config;
+ const struct ad799x_chip_config irq_config;
+};
+
+struct ad799x_state {
+ struct i2c_client *client;
+ const struct ad799x_chip_config *chip_config;
+ struct regulator *reg;
+ struct regulator *vref;
+ unsigned id;
+ u16 config;
+
+ u8 *rx_buf;
+ unsigned int transfer_size;
+};
+
+static int ad799x_write_config(struct ad799x_state *st, u16 val)
+{
+ switch (st->id) {
+ case ad7997:
+ case ad7998:
+ return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG,
+ val);
+ case ad7992:
+ case ad7993:
+ case ad7994:
+ return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG,
+ val);
+ default:
+ /* Will be written when doing a conversion */
+ st->config = val;
+ return 0;
+ }
+}
+
+static int ad799x_read_config(struct ad799x_state *st)
+{
+ switch (st->id) {
+ case ad7997:
+ case ad7998:
+ return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG);
+ case ad7992:
+ case ad7993:
+ case ad7994:
+ return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG);
+ default:
+ /* No readback support */
+ return st->config;
+ }
+}
+
+static int ad799x_update_config(struct ad799x_state *st, u16 config)
+{
+ int ret;
+
+ ret = ad799x_write_config(st, config);
+ if (ret < 0)
+ return ret;
+ ret = ad799x_read_config(st);
+ if (ret < 0)
+ return ret;
+ st->config = ret;
+
+ return 0;
+}
+
+static irqreturn_t ad799x_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad799x_state *st = iio_priv(indio_dev);
+ int b_sent;
+ u8 cmd;
+
+ switch (st->id) {
+ case ad7991:
+ case ad7995:
+ case ad7999:
+ cmd = st->config |
+ (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT);
+ break;
+ case ad7992:
+ case ad7993:
+ case ad7994:
+ cmd = (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT) |
+ AD7998_CONV_RES_REG;
+ break;
+ case ad7997:
+ case ad7998:
+ cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG;
+ break;
+ default:
+ cmd = 0;
+ }
+
+ b_sent = i2c_smbus_read_i2c_block_data(st->client,
+ cmd, st->transfer_size, st->rx_buf);
+ if (b_sent < 0)
+ goto out;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_get_time_ns(indio_dev));
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad799x_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ kfree(st->rx_buf);
+ st->rx_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!st->rx_buf)
+ return -ENOMEM;
+
+ st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2;
+
+ switch (st->id) {
+ case ad7992:
+ case ad7993:
+ case ad7994:
+ case ad7997:
+ case ad7998:
+ st->config &= ~(GENMASK(7, 0) << AD799X_CHANNEL_SHIFT);
+ st->config |= (*scan_mask << AD799X_CHANNEL_SHIFT);
+ return ad799x_write_config(st, st->config);
+ default:
+ return 0;
+ }
+}
+
+static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
+{
+ u8 cmd;
+
+ switch (st->id) {
+ case ad7991:
+ case ad7995:
+ case ad7999:
+ cmd = st->config | (BIT(ch) << AD799X_CHANNEL_SHIFT);
+ break;
+ case ad7992:
+ case ad7993:
+ case ad7994:
+ cmd = BIT(ch) << AD799X_CHANNEL_SHIFT;
+ break;
+ case ad7997:
+ case ad7998:
+ cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return i2c_smbus_read_word_swapped(st->client, cmd);
+}
+
+static int ad799x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = ad799x_scan_direct(st, chan->scan_index);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+ *val = (ret >> chan->scan_type.shift) &
+ GENMASK(chan->scan_type.realbits - 1, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->vref)
+ ret = regulator_get_voltage(st->vref);
+ else
+ ret = regulator_get_voltage(st->reg);
+
+ if (ret < 0)
+ return ret;
+ *val = ret / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+static const unsigned int ad7998_frequencies[] = {
+ [AD7998_CYC_DIS] = 0,
+ [AD7998_CYC_TCONF_32] = 15625,
+ [AD7998_CYC_TCONF_64] = 7812,
+ [AD7998_CYC_TCONF_128] = 3906,
+ [AD7998_CYC_TCONF_512] = 976,
+ [AD7998_CYC_TCONF_1024] = 488,
+ [AD7998_CYC_TCONF_2048] = 244,
+};
+
+static ssize_t ad799x_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", ad7998_frequencies[ret & AD7998_CYC_MASK]);
+}
+
+static ssize_t ad799x_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ long val;
+ int ret, i;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
+ if (ret < 0)
+ goto error_ret_mutex;
+ /* Wipe the bits clean */
+ ret &= ~AD7998_CYC_MASK;
+
+ for (i = 0; i < ARRAY_SIZE(ad7998_frequencies); i++)
+ if (val == ad7998_frequencies[i])
+ break;
+ if (i == ARRAY_SIZE(ad7998_frequencies)) {
+ ret = -EINVAL;
+ goto error_ret_mutex;
+ }
+
+ ret = i2c_smbus_write_byte_data(st->client, AD7998_CYCLE_TMR_REG,
+ ret | i);
+ if (ret < 0)
+ goto error_ret_mutex;
+ ret = len;
+
+error_ret_mutex:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad799x_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 ad799x_state *st = iio_priv(indio_dev);
+
+ if (!(st->config & AD7998_ALERT_EN))
+ return 0;
+
+ if ((st->config >> AD799X_CHANNEL_SHIFT) & BIT(chan->scan_index))
+ return 1;
+
+ return 0;
+}
+
+static int ad799x_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct ad799x_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ if (state)
+ st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT;
+ else
+ st->config &= ~(BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT);
+
+ if (st->config >> AD799X_CHANNEL_SHIFT)
+ st->config |= AD7998_ALERT_EN;
+ else
+ st->config &= ~AD7998_ALERT_EN;
+
+ ret = ad799x_write_config(st, st->config);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+}
+
+static unsigned int ad799x_threshold_reg(const struct iio_chan_spec *chan,
+ enum iio_event_direction dir,
+ enum iio_event_info info)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (dir == IIO_EV_DIR_FALLING)
+ return AD7998_DATALOW_REG(chan->channel);
+ else
+ return AD7998_DATAHIGH_REG(chan->channel);
+ case IIO_EV_INFO_HYSTERESIS:
+ return AD7998_HYST_REG(chan->channel);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad799x_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;
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = i2c_smbus_write_word_swapped(st->client,
+ ad799x_threshold_reg(chan, dir, info),
+ val << chan->scan_type.shift);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad799x_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;
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ ret = i2c_smbus_read_word_swapped(st->client,
+ ad799x_threshold_reg(chan, dir, info));
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ return ret;
+ *val = (ret >> chan->scan_type.shift) &
+ GENMASK(chan->scan_type.realbits - 1, 0);
+
+ return IIO_VAL_INT;
+}
+
+static irqreturn_t ad799x_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad799x_state *st = iio_priv(private);
+ int i, ret;
+
+ ret = i2c_smbus_read_byte_data(st->client, AD7998_ALERT_STAT_REG);
+ if (ret <= 0)
+ goto done;
+
+ if (i2c_smbus_write_byte_data(st->client, AD7998_ALERT_STAT_REG,
+ AD7998_ALERT_STAT_CLEAR) < 0)
+ goto done;
+
+ for (i = 0; i < 8; i++) {
+ if (ret & BIT(i))
+ iio_push_event(indio_dev,
+ i & 0x1 ?
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+ (i >> 1),
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING) :
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+ (i >> 1),
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
+
+done:
+ return IRQ_HANDLED;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ad799x_read_frequency,
+ ad799x_write_frequency);
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0");
+
+static struct attribute *ad799x_event_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad799x_event_attrs_group = {
+ .attrs = ad799x_event_attributes,
+};
+
+static const struct iio_info ad7991_info = {
+ .read_raw = &ad799x_read_raw,
+ .update_scan_mode = ad799x_update_scan_mode,
+};
+
+static const struct iio_info ad7993_4_7_8_noirq_info = {
+ .read_raw = &ad799x_read_raw,
+ .update_scan_mode = ad799x_update_scan_mode,
+};
+
+static const struct iio_info ad7993_4_7_8_irq_info = {
+ .read_raw = &ad799x_read_raw,
+ .event_attrs = &ad799x_event_attrs_group,
+ .read_event_config = &ad799x_read_event_config,
+ .write_event_config = &ad799x_write_event_config,
+ .read_event_value = &ad799x_read_event_value,
+ .write_event_value = &ad799x_write_event_value,
+ .update_scan_mode = ad799x_update_scan_mode,
+};
+
+static const struct iio_event_spec ad799x_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),
+ },
+};
+
+#define _AD799X_CHANNEL(_index, _realbits, _ev_spec, _num_ev_spec) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_realbits), \
+ .storagebits = 16, \
+ .shift = 12 - (_realbits), \
+ .endianness = IIO_BE, \
+ }, \
+ .event_spec = _ev_spec, \
+ .num_event_specs = _num_ev_spec, \
+}
+
+#define AD799X_CHANNEL(_index, _realbits) \
+ _AD799X_CHANNEL(_index, _realbits, NULL, 0)
+
+#define AD799X_CHANNEL_WITH_EVENTS(_index, _realbits) \
+ _AD799X_CHANNEL(_index, _realbits, ad799x_events, \
+ ARRAY_SIZE(ad799x_events))
+
+static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
+ [ad7991] = {
+ .num_channels = 5,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 12),
+ AD799X_CHANNEL(1, 12),
+ AD799X_CHANNEL(2, 12),
+ AD799X_CHANNEL(3, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .info = &ad7991_info,
+ },
+ },
+ [ad7995] = {
+ .num_channels = 5,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 10),
+ AD799X_CHANNEL(1, 10),
+ AD799X_CHANNEL(2, 10),
+ AD799X_CHANNEL(3, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .info = &ad7991_info,
+ },
+ },
+ [ad7999] = {
+ .num_channels = 5,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 8),
+ AD799X_CHANNEL(1, 8),
+ AD799X_CHANNEL(2, 8),
+ AD799X_CHANNEL(3, 8),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .info = &ad7991_info,
+ },
+ },
+ [ad7992] = {
+ .num_channels = 3,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 12),
+ AD799X_CHANNEL(1, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+ },
+ .info = &ad7993_4_7_8_noirq_info,
+ },
+ .irq_config = {
+ .channel = {
+ AD799X_CHANNEL_WITH_EVENTS(0, 12),
+ AD799X_CHANNEL_WITH_EVENTS(1, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+ },
+ .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
+ .info = &ad7993_4_7_8_irq_info,
+ },
+ },
+ [ad7993] = {
+ .num_channels = 5,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 10),
+ AD799X_CHANNEL(1, 10),
+ AD799X_CHANNEL(2, 10),
+ AD799X_CHANNEL(3, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .info = &ad7993_4_7_8_noirq_info,
+ },
+ .irq_config = {
+ .channel = {
+ AD799X_CHANNEL_WITH_EVENTS(0, 10),
+ AD799X_CHANNEL_WITH_EVENTS(1, 10),
+ AD799X_CHANNEL_WITH_EVENTS(2, 10),
+ AD799X_CHANNEL_WITH_EVENTS(3, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
+ .info = &ad7993_4_7_8_irq_info,
+ },
+ },
+ [ad7994] = {
+ .num_channels = 5,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 12),
+ AD799X_CHANNEL(1, 12),
+ AD799X_CHANNEL(2, 12),
+ AD799X_CHANNEL(3, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .info = &ad7993_4_7_8_noirq_info,
+ },
+ .irq_config = {
+ .channel = {
+ AD799X_CHANNEL_WITH_EVENTS(0, 12),
+ AD799X_CHANNEL_WITH_EVENTS(1, 12),
+ AD799X_CHANNEL_WITH_EVENTS(2, 12),
+ AD799X_CHANNEL_WITH_EVENTS(3, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ },
+ .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
+ .info = &ad7993_4_7_8_irq_info,
+ },
+ },
+ [ad7997] = {
+ .num_channels = 9,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 10),
+ AD799X_CHANNEL(1, 10),
+ AD799X_CHANNEL(2, 10),
+ AD799X_CHANNEL(3, 10),
+ AD799X_CHANNEL(4, 10),
+ AD799X_CHANNEL(5, 10),
+ AD799X_CHANNEL(6, 10),
+ AD799X_CHANNEL(7, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ },
+ .info = &ad7993_4_7_8_noirq_info,
+ },
+ .irq_config = {
+ .channel = {
+ AD799X_CHANNEL_WITH_EVENTS(0, 10),
+ AD799X_CHANNEL_WITH_EVENTS(1, 10),
+ AD799X_CHANNEL_WITH_EVENTS(2, 10),
+ AD799X_CHANNEL_WITH_EVENTS(3, 10),
+ AD799X_CHANNEL(4, 10),
+ AD799X_CHANNEL(5, 10),
+ AD799X_CHANNEL(6, 10),
+ AD799X_CHANNEL(7, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ },
+ .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
+ .info = &ad7993_4_7_8_irq_info,
+ },
+ },
+ [ad7998] = {
+ .num_channels = 9,
+ .noirq_config = {
+ .channel = {
+ AD799X_CHANNEL(0, 12),
+ AD799X_CHANNEL(1, 12),
+ AD799X_CHANNEL(2, 12),
+ AD799X_CHANNEL(3, 12),
+ AD799X_CHANNEL(4, 12),
+ AD799X_CHANNEL(5, 12),
+ AD799X_CHANNEL(6, 12),
+ AD799X_CHANNEL(7, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ },
+ .info = &ad7993_4_7_8_noirq_info,
+ },
+ .irq_config = {
+ .channel = {
+ AD799X_CHANNEL_WITH_EVENTS(0, 12),
+ AD799X_CHANNEL_WITH_EVENTS(1, 12),
+ AD799X_CHANNEL_WITH_EVENTS(2, 12),
+ AD799X_CHANNEL_WITH_EVENTS(3, 12),
+ AD799X_CHANNEL(4, 12),
+ AD799X_CHANNEL(5, 12),
+ AD799X_CHANNEL(6, 12),
+ AD799X_CHANNEL(7, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ },
+ .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
+ .info = &ad7993_4_7_8_irq_info,
+ },
+ },
+};
+
+static int ad799x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ int extra_config = 0;
+ struct ad799x_state *st;
+ struct iio_dev *indio_dev;
+ const struct ad799x_chip_info *chip_info =
+ &ad799x_chip_info_tbl[id->driver_data];
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ st->id = id->driver_data;
+ if (client->irq > 0 && chip_info->irq_config.info)
+ st->chip_config = &chip_info->irq_config;
+ else
+ st->chip_config = &chip_info->noirq_config;
+
+ /* TODO: Add pdata options for filtering and bit delay */
+
+ st->reg = devm_regulator_get(&client->dev, "vcc");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ /* check if an external reference is supplied */
+ st->vref = devm_regulator_get_optional(&client->dev, "vref");
+
+ if (IS_ERR(st->vref)) {
+ if (PTR_ERR(st->vref) == -ENODEV) {
+ st->vref = NULL;
+ dev_info(&client->dev, "Using VCC reference voltage\n");
+ } else {
+ ret = PTR_ERR(st->vref);
+ goto error_disable_reg;
+ }
+ }
+
+ if (st->vref) {
+ /*
+ * Use external reference voltage if supported by hardware.
+ * This is optional if voltage / regulator present, use VCC otherwise.
+ */
+ if ((st->id == ad7991) || (st->id == ad7995) || (st->id == ad7999)) {
+ dev_info(&client->dev, "Using external reference voltage\n");
+ extra_config |= AD7991_REF_SEL;
+ ret = regulator_enable(st->vref);
+ if (ret)
+ goto error_disable_reg;
+ } else {
+ st->vref = NULL;
+ dev_warn(&client->dev, "Supplied reference not supported\n");
+ }
+ }
+
+ st->client = client;
+
+ indio_dev->name = id->name;
+ indio_dev->info = st->chip_config->info;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_config->channel;
+ indio_dev->num_channels = chip_info->num_channels;
+
+ ret = ad799x_update_config(st, st->chip_config->default_config | extra_config);
+ if (ret)
+ goto error_disable_vref;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ad799x_trigger_handler, NULL);
+ if (ret)
+ goto error_disable_vref;
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL,
+ ad799x_event_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ client->name,
+ indio_dev);
+ if (ret)
+ goto error_cleanup_ring;
+ }
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_ring;
+
+ return 0;
+
+error_cleanup_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_vref:
+ if (st->vref)
+ regulator_disable(st->vref);
+error_disable_reg:
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static void ad799x_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (st->vref)
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+ kfree(st->rx_buf);
+}
+
+static int ad799x_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ad799x_state *st = iio_priv(indio_dev);
+
+ if (st->vref)
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static int ad799x_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ad799x_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(dev, "Unable to enable vcc regulator\n");
+ return ret;
+ }
+
+ if (st->vref) {
+ ret = regulator_enable(st->vref);
+ if (ret) {
+ regulator_disable(st->reg);
+ dev_err(dev, "Unable to enable vref regulator\n");
+ return ret;
+ }
+ }
+
+ /* resync config */
+ ret = ad799x_update_config(st, st->config);
+ if (ret) {
+ if (st->vref)
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ad799x_pm_ops, ad799x_suspend, ad799x_resume);
+
+static const struct i2c_device_id ad799x_id[] = {
+ { "ad7991", ad7991 },
+ { "ad7995", ad7995 },
+ { "ad7999", ad7999 },
+ { "ad7992", ad7992 },
+ { "ad7993", ad7993 },
+ { "ad7994", ad7994 },
+ { "ad7997", ad7997 },
+ { "ad7998", ad7998 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad799x_id);
+
+static struct i2c_driver ad799x_driver = {
+ .driver = {
+ .name = "ad799x",
+ .pm = pm_sleep_ptr(&ad799x_pm_ops),
+ },
+ .probe = ad799x_probe,
+ .remove = ad799x_remove,
+ .id_table = ad799x_id,
+};
+module_i2c_driver(ad799x_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD799x ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
new file mode 100644
index 000000000..811525857
--- /dev/null
+++ b/drivers/iio/adc/ad9467.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9467 SPI ADC driver
+ *
+ * Copyright 2012-2020 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include <linux/clk.h>
+
+#include <linux/iio/adc/adi-axi-adc.h>
+
+/*
+ * 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 AD9265 16-Bit, 125/105/80 MSPS ADC
+ */
+
+#define CHIPID_AD9265 0x64
+#define AD9265_DEF_OUTPUT_MODE 0x40
+#define AD9265_REG_VREF_MASK 0xC0
+
+/*
+ * Analog Devices AD9434 12-Bit, 370/500 MSPS ADC
+ */
+
+#define CHIPID_AD9434 0x6A
+#define AD9434_DEF_OUTPUT_MODE 0x00
+#define AD9434_REG_VREF_MASK 0xC0
+
+/*
+ * 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_AD9265,
+ ID_AD9434,
+ ID_AD9467,
+};
+
+struct ad9467_chip_info {
+ struct adi_axi_adc_chip_info axi_adc_info;
+ unsigned int default_output_mode;
+ unsigned int vref_mask;
+};
+
+#define to_ad9467_chip_info(_info) \
+ container_of(_info, struct ad9467_chip_info, axi_adc_info)
+
+struct ad9467_state {
+ struct spi_device *spi;
+ struct clk *clk;
+ unsigned int output_mode;
+ unsigned int (*scales)[2];
+
+ struct gpio_desc *pwrdown_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);
+ if (ret)
+ return ret;
+ return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+ }
+
+ ret = ad9467_spi_read(spi, reg);
+ if (ret < 0)
+ return ret;
+ *readval = ret;
+
+ return 0;
+}
+
+static const unsigned int ad9265_scale_table[][2] = {
+ {1250, 0x00}, {1500, 0x40}, {1750, 0x80}, {2000, 0xC0},
+};
+
+static const unsigned int ad9434_scale_table[][2] = {
+ {1600, 0x1C}, {1580, 0x1D}, {1550, 0x1E}, {1520, 0x1F}, {1500, 0x00},
+ {1470, 0x01}, {1440, 0x02}, {1420, 0x03}, {1390, 0x04}, {1360, 0x05},
+ {1340, 0x06}, {1310, 0x07}, {1280, 0x08}, {1260, 0x09}, {1230, 0x0A},
+ {1200, 0x0B}, {1180, 0x0C},
+};
+
+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), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = _si, \
+ .scan_type = { \
+ .sign = _sign, \
+ .realbits = _bits, \
+ .storagebits = 16, \
+ }, \
+}
+
+static const struct iio_chan_spec ad9434_channels[] = {
+ AD9467_CHAN(0, 0, 12, 'S'),
+};
+
+static const struct iio_chan_spec ad9467_channels[] = {
+ AD9467_CHAN(0, 0, 16, 'S'),
+};
+
+static const struct ad9467_chip_info ad9467_chip_tbl[] = {
+ [ID_AD9265] = {
+ .axi_adc_info = {
+ .id = CHIPID_AD9265,
+ .max_rate = 125000000UL,
+ .scale_table = ad9265_scale_table,
+ .num_scales = ARRAY_SIZE(ad9265_scale_table),
+ .channels = ad9467_channels,
+ .num_channels = ARRAY_SIZE(ad9467_channels),
+ },
+ .default_output_mode = AD9265_DEF_OUTPUT_MODE,
+ .vref_mask = AD9265_REG_VREF_MASK,
+ },
+ [ID_AD9434] = {
+ .axi_adc_info = {
+ .id = CHIPID_AD9434,
+ .max_rate = 500000000UL,
+ .scale_table = ad9434_scale_table,
+ .num_scales = ARRAY_SIZE(ad9434_scale_table),
+ .channels = ad9434_channels,
+ .num_channels = ARRAY_SIZE(ad9434_channels),
+ },
+ .default_output_mode = AD9434_DEF_OUTPUT_MODE,
+ .vref_mask = AD9434_REG_VREF_MASK,
+ },
+ [ID_AD9467] = {
+ .axi_adc_info = {
+ .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),
+ },
+ .default_output_mode = AD9467_DEF_OUTPUT_MODE,
+ .vref_mask = AD9467_REG_VREF_MASK,
+ },
+};
+
+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;
+ const struct ad9467_chip_info *info1 = to_ad9467_chip_info(info);
+ struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
+ unsigned int i, vref_val;
+ int ret;
+
+ ret = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF);
+ if (ret < 0)
+ return ret;
+
+ vref_val = ret & info1->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;
+ int ret;
+
+ 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;
+
+ ret = ad9467_spi_write(st->spi, AN877_ADC_REG_VREF,
+ info->scale_table[i][1]);
+ if (ret < 0)
+ return ret;
+
+ return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+ }
+
+ 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_read_avail(struct adi_axi_adc_conv *conv,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ const struct adi_axi_adc_chip_info *info = conv->chip_info;
+ struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)st->scales;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* Values are stored in a 2D matrix */
+ *length = info->num_scales * 2;
+ return IIO_AVAIL_LIST;
+ 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_scale_fill(struct adi_axi_adc_conv *conv)
+{
+ const struct adi_axi_adc_chip_info *info = conv->chip_info;
+ struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
+ unsigned int i, val1, val2;
+
+ st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales,
+ sizeof(*st->scales), GFP_KERNEL);
+ if (!st->scales)
+ return -ENOMEM;
+
+ for (i = 0; i < info->num_scales; i++) {
+ __ad9467_get_scale(conv, i, &val1, &val2);
+ st->scales[i][0] = val1;
+ st->scales[i][1] = val2;
+ }
+
+ return 0;
+}
+
+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_reset(struct device *dev)
+{
+ struct gpio_desc *gpio;
+
+ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR_OR_NULL(gpio))
+ return PTR_ERR_OR_ZERO(gpio);
+
+ fsleep(1);
+ gpiod_set_value_cansleep(gpio, 0);
+ fsleep(10 * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static int ad9467_probe(struct spi_device *spi)
+{
+ const struct ad9467_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_enabled(&spi->dev, "adc-clk");
+ if (IS_ERR(st->clk))
+ return PTR_ERR(st->clk);
+
+ 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);
+
+ ret = ad9467_reset(&spi->dev);
+ if (ret)
+ return ret;
+
+ conv->chip_info = &info->axi_adc_info;
+
+ ret = ad9467_scale_fill(conv);
+ if (ret)
+ return ret;
+
+ id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID);
+ if (id != conv->chip_info->id) {
+ dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n",
+ id, conv->chip_info->id);
+ return -ENODEV;
+ }
+
+ conv->reg_access = ad9467_reg_access;
+ conv->write_raw = ad9467_write_raw;
+ conv->read_raw = ad9467_read_raw;
+ conv->read_avail = ad9467_read_avail;
+ conv->preenable_setup = ad9467_preenable_setup;
+
+ st->output_mode = info->default_output_mode |
+ AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+
+ return 0;
+}
+
+static const struct of_device_id ad9467_of_match[] = {
+ { .compatible = "adi,ad9265", .data = &ad9467_chip_tbl[ID_AD9265], },
+ { .compatible = "adi,ad9434", .data = &ad9467_chip_tbl[ID_AD9434], },
+ { .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 <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_ADI_AXI);
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
new file mode 100644
index 000000000..7e2192870
--- /dev/null
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Support code for Analog Devices Sigma-Delta ADCs
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/align.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include <asm/unaligned.h>
+
+
+#define AD_SD_COMM_CHAN_MASK 0x3
+
+#define AD_SD_REG_COMM 0x00
+#define AD_SD_REG_DATA 0x03
+
+/**
+ * ad_sd_set_comm() - Set communications register
+ *
+ * @sigma_delta: The sigma delta device
+ * @comm: New value for the communications register
+ */
+void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
+{
+ /* Some variants use the lower two bits of the communications register
+ * to select the channel */
+ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, IIO_AD_SIGMA_DELTA);
+
+/**
+ * ad_sd_write_reg() - Write a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (0-3)
+ * @val: Value to write to the register
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
+ unsigned int size, unsigned int val)
+{
+ uint8_t *data = sigma_delta->tx_buf;
+ struct spi_transfer t = {
+ .tx_buf = data,
+ .len = size + 1,
+ .cs_change = sigma_delta->keep_cs_asserted,
+ };
+ struct spi_message m;
+ int ret;
+
+ data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm;
+
+ switch (size) {
+ case 3:
+ put_unaligned_be24(val, &data[1]);
+ break;
+ case 2:
+ put_unaligned_be16(val, &data[1]);
+ break;
+ case 1:
+ data[1] = val;
+ break;
+ case 0:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ if (sigma_delta->bus_locked)
+ ret = spi_sync_locked(sigma_delta->spi, &m);
+ else
+ ret = spi_sync(sigma_delta->spi, &m);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, IIO_AD_SIGMA_DELTA);
+
+static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
+ unsigned int reg, unsigned int size, uint8_t *val)
+{
+ uint8_t *data = sigma_delta->tx_buf;
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = data,
+ .len = 1,
+ }, {
+ .rx_buf = val,
+ .len = size,
+ .cs_change = sigma_delta->bus_locked,
+ },
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+
+ if (sigma_delta->info->has_registers) {
+ data[0] = reg << sigma_delta->info->addr_shift;
+ data[0] |= sigma_delta->info->read_mask;
+ data[0] |= sigma_delta->comm;
+ spi_message_add_tail(&t[0], &m);
+ }
+ spi_message_add_tail(&t[1], &m);
+
+ if (sigma_delta->bus_locked)
+ ret = spi_sync_locked(sigma_delta->spi, &m);
+ else
+ ret = spi_sync(sigma_delta->spi, &m);
+
+ return ret;
+}
+
+/**
+ * ad_sd_read_reg() - Read a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (1-4)
+ * @val: Read value
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
+ unsigned int reg, unsigned int size, unsigned int *val)
+{
+ int ret;
+
+ ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->rx_buf);
+ if (ret < 0)
+ goto out;
+
+ switch (size) {
+ case 4:
+ *val = get_unaligned_be32(sigma_delta->rx_buf);
+ break;
+ case 3:
+ *val = get_unaligned_be24(sigma_delta->rx_buf);
+ break;
+ case 2:
+ *val = get_unaligned_be16(sigma_delta->rx_buf);
+ break;
+ case 1:
+ *val = sigma_delta->rx_buf[0];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+out:
+ return ret;
+}
+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)
+{
+ uint8_t *buf;
+ unsigned int size;
+ int ret;
+
+ size = DIV_ROUND_UP(reset_length, 8);
+ buf = kcalloc(size, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ memset(buf, 0xff, size);
+ ret = spi_write(sigma_delta->spi, buf, size);
+ kfree(buf);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_reset, IIO_AD_SIGMA_DELTA);
+
+int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
+ unsigned int mode, unsigned int channel)
+{
+ int ret;
+ unsigned long timeout;
+
+ ret = ad_sigma_delta_set_channel(sigma_delta, channel);
+ if (ret)
+ return ret;
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
+ reinit_completion(&sigma_delta->completion);
+
+ ret = ad_sigma_delta_set_mode(sigma_delta, mode);
+ if (ret < 0)
+ goto out;
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+ timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
+ if (timeout == 0) {
+ sigma_delta->irq_dis = true;
+ disable_irq_nosync(sigma_delta->spi->irq);
+ ret = -EIO;
+ } else {
+ ret = 0;
+ }
+out:
+ sigma_delta->keep_cs_asserted = false;
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, IIO_AD_SIGMA_DELTA);
+
+/**
+ * ad_sd_calibrate_all() - Performs channel calibration
+ * @sigma_delta: The sigma delta device
+ * @cb: Array of channels and calibration type to perform
+ * @n: Number of items in cb
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
+ const struct ad_sd_calib_data *cb, unsigned int n)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < n; i++) {
+ ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, IIO_AD_SIGMA_DELTA);
+
+/**
+ * ad_sigma_delta_single_conversion() - Performs a single data conversion
+ * @indio_dev: The IIO device
+ * @chan: The conversion is done for this channel
+ * @val: Pointer to the location where to store the read value
+ *
+ * Returns: 0 on success, an error value otherwise.
+ */
+int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int sample, raw_sample;
+ unsigned int data_reg;
+ int ret = 0;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ad_sigma_delta_set_channel(sigma_delta, chan->address);
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
+ reinit_completion(&sigma_delta->completion);
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+ ret = wait_for_completion_interruptible_timeout(
+ &sigma_delta->completion, HZ);
+
+ if (ret == 0)
+ ret = -EIO;
+ if (ret < 0)
+ goto out;
+
+ if (sigma_delta->info->data_reg != 0)
+ data_reg = sigma_delta->info->data_reg;
+ else
+ data_reg = AD_SD_REG_DATA;
+
+ ret = ad_sd_read_reg(sigma_delta, data_reg,
+ DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
+ &raw_sample);
+
+out:
+ if (!sigma_delta->irq_dis) {
+ disable_irq_nosync(sigma_delta->spi->irq);
+ sigma_delta->irq_dis = true;
+ }
+
+ sigma_delta->keep_cs_asserted = false;
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret)
+ return ret;
+
+ sample = raw_sample >> chan->scan_type.shift;
+ sample &= (1 << chan->scan_type.realbits) - 1;
+ *val = sample;
+
+ ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, IIO_AD_SIGMA_DELTA);
+
+static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int i, slot, samples_buf_size;
+ unsigned int channel;
+ uint8_t *samples_buf;
+ int ret;
+
+ if (sigma_delta->num_slots == 1) {
+ channel = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ ret = ad_sigma_delta_set_channel(sigma_delta,
+ indio_dev->channels[channel].address);
+ if (ret)
+ return ret;
+ slot = 1;
+ } else {
+ /*
+ * At this point update_scan_mode already enabled the required channels.
+ * For sigma-delta sequencer drivers with multiple slots, an update_scan_mode
+ * implementation is mandatory.
+ */
+ slot = 0;
+ for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) {
+ sigma_delta->slots[slot] = indio_dev->channels[i].address;
+ slot++;
+ }
+ }
+
+ sigma_delta->active_slots = slot;
+ sigma_delta->current_slot = 0;
+
+ if (sigma_delta->active_slots > 1) {
+ ret = ad_sigma_delta_append_status(sigma_delta, true);
+ if (ret)
+ return ret;
+ }
+
+ samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits, 8);
+ samples_buf_size += sizeof(int64_t);
+ samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf,
+ samples_buf_size, GFP_KERNEL);
+ if (!samples_buf)
+ return -ENOMEM;
+
+ sigma_delta->samples_buf = samples_buf;
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
+
+ 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->spi->irq);
+
+ return 0;
+
+err_unlock:
+ spi_bus_unlock(sigma_delta->spi->master);
+
+ return ret;
+}
+
+static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(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->spi->irq);
+ sigma_delta->irq_dis = true;
+ }
+
+ sigma_delta->keep_cs_asserted = false;
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+ if (sigma_delta->status_appended)
+ ad_sigma_delta_append_status(sigma_delta, false);
+
+ ad_sigma_delta_disable_all(sigma_delta);
+ sigma_delta->bus_locked = false;
+ return spi_bus_unlock(sigma_delta->spi->master);
+}
+
+static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ uint8_t *data = sigma_delta->rx_buf;
+ unsigned int transfer_size;
+ unsigned int sample_size;
+ unsigned int sample_pos;
+ unsigned int status_pos;
+ unsigned int reg_size;
+ unsigned int data_reg;
+
+ reg_size = indio_dev->channels[0].scan_type.realbits +
+ indio_dev->channels[0].scan_type.shift;
+ reg_size = DIV_ROUND_UP(reg_size, 8);
+
+ if (sigma_delta->info->data_reg != 0)
+ data_reg = sigma_delta->info->data_reg;
+ else
+ data_reg = AD_SD_REG_DATA;
+
+ /* Status word will be appended to the sample during transfer */
+ if (sigma_delta->status_appended)
+ transfer_size = reg_size + 1;
+ else
+ transfer_size = reg_size;
+
+ switch (reg_size) {
+ case 4:
+ case 2:
+ case 1:
+ status_pos = reg_size;
+ ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[0]);
+ break;
+ case 3:
+ /*
+ * Data array after transfer will look like (if status is appended):
+ * data[] = { [0][sample][sample][sample][status] }
+ * Keeping the first byte 0 shifts the status postion by 1 byte to the right.
+ */
+ status_pos = reg_size + 1;
+
+ /* We store 24 bit samples in a 32 bit word. Keep the upper
+ * byte set to zero. */
+ ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]);
+ break;
+ }
+
+ /*
+ * For devices sampling only one channel at
+ * once, there is no need for sample number tracking.
+ */
+ if (sigma_delta->active_slots == 1) {
+ iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);
+ goto irq_handled;
+ }
+
+ if (sigma_delta->status_appended) {
+ u8 converted_channel;
+
+ converted_channel = data[status_pos] & sigma_delta->info->status_ch_mask;
+ if (converted_channel != sigma_delta->slots[sigma_delta->current_slot]) {
+ /*
+ * Desync occurred during continuous sampling of multiple channels.
+ * Drop this incomplete sample and start from first channel again.
+ */
+
+ sigma_delta->current_slot = 0;
+ goto irq_handled;
+ }
+ }
+
+ sample_size = indio_dev->channels[0].scan_type.storagebits / 8;
+ sample_pos = sample_size * sigma_delta->current_slot;
+ memcpy(&sigma_delta->samples_buf[sample_pos], data, sample_size);
+ sigma_delta->current_slot++;
+
+ if (sigma_delta->current_slot == sigma_delta->active_slots) {
+ sigma_delta->current_slot = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, sigma_delta->samples_buf,
+ pf->timestamp);
+ }
+
+irq_handled:
+ iio_trigger_notify_done(indio_dev->trig);
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+
+ return IRQ_HANDLED;
+}
+
+static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned long *mask)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ return bitmap_weight(mask, indio_dev->masklength) <= sigma_delta->num_slots;
+}
+
+static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
+ .postenable = &ad_sd_buffer_postenable,
+ .postdisable = &ad_sd_buffer_postdisable,
+ .validate_scan_mask = &ad_sd_validate_scan_mask,
+};
+
+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);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices
+ * @indio_dev: The IIO device
+ * @trig: The new trigger
+ *
+ * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta
+ * device, -EINVAL otherwise.
+ */
+int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ if (sigma_delta->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA);
+
+static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ if (dev != &sigma_delta->spi->dev) {
+ dev_err(dev, "Trigger parent should be '%s', got '%s'\n",
+ dev_name(dev), dev_name(&sigma_delta->spi->dev));
+ return -EFAULT;
+ }
+
+ sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
+ iio_device_id(indio_dev));
+ if (sigma_delta->trig == NULL)
+ return -ENOMEM;
+
+ init_completion(&sigma_delta->completion);
+
+ sigma_delta->irq_dis = true;
+
+ /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */
+ irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY);
+
+ ret = devm_request_irq(dev, sigma_delta->spi->irq,
+ ad_sd_data_rdy_trig_poll,
+ sigma_delta->info->irq_flags | IRQF_NO_AUTOEN,
+ indio_dev->name,
+ sigma_delta);
+ if (ret)
+ return ret;
+
+ iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
+
+ ret = devm_iio_trigger_register(dev, sigma_delta->trig);
+ if (ret)
+ return ret;
+
+ /* select default trigger */
+ indio_dev->trig = iio_trigger_get(sigma_delta->trig);
+
+ return 0;
+}
+
+/**
+ * devm_ad_sd_setup_buffer_and_trigger() - Device-managed buffer & trigger setup
+ * @dev: Device object to which to bind the life-time of the resources attached
+ * @indio_dev: The IIO device
+ */
+int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ sigma_delta->slots = devm_kcalloc(dev, sigma_delta->num_slots,
+ sizeof(*sigma_delta->slots), GFP_KERNEL);
+ if (!sigma_delta->slots)
+ return -ENOMEM;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad_sd_trigger_handler,
+ &ad_sd_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_ad_sd_probe_trigger(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, IIO_AD_SIGMA_DELTA);
+
+/**
+ * ad_sd_init() - Initializes a ad_sigma_delta struct
+ * @sigma_delta: The ad_sigma_delta device
+ * @indio_dev: The IIO device which the Sigma Delta device is used for
+ * @spi: The SPI device for the ad_sigma_delta device
+ * @info: Device specific callbacks and options
+ *
+ * This function needs to be called before any other operations are performed on
+ * the ad_sigma_delta struct.
+ */
+int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
+ struct spi_device *spi, const struct ad_sigma_delta_info *info)
+{
+ sigma_delta->spi = spi;
+ sigma_delta->info = info;
+
+ /* If the field is unset in ad_sigma_delta_info, asume there can only be 1 slot. */
+ if (!info->num_slots)
+ sigma_delta->num_slots = 1;
+ else
+ sigma_delta->num_slots = info->num_slots;
+
+ if (sigma_delta->num_slots > 1) {
+ if (!indio_dev->info->update_scan_mode) {
+ dev_err(&spi->dev, "iio_dev lacks update_scan_mode().\n");
+ return -EINVAL;
+ }
+
+ if (!info->disable_all) {
+ dev_err(&spi->dev, "ad_sigma_delta_info lacks disable_all().\n");
+ return -EINVAL;
+ }
+ }
+
+ iio_device_set_drvdata(indio_dev, sigma_delta);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ad_sd_init, IIO_AD_SIGMA_DELTA);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
new file mode 100644
index 000000000..ad386ac7f
--- /dev/null
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -0,0 +1,429 @@
+// 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 <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/adc/adi-axi-adc.h>
+
+/*
+ * 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_DMA_MINALIGN);
+}
+EXPORT_SYMBOL_NS_GPL(adi_axi_adc_conv_priv, IIO_ADI_AXI);
+
+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)
+{
+ 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";
+
+ return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent,
+ indio_dev, dma_name);
+}
+
+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_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct adi_axi_adc_state *st = iio_priv(indio_dev);
+ struct adi_axi_adc_conv *conv = &st->client->conv;
+
+ if (!conv->read_avail)
+ return -EOPNOTSUPP;
+
+ return conv->read_avail(conv, chan, vals, type, length, 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_DMA_MINALIGN);
+ if (sizeof_priv)
+ alloc_size += ALIGN(sizeof_priv, IIO_DMA_MINALIGN);
+
+ cl = kzalloc(alloc_size, GFP_KERNEL);
+ if (!cl)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_lock(&registered_clients_lock);
+
+ cl->dev = get_device(dev);
+
+ list_add_tail(&cl->entry, &registered_clients);
+
+ mutex_unlock(&registered_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(&registered_clients_lock);
+
+ list_del(&cl->entry);
+ put_device(cl->dev);
+
+ mutex_unlock(&registered_clients_lock);
+
+ kfree(cl);
+}
+
+static void devm_adi_axi_adc_conv_release(void *conv)
+{
+ adi_axi_adc_conv_unregister(conv);
+}
+
+struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev,
+ size_t sizeof_priv)
+{
+ struct adi_axi_adc_conv *conv;
+ int ret;
+
+ conv = adi_axi_adc_conv_register(dev, sizeof_priv);
+ if (IS_ERR(conv))
+ return conv;
+
+ ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release,
+ conv);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return conv;
+}
+EXPORT_SYMBOL_NS_GPL(devm_adi_axi_adc_conv_register, IIO_ADI_AXI);
+
+static const struct iio_info adi_axi_adc_info = {
+ .read_raw = &adi_axi_adc_read_raw,
+ .write_raw = &adi_axi_adc_write_raw,
+ .update_scan_mode = &adi_axi_adc_update_scan_mode,
+ .read_avail = &adi_axi_adc_read_avail,
+};
+
+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(&registered_clients_lock);
+
+ list_for_each_entry(cl, &registered_clients, entry) {
+ if (!cl->dev)
+ continue;
+
+ if (cl->dev->of_node != cln)
+ continue;
+
+ if (!try_module_get(cl->dev->driver->owner)) {
+ mutex_unlock(&registered_clients_lock);
+ of_node_put(cln);
+ return ERR_PTR(-ENODEV);
+ }
+
+ get_device(cl->dev);
+ cl->info = info;
+ mutex_unlock(&registered_clients_lock);
+ of_node_put(cln);
+ return cl;
+ }
+
+ mutex_unlock(&registered_clients_lock);
+ of_node_put(cln);
+
+ 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->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 <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
new file mode 100644
index 000000000..998e8bcc0
--- /dev/null
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -0,0 +1,728 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Aspeed AST2400/2500/2600 ADC
+ *
+ * Copyright (C) 2017 Google, Inc.
+ * Copyright (C) 2021 Aspeed Technology Inc.
+ *
+ * ADC clock formula:
+ * Ast2400/Ast2500:
+ * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1)
+ * Ast2600:
+ * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1)
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iopoll.h>
+
+#define ASPEED_RESOLUTION_BITS 10
+#define ASPEED_CLOCKS_PER_SAMPLE 12
+
+#define ASPEED_REG_ENGINE_CONTROL 0x00
+#define ASPEED_REG_INTERRUPT_CONTROL 0x04
+#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
+#define ASPEED_REG_CLOCK_CONTROL 0x0C
+#define ASPEED_REG_COMPENSATION_TRIM 0xC4
+/*
+ * The register offset between 0xC8~0xCC can be read and won't affect the
+ * hardware logic in each version of ADC.
+ */
+#define ASPEED_REG_MAX 0xD0
+
+#define ASPEED_ADC_ENGINE_ENABLE BIT(0)
+#define ASPEED_ADC_OP_MODE GENMASK(3, 1)
+#define ASPEED_ADC_OP_MODE_PWR_DOWN 0
+#define ASPEED_ADC_OP_MODE_STANDBY 1
+#define ASPEED_ADC_OP_MODE_NORMAL 7
+#define ASPEED_ADC_CTRL_COMPENSATION BIT(4)
+#define ASPEED_ADC_AUTO_COMPENSATION BIT(5)
+/*
+ * Bit 6 determines not only the reference voltage range but also the dividing
+ * circuit for battery sensing.
+ */
+#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6)
+#define ASPEED_ADC_REF_VOLTAGE_2500mV 0
+#define ASPEED_ADC_REF_VOLTAGE_1200mV 1
+#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2
+#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3
+#define ASPEED_ADC_BAT_SENSING_DIV BIT(6)
+#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0
+#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1
+#define ASPEED_ADC_CTRL_INIT_RDY BIT(8)
+#define ASPEED_ADC_CH7_MODE BIT(12)
+#define ASPEED_ADC_CH7_NORMAL 0
+#define ASPEED_ADC_CH7_BAT 1
+#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13)
+#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16)
+#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch))
+
+#define ASPEED_ADC_INIT_POLLING_TIME 500
+#define ASPEED_ADC_INIT_TIMEOUT 500000
+/*
+ * When the sampling rate is too high, the ADC may not have enough charging
+ * time, resulting in a low voltage value. Thus, the default uses a slow
+ * sampling rate for most use cases.
+ */
+#define ASPEED_ADC_DEF_SAMPLING_RATE 65000
+
+struct aspeed_adc_trim_locate {
+ const unsigned int offset;
+ const unsigned int field;
+};
+
+struct aspeed_adc_model_data {
+ const char *model_name;
+ unsigned int min_sampling_rate; // Hz
+ unsigned int max_sampling_rate; // Hz
+ unsigned int vref_fixed_mv;
+ bool wait_init_sequence;
+ bool need_prescaler;
+ bool bat_sense_sup;
+ u8 scaler_bit_width;
+ unsigned int num_channels;
+ const struct aspeed_adc_trim_locate *trim_locate;
+};
+
+struct adc_gain {
+ u8 mult;
+ u8 div;
+};
+
+struct aspeed_adc_data {
+ struct device *dev;
+ const struct aspeed_adc_model_data *model_data;
+ struct regulator *regulator;
+ void __iomem *base;
+ spinlock_t clk_lock;
+ struct clk_hw *fixed_div_clk;
+ struct clk_hw *clk_prescaler;
+ struct clk_hw *clk_scaler;
+ struct reset_control *rst;
+ int vref_mv;
+ u32 sample_period_ns;
+ int cv;
+ bool battery_sensing;
+ struct adc_gain battery_mode_gain;
+};
+
+#define ASPEED_CHAN(_idx, _data_reg_addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .address = (_data_reg_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
+ ASPEED_CHAN(0, 0x10),
+ ASPEED_CHAN(1, 0x12),
+ ASPEED_CHAN(2, 0x14),
+ ASPEED_CHAN(3, 0x16),
+ ASPEED_CHAN(4, 0x18),
+ ASPEED_CHAN(5, 0x1A),
+ ASPEED_CHAN(6, 0x1C),
+ ASPEED_CHAN(7, 0x1E),
+ ASPEED_CHAN(8, 0x20),
+ ASPEED_CHAN(9, 0x22),
+ ASPEED_CHAN(10, 0x24),
+ ASPEED_CHAN(11, 0x26),
+ ASPEED_CHAN(12, 0x28),
+ ASPEED_CHAN(13, 0x2A),
+ ASPEED_CHAN(14, 0x2C),
+ ASPEED_CHAN(15, 0x2E),
+};
+
+#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .address = (_data_reg_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = {
+ ASPEED_CHAN(0, 0x10),
+ ASPEED_CHAN(1, 0x12),
+ ASPEED_CHAN(2, 0x14),
+ ASPEED_CHAN(3, 0x16),
+ ASPEED_CHAN(4, 0x18),
+ ASPEED_CHAN(5, 0x1A),
+ ASPEED_CHAN(6, 0x1C),
+ ASPEED_BAT_CHAN(7, 0x1E),
+};
+
+static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev)
+{
+ struct device_node *syscon;
+ struct regmap *scu;
+ u32 scu_otp, trimming_val;
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ syscon = of_find_node_by_name(NULL, "syscon");
+ if (syscon == NULL) {
+ dev_warn(data->dev, "Couldn't find syscon node\n");
+ return -EOPNOTSUPP;
+ }
+ scu = syscon_node_to_regmap(syscon);
+ of_node_put(syscon);
+ if (IS_ERR(scu)) {
+ dev_warn(data->dev, "Failed to get syscon regmap\n");
+ return -EOPNOTSUPP;
+ }
+ if (data->model_data->trim_locate) {
+ if (regmap_read(scu, data->model_data->trim_locate->offset,
+ &scu_otp)) {
+ dev_warn(data->dev,
+ "Failed to get adc trimming data\n");
+ trimming_val = 0x8;
+ } else {
+ trimming_val =
+ ((scu_otp) &
+ (data->model_data->trim_locate->field)) >>
+ __ffs(data->model_data->trim_locate->field);
+ if (!trimming_val)
+ trimming_val = 0x8;
+ }
+ dev_dbg(data->dev,
+ "trimming val = %d, offset = %08x, fields = %08x\n",
+ trimming_val, data->model_data->trim_locate->offset,
+ data->model_data->trim_locate->field);
+ writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM);
+ }
+ return 0;
+}
+
+static int aspeed_adc_compensation(struct iio_dev *indio_dev)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ u32 index, adc_raw = 0;
+ u32 adc_engine_control_reg_val;
+
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE;
+ adc_engine_control_reg_val |=
+ (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+ ASPEED_ADC_ENGINE_ENABLE);
+ /*
+ * Enable compensating sensing:
+ * After that, the input voltage of ADC will force to half of the reference
+ * voltage. So the expected reading raw data will become half of the max
+ * value. We can get compensating value = 0x200 - ADC read raw value.
+ * It is recommended to average at least 10 samples to get a final CV.
+ */
+ writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION |
+ ASPEED_ADC_CTRL_CHANNEL_ENABLE(0),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable compensating sensing mode need to wait some time for ADC stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+
+ for (index = 0; index < 16; index++) {
+ /*
+ * Waiting for the sampling period ensures that the value acquired
+ * is fresh each time.
+ */
+ ndelay(data->sample_period_ns);
+ adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address);
+ }
+ adc_raw >>= 4;
+ data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw;
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ dev_dbg(data->dev, "Compensating value = %d\n", data->cv);
+
+ return 0;
+}
+
+static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ if (rate < data->model_data->min_sampling_rate ||
+ rate > data->model_data->max_sampling_rate)
+ return -EINVAL;
+ /* Each sampling needs 12 clocks to convert.*/
+ clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE);
+ rate = clk_get_rate(data->clk_scaler->clk);
+ data->sample_period_ns = DIV_ROUND_UP_ULL(
+ (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate);
+ dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate,
+ data->sample_period_ns);
+
+ return 0;
+}
+
+static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ u32 adc_engine_control_reg_val;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (data->battery_sensing && chan->channel == 7) {
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_CH7_MODE,
+ ASPEED_ADC_CH7_BAT) |
+ ASPEED_ADC_BAT_SENSING_ENABLE,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ /*
+ * After enable battery sensing mode need to wait some time for adc stable
+ * Experiment result is 1ms.
+ */
+ mdelay(1);
+ *val = readw(data->base + chan->address);
+ *val = (*val * data->battery_mode_gain.mult) /
+ data->battery_mode_gain.div;
+ /* Restore control register value */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ } else
+ *val = readw(data->base + chan->address);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (data->battery_sensing && chan->channel == 7)
+ *val = (data->cv * data->battery_mode_gain.mult) /
+ data->battery_mode_gain.div;
+ else
+ *val = data->cv;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = data->vref_mv;
+ *val2 = ASPEED_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = clk_get_rate(data->clk_scaler->clk) /
+ ASPEED_CLOCKS_PER_SAMPLE;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int aspeed_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return aspeed_adc_set_sampling_rate(indio_dev, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Technically, these could be written but the only reasons
+ * for doing so seem better handled in userspace. EPERM is
+ * returned to signal this is a policy choice rather than a
+ * hardware limitation.
+ */
+ return -EPERM;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int aspeed_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ if (!readval || reg % 4 || reg > ASPEED_REG_MAX)
+ return -EINVAL;
+
+ *readval = readl(data->base + reg);
+
+ return 0;
+}
+
+static const struct iio_info aspeed_adc_iio_info = {
+ .read_raw = aspeed_adc_read_raw,
+ .write_raw = aspeed_adc_write_raw,
+ .debugfs_reg_access = aspeed_adc_reg_access,
+};
+
+static void aspeed_adc_unregister_fixed_divider(void *data)
+{
+ struct clk_hw *clk = data;
+
+ clk_hw_unregister_fixed_factor(clk);
+}
+
+static void aspeed_adc_reset_assert(void *data)
+{
+ struct reset_control *rst = data;
+
+ reset_control_assert(rst);
+}
+
+static void aspeed_adc_clk_disable_unprepare(void *data)
+{
+ struct clk *clk = data;
+
+ clk_disable_unprepare(clk);
+}
+
+static void aspeed_adc_power_down(void *data)
+{
+ struct aspeed_adc_data *priv_data = data;
+
+ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN),
+ priv_data->base + ASPEED_REG_ENGINE_CONTROL);
+}
+
+static void aspeed_adc_reg_disable(void *data)
+{
+ struct regulator *reg = data;
+
+ regulator_disable(reg);
+}
+
+static int aspeed_adc_vref_config(struct iio_dev *indio_dev)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ int ret;
+ u32 adc_engine_control_reg_val;
+
+ if (data->model_data->vref_fixed_mv) {
+ data->vref_mv = data->model_data->vref_fixed_mv;
+ return 0;
+ }
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ data->regulator = devm_regulator_get_optional(data->dev, "vref");
+ if (!IS_ERR(data->regulator)) {
+ ret = regulator_enable(data->regulator);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(
+ data->dev, aspeed_adc_reg_disable, data->regulator);
+ if (ret)
+ return ret;
+ data->vref_mv = regulator_get_voltage(data->regulator);
+ /* Conversion from uV to mV */
+ data->vref_mv /= 1000;
+ if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700))
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(
+ ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_EXT_HIGH),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650))
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(
+ ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_EXT_LOW),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else {
+ dev_err(data->dev, "Regulator voltage %d not support",
+ data->vref_mv);
+ return -EOPNOTSUPP;
+ }
+ } else {
+ if (PTR_ERR(data->regulator) != -ENODEV)
+ return PTR_ERR(data->regulator);
+ data->vref_mv = 2500000;
+ of_property_read_u32(data->dev->of_node,
+ "aspeed,int-vref-microvolt",
+ &data->vref_mv);
+ /* Conversion from uV to mV */
+ data->vref_mv /= 1000;
+ if (data->vref_mv == 2500)
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_2500mV),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else if (data->vref_mv == 1200)
+ writel(adc_engine_control_reg_val |
+ FIELD_PREP(ASPEED_ADC_REF_VOLTAGE,
+ ASPEED_ADC_REF_VOLTAGE_1200mV),
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ else {
+ dev_err(data->dev, "Voltage %d not support", data->vref_mv);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+static int aspeed_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct aspeed_adc_data *data;
+ int ret;
+ u32 adc_engine_control_reg_val;
+ unsigned long scaler_flags = 0;
+ char clk_name[32], clk_parent_name[32];
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = &pdev->dev;
+ data->model_data = of_device_get_match_data(&pdev->dev);
+ platform_set_drvdata(pdev, indio_dev);
+
+ data->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ /* Register ADC clock prescaler with source specified by device tree. */
+ spin_lock_init(&data->clk_lock);
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
+ of_clk_get_parent_name(pdev->dev.of_node, 0));
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
+ data->model_data->model_name);
+ data->fixed_div_clk = clk_hw_register_fixed_factor(
+ &pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
+ if (IS_ERR(data->fixed_div_clk))
+ return PTR_ERR(data->fixed_div_clk);
+
+ ret = devm_add_action_or_reset(data->dev,
+ aspeed_adc_unregister_fixed_divider,
+ data->fixed_div_clk);
+ if (ret)
+ return ret;
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name);
+
+ if (data->model_data->need_prescaler) {
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
+ data->model_data->model_name);
+ data->clk_prescaler = devm_clk_hw_register_divider(
+ &pdev->dev, clk_name, clk_parent_name, 0,
+ data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
+ &data->clk_lock);
+ if (IS_ERR(data->clk_prescaler))
+ return PTR_ERR(data->clk_prescaler);
+ snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name),
+ clk_name);
+ scaler_flags = CLK_SET_RATE_PARENT;
+ }
+ /*
+ * Register ADC clock scaler downstream from the prescaler. Allow rate
+ * setting to adjust the prescaler as well.
+ */
+ snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
+ data->model_data->model_name);
+ data->clk_scaler = devm_clk_hw_register_divider(
+ &pdev->dev, clk_name, clk_parent_name, scaler_flags,
+ data->base + ASPEED_REG_CLOCK_CONTROL, 0,
+ data->model_data->scaler_bit_width,
+ data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0,
+ &data->clk_lock);
+ if (IS_ERR(data->clk_scaler))
+ return PTR_ERR(data->clk_scaler);
+
+ data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
+ if (IS_ERR(data->rst)) {
+ dev_err(&pdev->dev,
+ "invalid or missing reset controller device tree entry");
+ return PTR_ERR(data->rst);
+ }
+ reset_control_deassert(data->rst);
+
+ ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
+ data->rst);
+ if (ret)
+ return ret;
+
+ ret = aspeed_adc_vref_config(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = aspeed_adc_set_trim_data(indio_dev);
+ if (ret)
+ return ret;
+
+ if (of_find_property(data->dev->of_node, "aspeed,battery-sensing",
+ NULL)) {
+ if (data->model_data->bat_sense_sup) {
+ data->battery_sensing = 1;
+ if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
+ ASPEED_ADC_BAT_SENSING_DIV) {
+ data->battery_mode_gain.mult = 3;
+ data->battery_mode_gain.div = 1;
+ } else {
+ data->battery_mode_gain.mult = 3;
+ data->battery_mode_gain.div = 2;
+ }
+ } else
+ dev_warn(&pdev->dev,
+ "Failed to enable battery-sensing mode\n");
+ }
+
+ ret = clk_prepare_enable(data->clk_scaler->clk);
+ if (ret)
+ return ret;
+ ret = devm_add_action_or_reset(data->dev,
+ aspeed_adc_clk_disable_unprepare,
+ data->clk_scaler->clk);
+ if (ret)
+ return ret;
+ ret = aspeed_adc_set_sampling_rate(indio_dev,
+ ASPEED_ADC_DEF_SAMPLING_RATE);
+ if (ret)
+ return ret;
+
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |=
+ FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) |
+ ASPEED_ADC_ENGINE_ENABLE;
+ /* Enable engine in normal mode. */
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+
+ ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
+ data);
+ if (ret)
+ return ret;
+
+ if (data->model_data->wait_init_sequence) {
+ /* Wait for initial sequence complete. */
+ ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL,
+ adc_engine_control_reg_val,
+ adc_engine_control_reg_val &
+ ASPEED_ADC_CTRL_INIT_RDY,
+ ASPEED_ADC_INIT_POLLING_TIME,
+ ASPEED_ADC_INIT_TIMEOUT);
+ if (ret)
+ return ret;
+ }
+
+ aspeed_adc_compensation(indio_dev);
+ /* Start all channels in normal mode. */
+ adc_engine_control_reg_val =
+ readl(data->base + ASPEED_REG_ENGINE_CONTROL);
+ adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL;
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+
+ indio_dev->name = data->model_data->model_name;
+ indio_dev->info = &aspeed_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = data->battery_sensing ?
+ aspeed_adc_iio_bat_channels :
+ aspeed_adc_iio_channels;
+ indio_dev->num_channels = data->model_data->num_channels;
+
+ ret = devm_iio_device_register(data->dev, indio_dev);
+ return ret;
+}
+
+static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
+ .offset = 0x154,
+ .field = GENMASK(31, 28),
+};
+
+static const struct aspeed_adc_trim_locate ast2600_adc0_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(3, 0),
+};
+
+static const struct aspeed_adc_trim_locate ast2600_adc1_trim = {
+ .offset = 0x5d0,
+ .field = GENMASK(7, 4),
+};
+
+static const struct aspeed_adc_model_data ast2400_model_data = {
+ .model_name = "ast2400-adc",
+ .vref_fixed_mv = 2500,
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .need_prescaler = true,
+ .scaler_bit_width = 10,
+ .num_channels = 16,
+};
+
+static const struct aspeed_adc_model_data ast2500_model_data = {
+ .model_name = "ast2500-adc",
+ .vref_fixed_mv = 1800,
+ .min_sampling_rate = 1,
+ .max_sampling_rate = 1000000,
+ .wait_init_sequence = true,
+ .need_prescaler = true,
+ .scaler_bit_width = 10,
+ .num_channels = 16,
+ .trim_locate = &ast2500_adc_trim,
+};
+
+static const struct aspeed_adc_model_data ast2600_adc0_model_data = {
+ .model_name = "ast2600-adc0",
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .wait_init_sequence = true,
+ .bat_sense_sup = true,
+ .scaler_bit_width = 16,
+ .num_channels = 8,
+ .trim_locate = &ast2600_adc0_trim,
+};
+
+static const struct aspeed_adc_model_data ast2600_adc1_model_data = {
+ .model_name = "ast2600-adc1",
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+ .wait_init_sequence = true,
+ .bat_sense_sup = true,
+ .scaler_bit_width = 16,
+ .num_channels = 8,
+ .trim_locate = &ast2600_adc1_trim,
+};
+
+static const struct of_device_id aspeed_adc_matches[] = {
+ { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
+ { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
+ { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data },
+ { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
+
+static struct platform_driver aspeed_adc_driver = {
+ .probe = aspeed_adc_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = aspeed_adc_matches,
+ }
+};
+
+module_platform_driver(aspeed_adc_driver);
+
+MODULE_AUTHOR("Rick Altherr <raltherr@google.com>");
+MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
new file mode 100644
index 000000000..3ad5678f2
--- /dev/null
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -0,0 +1,2659 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Atmel ADC driver for SAMA5D2 devices and compatible.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ * 2021 Microchip Technology, Inc. and its subsidiaries
+ * 2021 Eugen Hristev <eugen.hristev@microchip.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/sched.h>
+#include <linux/units.h>
+#include <linux/wait.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <dt-bindings/iio/adc/at91-sama5d2_adc.h>
+
+struct at91_adc_reg_layout {
+/* Control Register */
+ u16 CR;
+/* Software Reset */
+#define AT91_SAMA5D2_CR_SWRST BIT(0)
+/* Start Conversion */
+#define AT91_SAMA5D2_CR_START BIT(1)
+/* Touchscreen Calibration */
+#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
+/* Comparison Restart */
+#define AT91_SAMA5D2_CR_CMPRST BIT(4)
+
+/* Mode Register */
+ u16 MR;
+/* Trigger Selection */
+#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
+/* ADTRG */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
+/* TIOA0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
+/* TIOA1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
+/* TIOA2 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
+/* PWM event line 0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
+/* PWM event line 1 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
+/* TIOA3 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
+/* RTCOUT0 */
+#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
+/* Sleep Mode */
+#define AT91_SAMA5D2_MR_SLEEP BIT(5)
+/* Fast Wake Up */
+#define AT91_SAMA5D2_MR_FWUP BIT(6)
+/* Prescaler Rate Selection */
+#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET)
+#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8
+#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff
+#define AT91_SAMA5D2_MR_PRESCAL_MASK GENMASK(15, 8)
+/* Startup Time */
+#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
+#define AT91_SAMA5D2_MR_STARTUP_MASK GENMASK(19, 16)
+/* Minimum startup time for temperature sensor */
+#define AT91_SAMA5D2_MR_STARTUP_TS_MIN (50)
+/* Analog Change */
+#define AT91_SAMA5D2_MR_ANACH BIT(23)
+/* Tracking Time */
+#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
+#define AT91_SAMA5D2_MR_TRACKTIM_TS 6
+#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xf
+/* Transfer Time */
+#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
+#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
+/* Use Sequence Enable */
+#define AT91_SAMA5D2_MR_USEQ BIT(31)
+
+/* Channel Sequence Register 1 */
+ u16 SEQR1;
+/* Channel Sequence Register 2 */
+ u16 SEQR2;
+/* Channel Enable Register */
+ u16 CHER;
+/* Channel Disable Register */
+ u16 CHDR;
+/* Channel Status Register */
+ u16 CHSR;
+/* Last Converted Data Register */
+ u16 LCDR;
+/* Interrupt Enable Register */
+ u16 IER;
+/* Interrupt Enable Register - TS X measurement ready */
+#define AT91_SAMA5D2_IER_XRDY BIT(20)
+/* Interrupt Enable Register - TS Y measurement ready */
+#define AT91_SAMA5D2_IER_YRDY BIT(21)
+/* Interrupt Enable Register - TS pressure measurement ready */
+#define AT91_SAMA5D2_IER_PRDY BIT(22)
+/* Interrupt Enable Register - Data ready */
+#define AT91_SAMA5D2_IER_DRDY BIT(24)
+/* Interrupt Enable Register - general overrun error */
+#define AT91_SAMA5D2_IER_GOVRE BIT(25)
+/* Interrupt Enable Register - Pen detect */
+#define AT91_SAMA5D2_IER_PEN BIT(29)
+/* Interrupt Enable Register - No pen detect */
+#define AT91_SAMA5D2_IER_NOPEN BIT(30)
+
+/* Interrupt Disable Register */
+ u16 IDR;
+/* Interrupt Mask Register */
+ u16 IMR;
+/* Interrupt Status Register */
+ u16 ISR;
+/* End of Conversion Interrupt Enable Register */
+ u16 EOC_IER;
+/* End of Conversion Interrupt Disable Register */
+ u16 EOC_IDR;
+/* End of Conversion Interrupt Mask Register */
+ u16 EOC_IMR;
+/* End of Conversion Interrupt Status Register */
+ u16 EOC_ISR;
+/* Interrupt Status Register - Pen touching sense status */
+#define AT91_SAMA5D2_ISR_PENS BIT(31)
+/* Last Channel Trigger Mode Register */
+ u16 LCTMR;
+/* Last Channel Compare Window Register */
+ u16 LCCWR;
+/* Overrun Status Register */
+ u16 OVER;
+/* Extended Mode Register */
+ u16 EMR;
+/* Extended Mode Register - Oversampling rate */
+#define AT91_SAMA5D2_EMR_OSR(V, M) (((V) << 16) & (M))
+#define AT91_SAMA5D2_EMR_OSR_1SAMPLES 0
+#define AT91_SAMA5D2_EMR_OSR_4SAMPLES 1
+#define AT91_SAMA5D2_EMR_OSR_16SAMPLES 2
+#define AT91_SAMA5D2_EMR_OSR_64SAMPLES 3
+#define AT91_SAMA5D2_EMR_OSR_256SAMPLES 4
+
+/* Extended Mode Register - TRACKX */
+#define AT91_SAMA5D2_TRACKX_MASK GENMASK(23, 22)
+#define AT91_SAMA5D2_TRACKX(x) (((x) << 22) & \
+ AT91_SAMA5D2_TRACKX_MASK)
+/* TRACKX for temperature sensor. */
+#define AT91_SAMA5D2_TRACKX_TS (1)
+
+/* Extended Mode Register - Averaging on single trigger event */
+#define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20)
+
+/* Compare Window Register */
+ u16 CWR;
+/* Channel Gain Register */
+ u16 CGR;
+/* Channel Offset Register */
+ u16 COR;
+/* Channel Offset Register differential offset - constant, not a register */
+ u16 COR_diff_offset;
+/* Analog Control Register */
+ u16 ACR;
+/* Analog Control Register - Pen detect sensitivity mask */
+#define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0)
+/* Analog Control Register - Source last channel */
+#define AT91_SAMA5D2_ACR_SRCLCH BIT(16)
+
+/* Touchscreen Mode Register */
+ u16 TSMR;
+/* Touchscreen Mode Register - No touch mode */
+#define AT91_SAMA5D2_TSMR_TSMODE_NONE 0
+/* Touchscreen Mode Register - 4 wire screen, no pressure measurement */
+#define AT91_SAMA5D2_TSMR_TSMODE_4WIRE_NO_PRESS 1
+/* Touchscreen Mode Register - 4 wire screen, pressure measurement */
+#define AT91_SAMA5D2_TSMR_TSMODE_4WIRE_PRESS 2
+/* Touchscreen Mode Register - 5 wire screen */
+#define AT91_SAMA5D2_TSMR_TSMODE_5WIRE 3
+/* Touchscreen Mode Register - Average samples mask */
+#define AT91_SAMA5D2_TSMR_TSAV_MASK GENMASK(5, 4)
+/* Touchscreen Mode Register - Average samples */
+#define AT91_SAMA5D2_TSMR_TSAV(x) ((x) << 4)
+/* Touchscreen Mode Register - Touch/trigger frequency ratio mask */
+#define AT91_SAMA5D2_TSMR_TSFREQ_MASK GENMASK(11, 8)
+/* Touchscreen Mode Register - Touch/trigger frequency ratio */
+#define AT91_SAMA5D2_TSMR_TSFREQ(x) ((x) << 8)
+/* Touchscreen Mode Register - Pen Debounce Time mask */
+#define AT91_SAMA5D2_TSMR_PENDBC_MASK GENMASK(31, 28)
+/* Touchscreen Mode Register - Pen Debounce Time */
+#define AT91_SAMA5D2_TSMR_PENDBC(x) ((x) << 28)
+/* Touchscreen Mode Register - No DMA for touch measurements */
+#define AT91_SAMA5D2_TSMR_NOTSDMA BIT(22)
+/* Touchscreen Mode Register - Disable pen detection */
+#define AT91_SAMA5D2_TSMR_PENDET_DIS (0 << 24)
+/* Touchscreen Mode Register - Enable pen detection */
+#define AT91_SAMA5D2_TSMR_PENDET_ENA BIT(24)
+
+/* Touchscreen X Position Register */
+ u16 XPOSR;
+/* Touchscreen Y Position Register */
+ u16 YPOSR;
+/* Touchscreen Pressure Register */
+ u16 PRESSR;
+/* Trigger Register */
+ u16 TRGR;
+/* Mask for TRGMOD field of TRGR register */
+#define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0)
+/* No trigger, only software trigger can start conversions */
+#define AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER 0
+/* Trigger Mode external trigger rising edge */
+#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE 1
+/* Trigger Mode external trigger falling edge */
+#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL 2
+/* Trigger Mode external trigger any edge */
+#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY 3
+/* Trigger Mode internal periodic */
+#define AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC 5
+/* Trigger Mode - trigger period mask */
+#define AT91_SAMA5D2_TRGR_TRGPER_MASK GENMASK(31, 16)
+/* Trigger Mode - trigger period */
+#define AT91_SAMA5D2_TRGR_TRGPER(x) ((x) << 16)
+
+/* Correction Select Register */
+ u16 COSR;
+/* Correction Value Register */
+ u16 CVR;
+/* Channel Error Correction Register */
+ u16 CECR;
+/* Write Protection Mode Register */
+ u16 WPMR;
+/* Write Protection Status Register */
+ u16 WPSR;
+/* Version Register */
+ u16 VERSION;
+/* Temperature Sensor Mode Register */
+ u16 TEMPMR;
+/* Temperature Sensor Mode - Temperature sensor on */
+#define AT91_SAMA5D2_TEMPMR_TEMPON BIT(0)
+};
+
+static const struct at91_adc_reg_layout sama5d2_layout = {
+ .CR = 0x00,
+ .MR = 0x04,
+ .SEQR1 = 0x08,
+ .SEQR2 = 0x0c,
+ .CHER = 0x10,
+ .CHDR = 0x14,
+ .CHSR = 0x18,
+ .LCDR = 0x20,
+ .IER = 0x24,
+ .IDR = 0x28,
+ .IMR = 0x2c,
+ .ISR = 0x30,
+ .LCTMR = 0x34,
+ .LCCWR = 0x38,
+ .OVER = 0x3c,
+ .EMR = 0x40,
+ .CWR = 0x44,
+ .CGR = 0x48,
+ .COR = 0x4c,
+ .COR_diff_offset = 16,
+ .ACR = 0x94,
+ .TSMR = 0xb0,
+ .XPOSR = 0xb4,
+ .YPOSR = 0xb8,
+ .PRESSR = 0xbc,
+ .TRGR = 0xc0,
+ .COSR = 0xd0,
+ .CVR = 0xd4,
+ .CECR = 0xd8,
+ .WPMR = 0xe4,
+ .WPSR = 0xe8,
+ .VERSION = 0xfc,
+};
+
+static const struct at91_adc_reg_layout sama7g5_layout = {
+ .CR = 0x00,
+ .MR = 0x04,
+ .SEQR1 = 0x08,
+ .SEQR2 = 0x0c,
+ .CHER = 0x10,
+ .CHDR = 0x14,
+ .CHSR = 0x18,
+ .LCDR = 0x20,
+ .IER = 0x24,
+ .IDR = 0x28,
+ .IMR = 0x2c,
+ .ISR = 0x30,
+ .EOC_IER = 0x34,
+ .EOC_IDR = 0x38,
+ .EOC_IMR = 0x3c,
+ .EOC_ISR = 0x40,
+ .TEMPMR = 0x44,
+ .OVER = 0x4c,
+ .EMR = 0x50,
+ .CWR = 0x54,
+ .COR = 0x5c,
+ .COR_diff_offset = 0,
+ .ACR = 0xe0,
+ .TRGR = 0x100,
+ .COSR = 0x104,
+ .CVR = 0x108,
+ .CECR = 0x10c,
+ .WPMR = 0x118,
+ .WPSR = 0x11c,
+ .VERSION = 0x130,
+};
+
+#define AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */
+#define AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US 200
+
+#define AT91_SAMA5D2_XYZ_MASK GENMASK(11, 0)
+
+#define AT91_SAMA5D2_MAX_POS_BITS 12
+
+#define AT91_HWFIFO_MAX_SIZE_STR "128"
+#define AT91_HWFIFO_MAX_SIZE 128
+
+#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .channel = num, \
+ .address = addr, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 14, \
+ .storagebits = 16, \
+ }, \
+ .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)|\
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = "CH"#num, \
+ .indexed = 1, \
+ }
+
+#define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .channel = num, \
+ .channel2 = num2, \
+ .address = addr, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 14, \
+ .storagebits = 16, \
+ }, \
+ .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)|\
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = "CH"#num"-CH"#num2, \
+ .indexed = 1, \
+ }
+
+#define AT91_SAMA5D2_CHAN_TOUCH(num, name, mod) \
+ { \
+ .type = IIO_POSITIONRELATIVE, \
+ .modified = 1, \
+ .channel = num, \
+ .channel2 = mod, \
+ .scan_index = num, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .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), \
+ .datasheet_name = name, \
+ }
+#define AT91_SAMA5D2_CHAN_PRESSURE(num, name) \
+ { \
+ .type = IIO_PRESSURE, \
+ .channel = num, \
+ .scan_index = num, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .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), \
+ .datasheet_name = name, \
+ }
+
+#define AT91_SAMA5D2_CHAN_TEMP(num, name, addr) \
+ { \
+ .type = IIO_TEMP, \
+ .channel = num, \
+ .address = addr, \
+ .scan_index = num, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = name, \
+ }
+
+#define at91_adc_readl(st, reg) \
+ readl_relaxed((st)->base + (st)->soc_info.platform->layout->reg)
+#define at91_adc_read_chan(st, reg) \
+ readl_relaxed((st)->base + reg)
+#define at91_adc_writel(st, reg, val) \
+ writel_relaxed(val, (st)->base + (st)->soc_info.platform->layout->reg)
+
+/**
+ * struct at91_adc_platform - at91-sama5d2 platform information struct
+ * @layout: pointer to the reg layout struct
+ * @adc_channels: pointer to an array of channels for registering in
+ * the iio subsystem
+ * @nr_channels: number of physical channels available
+ * @touch_chan_x: index of the touchscreen X channel
+ * @touch_chan_y: index of the touchscreen Y channel
+ * @touch_chan_p: index of the touchscreen P channel
+ * @max_channels: number of total channels
+ * @max_index: highest channel index (highest index may be higher
+ * than the total channel number)
+ * @hw_trig_cnt: number of possible hardware triggers
+ * @osr_mask: oversampling ratio bitmask on EMR register
+ * @oversampling_avail: available oversampling values
+ * @oversampling_avail_no: number of available oversampling values
+ * @chan_realbits: realbits for registered channels
+ * @temp_chan: temperature channel index
+ * @temp_sensor: temperature sensor supported
+ */
+struct at91_adc_platform {
+ const struct at91_adc_reg_layout *layout;
+ const struct iio_chan_spec (*adc_channels)[];
+ unsigned int nr_channels;
+ unsigned int touch_chan_x;
+ unsigned int touch_chan_y;
+ unsigned int touch_chan_p;
+ unsigned int max_channels;
+ unsigned int max_index;
+ unsigned int hw_trig_cnt;
+ unsigned int osr_mask;
+ unsigned int oversampling_avail[5];
+ unsigned int oversampling_avail_no;
+ unsigned int chan_realbits;
+ unsigned int temp_chan;
+ bool temp_sensor;
+};
+
+/**
+ * struct at91_adc_temp_sensor_clb - at91-sama5d2 temperature sensor
+ * calibration data structure
+ * @p1: P1 calibration temperature
+ * @p4: P4 calibration voltage
+ * @p6: P6 calibration voltage
+ */
+struct at91_adc_temp_sensor_clb {
+ u32 p1;
+ u32 p4;
+ u32 p6;
+};
+
+/**
+ * enum at91_adc_ts_clb_idx - calibration indexes in NVMEM buffer
+ * @AT91_ADC_TS_CLB_IDX_P1: index for P1
+ * @AT91_ADC_TS_CLB_IDX_P4: index for P4
+ * @AT91_ADC_TS_CLB_IDX_P6: index for P6
+ * @AT91_ADC_TS_CLB_IDX_MAX: max index for temperature calibration packet in OTP
+ */
+enum at91_adc_ts_clb_idx {
+ AT91_ADC_TS_CLB_IDX_P1 = 2,
+ AT91_ADC_TS_CLB_IDX_P4 = 5,
+ AT91_ADC_TS_CLB_IDX_P6 = 7,
+ AT91_ADC_TS_CLB_IDX_MAX = 19,
+};
+
+/* Temperature sensor calibration - Vtemp voltage sensitivity to temperature. */
+#define AT91_ADC_TS_VTEMP_DT (2080U)
+
+/**
+ * struct at91_adc_soc_info - at91-sama5d2 soc information struct
+ * @startup_time: device startup time
+ * @min_sample_rate: minimum sample rate in Hz
+ * @max_sample_rate: maximum sample rate in Hz
+ * @platform: pointer to the platform structure
+ * @temp_sensor_clb: temperature sensor calibration data structure
+ */
+struct at91_adc_soc_info {
+ unsigned startup_time;
+ unsigned min_sample_rate;
+ unsigned max_sample_rate;
+ const struct at91_adc_platform *platform;
+ struct at91_adc_temp_sensor_clb temp_sensor_clb;
+};
+
+struct at91_adc_trigger {
+ char *name;
+ unsigned int trgmod_value;
+ unsigned int edge_type;
+ bool hw_trig;
+};
+
+/**
+ * struct at91_adc_dma - at91-sama5d2 dma information struct
+ * @dma_chan: the dma channel acquired
+ * @rx_buf: dma coherent allocated area
+ * @rx_dma_buf: dma handler for the buffer
+ * @phys_addr: physical address of the ADC base register
+ * @buf_idx: index inside the dma buffer where reading was last done
+ * @rx_buf_sz: size of buffer used by DMA operation
+ * @watermark: number of conversions to copy before DMA triggers irq
+ * @dma_ts: hold the start timestamp of dma operation
+ */
+struct at91_adc_dma {
+ struct dma_chan *dma_chan;
+ u8 *rx_buf;
+ dma_addr_t rx_dma_buf;
+ phys_addr_t phys_addr;
+ int buf_idx;
+ int rx_buf_sz;
+ int watermark;
+ s64 dma_ts;
+};
+
+/**
+ * struct at91_adc_touch - at91-sama5d2 touchscreen information struct
+ * @sample_period_val: the value for periodic trigger interval
+ * @touching: is the pen touching the screen or not
+ * @x_pos: temporary placeholder for pressure computation
+ * @channels_bitmask: bitmask with the touchscreen channels enabled
+ * @workq: workqueue for buffer data pushing
+ */
+struct at91_adc_touch {
+ u16 sample_period_val;
+ bool touching;
+ u16 x_pos;
+ unsigned long channels_bitmask;
+ struct work_struct workq;
+};
+
+/**
+ * struct at91_adc_temp - at91-sama5d2 temperature information structure
+ * @sample_period_val: sample period value
+ * @saved_sample_rate: saved sample rate
+ * @saved_oversampling: saved oversampling
+ */
+struct at91_adc_temp {
+ u16 sample_period_val;
+ u16 saved_sample_rate;
+ 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;
+ struct clk *per_clk;
+ struct regulator *reg;
+ struct regulator *vref;
+ int vref_uv;
+ unsigned int current_sample_rate;
+ struct iio_trigger *trig;
+ const struct at91_adc_trigger *selected_trig;
+ const struct iio_chan_spec *chan;
+ bool conversion_done;
+ u32 conversion_value;
+ unsigned int oversampling_ratio;
+ struct at91_adc_soc_info soc_info;
+ wait_queue_head_t wq_data_available;
+ struct at91_adc_dma dma_st;
+ struct at91_adc_touch touch_st;
+ 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);
+ /*
+ * lock to prevent concurrent 'single conversion' requests through
+ * sysfs.
+ */
+ struct mutex lock;
+};
+
+static const struct at91_adc_trigger at91_adc_trigger_list[] = {
+ {
+ .name = "external_rising",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE,
+ .edge_type = IRQ_TYPE_EDGE_RISING,
+ .hw_trig = true,
+ },
+ {
+ .name = "external_falling",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL,
+ .edge_type = IRQ_TYPE_EDGE_FALLING,
+ .hw_trig = true,
+ },
+ {
+ .name = "external_any",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY,
+ .edge_type = IRQ_TYPE_EDGE_BOTH,
+ .hw_trig = true,
+ },
+ {
+ .name = "software",
+ .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER,
+ .edge_type = IRQ_TYPE_NONE,
+ .hw_trig = false,
+ },
+};
+
+static const struct iio_chan_spec at91_sama5d2_adc_channels[] = {
+ AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x50),
+ AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x54),
+ AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x58),
+ AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x5c),
+ AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x60),
+ AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x64),
+ AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x68),
+ AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x6c),
+ AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x70),
+ AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x74),
+ AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x78),
+ AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x7c),
+ /* original ABI has the differential channels with a gap in between */
+ AT91_SAMA5D2_CHAN_DIFF(12, 0, 1, 0x50),
+ AT91_SAMA5D2_CHAN_DIFF(14, 2, 3, 0x58),
+ AT91_SAMA5D2_CHAN_DIFF(16, 4, 5, 0x60),
+ AT91_SAMA5D2_CHAN_DIFF(18, 6, 7, 0x68),
+ AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x70),
+ AT91_SAMA5D2_CHAN_DIFF(22, 10, 11, 0x78),
+ IIO_CHAN_SOFT_TIMESTAMP(23),
+ AT91_SAMA5D2_CHAN_TOUCH(24, "x", IIO_MOD_X),
+ AT91_SAMA5D2_CHAN_TOUCH(25, "y", IIO_MOD_Y),
+ AT91_SAMA5D2_CHAN_PRESSURE(26, "pressure"),
+};
+
+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),
+ IIO_CHAN_SOFT_TIMESTAMP(24),
+ AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc),
+};
+
+static const struct at91_adc_platform sama5d2_platform = {
+ .layout = &sama5d2_layout,
+ .adc_channels = &at91_sama5d2_adc_channels,
+#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12
+#define AT91_SAMA5D2_DIFF_CHAN_CNT 6
+ .nr_channels = AT91_SAMA5D2_SINGLE_CHAN_CNT +
+ AT91_SAMA5D2_DIFF_CHAN_CNT,
+#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \
+ AT91_SAMA5D2_DIFF_CHAN_CNT * 2)
+ .touch_chan_x = AT91_SAMA5D2_TOUCH_X_CHAN_IDX,
+#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1)
+ .touch_chan_y = AT91_SAMA5D2_TOUCH_Y_CHAN_IDX,
+#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1)
+ .touch_chan_p = AT91_SAMA5D2_TOUCH_P_CHAN_IDX,
+#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX
+ .max_channels = ARRAY_SIZE(at91_sama5d2_adc_channels),
+ .max_index = AT91_SAMA5D2_MAX_CHAN_IDX,
+#define AT91_SAMA5D2_HW_TRIG_CNT 3
+ .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT,
+ .osr_mask = GENMASK(17, 16),
+ .oversampling_avail = { 1, 4, 16, },
+ .oversampling_avail_no = 3,
+ .chan_realbits = 14,
+};
+
+static const struct at91_adc_platform sama7g5_platform = {
+ .layout = &sama7g5_layout,
+ .adc_channels = &at91_sama7g5_adc_channels,
+#define AT91_SAMA7G5_SINGLE_CHAN_CNT 16
+#define AT91_SAMA7G5_DIFF_CHAN_CNT 8
+#define AT91_SAMA7G5_TEMP_CHAN_CNT 1
+ .nr_channels = AT91_SAMA7G5_SINGLE_CHAN_CNT +
+ AT91_SAMA7G5_DIFF_CHAN_CNT +
+ AT91_SAMA7G5_TEMP_CHAN_CNT,
+#define AT91_SAMA7G5_MAX_CHAN_IDX (AT91_SAMA7G5_SINGLE_CHAN_CNT + \
+ AT91_SAMA7G5_DIFF_CHAN_CNT + \
+ AT91_SAMA7G5_TEMP_CHAN_CNT)
+ .max_channels = ARRAY_SIZE(at91_sama7g5_adc_channels),
+ .max_index = AT91_SAMA7G5_MAX_CHAN_IDX,
+#define AT91_SAMA7G5_HW_TRIG_CNT 3
+ .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT,
+ .osr_mask = GENMASK(18, 16),
+ .oversampling_avail = { 1, 4, 16, 64, 256, },
+ .oversampling_avail_no = 5,
+ .chan_realbits = 16,
+ .temp_sensor = true,
+ .temp_chan = AT91_SAMA7G5_ADC_TEMP_CHANNEL,
+};
+
+static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ if (indio_dev->channels[i].scan_index == chan)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static inline struct iio_chan_spec const *
+at91_adc_chan_get(struct iio_dev *indio_dev, int chan)
+{
+ int index = at91_adc_chan_xlate(indio_dev, chan);
+
+ if (index < 0)
+ return NULL;
+ return indio_dev->channels + index;
+}
+
+static inline int at91_adc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ return at91_adc_chan_xlate(indio_dev, iiospec->args[0]);
+}
+
+static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev)
+{
+ u32 mask = 0;
+ u8 bit;
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->num_channels) {
+ struct iio_chan_spec const *chan =
+ at91_adc_chan_get(indio_dev, bit);
+ mask |= BIT(chan->channel);
+ }
+
+ return mask & GENMASK(st->soc_info.platform->nr_channels, 0);
+}
+
+static void at91_adc_cor(struct at91_adc_state *st,
+ struct iio_chan_spec const *chan)
+{
+ u32 cor, cur_cor;
+
+ cor = BIT(chan->channel) | BIT(chan->channel2);
+
+ cur_cor = at91_adc_readl(st, COR);
+ cor <<= st->soc_info.platform->layout->COR_diff_offset;
+ if (chan->differential)
+ at91_adc_writel(st, COR, cur_cor | cor);
+ else
+ at91_adc_writel(st, COR, cur_cor & ~cor);
+}
+
+static void at91_adc_irq_status(struct at91_adc_state *st, u32 *status,
+ u32 *eoc)
+{
+ *status = at91_adc_readl(st, ISR);
+ if (st->soc_info.platform->layout->EOC_ISR)
+ *eoc = at91_adc_readl(st, EOC_ISR);
+ else
+ *eoc = *status;
+}
+
+static void at91_adc_irq_mask(struct at91_adc_state *st, u32 *status, u32 *eoc)
+{
+ *status = at91_adc_readl(st, IMR);
+ if (st->soc_info.platform->layout->EOC_IMR)
+ *eoc = at91_adc_readl(st, EOC_IMR);
+ else
+ *eoc = *status;
+}
+
+static void at91_adc_eoc_dis(struct at91_adc_state *st, unsigned int channel)
+{
+ /*
+ * On some products having the EOC bits in a separate register,
+ * errata recommends not writing this register (EOC_IDR).
+ * On products having the EOC bits in the IDR register, it's fine to write it.
+ */
+ if (!st->soc_info.platform->layout->EOC_IDR)
+ at91_adc_writel(st, IDR, BIT(channel));
+}
+
+static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel)
+{
+ if (!st->soc_info.platform->layout->EOC_IDR)
+ at91_adc_writel(st, IER, BIT(channel));
+ else
+ at91_adc_writel(st, EOC_IER, BIT(channel));
+}
+
+static int at91_adc_config_emr(struct at91_adc_state *st,
+ u32 oversampling_ratio, u32 trackx)
+{
+ /* configure the extended mode register */
+ unsigned int emr, osr;
+ unsigned int osr_mask = st->soc_info.platform->osr_mask;
+ int i, ret;
+
+ /* Check against supported oversampling values. */
+ for (i = 0; i < st->soc_info.platform->oversampling_avail_no; i++) {
+ if (oversampling_ratio == st->soc_info.platform->oversampling_avail[i])
+ break;
+ }
+ if (i == st->soc_info.platform->oversampling_avail_no)
+ return -EINVAL;
+
+ /* select oversampling ratio from configuration */
+ switch (oversampling_ratio) {
+ case 1:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES,
+ osr_mask);
+ break;
+ case 4:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES,
+ osr_mask);
+ break;
+ case 16:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES,
+ osr_mask);
+ break;
+ case 64:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_64SAMPLES,
+ osr_mask);
+ break;
+ case 256:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_256SAMPLES,
+ osr_mask);
+ break;
+ }
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ emr = at91_adc_readl(st, EMR);
+ /* select oversampling per single trigger event */
+ emr |= AT91_SAMA5D2_EMR_ASTE(1);
+ /* delete leftover content if it's the case */
+ emr &= ~(osr_mask | AT91_SAMA5D2_TRACKX_MASK);
+ /* Update osr and trackx. */
+ emr |= osr | AT91_SAMA5D2_TRACKX(trackx);
+ at91_adc_writel(st, EMR, emr);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ st->oversampling_ratio = oversampling_ratio;
+
+ return 0;
+}
+
+static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val)
+{
+ int nbits, diff;
+
+ if (st->oversampling_ratio == 1)
+ nbits = 12;
+ else if (st->oversampling_ratio == 4)
+ nbits = 13;
+ else if (st->oversampling_ratio == 16)
+ nbits = 14;
+ else if (st->oversampling_ratio == 64)
+ nbits = 15;
+ else if (st->oversampling_ratio == 256)
+ nbits = 16;
+ else
+ /* Should not happen. */
+ return -EINVAL;
+
+ /*
+ * We have nbits of real data and channel is registered as
+ * st->soc_info.platform->chan_realbits, so shift left diff bits.
+ */
+ diff = st->soc_info.platform->chan_realbits - nbits;
+ *val <<= diff;
+
+ return IIO_VAL_INT;
+}
+
+static void at91_adc_adjust_val_osr_array(struct at91_adc_state *st, void *buf,
+ int len)
+{
+ int i = 0, val;
+ u16 *buf_u16 = (u16 *) buf;
+
+ /*
+ * We are converting each two bytes (each sample).
+ * First convert the byte based array to u16, and convert each sample
+ * separately.
+ * Each value is two bytes in an array of chars, so to not shift
+ * more than we need, save the value separately.
+ * len is in bytes, so divide by two to get number of samples.
+ */
+ while (i < len / 2) {
+ val = buf_u16[i];
+ at91_adc_adjust_val_osr(st, &val);
+ buf_u16[i] = val;
+ i++;
+ }
+}
+
+static int at91_adc_configure_touch(struct at91_adc_state *st, bool state)
+{
+ u32 clk_khz = st->current_sample_rate / 1000;
+ int i = 0, ret;
+ u16 pendbc;
+ u32 tsmr, acr;
+
+ if (state) {
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* disabling touch IRQs and setting mode to no touch enabled */
+ at91_adc_writel(st, IDR,
+ AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN);
+ at91_adc_writel(st, TSMR, 0);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ return 0;
+ }
+ /*
+ * debounce time is in microseconds, we need it in milliseconds to
+ * multiply with kilohertz, so, divide by 1000, but after the multiply.
+ * round up to make sure pendbc is at least 1
+ */
+ pendbc = round_up(AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US *
+ clk_khz / 1000, 1);
+
+ /* get the required exponent */
+ while (pendbc >> i++)
+ ;
+
+ pendbc = i;
+
+ tsmr = AT91_SAMA5D2_TSMR_TSMODE_4WIRE_PRESS;
+
+ tsmr |= AT91_SAMA5D2_TSMR_TSAV(2) & AT91_SAMA5D2_TSMR_TSAV_MASK;
+ tsmr |= AT91_SAMA5D2_TSMR_PENDBC(pendbc) &
+ AT91_SAMA5D2_TSMR_PENDBC_MASK;
+ tsmr |= AT91_SAMA5D2_TSMR_NOTSDMA;
+ tsmr |= AT91_SAMA5D2_TSMR_PENDET_ENA;
+ tsmr |= AT91_SAMA5D2_TSMR_TSFREQ(2) & AT91_SAMA5D2_TSMR_TSFREQ_MASK;
+
+ at91_adc_writel(st, TSMR, tsmr);
+
+ acr = at91_adc_readl(st, ACR);
+ acr &= ~AT91_SAMA5D2_ACR_PENDETSENS_MASK;
+ acr |= 0x02 & AT91_SAMA5D2_ACR_PENDETSENS_MASK;
+ at91_adc_writel(st, ACR, acr);
+
+ /* Sample Period Time = (TRGPER + 1) / ADCClock */
+ st->touch_st.sample_period_val =
+ round_up((AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US *
+ clk_khz / 1000) - 1, 1);
+ /* enable pen detect IRQ */
+ at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN);
+
+ return 0;
+}
+
+static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg)
+{
+ u32 val = 0;
+ u32 scale, result, pos;
+
+ /*
+ * to obtain the actual position we must divide by scale
+ * and multiply with max, where
+ * max = 2^AT91_SAMA5D2_MAX_POS_BITS - 1
+ */
+ /* first half of register is the x or y, second half is the scale */
+ if (reg == st->soc_info.platform->layout->XPOSR)
+ val = at91_adc_readl(st, XPOSR);
+ else if (reg == st->soc_info.platform->layout->YPOSR)
+ val = at91_adc_readl(st, YPOSR);
+
+ if (!val)
+ dev_dbg(&st->indio_dev->dev, "pos is 0\n");
+
+ pos = val & AT91_SAMA5D2_XYZ_MASK;
+ result = (pos << AT91_SAMA5D2_MAX_POS_BITS) - pos;
+ scale = (val >> 16) & AT91_SAMA5D2_XYZ_MASK;
+ if (scale == 0) {
+ dev_err(&st->indio_dev->dev, "scale is 0\n");
+ return 0;
+ }
+ result /= scale;
+
+ return result;
+}
+
+static u16 at91_adc_touch_x_pos(struct at91_adc_state *st)
+{
+ st->touch_st.x_pos = at91_adc_touch_pos(st, st->soc_info.platform->layout->XPOSR);
+ return st->touch_st.x_pos;
+}
+
+static u16 at91_adc_touch_y_pos(struct at91_adc_state *st)
+{
+ return at91_adc_touch_pos(st, st->soc_info.platform->layout->YPOSR);
+}
+
+static u16 at91_adc_touch_pressure(struct at91_adc_state *st)
+{
+ u32 val;
+ u32 z1, z2;
+ u32 pres;
+ u32 rxp = 1;
+ u32 factor = 1000;
+
+ /* calculate the pressure */
+ val = at91_adc_readl(st, PRESSR);
+ z1 = val & AT91_SAMA5D2_XYZ_MASK;
+ z2 = (val >> 16) & AT91_SAMA5D2_XYZ_MASK;
+
+ if (z1 != 0)
+ pres = rxp * (st->touch_st.x_pos * factor / 1024) *
+ (z2 * factor / z1 - factor) /
+ factor;
+ else
+ pres = 0xFFFF; /* no pen contact */
+
+ /*
+ * The pressure from device grows down, minimum is 0xFFFF, maximum 0x0.
+ * We compute it this way, but let's return it in the expected way,
+ * growing from 0 to 0xFFFF.
+ */
+ return 0xFFFF - pres;
+}
+
+static int at91_adc_read_position(struct at91_adc_state *st, int chan, u16 *val)
+{
+ *val = 0;
+ if (!st->touch_st.touching)
+ return -ENODATA;
+ if (chan == st->soc_info.platform->touch_chan_x)
+ *val = at91_adc_touch_x_pos(st);
+ else if (chan == st->soc_info.platform->touch_chan_y)
+ *val = at91_adc_touch_y_pos(st);
+ else
+ return -ENODATA;
+
+ return IIO_VAL_INT;
+}
+
+static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val)
+{
+ *val = 0;
+ if (!st->touch_st.touching)
+ return -ENODATA;
+ if (chan == st->soc_info.platform->touch_chan_p)
+ *val = at91_adc_touch_pressure(st);
+ else
+ return -ENODATA;
+
+ return IIO_VAL_INT;
+}
+
+static void at91_adc_configure_trigger_registers(struct at91_adc_state *st,
+ bool state)
+{
+ u32 status = at91_adc_readl(st, TRGR);
+
+ /* clear TRGMOD */
+ status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK;
+
+ if (state)
+ status |= st->selected_trig->trgmod_value;
+
+ /* set/unset hw trigger */
+ at91_adc_writel(st, TRGR, status);
+}
+
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+ struct at91_adc_state *st = iio_priv(indio);
+ int ret;
+
+ if (state) {
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ at91_adc_configure_trigger_registers(st, state);
+
+ if (!state) {
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ }
+
+ return 0;
+}
+
+static void at91_adc_reenable_trigger(struct iio_trigger *trig)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+ struct at91_adc_state *st = iio_priv(indio);
+
+ /* if we are using DMA, we must not reenable irq after each trigger */
+ if (st->dma_st.dma_chan)
+ return;
+
+ enable_irq(st->irq);
+
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, LCDR);
+}
+
+static const struct iio_trigger_ops at91_adc_trigger_ops = {
+ .set_trigger_state = &at91_adc_configure_trigger,
+ .reenable = &at91_adc_reenable_trigger,
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int at91_adc_dma_size_done(struct at91_adc_state *st)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+ int i, size;
+
+ status = dmaengine_tx_status(st->dma_st.dma_chan,
+ st->dma_st.dma_chan->cookie,
+ &state);
+ if (status != DMA_IN_PROGRESS)
+ return 0;
+
+ /* Transferred length is size in bytes from end of buffer */
+ i = st->dma_st.rx_buf_sz - state.residue;
+
+ /* Return available bytes */
+ if (i >= st->dma_st.buf_idx)
+ size = i - st->dma_st.buf_idx;
+ else
+ size = st->dma_st.rx_buf_sz + i - st->dma_st.buf_idx;
+ return size;
+}
+
+static void at91_dma_buffer_done(void *data)
+{
+ struct iio_dev *indio_dev = data;
+
+ iio_trigger_poll_chained(indio_dev->trig);
+}
+
+static int at91_adc_dma_start(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+ int ret;
+ u8 bit;
+
+ if (!st->dma_st.dma_chan)
+ return 0;
+
+ /* we start a new DMA, so set buffer index to start */
+ st->dma_st.buf_idx = 0;
+
+ /*
+ * compute buffer size w.r.t. watermark and enabled channels.
+ * scan_bytes is aligned so we need an exact size for DMA
+ */
+ st->dma_st.rx_buf_sz = 0;
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->num_channels) {
+ struct iio_chan_spec const *chan =
+ at91_adc_chan_get(indio_dev, bit);
+
+ if (!chan)
+ continue;
+
+ st->dma_st.rx_buf_sz += chan->scan_type.storagebits / 8;
+ }
+ st->dma_st.rx_buf_sz *= st->dma_st.watermark;
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(st->dma_st.dma_chan,
+ st->dma_st.rx_dma_buf,
+ st->dma_st.rx_buf_sz,
+ st->dma_st.rx_buf_sz / 2,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+ if (!desc) {
+ dev_err(&indio_dev->dev, "cannot prepare DMA cyclic\n");
+ return -EBUSY;
+ }
+
+ desc->callback = at91_dma_buffer_done;
+ desc->callback_param = indio_dev;
+
+ cookie = dmaengine_submit(desc);
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dev_err(&indio_dev->dev, "cannot submit DMA cyclic\n");
+ dmaengine_terminate_async(st->dma_st.dma_chan);
+ return ret;
+ }
+
+ /* enable general overrun error signaling */
+ at91_adc_writel(st, IER, AT91_SAMA5D2_IER_GOVRE);
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(st->dma_st.dma_chan);
+
+ /* consider current time as DMA start time for timestamps */
+ st->dma_st.dma_ts = iio_get_time_ns(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "DMA cyclic started\n");
+
+ return 0;
+}
+
+static bool at91_adc_buffer_check_use_irq(struct iio_dev *indio,
+ struct at91_adc_state *st)
+{
+ /* if using DMA, we do not use our own IRQ (we use DMA-controller) */
+ if (st->dma_st.dma_chan)
+ return false;
+ /* if the trigger is not ours, then it has its own IRQ */
+ if (iio_trigger_validate_own_device(indio->trig, indio))
+ return false;
+ return true;
+}
+
+static bool at91_adc_current_chan_is_touch(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ return !!bitmap_subset(indio_dev->active_scan_mask,
+ &st->touch_st.channels_bitmask,
+ st->soc_info.platform->max_index + 1);
+}
+
+static int at91_adc_buffer_prepare(struct iio_dev *indio_dev)
+{
+ int ret;
+ u8 bit;
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ /* check if we are enabling triggered buffer or the touchscreen */
+ if (at91_adc_current_chan_is_touch(indio_dev))
+ return at91_adc_configure_touch(st, true);
+
+ /* if we are not in triggered mode, we cannot enable the buffer. */
+ if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES))
+ return -EINVAL;
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ /* we continue with the triggered buffer */
+ ret = at91_adc_dma_start(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "buffer prepare failed\n");
+ goto pm_runtime_put;
+ }
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->num_channels) {
+ struct iio_chan_spec const *chan =
+ at91_adc_chan_get(indio_dev, bit);
+ if (!chan)
+ continue;
+ /* these channel types cannot be handled by this trigger */
+ if (chan->type == IIO_POSITIONRELATIVE ||
+ chan->type == IIO_PRESSURE ||
+ chan->type == IIO_TEMP)
+ continue;
+
+ at91_adc_cor(st, chan);
+
+ at91_adc_writel(st, CHER, BIT(chan->channel));
+ }
+
+ if (at91_adc_buffer_check_use_irq(indio_dev, st))
+ at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY);
+
+pm_runtime_put:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ return ret;
+}
+
+static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 bit;
+
+ /* check if we are disabling triggered buffer or the touchscreen */
+ if (at91_adc_current_chan_is_touch(indio_dev))
+ return at91_adc_configure_touch(st, false);
+
+ /* if we are not in triggered mode, nothing to do here */
+ if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES))
+ return -EINVAL;
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * For each enable channel we must disable it in hardware.
+ * In the case of DMA, we must read the last converted value
+ * to clear EOC status and not get a possible interrupt later.
+ * This value is being read by DMA from LCDR anyway, so it's not lost.
+ */
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->num_channels) {
+ struct iio_chan_spec const *chan =
+ at91_adc_chan_get(indio_dev, bit);
+
+ if (!chan)
+ continue;
+ /* these channel types are virtual, no need to do anything */
+ if (chan->type == IIO_POSITIONRELATIVE ||
+ chan->type == IIO_PRESSURE ||
+ chan->type == IIO_TEMP)
+ continue;
+
+ at91_adc_writel(st, CHDR, BIT(chan->channel));
+
+ if (st->dma_st.dma_chan)
+ at91_adc_read_chan(st, chan->address);
+ }
+
+ if (at91_adc_buffer_check_use_irq(indio_dev, st))
+ at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_DRDY);
+
+ /* read overflow register to clear possible overflow status */
+ at91_adc_readl(st, OVER);
+
+ /* if we are using DMA we must clear registers and end DMA */
+ if (st->dma_st.dma_chan)
+ dmaengine_terminate_sync(st->dma_st.dma_chan);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops at91_buffer_setup_ops = {
+ .postdisable = &at91_adc_buffer_postdisable,
+};
+
+static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
+ char *trigger_name)
+{
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name,
+ iio_device_id(indio), trigger_name);
+ if (!trig)
+ return ERR_PTR(-ENOMEM);
+
+ trig->dev.parent = indio->dev.parent;
+ iio_trigger_set_drvdata(trig, indio);
+ trig->ops = &at91_adc_trigger_ops;
+
+ ret = devm_iio_trigger_register(&indio->dev, trig);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return trig;
+}
+
+static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev,
+ struct iio_poll_func *pf)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int i = 0;
+ int val;
+ u8 bit;
+ u32 mask = at91_adc_active_scan_mask_to_reg(indio_dev);
+ unsigned int timeout = 50;
+ u32 status, imr, eoc = 0, eoc_imr;
+
+ /*
+ * Check if the conversion is ready. If not, wait a little bit, and
+ * in case of timeout exit with an error.
+ */
+ while (((eoc & mask) != mask) && timeout) {
+ at91_adc_irq_status(st, &status, &eoc);
+ at91_adc_irq_mask(st, &imr, &eoc_imr);
+ usleep_range(50, 100);
+ timeout--;
+ }
+
+ /* Cannot read data, not ready. Continue without reporting data */
+ if (!timeout)
+ return;
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->num_channels) {
+ struct iio_chan_spec const *chan =
+ at91_adc_chan_get(indio_dev, bit);
+
+ if (!chan)
+ continue;
+ /*
+ * Our external trigger only supports the voltage channels.
+ * In case someone requested a different type of channel
+ * just put zeroes to buffer.
+ * This should not happen because we check the scan mode
+ * and scan mask when we enable the buffer, and we don't allow
+ * the buffer to start with a mixed mask (voltage and something
+ * else).
+ * Thus, emit a warning.
+ */
+ if (chan->type == IIO_VOLTAGE) {
+ val = at91_adc_read_chan(st, chan->address);
+ at91_adc_adjust_val_osr(st, &val);
+ st->buffer[i] = val;
+ } else {
+ st->buffer[i] = 0;
+ WARN(true, "This trigger cannot handle this type of channel");
+ }
+ i++;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, st->buffer,
+ pf->timestamp);
+}
+
+static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int transferred_len = at91_adc_dma_size_done(st);
+ s64 ns = iio_get_time_ns(indio_dev);
+ s64 interval;
+ int sample_index = 0, sample_count, sample_size;
+
+ u32 status = at91_adc_readl(st, ISR);
+ /* if we reached this point, we cannot sample faster */
+ if (status & AT91_SAMA5D2_IER_GOVRE)
+ pr_info_ratelimited("%s: conversion overrun detected\n",
+ indio_dev->name);
+
+ sample_size = div_s64(st->dma_st.rx_buf_sz, st->dma_st.watermark);
+
+ sample_count = div_s64(transferred_len, sample_size);
+
+ /*
+ * interval between samples is total time since last transfer handling
+ * divided by the number of samples (total size divided by sample size)
+ */
+ interval = div_s64((ns - st->dma_st.dma_ts), sample_count);
+
+ while (transferred_len >= sample_size) {
+ /*
+ * for all the values in the current sample,
+ * adjust the values inside the buffer for oversampling
+ */
+ at91_adc_adjust_val_osr_array(st,
+ &st->dma_st.rx_buf[st->dma_st.buf_idx],
+ sample_size);
+
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ (st->dma_st.rx_buf + st->dma_st.buf_idx),
+ (st->dma_st.dma_ts + interval * sample_index));
+ /* adjust remaining length */
+ transferred_len -= sample_size;
+ /* adjust buffer index */
+ st->dma_st.buf_idx += sample_size;
+ /* in case of reaching end of buffer, reset index */
+ if (st->dma_st.buf_idx >= st->dma_st.rx_buf_sz)
+ st->dma_st.buf_idx = 0;
+ sample_index++;
+ }
+ /* adjust saved time for next transfer handling */
+ st->dma_st.dma_ts = iio_get_time_ns(indio_dev);
+}
+
+static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ /*
+ * If it's not our trigger, start a conversion now, as we are
+ * actually polling the trigger now.
+ */
+ if (iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
+ at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START);
+
+ if (st->dma_st.dma_chan)
+ at91_adc_trigger_handler_dma(indio_dev);
+ else
+ at91_adc_trigger_handler_nodma(indio_dev, pf);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned at91_adc_startup_time(unsigned startup_time_min,
+ unsigned adc_clk_khz)
+{
+ static const unsigned int startup_lookup[] = {
+ 0, 8, 16, 24,
+ 64, 80, 96, 112,
+ 512, 576, 640, 704,
+ 768, 832, 896, 960
+ };
+ unsigned ticks_min, i;
+
+ /*
+ * Since the adc frequency is checked before, there is no reason
+ * to not meet the startup time constraint.
+ */
+
+ ticks_min = startup_time_min * adc_clk_khz / 1000;
+ for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
+ if (startup_lookup[i] > ticks_min)
+ break;
+
+ return i;
+}
+
+static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq,
+ unsigned int startup_time,
+ unsigned int tracktim)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ unsigned f_per, prescal, startup, mr;
+ int ret;
+
+ f_per = clk_get_rate(st->per_clk);
+ prescal = (f_per / (2 * freq)) - 1;
+
+ startup = at91_adc_startup_time(startup_time, freq / 1000);
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return;
+
+ mr = at91_adc_readl(st, MR);
+ mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK);
+ mr |= AT91_SAMA5D2_MR_STARTUP(startup);
+ mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
+ mr |= AT91_SAMA5D2_MR_TRACKTIM(tracktim);
+ at91_adc_writel(st, MR, mr);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u, tracktim=%u\n",
+ freq, startup, prescal, tracktim);
+ st->current_sample_rate = freq;
+}
+
+static inline unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
+{
+ return st->current_sample_rate;
+}
+
+static void at91_adc_touch_data_handler(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ u8 bit;
+ u16 val;
+ int i = 0;
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ st->soc_info.platform->max_index + 1) {
+ struct iio_chan_spec const *chan =
+ at91_adc_chan_get(indio_dev, bit);
+
+ if (chan->type == IIO_POSITIONRELATIVE)
+ at91_adc_read_position(st, chan->channel, &val);
+ else if (chan->type == IIO_PRESSURE)
+ at91_adc_read_pressure(st, chan->channel, &val);
+ else
+ continue;
+ st->buffer[i] = val;
+ i++;
+ }
+ /*
+ * Schedule work to push to buffers.
+ * This is intended to push to the callback buffer that another driver
+ * registered. We are still in a handler from our IRQ. If we push
+ * directly, it means the other driver has it's callback called
+ * from our IRQ context. Which is something we better avoid.
+ * Let's schedule it after our IRQ is completed.
+ */
+ schedule_work(&st->touch_st.workq);
+}
+
+static void at91_adc_pen_detect_interrupt(struct at91_adc_state *st)
+{
+ at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN);
+ at91_adc_writel(st, IER, AT91_SAMA5D2_IER_NOPEN |
+ AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY |
+ AT91_SAMA5D2_IER_PRDY);
+ at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC |
+ AT91_SAMA5D2_TRGR_TRGPER(st->touch_st.sample_period_val));
+ st->touch_st.touching = true;
+}
+
+static void at91_adc_no_pen_detect_interrupt(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER);
+ at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_NOPEN |
+ AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY |
+ AT91_SAMA5D2_IER_PRDY);
+ st->touch_st.touching = false;
+
+ at91_adc_touch_data_handler(indio_dev);
+
+ at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN);
+}
+
+static void at91_adc_workq_handler(struct work_struct *workq)
+{
+ struct at91_adc_touch *touch_st = container_of(workq,
+ struct at91_adc_touch, workq);
+ struct at91_adc_state *st = container_of(touch_st,
+ struct at91_adc_state, touch_st);
+ struct iio_dev *indio_dev = st->indio_dev;
+
+ iio_push_to_buffers(indio_dev, st->buffer);
+}
+
+static irqreturn_t at91_adc_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio = private;
+ struct at91_adc_state *st = iio_priv(indio);
+ u32 status, eoc, imr, eoc_imr;
+ u32 rdy_mask = AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY |
+ AT91_SAMA5D2_IER_PRDY;
+
+ at91_adc_irq_status(st, &status, &eoc);
+ at91_adc_irq_mask(st, &imr, &eoc_imr);
+
+ if (!(status & imr) && !(eoc & eoc_imr))
+ return IRQ_NONE;
+ if (status & AT91_SAMA5D2_IER_PEN) {
+ /* pen detected IRQ */
+ at91_adc_pen_detect_interrupt(st);
+ } else if ((status & AT91_SAMA5D2_IER_NOPEN)) {
+ /* nopen detected IRQ */
+ at91_adc_no_pen_detect_interrupt(indio);
+ } else if ((status & AT91_SAMA5D2_ISR_PENS) &&
+ ((status & rdy_mask) == rdy_mask)) {
+ /* periodic trigger IRQ - during pen sense */
+ at91_adc_touch_data_handler(indio);
+ } else if (status & AT91_SAMA5D2_ISR_PENS) {
+ /*
+ * touching, but the measurements are not ready yet.
+ * read and ignore.
+ */
+ status = at91_adc_readl(st, XPOSR);
+ status = at91_adc_readl(st, YPOSR);
+ status = at91_adc_readl(st, PRESSR);
+ } else if (iio_buffer_enabled(indio) &&
+ (status & AT91_SAMA5D2_IER_DRDY)) {
+ /* triggered buffer without DMA */
+ disable_irq_nosync(irq);
+ iio_trigger_poll(indio->trig);
+ } else if (iio_buffer_enabled(indio) && st->dma_st.dma_chan) {
+ /* triggered buffer with DMA - should not happen */
+ disable_irq_nosync(irq);
+ WARN(true, "Unexpected irq occurred\n");
+ } else if (!iio_buffer_enabled(indio)) {
+ /* software requested conversion */
+ st->conversion_value = at91_adc_read_chan(st, st->chan->address);
+ st->conversion_done = true;
+ wake_up_interruptible(&st->wq_data_available);
+ }
+ return IRQ_HANDLED;
+}
+
+/* This needs to be called with direct mode claimed and st->lock locked. */
+static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ u16 tmp_val;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Keep in mind that we cannot use software trigger or touchscreen
+ * if external trigger is enabled
+ */
+ if (chan->type == IIO_POSITIONRELATIVE) {
+ ret = at91_adc_read_position(st, chan->channel,
+ &tmp_val);
+ *val = tmp_val;
+ if (ret > 0)
+ ret = at91_adc_adjust_val_osr(st, val);
+
+ goto pm_runtime_put;
+ }
+ if (chan->type == IIO_PRESSURE) {
+ ret = at91_adc_read_pressure(st, chan->channel,
+ &tmp_val);
+ *val = tmp_val;
+ if (ret > 0)
+ ret = at91_adc_adjust_val_osr(st, val);
+
+ goto pm_runtime_put;
+ }
+
+ /* in this case we have a voltage or temperature channel */
+
+ st->chan = chan;
+
+ at91_adc_cor(st, chan);
+ at91_adc_writel(st, CHER, BIT(chan->channel));
+ /*
+ * TEMPMR.TEMPON needs to update after CHER otherwise if none
+ * of the channels are enabled and TEMPMR.TEMPON = 1 will
+ * trigger DRDY interruption while preparing for temperature read.
+ */
+ if (chan->type == IIO_TEMP)
+ at91_adc_writel(st, TEMPMR, AT91_SAMA5D2_TEMPMR_TEMPON);
+ at91_adc_eoc_ena(st, chan->channel);
+ at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START);
+
+ ret = wait_event_interruptible_timeout(st->wq_data_available,
+ st->conversion_done,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+
+ if (ret > 0) {
+ *val = st->conversion_value;
+ ret = at91_adc_adjust_val_osr(st, val);
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(*val,
+ chan->scan_type.realbits - 1);
+ st->conversion_done = false;
+ }
+
+ at91_adc_eoc_dis(st, st->chan->channel);
+ if (chan->type == IIO_TEMP)
+ at91_adc_writel(st, TEMPMR, 0U);
+ at91_adc_writel(st, CHDR, BIT(chan->channel));
+
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, LCDR);
+
+pm_runtime_put:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ return ret;
+}
+
+static int at91_adc_read_info_locked(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ ret = at91_adc_read_info_raw(indio_dev, chan, val);
+ mutex_unlock(&st->lock);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static void at91_adc_temp_sensor_configure(struct at91_adc_state *st,
+ bool start)
+{
+ u32 sample_rate, oversampling_ratio;
+ u32 startup_time, tracktim, trackx;
+
+ if (start) {
+ /*
+ * Configure the sensor for best accuracy: 10MHz frequency,
+ * oversampling rate of 256, tracktim=0xf and trackx=1.
+ */
+ sample_rate = 10 * MEGA;
+ oversampling_ratio = 256;
+ startup_time = AT91_SAMA5D2_MR_STARTUP_TS_MIN;
+ tracktim = AT91_SAMA5D2_MR_TRACKTIM_TS;
+ trackx = AT91_SAMA5D2_TRACKX_TS;
+
+ st->temp_st.saved_sample_rate = st->current_sample_rate;
+ st->temp_st.saved_oversampling = st->oversampling_ratio;
+ } else {
+ /* Go back to previous settings. */
+ sample_rate = st->temp_st.saved_sample_rate;
+ oversampling_ratio = st->temp_st.saved_oversampling;
+ startup_time = st->soc_info.startup_time;
+ tracktim = 0;
+ trackx = 0;
+ }
+
+ at91_adc_setup_samp_freq(st->indio_dev, sample_rate, startup_time,
+ tracktim);
+ at91_adc_config_emr(st, oversampling_ratio, trackx);
+}
+
+static int at91_adc_read_temp(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb;
+ u64 div1, div2;
+ u32 tmp;
+ int ret, vbg, vtemp;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ mutex_lock(&st->lock);
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ goto unlock;
+
+ at91_adc_temp_sensor_configure(st, true);
+
+ /* Read VBG. */
+ tmp = at91_adc_readl(st, ACR);
+ tmp |= AT91_SAMA5D2_ACR_SRCLCH;
+ at91_adc_writel(st, ACR, tmp);
+ ret = at91_adc_read_info_raw(indio_dev, chan, &vbg);
+ if (ret < 0)
+ goto restore_config;
+
+ /* Read VTEMP. */
+ tmp &= ~AT91_SAMA5D2_ACR_SRCLCH;
+ at91_adc_writel(st, ACR, tmp);
+ ret = at91_adc_read_info_raw(indio_dev, chan, &vtemp);
+
+restore_config:
+ /* Revert previous settings. */
+ 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;
+
+ /*
+ * Temp[milli] = p1[milli] + (vtemp * clb->p6 - clb->p4 * vbg)/
+ * (vbg * AT91_ADC_TS_VTEMP_DT)
+ */
+ div1 = DIV_ROUND_CLOSEST_ULL(((u64)vtemp * clb->p6), vbg);
+ div1 = DIV_ROUND_CLOSEST_ULL((div1 * 1000), AT91_ADC_TS_VTEMP_DT);
+ div2 = DIV_ROUND_CLOSEST_ULL((u64)clb->p4, AT91_ADC_TS_VTEMP_DT);
+ div2 *= 1000;
+ *val = clb->p1 + (int)div1 - (int)div2;
+
+ return ret;
+}
+
+static int at91_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return at91_adc_read_info_locked(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_uv / 1000;
+ if (chan->differential)
+ *val *= 2;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+ return at91_adc_read_temp(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = at91_adc_get_sample_freq(st);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = st->oversampling_ratio;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int at91_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ /* if no change, optimize out */
+ if (val == st->oversampling_ratio)
+ return 0;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ 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);
+ 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;
+ 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);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int at91_adc_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = (int *)st->soc_info.platform->oversampling_avail;
+ *type = IIO_VAL_INT;
+ *length = st->soc_info.platform->oversampling_avail_no;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void at91_adc_dma_init(struct at91_adc_state *st)
+{
+ struct device *dev = &st->indio_dev->dev;
+ struct dma_slave_config config = {0};
+ /* we have 2 bytes for each channel */
+ unsigned int sample_size = st->soc_info.platform->nr_channels * 2;
+ /*
+ * We make the buffer double the size of the fifo,
+ * such that DMA uses one half of the buffer (full fifo size)
+ * and the software uses the other half to read/write.
+ */
+ unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE *
+ sample_size * 2, PAGE_SIZE);
+
+ if (st->dma_st.dma_chan)
+ return;
+
+ st->dma_st.dma_chan = dma_request_chan(dev, "rx");
+ if (IS_ERR(st->dma_st.dma_chan)) {
+ dev_info(dev, "can't get DMA channel\n");
+ st->dma_st.dma_chan = NULL;
+ goto dma_exit;
+ }
+
+ st->dma_st.rx_buf = dma_alloc_coherent(st->dma_st.dma_chan->device->dev,
+ pages * PAGE_SIZE,
+ &st->dma_st.rx_dma_buf,
+ GFP_KERNEL);
+ if (!st->dma_st.rx_buf) {
+ dev_info(dev, "can't allocate coherent DMA area\n");
+ goto dma_chan_disable;
+ }
+
+ /* Configure DMA channel to read data register */
+ config.direction = DMA_DEV_TO_MEM;
+ config.src_addr = (phys_addr_t)(st->dma_st.phys_addr
+ + st->soc_info.platform->layout->LCDR);
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ config.src_maxburst = 1;
+ config.dst_maxburst = 1;
+
+ if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) {
+ dev_info(dev, "can't configure DMA slave\n");
+ goto dma_free_area;
+ }
+
+ dev_info(dev, "using %s for rx DMA transfers\n",
+ dma_chan_name(st->dma_st.dma_chan));
+
+ return;
+
+dma_free_area:
+ dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE,
+ st->dma_st.rx_buf, st->dma_st.rx_dma_buf);
+dma_chan_disable:
+ dma_release_channel(st->dma_st.dma_chan);
+ st->dma_st.dma_chan = NULL;
+dma_exit:
+ dev_info(dev, "continuing without DMA support\n");
+}
+
+static void at91_adc_dma_disable(struct at91_adc_state *st)
+{
+ struct device *dev = &st->indio_dev->dev;
+ /* we have 2 bytes for each channel */
+ unsigned int sample_size = st->soc_info.platform->nr_channels * 2;
+ unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE *
+ sample_size * 2, PAGE_SIZE);
+
+ /* if we are not using DMA, just return */
+ if (!st->dma_st.dma_chan)
+ return;
+
+ /* wait for all transactions to be terminated first*/
+ dmaengine_terminate_sync(st->dma_st.dma_chan);
+
+ dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE,
+ st->dma_st.rx_buf, st->dma_st.rx_dma_buf);
+ dma_release_channel(st->dma_st.dma_chan);
+ st->dma_st.dma_chan = NULL;
+
+ dev_info(dev, "continuing without DMA support\n");
+}
+
+static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (val > AT91_HWFIFO_MAX_SIZE)
+ val = AT91_HWFIFO_MAX_SIZE;
+
+ if (!st->selected_trig->hw_trig) {
+ dev_dbg(&indio_dev->dev, "we need hw trigger for DMA\n");
+ return 0;
+ }
+
+ dev_dbg(&indio_dev->dev, "new watermark is %u\n", val);
+ st->dma_st.watermark = val;
+
+ /*
+ * The logic here is: if we have watermark 1, it means we do
+ * each conversion with it's own IRQ, thus we don't need DMA.
+ * If the watermark is higher, we do DMA to do all the transfers in bulk
+ */
+
+ if (val == 1)
+ at91_adc_dma_disable(st);
+ else if (val > 1)
+ at91_adc_dma_init(st);
+
+ /*
+ * We can start the DMA only after setting the watermark and
+ * having the DMA initialization completed
+ */
+ ret = at91_adc_buffer_prepare(indio_dev);
+ if (ret)
+ at91_adc_dma_disable(st);
+
+ return ret;
+}
+
+static int at91_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ if (bitmap_subset(scan_mask, &st->touch_st.channels_bitmask,
+ st->soc_info.platform->max_index + 1))
+ return 0;
+ /*
+ * if the new bitmap is a combination of touchscreen and regular
+ * channels, then we are not fine
+ */
+ if (bitmap_intersects(&st->touch_st.channels_bitmask, scan_mask,
+ st->soc_info.platform->max_index + 1))
+ return -EINVAL;
+ return 0;
+}
+
+static void at91_adc_hw_init(struct iio_dev *indio_dev)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST);
+ if (st->soc_info.platform->layout->EOC_IDR)
+ at91_adc_writel(st, EOC_IDR, 0xffffffff);
+ at91_adc_writel(st, IDR, 0xffffffff);
+ /*
+ * Transfer field must be set to 2 according to the datasheet and
+ * allows different analog settings for each channel.
+ */
+ at91_adc_writel(st, MR,
+ AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
+
+ at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate,
+ st->soc_info.startup_time, 0);
+
+ /* configure extended mode register */
+ at91_adc_config_emr(st, st->oversampling_ratio, 0);
+}
+
+static ssize_t at91_adc_get_fifo_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan);
+}
+
+static ssize_t at91_adc_get_watermark(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark);
+}
+
+static ssize_t hwfifo_watermark_min_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%s\n", "2");
+}
+
+static ssize_t hwfifo_watermark_max_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sysfs_emit(buf, "%s\n", AT91_HWFIFO_MAX_SIZE_STR);
+}
+
+static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
+ at91_adc_get_fifo_state, NULL, 0);
+static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
+ at91_adc_get_watermark, NULL, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0);
+static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0);
+
+static const struct attribute *at91_adc_fifo_attributes[] = {
+ &iio_dev_attr_hwfifo_watermark_min.dev_attr.attr,
+ &iio_dev_attr_hwfifo_watermark_max.dev_attr.attr,
+ &iio_dev_attr_hwfifo_watermark.dev_attr.attr,
+ &iio_dev_attr_hwfifo_enabled.dev_attr.attr,
+ NULL,
+};
+
+static const struct iio_info at91_adc_info = {
+ .read_avail = &at91_adc_read_avail,
+ .read_raw = &at91_adc_read_raw,
+ .write_raw = &at91_adc_write_raw,
+ .update_scan_mode = &at91_adc_update_scan_mode,
+ .fwnode_xlate = &at91_adc_fwnode_xlate,
+ .hwfifo_set_watermark = &at91_adc_set_watermark,
+};
+
+static int at91_adc_buffer_and_trigger_init(struct device *dev,
+ struct iio_dev *indio)
+{
+ struct at91_adc_state *st = iio_priv(indio);
+ const struct attribute **fifo_attrs;
+ int ret;
+
+ if (st->selected_trig->hw_trig)
+ fifo_attrs = at91_adc_fifo_attributes;
+ else
+ fifo_attrs = NULL;
+
+ ret = devm_iio_triggered_buffer_setup_ext(&indio->dev, indio,
+ &iio_pollfunc_store_time, &at91_adc_trigger_handler,
+ IIO_BUFFER_DIRECTION_IN, &at91_buffer_setup_ops, fifo_attrs);
+ if (ret < 0) {
+ dev_err(dev, "couldn't initialize the buffer.\n");
+ return ret;
+ }
+
+ if (!st->selected_trig->hw_trig)
+ return 0;
+
+ st->trig = at91_adc_allocate_trigger(indio, st->selected_trig->name);
+ if (IS_ERR(st->trig)) {
+ dev_err(dev, "could not allocate trigger\n");
+ return PTR_ERR(st->trig);
+ }
+
+ /*
+ * Initially the iio buffer has a length of 2 and
+ * a watermark of 1
+ */
+ st->dma_st.watermark = 1;
+
+ return 0;
+}
+
+static int at91_adc_temp_sensor_init(struct at91_adc_state *st,
+ struct device *dev)
+{
+ struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb;
+ struct nvmem_cell *temp_calib;
+ u32 *buf;
+ size_t len;
+ int ret = 0;
+
+ if (!st->soc_info.platform->temp_sensor)
+ return 0;
+
+ /* Get the calibration data from NVMEM. */
+ temp_calib = devm_nvmem_cell_get(dev, "temperature_calib");
+ if (IS_ERR(temp_calib)) {
+ ret = PTR_ERR(temp_calib);
+ if (ret != -ENOENT)
+ dev_err(dev, "Failed to get temperature_calib cell!\n");
+ return ret;
+ }
+
+ buf = nvmem_cell_read(temp_calib, &len);
+ if (IS_ERR(buf)) {
+ dev_err(dev, "Failed to read calibration data!\n");
+ return PTR_ERR(buf);
+ }
+ if (len < AT91_ADC_TS_CLB_IDX_MAX * 4) {
+ dev_err(dev, "Invalid calibration data!\n");
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ /* Store calibration data for later use. */
+ clb->p1 = buf[AT91_ADC_TS_CLB_IDX_P1];
+ clb->p4 = buf[AT91_ADC_TS_CLB_IDX_P4];
+ clb->p6 = buf[AT91_ADC_TS_CLB_IDX_P6];
+
+ /*
+ * We prepare here the conversion to milli to avoid doing it on hotpath.
+ */
+ clb->p1 = clb->p1 * 1000;
+
+free_buf:
+ kfree(buf);
+ return ret;
+}
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct at91_adc_state *st;
+ struct resource *res;
+ int ret, i, num_channels;
+ u32 edge_type = IRQ_TYPE_NONE;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->indio_dev = indio_dev;
+
+ st->soc_info.platform = device_get_match_data(dev);
+
+ ret = at91_adc_temp_sensor_init(st, &pdev->dev);
+ /* Don't register temperature channel if initialization failed. */
+ if (ret)
+ num_channels = st->soc_info.platform->max_channels - 1;
+ else
+ num_channels = st->soc_info.platform->max_channels;
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->info = &at91_adc_info;
+ indio_dev->channels = *st->soc_info.platform->adc_channels;
+ indio_dev->num_channels = num_channels;
+
+ bitmap_set(&st->touch_st.channels_bitmask,
+ st->soc_info.platform->touch_chan_x, 1);
+ bitmap_set(&st->touch_st.channels_bitmask,
+ st->soc_info.platform->touch_chan_y, 1);
+ bitmap_set(&st->touch_st.channels_bitmask,
+ st->soc_info.platform->touch_chan_p, 1);
+
+ st->oversampling_ratio = 1;
+
+ ret = device_property_read_u32(dev, "atmel,min-sample-rate-hz",
+ &st->soc_info.min_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,min-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "atmel,max-sample-rate-hz",
+ &st->soc_info.max_sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,max-sample-rate-hz\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "atmel,startup-time-ms",
+ &st->soc_info.startup_time);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "invalid or missing value for atmel,startup-time-ms\n");
+ return ret;
+ }
+
+ ret = device_property_read_u32(dev, "atmel,trigger-edge-type",
+ &edge_type);
+ if (ret) {
+ dev_dbg(&pdev->dev,
+ "atmel,trigger-edge-type not specified, only software trigger available\n");
+ }
+
+ st->selected_trig = NULL;
+
+ /* find the right trigger, or no trigger at all */
+ for (i = 0; i < st->soc_info.platform->hw_trig_cnt + 1; i++)
+ if (at91_adc_trigger_list[i].edge_type == edge_type) {
+ st->selected_trig = &at91_adc_trigger_list[i];
+ break;
+ }
+
+ if (!st->selected_trig) {
+ dev_err(&pdev->dev, "invalid external trigger edge value\n");
+ return -EINVAL;
+ }
+
+ init_waitqueue_head(&st->wq_data_available);
+ mutex_init(&st->lock);
+ INIT_WORK(&st->touch_st.workq, at91_adc_workq_handler);
+
+ st->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(st->base))
+ return PTR_ERR(st->base);
+
+ /* if we plan to use DMA, we need the physical address of the regs */
+ st->dma_st.phys_addr = res->start;
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (st->irq <= 0) {
+ if (!st->irq)
+ st->irq = -ENXIO;
+
+ return st->irq;
+ }
+
+ st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
+ if (IS_ERR(st->per_clk))
+ return PTR_ERR(st->per_clk);
+
+ st->reg = devm_regulator_get(&pdev->dev, "vddana");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
+ pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ goto reg_disable;
+
+ st->vref_uv = regulator_get_voltage(st->vref);
+ if (st->vref_uv <= 0) {
+ ret = -EINVAL;
+ goto vref_disable;
+ }
+
+ ret = clk_prepare_enable(st->per_clk);
+ if (ret)
+ goto vref_disable;
+
+ platform_set_drvdata(pdev, indio_dev);
+ st->dev = &pdev->dev;
+ pm_runtime_set_autosuspend_delay(st->dev, 500);
+ pm_runtime_use_autosuspend(st->dev);
+ pm_runtime_set_active(st->dev);
+ pm_runtime_enable(st->dev);
+ pm_runtime_get_noresume(st->dev);
+
+ at91_adc_hw_init(indio_dev);
+
+ ret = at91_adc_buffer_and_trigger_init(&pdev->dev, indio_dev);
+ if (ret < 0)
+ goto err_pm_disable;
+
+ if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32)))
+ dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n");
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto dma_disable;
+
+ if (st->selected_trig->hw_trig)
+ dev_info(&pdev->dev, "setting up trigger as %s\n",
+ st->selected_trig->name);
+
+ dev_info(&pdev->dev, "version: %x\n",
+ readl_relaxed(st->base + st->soc_info.platform->layout->VERSION));
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ return 0;
+
+dma_disable:
+ at91_adc_dma_disable(st);
+err_pm_disable:
+ pm_runtime_put_noidle(st->dev);
+ pm_runtime_disable(st->dev);
+ pm_runtime_set_suspended(st->dev);
+ pm_runtime_dont_use_autosuspend(st->dev);
+ clk_disable_unprepare(st->per_clk);
+vref_disable:
+ regulator_disable(st->vref);
+reg_disable:
+ regulator_disable(st->reg);
+ return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ at91_adc_dma_disable(st);
+
+ pm_runtime_disable(st->dev);
+ pm_runtime_set_suspended(st->dev);
+ clk_disable_unprepare(st->per_clk);
+
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static int at91_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ if (iio_buffer_enabled(indio_dev))
+ at91_adc_buffer_postdisable(indio_dev);
+
+ /*
+ * Do a sofware reset of the ADC before we go to suspend.
+ * this will ensure that all pins are free from being muxed by the ADC
+ * and can be used by for other devices.
+ * Otherwise, ADC will hog them and we can't go to suspend mode.
+ */
+ at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_noidle(st->dev);
+ clk_disable_unprepare(st->per_clk);
+ regulator_disable(st->vref);
+ regulator_disable(st->reg);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int at91_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ goto resume_failed;
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto resume_failed;
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ goto reg_disable_resume;
+
+ ret = clk_prepare_enable(st->per_clk);
+ if (ret)
+ goto vref_disable_resume;
+
+ pm_runtime_get_noresume(st->dev);
+
+ at91_adc_hw_init(indio_dev);
+
+ /* reconfiguring trigger hardware state */
+ if (iio_buffer_enabled(indio_dev)) {
+ ret = at91_adc_buffer_prepare(indio_dev);
+ if (ret)
+ goto pm_runtime_put;
+
+ at91_adc_configure_trigger_registers(st, true);
+ }
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ return 0;
+
+pm_runtime_put:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_noidle(st->dev);
+ clk_disable_unprepare(st->per_clk);
+vref_disable_resume:
+ regulator_disable(st->vref);
+reg_disable_resume:
+ regulator_disable(st->reg);
+resume_failed:
+ dev_err(&indio_dev->dev, "failed to resume\n");
+ return ret;
+}
+
+static int at91_adc_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ clk_disable(st->per_clk);
+
+ return 0;
+}
+
+static int at91_adc_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ return clk_enable(st->per_clk);
+}
+
+static const struct dev_pm_ops at91_adc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(at91_adc_suspend, at91_adc_resume)
+ RUNTIME_PM_OPS(at91_adc_runtime_suspend, at91_adc_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id at91_adc_dt_match[] = {
+ {
+ .compatible = "atmel,sama5d2-adc",
+ .data = (const void *)&sama5d2_platform,
+ }, {
+ .compatible = "microchip,sama7g5-adc",
+ .data = (const void *)&sama7g5_platform,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
+
+static struct platform_driver at91_adc_driver = {
+ .probe = at91_adc_probe,
+ .remove = at91_adc_remove,
+ .driver = {
+ .name = "at91-sama5d2_adc",
+ .of_match_table = at91_adc_dt_match,
+ .pm = pm_ptr(&at91_adc_pm_ops),
+ },
+};
+module_platform_driver(at91_adc_driver)
+
+MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@microchip.com>");
+MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com");
+MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
new file mode 100644
index 000000000..366e252eb
--- /dev/null
+++ b/drivers/iio/adc/at91_adc.c
@@ -0,0 +1,1398 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for the ADC present in the Atmel AT91 evaluation boards.
+ *
+ * Copyright 2011 Free Electrons
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/pinctrl/consumer.h>
+
+/* Registers */
+#define AT91_ADC_CR 0x00 /* Control Register */
+#define AT91_ADC_SWRST (1 << 0) /* Software Reset */
+#define AT91_ADC_START (1 << 1) /* Start Conversion */
+
+#define AT91_ADC_MR 0x04 /* Mode Register */
+#define AT91_ADC_TSAMOD (3 << 0) /* ADC mode */
+#define AT91_ADC_TSAMOD_ADC_ONLY_MODE (0 << 0) /* ADC Mode */
+#define AT91_ADC_TSAMOD_TS_ONLY_MODE (1 << 0) /* Touch Screen Only Mode */
+#define AT91_ADC_TRGEN (1 << 0) /* Trigger Enable */
+#define AT91_ADC_TRGSEL (7 << 1) /* Trigger Selection */
+#define AT91_ADC_TRGSEL_TC0 (0 << 1)
+#define AT91_ADC_TRGSEL_TC1 (1 << 1)
+#define AT91_ADC_TRGSEL_TC2 (2 << 1)
+#define AT91_ADC_TRGSEL_EXTERNAL (6 << 1)
+#define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */
+#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */
+#define AT91_ADC_PENDET (1 << 6) /* Pen contact detection enable */
+#define AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */
+#define AT91_ADC_PRESCAL_9G45 (0xff << 8)
+#define AT91_ADC_PRESCAL_(x) ((x) << 8)
+#define AT91_ADC_STARTUP_9260 (0x1f << 16) /* Startup Up Time */
+#define AT91_ADC_STARTUP_9G45 (0x7f << 16)
+#define AT91_ADC_STARTUP_9X5 (0xf << 16)
+#define AT91_ADC_STARTUP_(x) ((x) << 16)
+#define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */
+#define AT91_ADC_SHTIM_(x) ((x) << 24)
+#define AT91_ADC_PENDBC (0x0f << 28) /* Pen Debounce time */
+#define AT91_ADC_PENDBC_(x) ((x) << 28)
+
+#define AT91_ADC_TSR 0x0C
+#define AT91_ADC_TSR_SHTIM (0xf << 24) /* Sample & Hold Time */
+#define AT91_ADC_TSR_SHTIM_(x) ((x) << 24)
+
+#define AT91_ADC_CHER 0x10 /* Channel Enable Register */
+#define AT91_ADC_CHDR 0x14 /* Channel Disable Register */
+#define AT91_ADC_CHSR 0x18 /* Channel Status Register */
+#define AT91_ADC_CH(n) (1 << (n)) /* Channel Number */
+
+#define AT91_ADC_SR 0x1C /* Status Register */
+#define AT91_ADC_EOC(n) (1 << (n)) /* End of Conversion on Channel N */
+#define AT91_ADC_OVRE(n) (1 << ((n) + 8))/* Overrun Error on Channel N */
+#define AT91_ADC_DRDY (1 << 16) /* Data Ready */
+#define AT91_ADC_GOVRE (1 << 17) /* General Overrun Error */
+#define AT91_ADC_ENDRX (1 << 18) /* End of RX Buffer */
+#define AT91_ADC_RXFUFF (1 << 19) /* RX Buffer Full */
+
+#define AT91_ADC_SR_9X5 0x30 /* Status Register for 9x5 */
+#define AT91_ADC_SR_DRDY_9X5 (1 << 24) /* Data Ready */
+
+#define AT91_ADC_LCDR 0x20 /* Last Converted Data Register */
+#define AT91_ADC_LDATA (0x3ff)
+
+#define AT91_ADC_IER 0x24 /* Interrupt Enable Register */
+#define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */
+#define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */
+#define AT91RL_ADC_IER_PEN (1 << 20)
+#define AT91RL_ADC_IER_NOPEN (1 << 21)
+#define AT91_ADC_IER_PEN (1 << 29)
+#define AT91_ADC_IER_NOPEN (1 << 30)
+#define AT91_ADC_IER_XRDY (1 << 20)
+#define AT91_ADC_IER_YRDY (1 << 21)
+#define AT91_ADC_IER_PRDY (1 << 22)
+#define AT91_ADC_ISR_PENS (1 << 31)
+
+#define AT91_ADC_CHR(n) (0x30 + ((n) * 4)) /* Channel Data Register N */
+#define AT91_ADC_DATA (0x3ff)
+
+#define AT91_ADC_CDR0_9X5 (0x50) /* Channel Data Register 0 for 9X5 */
+
+#define AT91_ADC_ACR 0x94 /* Analog Control Register */
+#define AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */
+
+#define AT91_ADC_TSMR 0xB0
+#define AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */
+#define AT91_ADC_TSMR_TSMODE_NONE (0 << 0)
+#define AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0)
+#define AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0)
+#define AT91_ADC_TSMR_TSMODE_5WIRE (3 << 0)
+#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */
+#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4)
+#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */
+#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16)
+#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */
+#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28)
+#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */
+#define AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */
+#define AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */
+
+#define AT91_ADC_TSXPOSR 0xB4
+#define AT91_ADC_TSYPOSR 0xB8
+#define AT91_ADC_TSPRESSR 0xBC
+
+#define AT91_ADC_TRGR_9260 AT91_ADC_MR
+#define AT91_ADC_TRGR_9G45 0x08
+#define AT91_ADC_TRGR_9X5 0xC0
+
+/* Trigger Register bit field */
+#define AT91_ADC_TRGR_TRGPER (0xffff << 16)
+#define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16)
+#define AT91_ADC_TRGR_TRGMOD (0x7 << 0)
+#define AT91_ADC_TRGR_NONE (0 << 0)
+#define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0)
+
+#define AT91_ADC_CHAN(st, ch) \
+ (st->registers->channel_base + (ch * 4))
+#define at91_adc_readl(st, reg) \
+ (readl_relaxed(st->reg_base + reg))
+#define at91_adc_writel(st, reg, val) \
+ (writel_relaxed(val, st->reg_base + reg))
+
+#define DRIVER_NAME "at91_adc"
+#define MAX_POS_BITS 12
+
+#define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */
+#define TOUCH_PEN_DETECT_DEBOUNCE_US 200
+
+#define MAX_RLPOS_BITS 10
+#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */
+#define TOUCH_SHTIM 0xa
+#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */
+
+enum atmel_adc_ts_type {
+ ATMEL_ADC_TOUCHSCREEN_NONE = 0,
+ ATMEL_ADC_TOUCHSCREEN_4WIRE = 4,
+ ATMEL_ADC_TOUCHSCREEN_5WIRE = 5,
+};
+
+/**
+ * struct at91_adc_trigger - description of triggers
+ * @name: name of the trigger advertised to the user
+ * @value: value to set in the ADC's trigger setup register
+ * to enable the trigger
+ * @is_external: Does the trigger rely on an external pin?
+ */
+struct at91_adc_trigger {
+ const char *name;
+ u8 value;
+ bool is_external;
+};
+
+/**
+ * struct at91_adc_reg_desc - Various informations relative to registers
+ * @channel_base: Base offset for the channel data registers
+ * @drdy_mask: Mask of the DRDY field in the relevant registers
+ * (Interruptions registers mostly)
+ * @status_register: Offset of the Interrupt Status Register
+ * @trigger_register: Offset of the Trigger setup register
+ * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register
+ * @mr_startup_mask: Mask of the STARTUP field in the adc MR register
+ */
+struct at91_adc_reg_desc {
+ u8 channel_base;
+ u32 drdy_mask;
+ u8 status_register;
+ u8 trigger_register;
+ u32 mr_prescal_mask;
+ u32 mr_startup_mask;
+};
+
+struct at91_adc_caps {
+ bool has_ts; /* Support touch screen */
+ bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */
+ /*
+ * Numbers of sampling data will be averaged. Can be 0~3.
+ * Hardware can average (2 ^ ts_filter_average) sample data.
+ */
+ u8 ts_filter_average;
+ /* Pen Detection input pull-up resistor, can be 0~3 */
+ u8 ts_pen_detect_sensitivity;
+
+ /* startup time calculate function */
+ u32 (*calc_startup_ticks)(u32 startup_time, u32 adc_clk_khz);
+
+ u8 num_channels;
+
+ u8 low_res_bits;
+ u8 high_res_bits;
+ u32 trigger_number;
+ const struct at91_adc_trigger *triggers;
+ struct at91_adc_reg_desc registers;
+};
+
+struct at91_adc_state {
+ struct clk *adc_clk;
+ u16 *buffer;
+ unsigned long channels_mask;
+ struct clk *clk;
+ bool done;
+ int irq;
+ u16 last_value;
+ int chnb;
+ struct mutex lock;
+ u8 num_channels;
+ void __iomem *reg_base;
+ const struct at91_adc_reg_desc *registers;
+ u32 startup_time;
+ u8 sample_hold_time;
+ bool sleep_mode;
+ struct iio_trigger **trig;
+ bool use_external;
+ u32 vref_mv;
+ u32 res; /* resolution used for convertions */
+ wait_queue_head_t wq_data_avail;
+ const struct at91_adc_caps *caps;
+
+ /*
+ * Following ADC channels are shared by touchscreen:
+ *
+ * CH0 -- Touch screen XP/UL
+ * CH1 -- Touch screen XM/UR
+ * CH2 -- Touch screen YP/LL
+ * CH3 -- Touch screen YM/Sense
+ * CH4 -- Touch screen LR(5-wire only)
+ *
+ * The bitfields below represents the reserved channel in the
+ * touchscreen mode.
+ */
+#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 0)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 0)
+ enum atmel_adc_ts_type touchscreen_type;
+ struct input_dev *ts_input;
+
+ u16 ts_sample_period_val;
+ u32 ts_pressure_threshold;
+ u16 ts_pendbc;
+
+ bool ts_bufferedmeasure;
+ u32 ts_prev_absx;
+ u32 ts_prev_absy;
+};
+
+static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ struct at91_adc_state *st = iio_priv(idev);
+ struct iio_chan_spec const *chan;
+ int i, j = 0;
+
+ for (i = 0; i < idev->masklength; i++) {
+ if (!test_bit(i, idev->active_scan_mask))
+ continue;
+ chan = idev->channels + i;
+ st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp);
+
+ iio_trigger_notify_done(idev->trig);
+
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, AT91_ADC_LCDR);
+
+ enable_irq(st->irq);
+
+ return IRQ_HANDLED;
+}
+
+/* Handler for classic adc channel eoc trigger */
+static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+
+ if (iio_buffer_enabled(idev)) {
+ disable_irq_nosync(irq);
+ iio_trigger_poll(idev->trig);
+ } else {
+ st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb));
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, AT91_ADC_LCDR);
+ st->done = true;
+ wake_up_interruptible(&st->wq_data_avail);
+ }
+}
+
+static int at91_ts_sample(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ unsigned int xscale, yscale, reg, z1, z2;
+ unsigned int x, y, pres, xpos, ypos;
+ unsigned int rxp = 1;
+ unsigned int factor = 1000;
+
+ unsigned int xyz_mask_bits = st->res;
+ unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
+
+ /* calculate position */
+ /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
+ reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
+ xpos = reg & xyz_mask;
+ x = (xpos << MAX_POS_BITS) - xpos;
+ xscale = (reg >> 16) & xyz_mask;
+ if (xscale == 0) {
+ dev_err(&idev->dev, "Error: xscale == 0!\n");
+ return -1;
+ }
+ x /= xscale;
+
+ /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
+ reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
+ ypos = reg & xyz_mask;
+ y = (ypos << MAX_POS_BITS) - ypos;
+ yscale = (reg >> 16) & xyz_mask;
+ if (yscale == 0) {
+ dev_err(&idev->dev, "Error: yscale == 0!\n");
+ return -1;
+ }
+ y /= yscale;
+
+ /* calculate the pressure */
+ reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
+ z1 = reg & xyz_mask;
+ z2 = (reg >> 16) & xyz_mask;
+
+ if (z1 != 0)
+ pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
+ / factor;
+ else
+ pres = st->ts_pressure_threshold; /* no pen contacted */
+
+ dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
+ xpos, xscale, ypos, yscale, z1, z2, pres);
+
+ if (pres < st->ts_pressure_threshold) {
+ dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
+ x, y, pres / factor);
+ input_report_abs(st->ts_input, ABS_X, x);
+ input_report_abs(st->ts_input, ABS_Y, y);
+ input_report_abs(st->ts_input, ABS_PRESSURE, pres);
+ input_report_key(st->ts_input, BTN_TOUCH, 1);
+ input_sync(st->ts_input);
+ } else {
+ dev_dbg(&idev->dev, "pressure too low: not reporting\n");
+ }
+
+ return 0;
+}
+
+static irqreturn_t at91_adc_rl_interrupt(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct at91_adc_state *st = iio_priv(idev);
+ u32 status = at91_adc_readl(st, st->registers->status_register);
+ unsigned int reg;
+
+ status &= at91_adc_readl(st, AT91_ADC_IMR);
+ if (status & GENMASK(st->num_channels - 1, 0))
+ handle_adc_eoc_trigger(irq, idev);
+
+ if (status & AT91RL_ADC_IER_PEN) {
+ /* Disabling pen debounce is required to get a NOPEN irq */
+ reg = at91_adc_readl(st, AT91_ADC_MR);
+ reg &= ~AT91_ADC_PENDBC;
+ at91_adc_writel(st, AT91_ADC_MR, reg);
+
+ at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN);
+ at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN
+ | AT91_ADC_EOC(3));
+ /* Set up period trigger for sampling */
+ at91_adc_writel(st, st->registers->trigger_register,
+ AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+ AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+ } else if (status & AT91RL_ADC_IER_NOPEN) {
+ reg = at91_adc_readl(st, AT91_ADC_MR);
+ reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC;
+ at91_adc_writel(st, AT91_ADC_MR, reg);
+ at91_adc_writel(st, st->registers->trigger_register,
+ AT91_ADC_TRGR_NONE);
+
+ at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN
+ | AT91_ADC_EOC(3));
+ at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN);
+ st->ts_bufferedmeasure = false;
+ input_report_key(st->ts_input, BTN_TOUCH, 0);
+ input_sync(st->ts_input);
+ } else if (status & AT91_ADC_EOC(3) && st->ts_input) {
+ /* Conversion finished and we've a touchscreen */
+ if (st->ts_bufferedmeasure) {
+ /*
+ * Last measurement is always discarded, since it can
+ * be erroneous.
+ * Always report previous measurement
+ */
+ input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx);
+ input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy);
+ input_report_key(st->ts_input, BTN_TOUCH, 1);
+ input_sync(st->ts_input);
+ } else
+ st->ts_bufferedmeasure = true;
+
+ /* Now make new measurement */
+ st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3))
+ << MAX_RLPOS_BITS;
+ st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2));
+
+ st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1))
+ << MAX_RLPOS_BITS;
+ st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct at91_adc_state *st = iio_priv(idev);
+ u32 status = at91_adc_readl(st, st->registers->status_register);
+ const uint32_t ts_data_irq_mask =
+ AT91_ADC_IER_XRDY |
+ AT91_ADC_IER_YRDY |
+ AT91_ADC_IER_PRDY;
+
+ if (status & GENMASK(st->num_channels - 1, 0))
+ handle_adc_eoc_trigger(irq, idev);
+
+ if (status & AT91_ADC_IER_PEN) {
+ at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+ at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
+ ts_data_irq_mask);
+ /* Set up period trigger for sampling */
+ at91_adc_writel(st, st->registers->trigger_register,
+ AT91_ADC_TRGR_MOD_PERIOD_TRIG |
+ AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
+ } else if (status & AT91_ADC_IER_NOPEN) {
+ at91_adc_writel(st, st->registers->trigger_register, 0);
+ at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
+ ts_data_irq_mask);
+ at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+
+ input_report_key(st->ts_input, BTN_TOUCH, 0);
+ input_sync(st->ts_input);
+ } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
+ /* Now all touchscreen data is ready */
+
+ if (status & AT91_ADC_ISR_PENS) {
+ /* validate data by pen contact */
+ at91_ts_sample(idev);
+ } else {
+ /* triggered by event that is no pen contact, just read
+ * them to clean the interrupt and discard all.
+ */
+ at91_adc_readl(st, AT91_ADC_TSXPOSR);
+ at91_adc_readl(st, AT91_ADC_TSYPOSR);
+ at91_adc_readl(st, AT91_ADC_TSPRESSR);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int at91_adc_channel_init(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ struct iio_chan_spec *chan_array, *timestamp;
+ int bit, idx = 0;
+ unsigned long rsvd_mask = 0;
+
+ /* If touchscreen is enable, then reserve the adc channels */
+ if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+ rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
+ else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
+ rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+ /* set up the channel mask to reserve touchscreen channels */
+ st->channels_mask &= ~rsvd_mask;
+
+ idev->num_channels = bitmap_weight(&st->channels_mask,
+ st->num_channels) + 1;
+
+ chan_array = devm_kzalloc(&idev->dev,
+ ((idev->num_channels + 1) *
+ sizeof(struct iio_chan_spec)),
+ GFP_KERNEL);
+
+ if (!chan_array)
+ return -ENOMEM;
+
+ for_each_set_bit(bit, &st->channels_mask, st->num_channels) {
+ struct iio_chan_spec *chan = chan_array + idx;
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = bit;
+ chan->scan_index = idx;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = st->res;
+ chan->scan_type.storagebits = 16;
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ idx++;
+ }
+ timestamp = chan_array + idx;
+
+ timestamp->type = IIO_TIMESTAMP;
+ timestamp->channel = -1;
+ timestamp->scan_index = idx;
+ timestamp->scan_type.sign = 's';
+ timestamp->scan_type.realbits = 64;
+ timestamp->scan_type.storagebits = 64;
+
+ idev->channels = chan_array;
+ return idev->num_channels;
+}
+
+static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,
+ const struct at91_adc_trigger *triggers,
+ const char *trigger_name)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int i;
+
+ for (i = 0; i < st->caps->trigger_number; i++) {
+ char *name = kasprintf(GFP_KERNEL,
+ "%s-dev%d-%s",
+ idev->name,
+ iio_device_id(idev),
+ triggers[i].name);
+ if (!name)
+ return -ENOMEM;
+
+ if (strcmp(trigger_name, name) == 0) {
+ kfree(name);
+ if (triggers[i].value == 0)
+ return -EINVAL;
+ return triggers[i].value;
+ }
+
+ kfree(name);
+ }
+
+ return -EINVAL;
+}
+
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *idev = iio_trigger_get_drvdata(trig);
+ struct at91_adc_state *st = iio_priv(idev);
+ const struct at91_adc_reg_desc *reg = st->registers;
+ u32 status = at91_adc_readl(st, reg->trigger_register);
+ int value;
+ u8 bit;
+
+ value = at91_adc_get_trigger_value_by_name(idev,
+ st->caps->triggers,
+ idev->trig->name);
+ if (value < 0)
+ return value;
+
+ if (state) {
+ st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL);
+ if (st->buffer == NULL)
+ return -ENOMEM;
+
+ at91_adc_writel(st, reg->trigger_register,
+ status | value);
+
+ for_each_set_bit(bit, idev->active_scan_mask,
+ st->num_channels) {
+ struct iio_chan_spec const *chan = idev->channels + bit;
+ at91_adc_writel(st, AT91_ADC_CHER,
+ AT91_ADC_CH(chan->channel));
+ }
+
+ at91_adc_writel(st, AT91_ADC_IER, reg->drdy_mask);
+
+ } else {
+ at91_adc_writel(st, AT91_ADC_IDR, reg->drdy_mask);
+
+ at91_adc_writel(st, reg->trigger_register,
+ status & ~value);
+
+ for_each_set_bit(bit, idev->active_scan_mask,
+ st->num_channels) {
+ struct iio_chan_spec const *chan = idev->channels + bit;
+ at91_adc_writel(st, AT91_ADC_CHDR,
+ AT91_ADC_CH(chan->channel));
+ }
+ kfree(st->buffer);
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops at91_adc_trigger_ops = {
+ .set_trigger_state = &at91_adc_configure_trigger,
+};
+
+static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev,
+ const struct at91_adc_trigger *trigger)
+{
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name,
+ iio_device_id(idev), trigger->name);
+ if (trig == NULL)
+ return NULL;
+
+ iio_trigger_set_drvdata(trig, idev);
+ trig->ops = &at91_adc_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ iio_trigger_free(trig);
+ return NULL;
+ }
+
+ return trig;
+}
+
+static int at91_adc_trigger_init(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int i, ret;
+
+ st->trig = devm_kcalloc(&idev->dev,
+ st->caps->trigger_number, sizeof(*st->trig),
+ GFP_KERNEL);
+
+ if (st->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ for (i = 0; i < st->caps->trigger_number; i++) {
+ if (st->caps->triggers[i].is_external && !(st->use_external))
+ continue;
+
+ st->trig[i] = at91_adc_allocate_trigger(idev,
+ st->caps->triggers + i);
+ if (st->trig[i] == NULL) {
+ dev_err(&idev->dev,
+ "Could not allocate trigger %d\n", i);
+ ret = -ENOMEM;
+ goto error_trigger;
+ }
+ }
+
+ return 0;
+
+error_trigger:
+ for (i--; i >= 0; i--) {
+ iio_trigger_unregister(st->trig[i]);
+ iio_trigger_free(st->trig[i]);
+ }
+error_ret:
+ return ret;
+}
+
+static void at91_adc_trigger_remove(struct iio_dev *idev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int i;
+
+ for (i = 0; i < st->caps->trigger_number; i++) {
+ iio_trigger_unregister(st->trig[i]);
+ iio_trigger_free(st->trig[i]);
+ }
+}
+
+static int at91_adc_buffer_init(struct iio_dev *idev)
+{
+ return iio_triggered_buffer_setup(idev, &iio_pollfunc_store_time,
+ &at91_adc_trigger_handler, NULL);
+}
+
+static void at91_adc_buffer_remove(struct iio_dev *idev)
+{
+ iio_triggered_buffer_cleanup(idev);
+}
+
+static int at91_adc_read_raw(struct iio_dev *idev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+
+ st->chnb = chan->channel;
+ at91_adc_writel(st, AT91_ADC_CHER,
+ AT91_ADC_CH(chan->channel));
+ at91_adc_writel(st, AT91_ADC_IER, BIT(chan->channel));
+ at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_START);
+
+ ret = wait_event_interruptible_timeout(st->wq_data_avail,
+ st->done,
+ msecs_to_jiffies(1000));
+
+ /* Disable interrupts, regardless if adc conversion was
+ * successful or not
+ */
+ at91_adc_writel(st, AT91_ADC_CHDR,
+ AT91_ADC_CH(chan->channel));
+ at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
+
+ if (ret > 0) {
+ /* a valid conversion took place */
+ *val = st->last_value;
+ st->last_value = 0;
+ st->done = false;
+ ret = IIO_VAL_INT;
+ } else if (ret == 0) {
+ /* conversion timeout */
+ dev_err(&idev->dev, "ADC Channel %d timeout.\n",
+ chan->channel);
+ ret = -ETIMEDOUT;
+ }
+
+ mutex_unlock(&st->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+
+static u32 calc_startup_ticks_9260(u32 startup_time, u32 adc_clk_khz)
+{
+ /*
+ * Number of ticks needed to cover the startup time of the ADC
+ * as defined in the electrical characteristics of the board,
+ * divided by 8. The formula thus is :
+ * Startup Time = (ticks + 1) * 8 / ADC Clock
+ */
+ return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8;
+}
+
+static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz)
+{
+ /*
+ * For sama5d3x and at91sam9x5, the formula changes to:
+ * Startup Time = <lookup_table_value> / ADC Clock
+ */
+ static const int startup_lookup[] = {
+ 0, 8, 16, 24,
+ 64, 80, 96, 112,
+ 512, 576, 640, 704,
+ 768, 832, 896, 960
+ };
+ int i, size = ARRAY_SIZE(startup_lookup);
+ unsigned int ticks;
+
+ ticks = startup_time * adc_clk_khz / 1000;
+ for (i = 0; i < size; i++)
+ if (ticks < startup_lookup[i])
+ break;
+
+ ticks = i;
+ if (ticks == size)
+ /* Reach the end of lookup table */
+ ticks = size - 1;
+
+ return ticks;
+}
+
+static int at91_adc_probe_dt_ts(struct device_node *node,
+ struct at91_adc_state *st, struct device *dev)
+{
+ int ret;
+ u32 prop;
+
+ ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop);
+ if (ret) {
+ dev_info(dev, "ADC Touch screen is disabled.\n");
+ return 0;
+ }
+
+ switch (prop) {
+ case 4:
+ case 5:
+ st->touchscreen_type = prop;
+ break;
+ default:
+ dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n", prop);
+ return -EINVAL;
+ }
+
+ if (!st->caps->has_tsmr)
+ return 0;
+ prop = 0;
+ of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop);
+ st->ts_pressure_threshold = prop;
+ if (st->ts_pressure_threshold) {
+ return 0;
+ } else {
+ dev_err(dev, "Invalid pressure threshold for the touchscreen\n");
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info at91_adc_info = {
+ .read_raw = &at91_adc_read_raw,
+};
+
+/* Touchscreen related functions */
+static int atmel_ts_open(struct input_dev *dev)
+{
+ struct at91_adc_state *st = input_get_drvdata(dev);
+
+ if (st->caps->has_tsmr)
+ at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
+ else
+ at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN);
+ return 0;
+}
+
+static void atmel_ts_close(struct input_dev *dev)
+{
+ struct at91_adc_state *st = input_get_drvdata(dev);
+
+ if (st->caps->has_tsmr)
+ at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
+ else
+ at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN);
+}
+
+static int at91_ts_hw_init(struct iio_dev *idev, u32 adc_clk_khz)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ u32 reg = 0;
+ u32 tssctim = 0;
+ int i = 0;
+
+ /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
+ * pen detect noise.
+ * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
+ */
+ st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz /
+ 1000, 1);
+
+ while (st->ts_pendbc >> ++i)
+ ; /* Empty! Find the shift offset */
+ if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1))))
+ st->ts_pendbc = i;
+ else
+ st->ts_pendbc = i - 1;
+
+ if (!st->caps->has_tsmr) {
+ reg = at91_adc_readl(st, AT91_ADC_MR);
+ reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET;
+
+ reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC;
+ at91_adc_writel(st, AT91_ADC_MR, reg);
+
+ reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM;
+ at91_adc_writel(st, AT91_ADC_TSR, reg);
+
+ st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL *
+ adc_clk_khz / 1000) - 1, 1);
+
+ return 0;
+ }
+
+ /* Touchscreen Switches Closure time needed for allowing the value to
+ * stabilize.
+ * Switch Closure Time = (TSSCTIM * 4) ADCClock periods
+ */
+ tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4);
+ dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n",
+ adc_clk_khz, tssctim);
+
+ if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
+ reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
+ else
+ reg = AT91_ADC_TSMR_TSMODE_5WIRE;
+
+ reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM;
+ reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average)
+ & AT91_ADC_TSMR_TSAV;
+ reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC;
+ reg |= AT91_ADC_TSMR_NOTSDMA;
+ reg |= AT91_ADC_TSMR_PENDET_ENA;
+ reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */
+
+ at91_adc_writel(st, AT91_ADC_TSMR, reg);
+
+ /* Change adc internal resistor value for better pen detection,
+ * default value is 100 kOhm.
+ * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
+ * option only available on ES2 and higher
+ */
+ at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity
+ & AT91_ADC_ACR_PENDETSENS);
+
+ /* Sample Period Time = (TRGPER + 1) / ADCClock */
+ st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US *
+ adc_clk_khz / 1000) - 1, 1);
+
+ return 0;
+}
+
+static int at91_ts_register(struct iio_dev *idev,
+ struct platform_device *pdev)
+{
+ struct at91_adc_state *st = iio_priv(idev);
+ struct input_dev *input;
+ int ret;
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&idev->dev, "Failed to allocate TS device!\n");
+ return -ENOMEM;
+ }
+
+ input->name = DRIVER_NAME;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+ input->open = atmel_ts_open;
+ input->close = atmel_ts_close;
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ if (st->caps->has_tsmr) {
+ input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1,
+ 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1,
+ 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
+ } else {
+ if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) {
+ dev_err(&pdev->dev,
+ "This touchscreen controller only support 4 wires\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1,
+ 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1,
+ 0, 0);
+ }
+
+ st->ts_input = input;
+ input_set_drvdata(input, st);
+
+ ret = input_register_device(input);
+ if (ret)
+ goto err;
+
+ return ret;
+
+err:
+ input_free_device(st->ts_input);
+ return ret;
+}
+
+static void at91_ts_unregister(struct at91_adc_state *st)
+{
+ input_unregister_device(st->ts_input);
+}
+
+static int at91_adc_probe(struct platform_device *pdev)
+{
+ unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
+ struct device_node *node = pdev->dev.of_node;
+ int ret;
+ struct iio_dev *idev;
+ struct at91_adc_state *st;
+ u32 reg, prop;
+ char *s;
+
+ idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state));
+ if (!idev)
+ return -ENOMEM;
+
+ st = iio_priv(idev);
+
+ st->caps = of_device_get_match_data(&pdev->dev);
+
+ st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers");
+
+ if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) {
+ dev_err(&idev->dev, "Missing adc-channels-used property in the DT.\n");
+ return -EINVAL;
+ }
+ st->channels_mask = prop;
+
+ st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");
+
+ if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) {
+ dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n");
+ return -EINVAL;
+ }
+ st->startup_time = prop;
+
+ prop = 0;
+ of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop);
+ st->sample_hold_time = prop;
+
+ if (of_property_read_u32(node, "atmel,adc-vref", &prop)) {
+ dev_err(&idev->dev, "Missing adc-vref property in the DT.\n");
+ return -EINVAL;
+ }
+ st->vref_mv = prop;
+
+ st->res = st->caps->high_res_bits;
+ if (st->caps->low_res_bits &&
+ !of_property_read_string(node, "atmel,adc-use-res", (const char **)&s)
+ && !strcmp(s, "lowres"))
+ st->res = st->caps->low_res_bits;
+
+ dev_info(&idev->dev, "Resolution used: %u bits\n", st->res);
+
+ st->registers = &st->caps->registers;
+ st->num_channels = st->caps->num_channels;
+
+ /* Check if touchscreen is supported. */
+ if (st->caps->has_ts) {
+ ret = at91_adc_probe_dt_ts(node, st, &idev->dev);
+ if (ret)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, idev);
+
+ idev->name = dev_name(&pdev->dev);
+ idev->modes = INDIO_DIRECT_MODE;
+ idev->info = &at91_adc_info;
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (st->irq < 0)
+ return -ENODEV;
+
+ st->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(st->reg_base))
+ return PTR_ERR(st->reg_base);
+
+
+ /*
+ * Disable all IRQs before setting up the handler
+ */
+ at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
+ at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
+
+ if (st->caps->has_tsmr)
+ ret = request_irq(st->irq, at91_adc_9x5_interrupt, 0,
+ pdev->dev.driver->name, idev);
+ else
+ ret = request_irq(st->irq, at91_adc_rl_interrupt, 0,
+ pdev->dev.driver->name, idev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
+ return ret;
+ }
+
+ st->clk = devm_clk_get(&pdev->dev, "adc_clk");
+ if (IS_ERR(st->clk)) {
+ dev_err(&pdev->dev, "Failed to get the clock.\n");
+ ret = PTR_ERR(st->clk);
+ goto error_free_irq;
+ }
+
+ ret = clk_prepare_enable(st->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_free_irq;
+ }
+
+ st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk");
+ if (IS_ERR(st->adc_clk)) {
+ dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
+ ret = PTR_ERR(st->adc_clk);
+ goto error_disable_clk;
+ }
+
+ ret = clk_prepare_enable(st->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the ADC clock.\n");
+ goto error_disable_clk;
+ }
+
+ /*
+ * Prescaler rate computation using the formula from the Atmel's
+ * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being
+ * specified by the electrical characteristics of the board.
+ */
+ mstrclk = clk_get_rate(st->clk);
+ adc_clk = clk_get_rate(st->adc_clk);
+ adc_clk_khz = adc_clk / 1000;
+
+ dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
+ mstrclk, adc_clk);
+
+ prsc = (mstrclk / (2 * adc_clk)) - 1;
+
+ if (!st->startup_time) {
+ dev_err(&pdev->dev, "No startup time available.\n");
+ ret = -EINVAL;
+ goto error_disable_adc_clk;
+ }
+ ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz);
+
+ /*
+ * a minimal Sample and Hold Time is necessary for the ADC to guarantee
+ * the best converted final value between two channels selection
+ * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock
+ */
+ if (st->sample_hold_time > 0)
+ shtim = round_up((st->sample_hold_time * adc_clk_khz / 1000)
+ - 1, 1);
+ else
+ shtim = 0;
+
+ reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask;
+ reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask;
+ if (st->res == st->caps->low_res_bits)
+ reg |= AT91_ADC_LOWRES;
+ if (st->sleep_mode)
+ reg |= AT91_ADC_SLEEP;
+ reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM;
+ at91_adc_writel(st, AT91_ADC_MR, reg);
+
+ /* Setup the ADC channels available on the board */
+ ret = at91_adc_channel_init(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't initialize the channels.\n");
+ goto error_disable_adc_clk;
+ }
+
+ init_waitqueue_head(&st->wq_data_avail);
+ mutex_init(&st->lock);
+
+ /*
+ * Since touch screen will set trigger register as period trigger. So
+ * when touch screen is enabled, then we have to disable hardware
+ * trigger for classic adc.
+ */
+ if (!st->touchscreen_type) {
+ ret = at91_adc_buffer_init(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
+ goto error_disable_adc_clk;
+ }
+
+ ret = at91_adc_trigger_init(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
+ at91_adc_buffer_remove(idev);
+ goto error_disable_adc_clk;
+ }
+ } else {
+ ret = at91_ts_register(idev, pdev);
+ if (ret)
+ goto error_disable_adc_clk;
+
+ at91_ts_hw_init(idev, adc_clk_khz);
+ }
+
+ ret = iio_device_register(idev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto error_iio_device_register;
+ }
+
+ return 0;
+
+error_iio_device_register:
+ if (!st->touchscreen_type) {
+ at91_adc_trigger_remove(idev);
+ at91_adc_buffer_remove(idev);
+ } else {
+ at91_ts_unregister(st);
+ }
+error_disable_adc_clk:
+ clk_disable_unprepare(st->adc_clk);
+error_disable_clk:
+ clk_disable_unprepare(st->clk);
+error_free_irq:
+ free_irq(st->irq, idev);
+ return ret;
+}
+
+static int at91_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *idev = platform_get_drvdata(pdev);
+ struct at91_adc_state *st = iio_priv(idev);
+
+ iio_device_unregister(idev);
+ if (!st->touchscreen_type) {
+ at91_adc_trigger_remove(idev);
+ at91_adc_buffer_remove(idev);
+ } else {
+ at91_ts_unregister(st);
+ }
+ clk_disable_unprepare(st->adc_clk);
+ clk_disable_unprepare(st->clk);
+ free_irq(st->irq, idev);
+
+ return 0;
+}
+
+static int at91_adc_suspend(struct device *dev)
+{
+ struct iio_dev *idev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(idev);
+
+ pinctrl_pm_select_sleep_state(dev);
+ clk_disable_unprepare(st->clk);
+
+ return 0;
+}
+
+static int at91_adc_resume(struct device *dev)
+{
+ struct iio_dev *idev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(idev);
+
+ clk_prepare_enable(st->clk);
+ pinctrl_pm_select_default_state(dev);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend,
+ at91_adc_resume);
+
+static const struct at91_adc_trigger at91sam9260_triggers[] = {
+ { .name = "timer-counter-0", .value = 0x1 },
+ { .name = "timer-counter-1", .value = 0x3 },
+ { .name = "timer-counter-2", .value = 0x5 },
+ { .name = "external", .value = 0xd, .is_external = true },
+};
+
+static struct at91_adc_caps at91sam9260_caps = {
+ .calc_startup_ticks = calc_startup_ticks_9260,
+ .num_channels = 4,
+ .low_res_bits = 8,
+ .high_res_bits = 10,
+ .registers = {
+ .channel_base = AT91_ADC_CHR(0),
+ .drdy_mask = AT91_ADC_DRDY,
+ .status_register = AT91_ADC_SR,
+ .trigger_register = AT91_ADC_TRGR_9260,
+ .mr_prescal_mask = AT91_ADC_PRESCAL_9260,
+ .mr_startup_mask = AT91_ADC_STARTUP_9260,
+ },
+ .triggers = at91sam9260_triggers,
+ .trigger_number = ARRAY_SIZE(at91sam9260_triggers),
+};
+
+static const struct at91_adc_trigger at91sam9x5_triggers[] = {
+ { .name = "external-rising", .value = 0x1, .is_external = true },
+ { .name = "external-falling", .value = 0x2, .is_external = true },
+ { .name = "external-any", .value = 0x3, .is_external = true },
+ { .name = "continuous", .value = 0x6 },
+};
+
+static struct at91_adc_caps at91sam9rl_caps = {
+ .has_ts = true,
+ .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
+ .num_channels = 6,
+ .low_res_bits = 8,
+ .high_res_bits = 10,
+ .registers = {
+ .channel_base = AT91_ADC_CHR(0),
+ .drdy_mask = AT91_ADC_DRDY,
+ .status_register = AT91_ADC_SR,
+ .trigger_register = AT91_ADC_TRGR_9G45,
+ .mr_prescal_mask = AT91_ADC_PRESCAL_9260,
+ .mr_startup_mask = AT91_ADC_STARTUP_9G45,
+ },
+ .triggers = at91sam9x5_triggers,
+ .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
+};
+
+static struct at91_adc_caps at91sam9g45_caps = {
+ .has_ts = true,
+ .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
+ .num_channels = 8,
+ .low_res_bits = 8,
+ .high_res_bits = 10,
+ .registers = {
+ .channel_base = AT91_ADC_CHR(0),
+ .drdy_mask = AT91_ADC_DRDY,
+ .status_register = AT91_ADC_SR,
+ .trigger_register = AT91_ADC_TRGR_9G45,
+ .mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
+ .mr_startup_mask = AT91_ADC_STARTUP_9G45,
+ },
+ .triggers = at91sam9x5_triggers,
+ .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
+};
+
+static struct at91_adc_caps at91sam9x5_caps = {
+ .has_ts = true,
+ .has_tsmr = true,
+ .ts_filter_average = 3,
+ .ts_pen_detect_sensitivity = 2,
+ .calc_startup_ticks = calc_startup_ticks_9x5,
+ .num_channels = 12,
+ .low_res_bits = 8,
+ .high_res_bits = 10,
+ .registers = {
+ .channel_base = AT91_ADC_CDR0_9X5,
+ .drdy_mask = AT91_ADC_SR_DRDY_9X5,
+ .status_register = AT91_ADC_SR_9X5,
+ .trigger_register = AT91_ADC_TRGR_9X5,
+ /* prescal mask is same as 9G45 */
+ .mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
+ .mr_startup_mask = AT91_ADC_STARTUP_9X5,
+ },
+ .triggers = at91sam9x5_triggers,
+ .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
+};
+
+static struct at91_adc_caps sama5d3_caps = {
+ .has_ts = true,
+ .has_tsmr = true,
+ .ts_filter_average = 3,
+ .ts_pen_detect_sensitivity = 2,
+ .calc_startup_ticks = calc_startup_ticks_9x5,
+ .num_channels = 12,
+ .low_res_bits = 0,
+ .high_res_bits = 12,
+ .registers = {
+ .channel_base = AT91_ADC_CDR0_9X5,
+ .drdy_mask = AT91_ADC_SR_DRDY_9X5,
+ .status_register = AT91_ADC_SR_9X5,
+ .trigger_register = AT91_ADC_TRGR_9X5,
+ .mr_prescal_mask = AT91_ADC_PRESCAL_9G45,
+ .mr_startup_mask = AT91_ADC_STARTUP_9X5,
+ },
+ .triggers = at91sam9x5_triggers,
+ .trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
+};
+
+static const struct of_device_id at91_adc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9260-adc", .data = &at91sam9260_caps },
+ { .compatible = "atmel,at91sam9rl-adc", .data = &at91sam9rl_caps },
+ { .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps },
+ { .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps },
+ { .compatible = "atmel,sama5d3-adc", .data = &sama5d3_caps },
+ {},
+};
+MODULE_DEVICE_TABLE(of, at91_adc_dt_ids);
+
+static struct platform_driver at91_adc_driver = {
+ .probe = at91_adc_probe,
+ .remove = at91_adc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = at91_adc_dt_ids,
+ .pm = pm_sleep_ptr(&at91_adc_pm_ops),
+ },
+};
+
+module_platform_driver(at91_adc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atmel AT91 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
new file mode 100644
index 000000000..53bf7d489
--- /dev/null
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* ADC driver for AXP20X and AXP22X PMICs
+ *
+ * Copyright (c) 2016 Free Electrons NextThing Co.
+ * Quentin Schulz <quentin.schulz@free-electrons.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/axp20x.h>
+
+#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
+
+#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
+#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
+
+#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
+#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
+#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
+#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
+
+#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
+#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
+#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
+#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
+#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
+#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
+#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK)
+#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x))
+
+#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
+ { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .address = _reg, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _name, \
+ }
+
+#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
+ { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .address = _reg, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) |\
+ BIT(IIO_CHAN_INFO_OFFSET),\
+ .datasheet_name = _name, \
+ }
+
+struct axp_data;
+
+struct axp20x_adc_iio {
+ struct regmap *regmap;
+ const struct axp_data *data;
+};
+
+enum axp20x_adc_channel_v {
+ AXP20X_ACIN_V = 0,
+ AXP20X_VBUS_V,
+ AXP20X_TS_IN,
+ AXP20X_GPIO0_V,
+ AXP20X_GPIO1_V,
+ AXP20X_IPSOUT_V,
+ AXP20X_BATT_V,
+};
+
+enum axp20x_adc_channel_i {
+ AXP20X_ACIN_I = 0,
+ AXP20X_VBUS_I,
+ AXP20X_BATT_CHRG_I,
+ AXP20X_BATT_DISCHRG_I,
+};
+
+enum axp22x_adc_channel_v {
+ AXP22X_TS_IN = 0,
+ AXP22X_BATT_V,
+};
+
+enum axp22x_adc_channel_i {
+ AXP22X_BATT_CHRG_I = 1,
+ AXP22X_BATT_DISCHRG_I,
+};
+
+enum axp813_adc_channel_v {
+ AXP813_TS_IN = 0,
+ AXP813_GPIO0_V,
+ AXP813_BATT_V,
+};
+
+static struct iio_map axp20x_maps[] = {
+ {
+ .consumer_dev_name = "axp20x-usb-power-supply",
+ .consumer_channel = "vbus_v",
+ .adc_channel_label = "vbus_v",
+ }, {
+ .consumer_dev_name = "axp20x-usb-power-supply",
+ .consumer_channel = "vbus_i",
+ .adc_channel_label = "vbus_i",
+ }, {
+ .consumer_dev_name = "axp20x-ac-power-supply",
+ .consumer_channel = "acin_v",
+ .adc_channel_label = "acin_v",
+ }, {
+ .consumer_dev_name = "axp20x-ac-power-supply",
+ .consumer_channel = "acin_i",
+ .adc_channel_label = "acin_i",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_v",
+ .adc_channel_label = "batt_v",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_chrg_i",
+ .adc_channel_label = "batt_chrg_i",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_dischrg_i",
+ .adc_channel_label = "batt_dischrg_i",
+ }, { /* sentinel */ }
+};
+
+static struct iio_map axp22x_maps[] = {
+ {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_v",
+ .adc_channel_label = "batt_v",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_chrg_i",
+ .adc_channel_label = "batt_chrg_i",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_dischrg_i",
+ .adc_channel_label = "batt_dischrg_i",
+ }, { /* sentinel */ }
+};
+
+/*
+ * Channels are mapped by physical system. Their channels share the same index.
+ * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
+ * The only exception is for the battery. batt_v will be in_voltage6_raw and
+ * charge current in_current6_raw and discharge current will be in_current7_raw.
+ */
+static const struct iio_chan_spec axp20x_adc_channels[] = {
+ AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
+ AXP20X_ACIN_V_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT,
+ AXP20X_ACIN_I_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE,
+ AXP20X_VBUS_V_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT,
+ AXP20X_VBUS_I_ADC_H),
+ {
+ .type = IIO_TEMP,
+ .address = AXP20X_TEMP_ADC_H,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "pmic_temp",
+ },
+ AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
+ AXP20X_GPIO0_V_ADC_H),
+ AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
+ AXP20X_GPIO1_V_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
+ AXP20X_IPSOUT_V_HIGH_H),
+ AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP20X_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP20X_BATT_CHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
+ AXP20X_BATT_DISCHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP20X_TS_IN, "ts_v", IIO_VOLTAGE,
+ AXP20X_TS_IN_H),
+};
+
+static const struct iio_chan_spec axp22x_adc_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .address = AXP22X_PMIC_TEMP_H,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "pmic_temp",
+ },
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP20X_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP20X_BATT_CHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
+ AXP20X_BATT_DISCHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP22X_TS_IN, "ts_v", IIO_VOLTAGE,
+ AXP22X_TS_ADC_H),
+};
+
+static const struct iio_chan_spec axp813_adc_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .address = AXP22X_PMIC_TEMP_H,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "pmic_temp",
+ },
+ AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
+ AXP288_GP_ADC_H),
+ AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP20X_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP20X_BATT_CHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
+ AXP20X_BATT_DISCHRG_I_H),
+ AXP20X_ADC_CHANNEL(AXP813_TS_IN, "ts_v", IIO_VOLTAGE,
+ AXP288_TS_ADC_H),
+};
+
+static int axp20x_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ int size = 12;
+
+ /*
+ * N.B.: Unlike the Chinese datasheets tell, the charging current is
+ * stored on 12 bits, not 13 bits. Only discharging current is on 13
+ * bits.
+ */
+ if (chan->type == IIO_CURRENT && chan->channel == AXP20X_BATT_DISCHRG_I)
+ size = 13;
+ else
+ size = 12;
+
+ *val = axp20x_read_variable_width(info->regmap, chan->address, size);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+}
+
+static int axp22x_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+
+ *val = axp20x_read_variable_width(info->regmap, chan->address, 12);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+}
+
+static int axp813_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+
+ *val = axp20x_read_variable_width(info->regmap, chan->address, 12);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+}
+
+static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case AXP20X_ACIN_V:
+ case AXP20X_VBUS_V:
+ *val = 1;
+ *val2 = 700000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP20X_GPIO0_V:
+ case AXP20X_GPIO1_V:
+ *val = 0;
+ *val2 = 500000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP20X_BATT_V:
+ *val = 1;
+ *val2 = 100000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP20X_IPSOUT_V:
+ *val = 1;
+ *val2 = 400000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP20X_TS_IN:
+ /* 0.8 mV per LSB */
+ *val = 0;
+ *val2 = 800000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp22x_adc_scale_voltage(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case AXP22X_BATT_V:
+ /* 1.1 mV per LSB */
+ *val = 1;
+ *val2 = 100000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP22X_TS_IN:
+ /* 0.8 mV per LSB */
+ *val = 0;
+ *val2 = 800000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+static int axp813_adc_scale_voltage(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case AXP813_GPIO0_V:
+ *val = 0;
+ *val2 = 800000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP813_BATT_V:
+ *val = 1;
+ *val2 = 100000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP813_TS_IN:
+ /* 0.8 mV per LSB */
+ *val = 0;
+ *val2 = 800000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp20x_adc_scale_current(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case AXP20X_ACIN_I:
+ *val = 0;
+ *val2 = 625000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP20X_VBUS_I:
+ *val = 0;
+ *val2 = 375000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case AXP20X_BATT_DISCHRG_I:
+ case AXP20X_BATT_CHRG_I:
+ *val = 0;
+ *val2 = 500000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp20x_adc_scale_voltage(chan->channel, val, val2);
+
+ case IIO_CURRENT:
+ return axp20x_adc_scale_current(chan->channel, val, val2);
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp22x_adc_scale_voltage(chan->channel, val, val2);
+
+ case IIO_CURRENT:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp813_adc_scale_voltage(chan->channel, val, val2);
+
+ case IIO_CURRENT:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
+ int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val);
+ if (ret < 0)
+ return ret;
+
+ switch (channel) {
+ case AXP20X_GPIO0_V:
+ *val &= AXP20X_GPIO10_IN_RANGE_GPIO0;
+ break;
+
+ case AXP20X_GPIO1_V:
+ *val &= AXP20X_GPIO10_IN_RANGE_GPIO1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ *val = *val ? 700000 : 0;
+
+ return IIO_VAL_INT;
+}
+
+static int axp20x_adc_offset(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ return axp20x_adc_offset_voltage(indio_dev, chan->channel, val);
+
+ case IIO_TEMP:
+ *val = -1447;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp20x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ return axp20x_adc_offset(indio_dev, chan, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ return axp20x_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp20x_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp22x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ /* For PMIC temp only */
+ *val = -2677;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ return axp22x_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp22x_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp813_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -2667;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ return axp813_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp813_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axp20x_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ unsigned int reg, regval;
+
+ /*
+ * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
+ * for (independently) GPIO0 and GPIO1 when in ADC mode.
+ */
+ if (mask != IIO_CHAN_INFO_OFFSET)
+ return -EINVAL;
+
+ if (val != 0 && val != 700000)
+ return -EINVAL;
+
+ val = val ? 1 : 0;
+
+ switch (chan->channel) {
+ case AXP20X_GPIO0_V:
+ reg = AXP20X_GPIO10_IN_RANGE_GPIO0;
+ regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val);
+ break;
+
+ case AXP20X_GPIO1_V:
+ reg = AXP20X_GPIO10_IN_RANGE_GPIO1;
+ regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg,
+ regval);
+}
+
+static const struct iio_info axp20x_adc_iio_info = {
+ .read_raw = axp20x_read_raw,
+ .write_raw = axp20x_write_raw,
+};
+
+static const struct iio_info axp22x_adc_iio_info = {
+ .read_raw = axp22x_read_raw,
+};
+
+static const struct iio_info axp813_adc_iio_info = {
+ .read_raw = axp813_read_raw,
+};
+
+static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate)
+{
+ return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
+ AXP20X_ADC_RATE_MASK,
+ AXP20X_ADC_RATE_HZ(rate));
+}
+
+static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate)
+{
+ return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
+ AXP20X_ADC_RATE_MASK,
+ AXP22X_ADC_RATE_HZ(rate));
+}
+
+static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate)
+{
+ return regmap_update_bits(info->regmap, AXP813_ADC_RATE,
+ AXP813_ADC_RATE_MASK,
+ AXP813_ADC_RATE_HZ(rate));
+}
+
+struct axp_data {
+ const struct iio_info *iio_info;
+ int num_channels;
+ struct iio_chan_spec const *channels;
+ unsigned long adc_en1_mask;
+ int (*adc_rate)(struct axp20x_adc_iio *info,
+ int rate);
+ bool adc_en2;
+ struct iio_map *maps;
+};
+
+static const struct axp_data axp20x_data = {
+ .iio_info = &axp20x_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp20x_adc_channels),
+ .channels = axp20x_adc_channels,
+ .adc_en1_mask = AXP20X_ADC_EN1_MASK,
+ .adc_rate = axp20x_adc_rate,
+ .adc_en2 = true,
+ .maps = axp20x_maps,
+};
+
+static const struct axp_data axp22x_data = {
+ .iio_info = &axp22x_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp22x_adc_channels),
+ .channels = axp22x_adc_channels,
+ .adc_en1_mask = AXP22X_ADC_EN1_MASK,
+ .adc_rate = axp22x_adc_rate,
+ .adc_en2 = false,
+ .maps = axp22x_maps,
+};
+
+static const struct axp_data axp813_data = {
+ .iio_info = &axp813_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp813_adc_channels),
+ .channels = axp813_adc_channels,
+ .adc_en1_mask = AXP22X_ADC_EN1_MASK,
+ .adc_rate = axp813_adc_rate,
+ .adc_en2 = false,
+ .maps = axp22x_maps,
+};
+
+static const struct of_device_id axp20x_adc_of_match[] = {
+ { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
+ { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
+ { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
+
+static const struct platform_device_id axp20x_adc_id_match[] = {
+ { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
+ { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
+ { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
+
+static int axp20x_probe(struct platform_device *pdev)
+{
+ struct axp20x_adc_iio *info;
+ struct iio_dev *indio_dev;
+ struct axp20x_dev *axp20x_dev;
+ int ret;
+
+ axp20x_dev = dev_get_drvdata(pdev->dev.parent);
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ platform_set_drvdata(pdev, indio_dev);
+
+ info->regmap = axp20x_dev->regmap;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (!dev_fwnode(&pdev->dev)) {
+ const struct platform_device_id *id;
+
+ id = platform_get_device_id(pdev);
+ info->data = (const struct axp_data *)id->driver_data;
+ } else {
+ struct device *dev = &pdev->dev;
+
+ info->data = device_get_match_data(dev);
+ }
+
+ indio_dev->name = platform_get_device_id(pdev)->name;
+ indio_dev->info = info->data->iio_info;
+ indio_dev->num_channels = info->data->num_channels;
+ indio_dev->channels = info->data->channels;
+
+ /* Enable the ADCs on IP */
+ regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
+
+ if (info->data->adc_en2)
+ /* Enable GPIO0/1 and internal temperature ADCs */
+ regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
+ AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
+
+ /* Configure ADCs rate */
+ info->data->adc_rate(info, 100);
+
+ ret = iio_map_array_register(indio_dev, info->data->maps);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register IIO maps: %d\n", ret);
+ goto fail_map;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register the device\n");
+ goto fail_register;
+ }
+
+ return 0;
+
+fail_register:
+ iio_map_array_unregister(indio_dev);
+
+fail_map:
+ regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
+
+ if (info->data->adc_en2)
+ regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
+
+ return ret;
+}
+
+static int axp20x_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+
+ regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
+
+ if (info->data->adc_en2)
+ regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
+
+ return 0;
+}
+
+static struct platform_driver axp20x_adc_driver = {
+ .driver = {
+ .name = "axp20x-adc",
+ .of_match_table = axp20x_adc_of_match,
+ },
+ .id_table = axp20x_adc_id_match,
+ .probe = axp20x_probe,
+ .remove = axp20x_remove,
+};
+
+module_platform_driver(axp20x_adc_driver);
+
+MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs");
+MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
new file mode 100644
index 000000000..580361bd9
--- /dev/null
+++ b/drivers/iio/adc/axp288_adc.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+/*
+ * This mask enables all ADCs except for the battery temp-sensor (TS), that is
+ * left as-is to avoid breaking charging on devices without a temp-sensor.
+ */
+#define AXP288_ADC_EN_MASK 0xF0
+#define AXP288_ADC_TS_ENABLE 0x01
+
+#define AXP288_ADC_TS_BIAS_MASK GENMASK(5, 4)
+#define AXP288_ADC_TS_BIAS_20UA (0 << 4)
+#define AXP288_ADC_TS_BIAS_40UA (1 << 4)
+#define AXP288_ADC_TS_BIAS_60UA (2 << 4)
+#define AXP288_ADC_TS_BIAS_80UA (3 << 4)
+#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0)
+#define AXP288_ADC_TS_CURRENT_OFF (0 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0)
+#define AXP288_ADC_TS_CURRENT_ON (3 << 0)
+
+enum axp288_adc_id {
+ AXP288_ADC_TS,
+ AXP288_ADC_PMIC,
+ AXP288_ADC_GP,
+ AXP288_ADC_BATT_CHRG_I,
+ AXP288_ADC_BATT_DISCHRG_I,
+ AXP288_ADC_BATT_V,
+ AXP288_ADC_NR_CHAN,
+};
+
+struct axp288_adc_info {
+ int irq;
+ struct regmap *regmap;
+ bool ts_enabled;
+};
+
+static const struct iio_chan_spec axp288_adc_channels[] = {
+ {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 0,
+ .address = AXP288_TS_ADC_H,
+ .datasheet_name = "TS_PIN",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 1,
+ .address = AXP288_PMIC_ADC_H,
+ .datasheet_name = "PMIC_TEMP",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 2,
+ .address = AXP288_GP_ADC_H,
+ .datasheet_name = "GPADC",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 3,
+ .address = AXP20X_BATT_CHRG_I_H,
+ .datasheet_name = "BATT_CHG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 4,
+ .address = AXP20X_BATT_DISCHRG_I_H,
+ .datasheet_name = "BATT_DISCHRG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .indexed = 1,
+ .type = IIO_VOLTAGE,
+ .channel = 5,
+ .address = AXP20X_BATT_V_H,
+ .datasheet_name = "BATT_V",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+};
+
+/* for consumer drivers */
+static struct iio_map axp288_adc_default_maps[] = {
+ IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
+ IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
+ IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
+ 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,
+ struct regmap *regmap)
+{
+ u8 buf[2];
+
+ if (regmap_bulk_read(regmap, address, buf, 2))
+ return -EIO;
+ *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
+
+ return IIO_VAL_INT;
+}
+
+/*
+ * The current-source used for the battery temp-sensor (TS) is shared
+ * with the GPADC. For proper fuel-gauge and charger operation the TS
+ * current-source needs to be permanently on. But to read the GPADC we
+ * need to temporary switch the TS current-source to ondemand, so that
+ * the GPADC can use it, otherwise we will always read an all 0 value.
+ */
+static int axp288_adc_set_ts(struct axp288_adc_info *info,
+ unsigned int mode, unsigned long address)
+{
+ int ret;
+
+ /* No need to switch the current-source if the TS pin is disabled */
+ if (!info->ts_enabled)
+ return 0;
+
+ /* Channels other than GPADC do not need the current source */
+ if (address != AXP288_GP_ADC_H)
+ return 0;
+
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK, mode);
+ if (ret)
+ return ret;
+
+ /* When switching to the GPADC pin give things some time to settle */
+ if (mode == AXP288_ADC_TS_CURRENT_ON_ONDEMAND)
+ usleep_range(6000, 10000);
+
+ return 0;
+}
+
+static int axp288_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct axp288_adc_info *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON_ONDEMAND,
+ chan->address)) {
+ dev_err(&indio_dev->dev, "GPADC mode\n");
+ ret = -EINVAL;
+ break;
+ }
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON,
+ chan->address))
+ dev_err(&indio_dev->dev, "TS pin restore\n");
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+/*
+ * We rely on the machine's firmware to correctly setup the TS pin bias current
+ * at boot. This lists systems with broken fw where we need to set it ourselves.
+ */
+static const struct dmi_system_id axp288_adc_ts_bias_override[] = {
+ {
+ /* Lenovo Ideapad 100S (11 inch) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 100S-11IBY"),
+ },
+ .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
+ },
+ {
+ /* Nuvision Solo 10 Draw */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TMAX"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TM101W610L"),
+ },
+ .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
+ },
+ {}
+};
+
+static int axp288_adc_initialize(struct axp288_adc_info *info)
+{
+ const struct dmi_system_id *bias_override;
+ int ret, adc_enable_val;
+
+ bias_override = dmi_first_match(axp288_adc_ts_bias_override);
+ if (bias_override) {
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_BIAS_MASK,
+ (uintptr_t)bias_override->driver_data);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Determine if the TS pin is enabled and set the TS current-source
+ * accordingly.
+ */
+ ret = regmap_read(info->regmap, AXP20X_ADC_EN1, &adc_enable_val);
+ if (ret)
+ return ret;
+
+ if (adc_enable_val & AXP288_ADC_TS_ENABLE) {
+ info->ts_enabled = true;
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_ON);
+ } else {
+ info->ts_enabled = false;
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_OFF);
+ }
+ if (ret)
+ return ret;
+
+ /* Turn on the ADC for all channels except TS, leave TS as is */
+ return regmap_update_bits(info->regmap, AXP20X_ADC_EN1,
+ AXP288_ADC_EN_MASK, AXP288_ADC_EN_MASK);
+}
+
+static const struct iio_info axp288_adc_iio_info = {
+ .read_raw = &axp288_adc_read_raw,
+};
+
+static int axp288_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct axp288_adc_info *info;
+ struct iio_dev *indio_dev;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0)
+ return info->irq;
+
+ info->regmap = axp20x->regmap;
+ /*
+ * Set ADC to enabled state at all time, including system suspend.
+ * otherwise internal fuel gauge functionality may be affected.
+ */
+ ret = axp288_adc_initialize(info);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable ADC device\n");
+ return ret;
+ }
+
+ indio_dev->name = pdev->name;
+ indio_dev->channels = axp288_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
+ indio_dev->info = &axp288_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_map_array_register(&pdev->dev, indio_dev, axp288_adc_default_maps);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct platform_device_id axp288_adc_id_table[] = {
+ { .name = "axp288_adc" },
+ {},
+};
+
+static struct platform_driver axp288_adc_driver = {
+ .probe = axp288_adc_probe,
+ .id_table = axp288_adc_id_table,
+ .driver = {
+ .name = "axp288_adc",
+ },
+};
+
+MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
+
+module_platform_driver(axp288_adc_driver);
+
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
+MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c
new file mode 100644
index 000000000..44e1e53ad
--- /dev/null
+++ b/drivers/iio/adc/bcm_iproc_adc.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2016 Broadcom
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/iio.h>
+
+/* Below Register's are common to IPROC ADC and Touchscreen IP */
+#define IPROC_REGCTL1 0x00
+#define IPROC_REGCTL2 0x04
+#define IPROC_INTERRUPT_THRES 0x08
+#define IPROC_INTERRUPT_MASK 0x0c
+#define IPROC_INTERRUPT_STATUS 0x10
+#define IPROC_ANALOG_CONTROL 0x1c
+#define IPROC_CONTROLLER_STATUS 0x14
+#define IPROC_AUX_DATA 0x20
+#define IPROC_SOFT_BYPASS_CONTROL 0x38
+#define IPROC_SOFT_BYPASS_DATA 0x3C
+
+/* IPROC ADC Channel register offsets */
+#define IPROC_ADC_CHANNEL_REGCTL1 0x800
+#define IPROC_ADC_CHANNEL_REGCTL2 0x804
+#define IPROC_ADC_CHANNEL_STATUS 0x808
+#define IPROC_ADC_CHANNEL_INTERRUPT_STATUS 0x80c
+#define IPROC_ADC_CHANNEL_INTERRUPT_MASK 0x810
+#define IPROC_ADC_CHANNEL_DATA 0x814
+#define IPROC_ADC_CHANNEL_OFFSET 0x20
+
+/* Bit definitions for IPROC_REGCTL2 */
+#define IPROC_ADC_AUXIN_SCAN_ENA BIT(0)
+#define IPROC_ADC_PWR_LDO BIT(5)
+#define IPROC_ADC_PWR_ADC BIT(4)
+#define IPROC_ADC_PWR_BG BIT(3)
+#define IPROC_ADC_CONTROLLER_EN BIT(17)
+
+/* Bit definitions for IPROC_INTERRUPT_MASK and IPROC_INTERRUPT_STATUS */
+#define IPROC_ADC_AUXDATA_RDY_INTR BIT(3)
+#define IPROC_ADC_INTR 9
+#define IPROC_ADC_INTR_MASK (0xFF << IPROC_ADC_INTR)
+
+/* Bit definitions for IPROC_ANALOG_CONTROL */
+#define IPROC_ADC_CHANNEL_SEL 11
+#define IPROC_ADC_CHANNEL_SEL_MASK (0x7 << IPROC_ADC_CHANNEL_SEL)
+
+/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL1 */
+#define IPROC_ADC_CHANNEL_ROUNDS 0x2
+#define IPROC_ADC_CHANNEL_ROUNDS_MASK (0x3F << IPROC_ADC_CHANNEL_ROUNDS)
+#define IPROC_ADC_CHANNEL_MODE 0x1
+#define IPROC_ADC_CHANNEL_MODE_MASK (0x1 << IPROC_ADC_CHANNEL_MODE)
+#define IPROC_ADC_CHANNEL_MODE_TDM 0x1
+#define IPROC_ADC_CHANNEL_MODE_SNAPSHOT 0x0
+#define IPROC_ADC_CHANNEL_ENABLE 0x0
+#define IPROC_ADC_CHANNEL_ENABLE_MASK 0x1
+
+/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL2 */
+#define IPROC_ADC_CHANNEL_WATERMARK 0x0
+#define IPROC_ADC_CHANNEL_WATERMARK_MASK \
+ (0x3F << IPROC_ADC_CHANNEL_WATERMARK)
+
+#define IPROC_ADC_WATER_MARK_LEVEL 0x1
+
+/* Bit definitions for IPROC_ADC_CHANNEL_STATUS */
+#define IPROC_ADC_CHANNEL_DATA_LOST 0x0
+#define IPROC_ADC_CHANNEL_DATA_LOST_MASK \
+ (0x0 << IPROC_ADC_CHANNEL_DATA_LOST)
+#define IPROC_ADC_CHANNEL_VALID_ENTERIES 0x1
+#define IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK \
+ (0xFF << IPROC_ADC_CHANNEL_VALID_ENTERIES)
+#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES 0x9
+#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES_MASK \
+ (0xFF << IPROC_ADC_CHANNEL_TOTAL_ENTERIES)
+
+/* Bit definitions for IPROC_ADC_CHANNEL_INTERRUPT_MASK */
+#define IPROC_ADC_CHANNEL_WTRMRK_INTR 0x0
+#define IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK \
+ (0x1 << IPROC_ADC_CHANNEL_WTRMRK_INTR)
+#define IPROC_ADC_CHANNEL_FULL_INTR 0x1
+#define IPROC_ADC_CHANNEL_FULL_INTR_MASK \
+ (0x1 << IPROC_ADC_IPROC_ADC_CHANNEL_FULL_INTR)
+#define IPROC_ADC_CHANNEL_EMPTY_INTR 0x2
+#define IPROC_ADC_CHANNEL_EMPTY_INTR_MASK \
+ (0x1 << IPROC_ADC_CHANNEL_EMPTY_INTR)
+
+#define IPROC_ADC_WATER_MARK_INTR_ENABLE 0x1
+
+/* Number of time to retry a set of the interrupt mask reg */
+#define IPROC_ADC_INTMASK_RETRY_ATTEMPTS 10
+
+#define IPROC_ADC_READ_TIMEOUT (HZ*2)
+
+#define iproc_adc_dbg_reg(dev, priv, reg) \
+do { \
+ u32 val; \
+ regmap_read(priv->regmap, reg, &val); \
+ dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \
+} while (0)
+
+struct iproc_adc_priv {
+ struct regmap *regmap;
+ struct clk *adc_clk;
+ struct mutex mutex;
+ int irqno;
+ int chan_val;
+ int chan_id;
+ struct completion completion;
+};
+
+static void iproc_adc_reg_dump(struct iio_dev *indio_dev)
+{
+ struct device *dev = &indio_dev->dev;
+ struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
+
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL1);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL2);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_THRES);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_MASK);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_STATUS);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_CONTROLLER_STATUS);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_ANALOG_CONTROL);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_AUX_DATA);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_CONTROL);
+ iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_DATA);
+}
+
+static irqreturn_t iproc_adc_interrupt_thread(int irq, void *data)
+{
+ u32 channel_intr_status;
+ u32 intr_status;
+ u32 intr_mask;
+ struct iio_dev *indio_dev = data;
+ struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
+
+ /*
+ * This interrupt is shared with the touchscreen driver.
+ * Make sure this interrupt is intended for us.
+ * Handle only ADC channel specific interrupts.
+ */
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status);
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &intr_mask);
+ intr_status = intr_status & intr_mask;
+ channel_intr_status = (intr_status & IPROC_ADC_INTR_MASK) >>
+ IPROC_ADC_INTR;
+ if (channel_intr_status)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t iproc_adc_interrupt_handler(int irq, void *data)
+{
+ irqreturn_t retval = IRQ_NONE;
+ struct iproc_adc_priv *adc_priv;
+ struct iio_dev *indio_dev = data;
+ unsigned int valid_entries;
+ u32 intr_status;
+ u32 intr_channels;
+ u32 channel_status;
+ u32 ch_intr_status;
+
+ adc_priv = iio_priv(indio_dev);
+
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status);
+ dev_dbg(&indio_dev->dev, "iproc_adc_interrupt_handler(),INTRPT_STS:%x\n",
+ intr_status);
+
+ intr_channels = (intr_status & IPROC_ADC_INTR_MASK) >> IPROC_ADC_INTR;
+ if (intr_channels) {
+ regmap_read(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_INTERRUPT_STATUS +
+ IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id,
+ &ch_intr_status);
+
+ if (ch_intr_status & IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK) {
+ regmap_read(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_STATUS +
+ IPROC_ADC_CHANNEL_OFFSET *
+ adc_priv->chan_id,
+ &channel_status);
+
+ valid_entries = ((channel_status &
+ IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK) >>
+ IPROC_ADC_CHANNEL_VALID_ENTERIES);
+ if (valid_entries >= 1) {
+ regmap_read(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_DATA +
+ IPROC_ADC_CHANNEL_OFFSET *
+ adc_priv->chan_id,
+ &adc_priv->chan_val);
+ complete(&adc_priv->completion);
+ } else {
+ dev_err(&indio_dev->dev,
+ "No data rcvd on channel %d\n",
+ adc_priv->chan_id);
+ }
+ regmap_write(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_INTERRUPT_MASK +
+ IPROC_ADC_CHANNEL_OFFSET *
+ adc_priv->chan_id,
+ (ch_intr_status &
+ ~(IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK)));
+ }
+ regmap_write(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_INTERRUPT_STATUS +
+ IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id,
+ ch_intr_status);
+ regmap_write(adc_priv->regmap, IPROC_INTERRUPT_STATUS,
+ intr_channels);
+ retval = IRQ_HANDLED;
+ }
+
+ return retval;
+}
+
+static int iproc_adc_do_read(struct iio_dev *indio_dev,
+ int channel,
+ u16 *p_adc_data)
+{
+ int read_len = 0;
+ u32 val;
+ u32 mask;
+ u32 val_check;
+ int failed_cnt = 0;
+ struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
+
+ mutex_lock(&adc_priv->mutex);
+
+ /*
+ * After a read is complete the ADC interrupts will be disabled so
+ * we can assume this section of code is safe from interrupts.
+ */
+ adc_priv->chan_val = -1;
+ adc_priv->chan_id = channel;
+
+ reinit_completion(&adc_priv->completion);
+ /* Clear any pending interrupt */
+ regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS,
+ IPROC_ADC_INTR_MASK | IPROC_ADC_AUXDATA_RDY_INTR,
+ ((0x0 << channel) << IPROC_ADC_INTR) |
+ IPROC_ADC_AUXDATA_RDY_INTR);
+
+ /* Configure channel for snapshot mode and enable */
+ val = (BIT(IPROC_ADC_CHANNEL_ROUNDS) |
+ (IPROC_ADC_CHANNEL_MODE_SNAPSHOT << IPROC_ADC_CHANNEL_MODE) |
+ (0x1 << IPROC_ADC_CHANNEL_ENABLE));
+
+ mask = IPROC_ADC_CHANNEL_ROUNDS_MASK | IPROC_ADC_CHANNEL_MODE_MASK |
+ IPROC_ADC_CHANNEL_ENABLE_MASK;
+ regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL1 +
+ IPROC_ADC_CHANNEL_OFFSET * channel),
+ mask, val);
+
+ /* Set the Watermark for a channel */
+ regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL2 +
+ IPROC_ADC_CHANNEL_OFFSET * channel),
+ IPROC_ADC_CHANNEL_WATERMARK_MASK,
+ 0x1);
+
+ /* Enable water mark interrupt */
+ regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_INTERRUPT_MASK +
+ IPROC_ADC_CHANNEL_OFFSET *
+ channel),
+ IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK,
+ IPROC_ADC_WATER_MARK_INTR_ENABLE);
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val);
+
+ /* Enable ADC interrupt for a channel */
+ val |= (BIT(channel) << IPROC_ADC_INTR);
+ regmap_write(adc_priv->regmap, IPROC_INTERRUPT_MASK, val);
+
+ /*
+ * There seems to be a very rare issue where writing to this register
+ * does not take effect. To work around the issue we will try multiple
+ * writes. In total we will spend about 10*10 = 100 us attempting this.
+ * Testing has shown that this may loop a few time, but we have never
+ * hit the full count.
+ */
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check);
+ while (val_check != val) {
+ failed_cnt++;
+
+ if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS)
+ break;
+
+ udelay(10);
+ regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK,
+ IPROC_ADC_INTR_MASK,
+ ((0x1 << channel) <<
+ IPROC_ADC_INTR));
+
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check);
+ }
+
+ if (failed_cnt) {
+ dev_dbg(&indio_dev->dev,
+ "IntMask failed (%d times)", failed_cnt);
+ if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) {
+ dev_err(&indio_dev->dev,
+ "IntMask set failed. Read will likely fail.");
+ read_len = -EIO;
+ goto adc_err;
+ }
+ }
+ regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check);
+
+ if (wait_for_completion_timeout(&adc_priv->completion,
+ IPROC_ADC_READ_TIMEOUT) > 0) {
+
+ /* Only the lower 16 bits are relevant */
+ *p_adc_data = adc_priv->chan_val & 0xFFFF;
+ read_len = sizeof(*p_adc_data);
+
+ } else {
+ /*
+ * We never got the interrupt, something went wrong.
+ * Perhaps the interrupt may still be coming, we do not want
+ * that now. Lets disable the ADC interrupt, and clear the
+ * status to put it back in to normal state.
+ */
+ read_len = -ETIMEDOUT;
+ goto adc_err;
+ }
+ mutex_unlock(&adc_priv->mutex);
+
+ return read_len;
+
+adc_err:
+ regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK,
+ IPROC_ADC_INTR_MASK,
+ ((0x0 << channel) << IPROC_ADC_INTR));
+
+ regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS,
+ IPROC_ADC_INTR_MASK,
+ ((0x0 << channel) << IPROC_ADC_INTR));
+
+ dev_err(&indio_dev->dev, "Timed out waiting for ADC data!\n");
+ iproc_adc_reg_dump(indio_dev);
+ mutex_unlock(&adc_priv->mutex);
+
+ return read_len;
+}
+
+static int iproc_adc_enable(struct iio_dev *indio_dev)
+{
+ u32 val;
+ u32 channel_id;
+ struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
+ int ret;
+
+ /* Set i_amux = 3b'000, select channel 0 */
+ ret = regmap_update_bits(adc_priv->regmap, IPROC_ANALOG_CONTROL,
+ IPROC_ADC_CHANNEL_SEL_MASK, 0);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to write IPROC_ANALOG_CONTROL %d\n", ret);
+ return ret;
+ }
+ adc_priv->chan_val = -1;
+
+ /*
+ * PWR up LDO, ADC, and Band Gap (0 to enable)
+ * Also enable ADC controller (set high)
+ */
+ ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to read IPROC_REGCTL2 %d\n", ret);
+ return ret;
+ }
+
+ val &= ~(IPROC_ADC_PWR_LDO | IPROC_ADC_PWR_ADC | IPROC_ADC_PWR_BG);
+
+ ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to write IPROC_REGCTL2 %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to read IPROC_REGCTL2 %d\n", ret);
+ return ret;
+ }
+
+ val |= IPROC_ADC_CONTROLLER_EN;
+ ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to write IPROC_REGCTL2 %d\n", ret);
+ return ret;
+ }
+
+ for (channel_id = 0; channel_id < indio_dev->num_channels;
+ channel_id++) {
+ ret = regmap_write(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_INTERRUPT_MASK +
+ IPROC_ADC_CHANNEL_OFFSET * channel_id, 0);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to write ADC_CHANNEL_INTERRUPT_MASK %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regmap_write(adc_priv->regmap,
+ IPROC_ADC_CHANNEL_INTERRUPT_STATUS +
+ IPROC_ADC_CHANNEL_OFFSET * channel_id, 0);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to write ADC_CHANNEL_INTERRUPT_STATUS %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void iproc_adc_disable(struct iio_dev *indio_dev)
+{
+ u32 val;
+ int ret;
+ struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
+
+ ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to read IPROC_REGCTL2 %d\n", ret);
+ return;
+ }
+
+ val &= ~IPROC_ADC_CONTROLLER_EN;
+ ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val);
+ if (ret) {
+ dev_err(&indio_dev->dev,
+ "failed to write IPROC_REGCTL2 %d\n", ret);
+ return;
+ }
+}
+
+static int iproc_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ u16 adc_data;
+ int err;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ err = iproc_adc_do_read(indio_dev, chan->channel, &adc_data);
+ if (err < 0)
+ return err;
+ *val = adc_data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = 1800;
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info iproc_adc_iio_info = {
+ .read_raw = &iproc_adc_read_raw,
+};
+
+#define IPROC_ADC_CHANNEL(_index, _id) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _id, \
+}
+
+static const struct iio_chan_spec iproc_adc_iio_channels[] = {
+ IPROC_ADC_CHANNEL(0, "adc0"),
+ IPROC_ADC_CHANNEL(1, "adc1"),
+ IPROC_ADC_CHANNEL(2, "adc2"),
+ IPROC_ADC_CHANNEL(3, "adc3"),
+ IPROC_ADC_CHANNEL(4, "adc4"),
+ IPROC_ADC_CHANNEL(5, "adc5"),
+ IPROC_ADC_CHANNEL(6, "adc6"),
+ IPROC_ADC_CHANNEL(7, "adc7"),
+};
+
+static int iproc_adc_probe(struct platform_device *pdev)
+{
+ struct iproc_adc_priv *adc_priv;
+ struct iio_dev *indio_dev = NULL;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(*adc_priv));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ adc_priv = iio_priv(indio_dev);
+ platform_set_drvdata(pdev, indio_dev);
+
+ mutex_init(&adc_priv->mutex);
+
+ init_completion(&adc_priv->completion);
+
+ adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "adc-syscon");
+ if (IS_ERR(adc_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to get handle for tsc syscon\n");
+ ret = PTR_ERR(adc_priv->regmap);
+ return ret;
+ }
+
+ adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
+ if (IS_ERR(adc_priv->adc_clk)) {
+ dev_err(&pdev->dev,
+ "failed getting clock tsc_clk\n");
+ ret = PTR_ERR(adc_priv->adc_clk);
+ return ret;
+ }
+
+ adc_priv->irqno = platform_get_irq(pdev, 0);
+ if (adc_priv->irqno <= 0)
+ return -ENODEV;
+
+ ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
+ IPROC_ADC_AUXIN_SCAN_ENA, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno,
+ iproc_adc_interrupt_handler,
+ iproc_adc_interrupt_thread,
+ IRQF_SHARED, "iproc-adc", indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq error %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(adc_priv->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "clk_prepare_enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = iproc_adc_enable(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable adc %d\n", ret);
+ goto err_adc_enable;
+ }
+
+ indio_dev->name = "iproc-static-adc";
+ indio_dev->info = &iproc_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = iproc_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iproc_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio_device_register failed:err %d\n", ret);
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ iproc_adc_disable(indio_dev);
+err_adc_enable:
+ clk_disable_unprepare(adc_priv->adc_clk);
+
+ return ret;
+}
+
+static int iproc_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct iproc_adc_priv *adc_priv = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iproc_adc_disable(indio_dev);
+ clk_disable_unprepare(adc_priv->adc_clk);
+
+ return 0;
+}
+
+static const struct of_device_id iproc_adc_of_match[] = {
+ {.compatible = "brcm,iproc-static-adc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, iproc_adc_of_match);
+
+static struct platform_driver iproc_adc_driver = {
+ .probe = iproc_adc_probe,
+ .remove = iproc_adc_remove,
+ .driver = {
+ .name = "iproc-static-adc",
+ .of_match_table = iproc_adc_of_match,
+ },
+};
+module_platform_driver(iproc_adc_driver);
+
+MODULE_DESCRIPTION("Broadcom iProc ADC controller driver");
+MODULE_AUTHOR("Raveendra Padasalagi <raveendra.padasalagi@broadcom.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c
new file mode 100644
index 000000000..a4e7c7eff
--- /dev/null
+++ b/drivers/iio/adc/berlin2-adc.c
@@ -0,0 +1,371 @@
+/*
+ * Marvell Berlin2 ADC driver
+ *
+ * Copyright (C) 2015 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#define BERLIN2_SM_CTRL 0x14
+#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
+#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
+#define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */
+#define BERLIN2_SM_CTRL_ADC_SEL_MASK GENMASK(8, 5)
+#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK GENMASK(11, 10)
+#define BERLIN2_SM_CTRL_ADC_START BIT(12)
+#define BERLIN2_SM_CTRL_ADC_RESET BIT(13)
+#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14)
+#define BERLIN2_SM_CTRL_ADC_CONT_SINGLE (0x0 << 15)
+#define BERLIN2_SM_CTRL_ADC_CONT_CONTINUOUS (0x1 << 15)
+#define BERLIN2_SM_CTRL_ADC_BUFFER_EN BIT(16)
+#define BERLIN2_SM_CTRL_ADC_VREF_EXT (0x0 << 17)
+#define BERLIN2_SM_CTRL_ADC_VREF_INT (0x1 << 17)
+#define BERLIN2_SM_CTRL_ADC_ROTATE BIT(19)
+#define BERLIN2_SM_CTRL_TSEN_EN BIT(20)
+#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_125 (0x0 << 21) /* 1.25 MHz */
+#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_250 (0x1 << 21) /* 2.5 MHz */
+#define BERLIN2_SM_CTRL_TSEN_MODE_0_125 (0x0 << 22) /* 0-125 C */
+#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */
+#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29)
+#define BERLIN2_SM_ADC_DATA 0x20
+#define BERLIN2_SM_ADC_MASK GENMASK(9, 0)
+#define BERLIN2_SM_ADC_STATUS 0x1c
+#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
+#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0)
+#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */
+#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK GENMASK(31, 16)
+#define BERLIN2_SM_TSEN_STATUS 0x24
+#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0)
+#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1)
+#define BERLIN2_SM_TSEN_DATA 0x28
+#define BERLIN2_SM_TSEN_MASK GENMASK(9, 0)
+#define BERLIN2_SM_TSEN_CTRL 0x74
+#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK BIT(21)
+#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22)
+#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK GENMASK(25, 22)
+
+struct berlin2_adc_priv {
+ struct regmap *regmap;
+ struct mutex lock;
+ wait_queue_head_t wq;
+ bool data_available;
+ int data;
+};
+
+#define BERLIN2_ADC_CHANNEL(n, t) \
+ { \
+ .channel = n, \
+ .datasheet_name = "channel"#n, \
+ .type = t, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ }
+
+static const struct iio_chan_spec berlin2_adc_channels[] = {
+ BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(3, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(4, IIO_VOLTAGE), /* reserved */
+ BERLIN2_ADC_CHANNEL(5, IIO_VOLTAGE), /* reserved */
+ { /* temperature sensor */
+ .channel = 6,
+ .datasheet_name = "channel6",
+ .type = IIO_TEMP,
+ .indexed = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */
+ IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */
+};
+
+static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
+{
+ struct berlin2_adc_priv *priv = iio_priv(indio_dev);
+ int data, ret;
+
+ mutex_lock(&priv->lock);
+
+ /* Enable the interrupts */
+ regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
+ BERLIN2_SM_ADC_STATUS_INT_EN(channel));
+
+ /* Configure the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_RESET |
+ BERLIN2_SM_CTRL_ADC_SEL_MASK |
+ BERLIN2_SM_CTRL_ADC_START,
+ BERLIN2_SM_CTRL_ADC_SEL(channel) |
+ BERLIN2_SM_CTRL_ADC_START);
+
+ ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
+ msecs_to_jiffies(1000));
+
+ /* Disable the interrupts */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
+ BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
+
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_START, 0);
+
+ data = priv->data;
+ priv->data_available = false;
+
+ mutex_unlock(&priv->lock);
+
+ return data;
+}
+
+static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
+{
+ struct berlin2_adc_priv *priv = iio_priv(indio_dev);
+ int data, ret;
+
+ mutex_lock(&priv->lock);
+
+ /* Enable interrupts */
+ regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
+ BERLIN2_SM_TSEN_STATUS_INT_EN);
+
+ /* Configure the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_TSEN_RESET |
+ BERLIN2_SM_CTRL_ADC_ROTATE,
+ BERLIN2_SM_CTRL_ADC_ROTATE);
+
+ /* Configure the temperature sensor */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
+ BERLIN2_SM_TSEN_CTRL_TRIM_MASK |
+ BERLIN2_SM_TSEN_CTRL_SETTLING_MASK |
+ BERLIN2_SM_TSEN_CTRL_START,
+ BERLIN2_SM_TSEN_CTRL_TRIM(3) |
+ BERLIN2_SM_TSEN_CTRL_SETTLING_12 |
+ BERLIN2_SM_TSEN_CTRL_START);
+
+ ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
+ msecs_to_jiffies(1000));
+
+ /* Disable interrupts */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
+ BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
+
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
+ BERLIN2_SM_TSEN_CTRL_START, 0);
+
+ data = priv->data;
+ priv->data_available = false;
+
+ mutex_unlock(&priv->lock);
+
+ return data;
+}
+
+static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int temp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ *val = berlin2_adc_read(indio_dev, chan->channel);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+
+ temp = berlin2_adc_tsen_read(indio_dev);
+ if (temp < 0)
+ return temp;
+
+ if (temp > 2047)
+ temp -= 4096;
+
+ /* Convert to milli Celsius */
+ *val = ((temp * 100000) / 264 - 270000);
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t berlin2_adc_irq(int irq, void *private)
+{
+ struct berlin2_adc_priv *priv = iio_priv(private);
+ unsigned val;
+
+ regmap_read(priv->regmap, BERLIN2_SM_ADC_STATUS, &val);
+ if (val & BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK) {
+ regmap_read(priv->regmap, BERLIN2_SM_ADC_DATA, &priv->data);
+ priv->data &= BERLIN2_SM_ADC_MASK;
+
+ val &= ~BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK;
+ regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, val);
+
+ priv->data_available = true;
+ wake_up_interruptible(&priv->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t berlin2_adc_tsen_irq(int irq, void *private)
+{
+ struct berlin2_adc_priv *priv = iio_priv(private);
+ unsigned val;
+
+ regmap_read(priv->regmap, BERLIN2_SM_TSEN_STATUS, &val);
+ if (val & BERLIN2_SM_TSEN_STATUS_DATA_RDY) {
+ regmap_read(priv->regmap, BERLIN2_SM_TSEN_DATA, &priv->data);
+ priv->data &= BERLIN2_SM_TSEN_MASK;
+
+ val &= ~BERLIN2_SM_TSEN_STATUS_DATA_RDY;
+ regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, val);
+
+ priv->data_available = true;
+ wake_up_interruptible(&priv->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info berlin2_adc_info = {
+ .read_raw = berlin2_adc_read_raw,
+};
+
+static void berlin2_adc_powerdown(void *regmap)
+{
+ regmap_update_bits(regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_POWER, 0);
+
+}
+
+static int berlin2_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct berlin2_adc_priv *priv;
+ struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
+ int irq, tsen_irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!indio_dev) {
+ of_node_put(parent_np);
+ return -ENOMEM;
+ }
+
+ priv = iio_priv(indio_dev);
+
+ priv->regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ irq = platform_get_irq_byname(pdev, "adc");
+ if (irq < 0)
+ return irq;
+
+ tsen_irq = platform_get_irq_byname(pdev, "tsen");
+ if (tsen_irq < 0)
+ return tsen_irq;
+
+ ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
+ pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
+ 0, pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ init_waitqueue_head(&priv->wq);
+ mutex_init(&priv->lock);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &berlin2_adc_info;
+
+ indio_dev->channels = berlin2_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(berlin2_adc_channels);
+
+ /* Power up the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_POWER,
+ BERLIN2_SM_CTRL_ADC_POWER);
+
+ ret = devm_add_action_or_reset(&pdev->dev, berlin2_adc_powerdown,
+ priv->regmap);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id berlin2_adc_match[] = {
+ { .compatible = "marvell,berlin2-adc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, berlin2_adc_match);
+
+static struct platform_driver berlin2_adc_driver = {
+ .driver = {
+ .name = "berlin2-adc",
+ .of_match_table = berlin2_adc_match,
+ },
+ .probe = berlin2_adc_probe,
+};
+module_platform_driver(berlin2_adc_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell Berlin2 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
new file mode 100644
index 000000000..e16ac9356
--- /dev/null
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -0,0 +1,439 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2015 Imagination Technologies Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Registers */
+#define CC10001_ADC_CONFIG 0x00
+#define CC10001_ADC_START_CONV BIT(4)
+#define CC10001_ADC_MODE_SINGLE_CONV BIT(5)
+
+#define CC10001_ADC_DDATA_OUT 0x04
+#define CC10001_ADC_EOC 0x08
+#define CC10001_ADC_EOC_SET BIT(0)
+
+#define CC10001_ADC_CHSEL_SAMPLED 0x0c
+#define CC10001_ADC_POWER_DOWN 0x10
+#define CC10001_ADC_POWER_DOWN_SET BIT(0)
+
+#define CC10001_ADC_DEBUG 0x14
+#define CC10001_ADC_DATA_COUNT 0x20
+
+#define CC10001_ADC_DATA_MASK GENMASK(9, 0)
+#define CC10001_ADC_NUM_CHANNELS 8
+#define CC10001_ADC_CH_MASK GENMASK(2, 0)
+
+#define CC10001_INVALID_SAMPLED 0xffff
+#define CC10001_MAX_POLL_COUNT 20
+
+/*
+ * As per device specification, wait six clock cycles after power-up to
+ * activate START. Since adding two more clock cycles delay does not
+ * impact the performance too much, we are adding two additional cycles delay
+ * intentionally here.
+ */
+#define CC10001_WAIT_CYCLES 8
+
+struct cc10001_adc_device {
+ void __iomem *reg_base;
+ struct clk *adc_clk;
+ struct regulator *reg;
+ u16 *buf;
+
+ bool shared;
+ struct mutex lock;
+ unsigned int start_delay_ns;
+ unsigned int eoc_delay_ns;
+};
+
+static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev,
+ u32 reg, u32 val)
+{
+ writel(val, adc_dev->reg_base + reg);
+}
+
+static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
+ u32 reg)
+{
+ return readl(adc_dev->reg_base + reg);
+}
+
+static void cc10001_adc_power_up(struct cc10001_adc_device *adc_dev)
+{
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, 0);
+ ndelay(adc_dev->start_delay_ns);
+}
+
+static void cc10001_adc_power_down(struct cc10001_adc_device *adc_dev)
+{
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN,
+ CC10001_ADC_POWER_DOWN_SET);
+}
+
+static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
+ unsigned int channel)
+{
+ u32 val;
+
+ /* Channel selection and mode of operation */
+ val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+
+ udelay(1);
+ val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
+ val = val | CC10001_ADC_START_CONV;
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+}
+
+static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev,
+ unsigned int channel,
+ unsigned int delay)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int poll_count = 0;
+
+ while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) &
+ CC10001_ADC_EOC_SET)) {
+
+ ndelay(delay);
+ if (poll_count++ == CC10001_MAX_POLL_COUNT)
+ return CC10001_INVALID_SAMPLED;
+ }
+
+ poll_count = 0;
+ while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) &
+ CC10001_ADC_CH_MASK) != channel) {
+
+ ndelay(delay);
+ if (poll_count++ == CC10001_MAX_POLL_COUNT)
+ return CC10001_INVALID_SAMPLED;
+ }
+
+ /* Read the 10 bit output register */
+ return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) &
+ CC10001_ADC_DATA_MASK;
+}
+
+static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
+{
+ struct cc10001_adc_device *adc_dev;
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev;
+ unsigned int delay_ns;
+ unsigned int channel;
+ unsigned int scan_idx;
+ bool sample_invalid;
+ u16 *data;
+ int i;
+
+ indio_dev = pf->indio_dev;
+ adc_dev = iio_priv(indio_dev);
+ data = adc_dev->buf;
+
+ mutex_lock(&adc_dev->lock);
+
+ if (!adc_dev->shared)
+ cc10001_adc_power_up(adc_dev);
+
+ /* Calculate delay step for eoc and sampled data */
+ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
+
+ i = 0;
+ sample_invalid = false;
+ for_each_set_bit(scan_idx, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+
+ channel = indio_dev->channels[scan_idx].channel;
+ cc10001_adc_start(adc_dev, channel);
+
+ data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
+ if (data[i] == CC10001_INVALID_SAMPLED) {
+ dev_warn(&indio_dev->dev,
+ "invalid sample on channel %d\n", channel);
+ sample_invalid = true;
+ goto done;
+ }
+ i++;
+ }
+
+done:
+ if (!adc_dev->shared)
+ cc10001_adc_power_down(adc_dev);
+
+ mutex_unlock(&adc_dev->lock);
+
+ if (!sample_invalid)
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int delay_ns;
+ u16 val;
+
+ if (!adc_dev->shared)
+ cc10001_adc_power_up(adc_dev);
+
+ /* Calculate delay step for eoc and sampled data */
+ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
+
+ cc10001_adc_start(adc_dev, chan->channel);
+
+ val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
+
+ if (!adc_dev->shared)
+ cc10001_adc_power_down(adc_dev);
+
+ return val;
+}
+
+static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&adc_dev->lock);
+ *val = cc10001_adc_read_raw_voltage(indio_dev, chan);
+ mutex_unlock(&adc_dev->lock);
+
+ if (*val == CC10001_INVALID_SAMPLED)
+ return -EIO;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc_dev->reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cc10001_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+
+ kfree(adc_dev->buf);
+ adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!adc_dev->buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct iio_info cc10001_adc_info = {
+ .read_raw = &cc10001_adc_read_raw,
+ .update_scan_mode = &cc10001_update_scan_mode,
+};
+
+static int cc10001_adc_channel_init(struct iio_dev *indio_dev,
+ unsigned long channel_map)
+{
+ struct iio_chan_spec *chan_array, *timestamp;
+ unsigned int bit, idx = 0;
+
+ indio_dev->num_channels = bitmap_weight(&channel_map,
+ CC10001_ADC_NUM_CHANNELS) + 1;
+
+ chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels,
+ sizeof(struct iio_chan_spec),
+ GFP_KERNEL);
+ if (!chan_array)
+ return -ENOMEM;
+
+ for_each_set_bit(bit, &channel_map, CC10001_ADC_NUM_CHANNELS) {
+ struct iio_chan_spec *chan = &chan_array[idx];
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = bit;
+ chan->scan_index = idx;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 10;
+ chan->scan_type.storagebits = 16;
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ idx++;
+ }
+
+ timestamp = &chan_array[idx];
+ timestamp->type = IIO_TIMESTAMP;
+ timestamp->channel = -1;
+ timestamp->scan_index = idx;
+ timestamp->scan_type.sign = 's';
+ timestamp->scan_type.realbits = 64;
+ timestamp->scan_type.storagebits = 64;
+
+ indio_dev->channels = chan_array;
+
+ return 0;
+}
+
+static int cc10001_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct cc10001_adc_device *adc_dev;
+ unsigned long adc_clk_rate;
+ struct iio_dev *indio_dev;
+ unsigned long channel_map;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ adc_dev = iio_priv(indio_dev);
+
+ channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
+ if (!of_property_read_u32(node, "adc-reserved-channels", &ret)) {
+ adc_dev->shared = true;
+ channel_map &= ~ret;
+ }
+
+ adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(adc_dev->reg))
+ return PTR_ERR(adc_dev->reg);
+
+ ret = regulator_enable(adc_dev->reg);
+ if (ret)
+ return ret;
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &cc10001_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc_dev->reg_base)) {
+ ret = PTR_ERR(adc_dev->reg_base);
+ goto err_disable_reg;
+ }
+
+ adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(adc_dev->adc_clk)) {
+ dev_err(&pdev->dev, "failed to get the clock\n");
+ ret = PTR_ERR(adc_dev->adc_clk);
+ goto err_disable_reg;
+ }
+
+ ret = clk_prepare_enable(adc_dev->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable the clock\n");
+ goto err_disable_reg;
+ }
+
+ adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
+ if (!adc_clk_rate) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "null clock rate!\n");
+ goto err_disable_clk;
+ }
+
+ adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
+ adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
+
+ /*
+ * There is only one register to power-up/power-down the AUX ADC.
+ * If the ADC is shared among multiple CPUs, always power it up here.
+ * If the ADC is used only by the MIPS, power-up/power-down at runtime.
+ */
+ if (adc_dev->shared)
+ cc10001_adc_power_up(adc_dev);
+
+ /* Setup the ADC channels available on the device */
+ ret = cc10001_adc_channel_init(indio_dev, channel_map);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ mutex_init(&adc_dev->lock);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &cc10001_adc_trigger_h, NULL);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err_cleanup_buffer;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+
+err_cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_disable_clk:
+ clk_disable_unprepare(adc_dev->adc_clk);
+err_disable_reg:
+ regulator_disable(adc_dev->reg);
+ return ret;
+}
+
+static int cc10001_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+
+ cc10001_adc_power_down(adc_dev);
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ clk_disable_unprepare(adc_dev->adc_clk);
+ regulator_disable(adc_dev->reg);
+
+ return 0;
+}
+
+static const struct of_device_id cc10001_adc_dt_ids[] = {
+ { .compatible = "cosmic,10001-adc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids);
+
+static struct platform_driver cc10001_adc_driver = {
+ .driver = {
+ .name = "cc10001-adc",
+ .of_match_table = cc10001_adc_dt_ids,
+ },
+ .probe = cc10001_adc_probe,
+ .remove = cc10001_adc_remove,
+};
+module_platform_driver(cc10001_adc_driver);
+
+MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>");
+MODULE_DESCRIPTION("Cosmic Circuits ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c
new file mode 100644
index 000000000..b6c4ef704
--- /dev/null
+++ b/drivers/iio/adc/cpcap-adc.c
@@ -0,0 +1,1031 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
+ *
+ * Rewritten for Linux IIO framework with some code based on
+ * earlier driver found in the Motorola Linux kernel:
+ *
+ * Copyright (C) 2009-2010 Motorola, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/mfd/motorola-cpcap.h>
+
+/* Register CPCAP_REG_ADCC1 bits */
+#define CPCAP_BIT_ADEN_AUTO_CLR BIT(15) /* Currently unused */
+#define CPCAP_BIT_CAL_MODE BIT(14) /* Set with BIT_RAND0 */
+#define CPCAP_BIT_ADC_CLK_SEL1 BIT(13) /* Currently unused */
+#define CPCAP_BIT_ADC_CLK_SEL0 BIT(12) /* Currently unused */
+#define CPCAP_BIT_ATOX BIT(11)
+#define CPCAP_BIT_ATO3 BIT(10)
+#define CPCAP_BIT_ATO2 BIT(9)
+#define CPCAP_BIT_ATO1 BIT(8)
+#define CPCAP_BIT_ATO0 BIT(7)
+#define CPCAP_BIT_ADA2 BIT(6)
+#define CPCAP_BIT_ADA1 BIT(5)
+#define CPCAP_BIT_ADA0 BIT(4)
+#define CPCAP_BIT_AD_SEL1 BIT(3) /* Set for bank1 */
+#define CPCAP_BIT_RAND1 BIT(2) /* Set for channel 16 & 17 */
+#define CPCAP_BIT_RAND0 BIT(1) /* Set with CAL_MODE */
+#define CPCAP_BIT_ADEN BIT(0) /* Currently unused */
+
+#define CPCAP_REG_ADCC1_DEFAULTS (CPCAP_BIT_ADEN_AUTO_CLR | \
+ CPCAP_BIT_ADC_CLK_SEL0 | \
+ CPCAP_BIT_RAND1)
+
+/* Register CPCAP_REG_ADCC2 bits */
+#define CPCAP_BIT_CAL_FACTOR_ENABLE BIT(15) /* Currently unused */
+#define CPCAP_BIT_BATDETB_EN BIT(14) /* Currently unused */
+#define CPCAP_BIT_ADTRIG_ONESHOT BIT(13) /* Set for !TIMING_IMM */
+#define CPCAP_BIT_ASC BIT(12) /* Set for TIMING_IMM */
+#define CPCAP_BIT_ATOX_PS_FACTOR BIT(11)
+#define CPCAP_BIT_ADC_PS_FACTOR1 BIT(10)
+#define CPCAP_BIT_ADC_PS_FACTOR0 BIT(9)
+#define CPCAP_BIT_AD4_SELECT BIT(8) /* Currently unused */
+#define CPCAP_BIT_ADC_BUSY BIT(7) /* Currently unused */
+#define CPCAP_BIT_THERMBIAS_EN BIT(6) /* Bias for AD0_BATTDETB */
+#define CPCAP_BIT_ADTRIG_DIS BIT(5) /* Disable interrupt */
+#define CPCAP_BIT_LIADC BIT(4) /* Currently unused */
+#define CPCAP_BIT_TS_REFEN BIT(3) /* Currently unused */
+#define CPCAP_BIT_TS_M2 BIT(2) /* Currently unused */
+#define CPCAP_BIT_TS_M1 BIT(1) /* Currently unused */
+#define CPCAP_BIT_TS_M0 BIT(0) /* Currently unused */
+
+#define CPCAP_REG_ADCC2_DEFAULTS (CPCAP_BIT_AD4_SELECT | \
+ CPCAP_BIT_ADTRIG_DIS | \
+ CPCAP_BIT_LIADC | \
+ CPCAP_BIT_TS_M2 | \
+ CPCAP_BIT_TS_M1)
+
+#define CPCAP_MAX_TEMP_LVL 27
+#define CPCAP_FOUR_POINT_TWO_ADC 801
+#define ST_ADC_CAL_CHRGI_HIGH_THRESHOLD 530
+#define ST_ADC_CAL_CHRGI_LOW_THRESHOLD 494
+#define ST_ADC_CAL_BATTI_HIGH_THRESHOLD 530
+#define ST_ADC_CAL_BATTI_LOW_THRESHOLD 494
+#define ST_ADC_CALIBRATE_DIFF_THRESHOLD 3
+
+#define CPCAP_ADC_MAX_RETRIES 5 /* Calibration */
+
+/*
+ * struct cpcap_adc_ato - timing settings for cpcap adc
+ *
+ * Unfortunately no cpcap documentation available, please document when
+ * using these.
+ */
+struct cpcap_adc_ato {
+ unsigned short ato_in;
+ unsigned short atox_in;
+ unsigned short adc_ps_factor_in;
+ unsigned short atox_ps_factor_in;
+ unsigned short ato_out;
+ unsigned short atox_out;
+ unsigned short adc_ps_factor_out;
+ unsigned short atox_ps_factor_out;
+};
+
+/**
+ * struct cpcap_adc - cpcap adc device driver data
+ * @reg: cpcap regmap
+ * @dev: struct device
+ * @vendor: cpcap vendor
+ * @irq: interrupt
+ * @lock: mutex
+ * @ato: request timings
+ * @wq_data_avail: work queue
+ * @done: work done
+ */
+struct cpcap_adc {
+ struct regmap *reg;
+ struct device *dev;
+ u16 vendor;
+ int irq;
+ struct mutex lock; /* ADC register access lock */
+ const struct cpcap_adc_ato *ato;
+ wait_queue_head_t wq_data_avail;
+ bool done;
+};
+
+/*
+ * enum cpcap_adc_channel - cpcap adc channels
+ */
+enum cpcap_adc_channel {
+ /* Bank0 channels */
+ CPCAP_ADC_AD0, /* Battery temperature */
+ CPCAP_ADC_BATTP, /* Battery voltage */
+ CPCAP_ADC_VBUS, /* USB VBUS voltage */
+ CPCAP_ADC_AD3, /* Die temperature when charging */
+ CPCAP_ADC_BPLUS_AD4, /* Another battery or system voltage */
+ CPCAP_ADC_CHG_ISENSE, /* Calibrated charge current */
+ CPCAP_ADC_BATTI, /* Calibrated system current */
+ CPCAP_ADC_USB_ID, /* USB OTG ID, unused on droid 4? */
+
+ /* Bank1 channels */
+ CPCAP_ADC_AD8, /* Seems unused */
+ CPCAP_ADC_AD9, /* Seems unused */
+ CPCAP_ADC_LICELL, /* Maybe system voltage? Always 3V */
+ CPCAP_ADC_HV_BATTP, /* Another battery detection? */
+ CPCAP_ADC_TSX1_AD12, /* Seems unused, for touchscreen? */
+ CPCAP_ADC_TSX2_AD13, /* Seems unused, for touchscreen? */
+ CPCAP_ADC_TSY1_AD14, /* Seems unused, for touchscreen? */
+ CPCAP_ADC_TSY2_AD15, /* Seems unused, for touchscreen? */
+
+ /* Remuxed channels using bank0 entries */
+ CPCAP_ADC_BATTP_PI16, /* Alternative mux mode for BATTP */
+ CPCAP_ADC_BATTI_PI17, /* Alternative mux mode for BATTI */
+
+ CPCAP_ADC_CHANNEL_NUM,
+};
+
+/*
+ * enum cpcap_adc_timing - cpcap adc timing options
+ *
+ * CPCAP_ADC_TIMING_IMM seems to be immediate with no timings.
+ * Please document when using.
+ */
+enum cpcap_adc_timing {
+ CPCAP_ADC_TIMING_IMM,
+ CPCAP_ADC_TIMING_IN,
+ CPCAP_ADC_TIMING_OUT,
+};
+
+/**
+ * struct cpcap_adc_phasing_tbl - cpcap phasing table
+ * @offset: offset in the phasing table
+ * @multiplier: multiplier in the phasing table
+ * @divider: divider in the phasing table
+ * @min: minimum value
+ * @max: maximum value
+ */
+struct cpcap_adc_phasing_tbl {
+ short offset;
+ unsigned short multiplier;
+ unsigned short divider;
+ short min;
+ short max;
+};
+
+/**
+ * struct cpcap_adc_conversion_tbl - cpcap conversion table
+ * @conv_type: conversion type
+ * @align_offset: align offset
+ * @conv_offset: conversion offset
+ * @cal_offset: calibration offset
+ * @multiplier: conversion multiplier
+ * @divider: conversion divider
+ */
+struct cpcap_adc_conversion_tbl {
+ enum iio_chan_info_enum conv_type;
+ int align_offset;
+ int conv_offset;
+ int cal_offset;
+ int multiplier;
+ int divider;
+};
+
+/**
+ * struct cpcap_adc_request - cpcap adc request
+ * @channel: request channel
+ * @phase_tbl: channel phasing table
+ * @conv_tbl: channel conversion table
+ * @bank_index: channel index within the bank
+ * @timing: timing settings
+ * @result: result
+ */
+struct cpcap_adc_request {
+ int channel;
+ const struct cpcap_adc_phasing_tbl *phase_tbl;
+ const struct cpcap_adc_conversion_tbl *conv_tbl;
+ int bank_index;
+ enum cpcap_adc_timing timing;
+ int result;
+};
+
+/* Phasing table for channels. Note that channels 16 & 17 use BATTP and BATTI */
+static const struct cpcap_adc_phasing_tbl bank_phasing[] = {
+ /* Bank0 */
+ [CPCAP_ADC_AD0] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511},
+ [CPCAP_ADC_BATTI] = {0, 0x80, 0x80, -512, 511},
+ [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023},
+
+ /* Bank1 */
+ [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023},
+};
+
+/*
+ * Conversion table for channels. Updated during init based on calibration.
+ * Here too channels 16 & 17 use BATTP and BATTI.
+ */
+static struct cpcap_adc_conversion_tbl bank_conversion[] = {
+ /* Bank0 */
+ [CPCAP_ADC_AD0] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_BATTP] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023,
+ },
+ [CPCAP_ADC_VBUS] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 10000, 1023,
+ },
+ [CPCAP_ADC_AD3] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_BPLUS_AD4] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023,
+ },
+ [CPCAP_ADC_CHG_ISENSE] = {
+ IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023,
+ },
+ [CPCAP_ADC_BATTI] = {
+ IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023,
+ },
+ [CPCAP_ADC_USB_ID] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+
+ /* Bank1 */
+ [CPCAP_ADC_AD8] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_AD9] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_LICELL] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 3400, 1023,
+ },
+ [CPCAP_ADC_HV_BATTP] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSX1_AD12] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSX2_AD13] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSY1_AD14] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSY2_AD15] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+};
+
+/*
+ * Temperature lookup table of register values to milliCelcius.
+ * REVISIT: Check the duplicate 0x3ff entry in a freezer
+ */
+static const int temp_map[CPCAP_MAX_TEMP_LVL][2] = {
+ { 0x03ff, -40000 },
+ { 0x03ff, -35000 },
+ { 0x03ef, -30000 },
+ { 0x03b2, -25000 },
+ { 0x036c, -20000 },
+ { 0x0320, -15000 },
+ { 0x02d0, -10000 },
+ { 0x027f, -5000 },
+ { 0x022f, 0 },
+ { 0x01e4, 5000 },
+ { 0x019f, 10000 },
+ { 0x0161, 15000 },
+ { 0x012b, 20000 },
+ { 0x00fc, 25000 },
+ { 0x00d4, 30000 },
+ { 0x00b2, 35000 },
+ { 0x0095, 40000 },
+ { 0x007d, 45000 },
+ { 0x0069, 50000 },
+ { 0x0059, 55000 },
+ { 0x004b, 60000 },
+ { 0x003f, 65000 },
+ { 0x0036, 70000 },
+ { 0x002e, 75000 },
+ { 0x0027, 80000 },
+ { 0x0022, 85000 },
+ { 0x001d, 90000 },
+};
+
+#define CPCAP_CHAN(_type, _index, _address, _datasheet_name) { \
+ .type = (_type), \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 10, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+ .datasheet_name = (_datasheet_name), \
+}
+
+/*
+ * The datasheet names are from Motorola mapphone Linux kernel except
+ * for the last two which might be uncalibrated charge voltage and
+ * current.
+ */
+static const struct iio_chan_spec cpcap_adc_channels[] = {
+ /* Bank0 */
+ CPCAP_CHAN(IIO_TEMP, 0, CPCAP_REG_ADCD0, "battdetb"),
+ CPCAP_CHAN(IIO_VOLTAGE, 1, CPCAP_REG_ADCD1, "battp"),
+ CPCAP_CHAN(IIO_VOLTAGE, 2, CPCAP_REG_ADCD2, "vbus"),
+ CPCAP_CHAN(IIO_TEMP, 3, CPCAP_REG_ADCD3, "ad3"),
+ CPCAP_CHAN(IIO_VOLTAGE, 4, CPCAP_REG_ADCD4, "ad4"),
+ CPCAP_CHAN(IIO_CURRENT, 5, CPCAP_REG_ADCD5, "chg_isense"),
+ CPCAP_CHAN(IIO_CURRENT, 6, CPCAP_REG_ADCD6, "batti"),
+ CPCAP_CHAN(IIO_VOLTAGE, 7, CPCAP_REG_ADCD7, "usb_id"),
+
+ /* Bank1 */
+ CPCAP_CHAN(IIO_CURRENT, 8, CPCAP_REG_ADCD0, "ad8"),
+ CPCAP_CHAN(IIO_VOLTAGE, 9, CPCAP_REG_ADCD1, "ad9"),
+ CPCAP_CHAN(IIO_VOLTAGE, 10, CPCAP_REG_ADCD2, "licell"),
+ CPCAP_CHAN(IIO_VOLTAGE, 11, CPCAP_REG_ADCD3, "hv_battp"),
+ CPCAP_CHAN(IIO_VOLTAGE, 12, CPCAP_REG_ADCD4, "tsx1_ad12"),
+ CPCAP_CHAN(IIO_VOLTAGE, 13, CPCAP_REG_ADCD5, "tsx2_ad13"),
+ CPCAP_CHAN(IIO_VOLTAGE, 14, CPCAP_REG_ADCD6, "tsy1_ad14"),
+ CPCAP_CHAN(IIO_VOLTAGE, 15, CPCAP_REG_ADCD7, "tsy2_ad15"),
+
+ /* There are two registers with multiplexed functionality */
+ CPCAP_CHAN(IIO_VOLTAGE, 16, CPCAP_REG_ADCD0, "chg_vsense"),
+ CPCAP_CHAN(IIO_CURRENT, 17, CPCAP_REG_ADCD1, "batti2"),
+};
+
+static irqreturn_t cpcap_adc_irq_thread(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct cpcap_adc *ddata = iio_priv(indio_dev);
+ int error;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ return IRQ_NONE;
+
+ ddata->done = true;
+ wake_up_interruptible(&ddata->wq_data_avail);
+
+ return IRQ_HANDLED;
+}
+
+/* ADC calibration functions */
+static void cpcap_adc_setup_calibrate(struct cpcap_adc *ddata,
+ enum cpcap_adc_channel chan)
+{
+ unsigned int value = 0;
+ unsigned long timeout = jiffies + msecs_to_jiffies(3000);
+ int error;
+
+ if ((chan != CPCAP_ADC_CHG_ISENSE) &&
+ (chan != CPCAP_ADC_BATTI))
+ return;
+
+ value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0;
+ value |= ((chan << 4) &
+ (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0));
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+ CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+ CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+ CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+ CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+ CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0,
+ value);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ATOX_PS_FACTOR |
+ CPCAP_BIT_ADC_PS_FACTOR1 |
+ CPCAP_BIT_ADC_PS_FACTOR0,
+ 0);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ASC,
+ CPCAP_BIT_ASC);
+ if (error)
+ return;
+
+ do {
+ schedule_timeout_uninterruptible(1);
+ error = regmap_read(ddata->reg, CPCAP_REG_ADCC2, &value);
+ if (error)
+ return;
+ } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout));
+
+ if (value & CPCAP_BIT_ASC)
+ dev_err(ddata->dev,
+ "Timeout waiting for calibration to complete\n");
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ CPCAP_BIT_CAL_MODE, 0);
+ if (error)
+ return;
+}
+
+static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata,
+ int channel,
+ u16 calibration_register,
+ int lower_threshold,
+ int upper_threshold)
+{
+ unsigned int calibration_data[2];
+ unsigned short cal_data_diff;
+ int i, error;
+
+ for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) {
+ calibration_data[0] = 0;
+ calibration_data[1] = 0;
+
+ cpcap_adc_setup_calibrate(ddata, channel);
+ error = regmap_read(ddata->reg, calibration_register,
+ &calibration_data[0]);
+ if (error)
+ return error;
+ cpcap_adc_setup_calibrate(ddata, channel);
+ error = regmap_read(ddata->reg, calibration_register,
+ &calibration_data[1]);
+ if (error)
+ return error;
+
+ if (calibration_data[0] > calibration_data[1])
+ cal_data_diff =
+ calibration_data[0] - calibration_data[1];
+ else
+ cal_data_diff =
+ calibration_data[1] - calibration_data[0];
+
+ if (((calibration_data[1] >= lower_threshold) &&
+ (calibration_data[1] <= upper_threshold) &&
+ (cal_data_diff <= ST_ADC_CALIBRATE_DIFF_THRESHOLD)) ||
+ (ddata->vendor == CPCAP_VENDOR_TI)) {
+ bank_conversion[channel].cal_offset =
+ ((short)calibration_data[1] * -1) + 512;
+ dev_dbg(ddata->dev, "ch%i calibration complete: %i\n",
+ channel, bank_conversion[channel].cal_offset);
+ break;
+ }
+ usleep_range(5000, 10000);
+ }
+
+ return 0;
+}
+
+static int cpcap_adc_calibrate(struct cpcap_adc *ddata)
+{
+ int error;
+
+ error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_CHG_ISENSE,
+ CPCAP_REG_ADCAL1,
+ ST_ADC_CAL_CHRGI_LOW_THRESHOLD,
+ ST_ADC_CAL_CHRGI_HIGH_THRESHOLD);
+ if (error)
+ return error;
+
+ error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_BATTI,
+ CPCAP_REG_ADCAL2,
+ ST_ADC_CAL_BATTI_LOW_THRESHOLD,
+ ST_ADC_CAL_BATTI_HIGH_THRESHOLD);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+/* ADC setup, read and scale functions */
+static void cpcap_adc_setup_bank(struct cpcap_adc *ddata,
+ struct cpcap_adc_request *req)
+{
+ const struct cpcap_adc_ato *ato = ddata->ato;
+ unsigned short value1 = 0;
+ unsigned short value2 = 0;
+ int error;
+
+ if (!ato)
+ return;
+
+ switch (req->channel) {
+ case CPCAP_ADC_AD0:
+ value2 |= CPCAP_BIT_THERMBIAS_EN;
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_THERMBIAS_EN,
+ value2);
+ if (error)
+ return;
+ usleep_range(800, 1000);
+ break;
+ case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15:
+ value1 |= CPCAP_BIT_AD_SEL1;
+ break;
+ case CPCAP_ADC_BATTP_PI16 ... CPCAP_ADC_BATTI_PI17:
+ value1 |= CPCAP_BIT_RAND1;
+ break;
+ default:
+ break;
+ }
+
+ switch (req->timing) {
+ case CPCAP_ADC_TIMING_IN:
+ value1 |= ato->ato_in;
+ value1 |= ato->atox_in;
+ value2 |= ato->adc_ps_factor_in;
+ value2 |= ato->atox_ps_factor_in;
+ break;
+ case CPCAP_ADC_TIMING_OUT:
+ value1 |= ato->ato_out;
+ value1 |= ato->atox_out;
+ value2 |= ato->adc_ps_factor_out;
+ value2 |= ato->atox_ps_factor_out;
+ break;
+
+ case CPCAP_ADC_TIMING_IMM:
+ default:
+ break;
+ }
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+ CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+ CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+ CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+ CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+ CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0,
+ value1);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ATOX_PS_FACTOR |
+ CPCAP_BIT_ADC_PS_FACTOR1 |
+ CPCAP_BIT_ADC_PS_FACTOR0 |
+ CPCAP_BIT_THERMBIAS_EN,
+ value2);
+ if (error)
+ return;
+
+ if (req->timing == CPCAP_ADC_TIMING_IMM) {
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ASC,
+ CPCAP_BIT_ASC);
+ if (error)
+ return;
+ } else {
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_ONESHOT,
+ CPCAP_BIT_ADTRIG_ONESHOT);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS, 0);
+ if (error)
+ return;
+ }
+}
+
+static int cpcap_adc_start_bank(struct cpcap_adc *ddata,
+ struct cpcap_adc_request *req)
+{
+ int i, error;
+
+ req->timing = CPCAP_ADC_TIMING_IMM;
+ ddata->done = false;
+
+ for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) {
+ cpcap_adc_setup_bank(ddata, req);
+ error = wait_event_interruptible_timeout(ddata->wq_data_avail,
+ ddata->done,
+ msecs_to_jiffies(50));
+ if (error > 0)
+ return 0;
+
+ if (error == 0) {
+ error = -ETIMEDOUT;
+ continue;
+ }
+
+ if (error < 0)
+ return error;
+ }
+
+ return error;
+}
+
+static int cpcap_adc_stop_bank(struct cpcap_adc *ddata)
+{
+ int error;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ 0xffff,
+ CPCAP_REG_ADCC1_DEFAULTS);
+ if (error)
+ return error;
+
+ return regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ 0xffff,
+ CPCAP_REG_ADCC2_DEFAULTS);
+}
+
+static void cpcap_adc_phase(struct cpcap_adc_request *req)
+{
+ const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl;
+ const struct cpcap_adc_phasing_tbl *phase_tbl = req->phase_tbl;
+ int index = req->channel;
+
+ /* Remuxed channels 16 and 17 use BATTP and BATTI entries */
+ switch (req->channel) {
+ case CPCAP_ADC_BATTP:
+ case CPCAP_ADC_BATTP_PI16:
+ index = req->bank_index;
+ req->result -= phase_tbl[index].offset;
+ req->result -= CPCAP_FOUR_POINT_TWO_ADC;
+ req->result *= phase_tbl[index].multiplier;
+ if (phase_tbl[index].divider == 0)
+ return;
+ req->result /= phase_tbl[index].divider;
+ req->result += CPCAP_FOUR_POINT_TWO_ADC;
+ break;
+ case CPCAP_ADC_BATTI_PI17:
+ index = req->bank_index;
+ fallthrough;
+ default:
+ req->result += conv_tbl[index].cal_offset;
+ req->result += conv_tbl[index].align_offset;
+ req->result *= phase_tbl[index].multiplier;
+ if (phase_tbl[index].divider == 0)
+ return;
+ req->result /= phase_tbl[index].divider;
+ req->result += phase_tbl[index].offset;
+ break;
+ }
+
+ if (req->result < phase_tbl[index].min)
+ req->result = phase_tbl[index].min;
+ else if (req->result > phase_tbl[index].max)
+ req->result = phase_tbl[index].max;
+}
+
+/* Looks up temperatures in a table and calculates averages if needed */
+static int cpcap_adc_table_to_millicelcius(unsigned short value)
+{
+ int i, result = 0, alpha;
+
+ if (value <= temp_map[CPCAP_MAX_TEMP_LVL - 1][0])
+ return temp_map[CPCAP_MAX_TEMP_LVL - 1][1];
+
+ if (value >= temp_map[0][0])
+ return temp_map[0][1];
+
+ for (i = 0; i < CPCAP_MAX_TEMP_LVL - 1; i++) {
+ if ((value <= temp_map[i][0]) &&
+ (value >= temp_map[i + 1][0])) {
+ if (value == temp_map[i][0]) {
+ result = temp_map[i][1];
+ } else if (value == temp_map[i + 1][0]) {
+ result = temp_map[i + 1][1];
+ } else {
+ alpha = ((value - temp_map[i][0]) * 1000) /
+ (temp_map[i + 1][0] - temp_map[i][0]);
+
+ result = temp_map[i][1] +
+ ((alpha * (temp_map[i + 1][1] -
+ temp_map[i][1])) / 1000);
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+static void cpcap_adc_convert(struct cpcap_adc_request *req)
+{
+ const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl;
+ int index = req->channel;
+
+ /* Remuxed channels 16 and 17 use BATTP and BATTI entries */
+ switch (req->channel) {
+ case CPCAP_ADC_BATTP_PI16:
+ index = CPCAP_ADC_BATTP;
+ break;
+ case CPCAP_ADC_BATTI_PI17:
+ index = CPCAP_ADC_BATTI;
+ break;
+ default:
+ break;
+ }
+
+ /* No conversion for raw channels */
+ if (conv_tbl[index].conv_type == IIO_CHAN_INFO_RAW)
+ return;
+
+ /* Temperatures use a lookup table instead of conversion table */
+ if ((req->channel == CPCAP_ADC_AD0) ||
+ (req->channel == CPCAP_ADC_AD3)) {
+ req->result =
+ cpcap_adc_table_to_millicelcius(req->result);
+
+ return;
+ }
+
+ /* All processed channels use a conversion table */
+ req->result *= conv_tbl[index].multiplier;
+ if (conv_tbl[index].divider == 0)
+ return;
+ req->result /= conv_tbl[index].divider;
+ req->result += conv_tbl[index].conv_offset;
+}
+
+/*
+ * REVISIT: Check if timed sampling can use multiple channels at the
+ * same time. If not, replace channel_mask with just channel.
+ */
+static int cpcap_adc_read_bank_scaled(struct cpcap_adc *ddata,
+ struct cpcap_adc_request *req)
+{
+ int calibration_data, error, addr;
+
+ if (ddata->vendor == CPCAP_VENDOR_TI) {
+ error = regmap_read(ddata->reg, CPCAP_REG_ADCAL1,
+ &calibration_data);
+ if (error)
+ return error;
+ bank_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset =
+ ((short)calibration_data * -1) + 512;
+
+ error = regmap_read(ddata->reg, CPCAP_REG_ADCAL2,
+ &calibration_data);
+ if (error)
+ return error;
+ bank_conversion[CPCAP_ADC_BATTI].cal_offset =
+ ((short)calibration_data * -1) + 512;
+ }
+
+ addr = CPCAP_REG_ADCD0 + req->bank_index * 4;
+
+ error = regmap_read(ddata->reg, addr, &req->result);
+ if (error)
+ return error;
+
+ req->result &= 0x3ff;
+ cpcap_adc_phase(req);
+ cpcap_adc_convert(req);
+
+ return 0;
+}
+
+static int cpcap_adc_init_request(struct cpcap_adc_request *req,
+ int channel)
+{
+ req->channel = channel;
+ req->phase_tbl = bank_phasing;
+ req->conv_tbl = bank_conversion;
+
+ switch (channel) {
+ case CPCAP_ADC_AD0 ... CPCAP_ADC_USB_ID:
+ req->bank_index = channel;
+ break;
+ case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15:
+ req->bank_index = channel - 8;
+ break;
+ case CPCAP_ADC_BATTP_PI16:
+ req->bank_index = CPCAP_ADC_BATTP;
+ break;
+ case CPCAP_ADC_BATTI_PI17:
+ req->bank_index = CPCAP_ADC_BATTI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cpcap_adc_read_st_die_temp(struct cpcap_adc *ddata,
+ int addr, int *val)
+{
+ int error;
+
+ error = regmap_read(ddata->reg, addr, val);
+ if (error)
+ return error;
+
+ *val -= 282;
+ *val *= 114;
+ *val += 25000;
+
+ return 0;
+}
+
+static int cpcap_adc_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cpcap_adc *ddata = iio_priv(indio_dev);
+ struct cpcap_adc_request req;
+ int error;
+
+ error = cpcap_adc_init_request(&req, chan->channel);
+ if (error)
+ return error;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&ddata->lock);
+ error = cpcap_adc_start_bank(ddata, &req);
+ if (error)
+ goto err_unlock;
+ error = regmap_read(ddata->reg, chan->address, val);
+ if (error)
+ goto err_unlock;
+ error = cpcap_adc_stop_bank(ddata);
+ if (error)
+ goto err_unlock;
+ mutex_unlock(&ddata->lock);
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&ddata->lock);
+ error = cpcap_adc_start_bank(ddata, &req);
+ if (error)
+ goto err_unlock;
+ if ((ddata->vendor == CPCAP_VENDOR_ST) &&
+ (chan->channel == CPCAP_ADC_AD3)) {
+ error = cpcap_adc_read_st_die_temp(ddata,
+ chan->address,
+ &req.result);
+ if (error)
+ goto err_unlock;
+ } else {
+ error = cpcap_adc_read_bank_scaled(ddata, &req);
+ if (error)
+ goto err_unlock;
+ }
+ error = cpcap_adc_stop_bank(ddata);
+ if (error)
+ goto err_unlock;
+ mutex_unlock(&ddata->lock);
+ *val = req.result;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+
+err_unlock:
+ mutex_unlock(&ddata->lock);
+ dev_err(ddata->dev, "error reading ADC: %i\n", error);
+
+ return error;
+}
+
+static const struct iio_info cpcap_adc_info = {
+ .read_raw = &cpcap_adc_read,
+};
+
+/*
+ * Configuration for Motorola mapphone series such as droid 4.
+ * Copied from the Motorola mapphone kernel tree.
+ */
+static const struct cpcap_adc_ato mapphone_adc = {
+ .ato_in = 0x0480,
+ .atox_in = 0,
+ .adc_ps_factor_in = 0x0200,
+ .atox_ps_factor_in = 0,
+ .ato_out = 0,
+ .atox_out = 0,
+ .adc_ps_factor_out = 0,
+ .atox_ps_factor_out = 0,
+};
+
+static const struct of_device_id cpcap_adc_id_table[] = {
+ {
+ .compatible = "motorola,cpcap-adc",
+ },
+ {
+ .compatible = "motorola,mapphone-cpcap-adc",
+ .data = &mapphone_adc,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cpcap_adc_id_table);
+
+static int cpcap_adc_probe(struct platform_device *pdev)
+{
+ struct cpcap_adc *ddata;
+ struct iio_dev *indio_dev;
+ int error;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed to allocate iio device\n");
+
+ return -ENOMEM;
+ }
+ ddata = iio_priv(indio_dev);
+ ddata->ato = device_get_match_data(&pdev->dev);
+ if (!ddata->ato)
+ return -ENODEV;
+ ddata->dev = &pdev->dev;
+
+ mutex_init(&ddata->lock);
+ init_waitqueue_head(&ddata->wq_data_avail);
+
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->channels = cpcap_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cpcap_adc_channels);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &cpcap_adc_info;
+
+ ddata->reg = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!ddata->reg)
+ return -ENODEV;
+
+ error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor);
+ if (error)
+ return error;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ddata->irq = platform_get_irq_byname(pdev, "adcdone");
+ if (ddata->irq < 0)
+ return -ENODEV;
+
+ error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL,
+ cpcap_adc_irq_thread,
+ IRQF_TRIGGER_NONE | IRQF_ONESHOT,
+ "cpcap-adc", indio_dev);
+ if (error) {
+ dev_err(&pdev->dev, "could not get irq: %i\n",
+ error);
+
+ return error;
+ }
+
+ error = cpcap_adc_calibrate(ddata);
+ if (error)
+ return error;
+
+ dev_info(&pdev->dev, "CPCAP ADC device probed\n");
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver cpcap_adc_driver = {
+ .driver = {
+ .name = "cpcap_adc",
+ .of_match_table = cpcap_adc_id_table,
+ },
+ .probe = cpcap_adc_probe,
+};
+
+module_platform_driver(cpcap_adc_driver);
+
+MODULE_ALIAS("platform:cpcap_adc");
+MODULE_DESCRIPTION("CPCAP ADC driver");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
new file mode 100644
index 000000000..8f0d3fb63
--- /dev/null
+++ b/drivers/iio/adc/da9150-gpadc.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DA9150 GPADC Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+/* Channels */
+enum da9150_gpadc_hw_channel {
+ DA9150_GPADC_HW_CHAN_GPIOA_2V = 0,
+ DA9150_GPADC_HW_CHAN_GPIOA_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOB_2V,
+ DA9150_GPADC_HW_CHAN_GPIOB_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOC_2V,
+ DA9150_GPADC_HW_CHAN_GPIOC_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOD_2V,
+ DA9150_GPADC_HW_CHAN_GPIOD_2V_,
+ DA9150_GPADC_HW_CHAN_IBUS_SENSE,
+ DA9150_GPADC_HW_CHAN_IBUS_SENSE_,
+ DA9150_GPADC_HW_CHAN_VBUS_DIV,
+ DA9150_GPADC_HW_CHAN_VBUS_DIV_,
+ DA9150_GPADC_HW_CHAN_ID,
+ DA9150_GPADC_HW_CHAN_ID_,
+ DA9150_GPADC_HW_CHAN_VSYS,
+ DA9150_GPADC_HW_CHAN_VSYS_,
+ DA9150_GPADC_HW_CHAN_GPIOA_6V,
+ DA9150_GPADC_HW_CHAN_GPIOA_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOB_6V,
+ DA9150_GPADC_HW_CHAN_GPIOB_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOC_6V,
+ DA9150_GPADC_HW_CHAN_GPIOC_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOD_6V,
+ DA9150_GPADC_HW_CHAN_GPIOD_6V_,
+ DA9150_GPADC_HW_CHAN_VBAT,
+ DA9150_GPADC_HW_CHAN_VBAT_,
+ DA9150_GPADC_HW_CHAN_TBAT,
+ DA9150_GPADC_HW_CHAN_TBAT_,
+ DA9150_GPADC_HW_CHAN_TJUNC_CORE,
+ DA9150_GPADC_HW_CHAN_TJUNC_CORE_,
+ DA9150_GPADC_HW_CHAN_TJUNC_OVP,
+ DA9150_GPADC_HW_CHAN_TJUNC_OVP_,
+};
+
+enum da9150_gpadc_channel {
+ DA9150_GPADC_CHAN_GPIOA = 0,
+ DA9150_GPADC_CHAN_GPIOB,
+ DA9150_GPADC_CHAN_GPIOC,
+ DA9150_GPADC_CHAN_GPIOD,
+ DA9150_GPADC_CHAN_IBUS,
+ DA9150_GPADC_CHAN_VBUS,
+ DA9150_GPADC_CHAN_VSYS,
+ DA9150_GPADC_CHAN_VBAT,
+ DA9150_GPADC_CHAN_TBAT,
+ DA9150_GPADC_CHAN_TJUNC_CORE,
+ DA9150_GPADC_CHAN_TJUNC_OVP,
+};
+
+/* Private data */
+struct da9150_gpadc {
+ struct da9150 *da9150;
+ struct device *dev;
+
+ struct mutex lock;
+ struct completion complete;
+};
+
+
+static irqreturn_t da9150_gpadc_irq(int irq, void *data)
+{
+
+ struct da9150_gpadc *gpadc = data;
+
+ complete(&gpadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan)
+{
+ u8 result_regs[2];
+ int result;
+
+ mutex_lock(&gpadc->lock);
+
+ /* Set channel & enable measurement */
+ da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN,
+ (DA9150_GPADC_EN_MASK |
+ hw_chan << DA9150_GPADC_MUX_SHIFT));
+
+ /* Consume left-over completion from a previous timeout */
+ try_wait_for_completion(&gpadc->complete);
+
+ /* Check for actual completion */
+ wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
+
+ /* Read result and status from device */
+ da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
+
+ mutex_unlock(&gpadc->lock);
+
+ /* Check to make sure device really has completed reading */
+ if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
+ dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n",
+ hw_chan);
+ return -ETIMEDOUT;
+ }
+
+ /* LSBs - 2 bits */
+ result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
+ DA9150_GPADC_RES_L_SHIFT;
+ /* MSBs - 8 bits */
+ result |= result_regs[0] << DA9150_GPADC_RES_L_BITS;
+
+ return result;
+}
+
+static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (6 * ((raw_val * 1000) + 500)) / 1024;
+}
+
+static inline int da9150_gpadc_ibus_current_avg(int raw_val)
+{
+ /* Convert to mA */
+ return (4 * ((raw_val * 1000) + 500)) / 2048;
+}
+
+static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (21 * ((raw_val * 1000) + 500)) / 1024;
+}
+
+static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (3 * ((raw_val * 1000) + 500)) / 512;
+}
+
+static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel,
+ int hw_chan, int *val)
+{
+ int raw_val;
+
+ raw_val = da9150_gpadc_read_adc(gpadc, hw_chan);
+ if (raw_val < 0)
+ return raw_val;
+
+ switch (channel) {
+ case DA9150_GPADC_CHAN_GPIOA:
+ case DA9150_GPADC_CHAN_GPIOB:
+ case DA9150_GPADC_CHAN_GPIOC:
+ case DA9150_GPADC_CHAN_GPIOD:
+ *val = da9150_gpadc_gpio_6v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_IBUS:
+ *val = da9150_gpadc_ibus_current_avg(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VBUS:
+ *val = da9150_gpadc_vbus_21v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VSYS:
+ *val = da9150_gpadc_vsys_6v_voltage_now(raw_val);
+ break;
+ default:
+ /* No processing for other channels so return raw value */
+ *val = raw_val;
+ break;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int da9150_gpadc_read_scale(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case DA9150_GPADC_CHAN_VBAT:
+ *val = 2932;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ *val = 1000000;
+ *val2 = 4420;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9150_gpadc_read_offset(int channel, int *val)
+{
+ switch (channel) {
+ case DA9150_GPADC_CHAN_VBAT:
+ *val = 1500000 / 2932;
+ return IIO_VAL_INT;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ *val = -144;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct da9150_gpadc *gpadc = iio_priv(indio_dev);
+
+ if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) ||
+ (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP))
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ return da9150_gpadc_read_processed(gpadc, chan->channel,
+ chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ return da9150_gpadc_read_scale(chan->channel, val, val2);
+ case IIO_CHAN_INFO_OFFSET:
+ return da9150_gpadc_read_offset(chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info da9150_gpadc_info = {
+ .read_raw = &da9150_gpadc_read_raw,
+};
+
+#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info, \
+ _ext_name) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = DA9150_GPADC_CHAN_##_id, \
+ .address = DA9150_GPADC_HW_CHAN_##_hw_id, \
+ .info_mask_separate = chan_info, \
+ .extend_name = _ext_name, \
+ .datasheet_name = #_id, \
+}
+
+#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW), _ext_name)
+
+#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ _ext_name)
+
+#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_PROCESSED), _ext_name)
+
+/* Supported channels */
+static const struct iio_chan_spec da9150_gpadc_channels[] = {
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "ibus"),
+ DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "vbus"),
+ DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "vsys"),
+ DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "vbat"),
+ DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "tbat"),
+ DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP,
+ "tjunc_core"),
+ DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP,
+ "tjunc_ovp"),
+};
+
+/* Default maps used by da9150-charger */
+static struct iio_map da9150_gpadc_default_maps[] = {
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_IBUS",
+ .adc_channel_label = "IBUS",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBUS",
+ .adc_channel_label = "VBUS",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_TJUNC",
+ .adc_channel_label = "TJUNC_CORE",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBAT",
+ .adc_channel_label = "VBAT",
+ },
+ {},
+};
+
+static int da9150_gpadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+ struct da9150_gpadc *gpadc;
+ struct iio_dev *indio_dev;
+ int irq, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+ gpadc = iio_priv(indio_dev);
+
+ gpadc->da9150 = da9150;
+ gpadc->dev = dev;
+ mutex_init(&gpadc->lock);
+ init_completion(&gpadc->complete);
+
+ irq = platform_get_irq_byname(pdev, "GPADC");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
+ IRQF_ONESHOT, "GPADC", gpadc);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
+ return ret;
+ }
+
+ ret = devm_iio_map_array_register(&pdev->dev, indio_dev, da9150_gpadc_default_maps);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO maps: %d\n", ret);
+ return ret;
+ }
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->info = &da9150_gpadc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = da9150_gpadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver da9150_gpadc_driver = {
+ .driver = {
+ .name = "da9150-gpadc",
+ },
+ .probe = da9150_gpadc_probe,
+};
+
+module_platform_driver(da9150_gpadc_driver);
+
+MODULE_DESCRIPTION("GPADC Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
new file mode 100644
index 000000000..97d162a3c
--- /dev/null
+++ b/drivers/iio/adc/dln2-adc.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the Diolan DLN-2 USB-ADC adapter
+ *
+ * Copyright (c) 2017 Jack Andersen
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define DLN2_ADC_MOD_NAME "dln2-adc"
+
+#define DLN2_ADC_ID 0x06
+
+#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID)
+#define DLN2_ADC_ENABLE DLN2_CMD(0x02, DLN2_ADC_ID)
+#define DLN2_ADC_DISABLE DLN2_CMD(0x03, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_ENABLE DLN2_CMD(0x05, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_DISABLE DLN2_CMD(0x06, DLN2_ADC_ID)
+#define DLN2_ADC_SET_RESOLUTION DLN2_CMD(0x08, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_VAL DLN2_CMD(0x0A, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_ALL_VAL DLN2_CMD(0x0B, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_SET_CFG DLN2_CMD(0x0C, DLN2_ADC_ID)
+#define DLN2_ADC_CHANNEL_GET_CFG DLN2_CMD(0x0D, DLN2_ADC_ID)
+#define DLN2_ADC_CONDITION_MET_EV DLN2_CMD(0x10, DLN2_ADC_ID)
+
+#define DLN2_ADC_EVENT_NONE 0
+#define DLN2_ADC_EVENT_BELOW 1
+#define DLN2_ADC_EVENT_LEVEL_ABOVE 2
+#define DLN2_ADC_EVENT_OUTSIDE 3
+#define DLN2_ADC_EVENT_INSIDE 4
+#define DLN2_ADC_EVENT_ALWAYS 5
+
+#define DLN2_ADC_MAX_CHANNELS 8
+#define DLN2_ADC_DATA_BITS 10
+
+/*
+ * Plays similar role to iio_demux_table in subsystem core; except allocated
+ * in a fixed 8-element array.
+ */
+struct dln2_adc_demux_table {
+ unsigned int from;
+ unsigned int to;
+ unsigned int length;
+};
+
+struct dln2_adc {
+ struct platform_device *pdev;
+ struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1];
+ int port, trigger_chan;
+ struct iio_trigger *trig;
+ struct mutex mutex;
+ /* Cached sample period in milliseconds */
+ unsigned int sample_period;
+ /* 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 {
+ u8 port;
+ u8 chan;
+};
+
+struct dln2_adc_get_all_vals {
+ __le16 channel_mask;
+ __le16 values[DLN2_ADC_MAX_CHANNELS];
+};
+
+static void dln2_adc_add_demux(struct dln2_adc *dln2,
+ unsigned int in_loc, unsigned int out_loc,
+ unsigned int length)
+{
+ struct dln2_adc_demux_table *p = dln2->demux_count ?
+ &dln2->demux[dln2->demux_count - 1] : NULL;
+
+ if (p && p->from + p->length == in_loc &&
+ p->to + p->length == out_loc) {
+ p->length += length;
+ } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) {
+ p = &dln2->demux[dln2->demux_count++];
+ p->from = in_loc;
+ p->to = out_loc;
+ p->length = length;
+ }
+}
+
+static void dln2_adc_update_demux(struct dln2_adc *dln2)
+{
+ int in_ind = -1, out_ind;
+ unsigned int in_loc = 0, out_loc = 0;
+ struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev);
+
+ /* Clear out any old demux */
+ dln2->demux_count = 0;
+
+ /* Optimize all 8-channels case */
+ if (indio_dev->masklength &&
+ (*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;
+ }
+
+ /* Build demux table from fixed 8-channels to active_scan_mask */
+ for_each_set_bit(out_ind,
+ indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ /* Handle timestamp separately */
+ if (out_ind == DLN2_ADC_MAX_CHANNELS)
+ break;
+ for (++in_ind; in_ind != out_ind; ++in_ind)
+ in_loc += 2;
+ dln2_adc_add_demux(dln2, in_loc, out_loc, 2);
+ 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)
+{
+ int ret;
+ u8 port = dln2->port;
+ u8 count;
+ int olen = sizeof(count);
+
+ ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT,
+ &port, sizeof(port), &count, &olen);
+ if (ret < 0) {
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+ return ret;
+ }
+ if (olen < sizeof(count))
+ return -EPROTO;
+
+ return count;
+}
+
+static int dln2_adc_set_port_resolution(struct dln2_adc *dln2)
+{
+ int ret;
+ struct dln2_adc_port_chan port_chan = {
+ .port = dln2->port,
+ .chan = DLN2_ADC_DATA_BITS,
+ };
+
+ ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION,
+ &port_chan, sizeof(port_chan));
+ if (ret < 0)
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+ return ret;
+}
+
+static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2,
+ int channel, bool enable)
+{
+ int ret;
+ struct dln2_adc_port_chan port_chan = {
+ .port = dln2->port,
+ .chan = channel,
+ };
+ u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE;
+
+ ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan));
+ if (ret < 0)
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+ return ret;
+}
+
+static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable,
+ u16 *conflict_out)
+{
+ int ret;
+ u8 port = dln2->port;
+ __le16 conflict;
+ int olen = sizeof(conflict);
+ u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE;
+
+ if (conflict_out)
+ *conflict_out = 0;
+
+ ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port),
+ &conflict, &olen);
+ if (ret < 0) {
+ dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n",
+ __func__, (int)enable);
+ if (conflict_out && enable && olen >= sizeof(conflict))
+ *conflict_out = le16_to_cpu(conflict);
+ return ret;
+ }
+ if (enable && olen < sizeof(conflict))
+ return -EPROTO;
+
+ return ret;
+}
+
+static int dln2_adc_set_chan_period(struct dln2_adc *dln2,
+ unsigned int channel, unsigned int period)
+{
+ int ret;
+ struct {
+ struct dln2_adc_port_chan port_chan;
+ __u8 type;
+ __le16 period;
+ __le16 low;
+ __le16 high;
+ } __packed set_cfg = {
+ .port_chan.port = dln2->port,
+ .port_chan.chan = channel,
+ .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE,
+ .period = cpu_to_le16(period)
+ };
+
+ ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG,
+ &set_cfg, sizeof(set_cfg));
+ if (ret < 0)
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+ return ret;
+}
+
+static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel)
+{
+ int ret, i;
+ u16 conflict;
+ __le16 value;
+ int olen = sizeof(value);
+ struct dln2_adc_port_chan port_chan = {
+ .port = dln2->port,
+ .chan = channel,
+ };
+
+ ret = dln2_adc_set_chan_enabled(dln2, channel, true);
+ if (ret < 0)
+ return ret;
+
+ ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+ if (ret < 0) {
+ if (conflict) {
+ dev_err(&dln2->pdev->dev,
+ "ADC pins conflict with mask %04X\n",
+ (int)conflict);
+ ret = -EBUSY;
+ }
+ goto disable_chan;
+ }
+
+ /*
+ * Call GET_VAL twice due to initial zero-return immediately after
+ * enabling channel.
+ */
+ for (i = 0; i < 2; ++i) {
+ ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL,
+ &port_chan, sizeof(port_chan),
+ &value, &olen);
+ if (ret < 0) {
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+ goto disable_port;
+ }
+ if (olen < sizeof(value)) {
+ ret = -EPROTO;
+ goto disable_port;
+ }
+ }
+
+ ret = le16_to_cpu(value);
+
+disable_port:
+ dln2_adc_set_port_enabled(dln2, false, NULL);
+disable_chan:
+ dln2_adc_set_chan_enabled(dln2, channel, false);
+
+ return ret;
+}
+
+static int dln2_adc_read_all(struct dln2_adc *dln2,
+ struct dln2_adc_get_all_vals *get_all_vals)
+{
+ int ret;
+ __u8 port = dln2->port;
+ int olen = sizeof(*get_all_vals);
+
+ ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL,
+ &port, sizeof(port), get_all_vals, &olen);
+ if (ret < 0) {
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+ return ret;
+ }
+ if (olen < sizeof(*get_all_vals))
+ return -EPROTO;
+
+ return ret;
+}
+
+static int dln2_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret;
+ unsigned int microhertz;
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&dln2->mutex);
+ ret = dln2_adc_read(dln2, chan->channel);
+ mutex_unlock(&dln2->mutex);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * Voltage reference is fixed at 3.3v
+ * 3.3 / (1 << 10) * 1000000000
+ */
+ *val = 0;
+ *val2 = 3222656;
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (dln2->sample_period) {
+ microhertz = 1000000000 / dln2->sample_period;
+ *val = microhertz / 1000000;
+ *val2 = microhertz % 1000000;
+ } else {
+ *val = 0;
+ *val2 = 0;
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dln2_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int ret;
+ unsigned int microhertz;
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ microhertz = 1000000 * val + val2;
+
+ mutex_lock(&dln2->mutex);
+
+ dln2->sample_period =
+ microhertz ? 1000000000 / microhertz : UINT_MAX;
+ if (dln2->sample_period > 65535) {
+ dln2->sample_period = 65535;
+ dev_warn(&dln2->pdev->dev,
+ "clamping period to 65535ms\n");
+ }
+
+ /*
+ * The first requested channel is arbitrated as a shared
+ * trigger source, so only one event is registered with the
+ * DLN. The event handler will then read all enabled channel
+ * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain
+ * synchronization between ADC readings.
+ */
+ if (dln2->trigger_chan != -1)
+ ret = dln2_adc_set_chan_period(dln2,
+ dln2->trigger_chan, dln2->sample_period);
+ else
+ ret = 0;
+
+ mutex_unlock(&dln2->mutex);
+
+ return ret;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dln2_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+ int chan_count = indio_dev->num_channels - 1;
+ int ret, i, j;
+
+ mutex_lock(&dln2->mutex);
+
+ for (i = 0; i < chan_count; ++i) {
+ ret = dln2_adc_set_chan_enabled(dln2, i,
+ test_bit(i, scan_mask));
+ if (ret < 0) {
+ for (j = 0; j < i; ++j)
+ dln2_adc_set_chan_enabled(dln2, j, false);
+ mutex_unlock(&dln2->mutex);
+ dev_err(&dln2->pdev->dev,
+ "Unable to enable ADC channel %d\n", i);
+ return -EBUSY;
+ }
+ }
+
+ dln2_adc_update_demux(dln2);
+
+ mutex_unlock(&dln2->mutex);
+
+ return 0;
+}
+
+#define DLN2_ADC_CHAN(lval, idx) { \
+ lval.type = IIO_VOLTAGE; \
+ lval.channel = idx; \
+ lval.indexed = 1; \
+ lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW); \
+ lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ); \
+ lval.scan_index = idx; \
+ lval.scan_type.sign = 'u'; \
+ lval.scan_type.realbits = DLN2_ADC_DATA_BITS; \
+ lval.scan_type.storagebits = 16; \
+ lval.scan_type.endianness = IIO_LE; \
+}
+
+/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */
+#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) { \
+ lval.type = IIO_TIMESTAMP; \
+ lval.channel = -1; \
+ lval.scan_index = _si; \
+ lval.scan_type.sign = 's'; \
+ lval.scan_type.realbits = 64; \
+ lval.scan_type.storagebits = 64; \
+}
+
+static const struct iio_info dln2_adc_info = {
+ .read_raw = dln2_adc_read_raw,
+ .write_raw = dln2_adc_write_raw,
+ .update_scan_mode = dln2_update_scan_mode,
+};
+
+static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct {
+ __le16 values[DLN2_ADC_MAX_CHANNELS];
+ int64_t timestamp_space;
+ } data;
+ struct dln2_adc_get_all_vals dev_data;
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+ const struct dln2_adc_demux_table *t;
+ int ret, i;
+
+ mutex_lock(&dln2->mutex);
+ ret = dln2_adc_read_all(dln2, &dev_data);
+ mutex_unlock(&dln2->mutex);
+ if (ret < 0)
+ goto done;
+
+ /* Demux operation */
+ for (i = 0; i < dln2->demux_count; ++i) {
+ t = &dln2->demux[i];
+ memcpy((void *)data.values + t->to,
+ (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));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+ u16 conflict;
+ unsigned int trigger_chan;
+
+ mutex_lock(&dln2->mutex);
+
+ /* Enable ADC */
+ ret = dln2_adc_set_port_enabled(dln2, true, &conflict);
+ if (ret < 0) {
+ mutex_unlock(&dln2->mutex);
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+ if (conflict) {
+ dev_err(&dln2->pdev->dev,
+ "ADC pins conflict with mask %04X\n",
+ (int)conflict);
+ ret = -EBUSY;
+ }
+ return ret;
+ }
+
+ /* Assign trigger channel based on first enabled channel */
+ trigger_chan = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ if (trigger_chan < DLN2_ADC_MAX_CHANNELS) {
+ dln2->trigger_chan = trigger_chan;
+ ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan,
+ dln2->sample_period);
+ mutex_unlock(&dln2->mutex);
+ if (ret < 0) {
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+ return ret;
+ }
+ } else {
+ dln2->trigger_chan = -1;
+ mutex_unlock(&dln2->mutex);
+ }
+
+ return 0;
+}
+
+static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+ mutex_lock(&dln2->mutex);
+
+ /* Disable trigger channel */
+ if (dln2->trigger_chan != -1) {
+ dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0);
+ dln2->trigger_chan = -1;
+ }
+
+ /* Disable ADC */
+ ret = dln2_adc_set_port_enabled(dln2, false, NULL);
+
+ mutex_unlock(&dln2->mutex);
+ if (ret < 0)
+ dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__);
+
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = {
+ .postenable = dln2_adc_triggered_buffer_postenable,
+ .predisable = dln2_adc_triggered_buffer_predisable,
+};
+
+static void dln2_adc_event(struct platform_device *pdev, u16 echo,
+ const void *data, int len)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct dln2_adc *dln2 = iio_priv(indio_dev);
+
+ /* Called via URB completion handler */
+ iio_trigger_poll(dln2->trig);
+}
+
+static int dln2_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dln2_adc *dln2;
+ struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct iio_dev *indio_dev;
+ int i, ret, chans;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
+ if (!indio_dev) {
+ dev_err(dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ dln2 = iio_priv(indio_dev);
+ dln2->pdev = pdev;
+ dln2->port = pdata->port;
+ dln2->trigger_chan = -1;
+ mutex_init(&dln2->mutex);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = dln2_adc_set_port_resolution(dln2);
+ if (ret < 0) {
+ dev_err(dev, "failed to set ADC resolution to 10 bits\n");
+ return ret;
+ }
+
+ chans = dln2_adc_get_chan_count(dln2);
+ if (chans < 0) {
+ dev_err(dev, "failed to get channel count: %d\n", chans);
+ return chans;
+ }
+ if (chans > DLN2_ADC_MAX_CHANNELS) {
+ chans = DLN2_ADC_MAX_CHANNELS;
+ dev_warn(dev, "clamping channels to %d\n",
+ DLN2_ADC_MAX_CHANNELS);
+ }
+
+ for (i = 0; i < chans; ++i)
+ DLN2_ADC_CHAN(dln2->iio_channels[i], i)
+ IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i);
+
+ indio_dev->name = DLN2_ADC_MOD_NAME;
+ indio_dev->info = &dln2_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dln2->iio_channels;
+ indio_dev->num_channels = chans + 1;
+ indio_dev->setup_ops = &dln2_adc_buffer_setup_ops;
+
+ dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!dln2->trig) {
+ dev_err(dev, "failed to allocate trigger\n");
+ return -ENOMEM;
+ }
+ iio_trigger_set_drvdata(dln2->trig, dln2);
+ ret = devm_iio_trigger_register(dev, dln2->trig);
+ if (ret) {
+ dev_err(dev, "failed to register trigger: %d\n", ret);
+ return ret;
+ }
+ iio_trigger_set_immutable(indio_dev, dln2->trig);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ dln2_adc_trigger_h,
+ &dln2_adc_buffer_setup_ops);
+ if (ret) {
+ dev_err(dev, "failed to allocate triggered buffer: %d\n", ret);
+ return ret;
+ }
+
+ ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV,
+ dln2_adc_event);
+ if (ret) {
+ dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret);
+ return ret;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "failed to register iio device: %d\n", ret);
+ goto unregister_event;
+ }
+
+ return ret;
+
+unregister_event:
+ dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+
+ return ret;
+}
+
+static int dln2_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV);
+ return 0;
+}
+
+static struct platform_driver dln2_adc_driver = {
+ .driver.name = DLN2_ADC_MOD_NAME,
+ .probe = dln2_adc_probe,
+ .remove = dln2_adc_remove,
+};
+
+module_platform_driver(dln2_adc_driver);
+
+MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-adc");
diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
new file mode 100644
index 000000000..e911c25d1
--- /dev/null
+++ b/drivers/iio/adc/envelope-detector.c
@@ -0,0 +1,408 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for an envelope detector using a DAC and a comparator
+ *
+ * Copyright (C) 2016 Axentia Technologies AB
+ *
+ * Author: Peter Rosin <peda@axentia.se>
+ */
+
+/*
+ * The DAC is used to find the peak level of an alternating voltage input
+ * signal by a binary search using the output of a comparator wired to
+ * an interrupt pin. Like so:
+ * _
+ * | \
+ * input +------>-------|+ \
+ * | \
+ * .-------. | }---.
+ * | | | / |
+ * | dac|-->--|- / |
+ * | | |_/ |
+ * | | |
+ * | | |
+ * | irq|------<-------'
+ * | |
+ * '-------'
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+struct envelope {
+ spinlock_t comp_lock; /* protects comp */
+ int comp;
+
+ struct mutex read_lock; /* protects everything else */
+
+ int comp_irq;
+ u32 comp_irq_trigger;
+ u32 comp_irq_trigger_inv;
+
+ struct iio_channel *dac;
+ struct delayed_work comp_timeout;
+
+ unsigned int comp_interval;
+ bool invert;
+ u32 dac_max;
+
+ int high;
+ int level;
+ int low;
+
+ struct completion done;
+};
+
+/*
+ * The envelope_detector_comp_latch function works together with the compare
+ * interrupt service routine below (envelope_detector_comp_isr) as a latch
+ * (one-bit memory) for if the interrupt has triggered since last calling
+ * this function.
+ * The ..._comp_isr function disables the interrupt so that the cpu does not
+ * need to service a possible interrupt flood from the comparator when no-one
+ * cares anyway, and this ..._comp_latch function reenables them again if
+ * needed.
+ */
+static int envelope_detector_comp_latch(struct envelope *env)
+{
+ int comp;
+
+ spin_lock_irq(&env->comp_lock);
+ comp = env->comp;
+ env->comp = 0;
+ spin_unlock_irq(&env->comp_lock);
+
+ if (!comp)
+ return 0;
+
+ /*
+ * The irq was disabled, and is reenabled just now.
+ * But there might have been a pending irq that
+ * happened while the irq was disabled that fires
+ * just as the irq is reenabled. That is not what
+ * is desired.
+ */
+ enable_irq(env->comp_irq);
+
+ /* So, synchronize this possibly pending irq... */
+ synchronize_irq(env->comp_irq);
+
+ /* ...and redo the whole dance. */
+ spin_lock_irq(&env->comp_lock);
+ comp = env->comp;
+ env->comp = 0;
+ spin_unlock_irq(&env->comp_lock);
+
+ if (comp)
+ enable_irq(env->comp_irq);
+
+ return 1;
+}
+
+static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx)
+{
+ struct envelope *env = ctx;
+
+ spin_lock(&env->comp_lock);
+ env->comp = 1;
+ disable_irq_nosync(env->comp_irq);
+ spin_unlock(&env->comp_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void envelope_detector_setup_compare(struct envelope *env)
+{
+ int ret;
+
+ /*
+ * Do a binary search for the peak input level, and stop
+ * when that level is "trapped" between two adjacent DAC
+ * values.
+ * When invert is active, use the midpoint floor so that
+ * env->level ends up as env->low when the termination
+ * criteria below is fulfilled, and use the midpoint
+ * ceiling when invert is not active so that env->level
+ * ends up as env->high in that case.
+ */
+ env->level = (env->high + env->low + !env->invert) / 2;
+
+ if (env->high == env->low + 1) {
+ complete(&env->done);
+ return;
+ }
+
+ /* Set a "safe" DAC level (if there is such a thing)... */
+ ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
+ if (ret < 0)
+ goto err;
+
+ /* ...clear the comparison result... */
+ envelope_detector_comp_latch(env);
+
+ /* ...set the real DAC level... */
+ ret = iio_write_channel_raw(env->dac, env->level);
+ if (ret < 0)
+ goto err;
+
+ /* ...and wait for a bit to see if the latch catches anything. */
+ schedule_delayed_work(&env->comp_timeout,
+ msecs_to_jiffies(env->comp_interval));
+ return;
+
+err:
+ env->level = ret;
+ complete(&env->done);
+}
+
+static void envelope_detector_timeout(struct work_struct *work)
+{
+ struct envelope *env = container_of(work, struct envelope,
+ comp_timeout.work);
+
+ /* Adjust low/high depending on the latch content... */
+ if (!envelope_detector_comp_latch(env) ^ !env->invert)
+ env->low = env->level;
+ else
+ env->high = env->level;
+
+ /* ...and continue the search. */
+ envelope_detector_setup_compare(env);
+}
+
+static int envelope_detector_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct envelope *env = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * When invert is active, start with high=max+1 and low=0
+ * since we will end up with the low value when the
+ * termination criteria is fulfilled (rounding down). And
+ * start with high=max and low=-1 when invert is not active
+ * since we will end up with the high value in that case.
+ * This ensures that the returned value in both cases are
+ * in the same range as the DAC and is a value that has not
+ * triggered the comparator.
+ */
+ mutex_lock(&env->read_lock);
+ env->high = env->dac_max + env->invert;
+ env->low = -1 + env->invert;
+ envelope_detector_setup_compare(env);
+ wait_for_completion(&env->done);
+ if (env->level < 0) {
+ ret = env->level;
+ goto err_unlock;
+ }
+ *val = env->invert ? env->dac_max - env->level : env->level;
+ mutex_unlock(&env->read_lock);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ return iio_read_channel_scale(env->dac, val, val2);
+ }
+
+ return -EINVAL;
+
+err_unlock:
+ mutex_unlock(&env->read_lock);
+ return ret;
+}
+
+static ssize_t envelope_show_invert(struct iio_dev *indio_dev,
+ uintptr_t private,
+ struct iio_chan_spec const *ch, char *buf)
+{
+ struct envelope *env = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", env->invert);
+}
+
+static ssize_t envelope_store_invert(struct iio_dev *indio_dev,
+ uintptr_t private,
+ struct iio_chan_spec const *ch,
+ const char *buf, size_t len)
+{
+ struct envelope *env = iio_priv(indio_dev);
+ unsigned long invert;
+ int ret;
+ u32 trigger;
+
+ ret = kstrtoul(buf, 0, &invert);
+ if (ret < 0)
+ return ret;
+ if (invert > 1)
+ return -EINVAL;
+
+ trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger;
+
+ mutex_lock(&env->read_lock);
+ if (invert != env->invert)
+ ret = irq_set_irq_type(env->comp_irq, trigger);
+ if (!ret) {
+ env->invert = invert;
+ ret = len;
+ }
+ mutex_unlock(&env->read_lock);
+
+ return ret;
+}
+
+static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev,
+ uintptr_t private,
+ struct iio_chan_spec const *ch,
+ char *buf)
+{
+ struct envelope *env = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", env->comp_interval);
+}
+
+static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev,
+ uintptr_t private,
+ struct iio_chan_spec const *ch,
+ const char *buf, size_t len)
+{
+ struct envelope *env = iio_priv(indio_dev);
+ unsigned long interval;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &interval);
+ if (ret < 0)
+ return ret;
+ if (interval > 1000)
+ return -EINVAL;
+
+ mutex_lock(&env->read_lock);
+ env->comp_interval = interval;
+ mutex_unlock(&env->read_lock);
+
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = {
+ { .name = "invert",
+ .read = envelope_show_invert,
+ .write = envelope_store_invert, },
+ { .name = "compare_interval",
+ .read = envelope_show_comp_interval,
+ .write = envelope_store_comp_interval, },
+ { /* sentinel */ }
+};
+
+static const struct iio_chan_spec envelope_detector_iio_channel = {
+ .type = IIO_ALTVOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = envelope_detector_ext_info,
+ .indexed = 1,
+};
+
+static const struct iio_info envelope_detector_info = {
+ .read_raw = &envelope_detector_read_raw,
+};
+
+static int envelope_detector_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct envelope *env;
+ enum iio_chan_type type;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, indio_dev);
+ env = iio_priv(indio_dev);
+ env->comp_interval = 50; /* some sensible default? */
+
+ spin_lock_init(&env->comp_lock);
+ mutex_init(&env->read_lock);
+ init_completion(&env->done);
+ INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->info = &envelope_detector_info;
+ indio_dev->channels = &envelope_detector_iio_channel;
+ indio_dev->num_channels = 1;
+
+ env->dac = devm_iio_channel_get(dev, "dac");
+ if (IS_ERR(env->dac))
+ return dev_err_probe(dev, PTR_ERR(env->dac),
+ "failed to get dac input channel\n");
+
+ env->comp_irq = platform_get_irq_byname(pdev, "comp");
+ if (env->comp_irq < 0)
+ return env->comp_irq;
+
+ ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
+ 0, "envelope-detector", env);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request interrupt\n");
+
+ env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq);
+ if (env->comp_irq_trigger & IRQF_TRIGGER_RISING)
+ env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING;
+ if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING)
+ env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING;
+ if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH)
+ env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW;
+ if (env->comp_irq_trigger & IRQF_TRIGGER_LOW)
+ env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH;
+
+ ret = iio_get_channel_type(env->dac, &type);
+ if (ret < 0)
+ return ret;
+
+ if (type != IIO_VOLTAGE) {
+ dev_err(dev, "dac is of the wrong type\n");
+ return -EINVAL;
+ }
+
+ ret = iio_read_max_channel_raw(env->dac, &env->dac_max);
+ if (ret < 0) {
+ dev_err(dev, "dac does not indicate its raw maximum value\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id envelope_detector_match[] = {
+ { .compatible = "axentia,tse850-envelope-detector", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, envelope_detector_match);
+
+static struct platform_driver envelope_detector_driver = {
+ .probe = envelope_detector_probe,
+ .driver = {
+ .name = "iio-envelope-detector",
+ .of_match_table = envelope_detector_match,
+ },
+};
+module_platform_driver(envelope_detector_driver);
+
+MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c
new file mode 100644
index 000000000..fd5a9404c
--- /dev/null
+++ b/drivers/iio/adc/ep93xx_adc.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADC module on the Cirrus Logic EP93xx series of SoCs
+ *
+ * Copyright (C) 2015 Alexander Sverdlin
+ *
+ * The driver uses polling to get the conversion status. According to EP93xx
+ * datasheets, reading ADCResult register starts the conversion, but user is also
+ * responsible for ensuring that delay between adjacent conversion triggers is
+ * long enough so that maximum allowed conversion rate is not exceeded. This
+ * basically renders IRQ mode unusable.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/io.h>
+#include <linux/irqflags.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+/*
+ * This code could benefit from real HR Timers, but jiffy granularity would
+ * lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait
+ * in such case.
+ *
+ * HR Timers-based version loads CPU only up to 10% during back to back ADC
+ * conversion, while busy wait-based version consumes whole CPU power.
+ */
+#ifdef CONFIG_HIGH_RES_TIMERS
+#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax)
+#else
+#define ep93xx_adc_delay(usmin, usmax) udelay(usmin)
+#endif
+
+#define EP93XX_ADC_RESULT 0x08
+#define EP93XX_ADC_SDR BIT(31)
+#define EP93XX_ADC_SWITCH 0x18
+#define EP93XX_ADC_SW_LOCK 0x20
+
+struct ep93xx_adc_priv {
+ struct clk *clk;
+ void __iomem *base;
+ int lastch;
+ struct mutex lock;
+};
+
+#define EP93XX_ADC_CH(index, dname, swcfg) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .address = swcfg, \
+ .datasheet_name = dname, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+/*
+ * Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets.
+ * EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is
+ * not defined. So the last three are numbered randomly, let's say.
+ */
+static const struct iio_chan_spec ep93xx_adc_channels[8] = {
+ EP93XX_ADC_CH(0, "YM", 0x608),
+ EP93XX_ADC_CH(1, "SXP", 0x680),
+ EP93XX_ADC_CH(2, "SXM", 0x640),
+ EP93XX_ADC_CH(3, "SYP", 0x620),
+ EP93XX_ADC_CH(4, "SYM", 0x610),
+ EP93XX_ADC_CH(5, "XP", 0x601),
+ EP93XX_ADC_CH(6, "XM", 0x602),
+ EP93XX_ADC_CH(7, "YP", 0x604),
+};
+
+static int ep93xx_read_raw(struct iio_dev *iiodev,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct ep93xx_adc_priv *priv = iio_priv(iiodev);
+ unsigned long timeout;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&priv->lock);
+ if (priv->lastch != channel->channel) {
+ priv->lastch = channel->channel;
+ /*
+ * Switch register is software-locked, unlocking must be
+ * immediately followed by write
+ */
+ local_irq_disable();
+ writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK);
+ writel_relaxed(channel->address,
+ priv->base + EP93XX_ADC_SWITCH);
+ local_irq_enable();
+ /*
+ * Settling delay depends on module clock and could be
+ * 2ms or 500us
+ */
+ ep93xx_adc_delay(2000, 2000);
+ }
+ /* Start the conversion, eventually discarding old result */
+ readl_relaxed(priv->base + EP93XX_ADC_RESULT);
+ /* Ensure maximum conversion rate is not exceeded */
+ ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925),
+ DIV_ROUND_UP(1000000, 925));
+ /* At this point conversion must be completed, but anyway... */
+ ret = IIO_VAL_INT;
+ timeout = jiffies + msecs_to_jiffies(1) + 1;
+ while (1) {
+ u32 t;
+
+ t = readl_relaxed(priv->base + EP93XX_ADC_RESULT);
+ if (t & EP93XX_ADC_SDR) {
+ *value = sign_extend32(t, 15);
+ break;
+ }
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(&iiodev->dev, "Conversion timeout\n");
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ cpu_relax();
+ }
+ mutex_unlock(&priv->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_OFFSET:
+ /* According to datasheet, range is -25000..25000 */
+ *value = 25000;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ /* Typical supply voltage is 3.3v */
+ *value = (1ULL << 32) * 3300 / 50000;
+ *shift = 32;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ep93xx_adc_info = {
+ .read_raw = ep93xx_read_raw,
+};
+
+static int ep93xx_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct iio_dev *iiodev;
+ struct ep93xx_adc_priv *priv;
+ struct clk *pclk;
+
+ iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!iiodev)
+ return -ENOMEM;
+ priv = iio_priv(iiodev);
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ iiodev->name = dev_name(&pdev->dev);
+ iiodev->modes = INDIO_DIRECT_MODE;
+ iiodev->info = &ep93xx_adc_info;
+ iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels);
+ iiodev->channels = ep93xx_adc_channels;
+
+ priv->lastch = -1;
+ mutex_init(&priv->lock);
+
+ platform_set_drvdata(pdev, iiodev);
+
+ priv->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "Cannot obtain clock\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ pclk = clk_get_parent(priv->clk);
+ if (!pclk) {
+ dev_warn(&pdev->dev, "Cannot obtain parent clock\n");
+ } else {
+ /*
+ * This is actually a place for improvement:
+ * EP93xx ADC supports two clock divisors -- 4 and 16,
+ * resulting in conversion rates 3750 and 925 samples per second
+ * with 500us or 2ms settling time respectively.
+ * One might find this interesting enough to be configurable.
+ */
+ ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16);
+ if (ret)
+ dev_warn(&pdev->dev, "Cannot set clock rate\n");
+ /*
+ * We can tolerate rate setting failure because the module should
+ * work in any case.
+ */
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Cannot enable clock\n");
+ return ret;
+ }
+
+ ret = iio_device_register(iiodev);
+ if (ret)
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static int ep93xx_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *iiodev = platform_get_drvdata(pdev);
+ struct ep93xx_adc_priv *priv = iio_priv(iiodev);
+
+ iio_device_unregister(iiodev);
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_adc_driver = {
+ .driver = {
+ .name = "ep93xx-adc",
+ },
+ .probe = ep93xx_adc_probe,
+ .remove = ep93xx_adc_remove,
+};
+module_platform_driver(ep93xx_adc_driver);
+
+MODULE_AUTHOR("Alexander Sverdlin <alexander.sverdlin@gmail.com>");
+MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-adc");
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
new file mode 100644
index 000000000..43c8af41b
--- /dev/null
+++ b/drivers/iio/adc/exynos_adc.c
@@ -0,0 +1,1025 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * exynos_adc.c - Support for ADC in EXYNOS SoCs
+ *
+ * 8 ~ 10 channel, 10/12-bit ADC
+ *
+ * Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
+ */
+
+#include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+#include <linux/input.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <linux/platform_data/touchscreen-s3c2410.h>
+
+/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
+#define ADC_V1_CON(x) ((x) + 0x00)
+#define ADC_V1_TSC(x) ((x) + 0x04)
+#define ADC_V1_DLY(x) ((x) + 0x08)
+#define ADC_V1_DATX(x) ((x) + 0x0C)
+#define ADC_V1_DATY(x) ((x) + 0x10)
+#define ADC_V1_UPDN(x) ((x) + 0x14)
+#define ADC_V1_INTCLR(x) ((x) + 0x18)
+#define ADC_V1_MUX(x) ((x) + 0x1c)
+#define ADC_V1_CLRINTPNDNUP(x) ((x) + 0x20)
+
+/* S3C2410 ADC registers definitions */
+#define ADC_S3C2410_MUX(x) ((x) + 0x18)
+
+/* Future ADC_V2 registers definitions */
+#define ADC_V2_CON1(x) ((x) + 0x00)
+#define ADC_V2_CON2(x) ((x) + 0x04)
+#define ADC_V2_STAT(x) ((x) + 0x08)
+#define ADC_V2_INT_EN(x) ((x) + 0x10)
+#define ADC_V2_INT_ST(x) ((x) + 0x14)
+#define ADC_V2_VER(x) ((x) + 0x20)
+
+/* Bit definitions for ADC_V1 */
+#define ADC_V1_CON_RES (1u << 16)
+#define ADC_V1_CON_PRSCEN (1u << 14)
+#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
+#define ADC_V1_CON_STANDBY (1u << 2)
+
+/* Bit definitions for S3C2410 ADC */
+#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3)
+#define ADC_S3C2410_DATX_MASK 0x3FF
+#define ADC_S3C2416_CON_RES_SEL (1u << 3)
+
+/* touch screen always uses channel 0 */
+#define ADC_S3C2410_MUX_TS 0
+
+/* ADCTSC Register Bits */
+#define ADC_S3C2443_TSC_UD_SEN (1u << 8)
+#define ADC_S3C2410_TSC_YM_SEN (1u << 7)
+#define ADC_S3C2410_TSC_YP_SEN (1u << 6)
+#define ADC_S3C2410_TSC_XM_SEN (1u << 5)
+#define ADC_S3C2410_TSC_XP_SEN (1u << 4)
+#define ADC_S3C2410_TSC_PULL_UP_DISABLE (1u << 3)
+#define ADC_S3C2410_TSC_AUTO_PST (1u << 2)
+#define ADC_S3C2410_TSC_XY_PST(x) (((x) & 0x3) << 0)
+
+#define ADC_TSC_WAIT4INT (ADC_S3C2410_TSC_YM_SEN | \
+ ADC_S3C2410_TSC_YP_SEN | \
+ ADC_S3C2410_TSC_XP_SEN | \
+ ADC_S3C2410_TSC_XY_PST(3))
+
+#define ADC_TSC_AUTOPST (ADC_S3C2410_TSC_YM_SEN | \
+ ADC_S3C2410_TSC_YP_SEN | \
+ ADC_S3C2410_TSC_XP_SEN | \
+ ADC_S3C2410_TSC_AUTO_PST | \
+ ADC_S3C2410_TSC_XY_PST(0))
+
+/* Bit definitions for ADC_V2 */
+#define ADC_V2_CON1_SOFT_RESET (1u << 2)
+
+#define ADC_V2_CON2_OSEL (1u << 10)
+#define ADC_V2_CON2_ESEL (1u << 9)
+#define ADC_V2_CON2_HIGHF (1u << 8)
+#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4)
+#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0)
+#define ADC_V2_CON2_ACH_MASK 0xF
+
+#define MAX_ADC_V2_CHANNELS 10
+#define MAX_ADC_V1_CHANNELS 8
+#define MAX_EXYNOS3250_ADC_CHANNELS 2
+#define MAX_EXYNOS4212_ADC_CHANNELS 4
+#define MAX_S5PV210_ADC_CHANNELS 10
+
+/* Bit definitions common for ADC_V1 and ADC_V2 */
+#define ADC_CON_EN_START (1u << 0)
+#define ADC_CON_EN_START_MASK (0x3 << 0)
+#define ADC_DATX_PRESSED (1u << 15)
+#define ADC_DATX_MASK 0xFFF
+#define ADC_DATY_MASK 0xFFF
+
+#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
+
+#define EXYNOS_ADCV1_PHY_OFFSET 0x0718
+#define EXYNOS_ADCV2_PHY_OFFSET 0x0720
+
+struct exynos_adc {
+ struct exynos_adc_data *data;
+ struct device *dev;
+ struct input_dev *input;
+ void __iomem *regs;
+ struct regmap *pmu_map;
+ struct clk *clk;
+ struct clk *sclk;
+ unsigned int irq;
+ unsigned int tsirq;
+ unsigned int delay;
+ struct regulator *vdd;
+
+ struct completion completion;
+
+ u32 value;
+ unsigned int version;
+
+ bool ts_enabled;
+
+ bool read_ts;
+ u32 ts_x;
+ u32 ts_y;
+
+ /*
+ * Lock to protect from potential concurrent access to the
+ * completion callback during a manual conversion. For this driver
+ * a wait-callback is used to wait for the conversion result,
+ * so in the meantime no other read request (or conversion start)
+ * must be performed, otherwise it would interfere with the
+ * current conversion result.
+ */
+ struct mutex lock;
+};
+
+struct exynos_adc_data {
+ int num_channels;
+ bool needs_sclk;
+ bool needs_adc_phy;
+ int phy_offset;
+ u32 mask;
+
+ void (*init_hw)(struct exynos_adc *info);
+ void (*exit_hw)(struct exynos_adc *info);
+ void (*clear_irq)(struct exynos_adc *info);
+ void (*start_conv)(struct exynos_adc *info, unsigned long addr);
+};
+
+static void exynos_adc_unprepare_clk(struct exynos_adc *info)
+{
+ if (info->data->needs_sclk)
+ clk_unprepare(info->sclk);
+ clk_unprepare(info->clk);
+}
+
+static int exynos_adc_prepare_clk(struct exynos_adc *info)
+{
+ int ret;
+
+ ret = clk_prepare(info->clk);
+ if (ret) {
+ dev_err(info->dev, "failed preparing adc clock: %d\n", ret);
+ return ret;
+ }
+
+ if (info->data->needs_sclk) {
+ ret = clk_prepare(info->sclk);
+ if (ret) {
+ clk_unprepare(info->clk);
+ dev_err(info->dev,
+ "failed preparing sclk_adc clock: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void exynos_adc_disable_clk(struct exynos_adc *info)
+{
+ if (info->data->needs_sclk)
+ clk_disable(info->sclk);
+ clk_disable(info->clk);
+}
+
+static int exynos_adc_enable_clk(struct exynos_adc *info)
+{
+ int ret;
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ dev_err(info->dev, "failed enabling adc clock: %d\n", ret);
+ return ret;
+ }
+
+ if (info->data->needs_sclk) {
+ ret = clk_enable(info->sclk);
+ if (ret) {
+ clk_disable(info->clk);
+ dev_err(info->dev,
+ "failed enabling sclk_adc clock: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void exynos_adc_v1_init_hw(struct exynos_adc *info)
+{
+ u32 con1;
+
+ if (info->data->needs_adc_phy)
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
+
+ /* set default prescaler values and Enable prescaler */
+ con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
+
+ /* Enable 12-bit ADC resolution */
+ con1 |= ADC_V1_CON_RES;
+ writel(con1, ADC_V1_CON(info->regs));
+
+ /* set touchscreen delay */
+ writel(info->delay, ADC_V1_DLY(info->regs));
+}
+
+static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
+{
+ u32 con;
+
+ if (info->data->needs_adc_phy)
+ regmap_write(info->pmu_map, info->data->phy_offset, 0);
+
+ con = readl(ADC_V1_CON(info->regs));
+ con |= ADC_V1_CON_STANDBY;
+ writel(con, ADC_V1_CON(info->regs));
+}
+
+static void exynos_adc_v1_clear_irq(struct exynos_adc *info)
+{
+ writel(1, ADC_V1_INTCLR(info->regs));
+}
+
+static void exynos_adc_v1_start_conv(struct exynos_adc *info,
+ unsigned long addr)
+{
+ u32 con1;
+
+ writel(addr, ADC_V1_MUX(info->regs));
+
+ con1 = readl(ADC_V1_CON(info->regs));
+ writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+/* Exynos4212 and 4412 is like ADCv1 but with four channels only */
+static const struct exynos_adc_data exynos4212_adc_data = {
+ .num_channels = MAX_EXYNOS4212_ADC_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+ .needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .clear_irq = exynos_adc_v1_clear_irq,
+ .start_conv = exynos_adc_v1_start_conv,
+};
+
+static const struct exynos_adc_data exynos_adc_v1_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+ .needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .clear_irq = exynos_adc_v1_clear_irq,
+ .start_conv = exynos_adc_v1_start_conv,
+};
+
+static const struct exynos_adc_data exynos_adc_s5pv210_data = {
+ .num_channels = MAX_S5PV210_ADC_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .clear_irq = exynos_adc_v1_clear_irq,
+ .start_conv = exynos_adc_v1_start_conv,
+};
+
+static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
+ unsigned long addr)
+{
+ u32 con1;
+
+ /* Enable 12 bit ADC resolution */
+ con1 = readl(ADC_V1_CON(info->regs));
+ con1 |= ADC_S3C2416_CON_RES_SEL;
+ writel(con1, ADC_V1_CON(info->regs));
+
+ /* Select channel for S3C2416 */
+ writel(addr, ADC_S3C2410_MUX(info->regs));
+
+ con1 = readl(ADC_V1_CON(info->regs));
+ writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+static struct exynos_adc_data const exynos_adc_s3c2416_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .start_conv = exynos_adc_s3c2416_start_conv,
+};
+
+static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info,
+ unsigned long addr)
+{
+ u32 con1;
+
+ /* Select channel for S3C2433 */
+ writel(addr, ADC_S3C2410_MUX(info->regs));
+
+ con1 = readl(ADC_V1_CON(info->regs));
+ writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+static struct exynos_adc_data const exynos_adc_s3c2443_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .start_conv = exynos_adc_s3c2443_start_conv,
+};
+
+static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
+ unsigned long addr)
+{
+ u32 con1;
+
+ con1 = readl(ADC_V1_CON(info->regs));
+ con1 &= ~ADC_S3C2410_CON_SELMUX(0x7);
+ con1 |= ADC_S3C2410_CON_SELMUX(addr);
+ writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
+}
+
+static struct exynos_adc_data const exynos_adc_s3c24xx_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .start_conv = exynos_adc_s3c64xx_start_conv,
+};
+
+static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .clear_irq = exynos_adc_v1_clear_irq,
+ .start_conv = exynos_adc_s3c64xx_start_conv,
+};
+
+static void exynos_adc_v2_init_hw(struct exynos_adc *info)
+{
+ u32 con1, con2;
+
+ if (info->data->needs_adc_phy)
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
+
+ con1 = ADC_V2_CON1_SOFT_RESET;
+ writel(con1, ADC_V2_CON1(info->regs));
+
+ con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
+ ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ /* Enable interrupts */
+ writel(1, ADC_V2_INT_EN(info->regs));
+}
+
+static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
+{
+ u32 con;
+
+ if (info->data->needs_adc_phy)
+ regmap_write(info->pmu_map, info->data->phy_offset, 0);
+
+ con = readl(ADC_V2_CON1(info->regs));
+ con &= ~ADC_CON_EN_START;
+ writel(con, ADC_V2_CON1(info->regs));
+}
+
+static void exynos_adc_v2_clear_irq(struct exynos_adc *info)
+{
+ writel(1, ADC_V2_INT_ST(info->regs));
+}
+
+static void exynos_adc_v2_start_conv(struct exynos_adc *info,
+ unsigned long addr)
+{
+ u32 con1, con2;
+
+ con2 = readl(ADC_V2_CON2(info->regs));
+ con2 &= ~ADC_V2_CON2_ACH_MASK;
+ con2 |= ADC_V2_CON2_ACH_SEL(addr);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ con1 = readl(ADC_V2_CON1(info->regs));
+ writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs));
+}
+
+static const struct exynos_adc_data exynos_adc_v2_data = {
+ .num_channels = MAX_ADC_V2_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+ .needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV2_PHY_OFFSET,
+
+ .init_hw = exynos_adc_v2_init_hw,
+ .exit_hw = exynos_adc_v2_exit_hw,
+ .clear_irq = exynos_adc_v2_clear_irq,
+ .start_conv = exynos_adc_v2_start_conv,
+};
+
+static const struct exynos_adc_data exynos3250_adc_data = {
+ .num_channels = MAX_EXYNOS3250_ADC_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+ .needs_sclk = true,
+ .needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
+
+ .init_hw = exynos_adc_v2_init_hw,
+ .exit_hw = exynos_adc_v2_exit_hw,
+ .clear_irq = exynos_adc_v2_clear_irq,
+ .start_conv = exynos_adc_v2_start_conv,
+};
+
+static void exynos_adc_exynos7_init_hw(struct exynos_adc *info)
+{
+ u32 con1, con2;
+
+ con1 = ADC_V2_CON1_SOFT_RESET;
+ writel(con1, ADC_V2_CON1(info->regs));
+
+ con2 = readl(ADC_V2_CON2(info->regs));
+ con2 &= ~ADC_V2_CON2_C_TIME(7);
+ con2 |= ADC_V2_CON2_C_TIME(0);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ /* Enable interrupts */
+ writel(1, ADC_V2_INT_EN(info->regs));
+}
+
+static const struct exynos_adc_data exynos7_adc_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_exynos7_init_hw,
+ .exit_hw = exynos_adc_v2_exit_hw,
+ .clear_irq = exynos_adc_v2_clear_irq,
+ .start_conv = exynos_adc_v2_start_conv,
+};
+
+static const struct of_device_id exynos_adc_match[] = {
+ {
+ .compatible = "samsung,s3c2410-adc",
+ .data = &exynos_adc_s3c24xx_data,
+ }, {
+ .compatible = "samsung,s3c2416-adc",
+ .data = &exynos_adc_s3c2416_data,
+ }, {
+ .compatible = "samsung,s3c2440-adc",
+ .data = &exynos_adc_s3c24xx_data,
+ }, {
+ .compatible = "samsung,s3c2443-adc",
+ .data = &exynos_adc_s3c2443_data,
+ }, {
+ .compatible = "samsung,s3c6410-adc",
+ .data = &exynos_adc_s3c64xx_data,
+ }, {
+ .compatible = "samsung,s5pv210-adc",
+ .data = &exynos_adc_s5pv210_data,
+ }, {
+ .compatible = "samsung,exynos4212-adc",
+ .data = &exynos4212_adc_data,
+ }, {
+ .compatible = "samsung,exynos-adc-v1",
+ .data = &exynos_adc_v1_data,
+ }, {
+ .compatible = "samsung,exynos-adc-v2",
+ .data = &exynos_adc_v2_data,
+ }, {
+ .compatible = "samsung,exynos3250-adc",
+ .data = &exynos3250_adc_data,
+ }, {
+ .compatible = "samsung,exynos7-adc",
+ .data = &exynos7_adc_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_adc_match);
+
+static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(exynos_adc_match, pdev->dev.of_node);
+ return (struct exynos_adc_data *)match->data;
+}
+
+static int exynos_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct exynos_adc *info = iio_priv(indio_dev);
+ unsigned long timeout;
+ int ret;
+
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ ret = regulator_get_voltage(info->vdd);
+ if (ret < 0)
+ return ret;
+
+ /* Regulator voltage is in uV, but need mV */
+ *val = ret / 1000;
+ *val2 = info->data->mask;
+
+ return IIO_VAL_FRACTIONAL;
+ } else if (mask != IIO_CHAN_INFO_RAW) {
+ return -EINVAL;
+ }
+
+ mutex_lock(&info->lock);
+ reinit_completion(&info->completion);
+
+ /* Select the channel to be used and Trigger conversion */
+ if (info->data->start_conv)
+ info->data->start_conv(info, chan->address);
+
+ timeout = wait_for_completion_timeout(&info->completion,
+ EXYNOS_ADC_TIMEOUT);
+ if (timeout == 0) {
+ dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
+ if (info->data->init_hw)
+ info->data->init_hw(info);
+ ret = -ETIMEDOUT;
+ } else {
+ *val = info->value;
+ *val2 = 0;
+ ret = IIO_VAL_INT;
+ }
+
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
+{
+ struct exynos_adc *info = iio_priv(indio_dev);
+ unsigned long timeout;
+ int ret;
+
+ mutex_lock(&info->lock);
+ info->read_ts = true;
+
+ reinit_completion(&info->completion);
+
+ writel(ADC_S3C2410_TSC_PULL_UP_DISABLE | ADC_TSC_AUTOPST,
+ ADC_V1_TSC(info->regs));
+
+ /* Select the ts channel to be used and Trigger conversion */
+ info->data->start_conv(info, ADC_S3C2410_MUX_TS);
+
+ timeout = wait_for_completion_timeout(&info->completion,
+ EXYNOS_ADC_TIMEOUT);
+ if (timeout == 0) {
+ dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
+ if (info->data->init_hw)
+ info->data->init_hw(info);
+ ret = -ETIMEDOUT;
+ } else {
+ *x = info->ts_x;
+ *y = info->ts_y;
+ ret = 0;
+ }
+
+ info->read_ts = false;
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
+{
+ struct exynos_adc *info = dev_id;
+ u32 mask = info->data->mask;
+
+ /* Read value */
+ if (info->read_ts) {
+ info->ts_x = readl(ADC_V1_DATX(info->regs));
+ info->ts_y = readl(ADC_V1_DATY(info->regs));
+ writel(ADC_TSC_WAIT4INT | ADC_S3C2443_TSC_UD_SEN, ADC_V1_TSC(info->regs));
+ } else {
+ info->value = readl(ADC_V1_DATX(info->regs)) & mask;
+ }
+
+ /* clear irq */
+ if (info->data->clear_irq)
+ info->data->clear_irq(info);
+
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Here we (ab)use a threaded interrupt handler to stay running
+ * for as long as the touchscreen remains pressed, we report
+ * a new event with the latest data and then sleep until the
+ * next timer tick. This mirrors the behavior of the old
+ * driver, with much less code.
+ */
+static irqreturn_t exynos_ts_isr(int irq, void *dev_id)
+{
+ struct exynos_adc *info = dev_id;
+ struct iio_dev *dev = dev_get_drvdata(info->dev);
+ u32 x, y;
+ bool pressed;
+ int ret;
+
+ while (READ_ONCE(info->ts_enabled)) {
+ ret = exynos_read_s3c64xx_ts(dev, &x, &y);
+ if (ret == -ETIMEDOUT)
+ break;
+
+ pressed = x & y & ADC_DATX_PRESSED;
+ if (!pressed) {
+ input_report_key(info->input, BTN_TOUCH, 0);
+ input_sync(info->input);
+ break;
+ }
+
+ input_report_abs(info->input, ABS_X, x & ADC_DATX_MASK);
+ input_report_abs(info->input, ABS_Y, y & ADC_DATY_MASK);
+ input_report_key(info->input, BTN_TOUCH, 1);
+ input_sync(info->input);
+
+ usleep_range(1000, 1100);
+ }
+
+ writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
+
+ return IRQ_HANDLED;
+}
+
+static int exynos_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct exynos_adc *info = iio_priv(indio_dev);
+
+ if (readval == NULL)
+ return -EINVAL;
+
+ *readval = readl(info->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info exynos_adc_iio_info = {
+ .read_raw = &exynos_read_raw,
+ .debugfs_reg_access = &exynos_adc_reg_access,
+};
+
+#define ADC_CHANNEL(_index, _id) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .address = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _id, \
+}
+
+static const struct iio_chan_spec exynos_adc_iio_channels[] = {
+ ADC_CHANNEL(0, "adc0"),
+ ADC_CHANNEL(1, "adc1"),
+ ADC_CHANNEL(2, "adc2"),
+ ADC_CHANNEL(3, "adc3"),
+ ADC_CHANNEL(4, "adc4"),
+ ADC_CHANNEL(5, "adc5"),
+ ADC_CHANNEL(6, "adc6"),
+ ADC_CHANNEL(7, "adc7"),
+ ADC_CHANNEL(8, "adc8"),
+ ADC_CHANNEL(9, "adc9"),
+};
+
+static int exynos_adc_remove_devices(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int exynos_adc_ts_open(struct input_dev *dev)
+{
+ struct exynos_adc *info = input_get_drvdata(dev);
+
+ WRITE_ONCE(info->ts_enabled, true);
+ enable_irq(info->tsirq);
+
+ return 0;
+}
+
+static void exynos_adc_ts_close(struct input_dev *dev)
+{
+ struct exynos_adc *info = input_get_drvdata(dev);
+
+ WRITE_ONCE(info->ts_enabled, false);
+ disable_irq(info->tsirq);
+}
+
+static int exynos_adc_ts_init(struct exynos_adc *info)
+{
+ int ret;
+
+ if (info->tsirq <= 0)
+ return -ENODEV;
+
+ info->input = input_allocate_device();
+ if (!info->input)
+ return -ENOMEM;
+
+ info->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ info->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(info->input, ABS_X, 0, 0x3FF, 0, 0);
+ input_set_abs_params(info->input, ABS_Y, 0, 0x3FF, 0, 0);
+
+ info->input->name = "S3C24xx TouchScreen";
+ info->input->id.bustype = BUS_HOST;
+ info->input->open = exynos_adc_ts_open;
+ info->input->close = exynos_adc_ts_close;
+
+ input_set_drvdata(info->input, info);
+
+ ret = input_register_device(info->input);
+ if (ret) {
+ input_free_device(info->input);
+ return ret;
+ }
+
+ ret = request_threaded_irq(info->tsirq, NULL, exynos_ts_isr,
+ IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ "touchscreen", info);
+ if (ret)
+ input_unregister_device(info->input);
+
+ return ret;
+}
+
+static int exynos_adc_probe(struct platform_device *pdev)
+{
+ struct exynos_adc *info = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev);
+ struct iio_dev *indio_dev = NULL;
+ bool has_ts = false;
+ int ret;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+
+ info->data = exynos_adc_get_data(pdev);
+ if (!info->data) {
+ dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
+ return -EINVAL;
+ }
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+
+ if (info->data->needs_adc_phy) {
+ info->pmu_map = syscon_regmap_lookup_by_phandle(
+ pdev->dev.of_node,
+ "samsung,syscon-phandle");
+ if (IS_ERR(info->pmu_map)) {
+ dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
+ return PTR_ERR(info->pmu_map);
+ }
+ }
+
+ /* leave out any TS related code if unreachable */
+ if (IS_REACHABLE(CONFIG_INPUT)) {
+ has_ts = of_property_read_bool(pdev->dev.of_node,
+ "has-touchscreen") || pdata;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ info->irq = irq;
+
+ if (has_ts) {
+ irq = platform_get_irq(pdev, 1);
+ if (irq == -EPROBE_DEFER)
+ return irq;
+
+ info->tsirq = irq;
+ } else {
+ info->tsirq = -1;
+ }
+
+ info->dev = &pdev->dev;
+
+ init_completion(&info->completion);
+
+ 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);
+ }
+
+ if (info->data->needs_sclk) {
+ info->sclk = devm_clk_get(&pdev->dev, "sclk");
+ if (IS_ERR(info->sclk)) {
+ dev_err(&pdev->dev,
+ "failed getting sclk clock, err = %ld\n",
+ PTR_ERR(info->sclk));
+ return PTR_ERR(info->sclk);
+ }
+ }
+
+ info->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(info->vdd))
+ return dev_err_probe(&pdev->dev, PTR_ERR(info->vdd),
+ "failed getting regulator");
+
+ ret = regulator_enable(info->vdd);
+ if (ret)
+ return ret;
+
+ ret = exynos_adc_prepare_clk(info);
+ if (ret)
+ goto err_disable_reg;
+
+ ret = exynos_adc_enable_clk(info);
+ if (ret)
+ goto err_unprepare_clk;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &exynos_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = exynos_adc_iio_channels;
+ indio_dev->num_channels = info->data->num_channels;
+
+ mutex_init(&info->lock);
+
+ ret = request_irq(info->irq, exynos_adc_isr,
+ 0, dev_name(&pdev->dev), info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+ info->irq);
+ goto err_disable_clk;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_irq;
+
+ if (info->data->init_hw)
+ info->data->init_hw(info);
+
+ if (pdata)
+ info->delay = pdata->delay;
+ else
+ info->delay = 10000;
+
+ if (has_ts)
+ ret = exynos_adc_ts_init(info);
+ if (ret)
+ goto err_iio;
+
+ ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed adding child nodes\n");
+ goto err_of_populate;
+ }
+
+ return 0;
+
+err_of_populate:
+ device_for_each_child(&indio_dev->dev, NULL,
+ exynos_adc_remove_devices);
+ if (has_ts) {
+ input_unregister_device(info->input);
+ free_irq(info->tsirq, info);
+ }
+err_iio:
+ iio_device_unregister(indio_dev);
+err_irq:
+ free_irq(info->irq, info);
+err_disable_clk:
+ if (info->data->exit_hw)
+ info->data->exit_hw(info);
+ exynos_adc_disable_clk(info);
+err_unprepare_clk:
+ exynos_adc_unprepare_clk(info);
+err_disable_reg:
+ regulator_disable(info->vdd);
+ return ret;
+}
+
+static int exynos_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct exynos_adc *info = iio_priv(indio_dev);
+
+ if (IS_REACHABLE(CONFIG_INPUT) && info->input) {
+ free_irq(info->tsirq, info);
+ input_unregister_device(info->input);
+ }
+ device_for_each_child(&indio_dev->dev, NULL,
+ exynos_adc_remove_devices);
+ iio_device_unregister(indio_dev);
+ free_irq(info->irq, info);
+ if (info->data->exit_hw)
+ info->data->exit_hw(info);
+ exynos_adc_disable_clk(info);
+ exynos_adc_unprepare_clk(info);
+ regulator_disable(info->vdd);
+
+ return 0;
+}
+
+static int exynos_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct exynos_adc *info = iio_priv(indio_dev);
+
+ if (info->data->exit_hw)
+ info->data->exit_hw(info);
+ exynos_adc_disable_clk(info);
+ regulator_disable(info->vdd);
+
+ return 0;
+}
+
+static int exynos_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct exynos_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vdd);
+ if (ret)
+ return ret;
+
+ ret = exynos_adc_enable_clk(info);
+ if (ret)
+ return ret;
+
+ if (info->data->init_hw)
+ info->data->init_hw(info);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, exynos_adc_suspend,
+ exynos_adc_resume);
+
+static struct platform_driver exynos_adc_driver = {
+ .probe = exynos_adc_probe,
+ .remove = exynos_adc_remove,
+ .driver = {
+ .name = "exynos-adc",
+ .of_match_table = exynos_adc_match,
+ .pm = pm_sleep_ptr(&exynos_adc_pm_ops),
+ },
+};
+
+module_platform_driver(exynos_adc_driver);
+
+MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
new file mode 100644
index 000000000..551e83ae5
--- /dev/null
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This is the driver for the imx25 GCQ (Generic Conversion Queue)
+ * connected to the imx25 ADC.
+ */
+
+#include <dt-bindings/iio/adc/fsl-imx25-gcq.h>
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/imx25-tsadc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000))
+
+static const char * const driver_name = "mx25-gcq";
+
+enum mx25_gcq_cfgs {
+ MX25_CFG_XP = 0,
+ MX25_CFG_YP,
+ MX25_CFG_XN,
+ MX25_CFG_YN,
+ MX25_CFG_WIPER,
+ MX25_CFG_INAUX0,
+ MX25_CFG_INAUX1,
+ MX25_CFG_INAUX2,
+ MX25_NUM_CFGS,
+};
+
+struct mx25_gcq_priv {
+ struct regmap *regs;
+ struct completion completed;
+ struct clk *clk;
+ int irq;
+ struct regulator *vref[4];
+ u32 channel_vref_mv[MX25_NUM_CFGS];
+ /*
+ * Lock to protect the device state during a potential concurrent
+ * read access from userspace. Reading a raw value requires a sequence
+ * of register writes, then a wait for a completion callback,
+ * and finally a register read, during which userspace could issue
+ * another read request. This lock protects a read access from
+ * ocurring before another one has finished.
+ */
+ struct mutex lock;
+};
+
+#define MX25_CQG_CHAN(chan, id) {\
+ .type = IIO_VOLTAGE,\
+ .indexed = 1,\
+ .channel = chan,\
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE),\
+ .datasheet_name = id,\
+}
+
+static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = {
+ MX25_CQG_CHAN(MX25_CFG_XP, "xp"),
+ MX25_CQG_CHAN(MX25_CFG_YP, "yp"),
+ MX25_CQG_CHAN(MX25_CFG_XN, "xn"),
+ MX25_CQG_CHAN(MX25_CFG_YN, "yn"),
+ MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"),
+ MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"),
+ MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"),
+ MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"),
+};
+
+static const char * const mx25_gcq_refp_names[] = {
+ [MX25_ADC_REFP_YP] = "yp",
+ [MX25_ADC_REFP_XP] = "xp",
+ [MX25_ADC_REFP_INT] = "int",
+ [MX25_ADC_REFP_EXT] = "ext",
+};
+
+static irqreturn_t mx25_gcq_irq(int irq, void *data)
+{
+ struct mx25_gcq_priv *priv = data;
+ u32 stats;
+
+ regmap_read(priv->regs, MX25_ADCQ_SR, &stats);
+
+ if (stats & MX25_ADCQ_SR_EOQ) {
+ regmap_update_bits(priv->regs, MX25_ADCQ_MR,
+ MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ);
+ complete(&priv->completed);
+ }
+
+ /* Disable conversion queue run */
+ regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0);
+
+ /* Acknowledge all possible irqs */
+ regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR |
+ MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR |
+ MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD);
+
+ return IRQ_HANDLED;
+}
+
+static int mx25_gcq_get_raw_value(struct device *dev,
+ struct iio_chan_spec const *chan,
+ struct mx25_gcq_priv *priv,
+ int *val)
+{
+ long timeout;
+ u32 data;
+
+ /* Setup the configuration we want to use */
+ regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0,
+ MX25_ADCQ_ITEM(0, chan->channel));
+
+ regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0);
+
+ /* Trigger queue for one run */
+ regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS,
+ MX25_ADCQ_CR_FQS);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &priv->completed, MX25_GCQ_TIMEOUT);
+ if (timeout < 0) {
+ dev_err(dev, "ADC wait for measurement failed\n");
+ return timeout;
+ } else if (timeout == 0) {
+ dev_err(dev, "ADC timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ regmap_read(priv->regs, MX25_ADCQ_FIFO, &data);
+
+ *val = MX25_ADCQ_FIFO_DATA(data);
+
+ return IIO_VAL_INT;
+}
+
+static int mx25_gcq_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&priv->lock);
+ ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val);
+ mutex_unlock(&priv->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = priv->channel_vref_mv[chan->channel];
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mx25_gcq_iio_info = {
+ .read_raw = mx25_gcq_read_raw,
+};
+
+static const struct regmap_config mx25_gcq_regconfig = {
+ .max_register = 0x5c,
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static int mx25_gcq_ext_regulator_setup(struct device *dev,
+ struct mx25_gcq_priv *priv, u32 refp)
+{
+ char reg_name[12];
+ int ret;
+
+ if (priv->vref[refp])
+ return 0;
+
+ ret = snprintf(reg_name, sizeof(reg_name), "vref-%s",
+ mx25_gcq_refp_names[refp]);
+ if (ret < 0)
+ return ret;
+
+ priv->vref[refp] = devm_regulator_get_optional(dev, reg_name);
+ if (IS_ERR(priv->vref[refp]))
+ return dev_err_probe(dev, PTR_ERR(priv->vref[refp]),
+ "Error, trying to use external voltage reference without a %s regulator.",
+ reg_name);
+
+ return 0;
+}
+
+static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
+ struct mx25_gcq_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ struct device *dev = &pdev->dev;
+ int ret, i;
+
+ /*
+ * Setup all configurations registers with a default conversion
+ * configuration for each input
+ */
+ for (i = 0; i < MX25_NUM_CFGS; ++i)
+ regmap_write(priv->regs, MX25_ADCQ_CFG(i),
+ MX25_ADCQ_CFG_YPLL_OFF |
+ MX25_ADCQ_CFG_XNUR_OFF |
+ MX25_ADCQ_CFG_XPUL_OFF |
+ MX25_ADCQ_CFG_REFP_INT |
+ MX25_ADCQ_CFG_IN(i) |
+ MX25_ADCQ_CFG_REFN_NGND2);
+
+ for_each_child_of_node(np, child) {
+ u32 reg;
+ u32 refp = MX25_ADCQ_CFG_REFP_INT;
+ u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
+
+ ret = of_property_read_u32(child, "reg", &reg);
+ if (ret) {
+ dev_err(dev, "Failed to get reg property\n");
+ of_node_put(child);
+ return ret;
+ }
+
+ if (reg >= MX25_NUM_CFGS) {
+ dev_err(dev,
+ "reg value is greater than the number of available configuration registers\n");
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ of_property_read_u32(child, "fsl,adc-refp", &refp);
+ of_property_read_u32(child, "fsl,adc-refn", &refn);
+
+ switch (refp) {
+ case MX25_ADC_REFP_EXT:
+ case MX25_ADC_REFP_XP:
+ case MX25_ADC_REFP_YP:
+ ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ priv->channel_vref_mv[reg] =
+ regulator_get_voltage(priv->vref[refp]);
+ /* Conversion from uV to mV */
+ priv->channel_vref_mv[reg] /= 1000;
+ break;
+ case MX25_ADC_REFP_INT:
+ priv->channel_vref_mv[reg] = 2500;
+ break;
+ default:
+ dev_err(dev, "Invalid positive reference %d\n", refp);
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ /*
+ * Shift the read values to the correct positions within the
+ * register.
+ */
+ refp = MX25_ADCQ_CFG_REFP(refp);
+ refn = MX25_ADCQ_CFG_REFN(refn);
+
+ if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
+ dev_err(dev, "Invalid fsl,adc-refp property value\n");
+ of_node_put(child);
+ return -EINVAL;
+ }
+ if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
+ dev_err(dev, "Invalid fsl,adc-refn property value\n");
+ of_node_put(child);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
+ MX25_ADCQ_CFG_REFP_MASK |
+ MX25_ADCQ_CFG_REFN_MASK,
+ refp | refn);
+ }
+ regmap_update_bits(priv->regs, MX25_ADCQ_CR,
+ MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST,
+ MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST);
+
+ regmap_write(priv->regs, MX25_ADCQ_CR,
+ MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS);
+
+ return 0;
+}
+
+static int mx25_gcq_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct mx25_gcq_priv *priv;
+ struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ void __iomem *mem;
+ int ret;
+ int i;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+
+ mem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
+ if (IS_ERR(priv->regs)) {
+ dev_err(dev, "Failed to initialize regmap\n");
+ return PTR_ERR(priv->regs);
+ }
+
+ mutex_init(&priv->lock);
+
+ init_completion(&priv->completed);
+
+ ret = mx25_gcq_setup_cfgs(pdev, priv);
+ if (ret)
+ return ret;
+
+ for (i = 0; i != 4; ++i) {
+ if (!priv->vref[i])
+ continue;
+
+ ret = regulator_enable(priv->vref[i]);
+ if (ret)
+ goto err_regulator_disable;
+ }
+
+ priv->clk = tsadc->clk;
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock\n");
+ goto err_vref_disable;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto err_clk_unprepare;
+
+ priv->irq = ret;
+ ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
+ if (ret) {
+ dev_err(dev, "Failed requesting IRQ\n");
+ goto err_clk_unprepare;
+ }
+
+ indio_dev->channels = mx25_gcq_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
+ indio_dev->info = &mx25_gcq_iio_info;
+ indio_dev->name = driver_name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register iio device\n");
+ goto err_irq_free;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+
+err_irq_free:
+ free_irq(priv->irq, priv);
+err_clk_unprepare:
+ clk_disable_unprepare(priv->clk);
+err_vref_disable:
+ i = 4;
+err_regulator_disable:
+ for (; i-- > 0;) {
+ if (priv->vref[i])
+ regulator_disable(priv->vref[i]);
+ }
+ return ret;
+}
+
+static int mx25_gcq_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct mx25_gcq_priv *priv = iio_priv(indio_dev);
+ int i;
+
+ iio_device_unregister(indio_dev);
+ free_irq(priv->irq, priv);
+ clk_disable_unprepare(priv->clk);
+ for (i = 4; i-- > 0;) {
+ if (priv->vref[i])
+ regulator_disable(priv->vref[i]);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mx25_gcq_ids[] = {
+ { .compatible = "fsl,imx25-gcq", },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mx25_gcq_ids);
+
+static struct platform_driver mx25_gcq_driver = {
+ .driver = {
+ .name = "mx25-gcq",
+ .of_match_table = mx25_gcq_ids,
+ },
+ .probe = mx25_gcq_probe,
+ .remove = mx25_gcq_remove,
+};
+module_platform_driver(mx25_gcq_driver);
+
+MODULE_DESCRIPTION("ADC driver for Freescale mx25");
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
new file mode 100644
index 000000000..771fa12bd
--- /dev/null
+++ b/drivers/iio/adc/hi8435.c
@@ -0,0 +1,549 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Holt Integrated Circuits HI-8435 threshold detector driver
+ *
+ * Copyright (C) 2015 Zodiac Inflight Innovations
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+#define DRV_NAME "hi8435"
+
+/* Register offsets for HI-8435 */
+#define HI8435_CTRL_REG 0x02
+#define HI8435_PSEN_REG 0x04
+#define HI8435_TMDATA_REG 0x1E
+#define HI8435_GOCENHYS_REG 0x3A
+#define HI8435_SOCENHYS_REG 0x3C
+#define HI8435_SO7_0_REG 0x10
+#define HI8435_SO15_8_REG 0x12
+#define HI8435_SO23_16_REG 0x14
+#define HI8435_SO31_24_REG 0x16
+#define HI8435_SO31_0_REG 0x78
+
+#define HI8435_WRITE_OPCODE 0x00
+#define HI8435_READ_OPCODE 0x80
+
+/* CTRL register bits */
+#define HI8435_CTRL_TEST 0x01
+#define HI8435_CTRL_SRST 0x02
+
+struct hi8435_priv {
+ struct spi_device *spi;
+ struct mutex lock;
+
+ unsigned long event_scan_mask; /* soft mask/unmask channels events */
+ unsigned int event_prev_val;
+
+ unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
+ unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */
+ u8 reg_buffer[3] __aligned(IIO_DMA_MINALIGN);
+};
+
+static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
+{
+ reg |= HI8435_READ_OPCODE;
+ return spi_write_then_read(priv->spi, &reg, 1, val, 1);
+}
+
+static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
+{
+ int ret;
+ __be16 be_val;
+
+ reg |= HI8435_READ_OPCODE;
+ ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 2);
+ *val = be16_to_cpu(be_val);
+
+ return ret;
+}
+
+static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
+{
+ int ret;
+ __be32 be_val;
+
+ reg |= HI8435_READ_OPCODE;
+ ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 4);
+ *val = be32_to_cpu(be_val);
+
+ return ret;
+}
+
+static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
+{
+ priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+ priv->reg_buffer[1] = val;
+
+ return spi_write(priv->spi, priv->reg_buffer, 2);
+}
+
+static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
+{
+ priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+ priv->reg_buffer[1] = (val >> 8) & 0xff;
+ priv->reg_buffer[2] = val & 0xff;
+
+ return spi_write(priv->spi, priv->reg_buffer, 3);
+}
+
+static int hi8435_read_raw(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ u32 tmp;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);
+ if (ret < 0)
+ return ret;
+ *val = !!(tmp & BIT(chan->channel));
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hi8435_read_event_config(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+
+ return !!(priv->event_scan_mask & BIT(chan->channel));
+}
+
+static int hi8435_write_event_config(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u32 tmp;
+
+ if (state) {
+ ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp);
+ if (ret < 0)
+ return ret;
+ if (tmp & BIT(chan->channel))
+ priv->event_prev_val |= BIT(chan->channel);
+ else
+ priv->event_prev_val &= ~BIT(chan->channel);
+
+ priv->event_scan_mask |= BIT(chan->channel);
+ } else
+ priv->event_scan_mask &= ~BIT(chan->channel);
+
+ return 0;
+}
+
+static int hi8435_read_event_value(struct iio_dev *idev,
+ 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 hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 mode, psen;
+ u16 reg;
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+ if (ret < 0)
+ return ret;
+
+ /* Supply-Open or GND-Open sensing mode */
+ mode = !!(psen & BIT(chan->channel / 8));
+
+ ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (dir == IIO_EV_DIR_FALLING)
+ *val = ((reg & 0xff) - (reg >> 8)) / 2;
+ else if (dir == IIO_EV_DIR_RISING)
+ *val = ((reg & 0xff) + (reg >> 8)) / 2;
+
+ return IIO_VAL_INT;
+}
+
+static int hi8435_write_event_value(struct iio_dev *idev,
+ 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 hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 mode, psen;
+ u16 reg;
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+ if (ret < 0)
+ return ret;
+
+ /* Supply-Open or GND-Open sensing mode */
+ mode = !!(psen & BIT(chan->channel / 8));
+
+ ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (dir == IIO_EV_DIR_FALLING) {
+ /* falling threshold range 2..21V, hysteresis minimum 2V */
+ if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode])
+ return -EINVAL;
+
+ if (val == priv->threshold_lo[mode])
+ return 0;
+
+ priv->threshold_lo[mode] = val;
+
+ /* hysteresis must not be odd */
+ if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+ priv->threshold_hi[mode]--;
+ } else if (dir == IIO_EV_DIR_RISING) {
+ /* rising threshold range 3..22V, hysteresis minimum 2V */
+ if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2))
+ return -EINVAL;
+
+ if (val == priv->threshold_hi[mode])
+ return 0;
+
+ priv->threshold_hi[mode] = val;
+
+ /* hysteresis must not be odd */
+ if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+ priv->threshold_lo[mode]++;
+ }
+
+ /* program thresholds */
+ mutex_lock(&priv->lock);
+
+ ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, &reg);
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ /* hysteresis */
+ reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
+ reg <<= 8;
+ /* threshold center */
+ reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
+
+ ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, reg);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int hi8435_debugfs_reg_access(struct iio_dev *idev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 val;
+
+ if (readval != NULL) {
+ ret = hi8435_readb(priv, reg, &val);
+ *readval = val;
+ } else {
+ val = (u8)writeval;
+ ret = hi8435_writeb(priv, reg, val);
+ }
+
+ return ret;
+}
+
+static const struct iio_event_spec hi8435_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_ENABLE),
+ },
+};
+
+static int hi8435_get_sensing_mode(struct iio_dev *idev,
+ const struct iio_chan_spec *chan)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 reg;
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+ if (ret < 0)
+ return ret;
+
+ return !!(reg & BIT(chan->channel / 8));
+}
+
+static int hi8435_set_sensing_mode(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 reg;
+
+ mutex_lock(&priv->lock);
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ reg &= ~BIT(chan->channel / 8);
+ if (mode)
+ reg |= BIT(chan->channel / 8);
+
+ ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static const char * const hi8435_sensing_modes[] = { "GND-Open",
+ "Supply-Open" };
+
+static const struct iio_enum hi8435_sensing_mode = {
+ .items = hi8435_sensing_modes,
+ .num_items = ARRAY_SIZE(hi8435_sensing_modes),
+ .get = hi8435_get_sensing_mode,
+ .set = hi8435_set_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) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .event_spec = hi8435_events, \
+ .num_event_specs = ARRAY_SIZE(hi8435_events), \
+ .ext_info = hi8435_ext_info, \
+}
+
+static const struct iio_chan_spec hi8435_channels[] = {
+ HI8435_VOLTAGE_CHANNEL(0),
+ HI8435_VOLTAGE_CHANNEL(1),
+ HI8435_VOLTAGE_CHANNEL(2),
+ HI8435_VOLTAGE_CHANNEL(3),
+ HI8435_VOLTAGE_CHANNEL(4),
+ HI8435_VOLTAGE_CHANNEL(5),
+ HI8435_VOLTAGE_CHANNEL(6),
+ HI8435_VOLTAGE_CHANNEL(7),
+ HI8435_VOLTAGE_CHANNEL(8),
+ HI8435_VOLTAGE_CHANNEL(9),
+ HI8435_VOLTAGE_CHANNEL(10),
+ HI8435_VOLTAGE_CHANNEL(11),
+ HI8435_VOLTAGE_CHANNEL(12),
+ HI8435_VOLTAGE_CHANNEL(13),
+ HI8435_VOLTAGE_CHANNEL(14),
+ HI8435_VOLTAGE_CHANNEL(15),
+ HI8435_VOLTAGE_CHANNEL(16),
+ HI8435_VOLTAGE_CHANNEL(17),
+ HI8435_VOLTAGE_CHANNEL(18),
+ HI8435_VOLTAGE_CHANNEL(19),
+ HI8435_VOLTAGE_CHANNEL(20),
+ HI8435_VOLTAGE_CHANNEL(21),
+ HI8435_VOLTAGE_CHANNEL(22),
+ HI8435_VOLTAGE_CHANNEL(23),
+ HI8435_VOLTAGE_CHANNEL(24),
+ HI8435_VOLTAGE_CHANNEL(25),
+ HI8435_VOLTAGE_CHANNEL(26),
+ HI8435_VOLTAGE_CHANNEL(27),
+ HI8435_VOLTAGE_CHANNEL(28),
+ HI8435_VOLTAGE_CHANNEL(29),
+ HI8435_VOLTAGE_CHANNEL(30),
+ HI8435_VOLTAGE_CHANNEL(31),
+ IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static const struct iio_info hi8435_info = {
+ .read_raw = hi8435_read_raw,
+ .read_event_config = hi8435_read_event_config,
+ .write_event_config = hi8435_write_event_config,
+ .read_event_value = hi8435_read_event_value,
+ .write_event_value = hi8435_write_event_value,
+ .debugfs_reg_access = hi8435_debugfs_reg_access,
+};
+
+static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ enum iio_event_direction dir;
+ unsigned int i;
+ unsigned int status = priv->event_prev_val ^ val;
+
+ if (!status)
+ return;
+
+ for_each_set_bit(i, &priv->event_scan_mask, 32) {
+ if (status & BIT(i)) {
+ dir = val & BIT(i) ? IIO_EV_DIR_RISING :
+ IIO_EV_DIR_FALLING;
+ iio_push_event(idev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+ IIO_EV_TYPE_THRESH, dir),
+ iio_get_time_ns(idev));
+ }
+ }
+
+ priv->event_prev_val = val;
+}
+
+static irqreturn_t hi8435_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *idev = pf->indio_dev;
+ struct hi8435_priv *priv = iio_priv(idev);
+ u32 val;
+ int ret;
+
+ ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+ if (ret < 0)
+ goto err_read;
+
+ hi8435_iio_push_event(idev, val);
+
+err_read:
+ iio_trigger_notify_done(idev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void hi8435_triggered_event_cleanup(void *data)
+{
+ iio_triggered_event_cleanup(data);
+}
+
+static int hi8435_probe(struct spi_device *spi)
+{
+ struct iio_dev *idev;
+ struct hi8435_priv *priv;
+ struct gpio_desc *reset_gpio;
+ int ret;
+
+ idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+ if (!idev)
+ return -ENOMEM;
+
+ priv = iio_priv(idev);
+ priv->spi = spi;
+
+ reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio)) {
+ /* chip s/w reset if h/w reset failed */
+ hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
+ hi8435_writeb(priv, HI8435_CTRL_REG, 0);
+ } else {
+ udelay(5);
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ }
+
+ mutex_init(&priv->lock);
+
+ idev->name = spi_get_device_id(spi)->name;
+ idev->modes = INDIO_DIRECT_MODE;
+ idev->info = &hi8435_info;
+ idev->channels = hi8435_channels;
+ idev->num_channels = ARRAY_SIZE(hi8435_channels);
+
+ /* unmask all events */
+ priv->event_scan_mask = ~(0);
+ /*
+ * There is a restriction in the chip - the hysteresis can not be odd.
+ * If the hysteresis is set to odd value then chip gets into lock state
+ * and not functional anymore.
+ * After chip reset the thresholds are in undefined state, so we need to
+ * initialize thresholds to some initial values and then prevent
+ * userspace setting odd hysteresis.
+ *
+ * Set threshold low voltage to 2V, threshold high voltage to 4V
+ * for both GND-Open and Supply-Open sensing modes.
+ */
+ priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
+ priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
+ hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
+ hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
+
+ ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev,
+ hi8435_triggered_event_cleanup,
+ idev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, idev);
+}
+
+static const struct of_device_id hi8435_dt_ids[] = {
+ { .compatible = "holt,hi8435" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
+
+static const struct spi_device_id hi8435_id[] = {
+ { "hi8435", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, hi8435_id);
+
+static struct spi_driver hi8435_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = hi8435_dt_ids,
+ },
+ .probe = hi8435_probe,
+ .id_table = hi8435_id,
+};
+module_spi_driver(hi8435_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("HI-8435 threshold detector");
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
new file mode 100644
index 000000000..f7ee856a6
--- /dev/null
+++ b/drivers/iio/adc/hx711.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HX711: analog to digital converter for weight sensor module
+ *
+ * Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
+ */
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+/* gain to pulse and scale conversion */
+#define HX711_GAIN_MAX 3
+#define HX711_RESET_GAIN 128
+
+struct hx711_gain_to_scale {
+ int gain;
+ int gain_pulse;
+ int scale;
+ int channel;
+};
+
+/*
+ * .scale depends on AVDD which in turn is known as soon as the regulator
+ * is available
+ * therefore we set .scale in hx711_probe()
+ *
+ * channel A in documentation is channel 0 in source code
+ * channel B in documentation is channel 1 in source code
+ */
+static struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = {
+ { 128, 1, 0, 0 },
+ { 32, 2, 0, 1 },
+ { 64, 3, 0, 0 }
+};
+
+static int hx711_get_gain_to_pulse(int gain)
+{
+ int i;
+
+ for (i = 0; i < HX711_GAIN_MAX; i++)
+ if (hx711_gain_to_scale[i].gain == gain)
+ return hx711_gain_to_scale[i].gain_pulse;
+ return 1;
+}
+
+static int hx711_get_gain_to_scale(int gain)
+{
+ int i;
+
+ for (i = 0; i < HX711_GAIN_MAX; i++)
+ if (hx711_gain_to_scale[i].gain == gain)
+ return hx711_gain_to_scale[i].scale;
+ return 0;
+}
+
+static int hx711_get_scale_to_gain(int scale)
+{
+ int i;
+
+ for (i = 0; i < HX711_GAIN_MAX; i++)
+ if (hx711_gain_to_scale[i].scale == scale)
+ return hx711_gain_to_scale[i].gain;
+ return -EINVAL;
+}
+
+struct hx711_data {
+ struct device *dev;
+ struct gpio_desc *gpiod_pd_sck;
+ struct gpio_desc *gpiod_dout;
+ struct regulator *reg_avdd;
+ int gain_set; /* gain set on device */
+ int gain_chan_a; /* gain for channel A */
+ struct mutex lock;
+ /*
+ * triggered buffer
+ * 2x32-bit channel + 64-bit naturally aligned timestamp
+ */
+ u32 buffer[4] __aligned(8);
+ /*
+ * delay after a rising edge on SCK until the data is ready DOUT
+ * this is dependent on the hx711 where the datasheet tells a
+ * maximum value of 100 ns
+ * but also on potential parasitic capacities on the wiring
+ */
+ u32 data_ready_delay_ns;
+ u32 clock_frequency;
+};
+
+static int hx711_cycle(struct hx711_data *hx711_data)
+{
+ unsigned long flags;
+
+ /*
+ * if preempted for more then 60us while PD_SCK is high:
+ * hx711 is going in reset
+ * ==> measuring is false
+ */
+ local_irq_save(flags);
+ gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
+
+ /*
+ * wait until DOUT is ready
+ * it turned out that parasitic capacities are extending the time
+ * until DOUT has reached it's value
+ */
+ ndelay(hx711_data->data_ready_delay_ns);
+
+ /*
+ * here we are not waiting for 0.2 us as suggested by the datasheet,
+ * because the oscilloscope showed in a test scenario
+ * at least 1.15 us for PD_SCK high (T3 in datasheet)
+ * and 0.56 us for PD_SCK low on TI Sitara with 800 MHz
+ */
+ gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
+ local_irq_restore(flags);
+
+ /*
+ * make it a square wave for addressing cases with capacitance on
+ * PC_SCK
+ */
+ ndelay(hx711_data->data_ready_delay_ns);
+
+ /* sample as late as possible */
+ return gpiod_get_value(hx711_data->gpiod_dout);
+}
+
+static int hx711_read(struct hx711_data *hx711_data)
+{
+ int i, ret;
+ int value = 0;
+ int val = gpiod_get_value(hx711_data->gpiod_dout);
+
+ /* we double check if it's really down */
+ if (val)
+ return -EIO;
+
+ for (i = 0; i < 24; i++) {
+ value <<= 1;
+ ret = hx711_cycle(hx711_data);
+ if (ret)
+ value++;
+ }
+
+ value ^= 0x800000;
+
+ for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++)
+ hx711_cycle(hx711_data);
+
+ return value;
+}
+
+static int hx711_wait_for_ready(struct hx711_data *hx711_data)
+{
+ int i, val;
+
+ /*
+ * in some rare cases the reset takes quite a long time
+ * especially when the channel is changed.
+ * Allow up to one second for it
+ */
+ for (i = 0; i < 100; i++) {
+ val = gpiod_get_value(hx711_data->gpiod_dout);
+ if (!val)
+ break;
+ /* sleep at least 10 ms */
+ msleep(10);
+ }
+ if (val)
+ return -EIO;
+
+ return 0;
+}
+
+static int hx711_reset(struct hx711_data *hx711_data)
+{
+ int val = hx711_wait_for_ready(hx711_data);
+
+ if (val) {
+ /*
+ * an examination with the oszilloscope indicated
+ * that the first value read after the reset is not stable
+ * if we reset too short;
+ * the shorter the reset cycle
+ * the less reliable the first value after reset is;
+ * there were no problems encountered with a value
+ * of 10 ms or higher
+ */
+ gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
+ msleep(10);
+ gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
+
+ val = hx711_wait_for_ready(hx711_data);
+
+ /* after a reset the gain is 128 */
+ hx711_data->gain_set = HX711_RESET_GAIN;
+ }
+
+ return val;
+}
+
+static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
+{
+ int ret;
+
+ if (chan == 0) {
+ if (hx711_data->gain_set == 32) {
+ hx711_data->gain_set = hx711_data->gain_chan_a;
+
+ ret = hx711_read(hx711_data);
+ if (ret < 0)
+ return ret;
+
+ ret = hx711_wait_for_ready(hx711_data);
+ if (ret)
+ return ret;
+ }
+ } else {
+ if (hx711_data->gain_set != 32) {
+ hx711_data->gain_set = 32;
+
+ ret = hx711_read(hx711_data);
+ if (ret < 0)
+ return ret;
+
+ ret = hx711_wait_for_ready(hx711_data);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int hx711_reset_read(struct hx711_data *hx711_data, int chan)
+{
+ int ret;
+ int val;
+
+ /*
+ * hx711_reset() must be called from here
+ * because it could be calling hx711_read() by itself
+ */
+ if (hx711_reset(hx711_data)) {
+ dev_err(hx711_data->dev, "reset failed!");
+ return -EIO;
+ }
+
+ ret = hx711_set_gain_for_channel(hx711_data, chan);
+ if (ret < 0)
+ return ret;
+
+ val = hx711_read(hx711_data);
+
+ return val;
+}
+
+static int hx711_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct hx711_data *hx711_data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&hx711_data->lock);
+
+ *val = hx711_reset_read(hx711_data, chan->channel);
+
+ mutex_unlock(&hx711_data->lock);
+
+ if (*val < 0)
+ return *val;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ mutex_lock(&hx711_data->lock);
+
+ *val2 = hx711_get_gain_to_scale(hx711_data->gain_set);
+
+ mutex_unlock(&hx711_data->lock);
+
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hx711_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct hx711_data *hx711_data = iio_priv(indio_dev);
+ int ret;
+ int gain;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * a scale greater than 1 mV per LSB is not possible
+ * with the HX711, therefore val must be 0
+ */
+ if (val != 0)
+ return -EINVAL;
+
+ mutex_lock(&hx711_data->lock);
+
+ gain = hx711_get_scale_to_gain(val2);
+ if (gain < 0) {
+ mutex_unlock(&hx711_data->lock);
+ return gain;
+ }
+
+ if (gain != hx711_data->gain_set) {
+ hx711_data->gain_set = gain;
+ if (gain != 32)
+ hx711_data->gain_chan_a = gain;
+
+ ret = hx711_read(hx711_data);
+ if (ret < 0) {
+ mutex_unlock(&hx711_data->lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&hx711_data->lock);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static irqreturn_t hx711_trigger(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct hx711_data *hx711_data = iio_priv(indio_dev);
+ int i, j = 0;
+
+ mutex_lock(&hx711_data->lock);
+
+ memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer));
+
+ for (i = 0; i < indio_dev->masklength; i++) {
+ if (!test_bit(i, indio_dev->active_scan_mask))
+ continue;
+
+ hx711_data->buffer[j] = hx711_reset_read(hx711_data,
+ indio_dev->channels[i].channel);
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, hx711_data->buffer,
+ pf->timestamp);
+
+ mutex_unlock(&hx711_data->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t hx711_scale_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
+ int channel = iio_attr->address;
+ int i, len = 0;
+
+ for (i = 0; i < HX711_GAIN_MAX; i++)
+ if (hx711_gain_to_scale[i].channel == channel)
+ len += sprintf(buf + len, "0.%09d ",
+ hx711_gain_to_scale[i].scale);
+
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO,
+ hx711_scale_available_show, NULL, 0);
+
+static IIO_DEVICE_ATTR(in_voltage1_scale_available, S_IRUGO,
+ hx711_scale_available_show, NULL, 1);
+
+static struct attribute *hx711_attributes[] = {
+ &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group hx711_attribute_group = {
+ .attrs = hx711_attributes,
+};
+
+static const struct iio_info hx711_iio_info = {
+ .read_raw = hx711_read_raw,
+ .write_raw = hx711_write_raw,
+ .write_raw_get_fmt = hx711_write_raw_get_fmt,
+ .attrs = &hx711_attribute_group,
+};
+
+static const struct iio_chan_spec hx711_chan_spec[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .channel = 1,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int hx711_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct hx711_data *hx711_data;
+ struct iio_dev *indio_dev;
+ int ret;
+ int i;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data));
+ if (!indio_dev) {
+ dev_err(dev, "failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+
+ hx711_data = iio_priv(indio_dev);
+ hx711_data->dev = dev;
+
+ mutex_init(&hx711_data->lock);
+
+ /*
+ * PD_SCK stands for power down and serial clock input of HX711
+ * in the driver it is an output
+ */
+ hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
+ if (IS_ERR(hx711_data->gpiod_pd_sck)) {
+ dev_err(dev, "failed to get sck-gpiod: err=%ld\n",
+ PTR_ERR(hx711_data->gpiod_pd_sck));
+ return PTR_ERR(hx711_data->gpiod_pd_sck);
+ }
+
+ /*
+ * DOUT stands for serial data output of HX711
+ * for the driver it is an input
+ */
+ hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN);
+ if (IS_ERR(hx711_data->gpiod_dout)) {
+ dev_err(dev, "failed to get dout-gpiod: err=%ld\n",
+ PTR_ERR(hx711_data->gpiod_dout));
+ return PTR_ERR(hx711_data->gpiod_dout);
+ }
+
+ hx711_data->reg_avdd = devm_regulator_get(dev, "avdd");
+ if (IS_ERR(hx711_data->reg_avdd))
+ return PTR_ERR(hx711_data->reg_avdd);
+
+ ret = regulator_enable(hx711_data->reg_avdd);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * with
+ * full scale differential input range: AVDD / GAIN
+ * full scale output data: 2^24
+ * we can say:
+ * AVDD / GAIN = 2^24
+ * therefore:
+ * 1 LSB = AVDD / GAIN / 2^24
+ * AVDD is in uV, but we need 10^-9 mV
+ * approximately to fit into a 32 bit number:
+ * 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV]
+ */
+ ret = regulator_get_voltage(hx711_data->reg_avdd);
+ if (ret < 0)
+ goto error_regulator;
+
+ /* we need 10^-9 mV */
+ ret *= 100;
+
+ for (i = 0; i < HX711_GAIN_MAX; i++)
+ hx711_gain_to_scale[i].scale =
+ ret / hx711_gain_to_scale[i].gain / 1678;
+
+ hx711_data->gain_set = 128;
+ hx711_data->gain_chan_a = 128;
+
+ hx711_data->clock_frequency = 400000;
+ ret = of_property_read_u32(np, "clock-frequency",
+ &hx711_data->clock_frequency);
+
+ /*
+ * datasheet says the high level of PD_SCK has a maximum duration
+ * of 50 microseconds
+ */
+ if (hx711_data->clock_frequency < 20000) {
+ dev_warn(dev, "clock-frequency too low - assuming 400 kHz\n");
+ hx711_data->clock_frequency = 400000;
+ }
+
+ hx711_data->data_ready_delay_ns =
+ 1000000000 / hx711_data->clock_frequency;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = "hx711";
+ indio_dev->info = &hx711_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = hx711_chan_spec;
+ indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec);
+
+ ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
+ hx711_trigger, NULL);
+ if (ret < 0) {
+ dev_err(dev, "setup of iio triggered buffer failed\n");
+ goto error_regulator;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "Couldn't register the device\n");
+ goto error_buffer;
+ }
+
+ return 0;
+
+error_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+error_regulator:
+ regulator_disable(hx711_data->reg_avdd);
+
+ return ret;
+}
+
+static int hx711_remove(struct platform_device *pdev)
+{
+ struct hx711_data *hx711_data;
+ struct iio_dev *indio_dev;
+
+ indio_dev = platform_get_drvdata(pdev);
+ hx711_data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ regulator_disable(hx711_data->reg_avdd);
+
+ return 0;
+}
+
+static const struct of_device_id of_hx711_match[] = {
+ { .compatible = "avia,hx711", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_hx711_match);
+
+static struct platform_driver hx711_driver = {
+ .probe = hx711_probe,
+ .remove = hx711_remove,
+ .driver = {
+ .name = "hx711-gpio",
+ .of_match_table = of_hx711_match,
+ },
+};
+
+module_platform_driver(hx711_driver);
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hx711-gpio");
+
diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c
new file mode 100644
index 000000000..86caff1d0
--- /dev/null
+++ b/drivers/iio/adc/imx7d_adc.c
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale i.MX7D ADC driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/sysfs.h>
+
+/* ADC register */
+#define IMX7D_REG_ADC_CH_A_CFG1 0x00
+#define IMX7D_REG_ADC_CH_A_CFG2 0x10
+#define IMX7D_REG_ADC_CH_B_CFG1 0x20
+#define IMX7D_REG_ADC_CH_B_CFG2 0x30
+#define IMX7D_REG_ADC_CH_C_CFG1 0x40
+#define IMX7D_REG_ADC_CH_C_CFG2 0x50
+#define IMX7D_REG_ADC_CH_D_CFG1 0x60
+#define IMX7D_REG_ADC_CH_D_CFG2 0x70
+#define IMX7D_REG_ADC_CH_SW_CFG 0x80
+#define IMX7D_REG_ADC_TIMER_UNIT 0x90
+#define IMX7D_REG_ADC_DMA_FIFO 0xa0
+#define IMX7D_REG_ADC_FIFO_STATUS 0xb0
+#define IMX7D_REG_ADC_INT_SIG_EN 0xc0
+#define IMX7D_REG_ADC_INT_EN 0xd0
+#define IMX7D_REG_ADC_INT_STATUS 0xe0
+#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
+#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
+#define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110
+#define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120
+#define IMX7D_REG_ADC_ADC_CFG 0x130
+
+#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10
+#define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20
+
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
+
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
+
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
+
+#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
+
+#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8)
+#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9)
+#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10)
+#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11)
+#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
+ (IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
+ IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
+
+#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
+#define IMX7D_ADC_INPUT_CLK 24000000
+
+enum imx7d_adc_clk_pre_div {
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
+};
+
+enum imx7d_adc_average_num {
+ IMX7D_ADC_AVERAGE_NUM_4,
+ IMX7D_ADC_AVERAGE_NUM_8,
+ IMX7D_ADC_AVERAGE_NUM_16,
+ IMX7D_ADC_AVERAGE_NUM_32,
+};
+
+struct imx7d_adc_feature {
+ enum imx7d_adc_clk_pre_div clk_pre_div;
+ enum imx7d_adc_average_num avg_num;
+
+ u32 core_time_unit; /* impact the sample rate */
+};
+
+struct imx7d_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+
+ u32 vref_uv;
+ u32 value;
+ u32 channel;
+ u32 pre_div_num;
+
+ struct regulator *vref;
+ struct imx7d_adc_feature adc_feature;
+
+ struct completion completion;
+};
+
+struct imx7d_adc_analogue_core_clk {
+ u32 pre_div;
+ u32 reg_config;
+};
+
+#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
+ .pre_div = (_pre_div), \
+ .reg_config = (_reg_conf), \
+}
+
+static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
+};
+
+#define IMX7D_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec imx7d_adc_iio_channels[] = {
+ IMX7D_ADC_CHAN(0),
+ IMX7D_ADC_CHAN(1),
+ IMX7D_ADC_CHAN(2),
+ IMX7D_ADC_CHAN(3),
+ IMX7D_ADC_CHAN(4),
+ IMX7D_ADC_CHAN(5),
+ IMX7D_ADC_CHAN(6),
+ IMX7D_ADC_CHAN(7),
+ IMX7D_ADC_CHAN(8),
+ IMX7D_ADC_CHAN(9),
+ IMX7D_ADC_CHAN(10),
+ IMX7D_ADC_CHAN(11),
+ IMX7D_ADC_CHAN(12),
+ IMX7D_ADC_CHAN(13),
+ IMX7D_ADC_CHAN(14),
+ IMX7D_ADC_CHAN(15),
+};
+
+static const u32 imx7d_adc_average_num[] = {
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
+};
+
+static void imx7d_adc_feature_config(struct imx7d_adc *info)
+{
+ info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
+ info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
+ info->adc_feature.core_time_unit = 1;
+}
+
+static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
+{
+ struct imx7d_adc_feature *adc_feature = &info->adc_feature;
+ struct imx7d_adc_analogue_core_clk adc_analogure_clk;
+ u32 i;
+ u32 tmp_cfg1;
+ u32 sample_rate = 0;
+
+ /*
+ * Before sample set, disable channel A,B,C,D. Here we
+ * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
+ */
+ for (i = 0; i < 4; i++) {
+ tmp_cfg1 =
+ readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
+ tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
+ writel(tmp_cfg1,
+ info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
+ }
+
+ adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
+ sample_rate |= adc_analogure_clk.reg_config;
+ info->pre_div_num = adc_analogure_clk.pre_div;
+
+ sample_rate |= adc_feature->core_time_unit;
+ writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
+}
+
+static void imx7d_adc_hw_init(struct imx7d_adc *info)
+{
+ u32 cfg;
+
+ /* power up and enable adc analogue core */
+ cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
+ cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+
+ /* enable channel A,B,C,D interrupt */
+ writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
+ info->regs + IMX7D_REG_ADC_INT_SIG_EN);
+ writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
+ info->regs + IMX7D_REG_ADC_INT_EN);
+
+ imx7d_adc_sample_rate_set(info);
+}
+
+static void imx7d_adc_channel_set(struct imx7d_adc *info)
+{
+ u32 cfg1 = 0;
+ u32 cfg2;
+ u32 channel;
+
+ channel = info->channel;
+
+ /* the channel choose single conversion, and enable average mode */
+ cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
+
+ /*
+ * physical channel 0 chose logical channel A
+ * physical channel 1 chose logical channel B
+ * physical channel 2 chose logical channel C
+ * physical channel 3 chose logical channel D
+ */
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
+
+ /*
+ * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
+ * channel chosen
+ */
+ cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
+ IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
+
+ cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
+
+ /*
+ * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
+ * the channel chosen
+ */
+ writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
+ IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
+ writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
+}
+
+static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
+{
+ u32 analogue_core_clk;
+ u32 core_time_unit = info->adc_feature.core_time_unit;
+ u32 tmp;
+
+ analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
+ tmp = (core_time_unit + 1) * 6;
+
+ return analogue_core_clk / tmp;
+}
+
+static int imx7d_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ u32 channel;
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ reinit_completion(&info->completion);
+
+ channel = chan->channel & 0x03;
+ info->channel = channel;
+ imx7d_adc_channel_set(info);
+
+ ret = wait_for_completion_interruptible_timeout
+ (&info->completion, IMX7D_ADC_TIMEOUT);
+ if (ret == 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return -ETIMEDOUT;
+ }
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ *val = info->value;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ info->vref_uv = regulator_get_voltage(info->vref);
+ *val = info->vref_uv / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = imx7d_adc_get_sample_rate(info);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx7d_adc_read_data(struct imx7d_adc *info)
+{
+ u32 channel;
+ u32 value;
+
+ channel = info->channel & 0x03;
+
+ /*
+ * channel A and B conversion result share one register,
+ * bit[27~16] is the channel B conversion result,
+ * bit[11~0] is the channel A conversion result.
+ * channel C and D is the same.
+ */
+ if (channel < 2)
+ value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
+ else
+ value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
+ if (channel & 0x1) /* channel B or D */
+ value = (value >> 16) & 0xFFF;
+ else /* channel A or C */
+ value &= 0xFFF;
+
+ return value;
+}
+
+static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
+{
+ struct imx7d_adc *info = dev_id;
+ int status;
+
+ status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
+ info->value = imx7d_adc_read_data(info);
+ complete(&info->completion);
+
+ /*
+ * The register IMX7D_REG_ADC_INT_STATUS can't clear
+ * itself after read operation, need software to write
+ * 0 to the related bit. Here we clear the channel A/B/C/D
+ * conversion finished flag.
+ */
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ /*
+ * If the channel A/B/C/D conversion timeout, report it and clear these
+ * timeout flags.
+ */
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
+ dev_err(info->dev,
+ "ADC got conversion time out interrupt: 0x%08x\n",
+ status);
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int imx7d_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
+ return -EINVAL;
+
+ *readval = readl(info->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info imx7d_adc_iio_info = {
+ .read_raw = &imx7d_adc_read_raw,
+ .debugfs_reg_access = &imx7d_adc_reg_access,
+};
+
+static const struct of_device_id imx7d_adc_match[] = {
+ { .compatible = "fsl,imx7d-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx7d_adc_match);
+
+static void imx7d_adc_power_down(struct imx7d_adc *info)
+{
+ u32 adc_cfg;
+
+ adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
+ adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+}
+
+static int imx7d_adc_enable(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(info->dev,
+ "Can't enable adc reference top voltage, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(info->dev,
+ "Could not prepare or enable clock.\n");
+ regulator_disable(info->vref);
+ return ret;
+ }
+
+ imx7d_adc_hw_init(info);
+
+ return 0;
+}
+
+static int imx7d_adc_disable(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static void __imx7d_adc_disable(void *data)
+{
+ imx7d_adc_disable(data);
+}
+
+static int imx7d_adc_probe(struct platform_device *pdev)
+{
+ struct imx7d_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = dev;
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed getting irq\n");
+
+ info->clk = devm_clk_get(dev, "adc");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n");
+
+ info->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(info->vref))
+ return dev_err_probe(dev, PTR_ERR(info->vref),
+ "Failed getting reference voltage\n");
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->info = &imx7d_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx7d_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
+
+ ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);
+ if (ret < 0) {
+ dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
+ return ret;
+ }
+
+ imx7d_adc_feature_config(info);
+
+ ret = imx7d_adc_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable,
+ imx7d_adc_enable);
+
+static struct platform_driver imx7d_adc_driver = {
+ .probe = imx7d_adc_probe,
+ .driver = {
+ .name = "imx7d_adc",
+ .of_match_table = imx7d_adc_match,
+ .pm = pm_sleep_ptr(&imx7d_adc_pm_ops),
+ },
+};
+
+module_platform_driver(imx7d_adc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freescale IMX7D ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c
new file mode 100644
index 000000000..fff6e5a2d
--- /dev/null
+++ b/drivers/iio/adc/imx8qxp-adc.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NXP i.MX8QXP ADC driver
+ *
+ * Based on the work of Haibo Chen <haibo.chen@nxp.com>
+ * The initial developer of the original code is Haibo Chen.
+ * Portions created by Haibo Chen are Copyright (C) 2018 NXP.
+ * All Rights Reserved.
+ *
+ * Copyright (C) 2018 NXP
+ * Copyright (C) 2021 Cai Huoqing
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+
+#define ADC_DRIVER_NAME "imx8qxp-adc"
+
+/* Register map definition */
+#define IMX8QXP_ADR_ADC_CTRL 0x10
+#define IMX8QXP_ADR_ADC_STAT 0x14
+#define IMX8QXP_ADR_ADC_IE 0x18
+#define IMX8QXP_ADR_ADC_DE 0x1c
+#define IMX8QXP_ADR_ADC_CFG 0x20
+#define IMX8QXP_ADR_ADC_FCTRL 0x30
+#define IMX8QXP_ADR_ADC_SWTRIG 0x34
+#define IMX8QXP_ADR_ADC_TCTRL(tid) (0xc0 + (tid) * 4)
+#define IMX8QXP_ADR_ADC_CMDL(cid) (0x100 + (cid) * 8)
+#define IMX8QXP_ADR_ADC_CMDH(cid) (0x104 + (cid) * 8)
+#define IMX8QXP_ADR_ADC_RESFIFO 0x300
+#define IMX8QXP_ADR_ADC_TST 0xffc
+
+/* ADC bit shift */
+#define IMX8QXP_ADC_IE_FWMIE_MASK GENMASK(1, 0)
+#define IMX8QXP_ADC_CTRL_FIFO_RESET_MASK BIT(8)
+#define IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK BIT(1)
+#define IMX8QXP_ADC_CTRL_ADC_EN_MASK BIT(0)
+#define IMX8QXP_ADC_TCTRL_TCMD_MASK GENMASK(31, 24)
+#define IMX8QXP_ADC_TCTRL_TDLY_MASK GENMASK(23, 16)
+#define IMX8QXP_ADC_TCTRL_TPRI_MASK GENMASK(15, 8)
+#define IMX8QXP_ADC_TCTRL_HTEN_MASK GENMASK(7, 0)
+#define IMX8QXP_ADC_CMDL_CSCALE_MASK GENMASK(13, 8)
+#define IMX8QXP_ADC_CMDL_MODE_MASK BIT(7)
+#define IMX8QXP_ADC_CMDL_DIFF_MASK BIT(6)
+#define IMX8QXP_ADC_CMDL_ABSEL_MASK BIT(5)
+#define IMX8QXP_ADC_CMDL_ADCH_MASK GENMASK(2, 0)
+#define IMX8QXP_ADC_CMDH_NEXT_MASK GENMASK(31, 24)
+#define IMX8QXP_ADC_CMDH_LOOP_MASK GENMASK(23, 16)
+#define IMX8QXP_ADC_CMDH_AVGS_MASK GENMASK(15, 12)
+#define IMX8QXP_ADC_CMDH_STS_MASK BIT(8)
+#define IMX8QXP_ADC_CMDH_LWI_MASK GENMASK(7, 7)
+#define IMX8QXP_ADC_CMDH_CMPEN_MASK GENMASK(0, 0)
+#define IMX8QXP_ADC_CFG_PWREN_MASK BIT(28)
+#define IMX8QXP_ADC_CFG_PUDLY_MASK GENMASK(23, 16)
+#define IMX8QXP_ADC_CFG_REFSEL_MASK GENMASK(7, 6)
+#define IMX8QXP_ADC_CFG_PWRSEL_MASK GENMASK(5, 4)
+#define IMX8QXP_ADC_CFG_TPRICTRL_MASK GENMASK(3, 0)
+#define IMX8QXP_ADC_FCTRL_FWMARK_MASK GENMASK(20, 16)
+#define IMX8QXP_ADC_FCTRL_FCOUNT_MASK GENMASK(4, 0)
+#define IMX8QXP_ADC_RESFIFO_VAL_MASK GENMASK(18, 3)
+
+/* ADC PARAMETER*/
+#define IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL GENMASK(5, 0)
+#define IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL 0
+#define IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION 0
+#define IMX8QXP_ADC_CMDL_MODE_SINGLE 0
+#define IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS 0
+#define IMX8QXP_ADC_CMDH_CMPEN_DIS 0
+#define IMX8QXP_ADC_PAUSE_EN BIT(31)
+#define IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH 0
+
+#define IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS 0
+
+#define IMX8QXP_ADC_TIMEOUT msecs_to_jiffies(100)
+
+#define IMX8QXP_ADC_MAX_FIFO_SIZE 16
+
+struct imx8qxp_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct clk *ipg_clk;
+ struct regulator *vref;
+ /* Serialise ADC channel reads */
+ struct mutex lock;
+ struct completion completion;
+ u32 fifo[IMX8QXP_ADC_MAX_FIFO_SIZE];
+};
+
+#define IMX8QXP_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec imx8qxp_adc_iio_channels[] = {
+ IMX8QXP_ADC_CHAN(0),
+ IMX8QXP_ADC_CHAN(1),
+ IMX8QXP_ADC_CHAN(2),
+ IMX8QXP_ADC_CHAN(3),
+ IMX8QXP_ADC_CHAN(4),
+ IMX8QXP_ADC_CHAN(5),
+ IMX8QXP_ADC_CHAN(6),
+ IMX8QXP_ADC_CHAN(7),
+};
+
+static void imx8qxp_adc_reset(struct imx8qxp_adc *adc)
+{
+ u32 ctrl;
+
+ /*software reset, need to clear the set bit*/
+ ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL);
+ ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1);
+ writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL);
+ udelay(10);
+ ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1);
+ writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL);
+
+ /* reset the fifo */
+ ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_FIFO_RESET_MASK, 1);
+ writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL);
+}
+
+static void imx8qxp_adc_reg_config(struct imx8qxp_adc *adc, int channel)
+{
+ u32 adc_cfg, adc_tctrl, adc_cmdl, adc_cmdh;
+
+ /* ADC configuration */
+ adc_cfg = FIELD_PREP(IMX8QXP_ADC_CFG_PWREN_MASK, 1) |
+ FIELD_PREP(IMX8QXP_ADC_CFG_PUDLY_MASK, 0x80)|
+ FIELD_PREP(IMX8QXP_ADC_CFG_REFSEL_MASK, 0) |
+ FIELD_PREP(IMX8QXP_ADC_CFG_PWRSEL_MASK, 3) |
+ FIELD_PREP(IMX8QXP_ADC_CFG_TPRICTRL_MASK, 0);
+ writel(adc_cfg, adc->regs + IMX8QXP_ADR_ADC_CFG);
+
+ /* config the trigger control */
+ adc_tctrl = FIELD_PREP(IMX8QXP_ADC_TCTRL_TCMD_MASK, 1) |
+ FIELD_PREP(IMX8QXP_ADC_TCTRL_TDLY_MASK, 0) |
+ FIELD_PREP(IMX8QXP_ADC_TCTRL_TPRI_MASK, IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH) |
+ FIELD_PREP(IMX8QXP_ADC_TCTRL_HTEN_MASK, IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS);
+ writel(adc_tctrl, adc->regs + IMX8QXP_ADR_ADC_TCTRL(0));
+
+ /* config the cmd */
+ adc_cmdl = FIELD_PREP(IMX8QXP_ADC_CMDL_CSCALE_MASK, IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL) |
+ FIELD_PREP(IMX8QXP_ADC_CMDL_MODE_MASK, IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION) |
+ FIELD_PREP(IMX8QXP_ADC_CMDL_DIFF_MASK, IMX8QXP_ADC_CMDL_MODE_SINGLE) |
+ FIELD_PREP(IMX8QXP_ADC_CMDL_ABSEL_MASK, IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL) |
+ FIELD_PREP(IMX8QXP_ADC_CMDL_ADCH_MASK, channel);
+ writel(adc_cmdl, adc->regs + IMX8QXP_ADR_ADC_CMDL(0));
+
+ adc_cmdh = FIELD_PREP(IMX8QXP_ADC_CMDH_NEXT_MASK, 0) |
+ FIELD_PREP(IMX8QXP_ADC_CMDH_LOOP_MASK, 0) |
+ FIELD_PREP(IMX8QXP_ADC_CMDH_AVGS_MASK, 7) |
+ FIELD_PREP(IMX8QXP_ADC_CMDH_STS_MASK, 0) |
+ FIELD_PREP(IMX8QXP_ADC_CMDH_LWI_MASK, IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS) |
+ FIELD_PREP(IMX8QXP_ADC_CMDH_CMPEN_MASK, IMX8QXP_ADC_CMDH_CMPEN_DIS);
+ writel(adc_cmdh, adc->regs + IMX8QXP_ADR_ADC_CMDH(0));
+}
+
+static void imx8qxp_adc_fifo_config(struct imx8qxp_adc *adc)
+{
+ u32 fifo_ctrl, interrupt_en;
+
+ fifo_ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL);
+ fifo_ctrl &= ~IMX8QXP_ADC_FCTRL_FWMARK_MASK;
+ /* set the watermark level to 1 */
+ fifo_ctrl |= FIELD_PREP(IMX8QXP_ADC_FCTRL_FWMARK_MASK, 0);
+ writel(fifo_ctrl, adc->regs + IMX8QXP_ADR_ADC_FCTRL);
+
+ /* FIFO Watermark Interrupt Enable */
+ interrupt_en = readl(adc->regs + IMX8QXP_ADR_ADC_IE);
+ interrupt_en |= FIELD_PREP(IMX8QXP_ADC_IE_FWMIE_MASK, 1);
+ writel(interrupt_en, adc->regs + IMX8QXP_ADR_ADC_IE);
+}
+
+static void imx8qxp_adc_disable(struct imx8qxp_adc *adc)
+{
+ u32 ctrl;
+
+ ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL);
+ ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1);
+ writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL);
+}
+
+static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct imx8qxp_adc *adc = iio_priv(indio_dev);
+ struct device *dev = adc->dev;
+
+ u32 ctrl;
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&adc->lock);
+ reinit_completion(&adc->completion);
+
+ imx8qxp_adc_reg_config(adc, chan->channel);
+
+ imx8qxp_adc_fifo_config(adc);
+
+ /* adc enable */
+ ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL);
+ ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1);
+ writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL);
+ /* adc start */
+ writel(1, adc->regs + IMX8QXP_ADR_ADC_SWTRIG);
+
+ ret = wait_for_completion_interruptible_timeout(&adc->completion,
+ IMX8QXP_ADC_TIMEOUT);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_sync_autosuspend(dev);
+
+ if (ret == 0) {
+ mutex_unlock(&adc->lock);
+ return -ETIMEDOUT;
+ }
+ if (ret < 0) {
+ mutex_unlock(&adc->lock);
+ return ret;
+ }
+
+ *val = adc->fifo[0];
+
+ mutex_unlock(&adc->lock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc->vref);
+ if (ret < 0)
+ return ret;
+ *val = ret / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = clk_get_rate(adc->clk) / 3;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id)
+{
+ struct imx8qxp_adc *adc = dev_id;
+ u32 fifo_count;
+ int i;
+
+ fifo_count = FIELD_GET(IMX8QXP_ADC_FCTRL_FCOUNT_MASK,
+ readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL));
+
+ for (i = 0; i < fifo_count; i++)
+ adc->fifo[i] = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK,
+ readl_relaxed(adc->regs + IMX8QXP_ADR_ADC_RESFIFO));
+
+ if (fifo_count)
+ complete(&adc->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int imx8qxp_adc_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct imx8qxp_adc *adc = iio_priv(indio_dev);
+ struct device *dev = adc->dev;
+
+ if (!readval || reg % 4 || reg > IMX8QXP_ADR_ADC_TST)
+ return -EINVAL;
+
+ pm_runtime_get_sync(dev);
+
+ *readval = readl(adc->regs + reg);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_sync_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct iio_info imx8qxp_adc_iio_info = {
+ .read_raw = &imx8qxp_adc_read_raw,
+ .debugfs_reg_access = &imx8qxp_adc_reg_access,
+};
+
+static int imx8qxp_adc_probe(struct platform_device *pdev)
+{
+ struct imx8qxp_adc *adc;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->dev = dev;
+
+ mutex_init(&adc->lock);
+ adc->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc->regs))
+ return PTR_ERR(adc->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ adc->clk = devm_clk_get(dev, "per");
+ if (IS_ERR(adc->clk))
+ return dev_err_probe(dev, PTR_ERR(adc->clk), "Failed getting clock\n");
+
+ adc->ipg_clk = devm_clk_get(dev, "ipg");
+ if (IS_ERR(adc->ipg_clk))
+ return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), "Failed getting clock\n");
+
+ adc->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(adc->vref))
+ return dev_err_probe(dev, PTR_ERR(adc->vref), "Failed getting reference voltage\n");
+
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(dev, "Can't enable adc reference top voltage\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&adc->completion);
+
+ indio_dev->name = ADC_DRIVER_NAME;
+ indio_dev->info = &imx8qxp_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx8qxp_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx8qxp_adc_iio_channels);
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not prepare or enable the clock.\n");
+ goto error_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(adc->ipg_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not prepare or enable the clock.\n");
+ goto error_adc_clk_disable;
+ }
+
+ ret = devm_request_irq(dev, irq, imx8qxp_adc_isr, 0, ADC_DRIVER_NAME, adc);
+ if (ret < 0) {
+ dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
+ goto error_ipg_clk_disable;
+ }
+
+ imx8qxp_adc_reset(adc);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ imx8qxp_adc_disable(adc);
+ dev_err(dev, "Couldn't register the device.\n");
+ goto error_ipg_clk_disable;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, 50);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+
+error_ipg_clk_disable:
+ clk_disable_unprepare(adc->ipg_clk);
+error_adc_clk_disable:
+ clk_disable_unprepare(adc->clk);
+error_regulator_disable:
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int imx8qxp_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct imx8qxp_adc *adc = iio_priv(indio_dev);
+ struct device *dev = adc->dev;
+
+ pm_runtime_get_sync(dev);
+
+ iio_device_unregister(indio_dev);
+
+ imx8qxp_adc_disable(adc);
+
+ clk_disable_unprepare(adc->clk);
+ clk_disable_unprepare(adc->ipg_clk);
+ regulator_disable(adc->vref);
+
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+
+ return 0;
+}
+
+static int imx8qxp_adc_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx8qxp_adc *adc = iio_priv(indio_dev);
+
+ imx8qxp_adc_disable(adc);
+
+ clk_disable_unprepare(adc->clk);
+ clk_disable_unprepare(adc->ipg_clk);
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static int imx8qxp_adc_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx8qxp_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(dev, "Can't enable adc reference top voltage, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret) {
+ dev_err(dev, "Could not prepare or enable clock.\n");
+ goto err_disable_reg;
+ }
+
+ ret = clk_prepare_enable(adc->ipg_clk);
+ if (ret) {
+ dev_err(dev, "Could not prepare or enable clock.\n");
+ goto err_unprepare_clk;
+ }
+
+ imx8qxp_adc_reset(adc);
+
+ return 0;
+
+err_unprepare_clk:
+ clk_disable_unprepare(adc->clk);
+
+err_disable_reg:
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(imx8qxp_adc_pm_ops,
+ imx8qxp_adc_runtime_suspend,
+ imx8qxp_adc_runtime_resume, NULL);
+
+static const struct of_device_id imx8qxp_adc_match[] = {
+ { .compatible = "nxp,imx8qxp-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8qxp_adc_match);
+
+static struct platform_driver imx8qxp_adc_driver = {
+ .probe = imx8qxp_adc_probe,
+ .remove = imx8qxp_adc_remove,
+ .driver = {
+ .name = ADC_DRIVER_NAME,
+ .of_match_table = imx8qxp_adc_match,
+ .pm = pm_ptr(&imx8qxp_adc_pm_ops),
+ },
+};
+
+module_platform_driver(imx8qxp_adc_driver);
+
+MODULE_DESCRIPTION("i.MX8QuadXPlus ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
new file mode 100644
index 000000000..74092f383
--- /dev/null
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -0,0 +1,1104 @@
+/*
+ * INA2XX Current and Power Monitors
+ *
+ * Copyright 2015 Baylibre SAS.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on linux/drivers/iio/adc/ad7291.c
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Based on linux/drivers/hwmon/ina2xx.c
+ * Copyright 2012 Lothar Felten <l-felten@ti.com>
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * IIO driver for INA219-220-226-230-231
+ *
+ * Configurable 7-bit I2C slave address from 0x40 to 0x4F
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/sched/task.h>
+#include <linux/util_macros.h>
+
+#include <linux/platform_data/ina2xx.h>
+
+/* INA2XX registers definition */
+#define INA2XX_CONFIG 0x00
+#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
+#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
+#define INA2XX_POWER 0x03 /* readonly */
+#define INA2XX_CURRENT 0x04 /* readonly */
+#define INA2XX_CALIBRATION 0x05
+
+#define INA226_MASK_ENABLE 0x06
+#define INA226_CVRF BIT(3)
+
+#define INA2XX_MAX_REGISTERS 8
+
+/* settings - depend on use case */
+#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */
+#define INA219_DEFAULT_IT 532
+#define INA219_DEFAULT_BRNG 1 /* 32V */
+#define INA219_DEFAULT_PGA 125 /* 1000/8 */
+#define INA226_CONFIG_DEFAULT 0x4327
+#define INA226_DEFAULT_AVG 4
+#define INA226_DEFAULT_IT 1110
+
+#define INA2XX_RSHUNT_DEFAULT 10000
+
+/*
+ * bit masks for reading the settings in the configuration register
+ * FIXME: use regmap_fields.
+ */
+#define INA2XX_MODE_MASK GENMASK(3, 0)
+
+/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */
+#define INA219_PGA_MASK GENMASK(12, 11)
+#define INA219_SHIFT_PGA(val) ((val) << 11)
+
+/* VBus range: 32V (default), 16V */
+#define INA219_BRNG_MASK BIT(13)
+#define INA219_SHIFT_BRNG(val) ((val) << 13)
+
+/* Averaging for VBus/VShunt/Power */
+#define INA226_AVG_MASK GENMASK(11, 9)
+#define INA226_SHIFT_AVG(val) ((val) << 9)
+
+/* Integration time for VBus */
+#define INA219_ITB_MASK GENMASK(10, 7)
+#define INA219_SHIFT_ITB(val) ((val) << 7)
+#define INA226_ITB_MASK GENMASK(8, 6)
+#define INA226_SHIFT_ITB(val) ((val) << 6)
+
+/* Integration time for VShunt */
+#define INA219_ITS_MASK GENMASK(6, 3)
+#define INA219_SHIFT_ITS(val) ((val) << 3)
+#define INA226_ITS_MASK GENMASK(5, 3)
+#define INA226_SHIFT_ITS(val) ((val) << 3)
+
+/* INA219 Bus voltage register, low bits are flags */
+#define INA219_OVF BIT(0)
+#define INA219_CNVR BIT(1)
+#define INA219_BUS_VOLTAGE_SHIFT 3
+
+/* Cosmetic macro giving the sampling period for a full P=UxI cycle */
+#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \
+ * c->avg)
+
+static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT);
+}
+
+static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return (reg != INA2XX_CONFIG);
+}
+
+static inline bool is_signed_reg(unsigned int reg)
+{
+ return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT);
+}
+
+static const struct regmap_config ina2xx_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = INA2XX_MAX_REGISTERS,
+ .writeable_reg = ina2xx_is_writeable_reg,
+ .volatile_reg = ina2xx_is_volatile_reg,
+};
+
+enum ina2xx_ids { ina219, ina226 };
+
+struct ina2xx_config {
+ const char *name;
+ u16 config_default;
+ int calibration_value;
+ int shunt_voltage_lsb; /* nV */
+ int bus_voltage_shift; /* position of lsb */
+ int bus_voltage_lsb; /* uV */
+ /* fixed relation between current and power lsb, uW/uA */
+ int power_lsb_factor;
+ enum ina2xx_ids chip_id;
+};
+
+struct ina2xx_chip_info {
+ struct regmap *regmap;
+ struct task_struct *task;
+ const struct ina2xx_config *config;
+ struct mutex state_lock;
+ unsigned int shunt_resistor_uohm;
+ int avg;
+ int int_time_vbus; /* Bus voltage integration time uS */
+ int int_time_vshunt; /* Shunt voltage integration time uS */
+ int range_vbus; /* Bus voltage maximum in V */
+ int pga_gain_vshunt; /* Shunt voltage PGA gain */
+ bool allow_async_readout;
+ /* data buffer needs space for channel data and timestamp */
+ struct {
+ u16 chan[4];
+ u64 ts __aligned(8);
+ } scan;
+};
+
+static const struct ina2xx_config ina2xx_config[] = {
+ [ina219] = {
+ .name = "ina219",
+ .config_default = INA219_CONFIG_DEFAULT,
+ .calibration_value = 4096,
+ .shunt_voltage_lsb = 10000,
+ .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT,
+ .bus_voltage_lsb = 4000,
+ .power_lsb_factor = 20,
+ .chip_id = ina219,
+ },
+ [ina226] = {
+ .name = "ina226",
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_value = 2048,
+ .shunt_voltage_lsb = 2500,
+ .bus_voltage_shift = 0,
+ .bus_voltage_lsb = 1250,
+ .power_lsb_factor = 25,
+ .chip_id = ina226,
+ },
+};
+
+static int ina2xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int regval;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(chip->regmap, chan->address, &regval);
+ if (ret)
+ return ret;
+
+ if (is_signed_reg(chan->address))
+ *val = (s16) regval;
+ else
+ *val = regval;
+
+ if (chan->address == INA2XX_BUS_VOLTAGE)
+ *val >>= chip->config->bus_voltage_shift;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = chip->avg;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ *val2 = chip->int_time_vshunt;
+ else
+ *val2 = chip->int_time_vbus;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * Sample freq is read only, it is a consequence of
+ * 1/AVG*(CT_bus+CT_shunt).
+ */
+ *val = DIV_ROUND_CLOSEST(1000000, SAMPLING_PERIOD(chip));
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->address) {
+ case INA2XX_SHUNT_VOLTAGE:
+ /* processed (mV) = raw * lsb(nV) / 1000000 */
+ *val = chip->config->shunt_voltage_lsb;
+ *val2 = 1000000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_BUS_VOLTAGE:
+ /* processed (mV) = raw * lsb (uV) / 1000 */
+ *val = chip->config->bus_voltage_lsb;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_CURRENT:
+ /*
+ * processed (mA) = raw * current_lsb (mA)
+ * current_lsb (mA) = shunt_voltage_lsb (nV) /
+ * shunt_resistor (uOhm)
+ */
+ *val = chip->config->shunt_voltage_lsb;
+ *val2 = chip->shunt_resistor_uohm;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_POWER:
+ /*
+ * processed (mW) = raw * power_lsb (mW)
+ * power_lsb (mW) = power_lsb_factor (mW/mA) *
+ * current_lsb (mA)
+ */
+ *val = chip->config->power_lsb_factor *
+ chip->config->shunt_voltage_lsb;
+ *val2 = chip->shunt_resistor_uohm;
+ return IIO_VAL_FRACTIONAL;
+ }
+ return -EINVAL;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ switch (chan->address) {
+ case INA2XX_SHUNT_VOLTAGE:
+ *val = chip->pga_gain_vshunt;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+
+ case INA2XX_BUS_VOLTAGE:
+ *val = chip->range_vbus == 32 ? 1 : 2;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Available averaging rates for ina226. The indices correspond with
+ * the bit values expected by the chip (according to the ina226 datasheet,
+ * table 3 AVG bit settings, found at
+ * https://www.ti.com/lit/ds/symlink/ina226.pdf.
+ */
+static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
+
+static int ina226_set_average(struct ina2xx_chip_info *chip, unsigned int val,
+ unsigned int *config)
+{
+ int bits;
+
+ if (val > 1024 || val < 1)
+ return -EINVAL;
+
+ bits = find_closest(val, ina226_avg_tab,
+ ARRAY_SIZE(ina226_avg_tab));
+
+ chip->avg = ina226_avg_tab[bits];
+
+ *config &= ~INA226_AVG_MASK;
+ *config |= INA226_SHIFT_AVG(bits) & INA226_AVG_MASK;
+
+ return 0;
+}
+
+/* Conversion times in uS */
+static const int ina226_conv_time_tab[] = { 140, 204, 332, 588, 1100,
+ 2116, 4156, 8244 };
+
+static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits;
+
+ if (val_us > 8244 || val_us < 140)
+ return -EINVAL;
+
+ bits = find_closest(val_us, ina226_conv_time_tab,
+ ARRAY_SIZE(ina226_conv_time_tab));
+
+ chip->int_time_vbus = ina226_conv_time_tab[bits];
+
+ *config &= ~INA226_ITB_MASK;
+ *config |= INA226_SHIFT_ITB(bits) & INA226_ITB_MASK;
+
+ return 0;
+}
+
+static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits;
+
+ if (val_us > 8244 || val_us < 140)
+ return -EINVAL;
+
+ bits = find_closest(val_us, ina226_conv_time_tab,
+ ARRAY_SIZE(ina226_conv_time_tab));
+
+ chip->int_time_vshunt = ina226_conv_time_tab[bits];
+
+ *config &= ~INA226_ITS_MASK;
+ *config |= INA226_SHIFT_ITS(bits) & INA226_ITS_MASK;
+
+ return 0;
+}
+
+/* Conversion times in uS. */
+static const int ina219_conv_time_tab_subsample[] = { 84, 148, 276, 532 };
+static const int ina219_conv_time_tab_average[] = { 532, 1060, 2130, 4260,
+ 8510, 17020, 34050, 68100};
+
+static int ina219_lookup_int_time(unsigned int *val_us, int *bits)
+{
+ if (*val_us > 68100 || *val_us < 84)
+ return -EINVAL;
+
+ if (*val_us <= 532) {
+ *bits = find_closest(*val_us, ina219_conv_time_tab_subsample,
+ ARRAY_SIZE(ina219_conv_time_tab_subsample));
+ *val_us = ina219_conv_time_tab_subsample[*bits];
+ } else {
+ *bits = find_closest(*val_us, ina219_conv_time_tab_average,
+ ARRAY_SIZE(ina219_conv_time_tab_average));
+ *val_us = ina219_conv_time_tab_average[*bits];
+ *bits |= 0x8;
+ }
+
+ return 0;
+}
+
+static int ina219_set_int_time_vbus(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits, ret;
+ unsigned int val_us_best = val_us;
+
+ ret = ina219_lookup_int_time(&val_us_best, &bits);
+ if (ret)
+ return ret;
+
+ chip->int_time_vbus = val_us_best;
+
+ *config &= ~INA219_ITB_MASK;
+ *config |= INA219_SHIFT_ITB(bits) & INA219_ITB_MASK;
+
+ return 0;
+}
+
+static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip,
+ unsigned int val_us, unsigned int *config)
+{
+ int bits, ret;
+ unsigned int val_us_best = val_us;
+
+ ret = ina219_lookup_int_time(&val_us_best, &bits);
+ if (ret)
+ return ret;
+
+ chip->int_time_vshunt = val_us_best;
+
+ *config &= ~INA219_ITS_MASK;
+ *config |= INA219_SHIFT_ITS(bits) & INA219_ITS_MASK;
+
+ return 0;
+}
+
+static const int ina219_vbus_range_tab[] = { 1, 2 };
+static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip,
+ unsigned int range,
+ unsigned int *config)
+{
+ if (range == 1)
+ chip->range_vbus = 32;
+ else if (range == 2)
+ chip->range_vbus = 16;
+ else
+ return -EINVAL;
+
+ *config &= ~INA219_BRNG_MASK;
+ *config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK;
+
+ return 0;
+}
+
+static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 };
+static const int ina219_vshunt_gain_frac[] = {
+ 125, 1000, 250, 1000, 500, 1000, 1000, 1000 };
+
+static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip,
+ unsigned int gain,
+ unsigned int *config)
+{
+ int bits;
+
+ if (gain < 125 || gain > 1000)
+ return -EINVAL;
+
+ bits = find_closest(gain, ina219_vshunt_gain_tab,
+ ARRAY_SIZE(ina219_vshunt_gain_tab));
+
+ chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits];
+ bits = 3 - bits;
+
+ *config &= ~INA219_PGA_MASK;
+ *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK;
+
+ return 0;
+}
+
+static int ina2xx_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_HARDWAREGAIN:
+ switch (chan->address) {
+ case INA2XX_SHUNT_VOLTAGE:
+ *type = IIO_VAL_FRACTIONAL;
+ *length = sizeof(ina219_vshunt_gain_frac) / sizeof(int);
+ *vals = ina219_vshunt_gain_frac;
+ return IIO_AVAIL_LIST;
+
+ case INA2XX_BUS_VOLTAGE:
+ *type = IIO_VAL_INT;
+ *length = sizeof(ina219_vbus_range_tab) / sizeof(int);
+ *vals = ina219_vbus_range_tab;
+ return IIO_AVAIL_LIST;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ina2xx_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int config, tmp;
+ int ret;
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ mutex_lock(&chip->state_lock);
+
+ ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config);
+ if (ret)
+ goto err;
+
+ tmp = config;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = ina226_set_average(chip, val, &tmp);
+ break;
+
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chip->config->chip_id == ina226) {
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ ret = ina226_set_int_time_vshunt(chip, val2,
+ &tmp);
+ else
+ ret = ina226_set_int_time_vbus(chip, val2,
+ &tmp);
+ } else {
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ ret = ina219_set_int_time_vshunt(chip, val2,
+ &tmp);
+ else
+ ret = ina219_set_int_time_vbus(chip, val2,
+ &tmp);
+ }
+ break;
+
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ if (chan->address == INA2XX_SHUNT_VOLTAGE)
+ ret = ina219_set_vshunt_pga_gain(chip, val * 1000 +
+ val2 / 1000, &tmp);
+ else
+ ret = ina219_set_vbus_range_denom(chip, val, &tmp);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ if (!ret && (tmp != config))
+ ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp);
+err:
+ mutex_unlock(&chip->state_lock);
+
+ return ret;
+}
+
+static ssize_t ina2xx_allow_async_readout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%d\n", chip->allow_async_readout);
+}
+
+static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ bool val;
+ int ret;
+
+ ret = kstrtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ chip->allow_async_readout = val;
+
+ return len;
+}
+
+/*
+ * Calibration register is set to the best value, which eliminates
+ * truncation errors on calculating current register in hardware.
+ * According to datasheet (INA 226: eq. 3, INA219: eq. 4) the best values
+ * are 2048 for ina226 and 4096 for ina219. They are hardcoded as
+ * calibration_value.
+ */
+static int ina2xx_set_calibration(struct ina2xx_chip_info *chip)
+{
+ return regmap_write(chip->regmap, INA2XX_CALIBRATION,
+ chip->config->calibration_value);
+}
+
+static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
+{
+ if (val == 0 || val > INT_MAX)
+ return -EINVAL;
+
+ chip->shunt_resistor_uohm = val;
+
+ return 0;
+}
+
+static ssize_t ina2xx_shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ int vals[2] = { chip->shunt_resistor_uohm, 1000000 };
+
+ return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals);
+}
+
+static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
+ int val, val_fract, ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
+ if (ret)
+ return ret;
+
+ ret = set_shunt_resistor(chip, val * 1000000 + val_fract);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+#define INA219_CHAN(_type, _index, _address) { \
+ .type = (_type), \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ } \
+}
+
+#define INA226_CHAN(_type, _index, _address) { \
+ .type = (_type), \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ } \
+}
+
+/*
+ * Sampling Freq is a consequence of the integration times of
+ * the Voltage channels.
+ */
+#define INA219_CHAN_VOLTAGE(_index, _address, _shift) { \
+ .type = IIO_VOLTAGE, \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME) | \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .info_mask_separate_available = \
+ BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .shift = _shift, \
+ .realbits = 16 - _shift, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ } \
+}
+
+#define INA226_CHAN_VOLTAGE(_index, _address) { \
+ .type = IIO_VOLTAGE, \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ } \
+}
+
+
+static const struct iio_chan_spec ina226_channels[] = {
+ INA226_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE),
+ INA226_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE),
+ INA226_CHAN(IIO_POWER, 2, INA2XX_POWER),
+ INA226_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct iio_chan_spec ina219_channels[] = {
+ INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE, 0),
+ INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE, INA219_BUS_VOLTAGE_SHIFT),
+ INA219_CHAN(IIO_POWER, 2, INA2XX_POWER),
+ INA219_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static int ina2xx_conversion_ready(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ int ret;
+ unsigned int alert;
+
+ /*
+ * Because the timer thread and the chip conversion clock
+ * are asynchronous, the period difference will eventually
+ * result in reading V[k-1] again, or skip V[k] at time Tk.
+ * In order to resync the timer with the conversion process
+ * we check the ConVersionReadyFlag.
+ * On hardware that supports using the ALERT pin to toggle a
+ * GPIO a triggered buffer could be used instead.
+ * For now, we do an extra read of the MASK_ENABLE register (INA226)
+ * resp. the BUS_VOLTAGE register (INA219).
+ */
+ if (chip->config->chip_id == ina226) {
+ ret = regmap_read(chip->regmap,
+ INA226_MASK_ENABLE, &alert);
+ alert &= INA226_CVRF;
+ } else {
+ ret = regmap_read(chip->regmap,
+ INA2XX_BUS_VOLTAGE, &alert);
+ alert &= INA219_CNVR;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return !!alert;
+}
+
+static int ina2xx_work_buffer(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ int bit, ret, i = 0;
+ s64 time;
+
+ time = iio_get_time_ns(indio_dev);
+
+ /*
+ * Single register reads: bulk_read will not work with ina226/219
+ * as there is no auto-increment of the register pointer.
+ */
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ unsigned int val;
+
+ ret = regmap_read(chip->regmap,
+ INA2XX_SHUNT_VOLTAGE + bit, &val);
+ if (ret < 0)
+ return ret;
+
+ chip->scan.chan[i++] = val;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &chip->scan, time);
+
+ return 0;
+};
+
+static int ina2xx_capture_thread(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ int sampling_us = SAMPLING_PERIOD(chip);
+ int ret;
+ struct timespec64 next, now, delta;
+ s64 delay_us;
+
+ /*
+ * Poll a bit faster than the chip internal Fs, in case
+ * we wish to sync with the conversion ready flag.
+ */
+ if (!chip->allow_async_readout)
+ sampling_us -= 200;
+
+ ktime_get_ts64(&next);
+
+ do {
+ while (!chip->allow_async_readout) {
+ ret = ina2xx_conversion_ready(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * If the conversion was not yet finished,
+ * reset the reference timestamp.
+ */
+ if (ret == 0)
+ ktime_get_ts64(&next);
+ else
+ break;
+ }
+
+ ret = ina2xx_work_buffer(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ktime_get_ts64(&now);
+
+ /*
+ * Advance the timestamp for the next poll by one sampling
+ * interval, and sleep for the remainder (next - now)
+ * In case "next" has already passed, the interval is added
+ * multiple times, i.e. samples are dropped.
+ */
+ do {
+ timespec64_add_ns(&next, 1000 * sampling_us);
+ delta = timespec64_sub(next, now);
+ delay_us = div_s64(timespec64_to_ns(&delta), 1000);
+ } while (delay_us <= 0);
+
+ usleep_range(delay_us, (delay_us * 3) >> 1);
+
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ unsigned int sampling_us = SAMPLING_PERIOD(chip);
+ struct task_struct *task;
+
+ dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n",
+ (unsigned int)(*indio_dev->active_scan_mask),
+ 1000000 / sampling_us, chip->avg);
+
+ dev_dbg(&indio_dev->dev, "Expected work period: %u us\n", sampling_us);
+ dev_dbg(&indio_dev->dev, "Async readout mode: %d\n",
+ chip->allow_async_readout);
+
+ task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
+ "%s:%d-%uus", indio_dev->name,
+ iio_device_id(indio_dev),
+ sampling_us);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ chip->task = task;
+
+ return 0;
+}
+
+static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ if (chip->task) {
+ kthread_stop(chip->task);
+ chip->task = NULL;
+ }
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ina2xx_setup_ops = {
+ .postenable = &ina2xx_buffer_enable,
+ .predisable = &ina2xx_buffer_disable,
+};
+
+static int ina2xx_debug_reg(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval, unsigned *readval)
+{
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+
+ if (!readval)
+ return regmap_write(chip->regmap, reg, writeval);
+
+ return regmap_read(chip->regmap, reg, readval);
+}
+
+/* Possible integration times for vshunt and vbus */
+static IIO_CONST_ATTR_NAMED(ina219_integration_time_available,
+ integration_time_available,
+ "0.000084 0.000148 0.000276 0.000532 0.001060 0.002130 0.004260 0.008510 0.017020 0.034050 0.068100");
+
+static IIO_CONST_ATTR_NAMED(ina226_integration_time_available,
+ integration_time_available,
+ "0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244");
+
+static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR,
+ ina2xx_allow_async_readout_show,
+ ina2xx_allow_async_readout_store, 0);
+
+static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR,
+ ina2xx_shunt_resistor_show,
+ ina2xx_shunt_resistor_store, 0);
+
+static struct attribute *ina219_attributes[] = {
+ &iio_dev_attr_in_allow_async_readout.dev_attr.attr,
+ &iio_const_attr_ina219_integration_time_available.dev_attr.attr,
+ &iio_dev_attr_in_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute *ina226_attributes[] = {
+ &iio_dev_attr_in_allow_async_readout.dev_attr.attr,
+ &iio_const_attr_ina226_integration_time_available.dev_attr.attr,
+ &iio_dev_attr_in_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ina219_attribute_group = {
+ .attrs = ina219_attributes,
+};
+
+static const struct attribute_group ina226_attribute_group = {
+ .attrs = ina226_attributes,
+};
+
+static const struct iio_info ina219_info = {
+ .attrs = &ina219_attribute_group,
+ .read_raw = ina2xx_read_raw,
+ .read_avail = ina2xx_read_avail,
+ .write_raw = ina2xx_write_raw,
+ .debugfs_reg_access = ina2xx_debug_reg,
+};
+
+static const struct iio_info ina226_info = {
+ .attrs = &ina226_attribute_group,
+ .read_raw = ina2xx_read_raw,
+ .write_raw = ina2xx_write_raw,
+ .debugfs_reg_access = ina2xx_debug_reg,
+};
+
+/* Initialize the configuration and calibration registers. */
+static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config)
+{
+ int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
+ if (ret)
+ return ret;
+
+ return ina2xx_set_calibration(chip);
+}
+
+static int ina2xx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ina2xx_chip_info *chip;
+ struct iio_dev *indio_dev;
+ unsigned int val;
+ enum ina2xx_ids type;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ /* This is only used for device removal purposes. */
+ i2c_set_clientdata(client, indio_dev);
+
+ chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(chip->regmap);
+ }
+
+ if (client->dev.of_node)
+ type = (uintptr_t)of_device_get_match_data(&client->dev);
+ else
+ type = id->driver_data;
+ chip->config = &ina2xx_config[type];
+
+ mutex_init(&chip->state_lock);
+
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor", &val) < 0) {
+ struct ina2xx_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+
+ if (pdata)
+ val = pdata->shunt_uohms;
+ else
+ val = INA2XX_RSHUNT_DEFAULT;
+ }
+
+ ret = set_shunt_resistor(chip, val);
+ if (ret)
+ return ret;
+
+ /* Patch the current config register with default. */
+ val = chip->config->config_default;
+
+ if (type == ina226) {
+ ina226_set_average(chip, INA226_DEFAULT_AVG, &val);
+ ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val);
+ ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val);
+ } else {
+ chip->avg = 1;
+ ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val);
+ ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val);
+ ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val);
+ ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val);
+ }
+
+ ret = ina2xx_init(chip, val);
+ if (ret) {
+ dev_err(&client->dev, "error configuring the device\n");
+ return ret;
+ }
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ if (type == ina226) {
+ indio_dev->channels = ina226_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ina226_channels);
+ indio_dev->info = &ina226_info;
+ } else {
+ indio_dev->channels = ina219_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ina219_channels);
+ indio_dev->info = &ina219_info;
+ }
+ indio_dev->name = id ? id->name : chip->config->name;
+
+ ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
+ &ina2xx_setup_ops);
+ if (ret)
+ return ret;
+
+ return iio_device_register(indio_dev);
+}
+
+static void ina2xx_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ina2xx_chip_info *chip = iio_priv(indio_dev);
+ int ret;
+
+ iio_device_unregister(indio_dev);
+
+ /* Powerdown */
+ ret = regmap_update_bits(chip->regmap, INA2XX_CONFIG,
+ INA2XX_MODE_MASK, 0);
+ if (ret)
+ dev_warn(&client->dev, "Failed to power down device (%pe)\n",
+ ERR_PTR(ret));
+}
+
+static const struct i2c_device_id ina2xx_id[] = {
+ {"ina219", ina219},
+ {"ina220", ina219},
+ {"ina226", ina226},
+ {"ina230", ina226},
+ {"ina231", ina226},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+
+static const struct of_device_id ina2xx_of_match[] = {
+ {
+ .compatible = "ti,ina219",
+ .data = (void *)ina219
+ },
+ {
+ .compatible = "ti,ina220",
+ .data = (void *)ina219
+ },
+ {
+ .compatible = "ti,ina226",
+ .data = (void *)ina226
+ },
+ {
+ .compatible = "ti,ina230",
+ .data = (void *)ina226
+ },
+ {
+ .compatible = "ti,ina231",
+ .data = (void *)ina226
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ina2xx_of_match);
+
+static struct i2c_driver ina2xx_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ina2xx_of_match,
+ },
+ .probe = ina2xx_probe,
+ .remove = ina2xx_remove,
+ .id_table = ina2xx_id,
+};
+module_i2c_driver(ina2xx_driver);
+
+MODULE_AUTHOR("Marc Titinger <marc.titinger@baylibre.com>");
+MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
new file mode 100644
index 000000000..a7325dbbb
--- /dev/null
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -0,0 +1,923 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADC driver for the Ingenic JZ47xx SoCs
+ * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
+ *
+ * based on drivers/mfd/jz4740-adc.c
+ */
+
+#include <dt-bindings/iio/adc/ingenic,adc.h>
+#include <linux/clk.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+#define JZ_ADC_REG_ENABLE 0x00
+#define JZ_ADC_REG_CFG 0x04
+#define JZ_ADC_REG_CTRL 0x08
+#define JZ_ADC_REG_STATUS 0x0c
+#define JZ_ADC_REG_ADSAME 0x10
+#define JZ_ADC_REG_ADWAIT 0x14
+#define JZ_ADC_REG_ADTCH 0x18
+#define JZ_ADC_REG_ADBDAT 0x1c
+#define JZ_ADC_REG_ADSDAT 0x20
+#define JZ_ADC_REG_ADCMD 0x24
+#define JZ_ADC_REG_ADCLK 0x28
+
+#define JZ_ADC_REG_ENABLE_PD BIT(7)
+#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
+#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
+#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10)
+#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16)
+#define JZ_ADC_REG_CFG_CMD_SEL BIT(22)
+#define JZ_ADC_REG_CFG_VBAT_SEL BIT(30)
+#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10))
+#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
+#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
+#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
+#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16
+
+#define JZ_ADC_REG_ADCMD_YNADC BIT(7)
+#define JZ_ADC_REG_ADCMD_YPADC BIT(8)
+#define JZ_ADC_REG_ADCMD_XNADC BIT(9)
+#define JZ_ADC_REG_ADCMD_XPADC BIT(10)
+#define JZ_ADC_REG_ADCMD_VREFPYP BIT(11)
+#define JZ_ADC_REG_ADCMD_VREFPXP BIT(12)
+#define JZ_ADC_REG_ADCMD_VREFPXN BIT(13)
+#define JZ_ADC_REG_ADCMD_VREFPAUX BIT(14)
+#define JZ_ADC_REG_ADCMD_VREFPVDD33 BIT(15)
+#define JZ_ADC_REG_ADCMD_VREFNYN BIT(16)
+#define JZ_ADC_REG_ADCMD_VREFNXP BIT(17)
+#define JZ_ADC_REG_ADCMD_VREFNXN BIT(18)
+#define JZ_ADC_REG_ADCMD_VREFAUX BIT(19)
+#define JZ_ADC_REG_ADCMD_YNGRU BIT(20)
+#define JZ_ADC_REG_ADCMD_XNGRU BIT(21)
+#define JZ_ADC_REG_ADCMD_XPGRU BIT(22)
+#define JZ_ADC_REG_ADCMD_YPSUP BIT(23)
+#define JZ_ADC_REG_ADCMD_XNSUP BIT(24)
+#define JZ_ADC_REG_ADCMD_XPSUP BIT(25)
+
+#define JZ_ADC_AUX_VREF 3300
+#define JZ_ADC_AUX_VREF_BITS 12
+#define JZ_ADC_BATTERY_LOW_VREF 2500
+#define JZ_ADC_BATTERY_LOW_VREF_BITS 12
+#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500
+#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
+#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
+#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
+#define JZ4760_ADC_BATTERY_VREF 2500
+#define JZ4770_ADC_BATTERY_VREF 1200
+#define JZ4770_ADC_BATTERY_VREF_BITS 12
+
+#define JZ_ADC_IRQ_AUX BIT(0)
+#define JZ_ADC_IRQ_BATTERY BIT(1)
+#define JZ_ADC_IRQ_TOUCH BIT(2)
+#define JZ_ADC_IRQ_PEN_DOWN BIT(3)
+#define JZ_ADC_IRQ_PEN_UP BIT(4)
+#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5)
+#define JZ_ADC_IRQ_SLEEP BIT(7)
+
+struct ingenic_adc;
+
+struct ingenic_adc_soc_data {
+ unsigned int battery_high_vref;
+ unsigned int battery_high_vref_bits;
+ const int *battery_raw_avail;
+ size_t battery_raw_avail_size;
+ const int *battery_scale_avail;
+ size_t battery_scale_avail_size;
+ unsigned int battery_vref_mode: 1;
+ unsigned int has_aux_md: 1;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
+};
+
+struct ingenic_adc {
+ void __iomem *base;
+ struct clk *clk;
+ struct mutex lock;
+ struct mutex aux_lock;
+ const struct ingenic_adc_soc_data *soc_data;
+ bool low_vref_mode;
+};
+
+static void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ mutex_lock(&adc->lock);
+
+ /* Init ADCMD */
+ readl(adc->base + JZ_ADC_REG_ADCMD);
+
+ if (mask & 0x3) {
+ /* Second channel (INGENIC_ADC_TOUCH_YP): sample YP vs. GND */
+ writel(JZ_ADC_REG_ADCMD_XNGRU
+ | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33
+ | JZ_ADC_REG_ADCMD_YPADC,
+ adc->base + JZ_ADC_REG_ADCMD);
+
+ /* First channel (INGENIC_ADC_TOUCH_XP): sample XP vs. GND */
+ writel(JZ_ADC_REG_ADCMD_YNGRU
+ | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33
+ | JZ_ADC_REG_ADCMD_XPADC,
+ adc->base + JZ_ADC_REG_ADCMD);
+ }
+
+ if (mask & 0xc) {
+ /* Fourth channel (INGENIC_ADC_TOUCH_YN): sample YN vs. GND */
+ writel(JZ_ADC_REG_ADCMD_XNGRU
+ | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33
+ | JZ_ADC_REG_ADCMD_YNADC,
+ adc->base + JZ_ADC_REG_ADCMD);
+
+ /* Third channel (INGENIC_ADC_TOUCH_XN): sample XN vs. GND */
+ writel(JZ_ADC_REG_ADCMD_YNGRU
+ | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33
+ | JZ_ADC_REG_ADCMD_XNADC,
+ adc->base + JZ_ADC_REG_ADCMD);
+ }
+
+ if (mask & 0x30) {
+ /* Sixth channel (INGENIC_ADC_TOUCH_YD): sample YP vs. YN */
+ writel(JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33
+ | JZ_ADC_REG_ADCMD_YPADC,
+ adc->base + JZ_ADC_REG_ADCMD);
+
+ /* Fifth channel (INGENIC_ADC_TOUCH_XD): sample XP vs. XN */
+ writel(JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33
+ | JZ_ADC_REG_ADCMD_XPADC,
+ adc->base + JZ_ADC_REG_ADCMD);
+ }
+
+ /* We're done */
+ writel(0, adc->base + JZ_ADC_REG_ADCMD);
+
+ mutex_unlock(&adc->lock);
+}
+
+static void ingenic_adc_set_config(struct ingenic_adc *adc,
+ uint32_t mask,
+ uint32_t val)
+{
+ uint32_t cfg;
+
+ mutex_lock(&adc->lock);
+
+ cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask;
+ cfg |= val;
+ writel(cfg, adc->base + JZ_ADC_REG_CFG);
+
+ mutex_unlock(&adc->lock);
+}
+
+static void ingenic_adc_enable_unlocked(struct ingenic_adc *adc,
+ int engine,
+ bool enabled)
+{
+ u8 val;
+
+ val = readb(adc->base + JZ_ADC_REG_ENABLE);
+
+ if (enabled)
+ val |= BIT(engine);
+ else
+ val &= ~BIT(engine);
+
+ writeb(val, adc->base + JZ_ADC_REG_ENABLE);
+}
+
+static void ingenic_adc_enable(struct ingenic_adc *adc,
+ int engine,
+ bool enabled)
+{
+ mutex_lock(&adc->lock);
+ ingenic_adc_enable_unlocked(adc, engine, enabled);
+ mutex_unlock(&adc->lock);
+}
+
+static int ingenic_adc_capture(struct ingenic_adc *adc,
+ int engine)
+{
+ u32 cfg;
+ u8 val;
+ int ret;
+
+ /*
+ * Disable CMD_SEL temporarily, because it causes wrong VBAT readings,
+ * probably due to the switch of VREF. We must keep the lock here to
+ * avoid races with the buffer enable/disable functions.
+ */
+ mutex_lock(&adc->lock);
+ cfg = readl(adc->base + JZ_ADC_REG_CFG);
+ writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG);
+
+ ingenic_adc_enable_unlocked(adc, engine, true);
+ ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val,
+ !(val & BIT(engine)), 250, 1000);
+ if (ret)
+ ingenic_adc_enable_unlocked(adc, engine, false);
+
+ writel(cfg, adc->base + JZ_ADC_REG_CFG);
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long m)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+ struct device *dev = iio_dev->dev.parent;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case INGENIC_ADC_BATTERY:
+ if (!adc->soc_data->battery_vref_mode)
+ return -EINVAL;
+
+ ret = clk_enable(adc->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (val > JZ_ADC_BATTERY_LOW_VREF) {
+ ingenic_adc_set_config(adc,
+ JZ_ADC_REG_CFG_BAT_MD,
+ 0);
+ adc->low_vref_mode = false;
+ } else {
+ ingenic_adc_set_config(adc,
+ JZ_ADC_REG_CFG_BAT_MD,
+ JZ_ADC_REG_CFG_BAT_MD);
+ adc->low_vref_mode = true;
+ }
+
+ clk_disable(adc->clk);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const int jz4725b_adc_battery_raw_avail[] = {
+ 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
+};
+
+static const int jz4725b_adc_battery_scale_avail[] = {
+ JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
+ JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
+};
+
+static const int jz4740_adc_battery_raw_avail[] = {
+ 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
+};
+
+static const int jz4740_adc_battery_scale_avail[] = {
+ JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
+ JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
+};
+
+static const int jz4760_adc_battery_scale_avail[] = {
+ JZ4760_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
+};
+
+static const int jz4770_adc_battery_raw_avail[] = {
+ 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1,
+};
+
+static const int jz4770_adc_battery_scale_avail[] = {
+ JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_VREF_BITS,
+};
+
+static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
+{
+ struct clk *parent_clk;
+ unsigned long parent_rate, rate;
+ unsigned int div_main, div_10us;
+
+ parent_clk = clk_get_parent(adc->clk);
+ if (!parent_clk) {
+ dev_err(dev, "ADC clock has no parent\n");
+ return -ENODEV;
+ }
+ parent_rate = clk_get_rate(parent_clk);
+
+ /*
+ * The JZ4725B ADC works at 500 kHz to 8 MHz.
+ * We pick the highest rate possible.
+ * In practice we typically get 6 MHz, half of the 12 MHz EXT clock.
+ */
+ div_main = DIV_ROUND_UP(parent_rate, 8000000);
+ div_main = clamp(div_main, 1u, 64u);
+ rate = parent_rate / div_main;
+ if (rate < 500000 || rate > 8000000) {
+ dev_err(dev, "No valid divider for ADC main clock\n");
+ return -EINVAL;
+ }
+
+ /* We also need a divider that produces a 10us clock. */
+ div_10us = DIV_ROUND_UP(rate, 100000);
+
+ writel(((div_10us - 1) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) |
+ (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
+ adc->base + JZ_ADC_REG_ADCLK);
+
+ return 0;
+}
+
+static int jz4770_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
+{
+ struct clk *parent_clk;
+ unsigned long parent_rate, rate;
+ unsigned int div_main, div_ms, div_10us;
+
+ parent_clk = clk_get_parent(adc->clk);
+ if (!parent_clk) {
+ dev_err(dev, "ADC clock has no parent\n");
+ return -ENODEV;
+ }
+ parent_rate = clk_get_rate(parent_clk);
+
+ /*
+ * The JZ4770 ADC works at 20 kHz to 200 kHz.
+ * We pick the highest rate possible.
+ */
+ div_main = DIV_ROUND_UP(parent_rate, 200000);
+ div_main = clamp(div_main, 1u, 256u);
+ rate = parent_rate / div_main;
+ if (rate < 20000 || rate > 200000) {
+ dev_err(dev, "No valid divider for ADC main clock\n");
+ return -EINVAL;
+ }
+
+ /* We also need a divider that produces a 10us clock. */
+ div_10us = DIV_ROUND_UP(rate, 10000);
+ /* And another, which produces a 1ms clock. */
+ div_ms = DIV_ROUND_UP(rate, 1000);
+
+ writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) |
+ ((div_10us - 1) << JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB) |
+ (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
+ adc->base + JZ_ADC_REG_ADCLK);
+
+ return 0;
+}
+
+static const struct iio_chan_spec jz4740_channels[] = {
+ {
+ .extend_name = "aux",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX,
+ .scan_index = -1,
+ },
+ {
+ .extend_name = "battery",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_BATTERY,
+ .scan_index = -1,
+ },
+};
+
+static const struct iio_chan_spec jz4760_channels[] = {
+ {
+ .extend_name = "aux",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX0,
+ .scan_index = -1,
+ },
+ {
+ .extend_name = "aux1",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX,
+ .scan_index = -1,
+ },
+ {
+ .extend_name = "aux2",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX2,
+ .scan_index = -1,
+ },
+ {
+ .extend_name = "battery",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_BATTERY,
+ .scan_index = -1,
+ },
+};
+
+static const struct iio_chan_spec jz4770_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_XP,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_YP,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_XN,
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_YN,
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_XD,
+ .scan_index = 4,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_YD,
+ .scan_index = 5,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
+ {
+ .extend_name = "aux",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX,
+ .scan_index = -1,
+ },
+ {
+ .extend_name = "battery",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_BATTERY,
+ .scan_index = -1,
+ },
+ {
+ .extend_name = "aux2",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX2,
+ .scan_index = -1,
+ },
+};
+
+static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
+ .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF,
+ .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
+ .battery_raw_avail = jz4725b_adc_battery_raw_avail,
+ .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
+ .battery_scale_avail = jz4725b_adc_battery_scale_avail,
+ .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
+ .battery_vref_mode = true,
+ .has_aux_md = false,
+ .channels = jz4740_channels,
+ .num_channels = ARRAY_SIZE(jz4740_channels),
+ .init_clk_div = jz4725b_adc_init_clk_div,
+};
+
+static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
+ .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF,
+ .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
+ .battery_raw_avail = jz4740_adc_battery_raw_avail,
+ .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
+ .battery_scale_avail = jz4740_adc_battery_scale_avail,
+ .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
+ .battery_vref_mode = true,
+ .has_aux_md = false,
+ .channels = jz4740_channels,
+ .num_channels = ARRAY_SIZE(jz4740_channels),
+ .init_clk_div = NULL, /* no ADCLK register on JZ4740 */
+};
+
+static const struct ingenic_adc_soc_data jz4760_adc_soc_data = {
+ .battery_high_vref = JZ4760_ADC_BATTERY_VREF,
+ .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
+ .battery_raw_avail = jz4770_adc_battery_raw_avail,
+ .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
+ .battery_scale_avail = jz4760_adc_battery_scale_avail,
+ .battery_scale_avail_size = ARRAY_SIZE(jz4760_adc_battery_scale_avail),
+ .battery_vref_mode = false,
+ .has_aux_md = true,
+ .channels = jz4760_channels,
+ .num_channels = ARRAY_SIZE(jz4760_channels),
+ .init_clk_div = jz4770_adc_init_clk_div,
+};
+
+static const struct ingenic_adc_soc_data jz4770_adc_soc_data = {
+ .battery_high_vref = JZ4770_ADC_BATTERY_VREF,
+ .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS,
+ .battery_raw_avail = jz4770_adc_battery_raw_avail,
+ .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail),
+ .battery_scale_avail = jz4770_adc_battery_scale_avail,
+ .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail),
+ .battery_vref_mode = false,
+ .has_aux_md = true,
+ .channels = jz4770_channels,
+ .num_channels = ARRAY_SIZE(jz4770_channels),
+ .init_clk_div = jz4770_adc_init_clk_div,
+};
+
+static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type,
+ int *length,
+ long m)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *type = IIO_VAL_INT;
+ *length = adc->soc_data->battery_raw_avail_size;
+ *vals = adc->soc_data->battery_raw_avail;
+ return IIO_AVAIL_RANGE;
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_FRACTIONAL_LOG2;
+ *length = adc->soc_data->battery_scale_avail_size;
+ *vals = adc->soc_data->battery_scale_avail;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ingenic_adc_read_chan_info_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ int cmd, ret, engine = (chan->channel == INGENIC_ADC_BATTERY);
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ ret = clk_enable(adc->clk);
+ if (ret) {
+ dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* We cannot sample the aux channels in parallel. */
+ mutex_lock(&adc->aux_lock);
+ if (adc->soc_data->has_aux_md && engine == 0) {
+ switch (chan->channel) {
+ case INGENIC_ADC_AUX0:
+ cmd = 0;
+ break;
+ case INGENIC_ADC_AUX:
+ cmd = 1;
+ break;
+ case INGENIC_ADC_AUX2:
+ cmd = 2;
+ break;
+ }
+
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, cmd);
+ }
+
+ ret = ingenic_adc_capture(adc, engine);
+ if (ret)
+ goto out;
+
+ switch (chan->channel) {
+ case INGENIC_ADC_AUX0:
+ case INGENIC_ADC_AUX:
+ case INGENIC_ADC_AUX2:
+ *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
+ break;
+ case INGENIC_ADC_BATTERY:
+ *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
+ break;
+ }
+
+ ret = IIO_VAL_INT;
+out:
+ mutex_unlock(&adc->aux_lock);
+ clk_disable(adc->clk);
+
+ return ret;
+}
+
+static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ingenic_adc_read_chan_info_raw(iio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case INGENIC_ADC_AUX0:
+ case INGENIC_ADC_AUX:
+ case INGENIC_ADC_AUX2:
+ *val = JZ_ADC_AUX_VREF;
+ *val2 = JZ_ADC_AUX_VREF_BITS;
+ break;
+ case INGENIC_ADC_BATTERY:
+ if (adc->low_vref_mode) {
+ *val = JZ_ADC_BATTERY_LOW_VREF;
+ *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS;
+ } else {
+ *val = adc->soc_data->battery_high_vref;
+ *val2 = adc->soc_data->battery_high_vref_bits;
+ }
+ break;
+ }
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ int i;
+
+ if (!iiospec->nargs)
+ return -EINVAL;
+
+ for (i = 0; i < iio_dev->num_channels; ++i)
+ if (iio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info ingenic_adc_info = {
+ .write_raw = ingenic_adc_write_raw,
+ .read_raw = ingenic_adc_read_raw,
+ .read_avail = ingenic_adc_read_avail,
+ .fwnode_xlate = ingenic_adc_fwnode_xlate,
+};
+
+static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+ int ret;
+
+ ret = clk_enable(adc->clk);
+ if (ret) {
+ dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* It takes significant time for the touchscreen hw to stabilize. */
+ msleep(50);
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK,
+ JZ_ADC_REG_CFG_SAMPLE_NUM(4) |
+ JZ_ADC_REG_CFG_PULL_UP(4));
+
+ writew(80, adc->base + JZ_ADC_REG_ADWAIT);
+ writew(2, adc->base + JZ_ADC_REG_ADSAME);
+ writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL);
+ writel(0, adc->base + JZ_ADC_REG_ADTCH);
+
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL,
+ JZ_ADC_REG_CFG_CMD_SEL);
+ ingenic_adc_set_adcmd(iio_dev, iio_dev->active_scan_mask[0]);
+
+ ingenic_adc_enable(adc, 2, true);
+
+ return 0;
+}
+
+static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ ingenic_adc_enable(adc, 2, false);
+
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 0);
+
+ writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
+ writeb(0xff, adc->base + JZ_ADC_REG_STATUS);
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0);
+ writew(0, adc->base + JZ_ADC_REG_ADSAME);
+ writew(0, adc->base + JZ_ADC_REG_ADWAIT);
+ clk_disable(adc->clk);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = {
+ .postenable = &ingenic_adc_buffer_enable,
+ .predisable = &ingenic_adc_buffer_disable
+};
+
+static irqreturn_t ingenic_adc_irq(int irq, void *data)
+{
+ struct iio_dev *iio_dev = data;
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+ unsigned long mask = iio_dev->active_scan_mask[0];
+ unsigned int i;
+ u32 tdat[3];
+
+ for (i = 0; i < ARRAY_SIZE(tdat); mask >>= 2, i++) {
+ if (mask & 0x3)
+ tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH);
+ else
+ tdat[i] = 0;
+ }
+
+ iio_push_to_buffers(iio_dev, tdat);
+ writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int ingenic_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *iio_dev;
+ struct ingenic_adc *adc;
+ const struct ingenic_adc_soc_data *soc_data;
+ int irq, ret;
+
+ soc_data = device_get_match_data(dev);
+ if (!soc_data)
+ return -EINVAL;
+
+ iio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(iio_dev);
+ mutex_init(&adc->lock);
+ mutex_init(&adc->aux_lock);
+ adc->soc_data = soc_data;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0,
+ dev_name(dev), iio_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request irq: %d\n", ret);
+ return ret;
+ }
+
+ adc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc->base))
+ return PTR_ERR(adc->base);
+
+ adc->clk = devm_clk_get_prepared(dev, "adc");
+ if (IS_ERR(adc->clk)) {
+ dev_err(dev, "Unable to get clock\n");
+ return PTR_ERR(adc->clk);
+ }
+
+ ret = clk_enable(adc->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ /* Set clock dividers. */
+ if (soc_data->init_clk_div) {
+ ret = soc_data->init_clk_div(dev, adc);
+ if (ret) {
+ clk_disable_unprepare(adc->clk);
+ return ret;
+ }
+ }
+
+ /* Put hardware in a known passive state. */
+ writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
+ writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
+
+ /* JZ4760B specific */
+ if (device_property_present(dev, "ingenic,use-internal-divider"))
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL,
+ JZ_ADC_REG_CFG_VBAT_SEL);
+ else
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_VBAT_SEL, 0);
+
+ usleep_range(2000, 3000); /* Must wait at least 2ms. */
+ clk_disable(adc->clk);
+
+ iio_dev->name = "jz-adc";
+ iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ iio_dev->setup_ops = &ingenic_buffer_setup_ops;
+ iio_dev->channels = soc_data->channels;
+ iio_dev->num_channels = soc_data->num_channels;
+ iio_dev->info = &ingenic_adc_info;
+
+ ret = devm_iio_device_register(dev, iio_dev);
+ if (ret)
+ dev_err(dev, "Unable to register IIO device\n");
+
+ return ret;
+}
+
+static const struct of_device_id ingenic_adc_of_match[] = {
+ { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
+ { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
+ { .compatible = "ingenic,jz4760-adc", .data = &jz4760_adc_soc_data, },
+ { .compatible = "ingenic,jz4760b-adc", .data = &jz4760_adc_soc_data, },
+ { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
+
+static struct platform_driver ingenic_adc_driver = {
+ .driver = {
+ .name = "ingenic-adc",
+ .of_match_table = ingenic_adc_of_match,
+ },
+ .probe = ingenic_adc_probe,
+};
+module_platform_driver(ingenic_adc_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c
new file mode 100644
index 000000000..7263ad761
--- /dev/null
+++ b/drivers/iio/adc/intel_mrfld_adc.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADC driver for Basin Cove PMIC
+ *
+ * Copyright (C) 2012 Intel Corporation
+ * Author: Bin Yang <bin.yang@intel.com>
+ *
+ * Rewritten for upstream by:
+ * Vincent Pelletier <plr.vincent@gmail.com>
+ * Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/mfd/intel_soc_pmic_mrfld.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/driver.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+
+#include <asm/unaligned.h>
+
+#define BCOVE_GPADCREQ 0xDC
+#define BCOVE_GPADCREQ_BUSY BIT(0)
+#define BCOVE_GPADCREQ_IRQEN BIT(1)
+
+#define BCOVE_ADCIRQ_ALL ( \
+ BCOVE_ADCIRQ_BATTEMP | \
+ BCOVE_ADCIRQ_SYSTEMP | \
+ BCOVE_ADCIRQ_BATTID | \
+ BCOVE_ADCIRQ_VIBATT | \
+ BCOVE_ADCIRQ_CCTICK)
+
+#define BCOVE_ADC_TIMEOUT msecs_to_jiffies(1000)
+
+static const u8 mrfld_adc_requests[] = {
+ BCOVE_ADCIRQ_VIBATT,
+ BCOVE_ADCIRQ_BATTID,
+ BCOVE_ADCIRQ_VIBATT,
+ BCOVE_ADCIRQ_SYSTEMP,
+ BCOVE_ADCIRQ_BATTEMP,
+ BCOVE_ADCIRQ_BATTEMP,
+ BCOVE_ADCIRQ_SYSTEMP,
+ BCOVE_ADCIRQ_SYSTEMP,
+ BCOVE_ADCIRQ_SYSTEMP,
+};
+
+struct mrfld_adc {
+ struct regmap *regmap;
+ struct completion completion;
+ /* Lock to protect the IPC transfers */
+ struct mutex lock;
+};
+
+static irqreturn_t mrfld_adc_thread_isr(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct mrfld_adc *adc = iio_priv(indio_dev);
+
+ complete(&adc->completion);
+ return IRQ_HANDLED;
+}
+
+static int mrfld_adc_single_conv(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *result)
+{
+ struct mrfld_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->regmap;
+ unsigned int req;
+ long timeout;
+ __be16 value;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0);
+ regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0);
+
+ ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req,
+ !(req & BCOVE_GPADCREQ_BUSY),
+ 2000, 1000000);
+ if (ret)
+ goto done;
+
+ req = mrfld_adc_requests[chan->channel];
+ ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req);
+ if (ret)
+ goto done;
+
+ timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+ BCOVE_ADC_TIMEOUT);
+ if (timeout < 0) {
+ ret = timeout;
+ goto done;
+ }
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ ret = regmap_bulk_read(regmap, chan->address, &value, sizeof(value));
+ if (ret)
+ goto done;
+
+ *result = be16_to_cpu(value);
+ ret = IIO_VAL_INT;
+
+done:
+ regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff);
+ regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff);
+
+ return ret;
+}
+
+static int mrfld_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mrfld_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ ret = mrfld_adc_single_conv(indio_dev, chan, val);
+ mutex_unlock(&adc->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mrfld_adc_iio_info = {
+ .read_raw = &mrfld_adc_read_raw,
+};
+
+#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address) \
+ { \
+ .indexed = 1, \
+ .type = _type, \
+ .channel = _channel, \
+ .address = _address, \
+ .datasheet_name = _datasheet_name, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ }
+
+static const struct iio_chan_spec mrfld_adc_channels[] = {
+ BCOVE_ADC_CHANNEL(IIO_VOLTAGE, 0, "CH0", 0xE9),
+ BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB),
+ BCOVE_ADC_CHANNEL(IIO_CURRENT, 2, "CH2", 0xED),
+ BCOVE_ADC_CHANNEL(IIO_TEMP, 3, "CH3", 0xCC),
+ BCOVE_ADC_CHANNEL(IIO_TEMP, 4, "CH4", 0xC8),
+ BCOVE_ADC_CHANNEL(IIO_TEMP, 5, "CH5", 0xCA),
+ BCOVE_ADC_CHANNEL(IIO_TEMP, 6, "CH6", 0xC2),
+ BCOVE_ADC_CHANNEL(IIO_TEMP, 7, "CH7", 0xC4),
+ BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6),
+};
+
+static struct iio_map iio_maps[] = {
+ IIO_MAP("CH0", "bcove-battery", "VBATRSLT"),
+ IIO_MAP("CH1", "bcove-battery", "BATTID"),
+ IIO_MAP("CH2", "bcove-battery", "IBATRSLT"),
+ IIO_MAP("CH3", "bcove-temp", "PMICTEMP"),
+ IIO_MAP("CH4", "bcove-temp", "BATTEMP0"),
+ IIO_MAP("CH5", "bcove-temp", "BATTEMP1"),
+ 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)
+{
+ struct device *dev = &pdev->dev;
+ struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
+ struct iio_dev *indio_dev;
+ struct mrfld_adc *adc;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct mrfld_adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+
+ mutex_init(&adc->lock);
+ init_completion(&adc->completion);
+ adc->regmap = pmic->regmap;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr,
+ IRQF_ONESHOT | IRQF_SHARED, pdev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+
+ indio_dev->name = pdev->name;
+
+ indio_dev->channels = mrfld_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels);
+ indio_dev->info = &mrfld_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_map_array_register(dev, indio_dev, iio_maps);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct platform_device_id mrfld_adc_id_table[] = {
+ { .name = "mrfld_bcove_adc" },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table);
+
+static struct platform_driver mrfld_adc_driver = {
+ .driver = {
+ .name = "mrfld_bcove_adc",
+ },
+ .probe = mrfld_adc_probe,
+ .id_table = mrfld_adc_id_table,
+};
+module_platform_driver(mrfld_adc_driver);
+
+MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>");
+MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>");
+MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
+MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c
new file mode 100644
index 000000000..6d9b354bc
--- /dev/null
+++ b/drivers/iio/adc/lp8788_adc.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI LP8788 MFD - ADC driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* register address */
+#define LP8788_ADC_CONF 0x60
+#define LP8788_ADC_RAW 0x61
+#define LP8788_ADC_DONE 0x63
+
+#define ADC_CONV_START 1
+
+struct lp8788_adc {
+ struct lp8788 *lp;
+ struct iio_map *map;
+ struct mutex lock;
+};
+
+static const int lp8788_scale[LPADC_MAX] = {
+ [LPADC_VBATT_5P5] = 1343101,
+ [LPADC_VIN_CHG] = 3052503,
+ [LPADC_IBATT] = 610500,
+ [LPADC_IC_TEMP] = 61050,
+ [LPADC_VBATT_6P0] = 1465201,
+ [LPADC_VBATT_5P0] = 1221001,
+ [LPADC_ADC1] = 610500,
+ [LPADC_ADC2] = 610500,
+ [LPADC_VDD] = 1025641,
+ [LPADC_VCOIN] = 757020,
+ [LPADC_ADC3] = 610500,
+ [LPADC_ADC4] = 610500,
+};
+
+static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
+ int *val)
+{
+ unsigned int msb;
+ unsigned int lsb;
+ unsigned int result;
+ u8 data;
+ u8 rawdata[2];
+ int size = ARRAY_SIZE(rawdata);
+ int retry = 5;
+ int ret;
+
+ data = (id << 1) | ADC_CONV_START;
+ ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
+ if (ret)
+ goto err_io;
+
+ /* retry until adc conversion is done */
+ data = 0;
+ while (retry--) {
+ usleep_range(100, 200);
+
+ ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
+ if (ret)
+ goto err_io;
+
+ /* conversion done */
+ if (data)
+ break;
+ }
+
+ ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
+ if (ret)
+ goto err_io;
+
+ msb = (rawdata[0] << 4) & 0x00000ff0;
+ lsb = (rawdata[1] >> 4) & 0x0000000f;
+ result = msb | lsb;
+ *val = result;
+
+ return 0;
+
+err_io:
+ return ret;
+}
+
+static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lp8788_adc *adc = iio_priv(indio_dev);
+ enum lp8788_adc_id id = chan->channel;
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = lp8788_scale[id] / 1000000;
+ *val2 = lp8788_scale[id] % 1000000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static const struct iio_info lp8788_adc_info = {
+ .read_raw = &lp8788_adc_read_raw,
+};
+
+#define LP8788_CHAN(_id, _type) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = LPADC_##_id, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = #_id, \
+}
+
+static const struct iio_chan_spec lp8788_adc_channels[] = {
+ [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
+ [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
+ [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT),
+ [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP),
+ [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
+ [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
+ [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE),
+ [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE),
+ [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE),
+ [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
+ [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE),
+ [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE),
+};
+
+/* default maps used by iio consumer (lp8788-charger driver) */
+static struct iio_map lp8788_default_iio_maps[] = {
+ {
+ .consumer_dev_name = "lp8788-charger",
+ .consumer_channel = "lp8788_vbatt_5p0",
+ .adc_channel_label = "VBATT_5P0",
+ },
+ {
+ .consumer_dev_name = "lp8788-charger",
+ .consumer_channel = "lp8788_adc1",
+ .adc_channel_label = "ADC1",
+ },
+ { }
+};
+
+static int lp8788_iio_map_register(struct device *dev,
+ struct iio_dev *indio_dev,
+ struct lp8788_platform_data *pdata,
+ struct lp8788_adc *adc)
+{
+ struct iio_map *map;
+ int ret;
+
+ map = (!pdata || !pdata->adc_pdata) ?
+ lp8788_default_iio_maps : pdata->adc_pdata;
+
+ ret = devm_iio_map_array_register(dev, indio_dev, map);
+ if (ret) {
+ dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
+ return ret;
+ }
+
+ adc->map = map;
+ return 0;
+}
+
+static int lp8788_adc_probe(struct platform_device *pdev)
+{
+ struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+ struct iio_dev *indio_dev;
+ struct lp8788_adc *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->lp = lp;
+
+ ret = lp8788_iio_map_register(&pdev->dev, indio_dev, lp->pdata, adc);
+ if (ret)
+ return ret;
+
+ mutex_init(&adc->lock);
+
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &lp8788_adc_info;
+ indio_dev->channels = lp8788_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver lp8788_adc_driver = {
+ .probe = lp8788_adc_probe,
+ .driver = {
+ .name = LP8788_DEV_ADC,
+ },
+};
+module_platform_driver(lp8788_adc_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-adc");
diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c
new file mode 100644
index 000000000..450a243d1
--- /dev/null
+++ b/drivers/iio/adc/lpc18xx_adc.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IIO ADC driver for NXP LPC18xx ADC
+ *
+ * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
+ *
+ * UNSUPPORTED hardware features:
+ * - Hardware triggers
+ * - Burst mode
+ * - Interrupts
+ * - DMA
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/* LPC18XX ADC registers and bits */
+#define LPC18XX_ADC_CR 0x000
+#define LPC18XX_ADC_CR_CLKDIV_SHIFT 8
+#define LPC18XX_ADC_CR_PDN BIT(21)
+#define LPC18XX_ADC_CR_START_NOW (0x1 << 24)
+#define LPC18XX_ADC_GDR 0x004
+
+/* Data register bits */
+#define LPC18XX_ADC_SAMPLE_SHIFT 6
+#define LPC18XX_ADC_SAMPLE_MASK 0x3ff
+#define LPC18XX_ADC_CONV_DONE BIT(31)
+
+/* Clock should be 4.5 MHz or less */
+#define LPC18XX_ADC_CLK_TARGET 4500000
+
+struct lpc18xx_adc {
+ struct regulator *vref;
+ void __iomem *base;
+ struct device *dev;
+ struct mutex lock;
+ struct clk *clk;
+ u32 cr_reg;
+};
+
+#define LPC18XX_ADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _idx, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = {
+ LPC18XX_ADC_CHAN(0),
+ LPC18XX_ADC_CHAN(1),
+ LPC18XX_ADC_CHAN(2),
+ LPC18XX_ADC_CHAN(3),
+ LPC18XX_ADC_CHAN(4),
+ LPC18XX_ADC_CHAN(5),
+ LPC18XX_ADC_CHAN(6),
+ LPC18XX_ADC_CHAN(7),
+};
+
+static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch)
+{
+ int ret;
+ u32 reg;
+
+ reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW;
+ writel(reg, adc->base + LPC18XX_ADC_CR);
+
+ ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg,
+ reg & LPC18XX_ADC_CONV_DONE, 3, 9);
+ if (ret) {
+ dev_warn(adc->dev, "adc read timed out\n");
+ return ret;
+ }
+
+ return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK;
+}
+
+static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lpc18xx_adc *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *val = lpc18xx_adc_read_chan(adc, chan->channel);
+ mutex_unlock(&adc->lock);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(adc->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lpc18xx_adc_info = {
+ .read_raw = lpc18xx_adc_read_raw,
+};
+
+static void lpc18xx_clear_cr_reg(void *data)
+{
+ struct lpc18xx_adc *adc = data;
+
+ writel(0, adc->base + LPC18XX_ADC_CR);
+}
+
+static void lpc18xx_regulator_disable(void *vref)
+{
+ regulator_disable(vref);
+}
+
+static int lpc18xx_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct lpc18xx_adc *adc;
+ unsigned int clkdiv;
+ unsigned long rate;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->dev = &pdev->dev;
+ mutex_init(&adc->lock);
+
+ adc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc->base))
+ return PTR_ERR(adc->base);
+
+ adc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk),
+ "error getting clock\n");
+
+ adc->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(adc->vref))
+ return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref),
+ "error getting regulator\n");
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &lpc18xx_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = lpc18xx_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels);
+
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable regulator\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_regulator_disable, adc->vref);
+ if (ret)
+ return ret;
+
+ rate = clk_get_rate(adc->clk);
+ clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET);
+
+ adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) |
+ LPC18XX_ADC_CR_PDN;
+ writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR);
+
+ ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clear_cr_reg, adc);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id lpc18xx_adc_match[] = {
+ { .compatible = "nxp,lpc1850-adc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_adc_match);
+
+static struct platform_driver lpc18xx_adc_driver = {
+ .probe = lpc18xx_adc_probe,
+ .driver = {
+ .name = "lpc18xx-adc",
+ .of_match_table = lpc18xx_adc_match,
+ },
+};
+module_platform_driver(lpc18xx_adc_driver);
+
+MODULE_DESCRIPTION("LPC18xx ADC driver");
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c
new file mode 100644
index 000000000..b56ce1525
--- /dev/null
+++ b/drivers/iio/adc/lpc32xx_adc.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * lpc32xx_adc.c - Support for ADC in LPC32XX
+ *
+ * 3-channel, 10-bit ADC
+ *
+ * Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+/*
+ * LPC32XX registers definitions
+ */
+#define LPC32XXAD_SELECT(x) ((x) + 0x04)
+#define LPC32XXAD_CTRL(x) ((x) + 0x08)
+#define LPC32XXAD_VALUE(x) ((x) + 0x48)
+
+/* Bit definitions for LPC32XXAD_SELECT: */
+/* constant, always write this value! */
+#define LPC32XXAD_REFm 0x00000200
+/* constant, always write this value! */
+#define LPC32XXAD_REFp 0x00000080
+ /* multiple of this is the channel number: 0, 1, 2 */
+#define LPC32XXAD_IN 0x00000010
+/* constant, always write this value! */
+#define LPC32XXAD_INTERNAL 0x00000004
+
+/* Bit definitions for LPC32XXAD_CTRL: */
+#define LPC32XXAD_STROBE 0x00000002
+#define LPC32XXAD_PDN_CTRL 0x00000004
+
+/* Bit definitions for LPC32XXAD_VALUE: */
+#define LPC32XXAD_VALUE_MASK 0x000003FF
+
+#define LPC32XXAD_NAME "lpc32xx-adc"
+
+struct lpc32xx_adc_state {
+ void __iomem *adc_base;
+ struct clk *clk;
+ struct completion completion;
+ struct regulator *vref;
+
+ u32 value;
+};
+
+static int lpc32xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct lpc32xx_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = clk_prepare_enable(st->clk);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ /* Measurement setup */
+ __raw_writel(LPC32XXAD_INTERNAL | (chan->address) |
+ LPC32XXAD_REFp | LPC32XXAD_REFm,
+ LPC32XXAD_SELECT(st->adc_base));
+ /* Trigger conversion */
+ __raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE,
+ LPC32XXAD_CTRL(st->adc_base));
+ wait_for_completion(&st->completion); /* set by ISR */
+ clk_disable_unprepare(st->clk);
+ *val = st->value;
+ mutex_unlock(&indio_dev->mlock);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(st->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info lpc32xx_adc_iio_info = {
+ .read_raw = &lpc32xx_read_raw,
+};
+
+#define LPC32XX_ADC_CHANNEL_BASE(_index) \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .address = LPC32XXAD_IN * _index, \
+ .scan_index = _index,
+
+#define LPC32XX_ADC_CHANNEL(_index) { \
+ LPC32XX_ADC_CHANNEL_BASE(_index) \
+}
+
+#define LPC32XX_ADC_SCALE_CHANNEL(_index) { \
+ LPC32XX_ADC_CHANNEL_BASE(_index) \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+}
+
+static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
+ LPC32XX_ADC_CHANNEL(0),
+ LPC32XX_ADC_CHANNEL(1),
+ LPC32XX_ADC_CHANNEL(2),
+};
+
+static const struct iio_chan_spec lpc32xx_adc_iio_scale_channels[] = {
+ LPC32XX_ADC_SCALE_CHANNEL(0),
+ LPC32XX_ADC_SCALE_CHANNEL(1),
+ LPC32XX_ADC_SCALE_CHANNEL(2),
+};
+
+static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
+{
+ struct lpc32xx_adc_state *st = dev_id;
+
+ /* Read value and clear irq */
+ st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) &
+ LPC32XXAD_VALUE_MASK;
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int lpc32xx_adc_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_adc_state *st = NULL;
+ struct resource *res;
+ int retval = -ENODEV;
+ struct iio_dev *iodev = NULL;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get platform I/O memory\n");
+ return -ENXIO;
+ }
+
+ iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+ if (!iodev)
+ return -ENOMEM;
+
+ st = iio_priv(iodev);
+
+ st->adc_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!st->adc_base) {
+ dev_err(&pdev->dev, "failed mapping memory\n");
+ return -EBUSY;
+ }
+
+ st->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(st->clk)) {
+ dev_err(&pdev->dev, "failed getting clock\n");
+ return PTR_ERR(st->clk);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -ENXIO;
+
+ retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
+ LPC32XXAD_NAME, st);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "failed requesting interrupt\n");
+ return retval;
+ }
+
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref)) {
+ iodev->channels = lpc32xx_adc_iio_channels;
+ dev_info(&pdev->dev,
+ "Missing vref regulator: No scaling available\n");
+ } else {
+ iodev->channels = lpc32xx_adc_iio_scale_channels;
+ }
+
+ platform_set_drvdata(pdev, iodev);
+
+ init_completion(&st->completion);
+
+ iodev->name = LPC32XXAD_NAME;
+ iodev->info = &lpc32xx_adc_iio_info;
+ iodev->modes = INDIO_DIRECT_MODE;
+ iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
+
+ retval = devm_iio_device_register(&pdev->dev, iodev);
+ if (retval)
+ return retval;
+
+ dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq);
+
+ return 0;
+}
+
+static const struct of_device_id lpc32xx_adc_match[] = {
+ { .compatible = "nxp,lpc3220-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_adc_match);
+
+static struct platform_driver lpc32xx_adc_driver = {
+ .probe = lpc32xx_adc_probe,
+ .driver = {
+ .name = LPC32XXAD_NAME,
+ .of_match_table = lpc32xx_adc_match,
+ },
+};
+
+module_platform_driver(lpc32xx_adc_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("LPC32XX ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c
new file mode 100644
index 000000000..0e0fe881a
--- /dev/null
+++ b/drivers/iio/adc/ltc2471.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Linear Technology LTC2471 and LTC2473 voltage monitors
+ * The LTC2473 is identical to the 2471, but reports a differential signal.
+ *
+ * Copyright (C) 2017 Topic Embedded Products
+ * Author: Mike Looijmans <mike.looijmans@topic.nl>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+enum ltc2471_chips {
+ ltc2471,
+ ltc2473,
+};
+
+struct ltc2471_data {
+ struct i2c_client *client;
+};
+
+/* Reference voltage is 1.25V */
+#define LTC2471_VREF 1250
+
+/* Read two bytes from the I2C bus to obtain the ADC result */
+static int ltc2471_get_value(struct i2c_client *client)
+{
+ int ret;
+ __be16 buf;
+
+ ret = i2c_master_recv(client, (char *)&buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(buf))
+ return -EIO;
+
+ /* MSB first */
+ return be16_to_cpu(buf);
+}
+
+static int ltc2471_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ltc2471_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ltc2471_get_value(data->client);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->differential)
+ /* Output ranges from -VREF to +VREF */
+ *val = 2 * LTC2471_VREF;
+ else
+ /* Output ranges from 0 to VREF */
+ *val = LTC2471_VREF;
+ *val2 = 16; /* 16 data bits */
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ /* Only differential chip has this property */
+ *val = -LTC2471_VREF;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec ltc2471_channel[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static const struct iio_chan_spec ltc2473_channel[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .differential = 1,
+ },
+};
+
+static const struct iio_info ltc2471_info = {
+ .read_raw = ltc2471_read_raw,
+};
+
+static int ltc2471_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ltc2471_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+
+ indio_dev->name = id->name;
+ indio_dev->info = &ltc2471_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ if (id->driver_data == ltc2473)
+ indio_dev->channels = ltc2473_channel;
+ else
+ indio_dev->channels = ltc2471_channel;
+ indio_dev->num_channels = 1;
+
+ /* Trigger once to start conversion and check if chip is there */
+ ret = ltc2471_get_value(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "Cannot read from device.\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ltc2471_i2c_id[] = {
+ { "ltc2471", ltc2471 },
+ { "ltc2473", ltc2473 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id);
+
+static struct i2c_driver ltc2471_i2c_driver = {
+ .driver = {
+ .name = "ltc2471",
+ },
+ .probe = ltc2471_i2c_probe,
+ .id_table = ltc2471_i2c_id,
+};
+
+module_i2c_driver(ltc2471_i2c_driver);
+
+MODULE_DESCRIPTION("LTC2471/LTC2473 ADC driver");
+MODULE_AUTHOR("Topic Embedded Products");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c
new file mode 100644
index 000000000..37c762f82
--- /dev/null
+++ b/drivers/iio/adc/ltc2485.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ltc2485.c - Driver for Linear Technology LTC2485 ADC
+ *
+ * Copyright (C) 2016 Alison Schofield <amsfield22@gmail.com>
+ *
+ * Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Power-on configuration: rejects both 50/60Hz, operates at 1x speed */
+#define LTC2485_CONFIG_DEFAULT 0
+
+struct ltc2485_data {
+ struct i2c_client *client;
+ ktime_t time_prev; /* last conversion */
+};
+
+static void ltc2485_wait_conv(struct ltc2485_data *data)
+{
+ const unsigned int conv_time = 147; /* conversion time ms */
+ unsigned int time_elapsed;
+
+ /* delay if conversion time not passed since last read or write */
+ time_elapsed = ktime_ms_delta(ktime_get(), data->time_prev);
+
+ if (time_elapsed < conv_time)
+ msleep(conv_time - time_elapsed);
+}
+
+static int ltc2485_read(struct ltc2485_data *data, int *val)
+{
+ struct i2c_client *client = data->client;
+ __be32 buf = 0;
+ int ret;
+
+ ltc2485_wait_conv(data);
+
+ ret = i2c_master_recv(client, (char *)&buf, 4);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c_master_recv failed\n");
+ return ret;
+ }
+ data->time_prev = ktime_get();
+ *val = sign_extend32(be32_to_cpu(buf) >> 6, 24);
+
+ return ret;
+}
+
+static int ltc2485_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2485_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (mask == IIO_CHAN_INFO_RAW) {
+ ret = ltc2485_read(data, val);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ } else if (mask == IIO_CHAN_INFO_SCALE) {
+ *val = 5000; /* on board vref millivolts */
+ *val2 = 25; /* 25 (24 + sign) data bits */
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ } else {
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec ltc2485_channel[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)
+ },
+};
+
+static const struct iio_info ltc2485_info = {
+ .read_raw = ltc2485_read_raw,
+};
+
+static int ltc2485_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ltc2485_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ indio_dev->name = id->name;
+ indio_dev->info = &ltc2485_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ltc2485_channel;
+ indio_dev->num_channels = ARRAY_SIZE(ltc2485_channel);
+
+ ret = i2c_smbus_write_byte(data->client, LTC2485_CONFIG_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ data->time_prev = ktime_get();
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ltc2485_id[] = {
+ { "ltc2485", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc2485_id);
+
+static struct i2c_driver ltc2485_driver = {
+ .driver = {
+ .name = "ltc2485",
+ },
+ .probe = ltc2485_probe,
+ .id_table = ltc2485_id,
+};
+module_i2c_driver(ltc2485_driver);
+
+MODULE_AUTHOR("Alison Schofield <amsfield22@gmail.com>");
+MODULE_DESCRIPTION("Linear Technology LTC2485 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c
new file mode 100644
index 000000000..2593fa432
--- /dev/null
+++ b/drivers/iio/adc/ltc2496.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ltc2496.c - Driver for Analog Devices/Linear Technology LTC2496 ADC
+ *
+ * Based on ltc2497.c which has
+ * Copyright (C) 2017 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2496fc.pdf
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#include "ltc2497.h"
+
+struct ltc2496_driverdata {
+ /* this must be the first member */
+ struct ltc2497core_driverdata common_ddata;
+ struct spi_device *spi;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ unsigned char rxbuf[3] __aligned(IIO_DMA_MINALIGN);
+ unsigned char txbuf[3];
+};
+
+static int ltc2496_result_and_measure(struct ltc2497core_driverdata *ddata,
+ u8 address, int *val)
+{
+ struct ltc2496_driverdata *st =
+ container_of(ddata, struct ltc2496_driverdata, common_ddata);
+ struct spi_transfer t = {
+ .tx_buf = st->txbuf,
+ .rx_buf = st->rxbuf,
+ .len = sizeof(st->txbuf),
+ };
+ int ret;
+
+ st->txbuf[0] = LTC2497_ENABLE | address;
+
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret < 0) {
+ dev_err(&st->spi->dev, "spi_sync_transfer failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ if (val)
+ *val = ((st->rxbuf[0] & 0x3f) << 12 |
+ st->rxbuf[1] << 4 | st->rxbuf[2] >> 4) -
+ (1 << 17);
+
+ return 0;
+}
+
+static int ltc2496_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ltc2496_driverdata *st;
+ struct device *dev = &spi->dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+ st->common_ddata.result_and_measure = ltc2496_result_and_measure;
+ st->common_ddata.chip_info = device_get_match_data(dev);
+
+ return ltc2497core_probe(dev, indio_dev);
+}
+
+static void ltc2496_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ ltc2497core_remove(indio_dev);
+}
+
+static const struct ltc2497_chip_info ltc2496_info = {
+ .resolution = 16,
+ .name = NULL,
+};
+
+static const struct of_device_id ltc2496_of_match[] = {
+ { .compatible = "lltc,ltc2496", .data = &ltc2496_info, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltc2496_of_match);
+
+static struct spi_driver ltc2496_driver = {
+ .driver = {
+ .name = "ltc2496",
+ .of_match_table = ltc2496_of_match,
+ },
+ .probe = ltc2496_probe,
+ .remove = ltc2496_remove,
+};
+module_spi_driver(ltc2496_driver);
+
+MODULE_AUTHOR("Uwe Kleine-König <u.kleine-könig@pengutronix.de>");
+MODULE_DESCRIPTION("Linear Technology LTC2496 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
new file mode 100644
index 000000000..f52d37af4
--- /dev/null
+++ b/drivers/iio/adc/ltc2497-core.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ltc2497-core.c - Common code for Analog Devices/Linear Technology
+ * LTC2496 and LTC2497 ADCs
+ *
+ * Copyright (C) 2017 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include "ltc2497.h"
+
+#define LTC2497_SGL BIT(4)
+#define LTC2497_DIFF 0
+#define LTC2497_SIGN BIT(3)
+
+static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata)
+{
+ s64 time_elapsed;
+
+ time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev);
+
+ if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
+ /* delay if conversion time not passed
+ * since last read or write
+ */
+ if (msleep_interruptible(
+ LTC2497_CONVERSION_TIME_MS - time_elapsed))
+ return -ERESTARTSYS;
+
+ return 0;
+ }
+
+ if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
+ /* We're in automatic mode -
+ * so the last reading is still not outdated
+ */
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val)
+{
+ int ret;
+
+ ret = ltc2497core_wait_conv(ddata);
+ if (ret < 0)
+ return ret;
+
+ if (ret || ddata->addr_prev != address) {
+ ret = ddata->result_and_measure(ddata, address, NULL);
+ if (ret < 0)
+ return ret;
+ ddata->addr_prev = address;
+
+ if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
+ return -ERESTARTSYS;
+ }
+
+ ret = ddata->result_and_measure(ddata, address, val);
+ if (ret < 0)
+ return ret;
+
+ ddata->time_prev = ktime_get();
+
+ return ret;
+}
+
+static int ltc2497core_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = ltc2497core_read(ddata, chan->address, val);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(ddata->ref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = ddata->chip_info->resolution + 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+#define LTC2497_CHAN(_chan, _addr, _ds_name) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan), \
+ .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = (_ds_name), \
+}
+
+#define LTC2497_CHAN_DIFF(_chan, _addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \
+ .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\
+ .address = (_addr | _chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .differential = 1, \
+}
+
+static const struct iio_chan_spec ltc2497core_channel[] = {
+ LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
+ LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
+ LTC2497_CHAN(2, LTC2497_SGL, "CH2"),
+ LTC2497_CHAN(3, LTC2497_SGL, "CH3"),
+ LTC2497_CHAN(4, LTC2497_SGL, "CH4"),
+ LTC2497_CHAN(5, LTC2497_SGL, "CH5"),
+ LTC2497_CHAN(6, LTC2497_SGL, "CH6"),
+ LTC2497_CHAN(7, LTC2497_SGL, "CH7"),
+ LTC2497_CHAN(8, LTC2497_SGL, "CH8"),
+ LTC2497_CHAN(9, LTC2497_SGL, "CH9"),
+ LTC2497_CHAN(10, LTC2497_SGL, "CH10"),
+ LTC2497_CHAN(11, LTC2497_SGL, "CH11"),
+ LTC2497_CHAN(12, LTC2497_SGL, "CH12"),
+ LTC2497_CHAN(13, LTC2497_SGL, "CH13"),
+ LTC2497_CHAN(14, LTC2497_SGL, "CH14"),
+ LTC2497_CHAN(15, LTC2497_SGL, "CH15"),
+ LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
+};
+
+static const struct iio_info ltc2497core_info = {
+ .read_raw = ltc2497core_read_raw,
+};
+
+int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
+{
+ struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * Keep using dev_name() for the iio_dev's name on some of the parts,
+ * since updating it would result in a ABI breakage.
+ */
+ if (ddata->chip_info->name)
+ indio_dev->name = ddata->chip_info->name;
+ else
+ indio_dev->name = dev_name(dev);
+
+ indio_dev->info = &ltc2497core_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ltc2497core_channel;
+ indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
+
+ ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL);
+ if (ret < 0)
+ return ret;
+
+ ddata->ref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(ddata->ref))
+ return dev_err_probe(dev, PTR_ERR(ddata->ref),
+ "Failed to get vref regulator\n");
+
+ ret = regulator_enable(ddata->ref);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vref regulator: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ if (dev->platform_data) {
+ struct iio_map *plat_data;
+
+ plat_data = (struct iio_map *)dev->platform_data;
+
+ ret = iio_map_array_register(indio_dev, plat_data);
+ if (ret) {
+ dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
+ goto err_regulator_disable;
+ }
+ }
+
+ ddata->addr_prev = LTC2497_CONFIG_DEFAULT;
+ ddata->time_prev = ktime_get();
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err_array_unregister;
+
+ return 0;
+
+err_array_unregister:
+ iio_map_array_unregister(indio_dev);
+
+err_regulator_disable:
+ regulator_disable(ddata->ref);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(ltc2497core_probe, LTC2497);
+
+void ltc2497core_remove(struct iio_dev *indio_dev)
+{
+ struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_map_array_unregister(indio_dev);
+
+ regulator_disable(ddata->ref);
+}
+EXPORT_SYMBOL_NS(ltc2497core_remove, LTC2497);
+
+MODULE_DESCRIPTION("common code for LTC2496/LTC2497 drivers");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
new file mode 100644
index 000000000..e1e14f5d2
--- /dev/null
+++ b/drivers/iio/adc/ltc2497.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC
+ *
+ * Copyright (C) 2017 Analog Devices Inc.
+ *
+ * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#include <asm/unaligned.h>
+
+#include "ltc2497.h"
+
+enum ltc2497_chip_type {
+ TYPE_LTC2497,
+ TYPE_LTC2499,
+};
+
+struct ltc2497_driverdata {
+ /* this must be the first member */
+ struct ltc2497core_driverdata common_ddata;
+ struct i2c_client *client;
+ u32 recv_size;
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[3];
+ } data __aligned(IIO_DMA_MINALIGN);
+};
+
+static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
+ u8 address, int *val)
+{
+ struct ltc2497_driverdata *st =
+ container_of(ddata, struct ltc2497_driverdata, common_ddata);
+ int ret;
+
+ if (val) {
+ if (st->recv_size == 3)
+ ret = i2c_master_recv(st->client, (char *)&st->data.d8,
+ st->recv_size);
+ else
+ ret = i2c_master_recv(st->client, (char *)&st->data.d32,
+ st->recv_size);
+ if (ret < 0) {
+ dev_err(&st->client->dev, "i2c_master_recv failed\n");
+ return ret;
+ }
+
+ /*
+ * The data format is 16/24 bit 2s complement, but with an upper sign bit on the
+ * resolution + 1 position, which is set for positive values only. Given this
+ * bit's value, subtracting BIT(resolution + 1) from the ADC's result is
+ * equivalent to a sign extension.
+ */
+ if (st->recv_size == 3) {
+ *val = (get_unaligned_be24(st->data.d8) >> 6)
+ - BIT(ddata->chip_info->resolution + 1);
+ } else {
+ *val = (be32_to_cpu(st->data.d32) >> 6)
+ - BIT(ddata->chip_info->resolution + 1);
+ }
+
+ /*
+ * The part started a new conversion at the end of the above i2c
+ * transfer, so if the address didn't change since the last call
+ * everything is fine and we can return early.
+ * If not (which should only happen when some sort of bulk
+ * conversion is implemented) we have to program the new
+ * address. Note that this probably fails as the conversion that
+ * was triggered above is like not complete yet and the two
+ * operations have to be done in a single transfer.
+ */
+ if (ddata->addr_prev == address)
+ return 0;
+ }
+
+ ret = i2c_smbus_write_byte(st->client,
+ LTC2497_ENABLE | address);
+ if (ret)
+ dev_err(&st->client->dev, "i2c transfer failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+}
+
+static int ltc2497_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct ltc2497_chip_info *chip_info;
+ struct iio_dev *indio_dev;
+ struct ltc2497_driverdata *st;
+ struct device *dev = &client->dev;
+ u32 resolution;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ st->client = client;
+ st->common_ddata.result_and_measure = ltc2497_result_and_measure;
+
+ chip_info = device_get_match_data(dev);
+ if (!chip_info)
+ chip_info = (const struct ltc2497_chip_info *)id->driver_data;
+ st->common_ddata.chip_info = chip_info;
+
+ resolution = chip_info->resolution;
+ st->recv_size = BITS_TO_BYTES(resolution) + 1;
+
+ return ltc2497core_probe(dev, indio_dev);
+}
+
+static void ltc2497_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ ltc2497core_remove(indio_dev);
+}
+
+static const struct ltc2497_chip_info ltc2497_info[] = {
+ [TYPE_LTC2497] = {
+ .resolution = 16,
+ .name = NULL,
+ },
+ [TYPE_LTC2499] = {
+ .resolution = 24,
+ .name = "ltc2499",
+ },
+};
+
+static const struct i2c_device_id ltc2497_id[] = {
+ { "ltc2497", (kernel_ulong_t)&ltc2497_info[TYPE_LTC2497] },
+ { "ltc2499", (kernel_ulong_t)&ltc2497_info[TYPE_LTC2499] },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc2497_id);
+
+static const struct of_device_id ltc2497_of_match[] = {
+ { .compatible = "lltc,ltc2497", .data = &ltc2497_info[TYPE_LTC2497] },
+ { .compatible = "lltc,ltc2499", .data = &ltc2497_info[TYPE_LTC2499] },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltc2497_of_match);
+
+static struct i2c_driver ltc2497_driver = {
+ .driver = {
+ .name = "ltc2497",
+ .of_match_table = ltc2497_of_match,
+ },
+ .probe = ltc2497_probe,
+ .remove = ltc2497_remove,
+ .id_table = ltc2497_id,
+};
+module_i2c_driver(ltc2497_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h
new file mode 100644
index 000000000..e023de0d8
--- /dev/null
+++ b/drivers/iio/adc/ltc2497.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#define LTC2497_ENABLE 0xA0
+#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
+#define LTC2497_CONVERSION_TIME_MS 150ULL
+
+struct ltc2497_chip_info {
+ u32 resolution;
+ const char *name;
+};
+
+struct ltc2497core_driverdata {
+ struct regulator *ref;
+ ktime_t time_prev;
+ const struct ltc2497_chip_info *chip_info;
+ u8 addr_prev;
+ int (*result_and_measure)(struct ltc2497core_driverdata *ddata,
+ u8 address, int *val);
+};
+
+int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev);
+void ltc2497core_remove(struct iio_dev *indio_dev);
+
+MODULE_IMPORT_NS(LTC2497);
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
new file mode 100644
index 000000000..136fcf753
--- /dev/null
+++ b/drivers/iio/adc/max1027.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0-only
+ /*
+ * iio/adc/max1027.c
+ * Copyright (C) 2014 Philippe Reynes
+ *
+ * based on linux/drivers/iio/ad7923.c
+ * Copyright 2011 Analog Devices Inc (from AD7923 Driver)
+ * Copyright 2012 CS Systemes d'Information
+ *
+ * max1027.c
+ *
+ * Partial support for max1027 and similar chips.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define MAX1027_CONV_REG BIT(7)
+#define MAX1027_SETUP_REG BIT(6)
+#define MAX1027_AVG_REG BIT(5)
+#define MAX1027_RST_REG BIT(4)
+
+/* conversion register */
+#define MAX1027_TEMP BIT(0)
+#define MAX1027_SCAN_0_N (0x00 << 1)
+#define MAX1027_SCAN_N_M (0x01 << 1)
+#define MAX1027_SCAN_N (0x02 << 1)
+#define MAX1027_NOSCAN (0x03 << 1)
+#define MAX1027_CHAN(n) ((n) << 3)
+
+/* setup register */
+#define MAX1027_UNIPOLAR 0x02
+#define MAX1027_BIPOLAR 0x03
+#define MAX1027_REF_MODE0 (0x00 << 2)
+#define MAX1027_REF_MODE1 (0x01 << 2)
+#define MAX1027_REF_MODE2 (0x02 << 2)
+#define MAX1027_REF_MODE3 (0x03 << 2)
+#define MAX1027_CKS_MODE0 (0x00 << 4)
+#define MAX1027_CKS_MODE1 (0x01 << 4)
+#define MAX1027_CKS_MODE2 (0x02 << 4)
+#define MAX1027_CKS_MODE3 (0x03 << 4)
+
+/* averaging register */
+#define MAX1027_NSCAN_4 0x00
+#define MAX1027_NSCAN_8 0x01
+#define MAX1027_NSCAN_12 0x02
+#define MAX1027_NSCAN_16 0x03
+#define MAX1027_NAVG_4 (0x00 << 2)
+#define MAX1027_NAVG_8 (0x01 << 2)
+#define MAX1027_NAVG_16 (0x02 << 2)
+#define MAX1027_NAVG_32 (0x03 << 2)
+#define MAX1027_AVG_EN BIT(4)
+
+/* Device can achieve 300ksps so we assume a 3.33us conversion delay */
+#define MAX1027_CONVERSION_UDELAY 4
+
+enum max1027_id {
+ max1027,
+ max1029,
+ max1031,
+ max1227,
+ max1229,
+ max1231,
+};
+
+static const struct spi_device_id max1027_id[] = {
+ {"max1027", max1027},
+ {"max1029", max1029},
+ {"max1031", max1031},
+ {"max1227", max1227},
+ {"max1229", max1229},
+ {"max1231", max1231},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, max1027_id);
+
+static const struct of_device_id max1027_adc_dt_ids[] = {
+ { .compatible = "maxim,max1027" },
+ { .compatible = "maxim,max1029" },
+ { .compatible = "maxim,max1031" },
+ { .compatible = "maxim,max1227" },
+ { .compatible = "maxim,max1229" },
+ { .compatible = "maxim,max1231" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
+
+#define MAX1027_V_CHAN(index, depth) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = index + 1, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = depth, \
+ .storagebits = 16, \
+ .shift = (depth == 10) ? 2 : 0, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define MAX1027_T_CHAN \
+ { \
+ .type = IIO_TEMP, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define MAX1X27_CHANNELS(depth) \
+ MAX1027_T_CHAN, \
+ MAX1027_V_CHAN(0, depth), \
+ MAX1027_V_CHAN(1, depth), \
+ MAX1027_V_CHAN(2, depth), \
+ MAX1027_V_CHAN(3, depth), \
+ MAX1027_V_CHAN(4, depth), \
+ MAX1027_V_CHAN(5, depth), \
+ MAX1027_V_CHAN(6, depth), \
+ MAX1027_V_CHAN(7, depth)
+
+#define MAX1X29_CHANNELS(depth) \
+ MAX1X27_CHANNELS(depth), \
+ MAX1027_V_CHAN(8, depth), \
+ MAX1027_V_CHAN(9, depth), \
+ MAX1027_V_CHAN(10, depth), \
+ MAX1027_V_CHAN(11, depth)
+
+#define MAX1X31_CHANNELS(depth) \
+ MAX1X29_CHANNELS(depth), \
+ MAX1027_V_CHAN(12, depth), \
+ MAX1027_V_CHAN(13, depth), \
+ MAX1027_V_CHAN(14, depth), \
+ MAX1027_V_CHAN(15, depth)
+
+static const struct iio_chan_spec max1027_channels[] = {
+ MAX1X27_CHANNELS(10),
+};
+
+static const struct iio_chan_spec max1029_channels[] = {
+ MAX1X29_CHANNELS(10),
+};
+
+static const struct iio_chan_spec max1031_channels[] = {
+ MAX1X31_CHANNELS(10),
+};
+
+static const struct iio_chan_spec max1227_channels[] = {
+ MAX1X27_CHANNELS(12),
+};
+
+static const struct iio_chan_spec max1229_channels[] = {
+ MAX1X29_CHANNELS(12),
+};
+
+static const struct iio_chan_spec max1231_channels[] = {
+ MAX1X31_CHANNELS(12),
+};
+
+/*
+ * These devices are able to scan from 0 to N, N being the highest voltage
+ * channel requested by the user. The temperature can be included or not,
+ * but cannot be retrieved alone. Based on the below
+ * ->available_scan_masks, the core will select the most appropriate
+ * ->active_scan_mask and the "minimum" number of channels will be
+ * scanned and pushed to the buffers.
+ *
+ * For example, if the user wants channels 1, 4 and 5, all channels from
+ * 0 to 5 will be scanned and pushed to the IIO buffers. The core will then
+ * filter out the unneeded samples based on the ->active_scan_mask that has
+ * been selected and only channels 1, 4 and 5 will be available to the user
+ * in the shared buffer.
+ */
+#define MAX1X27_SCAN_MASK_TEMP BIT(0)
+
+#define MAX1X27_SCAN_MASKS(temp) \
+ GENMASK(1, 1 - (temp)), GENMASK(2, 1 - (temp)), \
+ GENMASK(3, 1 - (temp)), GENMASK(4, 1 - (temp)), \
+ GENMASK(5, 1 - (temp)), GENMASK(6, 1 - (temp)), \
+ GENMASK(7, 1 - (temp)), GENMASK(8, 1 - (temp))
+
+#define MAX1X29_SCAN_MASKS(temp) \
+ MAX1X27_SCAN_MASKS(temp), \
+ GENMASK(9, 1 - (temp)), GENMASK(10, 1 - (temp)), \
+ GENMASK(11, 1 - (temp)), GENMASK(12, 1 - (temp))
+
+#define MAX1X31_SCAN_MASKS(temp) \
+ MAX1X29_SCAN_MASKS(temp), \
+ GENMASK(13, 1 - (temp)), GENMASK(14, 1 - (temp)), \
+ GENMASK(15, 1 - (temp)), GENMASK(16, 1 - (temp))
+
+static const unsigned long max1027_available_scan_masks[] = {
+ MAX1X27_SCAN_MASKS(0),
+ MAX1X27_SCAN_MASKS(1),
+ 0x00000000,
+};
+
+static const unsigned long max1029_available_scan_masks[] = {
+ MAX1X29_SCAN_MASKS(0),
+ MAX1X29_SCAN_MASKS(1),
+ 0x00000000,
+};
+
+static const unsigned long max1031_available_scan_masks[] = {
+ MAX1X31_SCAN_MASKS(0),
+ MAX1X31_SCAN_MASKS(1),
+ 0x00000000,
+};
+
+struct max1027_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const unsigned long *available_scan_masks;
+};
+
+static const struct max1027_chip_info max1027_chip_info_tbl[] = {
+ [max1027] = {
+ .channels = max1027_channels,
+ .num_channels = ARRAY_SIZE(max1027_channels),
+ .available_scan_masks = max1027_available_scan_masks,
+ },
+ [max1029] = {
+ .channels = max1029_channels,
+ .num_channels = ARRAY_SIZE(max1029_channels),
+ .available_scan_masks = max1029_available_scan_masks,
+ },
+ [max1031] = {
+ .channels = max1031_channels,
+ .num_channels = ARRAY_SIZE(max1031_channels),
+ .available_scan_masks = max1031_available_scan_masks,
+ },
+ [max1227] = {
+ .channels = max1227_channels,
+ .num_channels = ARRAY_SIZE(max1227_channels),
+ .available_scan_masks = max1027_available_scan_masks,
+ },
+ [max1229] = {
+ .channels = max1229_channels,
+ .num_channels = ARRAY_SIZE(max1229_channels),
+ .available_scan_masks = max1029_available_scan_masks,
+ },
+ [max1231] = {
+ .channels = max1231_channels,
+ .num_channels = ARRAY_SIZE(max1231_channels),
+ .available_scan_masks = max1031_available_scan_masks,
+ },
+};
+
+struct max1027_state {
+ const struct max1027_chip_info *info;
+ struct spi_device *spi;
+ struct iio_trigger *trig;
+ __be16 *buffer;
+ struct mutex lock;
+ struct completion complete;
+
+ u8 reg __aligned(IIO_DMA_MINALIGN);
+};
+
+static int max1027_wait_eoc(struct iio_dev *indio_dev)
+{
+ struct max1027_state *st = iio_priv(indio_dev);
+ unsigned int conversion_time = MAX1027_CONVERSION_UDELAY;
+ int ret;
+
+ if (st->spi->irq) {
+ ret = wait_for_completion_timeout(&st->complete,
+ msecs_to_jiffies(1000));
+ reinit_completion(&st->complete);
+ if (!ret)
+ return -ETIMEDOUT;
+ } else {
+ if (indio_dev->active_scan_mask)
+ conversion_time *= hweight32(*indio_dev->active_scan_mask);
+
+ usleep_range(conversion_time, conversion_time * 2);
+ }
+
+ return 0;
+}
+
+/* Scan from chan 0 to the highest requested channel. Include temperature on demand. */
+static int max1027_configure_chans_and_start(struct iio_dev *indio_dev)
+{
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N;
+ st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2);
+ if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP)
+ st->reg |= MAX1027_TEMP;
+
+ return spi_write(st->spi, &st->reg, 1);
+}
+
+static int max1027_enable_trigger(struct iio_dev *indio_dev, bool enable)
+{
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2;
+
+ /*
+ * Start acquisition on:
+ * MODE0: external hardware trigger wired to the cnvst input pin
+ * MODE2: conversion register write
+ */
+ if (enable)
+ st->reg |= MAX1027_CKS_MODE0;
+ else
+ st->reg |= MAX1027_CKS_MODE2;
+
+ return spi_write(st->spi, &st->reg, 1);
+}
+
+static int max1027_read_single_value(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ 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;
+ if (chan->type == IIO_TEMP)
+ st->reg |= MAX1027_TEMP;
+ ret = spi_write(st->spi, &st->reg, 1);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "Failed to configure conversion register\n");
+ goto release;
+ }
+
+ /*
+ * For an unknown reason, when we use the mode "10" (write
+ * conversion register), the interrupt doesn't occur every time.
+ * So we just wait the maximum conversion time and deliver the value.
+ */
+ ret = max1027_wait_eoc(indio_dev);
+ if (ret)
+ goto release;
+
+ /* 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;
+
+ *val = be16_to_cpu(st->buffer[0]);
+
+ return IIO_VAL_INT;
+}
+
+static int max1027_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret = 0;
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max1027_read_single_value(indio_dev, chan, val);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = 1;
+ *val2 = 8;
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ case IIO_VOLTAGE:
+ *val = 2500;
+ *val2 = chan->scan_type.realbits;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct max1027_state *st = iio_priv(indio_dev);
+ u8 *val = (u8 *)st->buffer;
+
+ if (readval) {
+ int ret = spi_read(st->spi, val, 2);
+ *readval = be16_to_cpu(st->buffer[0]);
+ return ret;
+ }
+
+ *val = (u8)writeval;
+ return spi_write(st->spi, val, 1);
+}
+
+static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ /*
+ * In order to disable the convst trigger, start acquisition on
+ * conversion register write, which basically disables triggering
+ * conversions upon cnvst changes and thus has the effect of disabling
+ * the external hardware trigger.
+ */
+ ret = max1027_enable_trigger(indio_dev, state);
+ if (ret)
+ return ret;
+
+ if (state) {
+ ret = max1027_configure_chans_and_start(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max1027_read_scan(struct iio_dev *indio_dev)
+{
+ struct max1027_state *st = iio_priv(indio_dev);
+ unsigned int scanned_chans;
+ int ret;
+
+ scanned_chans = fls(*indio_dev->active_scan_mask) - 1;
+ if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP)
+ scanned_chans++;
+
+ /* fill buffer with all channel */
+ ret = spi_read(st->spi, st->buffer, scanned_chans * 2);
+ if (ret < 0)
+ return ret;
+
+ iio_push_to_buffers(indio_dev, st->buffer);
+
+ return 0;
+}
+
+static irqreturn_t max1027_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct max1027_state *st = iio_priv(indio_dev);
+
+ /*
+ * If buffers are disabled (raw read) or when using external triggers,
+ * we just need to unlock the waiters which will then handle the data.
+ *
+ * When using the internal trigger, we must hand-off the choice of the
+ * handler to the core which will then lookup through the interrupt tree
+ * for the right handler registered with iio_triggered_buffer_setup()
+ * to execute, as this trigger might very well be used in conjunction
+ * with another device. The core will then call the relevant handler to
+ * perform the data processing step.
+ */
+ if (!iio_buffer_enabled(indio_dev))
+ complete(&st->complete);
+ else
+ iio_trigger_poll(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t max1027_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ int ret;
+
+ if (!iio_trigger_using_own(indio_dev)) {
+ ret = max1027_configure_chans_and_start(indio_dev);
+ if (ret)
+ goto out;
+
+ /* This is a threaded handler, it is fine to wait for an IRQ */
+ ret = max1027_wait_eoc(indio_dev);
+ if (ret)
+ goto out;
+ }
+
+ ret = max1027_read_scan(indio_dev);
+out:
+ if (ret)
+ dev_err(&indio_dev->dev,
+ "Cannot read scanned values (%d)\n", ret);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_trigger_ops max1027_trigger_ops = {
+ .validate_device = &iio_trigger_validate_own_device,
+ .set_trigger_state = &max1027_set_cnvst_trigger_state,
+};
+
+static const struct iio_info max1027_info = {
+ .read_raw = &max1027_read_raw,
+ .debugfs_reg_access = &max1027_debugfs_reg_access,
+};
+
+static int max1027_probe(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct max1027_state *st;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev) {
+ pr_err("Can't allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+ st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ mutex_init(&st->lock);
+ init_completion(&st->complete);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &max1027_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->info->channels;
+ indio_dev->num_channels = st->info->num_channels;
+ indio_dev->available_scan_masks = st->info->available_scan_masks;
+
+ st->buffer = devm_kmalloc_array(&indio_dev->dev,
+ indio_dev->num_channels, 2,
+ GFP_KERNEL);
+ if (!st->buffer)
+ return -ENOMEM;
+
+ /* Enable triggered buffers */
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &max1027_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to setup buffer\n");
+ return ret;
+ }
+
+ /* If there is an EOC interrupt, register the cnvst hardware trigger */
+ if (spi->irq) {
+ st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger",
+ indio_dev->name);
+ if (!st->trig) {
+ ret = -ENOMEM;
+ dev_err(&indio_dev->dev,
+ "Failed to allocate iio trigger\n");
+ return ret;
+ }
+
+ st->trig->ops = &max1027_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(&indio_dev->dev,
+ st->trig);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "Failed to register iio trigger\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(&spi->dev, spi->irq, max1027_handler,
+ IRQF_TRIGGER_FALLING,
+ spi->dev.driver->name, indio_dev);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
+ return ret;
+ }
+ }
+
+ /* Internal reset */
+ st->reg = MAX1027_RST_REG;
+ ret = spi_write(st->spi, &st->reg, 1);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to reset the ADC\n");
+ return ret;
+ }
+
+ /* Disable averaging */
+ st->reg = MAX1027_AVG_REG;
+ ret = spi_write(st->spi, &st->reg, 1);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed to configure averaging register\n");
+ return ret;
+ }
+
+ /* Assume conversion on register write for now */
+ ret = max1027_enable_trigger(indio_dev, false);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static struct spi_driver max1027_driver = {
+ .driver = {
+ .name = "max1027",
+ .of_match_table = max1027_adc_dt_ids,
+ },
+ .probe = max1027_probe,
+ .id_table = max1027_id,
+};
+module_spi_driver(max1027_driver);
+
+MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
+MODULE_DESCRIPTION("MAX1X27/MAX1X29/MAX1X31 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
new file mode 100644
index 000000000..49e38dca8
--- /dev/null
+++ b/drivers/iio/adc/max11100.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * iio/adc/max11100.c
+ * Maxim max11100 ADC Driver with IIO interface
+ *
+ * Copyright (C) 2016-17 Renesas Electronics Corporation
+ * Copyright (C) 2016-17 Jacopo Mondi
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+/*
+ * LSB is the ADC single digital step
+ * 1 LSB = (vref_mv / 2 ^ 16)
+ *
+ * LSB is used to calculate analog voltage value
+ * from the number of ADC steps count
+ *
+ * Ain = (count * LSB)
+ */
+#define MAX11100_LSB_DIV (1 << 16)
+
+struct max11100_state {
+ struct regulator *vref_reg;
+ struct spi_device *spi;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ u8 buffer[3] __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct iio_chan_spec max11100_channels[] = {
+ { /* [0] */
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int max11100_read_single(struct iio_dev *indio_dev, int *val)
+{
+ int ret;
+ struct max11100_state *state = iio_priv(indio_dev);
+
+ ret = spi_read(state->spi, state->buffer, sizeof(state->buffer));
+ if (ret) {
+ dev_err(&indio_dev->dev, "SPI transfer failed\n");
+ return ret;
+ }
+
+ /* the first 8 bits sent out from ADC must be 0s */
+ if (state->buffer[0]) {
+ dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n");
+ return -EINVAL;
+ }
+
+ *val = get_unaligned_be16(&state->buffer[1]);
+
+ return 0;
+}
+
+static int max11100_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ int ret, vref_uv;
+ struct max11100_state *state = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max11100_read_single(indio_dev, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ vref_uv = regulator_get_voltage(state->vref_reg);
+ if (vref_uv < 0)
+ /* dummy regulator "get_voltage" returns -EINVAL */
+ return -EINVAL;
+
+ *val = vref_uv / 1000;
+ *val2 = MAX11100_LSB_DIV;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info max11100_info = {
+ .read_raw = max11100_read_raw,
+};
+
+static void max11100_regulator_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int max11100_probe(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct max11100_state *state;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ state->spi = spi;
+
+ indio_dev->name = "max11100";
+ indio_dev->info = &max11100_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max11100_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max11100_channels);
+
+ state->vref_reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(state->vref_reg))
+ return PTR_ERR(state->vref_reg);
+
+ ret = regulator_enable(state->vref_reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, max11100_regulator_disable,
+ state->vref_reg);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id max11100_ids[] = {
+ {.compatible = "maxim,max11100"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, max11100_ids);
+
+static struct spi_driver max11100_driver = {
+ .driver = {
+ .name = "max11100",
+ .of_match_table = max11100_ids,
+ },
+ .probe = max11100_probe,
+};
+
+module_spi_driver(max11100_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>");
+MODULE_DESCRIPTION("Maxim max11100 ADC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
new file mode 100644
index 000000000..75ab57d9a
--- /dev/null
+++ b/drivers/iio/adc/max1118.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
+ *
+ * Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
+ *
+ * SPI interface connections
+ *
+ * SPI MAXIM
+ * Master Direction MAX1117/8/9
+ * ------ --------- -----------
+ * nCS --> CNVST
+ * SCK --> SCLK
+ * MISO <-- DOUT
+ * ------ --------- -----------
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/regulator/consumer.h>
+
+enum max1118_id {
+ max1117,
+ max1118,
+ max1119,
+};
+
+struct max1118 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *reg;
+ /* Ensure natural alignment of buffer elements */
+ struct {
+ u8 channels[2];
+ s64 ts __aligned(8);
+ } scan;
+
+ u8 data __aligned(IIO_DMA_MINALIGN);
+};
+
+#define MAX1118_CHANNEL(ch) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (ch), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = ch, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+ }
+
+static const struct iio_chan_spec max1118_channels[] = {
+ MAX1118_CHANNEL(0),
+ MAX1118_CHANNEL(1),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int max1118_read(struct iio_dev *indio_dev, int channel)
+{
+ struct max1118 *adc = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ /*
+ * To select CH1 for conversion, CNVST pin must be brought high
+ * and low for a second time.
+ */
+ {
+ .len = 0,
+ .delay = { /* > CNVST Low Time 100 ns */
+ .value = 1,
+ .unit = SPI_DELAY_UNIT_USECS
+ },
+ .cs_change = 1,
+ },
+ /*
+ * The acquisition interval begins with the falling edge of
+ * CNVST. The total acquisition and conversion process takes
+ * <7.5us.
+ */
+ {
+ .len = 0,
+ .delay = {
+ .value = 8,
+ .unit = SPI_DELAY_UNIT_USECS
+ },
+ },
+ {
+ .rx_buf = &adc->data,
+ .len = 1,
+ },
+ };
+ int ret;
+
+ if (channel == 0)
+ ret = spi_sync_transfer(adc->spi, xfers + 1, 2);
+ else
+ ret = spi_sync_transfer(adc->spi, xfers, 3);
+
+ if (ret)
+ return ret;
+
+ return adc->data;
+}
+
+static int max1118_get_vref_mV(struct iio_dev *indio_dev)
+{
+ struct max1118 *adc = iio_priv(indio_dev);
+ const struct spi_device_id *id = spi_get_device_id(adc->spi);
+ int vref_uV;
+
+ switch (id->driver_data) {
+ case max1117:
+ return 2048;
+ case max1119:
+ return 4096;
+ case max1118:
+ vref_uV = regulator_get_voltage(adc->reg);
+ if (vref_uV < 0)
+ return vref_uV;
+ return vref_uV / 1000;
+ }
+
+ return -ENODEV;
+}
+
+static int max1118_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max1118 *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *val = max1118_read(indio_dev, chan->channel);
+ mutex_unlock(&adc->lock);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = max1118_get_vref_mV(indio_dev);
+ if (*val < 0)
+ return *val;
+ *val2 = 8;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info max1118_info = {
+ .read_raw = max1118_read_raw,
+};
+
+static irqreturn_t max1118_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct max1118 *adc = iio_priv(indio_dev);
+ int scan_index;
+ int i = 0;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan =
+ &indio_dev->channels[scan_index];
+ int ret = max1118_read(indio_dev, scan_chan->channel);
+
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ adc->scan.channels[i] = ret;
+ i++;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void max1118_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int max1118_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct max1118 *adc;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ if (id->driver_data == max1118) {
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return dev_err_probe(&spi->dev, PTR_ERR(adc->reg),
+ "failed to get vref regulator\n");
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, max1118_reg_disable,
+ adc->reg);
+ if (ret)
+ return ret;
+
+ }
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &max1118_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max1118_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max1118_channels);
+
+ /*
+ * To reinitiate a conversion on CH0, it is necessary to allow for a
+ * conversion to be complete and all of the data to be read out. Once
+ * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
+ * into AutoShutdown mode until the next conversion is initiated.
+ */
+ max1118_read(indio_dev, 0);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ max1118_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id max1118_id[] = {
+ { "max1117", max1117 },
+ { "max1118", max1118 },
+ { "max1119", max1119 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, max1118_id);
+
+static const struct of_device_id max1118_dt_ids[] = {
+ { .compatible = "maxim,max1117" },
+ { .compatible = "maxim,max1118" },
+ { .compatible = "maxim,max1119" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max1118_dt_ids);
+
+static struct spi_driver max1118_spi_driver = {
+ .driver = {
+ .name = "max1118",
+ .of_match_table = max1118_dt_ids,
+ },
+ .probe = max1118_probe,
+ .id_table = max1118_id,
+};
+module_spi_driver(max1118_spi_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c
new file mode 100644
index 000000000..65fc32971
--- /dev/null
+++ b/drivers/iio/adc/max11205.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim MAX11205 16-Bit Delta-Sigma ADC
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-max11205.pdf
+ * Copyright (C) 2022 Analog Devices, Inc.
+ * Author: Ramona Bolboaca <ramona.bolboaca@analog.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define MAX11205_BIT_SCALE 15
+#define MAX11205A_OUT_DATA_RATE 116
+#define MAX11205B_OUT_DATA_RATE 13
+
+enum max11205_chip_type {
+ TYPE_MAX11205A,
+ TYPE_MAX11205B,
+};
+
+struct max11205_chip_info {
+ unsigned int out_data_rate;
+ const char *name;
+};
+
+struct max11205_state {
+ const struct max11205_chip_info *chip_info;
+ struct regulator *vref;
+ struct ad_sigma_delta sd;
+};
+
+static const struct ad_sigma_delta_info max11205_sigma_delta_info = {
+ .has_registers = false,
+};
+
+static int max11205_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max11205_state *st = iio_priv(indio_dev);
+ int reg_mv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ reg_mv = regulator_get_voltage(st->vref);
+ if (reg_mv < 0)
+ return reg_mv;
+ reg_mv /= 1000;
+ *val = reg_mv;
+ *val2 = MAX11205_BIT_SCALE;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->chip_info->out_data_rate;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info max11205_iio_info = {
+ .read_raw = max11205_read_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+};
+
+static const struct iio_chan_spec max11205_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static const struct max11205_chip_info max11205_chip_info[] = {
+ [TYPE_MAX11205A] = {
+ .out_data_rate = MAX11205A_OUT_DATA_RATE,
+ .name = "max11205a",
+ },
+ [TYPE_MAX11205B] = {
+ .out_data_rate = MAX11205B_OUT_DATA_RATE,
+ .name = "max11205b",
+ },
+};
+
+static void max11205_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int max11205_probe(struct spi_device *spi)
+{
+ struct max11205_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info);
+
+ st->chip_info = device_get_match_data(&spi->dev);
+ if (!st->chip_info)
+ st->chip_info =
+ (const struct max11205_chip_info *)spi_get_device_id(spi)->driver_data;
+
+ indio_dev->name = st->chip_info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max11205_channels;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &max11205_iio_info;
+
+ st->vref = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->vref))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->vref),
+ "Failed to get vref regulator\n");
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, max11205_reg_disable, st->vref);
+ if (ret)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id max11205_spi_ids[] = {
+ { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] },
+ { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, max11205_spi_ids);
+
+static const struct of_device_id max11205_dt_ids[] = {
+ {
+ .compatible = "maxim,max11205a",
+ .data = &max11205_chip_info[TYPE_MAX11205A],
+ },
+ {
+ .compatible = "maxim,max11205b",
+ .data = &max11205_chip_info[TYPE_MAX11205B],
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max11205_dt_ids);
+
+static struct spi_driver max11205_spi_driver = {
+ .driver = {
+ .name = "max11205",
+ .of_match_table = max11205_dt_ids,
+ },
+ .probe = max11205_probe,
+ .id_table = max11205_spi_ids,
+};
+module_spi_driver(max11205_spi_driver);
+
+MODULE_AUTHOR("Ramona Bolboaca <ramona.bolboaca@analog.com>");
+MODULE_DESCRIPTION("MAX11205 ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c
new file mode 100644
index 000000000..a815ad1f6
--- /dev/null
+++ b/drivers/iio/adc/max1241.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MAX1241 low-power, 12-bit serial ADC
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-MAX1241.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define MAX1241_VAL_MASK GENMASK(11, 0)
+#define MAX1241_SHUTDOWN_DELAY_USEC 4
+
+enum max1241_id {
+ max1241,
+};
+
+struct max1241 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *vdd;
+ struct regulator *vref;
+ struct gpio_desc *shutdown;
+
+ __be16 data __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct iio_chan_spec max1241_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int max1241_read(struct max1241 *adc)
+{
+ struct spi_transfer xfers[] = {
+ /*
+ * Begin conversion by bringing /CS low for at least
+ * tconv us.
+ */
+ {
+ .len = 0,
+ .delay.value = 8,
+ .delay.unit = SPI_DELAY_UNIT_USECS,
+ },
+ /*
+ * Then read two bytes of data in our RX buffer.
+ */
+ {
+ .rx_buf = &adc->data,
+ .len = 2,
+ },
+ };
+
+ return spi_sync_transfer(adc->spi, xfers, ARRAY_SIZE(xfers));
+}
+
+static int max1241_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret, vref_uV;
+ struct max1241 *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+
+ if (adc->shutdown) {
+ gpiod_set_value(adc->shutdown, 0);
+ udelay(MAX1241_SHUTDOWN_DELAY_USEC);
+ ret = max1241_read(adc);
+ gpiod_set_value(adc->shutdown, 1);
+ } else
+ ret = max1241_read(adc);
+
+ if (ret) {
+ mutex_unlock(&adc->lock);
+ return ret;
+ }
+
+ *val = (be16_to_cpu(adc->data) >> 3) & MAX1241_VAL_MASK;
+
+ mutex_unlock(&adc->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ vref_uV = regulator_get_voltage(adc->vref);
+
+ if (vref_uV < 0)
+ return vref_uV;
+
+ *val = vref_uV / 1000;
+ *val2 = 12;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info max1241_info = {
+ .read_raw = max1241_read_raw,
+};
+
+static void max1241_disable_vdd_action(void *data)
+{
+ struct max1241 *adc = data;
+ struct device *dev = &adc->spi->dev;
+ int err;
+
+ err = regulator_disable(adc->vdd);
+ if (err)
+ dev_err(dev, "could not disable vdd regulator.\n");
+}
+
+static void max1241_disable_vref_action(void *data)
+{
+ struct max1241 *adc = data;
+ struct device *dev = &adc->spi->dev;
+ int err;
+
+ err = regulator_disable(adc->vref);
+ if (err)
+ dev_err(dev, "could not disable vref regulator.\n");
+}
+
+static int max1241_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct max1241 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ adc->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(adc->vdd))
+ return dev_err_probe(dev, PTR_ERR(adc->vdd),
+ "failed to get vdd regulator\n");
+
+ ret = regulator_enable(adc->vdd);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, max1241_disable_vdd_action, adc);
+ if (ret) {
+ dev_err(dev, "could not set up vdd regulator cleanup action\n");
+ return ret;
+ }
+
+ adc->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(adc->vref))
+ return dev_err_probe(dev, PTR_ERR(adc->vref),
+ "failed to get vref regulator\n");
+
+ ret = regulator_enable(adc->vref);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, max1241_disable_vref_action, adc);
+ if (ret) {
+ dev_err(dev, "could not set up vref regulator cleanup action\n");
+ return ret;
+ }
+
+ adc->shutdown = devm_gpiod_get_optional(dev, "shutdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(adc->shutdown))
+ return dev_err_probe(dev, PTR_ERR(adc->shutdown),
+ "cannot get shutdown gpio\n");
+
+ if (adc->shutdown)
+ dev_dbg(dev, "shutdown pin passed, low-power mode enabled");
+ else
+ dev_dbg(dev, "no shutdown pin passed, low-power mode disabled");
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &max1241_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max1241_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max1241_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id max1241_id[] = {
+ { "max1241", max1241 },
+ {}
+};
+
+static const struct of_device_id max1241_dt_ids[] = {
+ { .compatible = "maxim,max1241" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, max1241_dt_ids);
+
+static struct spi_driver max1241_spi_driver = {
+ .driver = {
+ .name = "max1241",
+ .of_match_table = max1241_dt_ids,
+ },
+ .probe = max1241_probe,
+ .id_table = max1241_id,
+};
+module_spi_driver(max1241_spi_driver);
+
+MODULE_AUTHOR("Alexandru Lazar <alazar@startmail.com>");
+MODULE_DESCRIPTION("MAX1241 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
new file mode 100644
index 000000000..a28cf86cd
--- /dev/null
+++ b/drivers/iio/adc/max1363.c
@@ -0,0 +1,1738 @@
+// SPDX-License-Identifier: GPL-2.0-only
+ /*
+ * iio/adc/max1363.c
+ * Copyright (C) 2008-2010 Jonathan Cameron
+ *
+ * based on linux/drivers/i2c/chips/max123x
+ * Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ * based on linux/drivers/acron/char/pcf8583.c
+ * Copyright (C) 2000 Russell King
+ *
+ * Driver for max1363 and similar chips.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define MAX1363_SETUP_BYTE(a) ((a) | 0x80)
+
+/* There is a fair bit more defined here than currently
+ * used, but the intention is to support everything these
+ * chips do in the long run */
+
+/* see data sheets */
+/* max1363 and max1236, max1237, max1238, max1239 */
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00
+#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20
+#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40
+#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60
+#define MAX1363_SETUP_POWER_UP_INT_REF 0x10
+#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00
+
+/* think about including max11600 etc - more settings */
+#define MAX1363_SETUP_EXT_CLOCK 0x08
+#define MAX1363_SETUP_INT_CLOCK 0x00
+#define MAX1363_SETUP_UNIPOLAR 0x00
+#define MAX1363_SETUP_BIPOLAR 0x04
+#define MAX1363_SETUP_RESET 0x00
+#define MAX1363_SETUP_NORESET 0x02
+/* max1363 only - though don't care on others.
+ * For now monitor modes are not implemented as the relevant
+ * line is not connected on my test board.
+ * The definitions are here as I intend to add this soon.
+ */
+#define MAX1363_SETUP_MONITOR_SETUP 0x01
+
+/* Specific to the max1363 */
+#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4))
+#define MAX1363_MON_INT_ENABLE 0x01
+
+/* defined for readability reasons */
+/* All chips */
+#define MAX1363_CONFIG_BYTE(a) ((a))
+
+#define MAX1363_CONFIG_SE 0x01
+#define MAX1363_CONFIG_DE 0x00
+#define MAX1363_CONFIG_SCAN_TO_CS 0x00
+#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20
+#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40
+#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60
+/* max123{6-9} only */
+#define MAX1236_SCAN_MID_TO_CHANNEL 0x40
+
+/* max1363 only - merely part of channel selects or don't care for others */
+#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18
+
+#define MAX1363_CHANNEL_SEL(a) ((a) << 1)
+
+/* max1363 strictly 0x06 - but doesn't matter */
+#define MAX1363_CHANNEL_SEL_MASK 0x1E
+#define MAX1363_SCAN_MASK 0x60
+#define MAX1363_SE_DE_MASK 0x01
+
+#define MAX1363_MAX_CHANNELS 25
+/**
+ * struct max1363_mode - scan mode information
+ * @conf: The corresponding value of the configuration register
+ * @modemask: Bit mask corresponding to channels enabled in this mode
+ */
+struct max1363_mode {
+ int8_t conf;
+ DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS);
+};
+
+/* This must be maintained along side the max1363_mode_table in max1363_core */
+enum max1363_modes {
+ /* Single read of a single channel */
+ _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11,
+ /* Differential single read */
+ d0m1, d2m3, d4m5, d6m7, d8m9, d10m11,
+ d1m0, d3m2, d5m4, d7m6, d9m8, d11m10,
+ /* Scan to channel and mid to channel where overlapping */
+ s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6,
+ s6to7, s0to7, s6to8, s0to8, s6to9,
+ s0to9, s6to10, s0to10, s6to11, s0to11,
+ /* Differential scan to channel and mid to channel where overlapping */
+ d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9,
+ d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2,
+ d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8,
+ d7m6to11m10, d1m0to11m10,
+};
+
+/**
+ * struct max1363_chip_info - chip specifc information
+ * @info: iio core function callbacks structure
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @mode_list: array of available scan modes
+ * @default_mode: the scan mode in which the chip starts up
+ * @int_vref_mv: the internal reference voltage
+ * @num_modes: number of modes
+ * @bits: accuracy of the adc in bits
+ */
+struct max1363_chip_info {
+ const struct iio_info *info;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ const enum max1363_modes *mode_list;
+ enum max1363_modes default_mode;
+ u16 int_vref_mv;
+ u8 num_modes;
+ u8 bits;
+};
+
+/**
+ * struct max1363_state - driver instance specific data
+ * @client: i2c_client
+ * @setupbyte: cache of current device setup byte
+ * @configbyte: cache of current device config byte
+ * @chip_info: chip model specific constants, available modes, etc.
+ * @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
+ * @mask_low: bitmask for enabled low thresholds
+ * @thresh_high: high threshold values
+ * @thresh_low: low threshold values
+ * @vref: Reference voltage regulator
+ * @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
+ */
+struct max1363_state {
+ struct i2c_client *client;
+ u8 setupbyte;
+ u8 configbyte;
+ const struct max1363_chip_info *chip_info;
+ 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 */
+ bool monitor_on;
+ unsigned int monitor_speed:3;
+ u8 mask_high;
+ u8 mask_low;
+ /* 4x unipolar first then the fours bipolar ones */
+ s16 thresh_high[8];
+ s16 thresh_low[8];
+ struct regulator *vref;
+ u32 vref_uv;
+ int (*send)(const struct i2c_client *client,
+ const char *buf, int count);
+ int (*recv)(const struct i2c_client *client,
+ char *buf, int count);
+};
+
+#define MAX1363_MODE_SINGLE(_num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask, \
+ }
+
+#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask, \
+ }
+
+/* note not available for max1363 hence naming */
+#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask \
+}
+
+#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_nump) \
+ | MAX1363_CONFIG_SCAN_SINGLE_1 \
+ | MAX1363_CONFIG_DE, \
+ .modemask[0] = _mask \
+ }
+
+/* Can't think how to automate naming so specify for now */
+#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1363_CONFIG_SCAN_TO_CS \
+ | MAX1363_CONFIG_DE, \
+ .modemask[0] = _mask \
+ }
+
+/* note only available for max1363 hence naming */
+#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \
+ .conf = MAX1363_CHANNEL_SEL(_num) \
+ | MAX1236_SCAN_MID_TO_CHANNEL \
+ | MAX1363_CONFIG_SE, \
+ .modemask[0] = _mask \
+}
+
+static const struct max1363_mode max1363_mode_table[] = {
+ /* All of the single channel options first */
+ MAX1363_MODE_SINGLE(0, 1 << 0),
+ MAX1363_MODE_SINGLE(1, 1 << 1),
+ MAX1363_MODE_SINGLE(2, 1 << 2),
+ MAX1363_MODE_SINGLE(3, 1 << 3),
+ MAX1363_MODE_SINGLE(4, 1 << 4),
+ MAX1363_MODE_SINGLE(5, 1 << 5),
+ MAX1363_MODE_SINGLE(6, 1 << 6),
+ MAX1363_MODE_SINGLE(7, 1 << 7),
+ MAX1363_MODE_SINGLE(8, 1 << 8),
+ MAX1363_MODE_SINGLE(9, 1 << 9),
+ MAX1363_MODE_SINGLE(10, 1 << 10),
+ MAX1363_MODE_SINGLE(11, 1 << 11),
+
+ MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12),
+ MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13),
+ MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14),
+ MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15),
+ MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16),
+ MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17),
+ MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18),
+ MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19),
+ MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20),
+ MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21),
+ MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22),
+ MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23),
+
+ /* The multichannel scans next */
+ MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003),
+ MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C),
+ MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F),
+ MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF),
+ MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0),
+ MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF),
+
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000),
+ MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000),
+ MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000),
+};
+
+static const struct max1363_mode
+*max1363_match_mode(const unsigned long *mask,
+ const struct max1363_chip_info *ci)
+{
+ int i;
+ if (mask)
+ for (i = 0; i < ci->num_modes; i++)
+ if (bitmap_subset(mask,
+ max1363_mode_table[ci->mode_list[i]].
+ modemask,
+ MAX1363_MAX_CHANNELS))
+ return &max1363_mode_table[ci->mode_list[i]];
+ return NULL;
+}
+
+static int max1363_smbus_send(const struct i2c_client *client, const char *buf,
+ int count)
+{
+ int i, err;
+
+ for (i = err = 0; err == 0 && i < count; ++i)
+ err = i2c_smbus_write_byte(client, buf[i]);
+
+ return err ? err : count;
+}
+
+static int max1363_smbus_recv(const struct i2c_client *client, char *buf,
+ int count)
+{
+ int i, ret;
+
+ for (i = 0; i < count; ++i) {
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0)
+ return ret;
+ buf[i] = ret;
+ }
+
+ return count;
+}
+
+static int max1363_write_basic_config(struct max1363_state *st)
+{
+ u8 tx_buf[2] = { st->setupbyte, st->configbyte };
+
+ return st->send(st->client, tx_buf, 2);
+}
+
+static int max1363_set_scan_mode(struct max1363_state *st)
+{
+ st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+ | MAX1363_SCAN_MASK
+ | MAX1363_SE_DE_MASK);
+ st->configbyte |= st->current_mode->conf;
+
+ return max1363_write_basic_config(st);
+}
+
+static int max1363_read_single_chan(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ long m)
+{
+ int ret = 0;
+ s32 data;
+ u8 rxbuf[2];
+ struct max1363_state *st = iio_priv(indio_dev);
+ struct i2c_client *client = st->client;
+
+ 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
+ * been implemented.
+ *
+ * Also, cannot read directly if buffered capture enabled.
+ */
+ if (st->monitor_on) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+
+ /* Check to see if current scan mode is correct */
+ if (st->current_mode != &max1363_mode_table[chan->address]) {
+ /* Update scan mode if needed */
+ st->current_mode = &max1363_mode_table[chan->address];
+ ret = max1363_set_scan_mode(st);
+ if (ret < 0)
+ goto error_ret;
+ }
+ if (st->chip_info->bits != 8) {
+ /* Get reading */
+ data = st->recv(client, rxbuf, 2);
+ if (data < 0) {
+ ret = data;
+ goto error_ret;
+ }
+ data = (rxbuf[1] | rxbuf[0] << 8) &
+ ((1 << st->chip_info->bits) - 1);
+ } else {
+ /* Get reading */
+ data = st->recv(client, rxbuf, 1);
+ if (data < 0) {
+ ret = data;
+ goto error_ret;
+ }
+ data = rxbuf[0];
+ }
+ *val = data;
+
+error_ret:
+ mutex_unlock(&st->lock);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+}
+
+static int max1363_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max1363_read_single_chan(indio_dev, chan, val, m);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_uv / 1000;
+ *val2 = st->chip_info->bits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Applies to max1363 */
+static const enum max1363_modes max1363_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, s0to3,
+ d0m1, d2m3, d1m0, d3m2,
+ d0m1to2m3, d1m0to3m2,
+};
+
+static const struct iio_event_spec max1363_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 MAX1363_CHAN_U(num, addr, si, bits, ev_spec, num_ev_spec) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .address = addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#num, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = bits, \
+ .storagebits = (bits > 8) ? 16 : 8, \
+ .endianness = IIO_BE, \
+ }, \
+ .scan_index = si, \
+ .event_spec = ev_spec, \
+ .num_event_specs = num_ev_spec, \
+ }
+
+/* bipolar channel */
+#define MAX1363_CHAN_B(num, num2, addr, si, bits, ev_spec, num_ev_spec) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .indexed = 1, \
+ .channel = num, \
+ .channel2 = num2, \
+ .address = addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#num"-AIN"#num2, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = bits, \
+ .storagebits = (bits > 8) ? 16 : 8, \
+ .endianness = IIO_BE, \
+ }, \
+ .scan_index = si, \
+ .event_spec = ev_spec, \
+ .num_event_specs = num_ev_spec, \
+ }
+
+#define MAX1363_4X_CHANS(bits, ev_spec, num_ev_spec) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, ev_spec, num_ev_spec), \
+ 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), \
+ IIO_CHAN_SOFT_TIMESTAMP(8) \
+ }
+
+static const struct iio_chan_spec max1036_channels[] =
+ MAX1363_4X_CHANS(8, NULL, 0);
+static const struct iio_chan_spec max1136_channels[] =
+ MAX1363_4X_CHANS(10, NULL, 0);
+static const struct iio_chan_spec max1236_channels[] =
+ MAX1363_4X_CHANS(12, NULL, 0);
+static const struct iio_chan_spec max1361_channels[] =
+ MAX1363_4X_CHANS(10, max1363_events, ARRAY_SIZE(max1363_events));
+static const struct iio_chan_spec max1363_channels[] =
+ MAX1363_4X_CHANS(12, max1363_events, ARRAY_SIZE(max1363_events));
+
+/* Applies to max1236, max1237 */
+static const enum max1363_modes max1236_mode_list[] = {
+ _s0, _s1, _s2, _s3,
+ s0to1, s0to2, 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,
+ 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,
+};
+
+#define MAX1363_12X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \
+ MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \
+ MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \
+ MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \
+ 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_U(8, _s8, 8, bits, NULL, 0), \
+ MAX1363_CHAN_U(9, _s9, 9, bits, NULL, 0), \
+ MAX1363_CHAN_U(10, _s10, 10, bits, NULL, 0), \
+ MAX1363_CHAN_U(11, _s11, 11, 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(8, 9, d8m9, 16, bits, NULL, 0), \
+ MAX1363_CHAN_B(10, 11, d10m11, 17, 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), \
+ MAX1363_CHAN_B(9, 8, d9m8, 22, bits, NULL, 0), \
+ MAX1363_CHAN_B(11, 10, d11m10, 23, bits, NULL, 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(24) \
+ }
+static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
+static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
+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,
+ 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,
+ d0m1, d2m3, d4m5, d6m7,
+ d1m0, d3m2, d5m4, d7m6,
+ d0m1to2m3, d0m1to4m5, d0m1to6m7,
+ d1m0to3m2, d1m0to5m4, d1m0to7m6,
+};
+
+#define MAX1363_8X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \
+ MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \
+ MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \
+ MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \
+ 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), \
+ IIO_CHAN_SOFT_TIMESTAMP(16) \
+}
+static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
+static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10);
+static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12);
+
+static const enum max1363_modes max11644_mode_list[] = {
+ _s0, _s1, s0to1, d0m1, d1m0,
+};
+
+#define MAX1363_2X_CHANS(bits) { \
+ MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \
+ MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \
+ MAX1363_CHAN_B(0, 1, d0m1, 2, bits, NULL, 0), \
+ MAX1363_CHAN_B(1, 0, d1m0, 3, bits, NULL, 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(4) \
+ }
+
+static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10);
+static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12);
+
+enum { max1361,
+ max1362,
+ max1363,
+ max1364,
+ max1036,
+ max1037,
+ max1038,
+ max1039,
+ max1136,
+ max1137,
+ max1138,
+ max1139,
+ max1236,
+ max1237,
+ max1238,
+ max1239,
+ max11600,
+ max11601,
+ max11602,
+ max11603,
+ max11604,
+ max11605,
+ max11606,
+ max11607,
+ max11608,
+ max11609,
+ max11610,
+ max11611,
+ max11612,
+ max11613,
+ max11614,
+ max11615,
+ max11616,
+ max11617,
+ max11644,
+ max11645,
+ max11646,
+ max11647
+};
+
+static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600,
+ 8300, 4200, 2000, 1000 };
+
+static ssize_t max1363_monitor_show_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct max1363_state *st = iio_priv(dev_to_iio_dev(dev));
+ return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]);
+}
+
+static ssize_t max1363_monitor_store_freq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct max1363_state *st = iio_priv(indio_dev);
+ int i, ret;
+ unsigned long val;
+ bool found = false;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++)
+ if (val == max1363_monitor_speeds[i]) {
+ found = true;
+ break;
+ }
+ if (!found)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ st->monitor_speed = i;
+ mutex_unlock(&st->lock);
+
+ return 0;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+ max1363_monitor_show_freq,
+ max1363_monitor_store_freq);
+
+static IIO_CONST_ATTR(sampling_frequency_available,
+ "133000 665000 33300 16600 8300 4200 2000 1000");
+
+static int max1363_read_thresh(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 max1363_state *st = iio_priv(indio_dev);
+ if (dir == IIO_EV_DIR_FALLING)
+ *val = st->thresh_low[chan->channel];
+ else
+ *val = st->thresh_high[chan->channel];
+ return IIO_VAL_INT;
+}
+
+static int max1363_write_thresh(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 max1363_state *st = iio_priv(indio_dev);
+ /* make it handle signed correctly as well */
+ switch (st->chip_info->bits) {
+ case 10:
+ if (val > 0x3FF)
+ return -EINVAL;
+ break;
+ case 12:
+ if (val > 0xFFF)
+ return -EINVAL;
+ break;
+ }
+
+ switch (dir) {
+ case IIO_EV_DIR_FALLING:
+ st->thresh_low[chan->channel] = val;
+ break;
+ case IIO_EV_DIR_RISING:
+ st->thresh_high[chan->channel] = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const u64 max1363_event_codes[] = {
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+};
+
+static irqreturn_t max1363_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct max1363_state *st = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns(indio_dev);
+ unsigned long mask, loc;
+ u8 rx;
+ u8 tx[2] = { st->setupbyte,
+ MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 };
+
+ st->recv(st->client, &rx, 1);
+ mask = rx;
+ for_each_set_bit(loc, &mask, 8)
+ iio_push_event(indio_dev, max1363_event_codes[loc], timestamp);
+ st->send(st->client, tx, 2);
+
+ return IRQ_HANDLED;
+}
+
+static int max1363_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 max1363_state *st = iio_priv(indio_dev);
+ int val;
+ int number = chan->channel;
+
+ 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(&st->lock);
+
+ return val;
+}
+
+static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
+{
+ u8 *tx_buf;
+ int ret, i = 3, j;
+ unsigned long numelements;
+ int len;
+ const long *modemask;
+
+ if (!enabled) {
+ /* transition to buffered capture is not currently supported */
+ st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP;
+ st->configbyte &= ~MAX1363_SCAN_MASK;
+ st->monitor_on = false;
+ return max1363_write_basic_config(st);
+ }
+
+ /* Ensure we are in the relevant mode */
+ st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP;
+ st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK
+ | MAX1363_SCAN_MASK
+ | MAX1363_SE_DE_MASK);
+ st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE;
+ if ((st->mask_low | st->mask_high) & 0x0F) {
+ st->configbyte |= max1363_mode_table[s0to3].conf;
+ modemask = max1363_mode_table[s0to3].modemask;
+ } else if ((st->mask_low | st->mask_high) & 0x30) {
+ st->configbyte |= max1363_mode_table[d0m1to2m3].conf;
+ modemask = max1363_mode_table[d0m1to2m3].modemask;
+ } else {
+ st->configbyte |= max1363_mode_table[d1m0to3m2].conf;
+ modemask = max1363_mode_table[d1m0to3m2].modemask;
+ }
+ numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS);
+ len = 3 * numelements + 3;
+ tx_buf = kmalloc(len, GFP_KERNEL);
+ if (!tx_buf) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ tx_buf[0] = st->configbyte;
+ tx_buf[1] = st->setupbyte;
+ tx_buf[2] = (st->monitor_speed << 1);
+
+ /*
+ * So we need to do yet another bit of nefarious scan mode
+ * setup to match what we need.
+ */
+ for (j = 0; j < 8; j++)
+ if (test_bit(j, modemask)) {
+ /* Establish the mode is in the scan */
+ if (st->mask_low & (1 << j)) {
+ tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF;
+ tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0;
+ } else if (j < 4) {
+ tx_buf[i] = 0;
+ tx_buf[i + 1] = 0;
+ } else {
+ tx_buf[i] = 0x80;
+ tx_buf[i + 1] = 0;
+ }
+ if (st->mask_high & (1 << j)) {
+ tx_buf[i + 1] |=
+ (st->thresh_high[j] >> 8) & 0x0F;
+ tx_buf[i + 2] = st->thresh_high[j] & 0xFF;
+ } else if (j < 4) {
+ tx_buf[i + 1] |= 0x0F;
+ tx_buf[i + 2] = 0xFF;
+ } else {
+ tx_buf[i + 1] |= 0x07;
+ tx_buf[i + 2] = 0xFF;
+ }
+ i += 3;
+ }
+
+
+ ret = st->send(st->client, tx_buf, len);
+ if (ret < 0)
+ goto error_ret;
+ if (ret != len) {
+ ret = -EIO;
+ goto error_ret;
+ }
+
+ /*
+ * Now that we hopefully have sensible thresholds in place it is
+ * time to turn the interrupts on.
+ * It is unclear from the data sheet if this should be necessary
+ * (i.e. whether monitor mode setup is atomic) but it appears to
+ * be in practice.
+ */
+ tx_buf[0] = st->setupbyte;
+ tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0;
+ ret = st->send(st->client, tx_buf, 2);
+ if (ret < 0)
+ goto error_ret;
+ if (ret != 2) {
+ ret = -EIO;
+ goto error_ret;
+ }
+ ret = 0;
+ st->monitor_on = true;
+error_ret:
+
+ kfree(tx_buf);
+
+ return ret;
+}
+
+/*
+ * To keep this manageable we always use one of 3 scan modes.
+ * Scan 0...3, 0-1,2-3 and 1-0,3-2
+ */
+
+static inline int __max1363_check_event_mask(int thismask, int checkmask)
+{
+ int ret = 0;
+ /* Is it unipolar */
+ if (thismask < 4) {
+ if (checkmask & ~0x0F) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+ } else if (thismask < 6) {
+ if (checkmask & ~0x30) {
+ ret = -EBUSY;
+ goto error_ret;
+ }
+ } else if (checkmask & ~0xC0)
+ ret = -EBUSY;
+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,
+ enum iio_event_direction dir, int state)
+{
+ int ret = 0;
+ struct max1363_state *st = iio_priv(indio_dev);
+ u16 unifiedmask;
+ int number = chan->channel;
+
+ 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) {
+
+ if (state == 0)
+ st->mask_low &= ~(1 << number);
+ else {
+ ret = __max1363_check_event_mask((1 << number),
+ unifiedmask);
+ if (ret)
+ goto error_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)
+ goto error_ret;
+ st->mask_high |= (1 << number);
+ }
+ }
+
+ max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
+error_ret:
+ mutex_unlock(&st->lock);
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+/*
+ * As with scan_elements, only certain sets of these can
+ * be combined.
+ */
+static struct attribute *max1363_event_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max1363_event_attribute_group = {
+ .attrs = max1363_event_attributes,
+};
+
+static int max1363_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+
+ /*
+ * Need to figure out the current mode based upon the requested
+ * scan mask in iio_dev
+ */
+ st->current_mode = max1363_match_mode(scan_mask, st->chip_info);
+ if (!st->current_mode)
+ return -EINVAL;
+ max1363_set_scan_mode(st);
+ return 0;
+}
+
+static const struct iio_info max1238_info = {
+ .read_raw = &max1363_read_raw,
+ .update_scan_mode = &max1363_update_scan_mode,
+};
+
+static const struct iio_info max1363_info = {
+ .read_event_value = &max1363_read_thresh,
+ .write_event_value = &max1363_write_thresh,
+ .read_event_config = &max1363_read_event_config,
+ .write_event_config = &max1363_write_event_config,
+ .read_raw = &max1363_read_raw,
+ .update_scan_mode = &max1363_update_scan_mode,
+ .event_attrs = &max1363_event_attribute_group,
+};
+
+/* max1363 and max1368 tested - rest from data sheet */
+static const struct max1363_chip_info max1363_chip_info_tbl[] = {
+ [max1361] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1361_channels,
+ .num_channels = ARRAY_SIZE(max1361_channels),
+ .info = &max1363_info,
+ },
+ [max1362] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1361_channels,
+ .num_channels = ARRAY_SIZE(max1361_channels),
+ .info = &max1363_info,
+ },
+ [max1363] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ .info = &max1363_info,
+ },
+ [max1364] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1363_mode_list,
+ .num_modes = ARRAY_SIZE(max1363_mode_list),
+ .default_mode = s0to3,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ .info = &max1363_info,
+ },
+ [max1036] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max1037] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max1038] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max1039] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max1136] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max1137] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max1138] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max1139] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max1236] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max1237] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1236_mode_list,
+ .num_modes = ARRAY_SIZE(max1236_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1236_channels,
+ .num_channels = ARRAY_SIZE(max1236_channels),
+ },
+ [max1238] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max1239] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11600] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max11601] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1036_channels,
+ .num_channels = ARRAY_SIZE(max1036_channels),
+ },
+ [max11602] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
+ },
+ [max11603] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11602_channels,
+ .num_channels = ARRAY_SIZE(max11602_channels),
+ },
+ [max11604] = {
+ .bits = 8,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max11605] = {
+ .bits = 8,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1038_channels,
+ .num_channels = ARRAY_SIZE(max1038_channels),
+ },
+ [max11606] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max11607] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1136_channels,
+ .num_channels = ARRAY_SIZE(max1136_channels),
+ },
+ [max11608] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
+ },
+ [max11609] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11608_channels,
+ .num_channels = ARRAY_SIZE(max11608_channels),
+ },
+ [max11610] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max11611] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1138_channels,
+ .num_channels = ARRAY_SIZE(max1138_channels),
+ },
+ [max11612] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ },
+ [max11613] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11607_mode_list,
+ .num_modes = ARRAY_SIZE(max11607_mode_list),
+ .default_mode = s0to3,
+ .info = &max1238_info,
+ .channels = max1363_channels,
+ .num_channels = ARRAY_SIZE(max1363_channels),
+ },
+ [max11614] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
+ },
+ [max11615] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11608_mode_list,
+ .num_modes = ARRAY_SIZE(max11608_mode_list),
+ .default_mode = s0to7,
+ .info = &max1238_info,
+ .channels = max11614_channels,
+ .num_channels = ARRAY_SIZE(max11614_channels),
+ },
+ [max11616] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11617] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max1238_mode_list,
+ .num_modes = ARRAY_SIZE(max1238_mode_list),
+ .default_mode = s0to11,
+ .info = &max1238_info,
+ .channels = max1238_channels,
+ .num_channels = ARRAY_SIZE(max1238_channels),
+ },
+ [max11644] = {
+ .bits = 12,
+ .int_vref_mv = 4096,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11644_channels,
+ .num_channels = ARRAY_SIZE(max11644_channels),
+ },
+ [max11645] = {
+ .bits = 12,
+ .int_vref_mv = 2048,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11644_channels,
+ .num_channels = ARRAY_SIZE(max11644_channels),
+ },
+ [max11646] = {
+ .bits = 10,
+ .int_vref_mv = 4096,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11646_channels,
+ .num_channels = ARRAY_SIZE(max11646_channels),
+ },
+ [max11647] = {
+ .bits = 10,
+ .int_vref_mv = 2048,
+ .mode_list = max11644_mode_list,
+ .num_modes = ARRAY_SIZE(max11644_mode_list),
+ .default_mode = s0to1,
+ .info = &max1238_info,
+ .channels = max11646_channels,
+ .num_channels = ARRAY_SIZE(max11646_channels),
+ },
+};
+
+static int max1363_initial_setup(struct max1363_state *st)
+{
+ st->setupbyte = MAX1363_SETUP_INT_CLOCK
+ | MAX1363_SETUP_UNIPOLAR
+ | MAX1363_SETUP_NORESET;
+
+ if (st->vref)
+ st->setupbyte |= MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF;
+ else
+ st->setupbyte |= MAX1363_SETUP_POWER_UP_INT_REF
+ | MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT;
+
+ /* Set scan mode writes the config anyway so wait until then */
+ st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte);
+ st->current_mode = &max1363_mode_table[st->chip_info->default_mode];
+ st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte);
+
+ return max1363_set_scan_mode(st);
+}
+
+static int max1363_alloc_scan_masks(struct iio_dev *indio_dev)
+{
+ struct max1363_state *st = iio_priv(indio_dev);
+ unsigned long *masks;
+ int i;
+
+ masks = devm_kzalloc(&indio_dev->dev,
+ array3_size(BITS_TO_LONGS(MAX1363_MAX_CHANNELS),
+ sizeof(long),
+ st->chip_info->num_modes + 1),
+ GFP_KERNEL);
+ if (!masks)
+ return -ENOMEM;
+
+ for (i = 0; i < st->chip_info->num_modes; i++)
+ bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i,
+ max1363_mode_table[st->chip_info->mode_list[i]]
+ .modemask, MAX1363_MAX_CHANNELS);
+
+ indio_dev->available_scan_masks = masks;
+
+ return 0;
+}
+
+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.
+ */
+ 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);
+ else
+ b_sent = st->recv(st->client, rxbuf, numvals);
+ if (b_sent < 0)
+ goto done_free;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, rxbuf,
+ iio_get_time_ns(indio_dev));
+
+done_free:
+ kfree(rxbuf);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+#define MAX1363_COMPATIBLE(of_compatible, cfg) { \
+ .compatible = of_compatible, \
+ .data = &max1363_chip_info_tbl[cfg], \
+}
+
+static const struct of_device_id max1363_of_match[] = {
+ MAX1363_COMPATIBLE("maxim,max1361", max1361),
+ MAX1363_COMPATIBLE("maxim,max1362", max1362),
+ MAX1363_COMPATIBLE("maxim,max1363", max1363),
+ MAX1363_COMPATIBLE("maxim,max1364", max1364),
+ MAX1363_COMPATIBLE("maxim,max1036", max1036),
+ MAX1363_COMPATIBLE("maxim,max1037", max1037),
+ MAX1363_COMPATIBLE("maxim,max1038", max1038),
+ MAX1363_COMPATIBLE("maxim,max1039", max1039),
+ MAX1363_COMPATIBLE("maxim,max1136", max1136),
+ MAX1363_COMPATIBLE("maxim,max1137", max1137),
+ MAX1363_COMPATIBLE("maxim,max1138", max1138),
+ MAX1363_COMPATIBLE("maxim,max1139", max1139),
+ MAX1363_COMPATIBLE("maxim,max1236", max1236),
+ MAX1363_COMPATIBLE("maxim,max1237", max1237),
+ MAX1363_COMPATIBLE("maxim,max1238", max1238),
+ MAX1363_COMPATIBLE("maxim,max1239", max1239),
+ MAX1363_COMPATIBLE("maxim,max11600", max11600),
+ MAX1363_COMPATIBLE("maxim,max11601", max11601),
+ MAX1363_COMPATIBLE("maxim,max11602", max11602),
+ MAX1363_COMPATIBLE("maxim,max11603", max11603),
+ MAX1363_COMPATIBLE("maxim,max11604", max11604),
+ MAX1363_COMPATIBLE("maxim,max11605", max11605),
+ MAX1363_COMPATIBLE("maxim,max11606", max11606),
+ MAX1363_COMPATIBLE("maxim,max11607", max11607),
+ MAX1363_COMPATIBLE("maxim,max11608", max11608),
+ MAX1363_COMPATIBLE("maxim,max11609", max11609),
+ MAX1363_COMPATIBLE("maxim,max11610", max11610),
+ MAX1363_COMPATIBLE("maxim,max11611", max11611),
+ MAX1363_COMPATIBLE("maxim,max11612", max11612),
+ MAX1363_COMPATIBLE("maxim,max11613", max11613),
+ MAX1363_COMPATIBLE("maxim,max11614", max11614),
+ MAX1363_COMPATIBLE("maxim,max11615", max11615),
+ MAX1363_COMPATIBLE("maxim,max11616", max11616),
+ MAX1363_COMPATIBLE("maxim,max11617", max11617),
+ MAX1363_COMPATIBLE("maxim,max11644", max11644),
+ MAX1363_COMPATIBLE("maxim,max11645", max11645),
+ MAX1363_COMPATIBLE("maxim,max11646", max11646),
+ MAX1363_COMPATIBLE("maxim,max11647", max11647),
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max1363_of_match);
+
+static void max1363_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int max1363_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct max1363_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *vref;
+
+ indio_dev = devm_iio_device_alloc(&client->dev,
+ sizeof(struct max1363_state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ mutex_init(&st->lock);
+ st->reg = devm_regulator_get(&client->dev, "vcc");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev, max1363_reg_disable, st->reg);
+ if (ret)
+ return ret;
+
+ st->chip_info = device_get_match_data(&client->dev);
+ if (!st->chip_info)
+ st->chip_info = &max1363_chip_info_tbl[id->driver_data];
+ st->client = client;
+
+ st->vref_uv = st->chip_info->int_vref_mv * 1000;
+ vref = devm_regulator_get_optional(&client->dev, "vref");
+ if (!IS_ERR(vref)) {
+ int vref_uv;
+
+ ret = regulator_enable(vref);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev, max1363_reg_disable, vref);
+ if (ret)
+ return ret;
+
+ st->vref = vref;
+ vref_uv = regulator_get_voltage(vref);
+ if (vref_uv <= 0)
+ return -EINVAL;
+
+ st->vref_uv = vref_uv;
+ }
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ st->send = i2c_master_send;
+ st->recv = i2c_master_recv;
+ } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)
+ && st->chip_info->bits == 8) {
+ st->send = max1363_smbus_send;
+ st->recv = max1363_smbus_recv;
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ ret = max1363_alloc_scan_masks(indio_dev);
+ if (ret)
+ return ret;
+
+ indio_dev->name = id->name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = st->chip_info->info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = max1363_initial_setup(st);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
+ &max1363_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, st->client->irq,
+ NULL,
+ &max1363_event_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "max1363_event",
+ indio_dev);
+
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id max1363_id[] = {
+ { "max1361", max1361 },
+ { "max1362", max1362 },
+ { "max1363", max1363 },
+ { "max1364", max1364 },
+ { "max1036", max1036 },
+ { "max1037", max1037 },
+ { "max1038", max1038 },
+ { "max1039", max1039 },
+ { "max1136", max1136 },
+ { "max1137", max1137 },
+ { "max1138", max1138 },
+ { "max1139", max1139 },
+ { "max1236", max1236 },
+ { "max1237", max1237 },
+ { "max1238", max1238 },
+ { "max1239", max1239 },
+ { "max11600", max11600 },
+ { "max11601", max11601 },
+ { "max11602", max11602 },
+ { "max11603", max11603 },
+ { "max11604", max11604 },
+ { "max11605", max11605 },
+ { "max11606", max11606 },
+ { "max11607", max11607 },
+ { "max11608", max11608 },
+ { "max11609", max11609 },
+ { "max11610", max11610 },
+ { "max11611", max11611 },
+ { "max11612", max11612 },
+ { "max11613", max11613 },
+ { "max11614", max11614 },
+ { "max11615", max11615 },
+ { "max11616", max11616 },
+ { "max11617", max11617 },
+ { "max11644", max11644 },
+ { "max11645", max11645 },
+ { "max11646", max11646 },
+ { "max11647", max11647 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, max1363_id);
+
+static struct i2c_driver max1363_driver = {
+ .driver = {
+ .name = "max1363",
+ .of_match_table = max1363_of_match,
+ },
+ .probe = max1363_probe,
+ .id_table = max1363_id,
+};
+module_i2c_driver(max1363_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Maxim 1363 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
new file mode 100644
index 000000000..f982f0030
--- /dev/null
+++ b/drivers/iio/adc/max9611.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * iio/adc/max9611.c
+ *
+ * Maxim max9611/max9612 high side current sense amplifier with
+ * 12-bit ADC interface.
+ *
+ * Copyright (C) 2017 Jacopo Mondi
+ */
+
+/*
+ * This driver supports input common-mode voltage, current-sense
+ * amplifier with programmable gains and die temperature reading from
+ * Maxim max9611/max9612.
+ *
+ * Op-amp, analog comparator, and watchdog functionalities are not
+ * supported by this driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#define DRIVER_NAME "max9611"
+
+/* max9611 register addresses */
+#define MAX9611_REG_CSA_DATA 0x00
+#define MAX9611_REG_RS_DATA 0x02
+#define MAX9611_REG_TEMP_DATA 0x08
+#define MAX9611_REG_CTRL1 0x0a
+#define MAX9611_REG_CTRL2 0x0b
+
+/* max9611 REG1 mux configuration options */
+#define MAX9611_MUX_MASK GENMASK(3, 0)
+#define MAX9611_MUX_SENSE_1x 0x00
+#define MAX9611_MUX_SENSE_4x 0x01
+#define MAX9611_MUX_SENSE_8x 0x02
+#define MAX9611_INPUT_VOLT 0x03
+#define MAX9611_MUX_TEMP 0x06
+
+/* max9611 voltage (both csa and input) helper macros */
+#define MAX9611_VOLTAGE_SHIFT 0x04
+#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT)
+
+/*
+ * max9611 current sense amplifier voltage output:
+ * LSB and offset values depends on selected gain (1x, 4x, 8x)
+ *
+ * GAIN LSB (nV) OFFSET (LSB steps)
+ * 1x 107500 1
+ * 4x 26880 1
+ * 8x 13440 3
+ *
+ * The complete formula to calculate current sense voltage is:
+ * (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3)
+ */
+#define MAX9611_CSA_1X_LSB_nV 107500
+#define MAX9611_CSA_4X_LSB_nV 26880
+#define MAX9611_CSA_8X_LSB_nV 13440
+
+#define MAX9611_CSA_1X_OFFS_RAW 1
+#define MAX9611_CSA_4X_OFFS_RAW 1
+#define MAX9611_CSA_8X_OFFS_RAW 3
+
+/*
+ * max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C
+ *
+ * The complete formula to calculate input common voltage is:
+ * (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000)
+ */
+#define MAX9611_CIM_LSB_mV 14
+#define MAX9611_CIM_OFFSET_RAW 1
+
+/*
+ * max9611 temperature reading: LSB is 480 milli degrees Celsius
+ *
+ * The complete formula to calculate temperature is:
+ * ((adc_read >> 7) * 1000) / (1 / 480 * 1000)
+ */
+#define MAX9611_TEMP_MAX_POS 0x7f80
+#define MAX9611_TEMP_MAX_NEG 0xff80
+#define MAX9611_TEMP_MIN_NEG 0xd980
+#define MAX9611_TEMP_MASK GENMASK(15, 7)
+#define MAX9611_TEMP_SHIFT 0x07
+#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
+#define MAX9611_TEMP_SCALE_NUM 1000000
+#define MAX9611_TEMP_SCALE_DIV 2083
+
+/*
+ * Conversion time is 2 ms (typically) at Ta=25 degreeC
+ * No maximum value is known, so play it safe.
+ */
+#define MAX9611_CONV_TIME_US_RANGE 3000, 3300
+
+struct max9611_dev {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct mutex lock;
+ unsigned int shunt_resistor_uohm;
+};
+
+enum max9611_conf_ids {
+ CONF_SENSE_1x,
+ CONF_SENSE_4x,
+ CONF_SENSE_8x,
+ CONF_IN_VOLT,
+ CONF_TEMP,
+};
+
+/*
+ * max9611_mux_conf - associate ADC mux configuration with register address
+ * where data shall be read from
+ */
+static const unsigned int max9611_mux_conf[][2] = {
+ [CONF_SENSE_1x] = { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
+ [CONF_SENSE_4x] = { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
+ [CONF_SENSE_8x] = { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
+ [CONF_IN_VOLT] = { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
+ [CONF_TEMP] = { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
+};
+
+enum max9611_csa_gain {
+ CSA_GAIN_1x = CONF_SENSE_1x,
+ CSA_GAIN_4x = CONF_SENSE_4x,
+ CSA_GAIN_8x = CONF_SENSE_8x,
+};
+
+enum max9611_csa_gain_params {
+ CSA_GAIN_LSB_nV,
+ CSA_GAIN_OFFS_RAW,
+};
+
+/*
+ * max9611_csa_gain_conf - associate gain multiplier with LSB and
+ * offset values.
+ *
+ * Group together parameters associated with configurable gain
+ * on current sense amplifier path to ADC interface.
+ * Current sense read routine adjusts gain until it gets a meaningful
+ * value; use this structure to retrieve the correct LSB and offset values.
+ */
+static const unsigned int max9611_gain_conf[][2] = {
+ [CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, },
+ [CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, },
+ [CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_OFFS_RAW, },
+};
+
+enum max9611_chan_addrs {
+ MAX9611_CHAN_VOLTAGE_INPUT,
+ MAX9611_CHAN_VOLTAGE_SENSE,
+ MAX9611_CHAN_TEMPERATURE,
+ MAX9611_CHAN_CURRENT_LOAD,
+ MAX9611_CHAN_POWER_LOAD,
+};
+
+static const struct iio_chan_spec max9611_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = MAX9611_CHAN_TEMPERATURE,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = MAX9611_CHAN_VOLTAGE_SENSE,
+ .indexed = 1,
+ .channel = 0,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = MAX9611_CHAN_VOLTAGE_INPUT,
+ .indexed = 1,
+ .channel = 1,
+ },
+ {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = MAX9611_CHAN_CURRENT_LOAD,
+ },
+ {
+ .type = IIO_POWER,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = MAX9611_CHAN_POWER_LOAD
+ },
+};
+
+/**
+ * max9611_read_single() - read a single value from ADC interface
+ *
+ * Data registers are 16 bit long, spread between two 8 bit registers
+ * with consecutive addresses.
+ * Configure ADC mux first, then read register at address "reg_addr".
+ * The smbus_read_word routine asks for 16 bits and the ADC is kind enough
+ * to return values from "reg_addr" and "reg_addr + 1" consecutively.
+ * Data are transmitted with big-endian ordering: MSB arrives first.
+ *
+ * @max9611: max9611 device
+ * @selector: index for mux and register configuration
+ * @raw_val: the value returned from ADC
+ */
+static int max9611_read_single(struct max9611_dev *max9611,
+ enum max9611_conf_ids selector,
+ u16 *raw_val)
+{
+ int ret;
+
+ u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK;
+ u8 reg_addr = max9611_mux_conf[selector][1];
+
+ /*
+ * Keep mutex lock held during read-write to avoid mux register
+ * (CTRL1) re-configuration.
+ */
+ mutex_lock(&max9611->lock);
+ ret = i2c_smbus_write_byte_data(max9611->i2c_client,
+ MAX9611_REG_CTRL1, mux_conf);
+ if (ret) {
+ dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
+ MAX9611_REG_CTRL1, mux_conf);
+ mutex_unlock(&max9611->lock);
+ return ret;
+ }
+
+ /* need a delay here to make register configuration stabilize. */
+
+ usleep_range(MAX9611_CONV_TIME_US_RANGE);
+
+ ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr);
+ if (ret < 0) {
+ dev_err(max9611->dev, "i2c read word from 0x%2x failed\n",
+ reg_addr);
+ mutex_unlock(&max9611->lock);
+ return ret;
+ }
+
+ *raw_val = ret;
+ mutex_unlock(&max9611->lock);
+
+ return 0;
+}
+
+/**
+ * max9611_read_csa_voltage() - read current sense amplifier output voltage
+ *
+ * Current sense amplifier output voltage is read through a configurable
+ * 1x, 4x or 8x gain.
+ * Start with plain 1x gain, and adjust gain control properly until a
+ * meaningful value is read from ADC output.
+ *
+ * @max9611: max9611 device
+ * @adc_raw: raw value read from ADC output
+ * @csa_gain: gain configuration option selector
+ */
+static int max9611_read_csa_voltage(struct max9611_dev *max9611,
+ u16 *adc_raw,
+ enum max9611_csa_gain *csa_gain)
+{
+ enum max9611_conf_ids gain_selectors[] = {
+ CONF_SENSE_1x,
+ CONF_SENSE_4x,
+ CONF_SENSE_8x
+ };
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) {
+ ret = max9611_read_single(max9611, gain_selectors[i], adc_raw);
+ if (ret)
+ return ret;
+
+ if (*adc_raw > 0) {
+ *csa_gain = (enum max9611_csa_gain)gain_selectors[i];
+ return 0;
+ }
+ }
+
+ return -EIO;
+}
+
+static int max9611_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max9611_dev *dev = iio_priv(indio_dev);
+ enum max9611_csa_gain gain_selector;
+ const unsigned int *csa_gain;
+ u16 adc_data;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+
+ switch (chan->address) {
+ case MAX9611_CHAN_TEMPERATURE:
+ ret = max9611_read_single(dev, CONF_TEMP,
+ &adc_data);
+ if (ret)
+ return -EINVAL;
+
+ *val = MAX9611_TEMP_RAW(adc_data);
+ return IIO_VAL_INT;
+
+ case MAX9611_CHAN_VOLTAGE_INPUT:
+ ret = max9611_read_single(dev, CONF_IN_VOLT,
+ &adc_data);
+ if (ret)
+ return -EINVAL;
+
+ *val = MAX9611_VOLTAGE_RAW(adc_data);
+ return IIO_VAL_INT;
+ }
+
+ break;
+
+ case IIO_CHAN_INFO_OFFSET:
+ /* MAX9611_CHAN_VOLTAGE_INPUT */
+ *val = MAX9611_CIM_OFFSET_RAW;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+
+ switch (chan->address) {
+ case MAX9611_CHAN_TEMPERATURE:
+ *val = MAX9611_TEMP_SCALE_NUM;
+ *val2 = MAX9611_TEMP_SCALE_DIV;
+
+ return IIO_VAL_FRACTIONAL;
+
+ case MAX9611_CHAN_VOLTAGE_INPUT:
+ *val = MAX9611_CIM_LSB_mV;
+
+ return IIO_VAL_INT;
+ }
+
+ break;
+
+ case IIO_CHAN_INFO_PROCESSED:
+
+ switch (chan->address) {
+ case MAX9611_CHAN_VOLTAGE_SENSE:
+ /*
+ * processed (mV): (raw - offset) * LSB (nV) / 10^6
+ *
+ * Even if max9611 can output raw csa voltage readings,
+ * use a produced value as scale depends on gain.
+ */
+ ret = max9611_read_csa_voltage(dev, &adc_data,
+ &gain_selector);
+ if (ret)
+ return -EINVAL;
+
+ csa_gain = max9611_gain_conf[gain_selector];
+
+ adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
+ *val = MAX9611_VOLTAGE_RAW(adc_data) *
+ csa_gain[CSA_GAIN_LSB_nV];
+ *val2 = 1000000;
+
+ return IIO_VAL_FRACTIONAL;
+
+ case MAX9611_CHAN_CURRENT_LOAD:
+ /* processed (mA): Vcsa (nV) / Rshunt (uOhm) */
+ ret = max9611_read_csa_voltage(dev, &adc_data,
+ &gain_selector);
+ if (ret)
+ return -EINVAL;
+
+ csa_gain = max9611_gain_conf[gain_selector];
+
+ adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
+ *val = MAX9611_VOLTAGE_RAW(adc_data) *
+ csa_gain[CSA_GAIN_LSB_nV];
+ *val2 = dev->shunt_resistor_uohm;
+
+ return IIO_VAL_FRACTIONAL;
+
+ case MAX9611_CHAN_POWER_LOAD:
+ /*
+ * processed (mW): Vin (mV) * Vcsa (uV) /
+ * Rshunt (uOhm)
+ */
+ ret = max9611_read_single(dev, CONF_IN_VOLT,
+ &adc_data);
+ if (ret)
+ return -EINVAL;
+
+ adc_data -= MAX9611_CIM_OFFSET_RAW;
+ *val = MAX9611_VOLTAGE_RAW(adc_data) *
+ MAX9611_CIM_LSB_mV;
+
+ ret = max9611_read_csa_voltage(dev, &adc_data,
+ &gain_selector);
+ if (ret)
+ return -EINVAL;
+
+ csa_gain = max9611_gain_conf[gain_selector];
+
+ /* divide by 10^3 here to avoid 32bit overflow */
+ adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
+ *val *= MAX9611_VOLTAGE_RAW(adc_data) *
+ csa_gain[CSA_GAIN_LSB_nV] / 1000;
+ *val2 = dev->shunt_resistor_uohm;
+
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t max9611_shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev));
+ unsigned int i, r;
+
+ i = max9611->shunt_resistor_uohm / 1000000;
+ r = max9611->shunt_resistor_uohm % 1000000;
+
+ return sysfs_emit(buf, "%u.%06u\n", i, r);
+}
+
+static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444,
+ max9611_shunt_resistor_show, NULL, 0);
+static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444,
+ max9611_shunt_resistor_show, NULL, 0);
+
+static struct attribute *max9611_attributes[] = {
+ &iio_dev_attr_in_power_shunt_resistor.dev_attr.attr,
+ &iio_dev_attr_in_current_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max9611_attribute_group = {
+ .attrs = max9611_attributes,
+};
+
+static const struct iio_info indio_info = {
+ .read_raw = max9611_read_raw,
+ .attrs = &max9611_attribute_group,
+};
+
+static int max9611_init(struct max9611_dev *max9611)
+{
+ struct i2c_client *client = max9611->i2c_client;
+ u16 regval;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ dev_err(max9611->dev,
+ "I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n");
+ return -EINVAL;
+ }
+
+ /* Make sure die temperature is in range to test communications. */
+ ret = max9611_read_single(max9611, CONF_TEMP, &regval);
+ if (ret)
+ return ret;
+
+ regval &= MAX9611_TEMP_MASK;
+
+ if ((regval > MAX9611_TEMP_MAX_POS &&
+ regval < MAX9611_TEMP_MIN_NEG) ||
+ regval > MAX9611_TEMP_MAX_NEG) {
+ dev_err(max9611->dev,
+ "Invalid value received from ADC 0x%4x: aborting\n",
+ regval);
+ return -EIO;
+ }
+
+ /* Mux shall be zeroed back before applying other configurations */
+ ret = i2c_smbus_write_byte_data(max9611->i2c_client,
+ MAX9611_REG_CTRL1, 0);
+ if (ret) {
+ dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
+ MAX9611_REG_CTRL1, 0);
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte_data(max9611->i2c_client,
+ MAX9611_REG_CTRL2, 0);
+ if (ret) {
+ dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
+ MAX9611_REG_CTRL2, 0);
+ return ret;
+ }
+ usleep_range(MAX9611_CONV_TIME_US_RANGE);
+
+ return 0;
+}
+
+static const struct of_device_id max9611_of_table[] = {
+ {.compatible = "maxim,max9611", .data = "max9611"},
+ {.compatible = "maxim,max9612", .data = "max9612"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, max9611_of_table);
+static int max9611_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const char * const shunt_res_prop = "shunt-resistor-micro-ohms";
+ struct max9611_dev *max9611;
+ struct iio_dev *indio_dev;
+ struct device *dev = &client->dev;
+ unsigned int of_shunt;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ max9611 = iio_priv(indio_dev);
+ max9611->dev = dev;
+ max9611->i2c_client = client;
+ mutex_init(&max9611->lock);
+
+ ret = device_property_read_u32(dev, shunt_res_prop, &of_shunt);
+ if (ret) {
+ dev_err(dev, "Missing %s property for %pfw node\n",
+ shunt_res_prop, dev_fwnode(dev));
+ return ret;
+ }
+ max9611->shunt_resistor_uohm = of_shunt;
+
+ ret = max9611_init(max9611);
+ if (ret)
+ return ret;
+
+ indio_dev->name = device_get_match_data(dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &indio_info;
+ indio_dev->channels = max9611_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max9611_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct i2c_driver max9611_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = max9611_of_table,
+ },
+ .probe = max9611_probe,
+};
+module_i2c_driver(max9611_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
+MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
new file mode 100644
index 000000000..f3b81798b
--- /dev/null
+++ b/drivers/iio/adc/mcp320x.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
+ * Copyright (C) 2014 Rose Technology
+ * Allan Bendorff Jensen <abj@rosetechnology.dk>
+ * Soren Andersen <san@rosetechnology.dk>
+ *
+ * Driver for following ADC chips from Microchip Technology's:
+ * 10 Bit converter
+ * MCP3001
+ * MCP3002
+ * MCP3004
+ * MCP3008
+ * ------------
+ * 12 bit converter
+ * MCP3201
+ * MCP3202
+ * MCP3204
+ * MCP3208
+ * ------------
+ * 13 bit converter
+ * MCP3301
+ * ------------
+ * 22 bit converter
+ * MCP3550
+ * MCP3551
+ * MCP3553
+ *
+ * Datasheet can be found here:
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf mcp3301
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21950D.pdf mcp3550/1/3
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+
+enum {
+ mcp3001,
+ mcp3002,
+ mcp3004,
+ mcp3008,
+ mcp3201,
+ mcp3202,
+ mcp3204,
+ mcp3208,
+ mcp3301,
+ mcp3550_50,
+ mcp3550_60,
+ mcp3551,
+ mcp3553,
+};
+
+struct mcp320x_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int resolution;
+ unsigned int conv_time; /* usec */
+};
+
+/**
+ * struct mcp320x - Microchip SPI ADC instance
+ * @spi: SPI slave (parent of the IIO device)
+ * @msg: SPI message to select a channel and receive a value from the ADC
+ * @transfer: SPI transfers used by @msg
+ * @start_conv_msg: SPI message to start a conversion by briefly asserting CS
+ * @start_conv_transfer: SPI transfer used by @start_conv_msg
+ * @reg: regulator generating Vref
+ * @lock: protects read sequences
+ * @chip_info: ADC properties
+ * @tx_buf: buffer for @transfer[0] (not used on single-channel converters)
+ * @rx_buf: buffer for @transfer[1]
+ */
+struct mcp320x {
+ struct spi_device *spi;
+ struct spi_message msg;
+ struct spi_transfer transfer[2];
+ struct spi_message start_conv_msg;
+ struct spi_transfer start_conv_transfer;
+
+ struct regulator *reg;
+ struct mutex lock;
+ const struct mcp320x_chip_info *chip_info;
+
+ u8 tx_buf __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[4];
+};
+
+static int mcp320x_channel_to_tx_data(int device_index,
+ const unsigned int channel, bool differential)
+{
+ int start_bit = 1;
+
+ switch (device_index) {
+ case mcp3002:
+ case mcp3202:
+ return ((start_bit << 4) | (!differential << 3) |
+ (channel << 2));
+ case mcp3004:
+ case mcp3204:
+ case mcp3008:
+ case mcp3208:
+ return ((start_bit << 6) | (!differential << 5) |
+ (channel << 2));
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
+ bool differential, int device_index, int *val)
+{
+ int ret;
+
+ if (adc->chip_info->conv_time) {
+ ret = spi_sync(adc->spi, &adc->start_conv_msg);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(adc->chip_info->conv_time,
+ adc->chip_info->conv_time + 100);
+ }
+
+ memset(&adc->rx_buf, 0, sizeof(adc->rx_buf));
+ if (adc->chip_info->num_channels > 1)
+ adc->tx_buf = mcp320x_channel_to_tx_data(device_index, channel,
+ differential);
+
+ ret = spi_sync(adc->spi, &adc->msg);
+ if (ret < 0)
+ return ret;
+
+ switch (device_index) {
+ case mcp3001:
+ *val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+ return 0;
+ case mcp3002:
+ case mcp3004:
+ case mcp3008:
+ *val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+ return 0;
+ case mcp3201:
+ *val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+ return 0;
+ case mcp3202:
+ case mcp3204:
+ case mcp3208:
+ *val = (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+ return 0;
+ case mcp3301:
+ *val = sign_extend32((adc->rx_buf[0] & 0x1f) << 8
+ | adc->rx_buf[1], 12);
+ return 0;
+ case mcp3550_50:
+ case mcp3550_60:
+ case mcp3551:
+ case mcp3553: {
+ u32 raw = be32_to_cpup((__be32 *)adc->rx_buf);
+
+ if (!(adc->spi->mode & SPI_CPOL))
+ raw <<= 1; /* strip Data Ready bit in SPI mode 0,0 */
+
+ /*
+ * If the input is within -vref and vref, bit 21 is the sign.
+ * Up to 12% overrange or underrange are allowed, in which case
+ * bit 23 is the sign and bit 0 to 21 is the value.
+ */
+ raw >>= 8;
+ if (raw & BIT(22) && raw & BIT(23))
+ return -EIO; /* cannot have overrange AND underrange */
+ else if (raw & BIT(22))
+ raw &= ~BIT(22); /* overrange */
+ else if (raw & BIT(23) || raw & BIT(21))
+ raw |= GENMASK(31, 22); /* underrange or negative */
+
+ *val = (s32)raw;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp320x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp320x *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+ int device_index = 0;
+
+ mutex_lock(&adc->lock);
+
+ device_index = spi_get_device_id(adc->spi)->driver_data;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp320x_adc_conversion(adc, channel->address,
+ channel->differential, device_index, val);
+ if (ret < 0)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc->reg);
+ if (ret < 0)
+ goto out;
+
+ /* convert regulator output voltage to mV */
+ *val = ret / 1000;
+ *val2 = adc->chip_info->resolution;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+#define MCP320X_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (num), \
+ .address = (num), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ }
+
+#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .address = (chan1), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ }
+
+static const struct iio_chan_spec mcp3201_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+};
+
+static const struct iio_chan_spec mcp3202_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
+};
+
+static const struct iio_chan_spec mcp3204_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL(2),
+ MCP320X_VOLTAGE_CHANNEL(3),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
+};
+
+static const struct iio_chan_spec mcp3208_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL(2),
+ MCP320X_VOLTAGE_CHANNEL(3),
+ MCP320X_VOLTAGE_CHANNEL(4),
+ MCP320X_VOLTAGE_CHANNEL(5),
+ MCP320X_VOLTAGE_CHANNEL(6),
+ MCP320X_VOLTAGE_CHANNEL(7),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6),
+};
+
+static const struct iio_info mcp320x_info = {
+ .read_raw = mcp320x_read_raw,
+};
+
+static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
+ [mcp3001] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 10
+ },
+ [mcp3002] = {
+ .channels = mcp3202_channels,
+ .num_channels = ARRAY_SIZE(mcp3202_channels),
+ .resolution = 10
+ },
+ [mcp3004] = {
+ .channels = mcp3204_channels,
+ .num_channels = ARRAY_SIZE(mcp3204_channels),
+ .resolution = 10
+ },
+ [mcp3008] = {
+ .channels = mcp3208_channels,
+ .num_channels = ARRAY_SIZE(mcp3208_channels),
+ .resolution = 10
+ },
+ [mcp3201] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 12
+ },
+ [mcp3202] = {
+ .channels = mcp3202_channels,
+ .num_channels = ARRAY_SIZE(mcp3202_channels),
+ .resolution = 12
+ },
+ [mcp3204] = {
+ .channels = mcp3204_channels,
+ .num_channels = ARRAY_SIZE(mcp3204_channels),
+ .resolution = 12
+ },
+ [mcp3208] = {
+ .channels = mcp3208_channels,
+ .num_channels = ARRAY_SIZE(mcp3208_channels),
+ .resolution = 12
+ },
+ [mcp3301] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 13
+ },
+ [mcp3550_50] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 21,
+ /* 2% max deviation + 144 clock periods to exit shutdown */
+ .conv_time = 80000 * 1.02 + 144000 / 102.4,
+ },
+ [mcp3550_60] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 21,
+ .conv_time = 66670 * 1.02 + 144000 / 122.88,
+ },
+ [mcp3551] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 21,
+ .conv_time = 73100 * 1.02 + 144000 / 112.64,
+ },
+ [mcp3553] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 21,
+ .conv_time = 16670 * 1.02 + 144000 / 122.88,
+ },
+};
+
+static int mcp320x_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp320x *adc;
+ const struct mcp320x_chip_info *chip_info;
+ int ret, device_index;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp320x_info;
+ spi_set_drvdata(spi, indio_dev);
+
+ device_index = spi_get_device_id(spi)->driver_data;
+ chip_info = &mcp320x_chip_infos[device_index];
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
+
+ adc->chip_info = chip_info;
+
+ adc->transfer[0].tx_buf = &adc->tx_buf;
+ adc->transfer[0].len = sizeof(adc->tx_buf);
+ adc->transfer[1].rx_buf = adc->rx_buf;
+ adc->transfer[1].len = DIV_ROUND_UP(chip_info->resolution, 8);
+
+ if (chip_info->num_channels == 1)
+ /* single-channel converters are rx only (no MOSI pin) */
+ spi_message_init_with_transfers(&adc->msg,
+ &adc->transfer[1], 1);
+ else
+ spi_message_init_with_transfers(&adc->msg, adc->transfer,
+ ARRAY_SIZE(adc->transfer));
+
+ switch (device_index) {
+ case mcp3550_50:
+ case mcp3550_60:
+ case mcp3551:
+ case mcp3553:
+ /* rx len increases from 24 to 25 bit in SPI mode 0,0 */
+ if (!(spi->mode & SPI_CPOL))
+ adc->transfer[1].len++;
+
+ /* conversions are started by asserting CS pin for 8 usec */
+ adc->start_conv_transfer.delay.value = 8;
+ adc->start_conv_transfer.delay.unit = SPI_DELAY_UNIT_USECS;
+ spi_message_init_with_transfers(&adc->start_conv_msg,
+ &adc->start_conv_transfer, 1);
+
+ /*
+ * If CS was previously kept low (continuous conversion mode)
+ * and then changed to high, the chip is in shutdown.
+ * Sometimes it fails to wake from shutdown and clocks out
+ * only 0xffffff. The magic sequence of performing two
+ * conversions without delay between them resets the chip
+ * and ensures all subsequent conversions succeed.
+ */
+ mcp320x_adc_conversion(adc, 0, 1, device_index, &ret);
+ mcp320x_adc_conversion(adc, 0, 1, device_index, &ret);
+ }
+
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return PTR_ERR(adc->reg);
+
+ ret = regulator_enable(adc->reg);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&adc->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto reg_disable;
+
+ return 0;
+
+reg_disable:
+ regulator_disable(adc->reg);
+
+ return ret;
+}
+
+static void mcp320x_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct mcp320x *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(adc->reg);
+}
+
+static const struct of_device_id mcp320x_dt_ids[] = {
+ /* NOTE: The use of compatibles with no vendor prefix is deprecated. */
+ { .compatible = "mcp3001" },
+ { .compatible = "mcp3002" },
+ { .compatible = "mcp3004" },
+ { .compatible = "mcp3008" },
+ { .compatible = "mcp3201" },
+ { .compatible = "mcp3202" },
+ { .compatible = "mcp3204" },
+ { .compatible = "mcp3208" },
+ { .compatible = "mcp3301" },
+ { .compatible = "microchip,mcp3001" },
+ { .compatible = "microchip,mcp3002" },
+ { .compatible = "microchip,mcp3004" },
+ { .compatible = "microchip,mcp3008" },
+ { .compatible = "microchip,mcp3201" },
+ { .compatible = "microchip,mcp3202" },
+ { .compatible = "microchip,mcp3204" },
+ { .compatible = "microchip,mcp3208" },
+ { .compatible = "microchip,mcp3301" },
+ { .compatible = "microchip,mcp3550-50" },
+ { .compatible = "microchip,mcp3550-60" },
+ { .compatible = "microchip,mcp3551" },
+ { .compatible = "microchip,mcp3553" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
+
+static const struct spi_device_id mcp320x_id[] = {
+ { "mcp3001", mcp3001 },
+ { "mcp3002", mcp3002 },
+ { "mcp3004", mcp3004 },
+ { "mcp3008", mcp3008 },
+ { "mcp3201", mcp3201 },
+ { "mcp3202", mcp3202 },
+ { "mcp3204", mcp3204 },
+ { "mcp3208", mcp3208 },
+ { "mcp3301", mcp3301 },
+ { "mcp3550-50", mcp3550_50 },
+ { "mcp3550-60", mcp3550_60 },
+ { "mcp3551", mcp3551 },
+ { "mcp3553", mcp3553 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp320x_id);
+
+static struct spi_driver mcp320x_driver = {
+ .driver = {
+ .name = "mcp320x",
+ .of_match_table = mcp320x_dt_ids,
+ },
+ .probe = mcp320x_probe,
+ .remove = mcp320x_remove,
+ .id_table = mcp320x_id,
+};
+module_spi_driver(mcp320x_driver);
+
+MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
+MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
new file mode 100644
index 000000000..da353dcb1
--- /dev/null
+++ b/drivers/iio/adc/mcp3422.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family
+ *
+ * Copyright (C) 2013, Angelo Compagnucci
+ * Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
+ *
+ * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf
+ * https://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf
+ *
+ * This driver exports the value of analog input voltage to sysfs, the
+ * voltage unit is nV.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Masks */
+#define MCP3422_CHANNEL_MASK 0x60
+#define MCP3422_PGA_MASK 0x03
+#define MCP3422_SRATE_MASK 0x0C
+#define MCP3422_SRATE_240 0x0
+#define MCP3422_SRATE_60 0x1
+#define MCP3422_SRATE_15 0x2
+#define MCP3422_SRATE_3 0x3
+#define MCP3422_PGA_1 0
+#define MCP3422_PGA_2 1
+#define MCP3422_PGA_4 2
+#define MCP3422_PGA_8 3
+#define MCP3422_CONT_SAMPLING 0x10
+
+#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5)
+#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK)
+#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2)
+
+#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK)
+#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK)
+#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK)
+
+#define MCP3422_CHAN(_index) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ }
+
+static const int mcp3422_scales[4][4] = {
+ { 1000000, 500000, 250000, 125000 },
+ { 250000, 125000, 62500, 31250 },
+ { 62500, 31250, 15625, 7812 },
+ { 15625, 7812, 3906, 1953 } };
+
+/* Constant msleep times for data acquisitions */
+static const int mcp3422_read_times[4] = {
+ [MCP3422_SRATE_240] = 1000 / 240,
+ [MCP3422_SRATE_60] = 1000 / 60,
+ [MCP3422_SRATE_15] = 1000 / 15,
+ [MCP3422_SRATE_3] = 1000 / 3 };
+
+/* sample rates to integer conversion table */
+static const int mcp3422_sample_rates[4] = {
+ [MCP3422_SRATE_240] = 240,
+ [MCP3422_SRATE_60] = 60,
+ [MCP3422_SRATE_15] = 15,
+ [MCP3422_SRATE_3] = 3 };
+
+/* sample rates to sign extension table */
+static const int mcp3422_sign_extend[4] = {
+ [MCP3422_SRATE_240] = 11,
+ [MCP3422_SRATE_60] = 13,
+ [MCP3422_SRATE_15] = 15,
+ [MCP3422_SRATE_3] = 17 };
+
+/* Client data (each client gets its own) */
+struct mcp3422 {
+ struct i2c_client *i2c;
+ u8 id;
+ u8 config;
+ u8 pga[4];
+ struct mutex lock;
+};
+
+static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig)
+{
+ int ret;
+
+ ret = i2c_master_send(adc->i2c, &newconfig, 1);
+ if (ret > 0) {
+ adc->config = newconfig;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config)
+{
+ int ret = 0;
+ u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
+ u8 buf[4] = {0, 0, 0, 0};
+ u32 temp;
+
+ if (sample_rate == MCP3422_SRATE_3) {
+ ret = i2c_master_recv(adc->i2c, buf, 4);
+ temp = get_unaligned_be24(&buf[0]);
+ *config = buf[3];
+ } else {
+ ret = i2c_master_recv(adc->i2c, buf, 3);
+ temp = get_unaligned_be16(&buf[0]);
+ *config = buf[2];
+ }
+
+ *value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]);
+
+ return ret;
+}
+
+static int mcp3422_read_channel(struct mcp3422 *adc,
+ struct iio_chan_spec const *channel, int *value)
+{
+ int ret;
+ u8 config;
+ u8 req_channel = channel->channel;
+
+ mutex_lock(&adc->lock);
+
+ if (req_channel != MCP3422_CHANNEL(adc->config)) {
+ config = adc->config;
+ config &= ~MCP3422_CHANNEL_MASK;
+ config |= MCP3422_CHANNEL_VALUE(req_channel);
+ config &= ~MCP3422_PGA_MASK;
+ config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
+ ret = mcp3422_update_config(adc, config);
+ if (ret < 0) {
+ mutex_unlock(&adc->lock);
+ return ret;
+ }
+ msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]);
+ }
+
+ ret = mcp3422_read(adc, value, &config);
+
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static int mcp3422_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *val1,
+ int *val2, long mask)
+{
+ struct mcp3422 *adc = iio_priv(iio);
+ int err;
+
+ u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
+ u8 pga = MCP3422_PGA(adc->config);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ err = mcp3422_read_channel(adc, channel, val1);
+ if (err < 0)
+ return -EINVAL;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+
+ *val1 = 0;
+ *val2 = mcp3422_scales[sample_rate][pga];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)];
+ return IIO_VAL_INT;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int mcp3422_write_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int val1,
+ int val2, long mask)
+{
+ struct mcp3422 *adc = iio_priv(iio);
+ u8 temp;
+ u8 config = adc->config;
+ u8 req_channel = channel->channel;
+ u8 sample_rate = MCP3422_SAMPLE_RATE(config);
+ u8 i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val1 != 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) {
+ if (val2 == mcp3422_scales[sample_rate][i]) {
+ adc->pga[req_channel] = i;
+
+ config &= ~MCP3422_CHANNEL_MASK;
+ config |= MCP3422_CHANNEL_VALUE(req_channel);
+ config &= ~MCP3422_PGA_MASK;
+ config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
+
+ return mcp3422_update_config(adc, config);
+ }
+ }
+ return -EINVAL;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (val1) {
+ case 240:
+ temp = MCP3422_SRATE_240;
+ break;
+ case 60:
+ temp = MCP3422_SRATE_60;
+ break;
+ case 15:
+ temp = MCP3422_SRATE_15;
+ break;
+ case 3:
+ if (adc->id > 4)
+ return -EINVAL;
+ temp = MCP3422_SRATE_3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ config &= ~MCP3422_CHANNEL_MASK;
+ config |= MCP3422_CHANNEL_VALUE(req_channel);
+ config &= ~MCP3422_SRATE_MASK;
+ config |= MCP3422_SAMPLE_RATE_VALUE(temp);
+
+ return mcp3422_update_config(adc, config);
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int mcp3422_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_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t mcp3422_show_samp_freqs(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
+
+ if (adc->id > 4)
+ return sprintf(buf, "240 60 15\n");
+
+ return sprintf(buf, "240 60 15 3\n");
+}
+
+static ssize_t mcp3422_show_scales(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
+ u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
+
+ return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n",
+ mcp3422_scales[sample_rate][0],
+ mcp3422_scales[sample_rate][1],
+ mcp3422_scales[sample_rate][2],
+ mcp3422_scales[sample_rate][3]);
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+ mcp3422_show_samp_freqs, NULL, 0);
+static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
+ mcp3422_show_scales, NULL, 0);
+
+static struct attribute *mcp3422_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group mcp3422_attribute_group = {
+ .attrs = mcp3422_attributes,
+};
+
+static const struct iio_chan_spec mcp3421_channels[] = {
+ MCP3422_CHAN(0),
+};
+
+static const struct iio_chan_spec mcp3422_channels[] = {
+ MCP3422_CHAN(0),
+ MCP3422_CHAN(1),
+};
+
+static const struct iio_chan_spec mcp3424_channels[] = {
+ MCP3422_CHAN(0),
+ MCP3422_CHAN(1),
+ MCP3422_CHAN(2),
+ MCP3422_CHAN(3),
+};
+
+static const struct iio_info mcp3422_info = {
+ .read_raw = mcp3422_read_raw,
+ .write_raw = mcp3422_write_raw,
+ .write_raw_get_fmt = mcp3422_write_raw_get_fmt,
+ .attrs = &mcp3422_attribute_group,
+};
+
+static int mcp3422_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct mcp3422 *adc;
+ int err;
+ u8 config;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->i2c = client;
+ adc->id = (u8)(id->driver_data);
+
+ mutex_init(&adc->lock);
+
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp3422_info;
+
+ switch (adc->id) {
+ case 1:
+ case 5:
+ indio_dev->channels = mcp3421_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
+ break;
+ case 2:
+ case 3:
+ case 6:
+ case 7:
+ indio_dev->channels = mcp3422_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels);
+ break;
+ case 4:
+ case 8:
+ indio_dev->channels = mcp3424_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels);
+ break;
+ }
+
+ /* meaningful default configuration */
+ config = (MCP3422_CONT_SAMPLING
+ | MCP3422_CHANNEL_VALUE(0)
+ | MCP3422_PGA_VALUE(MCP3422_PGA_1)
+ | MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240));
+ err = mcp3422_update_config(adc, config);
+ if (err < 0)
+ return err;
+
+ err = devm_iio_device_register(&client->dev, indio_dev);
+ if (err < 0)
+ return err;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mcp3422_id[] = {
+ { "mcp3421", 1 },
+ { "mcp3422", 2 },
+ { "mcp3423", 3 },
+ { "mcp3424", 4 },
+ { "mcp3425", 5 },
+ { "mcp3426", 6 },
+ { "mcp3427", 7 },
+ { "mcp3428", 8 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp3422_id);
+
+static const struct of_device_id mcp3422_of_match[] = {
+ { .compatible = "mcp3422" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp3422_of_match);
+
+static struct i2c_driver mcp3422_driver = {
+ .driver = {
+ .name = "mcp3422",
+ .of_match_table = mcp3422_of_match,
+ },
+ .probe = mcp3422_probe,
+ .id_table = mcp3422_id,
+};
+module_i2c_driver(mcp3422_driver);
+
+MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
+MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
new file mode 100644
index 000000000..76b334f5a
--- /dev/null
+++ b/drivers/iio/adc/mcp3911.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Microchip MCP3911, Two-channel Analog Front End
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/trigger.h>
+
+#include <asm/unaligned.h>
+
+#define MCP3911_REG_CHANNEL0 0x00
+#define MCP3911_REG_CHANNEL1 0x03
+#define MCP3911_REG_MOD 0x06
+#define MCP3911_REG_PHASE 0x07
+#define MCP3911_REG_GAIN 0x09
+
+#define MCP3911_REG_STATUSCOM 0x0a
+#define MCP3911_STATUSCOM_DRHIZ BIT(12)
+#define MCP3911_STATUSCOM_READ GENMASK(7, 6)
+#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
+#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
+#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
+#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1)
+
+#define MCP3911_REG_CONFIG 0x0c
+#define MCP3911_CONFIG_CLKEXT BIT(1)
+#define MCP3911_CONFIG_VREFEXT BIT(2)
+#define MCP3911_CONFIG_OSR GENMASK(13, 11)
+
+#define MCP3911_REG_OFFCAL_CH0 0x0e
+#define MCP3911_REG_GAINCAL_CH0 0x11
+#define MCP3911_REG_OFFCAL_CH1 0x14
+#define MCP3911_REG_GAINCAL_CH1 0x17
+#define MCP3911_REG_VREFCAL 0x1a
+
+#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3)
+#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6)
+
+/* Internal voltage reference in mV */
+#define MCP3911_INT_VREF_MV 1200
+
+#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 6) | (1 << 0)) & 0xff)
+#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 6) | (0 << 0)) & 0xff)
+#define MCP3911_REG_MASK GENMASK(4, 1)
+
+#define MCP3911_NUM_CHANNELS 2
+
+static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 };
+
+struct mcp3911 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *vref;
+ struct clk *clki;
+ u32 dev_addr;
+ struct iio_trigger *trig;
+ struct {
+ u32 channels[MCP3911_NUM_CHANNELS];
+ s64 ts __aligned(8);
+ } scan;
+
+ u8 tx_buf __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[MCP3911_NUM_CHANNELS * 3];
+};
+
+static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
+{
+ int ret;
+
+ reg = MCP3911_REG_READ(reg, adc->dev_addr);
+ ret = spi_write_then_read(adc->spi, &reg, 1, val, len);
+ if (ret < 0)
+ return ret;
+
+ be32_to_cpus(val);
+ *val >>= ((4 - len) * 8);
+ dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%lx\n", *val,
+ FIELD_GET(MCP3911_REG_MASK, reg));
+ return ret;
+}
+
+static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
+{
+ dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
+
+ val <<= (3 - len) * 8;
+ cpu_to_be32s(&val);
+ val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
+
+ return spi_write(adc->spi, &val, len + 1);
+}
+
+static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
+ u32 val, u8 len)
+{
+ u32 tmp;
+ int ret;
+
+ ret = mcp3911_read(adc, reg, &tmp, len);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ val |= tmp & ~mask;
+ return mcp3911_write(adc, reg, val, len);
+}
+
+static int mcp3911_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_OVERSAMPLING_RATIO:
+ return IIO_VAL_INT;
+ default:
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+}
+
+static int mcp3911_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *type = IIO_VAL_INT;
+ *vals = mcp3911_osr_table;
+ *length = ARRAY_SIZE(mcp3911_osr_table);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp3911_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp3911_read(adc,
+ MCP3911_CHANNEL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ *val = sign_extend32(*val, 23);
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_OFFSET:
+ ret = mcp3911_read(adc,
+ MCP3911_OFFCAL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2);
+ if (ret)
+ goto out;
+
+ *val = FIELD_GET(MCP3911_CONFIG_OSR, *val);
+ *val = 32 << *val;
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (adc->vref) {
+ ret = regulator_get_voltage(adc->vref);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get vref voltage: %d\n",
+ ret);
+ goto out;
+ }
+
+ *val = ret / 1000;
+ } else {
+ *val = MCP3911_INT_VREF_MV;
+ }
+
+ /*
+ * For 24bit Conversion
+ * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5
+ * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5)
+ */
+
+ /* val2 = (2^23 * 1.5) */
+ *val2 = 12582912;
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+static int mcp3911_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int val,
+ int val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ if (val2 != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Write offset */
+ ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
+ 3);
+ if (ret)
+ goto out;
+
+ /* Enable offset*/
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
+ MCP3911_STATUSCOM_EN_OFFCAL,
+ MCP3911_STATUSCOM_EN_OFFCAL, 2);
+ break;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ for (int i = 0; i < ARRAY_SIZE(mcp3911_osr_table); i++) {
+ if (val == mcp3911_osr_table[i]) {
+ val = FIELD_PREP(MCP3911_CONFIG_OSR, i);
+ ret = mcp3911_update(adc, MCP3911_REG_CONFIG, MCP3911_CONFIG_OSR,
+ val, 2);
+ break;
+ }
+ }
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+#define MCP3911_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = idx, \
+ .scan_index = idx, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 24, \
+ .storagebits = 32, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec mcp3911_channels[] = {
+ MCP3911_CHAN(0),
+ MCP3911_CHAN(1),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &adc->tx_buf,
+ .len = 1,
+ }, {
+ .rx_buf = adc->rx_buf,
+ .len = sizeof(adc->rx_buf),
+ },
+ };
+ int scan_index;
+ int i = 0;
+ int ret;
+
+ mutex_lock(&adc->lock);
+ adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr);
+ ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer));
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index];
+
+ 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));
+out:
+ mutex_unlock(&adc->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info mcp3911_info = {
+ .read_raw = mcp3911_read_raw,
+ .write_raw = mcp3911_write_raw,
+ .read_avail = mcp3911_read_avail,
+ .write_raw_get_fmt = mcp3911_write_raw_get_fmt,
+};
+
+static int mcp3911_config(struct mcp3911 *adc)
+{
+ struct device *dev = &adc->spi->dev;
+ u32 regval;
+ int ret;
+
+ ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr);
+
+ /*
+ * Fallback to "device-addr" due to historical mismatch between
+ * dt-bindings and implementation
+ */
+ if (ret)
+ device_property_read_u32(dev, "device-addr", &adc->dev_addr);
+ if (adc->dev_addr > 3) {
+ dev_err(&adc->spi->dev,
+ "invalid device address (%i). Must be in range 0-3.\n",
+ adc->dev_addr);
+ return -EINVAL;
+ }
+ dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
+
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &regval, 2);
+ if (ret)
+ return ret;
+
+ regval &= ~MCP3911_CONFIG_VREFEXT;
+ if (adc->vref) {
+ dev_dbg(&adc->spi->dev, "use external voltage reference\n");
+ regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1);
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use internal voltage reference (1.2V)\n");
+ regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0);
+ }
+
+ regval &= ~MCP3911_CONFIG_CLKEXT;
+ if (adc->clki) {
+ dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
+ regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1);
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use crystal oscillator as clocksource\n");
+ regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0);
+ }
+
+ ret = mcp3911_write(adc, MCP3911_REG_CONFIG, regval, 2);
+ if (ret)
+ return ret;
+
+ ret = mcp3911_read(adc, MCP3911_REG_STATUSCOM, &regval, 2);
+ if (ret)
+ return ret;
+
+ /* Address counter incremented, cycle through register types */
+ regval &= ~MCP3911_STATUSCOM_READ;
+ regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02);
+
+ return mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2);
+}
+
+static void mcp3911_cleanup_regulator(void *vref)
+{
+ regulator_disable(vref);
+}
+
+static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ struct mcp3911 *adc = iio_trigger_get_drvdata(trig);
+
+ if (enable)
+ enable_irq(adc->spi->irq);
+ else
+ disable_irq(adc->spi->irq);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mcp3911_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+ .set_trigger_state = mcp3911_set_trigger_state,
+};
+
+static int mcp3911_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp3911 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
+ if (IS_ERR(adc->vref)) {
+ if (PTR_ERR(adc->vref) == -ENODEV) {
+ adc->vref = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get regulator (%ld)\n",
+ PTR_ERR(adc->vref));
+ return PTR_ERR(adc->vref);
+ }
+
+ } else {
+ ret = regulator_enable(adc->vref);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev,
+ mcp3911_cleanup_regulator, adc->vref);
+ if (ret)
+ return ret;
+ }
+
+ adc->clki = devm_clk_get_enabled(&adc->spi->dev, NULL);
+ if (IS_ERR(adc->clki)) {
+ if (PTR_ERR(adc->clki) == -ENOENT) {
+ adc->clki = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get adc clk (%ld)\n",
+ PTR_ERR(adc->clki));
+ return PTR_ERR(adc->clki);
+ }
+ }
+
+ ret = mcp3911_config(adc);
+ if (ret)
+ return ret;
+
+ if (device_property_read_bool(&adc->spi->dev, "microchip,data-ready-hiz"))
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ,
+ 0, 2);
+ else
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ,
+ MCP3911_STATUSCOM_DRHIZ, 2);
+ if (ret)
+ return ret;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp3911_info;
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->channels = mcp3911_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
+
+ mutex_init(&adc->lock);
+
+ if (spi->irq > 0) {
+ adc->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!adc->trig)
+ return -ENOMEM;
+
+ adc->trig->ops = &mcp3911_trigger_ops;
+ iio_trigger_set_drvdata(adc->trig, adc);
+ ret = devm_iio_trigger_register(&spi->dev, adc->trig);
+ if (ret)
+ return ret;
+
+ /*
+ * The device generates interrupts as long as it is powered up.
+ * Some platforms might not allow the option to power it down so
+ * don't enable the interrupt to avoid extra load on the system.
+ */
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ &iio_trigger_generic_data_rdy_poll, IRQF_NO_AUTOEN | IRQF_ONESHOT,
+ indio_dev->name, adc->trig);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ NULL,
+ mcp3911_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&adc->spi->dev, indio_dev);
+}
+
+static const struct of_device_id mcp3911_dt_ids[] = {
+ { .compatible = "microchip,mcp3911" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
+
+static const struct spi_device_id mcp3911_id[] = {
+ { "mcp3911", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp3911_id);
+
+static struct spi_driver mcp3911_driver = {
+ .driver = {
+ .name = "mcp3911",
+ .of_match_table = mcp3911_dt_ids,
+ },
+ .probe = mcp3911_probe,
+ .id_table = mcp3911_id,
+};
+module_spi_driver(mcp3911_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
+MODULE_DESCRIPTION("Microchip Technology MCP3911");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
new file mode 100644
index 000000000..adc5ceaef
--- /dev/null
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MEN 16z188 Analog to Digial Converter
+ *
+ * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
+ * Author: Johannes Thumshirn <johannes.thumshirn@men.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mcb.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+
+#define Z188_ADC_MAX_CHAN 8
+#define Z188_ADC_GAIN 0x0700000
+#define Z188_MODE_VOLTAGE BIT(27)
+#define Z188_CFG_AUTO 0x1
+#define Z188_CTRL_REG 0x40
+
+#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc)
+#define ADC_OVR(x) ((x) & 0x1)
+
+struct z188_adc {
+ struct resource *mem;
+ void __iomem *base;
+};
+
+#define Z188_ADC_CHANNEL(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+static const struct iio_chan_spec z188_adc_iio_channels[] = {
+ Z188_ADC_CHANNEL(0),
+ Z188_ADC_CHANNEL(1),
+ Z188_ADC_CHANNEL(2),
+ Z188_ADC_CHANNEL(3),
+ Z188_ADC_CHANNEL(4),
+ Z188_ADC_CHANNEL(5),
+ Z188_ADC_CHANNEL(6),
+ Z188_ADC_CHANNEL(7),
+};
+
+static int z188_iio_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long info)
+{
+ struct z188_adc *adc = iio_priv(iio_dev);
+ int ret;
+ u16 tmp;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ tmp = readw(adc->base + chan->channel * 4);
+
+ if (ADC_OVR(tmp)) {
+ dev_info(&iio_dev->dev,
+ "Oversampling error on ADC channel %d\n",
+ chan->channel);
+ return -EIO;
+ }
+ *val = ADC_DATA(tmp);
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info z188_adc_info = {
+ .read_raw = &z188_iio_read_raw,
+};
+
+static void men_z188_config_channels(void __iomem *addr)
+{
+ int i;
+ u32 cfg;
+ u32 ctl;
+
+ ctl = readl(addr + Z188_CTRL_REG);
+ ctl |= Z188_CFG_AUTO;
+ writel(ctl, addr + Z188_CTRL_REG);
+
+ for (i = 0; i < Z188_ADC_MAX_CHAN; i++) {
+ cfg = readl(addr + i);
+ cfg &= ~Z188_ADC_GAIN;
+ cfg |= Z188_MODE_VOLTAGE;
+ writel(cfg, addr + i);
+ }
+}
+
+static int men_z188_probe(struct mcb_device *dev,
+ const struct mcb_device_id *id)
+{
+ struct z188_adc *adc;
+ struct iio_dev *indio_dev;
+ struct resource *mem;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ indio_dev->name = "z188-adc";
+ indio_dev->info = &z188_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = z188_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels);
+
+ mem = mcb_request_mem(dev, "z188-adc");
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ adc->base = ioremap(mem->start, resource_size(mem));
+ if (adc->base == NULL)
+ goto err;
+
+ men_z188_config_channels(adc->base);
+
+ adc->mem = mem;
+ mcb_set_drvdata(dev, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_unmap;
+
+ return 0;
+
+err_unmap:
+ iounmap(adc->base);
+err:
+ mcb_release_mem(mem);
+ return -ENXIO;
+}
+
+static void men_z188_remove(struct mcb_device *dev)
+{
+ struct iio_dev *indio_dev = mcb_get_drvdata(dev);
+ struct z188_adc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iounmap(adc->base);
+ mcb_release_mem(adc->mem);
+}
+
+static const struct mcb_device_id men_z188_ids[] = {
+ { .device = 0xbc },
+ { }
+};
+MODULE_DEVICE_TABLE(mcb, men_z188_ids);
+
+static struct mcb_driver men_z188_driver = {
+ .driver = {
+ .name = "z188-adc",
+ .owner = THIS_MODULE,
+ },
+ .probe = men_z188_probe,
+ .remove = men_z188_remove,
+ .id_table = men_z188_ids,
+};
+module_mcb_driver(men_z188_driver);
+
+MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
+MODULE_ALIAS("mcb:16z188");
+MODULE_IMPORT_NS(MCB);
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
new file mode 100644
index 000000000..eb965974e
--- /dev/null
+++ b/drivers/iio/adc/meson_saradc.c
@@ -0,0 +1,1315 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic Meson Successive Approximation Register (SAR) A/D Converter
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/syscon.h>
+
+#define MESON_SAR_ADC_REG0 0x00
+ #define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
+ #define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28)
+ #define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30)
+ #define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29)
+ #define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28)
+ #define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27)
+ #define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26)
+ #define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21)
+ #define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19)
+ #define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16)
+ #define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15)
+ #define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14)
+ #define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12)
+ #define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10)
+ #define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9)
+ #define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4)
+ #define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3)
+ #define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2)
+ #define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1)
+ #define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0)
+
+#define MESON_SAR_ADC_CHAN_LIST 0x04
+ #define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24)
+ #define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \
+ (GENMASK(2, 0) << ((_chan) * 3))
+
+#define MESON_SAR_ADC_AVG_CNTL 0x08
+ #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \
+ (16 + ((_chan) * 2))
+ #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \
+ (GENMASK(17, 16) << ((_chan) * 2))
+ #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \
+ (0 + ((_chan) * 2))
+ #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \
+ (GENMASK(1, 0) << ((_chan) * 2))
+
+#define MESON_SAR_ADC_REG3 0x0c
+ #define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31)
+ #define MESON_SAR_ADC_REG3_CLK_EN BIT(30)
+ #define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28)
+ #define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27)
+ #define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26)
+ #define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23)
+ #define MESON_SAR_ADC_REG3_DETECT_EN BIT(22)
+ #define MESON_SAR_ADC_REG3_ADC_EN BIT(21)
+ #define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18)
+ #define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16)
+ #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10
+ #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 6
+ #define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8)
+ #define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0)
+
+#define MESON_SAR_ADC_DELAY 0x10
+ #define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24)
+ #define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15)
+ #define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14)
+ #define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16)
+ #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8)
+ #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0)
+
+#define MESON_SAR_ADC_LAST_RD 0x14
+ #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16)
+ #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0)
+
+#define MESON_SAR_ADC_FIFO_RD 0x18
+ #define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12)
+ #define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0)
+
+#define MESON_SAR_ADC_AUX_SW 0x1c
+ #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(_chan) \
+ (8 + (((_chan) - 2) * 3))
+ #define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6)
+ #define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5)
+ #define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4)
+ #define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3)
+ #define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2)
+ #define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1)
+ #define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0)
+
+#define MESON_SAR_ADC_CHAN_10_SW 0x20
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1)
+ #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0)
+
+#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1)
+ #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0)
+
+#define MESON_SAR_ADC_DELTA_10 0x28
+ #define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27)
+ #define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
+ #define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
+ #define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
+ #define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
+ #define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
+ #define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
+
+/*
+ * NOTE: registers from here are undocumented (the vendor Linux kernel driver
+ * and u-boot source served as reference). These only seem to be relevant on
+ * GXBB and newer.
+ */
+#define MESON_SAR_ADC_REG11 0x2c
+ #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13)
+
+#define MESON_SAR_ADC_REG13 0x34
+ #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
+
+#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
+#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
+#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6
+#define MESON_SAR_ADC_TEMP_OFFSET 27
+
+/* temperature sensor calibration information in eFuse */
+#define MESON_SAR_ADC_EFUSE_BYTES 4
+#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0)
+#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7)
+
+#define MESON_HHI_DPLL_TOP_0 0x318
+#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9)
+
+/* for use with IIO_VAL_INT_PLUS_MICRO */
+#define MILLION 1000000
+
+#define MESON_SAR_ADC_CHAN(_chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _chan, \
+ .address = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .datasheet_name = "SAR_ADC_CH"#_chan, \
+}
+
+#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \
+ .type = IIO_TEMP, \
+ .channel = _chan, \
+ .address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .datasheet_name = "TEMP_SENSOR", \
+}
+
+static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
+ MESON_SAR_ADC_CHAN(0),
+ MESON_SAR_ADC_CHAN(1),
+ MESON_SAR_ADC_CHAN(2),
+ MESON_SAR_ADC_CHAN(3),
+ MESON_SAR_ADC_CHAN(4),
+ MESON_SAR_ADC_CHAN(5),
+ MESON_SAR_ADC_CHAN(6),
+ MESON_SAR_ADC_CHAN(7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
+ MESON_SAR_ADC_CHAN(0),
+ MESON_SAR_ADC_CHAN(1),
+ MESON_SAR_ADC_CHAN(2),
+ MESON_SAR_ADC_CHAN(3),
+ MESON_SAR_ADC_CHAN(4),
+ MESON_SAR_ADC_CHAN(5),
+ MESON_SAR_ADC_CHAN(6),
+ MESON_SAR_ADC_CHAN(7),
+ MESON_SAR_ADC_TEMP_CHAN(8),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
+enum meson_sar_adc_avg_mode {
+ NO_AVERAGING = 0x0,
+ MEAN_AVERAGING = 0x1,
+ MEDIAN_AVERAGING = 0x2,
+};
+
+enum meson_sar_adc_num_samples {
+ ONE_SAMPLE = 0x0,
+ TWO_SAMPLES = 0x1,
+ FOUR_SAMPLES = 0x2,
+ EIGHT_SAMPLES = 0x3,
+};
+
+enum meson_sar_adc_chan7_mux_sel {
+ CHAN7_MUX_VSS = 0x0,
+ CHAN7_MUX_VDD_DIV4 = 0x1,
+ CHAN7_MUX_VDD_DIV2 = 0x2,
+ CHAN7_MUX_VDD_MUL3_DIV4 = 0x3,
+ CHAN7_MUX_VDD = 0x4,
+ CHAN7_MUX_CH7_INPUT = 0x7,
+};
+
+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;
+};
+
+struct meson_sar_adc_data {
+ const struct meson_sar_adc_param *param;
+ const char *name;
+};
+
+struct meson_sar_adc_priv {
+ struct regmap *regmap;
+ struct regulator *vref;
+ const struct meson_sar_adc_param *param;
+ struct clk *clkin;
+ struct clk *core_clk;
+ struct clk *adc_sel_clk;
+ struct clk *adc_clk;
+ struct clk_gate clk_gate;
+ struct clk *adc_div_clk;
+ struct clk_divider clk_div;
+ struct completion done;
+ int calibbias;
+ int calibscale;
+ struct regmap *tsc_regmap;
+ bool temperature_sensor_calibrated;
+ u8 temperature_sensor_coefficient;
+ u16 temperature_sensor_adc_val;
+};
+
+static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = MESON_SAR_ADC_REG13,
+};
+
+static const struct regmap_config meson_sar_adc_regmap_config_meson8 = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = MESON_SAR_ADC_DELTA_10,
+};
+
+static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ u32 regval;
+
+ regmap_read(priv->regmap, MESON_SAR_ADC_REG0, &regval);
+
+ return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
+}
+
+static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int tmp;
+
+ /* use val_calib = scale * val_raw + offset calibration function */
+ tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
+
+ return clamp(tmp, 0, (1 << priv->param->resolution) - 1);
+}
+
+static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int val;
+
+ /*
+ * NOTE: we need a small delay before reading the status, otherwise
+ * the sample engine may not have started internally (which would
+ * seem to us that sampling is already finished).
+ */
+ udelay(1);
+ return regmap_read_poll_timeout_atomic(priv->regmap, MESON_SAR_ADC_REG0, val,
+ !FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, val),
+ 1, 10000);
+}
+
+static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int regval, fifo_chan, fifo_val, count;
+
+ if (!wait_for_completion_timeout(&priv->done,
+ msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ count = meson_sar_adc_get_fifo_count(indio_dev);
+ if (count != 1) {
+ dev_err(dev, "ADC FIFO has %d element(s) instead of one\n", count);
+ return -EINVAL;
+ }
+
+ regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &regval);
+ fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
+ if (fifo_chan != chan->address) {
+ dev_err(dev, "ADC FIFO entry belongs to channel %d instead of %lu\n",
+ fifo_chan, chan->address);
+ return -EINVAL;
+ }
+
+ fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
+ fifo_val &= GENMASK(priv->param->resolution - 1, 0);
+ *val = meson_sar_adc_calib_val(indio_dev, fifo_val);
+
+ return 0;
+}
+
+static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum meson_sar_adc_avg_mode mode,
+ enum meson_sar_adc_num_samples samples)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int val, address = chan->address;
+
+ val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
+ MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address),
+ val);
+
+ val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
+ MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val);
+}
+
+static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ u32 regval;
+
+ /*
+ * the SAR ADC engine allows sampling multiple channels at the same
+ * time. to keep it simple we're only working with one *internal*
+ * channel, which starts counting at index 0 (which means: count = 1).
+ */
+ regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
+ MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval);
+
+ /* map channel index 0 to the channel which we want to read */
+ regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
+ chan->address);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
+ MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
+
+ regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
+ chan->address);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
+ MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
+ regval);
+
+ regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
+ chan->address);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
+ MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
+ regval);
+
+ if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) {
+ if (chan->type == IIO_TEMP)
+ regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL;
+ else
+ regval = 0;
+
+ regmap_update_bits(priv->regmap,
+ MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
+ }
+}
+
+static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
+ enum meson_sar_adc_chan7_mux_sel sel)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ u32 regval;
+
+ regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+ MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval);
+
+ usleep_range(10, 20);
+}
+
+static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+
+ reinit_completion(&priv->done);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_FIFO_IRQ_EN,
+ MESON_SAR_ADC_REG0_FIFO_IRQ_EN);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
+ MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_SAMPLING_START,
+ MESON_SAR_ADC_REG0_SAMPLING_START);
+}
+
+static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_SAMPLING_STOP,
+ MESON_SAR_ADC_REG0_SAMPLING_STOP);
+
+ /* wait until all modules are stopped */
+ meson_sar_adc_wait_busy_clear(indio_dev);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0);
+}
+
+static int meson_sar_adc_lock(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int val, ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (priv->param->has_bl30_integration) {
+ /* prevent BL30 from using the SAR ADC while we are using it */
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
+ MESON_SAR_ADC_DELAY_KERNEL_BUSY,
+ MESON_SAR_ADC_DELAY_KERNEL_BUSY);
+
+ udelay(1);
+
+ /*
+ * wait until BL30 releases it's lock (so we can use the SAR
+ * ADC)
+ */
+ ret = regmap_read_poll_timeout_atomic(priv->regmap, MESON_SAR_ADC_DELAY, val,
+ !(val & MESON_SAR_ADC_DELAY_BL30_BUSY),
+ 1, 10000);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void meson_sar_adc_unlock(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+
+ if (priv->param->has_bl30_integration)
+ /* allow BL30 to use the SAR ADC again */
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
+ MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
+
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static void meson_sar_adc_clear_fifo(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ unsigned int count, tmp;
+
+ for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) {
+ if (!meson_sar_adc_get_fifo_count(indio_dev))
+ break;
+
+ regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &tmp);
+ }
+}
+
+static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum meson_sar_adc_avg_mode avg_mode,
+ enum meson_sar_adc_num_samples avg_samples,
+ int *val)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated)
+ return -ENOTSUPP;
+
+ ret = meson_sar_adc_lock(indio_dev);
+ if (ret)
+ return ret;
+
+ /* clear the FIFO to make sure we're not reading old values */
+ meson_sar_adc_clear_fifo(indio_dev);
+
+ meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples);
+
+ meson_sar_adc_enable_channel(indio_dev, chan);
+
+ meson_sar_adc_start_sample_engine(indio_dev);
+ ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val);
+ meson_sar_adc_stop_sample_engine(indio_dev);
+
+ meson_sar_adc_unlock(indio_dev);
+
+ if (ret) {
+ dev_warn(dev, "failed to read sample for channel %lu: %d\n",
+ chan->address, ret);
+ return ret;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return meson_sar_adc_get_sample(indio_dev, chan, NO_AVERAGING,
+ ONE_SAMPLE, val);
+
+ case IIO_CHAN_INFO_AVERAGE_RAW:
+ return meson_sar_adc_get_sample(indio_dev, chan,
+ MEAN_AVERAGING, EIGHT_SAMPLES,
+ val);
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLTAGE) {
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "failed to get vref voltage: %d\n", ret);
+ return ret;
+ }
+
+ *val = ret / 1000;
+ *val2 = priv->param->resolution;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ } else if (chan->type == IIO_TEMP) {
+ /* SoC specific multiplier and divider */
+ *val = priv->param->temperature_multiplier;
+ *val2 = priv->param->temperature_divider;
+
+ /* celsius to millicelsius */
+ *val *= 1000;
+
+ return IIO_VAL_FRACTIONAL;
+ } else {
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *val = priv->calibbias;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = priv->calibscale / MILLION;
+ *val2 = priv->calibscale % MILLION;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_CHAN_INFO_OFFSET:
+ *val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET *
+ priv->param->temperature_divider,
+ priv->param->temperature_multiplier);
+ *val -= priv->temperature_sensor_adc_val;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
+ void __iomem *base)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct clk_init_data init;
+ const char *clk_parents[1];
+
+ init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#adc_div", dev_name(dev));
+ if (!init.name)
+ return -ENOMEM;
+
+ init.flags = 0;
+ init.ops = &clk_divider_ops;
+ clk_parents[0] = __clk_get_name(priv->clkin);
+ init.parent_names = clk_parents;
+ init.num_parents = 1;
+
+ priv->clk_div.reg = base + MESON_SAR_ADC_REG3;
+ priv->clk_div.shift = MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT;
+ priv->clk_div.width = MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH;
+ priv->clk_div.hw.init = &init;
+ priv->clk_div.flags = 0;
+
+ priv->adc_div_clk = devm_clk_register(dev, &priv->clk_div.hw);
+ if (WARN_ON(IS_ERR(priv->adc_div_clk)))
+ return PTR_ERR(priv->adc_div_clk);
+
+ init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#adc_en", dev_name(dev));
+ if (!init.name)
+ return -ENOMEM;
+
+ init.flags = CLK_SET_RATE_PARENT;
+ init.ops = &clk_gate_ops;
+ clk_parents[0] = __clk_get_name(priv->adc_div_clk);
+ init.parent_names = clk_parents;
+ init.num_parents = 1;
+
+ priv->clk_gate.reg = base + MESON_SAR_ADC_REG3;
+ priv->clk_gate.bit_idx = __ffs(MESON_SAR_ADC_REG3_CLK_EN);
+ priv->clk_gate.hw.init = &init;
+
+ priv->adc_clk = devm_clk_register(dev, &priv->clk_gate.hw);
+ if (WARN_ON(IS_ERR(priv->adc_clk)))
+ return PTR_ERR(priv->adc_clk);
+
+ return 0;
+}
+
+static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ u8 *buf, trimming_bits, trimming_mask, upper_adc_val;
+ struct device *dev = indio_dev->dev.parent;
+ struct nvmem_cell *temperature_calib;
+ size_t read_len;
+ int ret;
+
+ temperature_calib = devm_nvmem_cell_get(dev, "temperature_calib");
+ if (IS_ERR(temperature_calib)) {
+ ret = PTR_ERR(temperature_calib);
+
+ /*
+ * leave the temperature sensor disabled if no calibration data
+ * was passed via nvmem-cells.
+ */
+ if (ret == -ENODEV)
+ return 0;
+
+ return dev_err_probe(dev, ret, "failed to get temperature_calib cell\n");
+ }
+
+ priv->tsc_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "amlogic,hhi-sysctrl");
+ if (IS_ERR(priv->tsc_regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->tsc_regmap),
+ "failed to get amlogic,hhi-sysctrl regmap\n");
+
+ read_len = MESON_SAR_ADC_EFUSE_BYTES;
+ buf = nvmem_cell_read(temperature_calib, &read_len);
+ if (IS_ERR(buf))
+ return dev_err_probe(dev, PTR_ERR(buf), "failed to read temperature_calib cell\n");
+ if (read_len != MESON_SAR_ADC_EFUSE_BYTES) {
+ kfree(buf);
+ return dev_err_probe(dev, -EINVAL, "invalid read size of temperature_calib cell\n");
+ }
+
+ trimming_bits = priv->param->temperature_trimming_bits;
+ trimming_mask = BIT(trimming_bits) - 1;
+
+ priv->temperature_sensor_calibrated =
+ buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED;
+ priv->temperature_sensor_coefficient = buf[2] & trimming_mask;
+
+ upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL,
+ buf[3]);
+
+ priv->temperature_sensor_adc_val = buf[2];
+ priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE;
+ priv->temperature_sensor_adc_val >>= trimming_bits;
+
+ kfree(buf);
+
+ return 0;
+}
+
+static int meson_sar_adc_init(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int regval, i, ret;
+
+ /*
+ * make sure we start at CH7 input since the other muxes are only used
+ * for internal calibration.
+ */
+ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
+
+ if (priv->param->has_bl30_integration) {
+ /*
+ * leave sampling delay and the input clocks as configured by
+ * BL30 to make sure BL30 gets the values it expects when
+ * reading the temperature sensor.
+ */
+ regmap_read(priv->regmap, MESON_SAR_ADC_REG3, &regval);
+ if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED)
+ return 0;
+ }
+
+ meson_sar_adc_stop_sample_engine(indio_dev);
+
+ /*
+ * disable this bit as seems to be only relevant for Meson6 (based
+ * on the vendor driver), which we don't support at the moment.
+ */
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, 0);
+
+ /* disable all channels by default */
+ regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+ MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+ MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY,
+ MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY);
+
+ /* delay between two samples = (10+1) * 1uS */
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
+ MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
+ FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK,
+ 10));
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
+ MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
+ FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK,
+ 0));
+
+ /* delay between two samples = (10+1) * 1uS */
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
+ MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
+ FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK,
+ 10));
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
+ MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
+ FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK,
+ 1));
+
+ /*
+ * set up the input channel muxes in MESON_SAR_ADC_CHAN_10_SW
+ * (0 = SAR_ADC_CH0, 1 = SAR_ADC_CH1)
+ */
+ regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, 0);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
+ MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK,
+ regval);
+ regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, 1);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW,
+ MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK,
+ regval);
+
+ /*
+ * set up the input channel muxes in MESON_SAR_ADC_AUX_SW
+ * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable
+ * MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW and
+ * MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW like the vendor driver.
+ */
+ regval = 0;
+ for (i = 2; i <= 7; i++)
+ regval |= i << MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(i);
+ regval |= MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW;
+ regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW;
+ regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval);
+
+ if (priv->temperature_sensor_calibrated) {
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0);
+
+ /*
+ * set bits [3:0] of the TSC (temperature sensor coefficient)
+ * to get the correct values when reading the temperature.
+ */
+ regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK,
+ priv->temperature_sensor_coefficient);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval);
+
+ if (priv->param->temperature_trimming_bits == 5) {
+ if (priv->temperature_sensor_coefficient & BIT(4))
+ regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4;
+ else
+ regval = 0;
+
+ /*
+ * bit [4] (the 5th bit when starting to count at 1)
+ * of the TSC is located in the HHI register area.
+ */
+ regmap_update_bits(priv->tsc_regmap,
+ MESON_HHI_DPLL_TOP_0,
+ MESON_HHI_DPLL_TOP_0_TSC_BIT4,
+ regval);
+ }
+ } else {
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1, 0);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
+ }
+
+ ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set adc parent to clkin\n");
+
+ ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to set adc clock rate\n");
+
+ return 0;
+}
+
+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;
+ 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);
+}
+
+static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+ u32 regval;
+
+ ret = meson_sar_adc_lock(indio_dev);
+ if (ret)
+ goto err_lock;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable vref regulator\n");
+ goto err_vref;
+ }
+
+ ret = clk_prepare_enable(priv->core_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable core clk\n");
+ goto err_core_clk;
+ }
+
+ regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
+
+ meson_sar_adc_set_bandgap(indio_dev, true);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+ MESON_SAR_ADC_REG3_ADC_EN,
+ MESON_SAR_ADC_REG3_ADC_EN);
+
+ udelay(5);
+
+ ret = clk_prepare_enable(priv->adc_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable adc clk\n");
+ goto err_adc_clk;
+ }
+
+ meson_sar_adc_unlock(indio_dev);
+
+ return 0;
+
+err_adc_clk:
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+ MESON_SAR_ADC_REG3_ADC_EN, 0);
+ meson_sar_adc_set_bandgap(indio_dev, false);
+ clk_disable_unprepare(priv->core_clk);
+err_core_clk:
+ regulator_disable(priv->vref);
+err_vref:
+ meson_sar_adc_unlock(indio_dev);
+err_lock:
+ return ret;
+}
+
+static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ ret = meson_sar_adc_lock(indio_dev);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(priv->adc_clk);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3,
+ MESON_SAR_ADC_REG3_ADC_EN, 0);
+
+ meson_sar_adc_set_bandgap(indio_dev, false);
+
+ clk_disable_unprepare(priv->core_clk);
+
+ regulator_disable(priv->vref);
+
+ meson_sar_adc_unlock(indio_dev);
+
+ return 0;
+}
+
+static irqreturn_t meson_sar_adc_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ unsigned int cnt, threshold;
+ u32 regval;
+
+ regmap_read(priv->regmap, MESON_SAR_ADC_REG0, &regval);
+ cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
+ threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
+
+ if (cnt < threshold)
+ return IRQ_NONE;
+
+ complete(&priv->done);
+
+ return IRQ_HANDLED;
+}
+
+static int meson_sar_adc_calib(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int ret, nominal0, nominal1, value0, value1;
+
+ /* use points 25% and 75% for calibration */
+ nominal0 = (1 << priv->param->resolution) / 4;
+ nominal1 = (1 << priv->param->resolution) * 3 / 4;
+
+ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
+ usleep_range(10, 20);
+ ret = meson_sar_adc_get_sample(indio_dev,
+ &indio_dev->channels[7],
+ MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
+ if (ret < 0)
+ goto out;
+
+ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
+ usleep_range(10, 20);
+ ret = meson_sar_adc_get_sample(indio_dev,
+ &indio_dev->channels[7],
+ MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
+ if (ret < 0)
+ goto out;
+
+ if (value1 <= value0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION,
+ value1 - value0);
+ priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale,
+ MILLION);
+ ret = 0;
+out:
+ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
+
+ return ret;
+}
+
+static const struct iio_info meson_sar_adc_iio_info = {
+ .read_raw = meson_sar_adc_iio_info_read_raw,
+};
+
+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,
+ .temperature_multiplier = 18 * 10000,
+ .temperature_divider = 1024 * 10 * 85,
+};
+
+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,
+ .temperature_multiplier = 10,
+ .temperature_divider = 32,
+};
+
+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,
+};
+
+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,
+};
+
+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,
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
+ .param = &meson_sar_adc_meson8_param,
+ .name = "meson-meson8-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
+ .param = &meson_sar_adc_meson8b_param,
+ .name = "meson-meson8b-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = {
+ .param = &meson_sar_adc_meson8b_param,
+ .name = "meson-meson8m2-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = {
+ .param = &meson_sar_adc_gxbb_param,
+ .name = "meson-gxbb-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_gxl_data = {
+ .param = &meson_sar_adc_gxl_param,
+ .name = "meson-gxl-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_gxm_data = {
+ .param = &meson_sar_adc_gxl_param,
+ .name = "meson-gxm-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_axg_data = {
+ .param = &meson_sar_adc_gxl_param,
+ .name = "meson-axg-saradc",
+};
+
+static const struct meson_sar_adc_data meson_sar_adc_g12a_data = {
+ .param = &meson_sar_adc_g12a_param,
+ .name = "meson-g12a-saradc",
+};
+
+static const struct of_device_id meson_sar_adc_of_match[] = {
+ {
+ .compatible = "amlogic,meson8-saradc",
+ .data = &meson_sar_adc_meson8_data,
+ }, {
+ .compatible = "amlogic,meson8b-saradc",
+ .data = &meson_sar_adc_meson8b_data,
+ }, {
+ .compatible = "amlogic,meson8m2-saradc",
+ .data = &meson_sar_adc_meson8m2_data,
+ }, {
+ .compatible = "amlogic,meson-gxbb-saradc",
+ .data = &meson_sar_adc_gxbb_data,
+ }, {
+ .compatible = "amlogic,meson-gxl-saradc",
+ .data = &meson_sar_adc_gxl_data,
+ }, {
+ .compatible = "amlogic,meson-gxm-saradc",
+ .data = &meson_sar_adc_gxm_data,
+ }, {
+ .compatible = "amlogic,meson-axg-saradc",
+ .data = &meson_sar_adc_axg_data,
+ }, {
+ .compatible = "amlogic,meson-g12a-saradc",
+ .data = &meson_sar_adc_g12a_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match);
+
+static int meson_sar_adc_probe(struct platform_device *pdev)
+{
+ const struct meson_sar_adc_data *match_data;
+ struct meson_sar_adc_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ void __iomem *base;
+ int irq, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return dev_err_probe(dev, -ENOMEM, "failed allocating iio device\n");
+
+ priv = iio_priv(indio_dev);
+ init_completion(&priv->done);
+
+ match_data = of_device_get_match_data(dev);
+ if (!match_data)
+ return dev_err_probe(dev, -ENODEV, "failed to get match data\n");
+
+ priv->param = match_data->param;
+
+ indio_dev->name = match_data->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &meson_sar_adc_iio_info;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(dev, base, priv->param->regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ irq = irq_of_parse_and_map(dev->of_node, 0);
+ if (!irq)
+ return -EINVAL;
+
+ ret = devm_request_irq(dev, irq, meson_sar_adc_irq, IRQF_SHARED, dev_name(dev), indio_dev);
+ if (ret)
+ return ret;
+
+ priv->clkin = devm_clk_get(dev, "clkin");
+ if (IS_ERR(priv->clkin))
+ return dev_err_probe(dev, PTR_ERR(priv->clkin), "failed to get clkin\n");
+
+ priv->core_clk = devm_clk_get(dev, "core");
+ if (IS_ERR(priv->core_clk))
+ return dev_err_probe(dev, PTR_ERR(priv->core_clk), "failed to get core clk\n");
+
+ priv->adc_clk = devm_clk_get_optional(dev, "adc_clk");
+ if (IS_ERR(priv->adc_clk))
+ return dev_err_probe(dev, PTR_ERR(priv->adc_clk), "failed to get adc clk\n");
+
+ priv->adc_sel_clk = devm_clk_get_optional(dev, "adc_sel");
+ if (IS_ERR(priv->adc_sel_clk))
+ return dev_err_probe(dev, PTR_ERR(priv->adc_sel_clk), "failed to get adc_sel clk\n");
+
+ /* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */
+ if (!priv->adc_clk) {
+ ret = meson_sar_adc_clk_init(indio_dev, base);
+ if (ret)
+ return ret;
+ }
+
+ priv->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(priv->vref))
+ return dev_err_probe(dev, PTR_ERR(priv->vref), "failed to get vref regulator\n");
+
+ priv->calibscale = MILLION;
+
+ if (priv->param->temperature_trimming_bits) {
+ ret = meson_sar_adc_temp_sensor_init(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->temperature_sensor_calibrated) {
+ indio_dev->channels = meson_sar_adc_and_temp_iio_channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels);
+ } else {
+ indio_dev->channels = meson_sar_adc_iio_channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(meson_sar_adc_iio_channels);
+ }
+
+ ret = meson_sar_adc_init(indio_dev);
+ if (ret)
+ goto err;
+
+ ret = meson_sar_adc_hw_enable(indio_dev);
+ if (ret)
+ goto err;
+
+ ret = meson_sar_adc_calib(indio_dev);
+ if (ret)
+ dev_warn(dev, "calibration failed\n");
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_hw;
+
+ return 0;
+
+err_hw:
+ meson_sar_adc_hw_disable(indio_dev);
+err:
+ return ret;
+}
+
+static int meson_sar_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+
+ return meson_sar_adc_hw_disable(indio_dev);
+}
+
+static int meson_sar_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ return meson_sar_adc_hw_disable(indio_dev);
+}
+
+static int meson_sar_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ return meson_sar_adc_hw_enable(indio_dev);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops,
+ meson_sar_adc_suspend, meson_sar_adc_resume);
+
+static struct platform_driver meson_sar_adc_driver = {
+ .probe = meson_sar_adc_probe,
+ .remove = meson_sar_adc_remove,
+ .driver = {
+ .name = "meson-saradc",
+ .of_match_table = meson_sar_adc_of_match,
+ .pm = pm_sleep_ptr(&meson_sar_adc_pm_ops),
+ },
+};
+
+module_platform_driver(meson_sar_adc_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c
new file mode 100644
index 000000000..88e947f30
--- /dev/null
+++ b/drivers/iio/adc/mp2629_adc.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MP2629 Driver for ADC
+ *
+ * Copyright 2020 Monolithic Power Systems, Inc
+ *
+ * Author: Saravanan Sekar <sravanhome@gmail.com>
+ */
+
+#include <linux/iio/driver.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/mp2629.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define MP2629_REG_ADC_CTRL 0x03
+#define MP2629_REG_BATT_VOLT 0x0e
+#define MP2629_REG_SYSTEM_VOLT 0x0f
+#define MP2629_REG_INPUT_VOLT 0x11
+#define MP2629_REG_BATT_CURRENT 0x12
+#define MP2629_REG_INPUT_CURRENT 0x13
+
+#define MP2629_ADC_START BIT(7)
+#define MP2629_ADC_CONTINUOUS BIT(6)
+
+#define MP2629_MAP(_mp, _mpc) IIO_MAP(#_mp, "mp2629_charger", "mp2629-"_mpc)
+
+#define MP2629_ADC_CHAN(_ch, _type) { \
+ .type = _type, \
+ .indexed = 1, \
+ .datasheet_name = #_ch, \
+ .channel = MP2629_ ## _ch, \
+ .address = MP2629_REG_ ## _ch, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+struct mp2629_adc {
+ struct regmap *regmap;
+ struct device *dev;
+};
+
+static struct iio_chan_spec mp2629_channels[] = {
+ MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE),
+ MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE),
+ MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE),
+ MP2629_ADC_CHAN(BATT_CURRENT, IIO_CURRENT),
+ MP2629_ADC_CHAN(INPUT_CURRENT, IIO_CURRENT)
+};
+
+static struct iio_map mp2629_adc_maps[] = {
+ MP2629_MAP(BATT_VOLT, "batt-volt"),
+ MP2629_MAP(SYSTEM_VOLT, "system-volt"),
+ MP2629_MAP(INPUT_VOLT, "input-volt"),
+ MP2629_MAP(BATT_CURRENT, "batt-current"),
+ MP2629_MAP(INPUT_CURRENT, "input-current"),
+ { }
+};
+
+static int mp2629_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mp2629_adc *info = iio_priv(indio_dev);
+ unsigned int rval;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(info->regmap, chan->address, &rval);
+ if (ret)
+ return ret;
+
+ if (chan->channel == MP2629_INPUT_VOLT)
+ rval &= GENMASK(6, 0);
+ *val = rval;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case MP2629_BATT_VOLT:
+ case MP2629_SYSTEM_VOLT:
+ *val = 20;
+ return IIO_VAL_INT;
+
+ case MP2629_INPUT_VOLT:
+ *val = 60;
+ return IIO_VAL_INT;
+
+ case MP2629_BATT_CURRENT:
+ *val = 175;
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL;
+
+ case MP2629_INPUT_CURRENT:
+ *val = 133;
+ *val2 = 10;
+ return IIO_VAL_FRACTIONAL;
+
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mp2629_adc_info = {
+ .read_raw = &mp2629_read_raw,
+};
+
+static int mp2629_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mp2629_data *ddata = dev_get_drvdata(dev->parent);
+ struct mp2629_adc *info;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ info->regmap = ddata->regmap;
+ info->dev = dev;
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
+ MP2629_ADC_START | MP2629_ADC_CONTINUOUS,
+ MP2629_ADC_START | MP2629_ADC_CONTINUOUS);
+ if (ret) {
+ dev_err(dev, "adc enable fail: %d\n", ret);
+ return ret;
+ }
+
+ ret = iio_map_array_register(indio_dev, mp2629_adc_maps);
+ if (ret) {
+ dev_err(dev, "IIO maps register fail: %d\n", ret);
+ goto fail_disable;
+ }
+
+ indio_dev->name = "mp2629-adc";
+ indio_dev->channels = mp2629_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mp2629_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mp2629_adc_info;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "IIO device register fail: %d\n", ret);
+ goto fail_map_unregister;
+ }
+
+ return 0;
+
+fail_map_unregister:
+ iio_map_array_unregister(indio_dev);
+
+fail_disable:
+ regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
+ MP2629_ADC_CONTINUOUS, 0);
+ regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
+ MP2629_ADC_START, 0);
+
+ return ret;
+}
+
+static int mp2629_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct mp2629_adc *info = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_map_array_unregister(indio_dev);
+
+ regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
+ MP2629_ADC_CONTINUOUS, 0);
+ regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL,
+ MP2629_ADC_START, 0);
+
+ return 0;
+}
+
+static const struct of_device_id mp2629_adc_of_match[] = {
+ { .compatible = "mps,mp2629_adc"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp2629_adc_of_match);
+
+static struct platform_driver mp2629_adc_driver = {
+ .driver = {
+ .name = "mp2629_adc",
+ .of_match_table = mp2629_adc_of_match,
+ },
+ .probe = mp2629_adc_probe,
+ .remove = mp2629_adc_remove,
+};
+module_platform_driver(mp2629_adc_driver);
+
+MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
+MODULE_DESCRIPTION("MP2629 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c
new file mode 100644
index 000000000..3710473e5
--- /dev/null
+++ b/drivers/iio/adc/mt6360-adc.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <asm/unaligned.h>
+
+#define MT6360_REG_PMUCHGCTRL3 0x313
+#define MT6360_REG_PMUADCCFG 0x356
+#define MT6360_REG_PMUADCIDLET 0x358
+#define MT6360_REG_PMUADCRPT1 0x35A
+
+/* PMUCHGCTRL3 0x313 */
+#define MT6360_AICR_MASK GENMASK(7, 2)
+#define MT6360_AICR_SHFT 2
+#define MT6360_AICR_400MA 0x6
+/* PMUADCCFG 0x356 */
+#define MT6360_ADCEN_MASK BIT(15)
+/* PMUADCRPT1 0x35A */
+#define MT6360_PREFERCH_MASK GENMASK(7, 4)
+#define MT6360_PREFERCH_SHFT 4
+#define MT6360_RPTCH_MASK GENMASK(3, 0)
+#define MT6360_NO_PREFER 15
+
+/* Time in ms */
+#define ADC_WAIT_TIME_MS 25
+#define ADC_CONV_TIMEOUT_MS 100
+#define ADC_LOOP_TIME_US 2000
+
+enum {
+ MT6360_CHAN_USBID = 0,
+ MT6360_CHAN_VBUSDIV5,
+ MT6360_CHAN_VBUSDIV2,
+ MT6360_CHAN_VSYS,
+ MT6360_CHAN_VBAT,
+ MT6360_CHAN_IBUS,
+ MT6360_CHAN_IBAT,
+ MT6360_CHAN_CHG_VDDP,
+ MT6360_CHAN_TEMP_JC,
+ MT6360_CHAN_VREF_TS,
+ MT6360_CHAN_TS,
+ MT6360_CHAN_MAX
+};
+
+struct mt6360_adc_data {
+ struct device *dev;
+ struct regmap *regmap;
+ /* Due to only one set of ADC control, this lock is used to prevent the race condition */
+ struct mutex adc_lock;
+ ktime_t last_off_timestamps[MT6360_CHAN_MAX];
+};
+
+static int mt6360_adc_read_channel(struct mt6360_adc_data *mad, int channel, int *val)
+{
+ __be16 adc_enable;
+ u8 rpt[3];
+ ktime_t predict_end_t, timeout;
+ unsigned int pre_wait_time;
+ int ret;
+
+ mutex_lock(&mad->adc_lock);
+
+ /* Select the preferred ADC channel */
+ ret = regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
+ channel << MT6360_PREFERCH_SHFT);
+ if (ret)
+ goto out_adc_lock;
+
+ adc_enable = cpu_to_be16(MT6360_ADCEN_MASK | BIT(channel));
+ ret = regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable));
+ if (ret)
+ goto out_adc_lock;
+
+ predict_end_t = ktime_add_ms(mad->last_off_timestamps[channel], 2 * ADC_WAIT_TIME_MS);
+
+ if (ktime_after(ktime_get(), predict_end_t))
+ pre_wait_time = ADC_WAIT_TIME_MS;
+ else
+ pre_wait_time = 3 * ADC_WAIT_TIME_MS;
+
+ if (msleep_interruptible(pre_wait_time)) {
+ ret = -ERESTARTSYS;
+ goto out_adc_conv;
+ }
+
+ timeout = ktime_add_ms(ktime_get(), ADC_CONV_TIMEOUT_MS);
+ while (true) {
+ ret = regmap_raw_read(mad->regmap, MT6360_REG_PMUADCRPT1, rpt, sizeof(rpt));
+ if (ret)
+ goto out_adc_conv;
+
+ /*
+ * There are two functions, ZCV and TypeC OTP, running ADC VBAT and TS in
+ * background, and ADC samples are taken on a fixed frequency no matter read the
+ * previous one or not.
+ * To avoid conflict, We set minimum time threshold after enable ADC and
+ * check report channel is the same.
+ * The worst case is run the same ADC twice and background function is also running,
+ * ADC conversion sequence is desire channel before start ADC, background ADC,
+ * desire channel after start ADC.
+ * So the minimum correct data is three times of typical conversion time.
+ */
+ if ((rpt[0] & MT6360_RPTCH_MASK) == channel)
+ break;
+
+ if (ktime_compare(ktime_get(), timeout) > 0) {
+ ret = -ETIMEDOUT;
+ goto out_adc_conv;
+ }
+
+ usleep_range(ADC_LOOP_TIME_US / 2, ADC_LOOP_TIME_US);
+ }
+
+ *val = rpt[1] << 8 | rpt[2];
+ ret = IIO_VAL_INT;
+
+out_adc_conv:
+ /* Only keep ADC enable */
+ adc_enable = cpu_to_be16(MT6360_ADCEN_MASK);
+ regmap_raw_write(mad->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable));
+ mad->last_off_timestamps[channel] = ktime_get();
+ /* Config prefer channel to NO_PREFER */
+ regmap_update_bits(mad->regmap, MT6360_REG_PMUADCRPT1, MT6360_PREFERCH_MASK,
+ MT6360_NO_PREFER << MT6360_PREFERCH_SHFT);
+out_adc_lock:
+ mutex_unlock(&mad->adc_lock);
+
+ return ret;
+}
+
+static int mt6360_adc_read_scale(struct mt6360_adc_data *mad, int channel, int *val, int *val2)
+{
+ unsigned int regval;
+ int ret;
+
+ switch (channel) {
+ case MT6360_CHAN_USBID:
+ case MT6360_CHAN_VSYS:
+ case MT6360_CHAN_VBAT:
+ case MT6360_CHAN_CHG_VDDP:
+ case MT6360_CHAN_VREF_TS:
+ case MT6360_CHAN_TS:
+ *val = 1250;
+ return IIO_VAL_INT;
+ case MT6360_CHAN_VBUSDIV5:
+ *val = 6250;
+ return IIO_VAL_INT;
+ case MT6360_CHAN_VBUSDIV2:
+ case MT6360_CHAN_IBUS:
+ case MT6360_CHAN_IBAT:
+ *val = 2500;
+
+ if (channel == MT6360_CHAN_IBUS) {
+ /* IBUS will be affected by input current limit for the different Ron */
+ /* Check whether the config is <400mA or not */
+ ret = regmap_read(mad->regmap, MT6360_REG_PMUCHGCTRL3, &regval);
+ if (ret)
+ return ret;
+
+ regval = (regval & MT6360_AICR_MASK) >> MT6360_AICR_SHFT;
+ if (regval < MT6360_AICR_400MA)
+ *val = 1900;
+ }
+
+ return IIO_VAL_INT;
+ case MT6360_CHAN_TEMP_JC:
+ *val = 105;
+ *val2 = 100;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static int mt6360_adc_read_offset(struct mt6360_adc_data *mad, int channel, int *val)
+{
+ *val = (channel == MT6360_CHAN_TEMP_JC) ? -80 : 0;
+ return IIO_VAL_INT;
+}
+
+static int mt6360_adc_read_raw(struct iio_dev *iio_dev, const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct mt6360_adc_data *mad = iio_priv(iio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return mt6360_adc_read_channel(mad, chan->channel, val);
+ case IIO_CHAN_INFO_SCALE:
+ return mt6360_adc_read_scale(mad, chan->channel, val, val2);
+ case IIO_CHAN_INFO_OFFSET:
+ return mt6360_adc_read_offset(mad, chan->channel, val);
+ }
+
+ return -EINVAL;
+}
+
+static const char *mt6360_channel_labels[MT6360_CHAN_MAX] = {
+ "usbid", "vbusdiv5", "vbusdiv2", "vsys", "vbat", "ibus", "ibat", "chg_vddp",
+ "temp_jc", "vref_ts", "ts",
+};
+
+static int mt6360_adc_read_label(struct iio_dev *iio_dev, const struct iio_chan_spec *chan,
+ char *label)
+{
+ return snprintf(label, PAGE_SIZE, "%s\n", mt6360_channel_labels[chan->channel]);
+}
+
+static const struct iio_info mt6360_adc_iio_info = {
+ .read_raw = mt6360_adc_read_raw,
+ .read_label = mt6360_adc_read_label,
+};
+
+#define MT6360_ADC_CHAN(_idx, _type) { \
+ .type = _type, \
+ .channel = MT6360_CHAN_##_idx, \
+ .scan_index = MT6360_CHAN_##_idx, \
+ .datasheet_name = #_idx, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec mt6360_adc_channels[] = {
+ MT6360_ADC_CHAN(USBID, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBUSDIV5, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBUSDIV2, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VSYS, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(VBAT, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(IBUS, IIO_CURRENT),
+ MT6360_ADC_CHAN(IBAT, IIO_CURRENT),
+ MT6360_ADC_CHAN(CHG_VDDP, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(TEMP_JC, IIO_TEMP),
+ MT6360_ADC_CHAN(VREF_TS, IIO_VOLTAGE),
+ MT6360_ADC_CHAN(TS, IIO_VOLTAGE),
+ IIO_CHAN_SOFT_TIMESTAMP(MT6360_CHAN_MAX),
+};
+
+static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mt6360_adc_data *mad = iio_priv(indio_dev);
+ struct {
+ u16 values[MT6360_CHAN_MAX];
+ int64_t timestamp;
+ } data __aligned(8);
+ int i = 0, bit, val, ret;
+
+ memset(&data, 0, sizeof(data));
+ for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+ ret = mt6360_adc_read_channel(mad, bit, &val);
+ if (ret < 0) {
+ dev_warn(&indio_dev->dev, "Failed to get channel %d conversion val\n", bit);
+ goto out;
+ }
+
+ data.values[i++] = val;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev));
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static inline int mt6360_adc_reset(struct mt6360_adc_data *info)
+{
+ __be16 adc_enable;
+ ktime_t all_off_time;
+ int i, ret;
+
+ /* Clear ADC idle wait time to 0 */
+ ret = regmap_write(info->regmap, MT6360_REG_PMUADCIDLET, 0);
+ if (ret)
+ return ret;
+
+ /* Only keep ADC enable, but keep all channels off */
+ adc_enable = cpu_to_be16(MT6360_ADCEN_MASK);
+ ret = regmap_raw_write(info->regmap, MT6360_REG_PMUADCCFG, &adc_enable, sizeof(adc_enable));
+ if (ret)
+ return ret;
+
+ /* Reset all channel off time to the current one */
+ all_off_time = ktime_get();
+ for (i = 0; i < MT6360_CHAN_MAX; i++)
+ info->last_off_timestamps[i] = all_off_time;
+
+ return 0;
+}
+
+static int mt6360_adc_probe(struct platform_device *pdev)
+{
+ struct mt6360_adc_data *mad;
+ struct regmap *regmap;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap) {
+ dev_err(&pdev->dev, "Failed to get parent regmap\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*mad));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ mad = iio_priv(indio_dev);
+ mad->dev = &pdev->dev;
+ mad->regmap = regmap;
+ mutex_init(&mad->adc_lock);
+
+ ret = mt6360_adc_reset(mad);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to reset adc\n");
+ return ret;
+ }
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &mt6360_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mt6360_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mt6360_adc_channels);
+
+ ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, NULL,
+ mt6360_adc_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate iio trigger buffer\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static const struct of_device_id mt6360_adc_of_id[] = {
+ { .compatible = "mediatek,mt6360-adc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mt6360_adc_of_id);
+
+static struct platform_driver mt6360_adc_driver = {
+ .driver = {
+ .name = "mt6360-adc",
+ .of_match_table = mt6360_adc_of_id,
+ },
+ .probe = mt6360_adc_probe,
+};
+module_platform_driver(mt6360_adc_driver);
+
+MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
+MODULE_DESCRIPTION("MT6360 ADC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c
new file mode 100644
index 000000000..0e134777b
--- /dev/null
+++ b/drivers/iio/adc/mt6577_auxadc.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/iopoll.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+
+/* Register definitions */
+#define MT6577_AUXADC_CON0 0x00
+#define MT6577_AUXADC_CON1 0x04
+#define MT6577_AUXADC_CON2 0x10
+#define MT6577_AUXADC_STA BIT(0)
+
+#define MT6577_AUXADC_DAT0 0x14
+#define MT6577_AUXADC_RDY0 BIT(12)
+
+#define MT6577_AUXADC_MISC 0x94
+#define MT6577_AUXADC_PDN_EN BIT(14)
+
+#define MT6577_AUXADC_DAT_MASK 0xfff
+#define MT6577_AUXADC_SLEEP_US 1000
+#define MT6577_AUXADC_TIMEOUT_US 10000
+#define MT6577_AUXADC_POWER_READY_MS 1
+#define MT6577_AUXADC_SAMPLE_READY_US 25
+
+struct mtk_auxadc_compatible {
+ bool sample_data_cali;
+ bool check_global_idle;
+};
+
+struct mt6577_auxadc_device {
+ void __iomem *reg_base;
+ struct clk *adc_clk;
+ struct mutex lock;
+ const struct mtk_auxadc_compatible *dev_comp;
+};
+
+static const struct mtk_auxadc_compatible mt8186_compat = {
+ .sample_data_cali = false,
+ .check_global_idle = false,
+};
+
+static const struct mtk_auxadc_compatible mt8173_compat = {
+ .sample_data_cali = false,
+ .check_global_idle = true,
+};
+
+static const struct mtk_auxadc_compatible mt6765_compat = {
+ .sample_data_cali = true,
+ .check_global_idle = false,
+};
+
+#define MT6577_AUXADC_CHANNEL(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
+static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
+ MT6577_AUXADC_CHANNEL(0),
+ MT6577_AUXADC_CHANNEL(1),
+ MT6577_AUXADC_CHANNEL(2),
+ MT6577_AUXADC_CHANNEL(3),
+ MT6577_AUXADC_CHANNEL(4),
+ MT6577_AUXADC_CHANNEL(5),
+ MT6577_AUXADC_CHANNEL(6),
+ MT6577_AUXADC_CHANNEL(7),
+ MT6577_AUXADC_CHANNEL(8),
+ MT6577_AUXADC_CHANNEL(9),
+ MT6577_AUXADC_CHANNEL(10),
+ MT6577_AUXADC_CHANNEL(11),
+ MT6577_AUXADC_CHANNEL(12),
+ MT6577_AUXADC_CHANNEL(13),
+ MT6577_AUXADC_CHANNEL(14),
+ MT6577_AUXADC_CHANNEL(15),
+};
+
+/* For Voltage calculation */
+#define VOLTAGE_FULL_RANGE 1500 /* VA voltage */
+#define AUXADC_PRECISE 4096 /* 12 bits */
+
+static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali)
+{
+ return rawdata;
+}
+
+static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
+ u32 or_mask, u32 and_mask)
+{
+ u32 val;
+
+ val = readl(reg);
+ val |= or_mask;
+ val &= ~and_mask;
+ writel(val, reg);
+}
+
+static int mt6577_auxadc_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ u32 val;
+ void __iomem *reg_channel;
+ int ret;
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+ reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
+ chan->channel * 0x04;
+
+ mutex_lock(&adc_dev->lock);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
+ 0, 1 << chan->channel);
+
+ /* read channel and make sure old ready bit == 0 */
+ ret = readl_poll_timeout(reg_channel, val,
+ ((val & MT6577_AUXADC_RDY0) == 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for channel[%d] ready bit clear time out\n",
+ chan->channel);
+ goto err_timeout;
+ }
+
+ /* set bit to trigger sample */
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
+ 1 << chan->channel, 0);
+
+ /* we must delay here for hardware sample channel data */
+ udelay(MT6577_AUXADC_SAMPLE_READY_US);
+
+ if (adc_dev->dev_comp->check_global_idle) {
+ /* check MTK_AUXADC_CON2 if auxadc is idle */
+ ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2,
+ val, ((val & MT6577_AUXADC_STA) == 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for auxadc idle time out\n");
+ goto err_timeout;
+ }
+ }
+
+ /* read channel and make sure ready bit == 1 */
+ ret = readl_poll_timeout(reg_channel, val,
+ ((val & MT6577_AUXADC_RDY0) != 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for channel[%d] data ready time out\n",
+ chan->channel);
+ goto err_timeout;
+ }
+
+ /* read data */
+ val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
+
+ mutex_unlock(&adc_dev->lock);
+
+ return val;
+
+err_timeout:
+
+ mutex_unlock(&adc_dev->lock);
+
+ return -ETIMEDOUT;
+}
+
+static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long info)
+{
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_PROCESSED:
+ *val = mt6577_auxadc_read(indio_dev, chan);
+ if (*val < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to sample data on channel[%d]\n",
+ chan->channel);
+ return *val;
+ }
+ if (adc_dev->dev_comp->sample_data_cali)
+ *val = mt_auxadc_get_cali_data(*val, true);
+
+ /* Convert adc raw data to voltage: 0 - 1500 mV */
+ *val = *val * VOLTAGE_FULL_RANGE / AUXADC_PRECISE;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mt6577_auxadc_info = {
+ .read_raw = &mt6577_auxadc_read_raw,
+};
+
+static int mt6577_auxadc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+ int ret;
+
+ ret = clk_prepare_enable(adc_dev->adc_clk);
+ if (ret) {
+ pr_err("failed to enable auxadc clock\n");
+ return ret;
+ }
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ MT6577_AUXADC_PDN_EN, 0);
+ mdelay(MT6577_AUXADC_POWER_READY_MS);
+
+ return 0;
+}
+
+static int mt6577_auxadc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ 0, MT6577_AUXADC_PDN_EN);
+ clk_disable_unprepare(adc_dev->adc_clk);
+
+ return 0;
+}
+
+static int mt6577_auxadc_probe(struct platform_device *pdev)
+{
+ struct mt6577_auxadc_device *adc_dev;
+ unsigned long adc_clk_rate;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc_dev = iio_priv(indio_dev);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &mt6577_auxadc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mt6577_auxadc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
+
+ adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(adc_dev->reg_base)) {
+ dev_err(&pdev->dev, "failed to get auxadc base address\n");
+ return PTR_ERR(adc_dev->reg_base);
+ }
+
+ adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(adc_dev->adc_clk)) {
+ dev_err(&pdev->dev, "failed to get auxadc clock\n");
+ return PTR_ERR(adc_dev->adc_clk);
+ }
+
+ ret = clk_prepare_enable(adc_dev->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable auxadc clock\n");
+ return ret;
+ }
+
+ adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
+ if (!adc_clk_rate) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "null clock rate\n");
+ goto err_disable_clk;
+ }
+
+ adc_dev->dev_comp = device_get_match_data(&pdev->dev);
+
+ mutex_init(&adc_dev->lock);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ MT6577_AUXADC_PDN_EN, 0);
+ mdelay(MT6577_AUXADC_POWER_READY_MS);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register iio device\n");
+ goto err_power_off;
+ }
+
+ return 0;
+
+err_power_off:
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ 0, MT6577_AUXADC_PDN_EN);
+err_disable_clk:
+ clk_disable_unprepare(adc_dev->adc_clk);
+ return ret;
+}
+
+static int mt6577_auxadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ 0, MT6577_AUXADC_PDN_EN);
+
+ clk_disable_unprepare(adc_dev->adc_clk);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops,
+ mt6577_auxadc_suspend,
+ mt6577_auxadc_resume);
+
+static const struct of_device_id mt6577_auxadc_of_match[] = {
+ { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat },
+ { .compatible = "mediatek,mt8186-auxadc", .data = &mt8186_compat },
+ { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
+
+static struct platform_driver mt6577_auxadc_driver = {
+ .driver = {
+ .name = "mt6577-auxadc",
+ .of_match_table = mt6577_auxadc_of_match,
+ .pm = pm_sleep_ptr(&mt6577_auxadc_pm_ops),
+ },
+ .probe = mt6577_auxadc_probe,
+ .remove = mt6577_auxadc_remove,
+};
+module_platform_driver(mt6577_auxadc_driver);
+
+MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
+MODULE_DESCRIPTION("MTK AUXADC Device Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
new file mode 100644
index 000000000..a50f39143
--- /dev/null
+++ b/drivers/iio/adc/mxs-lradc-adc.c
@@ -0,0 +1,835 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale MXS LRADC ADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
+ *
+ * Authors:
+ * Marek Vasut <marex@denx.de>
+ * Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/mxs-lradc.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * Make this runtime configurable if necessary. Currently, if the buffered mode
+ * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
+ * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
+ * seconds. The result is that the samples arrive every 500mS.
+ */
+#define LRADC_DELAY_TIMER_PER 200
+#define LRADC_DELAY_TIMER_LOOP 5
+
+#define VREF_MV_BASE 1850
+
+static const char *mx23_lradc_adc_irq_names[] = {
+ "mxs-lradc-channel0",
+ "mxs-lradc-channel1",
+ "mxs-lradc-channel2",
+ "mxs-lradc-channel3",
+ "mxs-lradc-channel4",
+ "mxs-lradc-channel5",
+};
+
+static const char *mx28_lradc_adc_irq_names[] = {
+ "mxs-lradc-thresh0",
+ "mxs-lradc-thresh1",
+ "mxs-lradc-channel0",
+ "mxs-lradc-channel1",
+ "mxs-lradc-channel2",
+ "mxs-lradc-channel3",
+ "mxs-lradc-channel4",
+ "mxs-lradc-channel5",
+ "mxs-lradc-button0",
+ "mxs-lradc-button1",
+};
+
+static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
+ [IMX23_LRADC] = {
+ VREF_MV_BASE, /* CH0 */
+ VREF_MV_BASE, /* CH1 */
+ VREF_MV_BASE, /* CH2 */
+ VREF_MV_BASE, /* CH3 */
+ VREF_MV_BASE, /* CH4 */
+ VREF_MV_BASE, /* CH5 */
+ VREF_MV_BASE * 2, /* CH6 VDDIO */
+ VREF_MV_BASE * 4, /* CH7 VBATT */
+ VREF_MV_BASE, /* CH8 Temp sense 0 */
+ VREF_MV_BASE, /* CH9 Temp sense 1 */
+ VREF_MV_BASE, /* CH10 */
+ VREF_MV_BASE, /* CH11 */
+ VREF_MV_BASE, /* CH12 USB_DP */
+ VREF_MV_BASE, /* CH13 USB_DN */
+ VREF_MV_BASE, /* CH14 VBG */
+ VREF_MV_BASE * 4, /* CH15 VDD5V */
+ },
+ [IMX28_LRADC] = {
+ VREF_MV_BASE, /* CH0 */
+ VREF_MV_BASE, /* CH1 */
+ VREF_MV_BASE, /* CH2 */
+ VREF_MV_BASE, /* CH3 */
+ VREF_MV_BASE, /* CH4 */
+ VREF_MV_BASE, /* CH5 */
+ VREF_MV_BASE, /* CH6 */
+ VREF_MV_BASE * 4, /* CH7 VBATT */
+ VREF_MV_BASE, /* CH8 Temp sense 0 */
+ VREF_MV_BASE, /* CH9 Temp sense 1 */
+ VREF_MV_BASE * 2, /* CH10 VDDIO */
+ VREF_MV_BASE, /* CH11 VTH */
+ VREF_MV_BASE * 2, /* CH12 VDDA */
+ VREF_MV_BASE, /* CH13 VDDD */
+ VREF_MV_BASE, /* CH14 VBG */
+ VREF_MV_BASE * 4, /* CH15 VDD5V */
+ },
+};
+
+enum mxs_lradc_divbytwo {
+ MXS_LRADC_DIV_DISABLED = 0,
+ MXS_LRADC_DIV_ENABLED,
+};
+
+struct mxs_lradc_scale {
+ unsigned int integer;
+ unsigned int nano;
+};
+
+struct mxs_lradc_adc {
+ struct mxs_lradc *lradc;
+ struct device *dev;
+
+ void __iomem *base;
+ /* Maximum of 8 channels + 8 byte ts */
+ u32 buffer[10] __aligned(8);
+ struct iio_trigger *trig;
+ struct completion completion;
+ spinlock_t lock;
+
+ const u32 *vref_mv;
+ struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2];
+ unsigned long is_divided;
+};
+
+
+/* Raw I/O operations */
+static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
+ int *val)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio_dev);
+ struct mxs_lradc *lradc = adc->lradc;
+ int ret;
+
+ /*
+ * See if there is no buffered operation in progress. If there is simply
+ * bail out. This can be improved to support both buffered and raw IO at
+ * 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;
+
+ reinit_completion(&adc->completion);
+
+ /*
+ * No buffered operation in progress, map the channel and trigger it.
+ * Virtual channel 0 is always used here as the others are always not
+ * used if doing raw sampling.
+ */
+ if (lradc->soc == IMX28_LRADC)
+ writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
+ adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ /* Enable / disable the divider per requirement */
+ if (test_bit(chan, &adc->is_divided))
+ writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
+ adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
+ else
+ writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
+ adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
+
+ /* Clean the slot's previous content, then set new one. */
+ writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
+ adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
+ writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
+
+ writel(0, adc->base + LRADC_CH(0));
+
+ /* Enable the IRQ and start sampling the channel. */
+ writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
+ adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+ writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+
+ /* Wait for completion on the channel, 1 second max. */
+ ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
+ if (!ret)
+ ret = -ETIMEDOUT;
+ if (ret < 0)
+ goto err;
+
+ /* Read the data. */
+ *val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
+ ret = IIO_VAL_INT;
+
+err:
+ writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
+ adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ iio_device_release_direct_mode(iio_dev);
+
+ return ret;
+}
+
+static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
+{
+ int ret, min, max;
+
+ ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
+ if (ret != IIO_VAL_INT)
+ return ret;
+
+ ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
+ if (ret != IIO_VAL_INT)
+ return ret;
+
+ *val = max - min;
+
+ return IIO_VAL_INT;
+}
+
+static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long m)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_TEMP)
+ return mxs_lradc_adc_read_temp(iio_dev, val);
+
+ return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ /*
+ * From the datasheet, we have to multiply by 1.012 and
+ * divide by 4
+ */
+ *val = 0;
+ *val2 = 253000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ *val = adc->vref_mv[chan->channel];
+ *val2 = chan->scan_type.realbits -
+ test_bit(chan->channel, &adc->is_divided);
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ /*
+ * The calculated value from the ADC is in Kelvin, we
+ * want Celsius for hwmon so the offset is -273.15
+ * The offset is applied before scaling so it is
+ * actually -213.15 * 4 / 1.012 = -1079.644268
+ */
+ *val = -1079;
+ *val2 = 644268;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long m)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio_dev);
+ struct mxs_lradc_scale *scale_avail =
+ adc->scale_avail[chan->channel];
+ int ret;
+
+ ret = iio_device_claim_direct_mode(iio_dev);
+ if (ret)
+ return ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = -EINVAL;
+ if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
+ val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
+ /* divider by two disabled */
+ clear_bit(chan->channel, &adc->is_divided);
+ ret = 0;
+ } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
+ val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
+ /* divider by two enabled */
+ set_bit(chan->channel, &adc->is_divided);
+ ret = 0;
+ }
+
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(iio_dev);
+
+ return ret;
+}
+
+static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ long m)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *iio = dev_to_iio_dev(dev);
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
+ int i, ch, len = 0;
+
+ ch = iio_attr->address;
+ for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
+ len += sprintf(buf + len, "%u.%09u ",
+ adc->scale_avail[ch][i].integer,
+ adc->scale_avail[ch][i].nano);
+
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+#define SHOW_SCALE_AVAILABLE_ATTR(ch)\
+ IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\
+ mxs_lradc_adc_show_scale_avail, NULL, ch)
+
+static SHOW_SCALE_AVAILABLE_ATTR(0);
+static SHOW_SCALE_AVAILABLE_ATTR(1);
+static SHOW_SCALE_AVAILABLE_ATTR(2);
+static SHOW_SCALE_AVAILABLE_ATTR(3);
+static SHOW_SCALE_AVAILABLE_ATTR(4);
+static SHOW_SCALE_AVAILABLE_ATTR(5);
+static SHOW_SCALE_AVAILABLE_ATTR(6);
+static SHOW_SCALE_AVAILABLE_ATTR(7);
+static SHOW_SCALE_AVAILABLE_ATTR(10);
+static SHOW_SCALE_AVAILABLE_ATTR(11);
+static SHOW_SCALE_AVAILABLE_ATTR(12);
+static SHOW_SCALE_AVAILABLE_ATTR(13);
+static SHOW_SCALE_AVAILABLE_ATTR(14);
+static SHOW_SCALE_AVAILABLE_ATTR(15);
+
+static struct attribute *mxs_lradc_adc_attributes[] = {
+ &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mxs_lradc_adc_attribute_group = {
+ .attrs = mxs_lradc_adc_attributes,
+};
+
+static const struct iio_info mxs_lradc_adc_iio_info = {
+ .read_raw = mxs_lradc_adc_read_raw,
+ .write_raw = mxs_lradc_adc_write_raw,
+ .write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt,
+ .attrs = &mxs_lradc_adc_attribute_group,
+};
+
+/* IRQ Handling */
+static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
+{
+ struct iio_dev *iio = data;
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ struct mxs_lradc *lradc = adc->lradc;
+ unsigned long reg = readl(adc->base + LRADC_CTRL1);
+ unsigned long flags;
+
+ if (!(reg & mxs_lradc_irq_mask(lradc)))
+ return IRQ_NONE;
+
+ if (iio_buffer_enabled(iio)) {
+ if (reg & lradc->buffer_vchans) {
+ spin_lock_irqsave(&adc->lock, flags);
+ iio_trigger_poll(iio->trig);
+ spin_unlock_irqrestore(&adc->lock, flags);
+ }
+ } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
+ complete(&adc->completion);
+ }
+
+ writel(reg & mxs_lradc_irq_mask(lradc),
+ adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ return IRQ_HANDLED;
+}
+
+
+/* Trigger handling */
+static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio = pf->indio_dev;
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ const u32 chan_value = LRADC_CH_ACCUMULATE |
+ ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+ unsigned int i, j = 0;
+
+ for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
+ adc->buffer[j] = readl(adc->base + LRADC_CH(j));
+ writel(chan_value, adc->base + LRADC_CH(j));
+ adc->buffer[j] &= LRADC_CH_VALUE_MASK;
+ adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
+
+ iio_trigger_notify_done(iio->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *iio = iio_trigger_get_drvdata(trig);
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
+
+ writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st));
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
+ .set_trigger_state = &mxs_lradc_adc_configure_trigger,
+};
+
+static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
+{
+ int ret;
+ struct iio_trigger *trig;
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+
+ trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
+ iio_device_id(iio));
+ if (!trig)
+ return -ENOMEM;
+
+ trig->dev.parent = adc->dev;
+ iio_trigger_set_drvdata(trig, iio);
+ trig->ops = &mxs_lradc_adc_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ return ret;
+
+ adc->trig = trig;
+
+ return 0;
+}
+
+static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+
+ iio_trigger_unregister(adc->trig);
+}
+
+static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ struct mxs_lradc *lradc = adc->lradc;
+ int chan, ofs = 0;
+ unsigned long enable = 0;
+ u32 ctrl4_set = 0;
+ u32 ctrl4_clr = 0;
+ u32 ctrl1_irq = 0;
+ const u32 chan_value = LRADC_CH_ACCUMULATE |
+ ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+
+ if (lradc->soc == IMX28_LRADC)
+ writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(lradc->buffer_vchans,
+ adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
+ ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
+ ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
+ ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
+ writel(chan_value, adc->base + LRADC_CH(ofs));
+ bitmap_set(&enable, ofs, 1);
+ ofs++;
+ }
+
+ writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
+ adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
+ writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
+ writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
+ writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+ writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
+ adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
+
+ return 0;
+}
+
+static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ struct mxs_lradc *lradc = adc->lradc;
+
+ writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
+ adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
+
+ writel(lradc->buffer_vchans,
+ adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+ if (lradc->soc == IMX28_LRADC)
+ writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ return 0;
+}
+
+static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
+ const unsigned long *mask)
+{
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+ struct mxs_lradc *lradc = adc->lradc;
+ const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
+ int rsvd_chans = 0;
+ unsigned long rsvd_mask = 0;
+
+ if (lradc->use_touchbutton)
+ rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
+ if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE)
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
+ if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+ if (lradc->use_touchbutton)
+ rsvd_chans++;
+ if (lradc->touchscreen_wire)
+ rsvd_chans += 2;
+
+ /* Test for attempts to map channels with special mode of operation. */
+ if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
+ return false;
+
+ /* Test for attempts to map more channels then available slots. */
+ if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
+ return false;
+
+ return true;
+}
+
+static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
+ .preenable = &mxs_lradc_adc_buffer_preenable,
+ .postdisable = &mxs_lradc_adc_buffer_postdisable,
+ .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
+};
+
+/* Driver initialization */
+#define MXS_ADC_CHAN(idx, chan_type, name) { \
+ .type = (chan_type), \
+ .indexed = 1, \
+ .scan_index = (idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .channel = (idx), \
+ .address = (idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = LRADC_RESOLUTION, \
+ .storagebits = 32, \
+ }, \
+ .datasheet_name = (name), \
+}
+
+static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
+ MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
+ MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
+ MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
+ MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
+ MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
+ MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
+ MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
+ MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
+ /* Combined Temperature sensors */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = 8,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .channel = 8,
+ .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
+ .datasheet_name = "TEMP_DIE",
+ },
+ /* Hidden channel to keep indexes */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = -1,
+ .channel = 9,
+ },
+ MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
+ MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
+ MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
+ MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
+ MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
+ MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
+};
+
+static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
+ MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
+ MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
+ MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
+ MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
+ MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
+ MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
+ MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
+ MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
+ /* Combined Temperature sensors */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = 8,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .channel = 8,
+ .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
+ .datasheet_name = "TEMP_DIE",
+ },
+ /* Hidden channel to keep indexes */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = -1,
+ .channel = 9,
+ },
+ MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
+ MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
+ MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
+ MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
+ MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
+ MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
+};
+
+static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
+{
+ /* The ADC always uses DELAY CHANNEL 0. */
+ const u32 adc_cfg =
+ (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
+ (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
+
+ /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
+ writel(adc_cfg, adc->base + LRADC_DELAY(0));
+
+ /*
+ * Start internal temperature sensing by clearing bit
+ * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared
+ * after power up.
+ */
+ writel(0, adc->base + LRADC_CTRL2);
+}
+
+static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
+{
+ writel(0, adc->base + LRADC_DELAY(0));
+}
+
+static int mxs_lradc_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
+ struct mxs_lradc_adc *adc;
+ struct iio_dev *iio;
+ struct resource *iores;
+ int ret, irq, virq, i, s, n;
+ u64 scale_uv;
+ const char **irq_name;
+
+ /* Allocate the IIO device. */
+ iio = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!iio) {
+ dev_err(dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(iio);
+ adc->lradc = lradc;
+ adc->dev = dev;
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!iores)
+ return -EINVAL;
+
+ adc->base = devm_ioremap(dev, iores->start, resource_size(iores));
+ if (!adc->base)
+ return -ENOMEM;
+
+ init_completion(&adc->completion);
+ spin_lock_init(&adc->lock);
+
+ platform_set_drvdata(pdev, iio);
+
+ iio->name = pdev->name;
+ iio->dev.of_node = dev->parent->of_node;
+ iio->info = &mxs_lradc_adc_iio_info;
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->masklength = LRADC_MAX_TOTAL_CHANS;
+
+ if (lradc->soc == IMX23_LRADC) {
+ iio->channels = mx23_lradc_chan_spec;
+ iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
+ irq_name = mx23_lradc_adc_irq_names;
+ n = ARRAY_SIZE(mx23_lradc_adc_irq_names);
+ } else {
+ iio->channels = mx28_lradc_chan_spec;
+ iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
+ irq_name = mx28_lradc_adc_irq_names;
+ n = ARRAY_SIZE(mx28_lradc_adc_irq_names);
+ }
+
+ ret = stmp_reset_block(adc->base);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < n; i++) {
+ irq = platform_get_irq_byname(pdev, irq_name[i]);
+ if (irq < 0)
+ return irq;
+
+ virq = irq_of_parse_and_map(dev->parent->of_node, irq);
+
+ ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq,
+ 0, irq_name[i], iio);
+ if (ret)
+ return ret;
+ }
+
+ ret = mxs_lradc_adc_trigger_init(iio);
+ if (ret)
+ return ret;
+
+ ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
+ &mxs_lradc_adc_trigger_handler,
+ &mxs_lradc_adc_buffer_ops);
+ if (ret)
+ goto err_trig;
+
+ adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
+ for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
+ /*
+ * [s=0] = optional divider by two disabled (default)
+ * [s=1] = optional divider by two enabled
+ *
+ * The scale is calculated by doing:
+ * Vref >> (realbits - s)
+ * which multiplies by two on the second component
+ * of the array.
+ */
+ scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
+ (LRADC_RESOLUTION - s);
+ adc->scale_avail[i][s].nano =
+ do_div(scale_uv, 100000000) * 10;
+ adc->scale_avail[i][s].integer = scale_uv;
+ }
+ }
+
+ /* Configure the hardware. */
+ mxs_lradc_adc_hw_init(adc);
+
+ /* Register IIO device. */
+ ret = iio_device_register(iio);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device\n");
+ goto err_dev;
+ }
+
+ return 0;
+
+err_dev:
+ mxs_lradc_adc_hw_stop(adc);
+ iio_triggered_buffer_cleanup(iio);
+err_trig:
+ mxs_lradc_adc_trigger_remove(iio);
+ return ret;
+}
+
+static int mxs_lradc_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *iio = platform_get_drvdata(pdev);
+ struct mxs_lradc_adc *adc = iio_priv(iio);
+
+ iio_device_unregister(iio);
+ mxs_lradc_adc_hw_stop(adc);
+ iio_triggered_buffer_cleanup(iio);
+ mxs_lradc_adc_trigger_remove(iio);
+
+ return 0;
+}
+
+static struct platform_driver mxs_lradc_adc_driver = {
+ .driver = {
+ .name = "mxs-lradc-adc",
+ },
+ .probe = mxs_lradc_adc_probe,
+ .remove = mxs_lradc_adc_remove,
+};
+module_platform_driver(mxs_lradc_adc_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxs-lradc-adc");
diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c
new file mode 100644
index 000000000..c1261ecd4
--- /dev/null
+++ b/drivers/iio/adc/nau7802.c
@@ -0,0 +1,560 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for the Nuvoton NAU7802 ADC
+ *
+ * Copyright 2013 Free Electrons
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/wait.h>
+#include <linux/log2.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define NAU7802_REG_PUCTRL 0x00
+#define NAU7802_PUCTRL_RR(x) (x << 0)
+#define NAU7802_PUCTRL_RR_BIT NAU7802_PUCTRL_RR(1)
+#define NAU7802_PUCTRL_PUD(x) (x << 1)
+#define NAU7802_PUCTRL_PUD_BIT NAU7802_PUCTRL_PUD(1)
+#define NAU7802_PUCTRL_PUA(x) (x << 2)
+#define NAU7802_PUCTRL_PUA_BIT NAU7802_PUCTRL_PUA(1)
+#define NAU7802_PUCTRL_PUR(x) (x << 3)
+#define NAU7802_PUCTRL_PUR_BIT NAU7802_PUCTRL_PUR(1)
+#define NAU7802_PUCTRL_CS(x) (x << 4)
+#define NAU7802_PUCTRL_CS_BIT NAU7802_PUCTRL_CS(1)
+#define NAU7802_PUCTRL_CR(x) (x << 5)
+#define NAU7802_PUCTRL_CR_BIT NAU7802_PUCTRL_CR(1)
+#define NAU7802_PUCTRL_AVDDS(x) (x << 7)
+#define NAU7802_PUCTRL_AVDDS_BIT NAU7802_PUCTRL_AVDDS(1)
+#define NAU7802_REG_CTRL1 0x01
+#define NAU7802_CTRL1_VLDO(x) (x << 3)
+#define NAU7802_CTRL1_GAINS(x) (x)
+#define NAU7802_CTRL1_GAINS_BITS 0x07
+#define NAU7802_REG_CTRL2 0x02
+#define NAU7802_CTRL2_CHS(x) (x << 7)
+#define NAU7802_CTRL2_CRS(x) (x << 4)
+#define NAU7802_SAMP_FREQ_320 0x07
+#define NAU7802_CTRL2_CHS_BIT NAU7802_CTRL2_CHS(1)
+#define NAU7802_REG_ADC_B2 0x12
+#define NAU7802_REG_ADC_B1 0x13
+#define NAU7802_REG_ADC_B0 0x14
+#define NAU7802_REG_ADC_CTRL 0x15
+
+#define NAU7802_MIN_CONVERSIONS 6
+
+struct nau7802_state {
+ struct i2c_client *client;
+ s32 last_value;
+ struct mutex lock;
+ struct mutex data_lock;
+ u32 vref_mv;
+ u32 conversion_count;
+ u32 min_conversions;
+ u8 sample_rate;
+ u32 scale_avail[8];
+ struct completion value_ok;
+};
+
+#define NAU7802_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan), \
+ .scan_index = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) \
+}
+
+static const struct iio_chan_spec nau7802_chan_array[] = {
+ NAU7802_CHANNEL(0),
+ NAU7802_CHANNEL(1),
+};
+
+static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80,
+ 10, 10, 10, 320};
+
+static ssize_t nau7802_show_scales(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev));
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ",
+ st->scale_avail[i]);
+
+ buf[len-1] = '\n';
+
+ return len;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320");
+
+static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales,
+ NULL, 0);
+
+static struct attribute *nau7802_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group nau7802_attribute_group = {
+ .attrs = nau7802_attributes,
+};
+
+static int nau7802_set_gain(struct nau7802_state *st, int gain)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ st->conversion_count = 0;
+
+ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1);
+ if (ret < 0)
+ goto nau7802_sysfs_set_gain_out;
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1,
+ (ret & (~NAU7802_CTRL1_GAINS_BITS)) |
+ gain);
+
+nau7802_sysfs_set_gain_out:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int nau7802_read_conversion(struct nau7802_state *st)
+{
+ int data;
+
+ mutex_lock(&st->data_lock);
+ data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B2);
+ if (data < 0)
+ goto nau7802_read_conversion_out;
+ st->last_value = data << 16;
+
+ data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B1);
+ if (data < 0)
+ goto nau7802_read_conversion_out;
+ st->last_value |= data << 8;
+
+ data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B0);
+ if (data < 0)
+ goto nau7802_read_conversion_out;
+ st->last_value |= data;
+
+ st->last_value = sign_extend32(st->last_value, 23);
+
+nau7802_read_conversion_out:
+ mutex_unlock(&st->data_lock);
+
+ return data;
+}
+
+/*
+ * Conversions are synchronised on the rising edge of NAU7802_PUCTRL_CS_BIT
+ */
+static int nau7802_sync(struct nau7802_state *st)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
+ ret | NAU7802_PUCTRL_CS_BIT);
+
+ return ret;
+}
+
+static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct nau7802_state *st = iio_priv(indio_dev);
+ int status;
+
+ status = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
+ if (status < 0)
+ return IRQ_HANDLED;
+
+ if (!(status & NAU7802_PUCTRL_CR_BIT))
+ return IRQ_NONE;
+
+ if (nau7802_read_conversion(st) < 0)
+ return IRQ_HANDLED;
+
+ /*
+ * Because there is actually only one ADC for both channels, we have to
+ * wait for enough conversions to happen before getting a significant
+ * value when changing channels and the values are far apart.
+ */
+ if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
+ st->conversion_count++;
+ if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
+ complete(&st->value_ok);
+
+ return IRQ_HANDLED;
+}
+
+static int nau7802_read_irq(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct nau7802_state *st = iio_priv(indio_dev);
+ int ret;
+
+ reinit_completion(&st->value_ok);
+ enable_irq(st->client->irq);
+
+ nau7802_sync(st);
+
+ /* read registers to ensure we flush everything */
+ ret = nau7802_read_conversion(st);
+ if (ret < 0)
+ goto read_chan_info_failure;
+
+ /* Wait for a conversion to finish */
+ ret = wait_for_completion_interruptible_timeout(&st->value_ok,
+ msecs_to_jiffies(1000));
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+
+ if (ret < 0)
+ goto read_chan_info_failure;
+
+ disable_irq(st->client->irq);
+
+ *val = st->last_value;
+
+ return IIO_VAL_INT;
+
+read_chan_info_failure:
+ disable_irq(st->client->irq);
+
+ return ret;
+}
+
+static int nau7802_read_poll(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct nau7802_state *st = iio_priv(indio_dev);
+ int ret;
+
+ nau7802_sync(st);
+
+ /* read registers to ensure we flush everything */
+ ret = nau7802_read_conversion(st);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Because there is actually only one ADC for both channels, we have to
+ * wait for enough conversions to happen before getting a significant
+ * value when changing channels and the values are far appart.
+ */
+ do {
+ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
+ if (ret < 0)
+ return ret;
+
+ while (!(ret & NAU7802_PUCTRL_CR_BIT)) {
+ if (st->sample_rate != NAU7802_SAMP_FREQ_320)
+ msleep(20);
+ else
+ mdelay(4);
+ ret = i2c_smbus_read_byte_data(st->client,
+ NAU7802_REG_PUCTRL);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = nau7802_read_conversion(st);
+ if (ret < 0)
+ return ret;
+ if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
+ st->conversion_count++;
+ } while (st->conversion_count < NAU7802_MIN_CONVERSIONS);
+
+ *val = st->last_value;
+
+ return IIO_VAL_INT;
+}
+
+static int nau7802_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct nau7802_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ /*
+ * Select the channel to use
+ * - Channel 1 is value 0 in the CHS register
+ * - Channel 2 is value 1 in the CHS register
+ */
+ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ if (((ret & NAU7802_CTRL2_CHS_BIT) && !chan->channel) ||
+ (!(ret & NAU7802_CTRL2_CHS_BIT) &&
+ chan->channel)) {
+ st->conversion_count = 0;
+ ret = i2c_smbus_write_byte_data(st->client,
+ NAU7802_REG_CTRL2,
+ NAU7802_CTRL2_CHS(chan->channel) |
+ NAU7802_CTRL2_CRS(st->sample_rate));
+
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ }
+
+ if (st->client->irq)
+ ret = nau7802_read_irq(indio_dev, chan, val);
+ else
+ ret = nau7802_read_poll(indio_dev, chan, val);
+
+ mutex_unlock(&st->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * We have 24 bits of signed data, that means 23 bits of data
+ * plus the sign bit
+ */
+ *val = st->vref_mv;
+ *val2 = 23 + (ret & NAU7802_CTRL1_GAINS_BITS);
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = nau7802_sample_freq_avail[st->sample_rate];
+ *val2 = 0;
+ return IIO_VAL_INT;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int nau7802_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct nau7802_state *st = iio_priv(indio_dev);
+ int i, ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ if (val2 == st->scale_avail[i])
+ return nau7802_set_gain(st, i);
+
+ break;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < ARRAY_SIZE(nau7802_sample_freq_avail); i++)
+ if (val == nau7802_sample_freq_avail[i]) {
+ mutex_lock(&st->lock);
+ st->sample_rate = i;
+ st->conversion_count = 0;
+ ret = i2c_smbus_write_byte_data(st->client,
+ NAU7802_REG_CTRL2,
+ NAU7802_CTRL2_CRS(st->sample_rate));
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int nau7802_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static const struct iio_info nau7802_info = {
+ .read_raw = &nau7802_read_raw,
+ .write_raw = &nau7802_write_raw,
+ .write_raw_get_fmt = nau7802_write_raw_get_fmt,
+ .attrs = &nau7802_attribute_group,
+};
+
+static int nau7802_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct nau7802_state *st;
+ int i, ret;
+ u8 data;
+ u32 tmp = 0;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &nau7802_info;
+
+ st->client = client;
+
+ /* Reset the device */
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
+ NAU7802_PUCTRL_RR_BIT);
+ if (ret < 0)
+ return ret;
+
+ /* Enter normal operation mode */
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
+ NAU7802_PUCTRL_PUD_BIT);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * After about 200 usecs, the device should be ready and then
+ * the Power Up bit will be set to 1. If not, wait for it.
+ */
+ udelay(210);
+ ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
+ if (ret < 0)
+ return ret;
+ if (!(ret & NAU7802_PUCTRL_PUR_BIT))
+ return ret;
+
+ device_property_read_u32(&client->dev, "nuvoton,vldo", &tmp);
+ st->vref_mv = tmp;
+
+ data = NAU7802_PUCTRL_PUD_BIT | NAU7802_PUCTRL_PUA_BIT |
+ NAU7802_PUCTRL_CS_BIT;
+ if (tmp >= 2400)
+ data |= NAU7802_PUCTRL_AVDDS_BIT;
+
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, data);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_ADC_CTRL, 0x30);
+ if (ret < 0)
+ return ret;
+
+ if (tmp >= 2400) {
+ data = NAU7802_CTRL1_VLDO((4500 - tmp) / 300);
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1,
+ data);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ st->scale_avail[i] = (((u64)st->vref_mv) * 1000000000ULL)
+ >> (23 + i);
+
+ init_completion(&st->value_ok);
+
+ /*
+ * The ADC fires continuously and we can't do anything about
+ * it. So we need to have the IRQ disabled by default, and we
+ * will enable them back when we will need them..
+ */
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL,
+ nau7802_eoc_trigger,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT |
+ IRQF_NO_AUTOEN,
+ client->dev.driver->name,
+ indio_dev);
+ if (ret) {
+ /*
+ * What may happen here is that our IRQ controller is
+ * not able to get level interrupt but this is required
+ * by this ADC as when going over 40 sample per second,
+ * the interrupt line may stay high between conversions.
+ * So, we continue no matter what but we switch to
+ * polling mode.
+ */
+ dev_info(&client->dev,
+ "Failed to allocate IRQ, using polling mode\n");
+ client->irq = 0;
+ }
+ }
+
+ if (!client->irq) {
+ /*
+ * We are polling, use the fastest sample rate by
+ * default
+ */
+ st->sample_rate = NAU7802_SAMP_FREQ_320;
+ ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2,
+ NAU7802_CTRL2_CRS(st->sample_rate));
+ if (ret)
+ return ret;
+ }
+
+ /* Setup the ADC channels available on the board */
+ indio_dev->num_channels = ARRAY_SIZE(nau7802_chan_array);
+ indio_dev->channels = nau7802_chan_array;
+
+ mutex_init(&st->lock);
+ mutex_init(&st->data_lock);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id nau7802_i2c_id[] = {
+ { "nau7802", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);
+
+static const struct of_device_id nau7802_dt_ids[] = {
+ { .compatible = "nuvoton,nau7802" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, nau7802_dt_ids);
+
+static struct i2c_driver nau7802_driver = {
+ .probe_new = nau7802_probe,
+ .id_table = nau7802_i2c_id,
+ .driver = {
+ .name = "nau7802",
+ .of_match_table = nau7802_dt_ids,
+ },
+};
+
+module_i2c_driver(nau7802_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nuvoton NAU7802 ADC Driver");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
new file mode 100644
index 000000000..ba4cd8f49
--- /dev/null
+++ b/drivers/iio/adc/npcm_adc.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/reset.h>
+
+struct npcm_adc_info {
+ u32 data_mask;
+ u32 internal_vref;
+ u32 res_bits;
+};
+
+struct npcm_adc {
+ bool int_status;
+ u32 adc_sample_hz;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *adc_clk;
+ wait_queue_head_t wq;
+ struct regulator *vref;
+ struct reset_control *reset;
+ /*
+ * Lock to protect the device state during a potential concurrent
+ * read access from userspace. Reading a raw value requires a sequence
+ * of register writes, then a wait for a event and finally a register
+ * read, during which userspace could issue another read request.
+ * This lock protects a read access from ocurring before another one
+ * has finished.
+ */
+ struct mutex lock;
+ const struct npcm_adc_info *data;
+};
+
+/* ADC registers */
+#define NPCM_ADCCON 0x00
+#define NPCM_ADCDATA 0x04
+
+/* ADCCON Register Bits */
+#define NPCM_ADCCON_ADC_INT_EN BIT(21)
+#define NPCM_ADCCON_REFSEL BIT(19)
+#define NPCM_ADCCON_ADC_INT_ST BIT(18)
+#define NPCM_ADCCON_ADC_EN BIT(17)
+#define NPCM_ADCCON_ADC_RST BIT(16)
+#define NPCM_ADCCON_ADC_CONV BIT(13)
+
+#define NPCM_ADCCON_CH_MASK GENMASK(27, 24)
+#define NPCM_ADCCON_CH(x) ((x) << 24)
+#define NPCM_ADCCON_DIV_SHIFT 1
+#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1)
+
+#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
+
+/* ADC General Definition */
+static const struct npcm_adc_info npxm7xx_adc_info = {
+ .data_mask = GENMASK(9, 0),
+ .internal_vref = 2048,
+ .res_bits = 10,
+};
+
+static const struct npcm_adc_info npxm8xx_adc_info = {
+ .data_mask = GENMASK(11, 0),
+ .internal_vref = 1229,
+ .res_bits = 12,
+};
+
+#define NPCM_ADC_CHAN(ch) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = ch, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec npcm_adc_iio_channels[] = {
+ NPCM_ADC_CHAN(0),
+ NPCM_ADC_CHAN(1),
+ NPCM_ADC_CHAN(2),
+ NPCM_ADC_CHAN(3),
+ NPCM_ADC_CHAN(4),
+ NPCM_ADC_CHAN(5),
+ NPCM_ADC_CHAN(6),
+ NPCM_ADC_CHAN(7),
+};
+
+static irqreturn_t npcm_adc_isr(int irq, void *data)
+{
+ u32 regtemp;
+ struct iio_dev *indio_dev = data;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
+ iowrite32(regtemp, info->regs + NPCM_ADCCON);
+ wake_up_interruptible(&info->wq);
+ info->int_status = true;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
+{
+ int ret;
+ u32 regtemp;
+
+ /* Select ADC channel */
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ regtemp &= ~NPCM_ADCCON_CH_MASK;
+ info->int_status = false;
+ iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
+ NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ ret = wait_event_interruptible_timeout(info->wq, info->int_status,
+ msecs_to_jiffies(10));
+ if (ret == 0) {
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if (regtemp & NPCM_ADCCON_ADC_CONV) {
+ /* if conversion failed - reset ADC module */
+ reset_control_assert(info->reset);
+ msleep(100);
+ reset_control_deassert(info->reset);
+ msleep(100);
+
+ /* Enable ADC and start conversion module */
+ iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
+ info->regs + NPCM_ADCCON);
+ dev_err(info->dev, "RESET ADC Complete\n");
+ }
+ return -ETIMEDOUT;
+ }
+ if (ret < 0)
+ return ret;
+
+ *val = ioread32(info->regs + NPCM_ADCDATA);
+ *val &= info->data->data_mask;
+
+ return 0;
+}
+
+static int npcm_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ int vref_uv;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&info->lock);
+ ret = npcm_adc_read(info, val, chan->channel);
+ mutex_unlock(&info->lock);
+ if (ret) {
+ dev_err(info->dev, "NPCM ADC read failed\n");
+ return ret;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (!IS_ERR(info->vref)) {
+ vref_uv = regulator_get_voltage(info->vref);
+ *val = vref_uv / 1000;
+ } else {
+ *val = info->data->internal_vref;
+ }
+ *val2 = info->data->res_bits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = info->adc_sample_hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info npcm_adc_iio_info = {
+ .read_raw = &npcm_adc_read_raw,
+};
+
+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);
+
+static int npcm_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq;
+ u32 div;
+ u32 reg_con;
+ struct npcm_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+ info = iio_priv(indio_dev);
+
+ info->data = device_get_match_data(dev);
+ if (!info->data)
+ return -EINVAL;
+
+ mutex_init(&info->lock);
+
+ info->dev = &pdev->dev;
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->reset = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(info->reset))
+ return PTR_ERR(info->reset);
+
+ info->adc_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->adc_clk)) {
+ dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
+ return PTR_ERR(info->adc_clk);
+ }
+
+ /* calculate ADC clock sample rate */
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ div = reg_con & NPCM_ADCCON_DIV_MASK;
+ div = div >> NPCM_ADCCON_DIV_SHIFT;
+ info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto err_disable_clk;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
+ "NPCM_ADC", indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto err_disable_clk;
+ }
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
+ if (!IS_ERR(info->vref)) {
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
+ goto err_disable_clk;
+ }
+
+ iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ } else {
+ /*
+ * Any error which is not ENODEV indicates the regulator
+ * has been specified and so is a failure case.
+ */
+ if (PTR_ERR(info->vref) != -ENODEV) {
+ ret = PTR_ERR(info->vref);
+ goto err_disable_clk;
+ }
+
+ /* Use internal reference */
+ iowrite32(reg_con | NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ }
+
+ init_waitqueue_head(&info->wq);
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ reg_con |= NPCM_ADC_ENABLE;
+
+ /* Enable the ADC Module */
+ iowrite32(reg_con, info->regs + NPCM_ADCCON);
+
+ /* Start ADC conversion */
+ iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ platform_set_drvdata(pdev, indio_dev);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &npcm_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = npcm_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto err_iio_register;
+ }
+
+ pr_info("NPCM ADC driver probed\n");
+
+ return 0;
+
+err_iio_register:
+ iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+err_disable_clk:
+ clk_disable_unprepare(info->adc_clk);
+
+ return ret;
+}
+
+static int npcm_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct npcm_adc *info = iio_priv(indio_dev);
+ u32 regtemp;
+
+ iio_device_unregister(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+ clk_disable_unprepare(info->adc_clk);
+
+ return 0;
+}
+
+static struct platform_driver npcm_adc_driver = {
+ .probe = npcm_adc_probe,
+ .remove = npcm_adc_remove,
+ .driver = {
+ .name = "npcm_adc",
+ .of_match_table = npcm_adc_match,
+ },
+};
+
+module_platform_driver(npcm_adc_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c
new file mode 100644
index 000000000..849a697a4
--- /dev/null
+++ b/drivers/iio/adc/palmas_gpadc.c
@@ -0,0 +1,849 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * palmas-adc.c -- TI PALMAS GPADC.
+ *
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Pradeep Goudagunta <pgoudagunta@nvidia.com>
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/mfd/palmas.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+#define MOD_NAME "palmas-gpadc"
+#define PALMAS_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(5000))
+#define PALMAS_TO_BE_CALCULATED 0
+#define PALMAS_GPADC_TRIMINVALID -1
+
+struct palmas_gpadc_info {
+/* calibration codes and regs */
+ int x1; /* lower ideal code */
+ int x2; /* higher ideal code */
+ int v1; /* expected lower volt reading */
+ int v2; /* expected higher volt reading */
+ u8 trim1_reg; /* register number for lower trim */
+ u8 trim2_reg; /* register number for upper trim */
+ int gain; /* calculated from above (after reading trim regs) */
+ int offset; /* calculated from above (after reading trim regs) */
+ int gain_error; /* calculated from above (after reading trim regs) */
+ bool is_uncalibrated; /* if channel has calibration data */
+};
+
+#define PALMAS_ADC_INFO(_chan, _x1, _x2, _v1, _v2, _t1, _t2, _is_uncalibrated) \
+ [PALMAS_ADC_CH_##_chan] = { \
+ .x1 = _x1, \
+ .x2 = _x2, \
+ .v1 = _v1, \
+ .v2 = _v2, \
+ .gain = PALMAS_TO_BE_CALCULATED, \
+ .offset = PALMAS_TO_BE_CALCULATED, \
+ .gain_error = PALMAS_TO_BE_CALCULATED, \
+ .trim1_reg = PALMAS_GPADC_TRIM##_t1, \
+ .trim2_reg = PALMAS_GPADC_TRIM##_t2, \
+ .is_uncalibrated = _is_uncalibrated \
+ }
+
+static struct palmas_gpadc_info palmas_gpadc_info[] = {
+ PALMAS_ADC_INFO(IN0, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN1, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN2, 2064, 3112, 1260, 1900, 3, 4, false),
+ PALMAS_ADC_INFO(IN3, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN4, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN5, 2064, 3112, 630, 950, 1, 2, false),
+ PALMAS_ADC_INFO(IN6, 2064, 3112, 2520, 3800, 5, 6, false),
+ PALMAS_ADC_INFO(IN7, 2064, 3112, 2520, 3800, 7, 8, false),
+ PALMAS_ADC_INFO(IN8, 2064, 3112, 3150, 4750, 9, 10, false),
+ PALMAS_ADC_INFO(IN9, 2064, 3112, 5670, 8550, 11, 12, false),
+ PALMAS_ADC_INFO(IN10, 2064, 3112, 3465, 5225, 13, 14, false),
+ PALMAS_ADC_INFO(IN11, 0, 0, 0, 0, INVALID, INVALID, true),
+ PALMAS_ADC_INFO(IN12, 0, 0, 0, 0, INVALID, INVALID, true),
+ PALMAS_ADC_INFO(IN13, 0, 0, 0, 0, INVALID, INVALID, true),
+ PALMAS_ADC_INFO(IN14, 2064, 3112, 3645, 5225, 15, 16, false),
+ PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true),
+};
+
+/*
+ * struct palmas_gpadc - the palmas_gpadc structure
+ * @ch0_current: channel 0 current source setting
+ * 0: 0 uA
+ * 1: 5 uA
+ * 2: 15 uA
+ * 3: 20 uA
+ * @ch3_current: channel 0 current source setting
+ * 0: 0 uA
+ * 1: 10 uA
+ * 2: 400 uA
+ * 3: 800 uA
+ * @extended_delay: enable the gpadc extended delay mode
+ * @auto_conversion_period: define the auto_conversion_period
+ * @lock: Lock to protect the device state during a potential concurrent
+ * read access from userspace. Reading a raw value requires a sequence
+ * of register writes, then a wait for a completion callback,
+ * and finally a register read, during which userspace could issue
+ * another read request. This lock protects a read access from
+ * ocurring before another one has finished.
+ *
+ * This is the palmas_gpadc structure to store run-time information
+ * and pointers for this driver instance.
+ */
+struct palmas_gpadc {
+ struct device *dev;
+ struct palmas *palmas;
+ u8 ch0_current;
+ u8 ch3_current;
+ bool extended_delay;
+ int irq;
+ int irq_auto_0;
+ int irq_auto_1;
+ struct palmas_gpadc_info *adc_info;
+ struct completion conv_completion;
+ struct palmas_adc_wakeup_property wakeup1_data;
+ struct palmas_adc_wakeup_property wakeup2_data;
+ bool wakeup1_enable;
+ bool wakeup2_enable;
+ int auto_conversion_period;
+ struct mutex lock;
+};
+
+/*
+ * GPADC lock issue in AUTO mode.
+ * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO
+ * mode feature.
+ * Details:
+ * When the AUTO mode is the only conversion mode enabled, if the AUTO
+ * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0
+ * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the
+ * conversion mechanism can be seen as locked meaning that all following
+ * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE
+ * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock
+ * the GPADC.
+ *
+ * Workaround(s):
+ * To avoid the lock mechanism, the workaround to follow before any stop
+ * conversion request is:
+ * Force the GPADC state machine to be ON by using the GPADC_CTRL1.
+ * GPADC_FORCE bit = 1
+ * Shutdown the GPADC AUTO conversion using
+ * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0.
+ * After 100us, force the GPADC state machine to be OFF by using the
+ * GPADC_CTRL1. GPADC_FORCE bit = 0
+ */
+
+static int palmas_disable_auto_conversion(struct palmas_gpadc *adc)
+{
+ int ret;
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE);
+ if (ret < 0) {
+ dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 |
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0,
+ 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret);
+ return ret;
+ }
+
+ udelay(100);
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
+ if (ret < 0)
+ dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret);
+
+ return ret;
+}
+
+static irqreturn_t palmas_gpadc_irq(int irq, void *data)
+{
+ struct palmas_gpadc *adc = data;
+
+ complete(&adc->conv_completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data)
+{
+ struct palmas_gpadc *adc = data;
+
+ dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq);
+ palmas_disable_auto_conversion(adc);
+
+ return IRQ_HANDLED;
+}
+
+static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc,
+ bool mask)
+{
+ int ret;
+
+ if (!mask)
+ ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE,
+ PALMAS_INT3_MASK,
+ PALMAS_INT3_MASK_GPADC_EOC_SW, 0);
+ else
+ ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE,
+ PALMAS_INT3_MASK,
+ PALMAS_INT3_MASK_GPADC_EOC_SW,
+ PALMAS_INT3_MASK_GPADC_EOC_SW);
+ if (ret < 0)
+ dev_err(adc->dev, "GPADC INT MASK update failed: %d\n", ret);
+
+ return ret;
+}
+
+static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan,
+ int enable)
+{
+ unsigned int mask, val;
+ int ret;
+
+ if (enable) {
+ val = (adc->extended_delay
+ << PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT);
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_RT_CTRL,
+ PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val);
+ if (ret < 0) {
+ dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret);
+ return ret;
+ }
+
+ mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK |
+ PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK |
+ PALMAS_GPADC_CTRL1_GPADC_FORCE);
+ val = (adc->ch0_current
+ << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT);
+ val |= (adc->ch3_current
+ << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT);
+ val |= PALMAS_GPADC_CTRL1_GPADC_FORCE;
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1, mask, val);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "Failed to update current setting: %d\n", ret);
+ return ret;
+ }
+
+ mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK |
+ PALMAS_GPADC_SW_SELECT_SW_CONV_EN);
+ val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN);
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_SELECT, mask, val);
+ if (ret < 0) {
+ dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_SELECT, 0);
+ if (ret < 0)
+ dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret);
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_CTRL1,
+ PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "CTRL1 update failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan)
+{
+ int ret;
+
+ ret = palmas_gpadc_enable(adc, adc_chan, true);
+ if (ret < 0)
+ return ret;
+
+ return palmas_gpadc_start_mask_interrupt(adc, 0);
+}
+
+static void palmas_gpadc_read_done(struct palmas_gpadc *adc, int adc_chan)
+{
+ palmas_gpadc_start_mask_interrupt(adc, 1);
+ palmas_gpadc_enable(adc, adc_chan, false);
+}
+
+static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan)
+{
+ int k;
+ int d1;
+ int d2;
+ int ret;
+ int gain;
+ int x1 = adc->adc_info[adc_chan].x1;
+ int x2 = adc->adc_info[adc_chan].x2;
+ int v1 = adc->adc_info[adc_chan].v1;
+ int v2 = adc->adc_info[adc_chan].v2;
+
+ ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
+ adc->adc_info[adc_chan].trim1_reg, &d1);
+ if (ret < 0) {
+ dev_err(adc->dev, "TRIM read failed: %d\n", ret);
+ goto scrub;
+ }
+
+ ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
+ adc->adc_info[adc_chan].trim2_reg, &d2);
+ if (ret < 0) {
+ dev_err(adc->dev, "TRIM read failed: %d\n", ret);
+ goto scrub;
+ }
+
+ /* gain error calculation */
+ k = (1000 + (1000 * (d2 - d1)) / (x2 - x1));
+
+ /* gain calculation */
+ gain = ((v2 - v1) * 1000) / (x2 - x1);
+
+ adc->adc_info[adc_chan].gain_error = k;
+ adc->adc_info[adc_chan].gain = gain;
+ /* offset Calculation */
+ adc->adc_info[adc_chan].offset = (d1 * 1000) - ((k - 1000) * x1);
+
+scrub:
+ return ret;
+}
+
+static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan)
+{
+ unsigned int val;
+ int ret;
+
+ init_completion(&adc->conv_completion);
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_SELECT,
+ PALMAS_GPADC_SW_SELECT_SW_START_CONV0,
+ PALMAS_GPADC_SW_SELECT_SW_START_CONV0);
+ if (ret < 0) {
+ dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&adc->conv_completion,
+ PALMAS_ADC_CONVERSION_TIMEOUT);
+ if (ret == 0) {
+ dev_err(adc->dev, "conversion not completed\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_SW_CONV0_LSB, &val, 2);
+ if (ret < 0) {
+ dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = val & 0xFFF;
+
+ return ret;
+}
+
+static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc,
+ int adc_chan, int val)
+{
+ if (!adc->adc_info[adc_chan].is_uncalibrated)
+ val = (val*1000 - adc->adc_info[adc_chan].offset) /
+ adc->adc_info[adc_chan].gain_error;
+
+ if (val < 0) {
+ if (val < -10)
+ dev_err(adc->dev, "Mismatch with calibration var = %d\n", val);
+ return 0;
+ }
+
+ val = (val * adc->adc_info[adc_chan].gain) / 1000;
+
+ return val;
+}
+
+static int palmas_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+ int adc_chan = chan->channel;
+ int ret = 0;
+
+ if (adc_chan > PALMAS_ADC_CH_MAX)
+ return -EINVAL;
+
+ mutex_lock(&adc->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = palmas_gpadc_read_prepare(adc, adc_chan);
+ if (ret < 0)
+ goto out;
+
+ ret = palmas_gpadc_start_conversion(adc, adc_chan);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "ADC start conversion failed\n");
+ goto out;
+ }
+
+ if (mask == IIO_CHAN_INFO_PROCESSED)
+ ret = palmas_gpadc_get_calibrated_code(
+ adc, adc_chan, ret);
+
+ *val = ret;
+
+ ret = IIO_VAL_INT;
+ goto out;
+ }
+
+ mutex_unlock(&adc->lock);
+ return ret;
+
+out:
+ palmas_gpadc_read_done(adc, adc_chan);
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static const struct iio_info palmas_gpadc_iio_info = {
+ .read_raw = palmas_gpadc_read_raw,
+};
+
+#define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \
+{ \
+ .datasheet_name = PALMAS_DATASHEET_NAME(chan), \
+ .type = _type, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(chan_info), \
+ .indexed = 1, \
+ .channel = PALMAS_ADC_CH_##chan, \
+}
+
+static const struct iio_chan_spec palmas_gpadc_iio_channel[] = {
+ PALMAS_ADC_CHAN_IIO(IN0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN1, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN3, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN4, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN12, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN13, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ PALMAS_ADC_CHAN_IIO(IN14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+};
+
+static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev,
+ struct palmas_gpadc_platform_data **gpadc_pdata)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct palmas_gpadc_platform_data *gp_data;
+ int ret;
+ u32 pval;
+
+ gp_data = devm_kzalloc(&pdev->dev, sizeof(*gp_data), GFP_KERNEL);
+ if (!gp_data)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(np, "ti,channel0-current-microamp", &pval);
+ if (!ret)
+ gp_data->ch0_current = pval;
+
+ ret = of_property_read_u32(np, "ti,channel3-current-microamp", &pval);
+ if (!ret)
+ gp_data->ch3_current = pval;
+
+ gp_data->extended_delay = of_property_read_bool(np,
+ "ti,enable-extended-delay");
+
+ *gpadc_pdata = gp_data;
+
+ return 0;
+}
+
+static int palmas_gpadc_probe(struct platform_device *pdev)
+{
+ struct palmas_gpadc *adc;
+ struct palmas_platform_data *pdata;
+ struct palmas_gpadc_platform_data *gpadc_pdata = NULL;
+ struct iio_dev *indio_dev;
+ int ret, i;
+
+ pdata = dev_get_platdata(pdev->dev.parent);
+
+ if (pdata && pdata->gpadc_pdata)
+ gpadc_pdata = pdata->gpadc_pdata;
+
+ if (!gpadc_pdata && pdev->dev.of_node) {
+ ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata);
+ if (ret < 0)
+ return ret;
+ }
+ if (!gpadc_pdata)
+ return -EINVAL;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "iio_device_alloc failed\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->dev = &pdev->dev;
+ adc->palmas = dev_get_drvdata(pdev->dev.parent);
+ adc->adc_info = palmas_gpadc_info;
+
+ mutex_init(&adc->lock);
+
+ init_completion(&adc->conv_completion);
+ platform_set_drvdata(pdev, indio_dev);
+
+ adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms;
+ adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ);
+ if (adc->irq < 0) {
+ dev_err(adc->dev,
+ "get virq failed: %d\n", adc->irq);
+ ret = adc->irq;
+ goto out;
+ }
+ ret = request_threaded_irq(adc->irq, NULL,
+ palmas_gpadc_irq,
+ IRQF_ONESHOT, dev_name(adc->dev),
+ adc);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "request irq %d failed: %d\n", adc->irq, ret);
+ goto out;
+ }
+
+ if (gpadc_pdata->adc_wakeup1_data) {
+ memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data,
+ sizeof(adc->wakeup1_data));
+ adc->wakeup1_enable = true;
+ adc->irq_auto_0 = platform_get_irq(pdev, 1);
+ ret = request_threaded_irq(adc->irq_auto_0, NULL,
+ palmas_gpadc_irq_auto,
+ IRQF_ONESHOT,
+ "palmas-adc-auto-0", adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "request auto0 irq %d failed: %d\n",
+ adc->irq_auto_0, ret);
+ goto out_irq_free;
+ }
+ }
+
+ if (gpadc_pdata->adc_wakeup2_data) {
+ memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data,
+ sizeof(adc->wakeup2_data));
+ adc->wakeup2_enable = true;
+ adc->irq_auto_1 = platform_get_irq(pdev, 2);
+ ret = request_threaded_irq(adc->irq_auto_1, NULL,
+ palmas_gpadc_irq_auto,
+ IRQF_ONESHOT,
+ "palmas-adc-auto-1", adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "request auto1 irq %d failed: %d\n",
+ adc->irq_auto_1, ret);
+ goto out_irq_auto0_free;
+ }
+ }
+
+ /* set the current source 0 (value 0/5/15/20 uA => 0..3) */
+ if (gpadc_pdata->ch0_current <= 1)
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0;
+ else if (gpadc_pdata->ch0_current <= 5)
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_5;
+ else if (gpadc_pdata->ch0_current <= 15)
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_15;
+ else
+ adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_20;
+
+ /* set the current source 3 (value 0/10/400/800 uA => 0..3) */
+ if (gpadc_pdata->ch3_current <= 1)
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_0;
+ else if (gpadc_pdata->ch3_current <= 10)
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_10;
+ else if (gpadc_pdata->ch3_current <= 400)
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_400;
+ else
+ adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_800;
+
+ adc->extended_delay = gpadc_pdata->extended_delay;
+
+ indio_dev->name = MOD_NAME;
+ indio_dev->info = &palmas_gpadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = palmas_gpadc_iio_channel;
+ indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(adc->dev, "iio_device_register() failed: %d\n", ret);
+ goto out_irq_auto1_free;
+ }
+
+ device_set_wakeup_capable(&pdev->dev, 1);
+ for (i = 0; i < PALMAS_ADC_CH_MAX; i++) {
+ if (!(adc->adc_info[i].is_uncalibrated))
+ palmas_gpadc_calibrate(adc, i);
+ }
+
+ if (adc->wakeup1_enable || adc->wakeup2_enable)
+ device_wakeup_enable(&pdev->dev);
+
+ return 0;
+
+out_irq_auto1_free:
+ if (gpadc_pdata->adc_wakeup2_data)
+ free_irq(adc->irq_auto_1, adc);
+out_irq_auto0_free:
+ if (gpadc_pdata->adc_wakeup1_data)
+ free_irq(adc->irq_auto_0, adc);
+out_irq_free:
+ free_irq(adc->irq, adc);
+out:
+ return ret;
+}
+
+static int palmas_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev);
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+
+ if (adc->wakeup1_enable || adc->wakeup2_enable)
+ device_wakeup_disable(&pdev->dev);
+ iio_device_unregister(indio_dev);
+ free_irq(adc->irq, adc);
+ if (adc->wakeup1_enable)
+ free_irq(adc->irq_auto_0, adc);
+ if (adc->wakeup2_enable)
+ free_irq(adc->irq_auto_1, adc);
+
+ return 0;
+}
+
+static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc)
+{
+ int adc_period, conv;
+ int i;
+ int ch0 = 0, ch1 = 0;
+ int thres;
+ int ret;
+
+ adc_period = adc->auto_conversion_period;
+ for (i = 0; i < 16; ++i) {
+ if (((1000 * (1 << i)) / 32) >= adc_period)
+ break;
+ }
+ if (i > 0)
+ i--;
+ adc_period = i;
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK,
+ adc_period);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
+ return ret;
+ }
+
+ conv = 0;
+ if (adc->wakeup1_enable) {
+ int polarity;
+
+ ch0 = adc->wakeup1_data.adc_channel_number;
+ conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
+ if (adc->wakeup1_data.adc_high_threshold > 0) {
+ thres = adc->wakeup1_data.adc_high_threshold;
+ polarity = 0;
+ } else {
+ thres = adc->wakeup1_data.adc_low_threshold;
+ polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV0_LSB write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV0_MSB,
+ ((thres >> 8) & 0xF) | polarity);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV0_MSB write failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (adc->wakeup2_enable) {
+ int polarity;
+
+ ch1 = adc->wakeup2_data.adc_channel_number;
+ conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
+ if (adc->wakeup2_data.adc_high_threshold > 0) {
+ thres = adc->wakeup2_data.adc_high_threshold;
+ polarity = 0;
+ } else {
+ thres = adc->wakeup2_data.adc_low_threshold;
+ polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV1_LSB write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV1_MSB,
+ ((thres >> 8) & 0xF) | polarity);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV1_MSB write failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN |
+ PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv);
+ if (ret < 0)
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
+
+ return ret;
+}
+
+static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc)
+{
+ int ret;
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_SELECT, 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_disable_auto_conversion(adc);
+ if (ret < 0)
+ dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret);
+
+ return ret;
+}
+
+static int palmas_gpadc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+ int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
+ int ret;
+
+ if (!device_may_wakeup(dev) || !wakeup)
+ return 0;
+
+ ret = palmas_adc_wakeup_configure(adc);
+ if (ret < 0)
+ return ret;
+
+ if (adc->wakeup1_enable)
+ enable_irq_wake(adc->irq_auto_0);
+
+ if (adc->wakeup2_enable)
+ enable_irq_wake(adc->irq_auto_1);
+
+ return 0;
+}
+
+static int palmas_gpadc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct palmas_gpadc *adc = iio_priv(indio_dev);
+ int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
+ int ret;
+
+ if (!device_may_wakeup(dev) || !wakeup)
+ return 0;
+
+ ret = palmas_adc_wakeup_reset(adc);
+ if (ret < 0)
+ return ret;
+
+ if (adc->wakeup1_enable)
+ disable_irq_wake(adc->irq_auto_0);
+
+ if (adc->wakeup2_enable)
+ disable_irq_wake(adc->irq_auto_1);
+
+ return 0;
+};
+
+static DEFINE_SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_gpadc_suspend,
+ palmas_gpadc_resume);
+
+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);
+
+static struct platform_driver palmas_gpadc_driver = {
+ .probe = palmas_gpadc_probe,
+ .remove = palmas_gpadc_remove,
+ .driver = {
+ .name = MOD_NAME,
+ .pm = pm_sleep_ptr(&palmas_pm_ops),
+ .of_match_table = of_palmas_gpadc_match_tbl,
+ },
+};
+module_platform_driver(palmas_gpadc_driver);
+
+MODULE_DESCRIPTION("palmas GPADC driver");
+MODULE_AUTHOR("Pradeep Goudagunta<pgoudagunta@nvidia.com>");
+MODULE_ALIAS("platform:palmas-gpadc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
new file mode 100644
index 000000000..eb424496e
--- /dev/null
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -0,0 +1,1028 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm PM8xxx PMIC XOADC driver
+ *
+ * These ADCs are known as HK/XO (house keeping / chrystal oscillator)
+ * "XO" in "XOADC" means Chrystal Oscillator. It's a bunch of
+ * specific-purpose and general purpose ADC converters and channels.
+ *
+ * Copyright (C) 2017 Linaro Ltd.
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+
+#include <linux/iio/adc/qcom-vadc-common.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+
+/*
+ * Definitions for the "user processor" registers lifted from the v3.4
+ * Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:
+ * drivers/misc/pmic8058-xoadc.c
+ * drivers/hwmon/pm8xxx-adc.c
+ * None of them contain any complete register specification, so this is
+ * a best effort of combining the information.
+ */
+
+/* These appear to be "battery monitor" registers */
+#define ADC_ARB_BTM_CNTRL1 0x17e
+#define ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0)
+#define ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5)
+#define ADC_ARB_BTM_CNTRL1_EOC BIT(6)
+#define ADC_ARB_BTM_CNTRL1_REQ BIT(7)
+
+#define ADC_ARB_BTM_AMUX_CNTRL 0x17f
+#define ADC_ARB_BTM_ANA_PARAM 0x180
+#define ADC_ARB_BTM_DIG_PARAM 0x181
+#define ADC_ARB_BTM_RSV 0x182
+#define ADC_ARB_BTM_DATA1 0x183
+#define ADC_ARB_BTM_DATA0 0x184
+#define ADC_ARB_BTM_BAT_COOL_THR1 0x185
+#define ADC_ARB_BTM_BAT_COOL_THR0 0x186
+#define ADC_ARB_BTM_BAT_WARM_THR1 0x187
+#define ADC_ARB_BTM_BAT_WARM_THR0 0x188
+#define ADC_ARB_BTM_CNTRL2 0x18c
+
+/* Proper ADC registers */
+
+#define ADC_ARB_USRP_CNTRL 0x197
+#define ADC_ARB_USRP_CNTRL_EN_ARB BIT(0)
+#define ADC_ARB_USRP_CNTRL_RSV1 BIT(1)
+#define ADC_ARB_USRP_CNTRL_RSV2 BIT(2)
+#define ADC_ARB_USRP_CNTRL_RSV3 BIT(3)
+#define ADC_ARB_USRP_CNTRL_RSV4 BIT(4)
+#define ADC_ARB_USRP_CNTRL_RSV5 BIT(5)
+#define ADC_ARB_USRP_CNTRL_EOC BIT(6)
+#define ADC_ARB_USRP_CNTRL_REQ BIT(7)
+
+#define ADC_ARB_USRP_AMUX_CNTRL 0x198
+/*
+ * The channel mask includes the bits selecting channel mux and prescaler
+ * on PM8058, or channel mux and premux on PM8921.
+ */
+#define ADC_ARB_USRP_AMUX_CNTRL_CHAN_MASK 0xfc
+#define ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0)
+#define ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1)
+/* On PM8058 this is prescaling, on PM8921 this is premux */
+#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX0 BIT(2)
+#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX1 BIT(3)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7)
+#define ADC_AMUX_PREMUX_SHIFT 2
+#define ADC_AMUX_SEL_SHIFT 4
+
+/* We know very little about the bits in this register */
+#define ADC_ARB_USRP_ANA_PARAM 0x199
+#define ADC_ARB_USRP_ANA_PARAM_DIS 0xFE
+#define ADC_ARB_USRP_ANA_PARAM_EN 0xFF
+
+#define ADC_ARB_USRP_DIG_PARAM 0x19A
+#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0)
+#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1)
+#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2)
+#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3)
+#define ADC_ARB_USRP_DIG_PARAM_EOC BIT(4)
+/*
+ * On a later ADC the decimation factors are defined as
+ * 00 = 512, 01 = 1024, 10 = 2048, 11 = 4096 so assume this
+ * holds also for this older XOADC.
+ */
+#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5)
+#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6)
+#define ADC_ARB_USRP_DIG_PARAM_EN BIT(7)
+#define ADC_DIG_PARAM_DEC_SHIFT 5
+
+#define ADC_ARB_USRP_RSV 0x19B
+#define ADC_ARB_USRP_RSV_RST BIT(0)
+#define ADC_ARB_USRP_RSV_DTEST0 BIT(1)
+#define ADC_ARB_USRP_RSV_DTEST1 BIT(2)
+#define ADC_ARB_USRP_RSV_OP BIT(3)
+#define ADC_ARB_USRP_RSV_IP_SEL0 BIT(4)
+#define ADC_ARB_USRP_RSV_IP_SEL1 BIT(5)
+#define ADC_ARB_USRP_RSV_IP_SEL2 BIT(6)
+#define ADC_ARB_USRP_RSV_TRM BIT(7)
+#define ADC_RSV_IP_SEL_SHIFT 4
+
+#define ADC_ARB_USRP_DATA0 0x19D
+#define ADC_ARB_USRP_DATA1 0x19C
+
+/*
+ * Physical channels which MUST exist on all PM variants in order to provide
+ * proper reference points for calibration.
+ *
+ * @PM8XXX_CHANNEL_INTERNAL: 625mV reference channel
+ * @PM8XXX_CHANNEL_125V: 1250mV reference channel
+ * @PM8XXX_CHANNEL_INTERNAL_2: 325mV reference channel
+ * @PM8XXX_CHANNEL_MUXOFF: channel to reduce input load on mux, apparently also
+ * measures XO temperature
+ */
+#define PM8XXX_CHANNEL_INTERNAL 0x0c
+#define PM8XXX_CHANNEL_125V 0x0d
+#define PM8XXX_CHANNEL_INTERNAL_2 0x0e
+#define PM8XXX_CHANNEL_MUXOFF 0x0f
+
+/*
+ * PM8058 AMUX premux scaling, two bits. This is done of the channel before
+ * reaching the AMUX.
+ */
+#define PM8058_AMUX_PRESCALE_0 0x0 /* No scaling on the signal */
+#define PM8058_AMUX_PRESCALE_1 0x1 /* Unity scaling selected by the user */
+#define PM8058_AMUX_PRESCALE_1_DIV3 0x2 /* 1/3 prescaler on the input */
+
+/* Defines reference voltage for the XOADC */
+#define AMUX_RSV0 0x0 /* XO_IN/XOADC_GND, special selection to read XO temp */
+#define AMUX_RSV1 0x1 /* PMIC_IN/XOADC_GND */
+#define AMUX_RSV2 0x2 /* PMIC_IN/BMS_CSP */
+#define AMUX_RSV3 0x3 /* not used */
+#define AMUX_RSV4 0x4 /* XOADC_GND/XOADC_GND */
+#define AMUX_RSV5 0x5 /* XOADC_VREF/XOADC_GND */
+#define XOADC_RSV_MAX 5 /* 3 bits 0..7, 3 and 6,7 are invalid */
+
+/**
+ * struct xoadc_channel - encodes channel properties and defaults
+ * @datasheet_name: the hardwarename of this channel
+ * @pre_scale_mux: prescale (PM8058) or premux (PM8921) for selecting
+ * this channel. Both this and the amux channel is needed to uniquely
+ * identify a channel. Values 0..3.
+ * @amux_channel: value of the ADC_ARB_USRP_AMUX_CNTRL register for this
+ * channel, bits 4..7, selects the amux, values 0..f
+ * @prescale: the channels have hard-coded prescale ratios defined
+ * by the hardware, this tells us what it is
+ * @type: corresponding IIO channel type, usually IIO_VOLTAGE or
+ * IIO_TEMP
+ * @scale_fn_type: the liner interpolation etc to convert the
+ * ADC code to the value that IIO expects, in uV or millicelsius
+ * etc. This scale function can be pretty elaborate if different
+ * thermistors are connected or other hardware characteristics are
+ * deployed.
+ * @amux_ip_rsv: ratiometric scale value used by the analog muxer: this
+ * selects the reference voltage for ratiometric scaling
+ */
+struct xoadc_channel {
+ const char *datasheet_name;
+ u8 pre_scale_mux:2;
+ u8 amux_channel:4;
+ const struct u32_fract prescale;
+ enum iio_chan_type type;
+ enum vadc_scale_fn_type scale_fn_type;
+ u8 amux_ip_rsv:3;
+};
+
+/**
+ * struct xoadc_variant - encodes the XOADC variant characteristics
+ * @name: name of this PMIC variant
+ * @channels: the hardware channels and respective settings and defaults
+ * @broken_ratiometric: if the PMIC has broken ratiometric scaling (this
+ * is a known problem on PM8058)
+ * @prescaling: this variant uses AMUX bits 2 & 3 for prescaling (PM8058)
+ * @second_level_mux: this variant uses AMUX bits 2 & 3 for a second level
+ * mux
+ */
+struct xoadc_variant {
+ const char name[16];
+ const struct xoadc_channel *channels;
+ bool broken_ratiometric;
+ bool prescaling;
+ bool second_level_mux;
+};
+
+/*
+ * XOADC_CHAN macro parameters:
+ * _dname: the name of the channel
+ * _presmux: prescaler (PM8058) or premux (PM8921) setting for this channel
+ * _amux: the value in bits 2..7 of the ADC_ARB_USRP_AMUX_CNTRL register
+ * for this channel. On some PMICs some of the bits select a prescaler, and
+ * on some PMICs some of the bits select various complex multiplex settings.
+ * _type: IIO channel type
+ * _prenum: prescaler numerator (dividend)
+ * _preden: prescaler denominator (divisor)
+ * _scale: scaling function type, this selects how the raw valued is mangled
+ * to output the actual processed measurement
+ * _amip: analog mux input parent when using ratiometric measurements
+ */
+#define XOADC_CHAN(_dname, _presmux, _amux, _type, _prenum, _preden, _scale, _amip) \
+ { \
+ .datasheet_name = __stringify(_dname), \
+ .pre_scale_mux = _presmux, \
+ .amux_channel = _amux, \
+ .prescale = { \
+ .numerator = _prenum, .denominator = _preden, \
+ }, \
+ .type = _type, \
+ .scale_fn_type = _scale, \
+ .amux_ip_rsv = _amip, \
+ }
+
+/*
+ * Taken from arch/arm/mach-msm/board-9615.c in the vendor tree:
+ * TODO: incomplete, needs testing.
+ */
+static const struct xoadc_channel pm8018_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VPH_PWR, 0x00, 0x02, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ /* Used for battery ID or battery temperature */
+ XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV2),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ { }, /* Sentinel */
+};
+
+/*
+ * Taken from arch/arm/mach-msm/board-8930-pmic.c in the vendor tree:
+ * TODO: needs testing.
+ */
+static const struct xoadc_channel pm8038_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* AMUX8 used for battery temperature in most cases */
+ XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV2),
+ XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ { }, /* Sentinel */
+};
+
+/*
+ * This was created by cross-referencing the vendor tree
+ * arch/arm/mach-msm/board-msm8x60.c msm_adc_channels_data[]
+ * with the "channel types" (first field) to find the right
+ * configuration for these channels on an MSM8x60 i.e. PM8058
+ * setup.
+ */
+static const struct xoadc_channel pm8058_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 10, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ /*
+ * AMUX channels 5 thru 9 are referred to as MPP5 thru MPP9 in
+ * some code and documentation. But they are really just 5
+ * channels just like any other. They are connected to a switching
+ * matrix where they can be routed to any of the MPPs, not just
+ * 1-to-1 onto MPP5 thru 9, so naming them MPP5 thru MPP9 is
+ * very confusing.
+ */
+ XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ /* There are also "unity" and divided by 3 channels (prescaler) but noone is using them */
+ { }, /* Sentinel */
+};
+
+/*
+ * The PM8921 has some pre-muxing on its channels, this comes from the vendor tree
+ * include/linux/mfd/pm8xxx/pm8xxx-adc.h
+ * board-flo-pmic.c (Nexus 7) and board-8064-pmic.c
+ */
+static const struct xoadc_channel pm8921_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ /* channel "ICHG" is reserved and not used on PM8921 */
+ XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(IBAT, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* CHAN 6 & 7 (MPP1 & MPP2) are reserved for MPP channels on PM8921 */
+ XOADC_CHAN(BATT_THERM, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV1),
+ XOADC_CHAN(BATT_ID, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* FIXME: look into the scaling of this temperature */
+ XOADC_CHAN(CHG_TEMP, 0x00, 0x0e, IIO_TEMP, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ /* The following channels have premux bit 0 set to 1 (all end in 4) */
+ XOADC_CHAN(ATEST_8, 0x01, 0x00, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* Set scaling to 1/2 based on the name for these two */
+ XOADC_CHAN(USB_SNS_DIV20, 0x01, 0x01, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN_SNS_DIV20, 0x01, 0x02, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX3, 0x01, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX4, 0x01, 0x04, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX5, 0x01, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6, 0x01, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7, 0x01, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX8, 0x01, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* Internal test signals, I think */
+ XOADC_CHAN(ATEST_1, 0x01, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_2, 0x01, 0x0a, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_3, 0x01, 0x0b, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_4, 0x01, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_5, 0x01, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_6, 0x01, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_7, 0x01, 0x0f, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* The following channels have premux bit 1 set to 1 (all end in 8) */
+ /* I guess even ATEST8 will be divided by 3 here */
+ XOADC_CHAN(ATEST_8, 0x02, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ /* I guess div 2 div 3 becomes div 6 */
+ XOADC_CHAN(USB_SNS_DIV20_DIV3, 0x02, 0x01, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN_SNS_DIV20_DIV3, 0x02, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX3_DIV3, 0x02, 0x03, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX4_DIV3, 0x02, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX5_DIV3, 0x02, 0x05, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6_DIV3, 0x02, 0x06, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7_DIV3, 0x02, 0x07, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX8_DIV3, 0x02, 0x08, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_1_DIV3, 0x02, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_2_DIV3, 0x02, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_3_DIV3, 0x02, 0x0b, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_4_DIV3, 0x02, 0x0c, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_5_DIV3, 0x02, 0x0d, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_6_DIV3, 0x02, 0x0e, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_7_DIV3, 0x02, 0x0f, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ { }, /* Sentinel */
+};
+
+/**
+ * struct pm8xxx_chan_info - ADC channel information
+ * @name: name of this channel
+ * @hwchan: pointer to hardware channel information (muxing & scaling settings)
+ * @calibration: whether to use absolute or ratiometric calibration
+ * @scale_fn_type: scaling function type
+ * @decimation: 0,1,2,3
+ * @amux_ip_rsv: ratiometric scale value if using ratiometric
+ * calibration: 0, 1, 2, 4, 5.
+ */
+struct pm8xxx_chan_info {
+ const char *name;
+ const struct xoadc_channel *hwchan;
+ enum vadc_calibration calibration;
+ u8 decimation:2;
+ u8 amux_ip_rsv:3;
+};
+
+/**
+ * struct pm8xxx_xoadc - state container for the XOADC
+ * @dev: pointer to device
+ * @map: regmap to access registers
+ * @variant: XOADC variant characteristics
+ * @vref: reference voltage regulator
+ * characteristics of the channels, and sensible default settings
+ * @nchans: number of channels, configured by the device tree
+ * @chans: the channel information per-channel, configured by the device tree
+ * @iio_chans: IIO channel specifiers
+ * @graph: linear calibration parameters for absolute and
+ * ratiometric measurements
+ * @complete: completion to indicate end of conversion
+ * @lock: lock to restrict access to the hardware to one client at the time
+ */
+struct pm8xxx_xoadc {
+ struct device *dev;
+ struct regmap *map;
+ const struct xoadc_variant *variant;
+ struct regulator *vref;
+ unsigned int nchans;
+ struct pm8xxx_chan_info *chans;
+ struct iio_chan_spec *iio_chans;
+ struct vadc_linear_graph graph[2];
+ struct completion complete;
+ struct mutex lock;
+};
+
+static irqreturn_t pm8xxx_eoc_irq(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static struct pm8xxx_chan_info *
+pm8xxx_get_channel(struct pm8xxx_xoadc *adc, u8 chan)
+{
+ int i;
+
+ for (i = 0; i < adc->nchans; i++) {
+ struct pm8xxx_chan_info *ch = &adc->chans[i];
+ if (ch->hwchan->amux_channel == chan)
+ return ch;
+ }
+ return NULL;
+}
+
+static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc,
+ const struct pm8xxx_chan_info *ch,
+ u8 rsv, u16 *adc_code,
+ bool force_ratiometric)
+{
+ int ret;
+ unsigned int val;
+ u8 rsvmask, rsvval;
+ u8 lsb, msb;
+
+ dev_dbg(adc->dev, "read channel \"%s\", amux %d, prescale/mux: %d, rsv %d\n",
+ ch->name, ch->hwchan->amux_channel, ch->hwchan->pre_scale_mux, rsv);
+
+ mutex_lock(&adc->lock);
+
+ /* Mux in this channel */
+ val = ch->hwchan->amux_channel << ADC_AMUX_SEL_SHIFT;
+ val |= ch->hwchan->pre_scale_mux << ADC_AMUX_PREMUX_SHIFT;
+ ret = regmap_write(adc->map, ADC_ARB_USRP_AMUX_CNTRL, val);
+ if (ret)
+ goto unlock;
+
+ /* Set up ratiometric scale value, mask off all bits except these */
+ rsvmask = (ADC_ARB_USRP_RSV_RST | ADC_ARB_USRP_RSV_DTEST0 |
+ ADC_ARB_USRP_RSV_DTEST1 | ADC_ARB_USRP_RSV_OP);
+ if (adc->variant->broken_ratiometric && !force_ratiometric) {
+ /*
+ * Apparently the PM8058 has some kind of bug which is
+ * reflected in the vendor tree drivers/misc/pmix8058-xoadc.c
+ * which just hardcodes the RSV selector to SEL1 (0x20) for
+ * most cases and SEL0 (0x10) for the MUXOFF channel only.
+ * If we force ratiometric (currently only done when attempting
+ * to do ratiometric calibration) this doesn't seem to work
+ * very well and I suspect ratiometric conversion is simply
+ * broken or not supported on the PM8058.
+ *
+ * Maybe IO_SEL2 doesn't exist on PM8058 and bits 4 & 5 select
+ * the mode alone.
+ *
+ * Some PM8058 register documentation would be nice to get
+ * this right.
+ */
+ if (ch->hwchan->amux_channel == PM8XXX_CHANNEL_MUXOFF)
+ rsvval = ADC_ARB_USRP_RSV_IP_SEL0;
+ else
+ rsvval = ADC_ARB_USRP_RSV_IP_SEL1;
+ } else {
+ if (rsv == 0xff)
+ rsvval = (ch->amux_ip_rsv << ADC_RSV_IP_SEL_SHIFT) |
+ ADC_ARB_USRP_RSV_TRM;
+ else
+ rsvval = (rsv << ADC_RSV_IP_SEL_SHIFT) |
+ ADC_ARB_USRP_RSV_TRM;
+ }
+
+ ret = regmap_update_bits(adc->map,
+ ADC_ARB_USRP_RSV,
+ ~rsvmask,
+ rsvval);
+ if (ret)
+ goto unlock;
+
+ ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM,
+ ADC_ARB_USRP_ANA_PARAM_DIS);
+ if (ret)
+ goto unlock;
+
+ /* Decimation factor */
+ ret = regmap_write(adc->map, ADC_ARB_USRP_DIG_PARAM,
+ ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 |
+ ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 |
+ ch->decimation << ADC_DIG_PARAM_DEC_SHIFT);
+ if (ret)
+ goto unlock;
+
+ ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM,
+ ADC_ARB_USRP_ANA_PARAM_EN);
+ if (ret)
+ goto unlock;
+
+ /* Enable the arbiter, the Qualcomm code does it twice like this */
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL,
+ ADC_ARB_USRP_CNTRL_EN_ARB);
+ if (ret)
+ goto unlock;
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL,
+ ADC_ARB_USRP_CNTRL_EN_ARB);
+ if (ret)
+ goto unlock;
+
+
+ /* Fire a request! */
+ reinit_completion(&adc->complete);
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL,
+ ADC_ARB_USRP_CNTRL_EN_ARB |
+ ADC_ARB_USRP_CNTRL_REQ);
+ if (ret)
+ goto unlock;
+
+ /* Next the interrupt occurs */
+ ret = wait_for_completion_timeout(&adc->complete,
+ VADC_CONV_TIME_MAX_US);
+ if (!ret) {
+ dev_err(adc->dev, "conversion timed out\n");
+ ret = -ETIMEDOUT;
+ goto unlock;
+ }
+
+ ret = regmap_read(adc->map, ADC_ARB_USRP_DATA0, &val);
+ if (ret)
+ goto unlock;
+ lsb = val;
+ ret = regmap_read(adc->map, ADC_ARB_USRP_DATA1, &val);
+ if (ret)
+ goto unlock;
+ msb = val;
+ *adc_code = (msb << 8) | lsb;
+
+ /* Turn off the ADC by setting the arbiter to 0 twice */
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0);
+ if (ret)
+ goto unlock;
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0);
+ if (ret)
+ goto unlock;
+
+unlock:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+static int pm8xxx_read_channel(struct pm8xxx_xoadc *adc,
+ const struct pm8xxx_chan_info *ch,
+ u16 *adc_code)
+{
+ /*
+ * Normally we just use the ratiometric scale value (RSV) predefined
+ * for the channel, but during calibration we need to modify this
+ * so this wrapper is a helper hiding the more complex version.
+ */
+ return pm8xxx_read_channel_rsv(adc, ch, 0xff, adc_code, false);
+}
+
+static int pm8xxx_calibrate_device(struct pm8xxx_xoadc *adc)
+{
+ const struct pm8xxx_chan_info *ch;
+ u16 read_1250v;
+ u16 read_0625v;
+ u16 read_nomux_rsv5;
+ u16 read_nomux_rsv4;
+ int ret;
+
+ adc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
+ adc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
+
+ /* Common reference channel calibration */
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V);
+ if (!ch)
+ return -ENODEV;
+ ret = pm8xxx_read_channel(adc, ch, &read_1250v);
+ if (ret) {
+ dev_err(adc->dev, "could not read 1.25V reference channel\n");
+ return -ENODEV;
+ }
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL);
+ if (!ch)
+ return -ENODEV;
+ ret = pm8xxx_read_channel(adc, ch, &read_0625v);
+ if (ret) {
+ dev_err(adc->dev, "could not read 0.625V reference channel\n");
+ return -ENODEV;
+ }
+ if (read_1250v == read_0625v) {
+ dev_err(adc->dev, "read same ADC code for 1.25V and 0.625V\n");
+ return -ENODEV;
+ }
+
+ adc->graph[VADC_CALIB_ABSOLUTE].dy = read_1250v - read_0625v;
+ adc->graph[VADC_CALIB_ABSOLUTE].gnd = read_0625v;
+
+ dev_info(adc->dev, "absolute calibration dx = %d uV, dy = %d units\n",
+ VADC_ABSOLUTE_RANGE_UV, adc->graph[VADC_CALIB_ABSOLUTE].dy);
+
+ /* Ratiometric calibration */
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF);
+ if (!ch)
+ return -ENODEV;
+ ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV5,
+ &read_nomux_rsv5, true);
+ if (ret) {
+ dev_err(adc->dev, "could not read MUXOFF reference channel\n");
+ return -ENODEV;
+ }
+ ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV4,
+ &read_nomux_rsv4, true);
+ if (ret) {
+ dev_err(adc->dev, "could not read MUXOFF reference channel\n");
+ return -ENODEV;
+ }
+ adc->graph[VADC_CALIB_RATIOMETRIC].dy =
+ read_nomux_rsv5 - read_nomux_rsv4;
+ adc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_nomux_rsv4;
+
+ dev_info(adc->dev, "ratiometric calibration dx = %d, dy = %d units\n",
+ VADC_RATIOMETRIC_RANGE,
+ adc->graph[VADC_CALIB_RATIOMETRIC].dy);
+
+ return 0;
+}
+
+static int pm8xxx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+ const struct pm8xxx_chan_info *ch;
+ u16 adc_code;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ch = pm8xxx_get_channel(adc, chan->address);
+ if (!ch) {
+ dev_err(adc->dev, "no such channel %lu\n",
+ chan->address);
+ return -EINVAL;
+ }
+ ret = pm8xxx_read_channel(adc, ch, &adc_code);
+ if (ret)
+ return ret;
+
+ ret = qcom_vadc_scale(ch->hwchan->scale_fn_type,
+ &adc->graph[ch->calibration],
+ &ch->hwchan->prescale,
+ (ch->calibration == VADC_CALIB_ABSOLUTE),
+ adc_code, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ ch = pm8xxx_get_channel(adc, chan->address);
+ if (!ch) {
+ dev_err(adc->dev, "no such channel %lu\n",
+ chan->address);
+ return -EINVAL;
+ }
+ ret = pm8xxx_read_channel(adc, ch, &adc_code);
+ if (ret)
+ return ret;
+
+ *val = (int)adc_code;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pm8xxx_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+ u8 pre_scale_mux;
+ u8 amux_channel;
+ unsigned int i;
+
+ /*
+ * First cell is prescaler or premux, second cell is analog
+ * mux.
+ */
+ if (iiospec->nargs != 2) {
+ dev_err(&indio_dev->dev, "wrong number of arguments for %pfwP need 2 got %d\n",
+ iiospec->fwnode,
+ iiospec->nargs);
+ return -EINVAL;
+ }
+ pre_scale_mux = (u8)iiospec->args[0];
+ amux_channel = (u8)iiospec->args[1];
+ dev_dbg(&indio_dev->dev, "pre scale/mux: %02x, amux: %02x\n",
+ pre_scale_mux, amux_channel);
+
+ /* We need to match exactly on the prescale/premux and channel */
+ for (i = 0; i < adc->nchans; i++)
+ if (adc->chans[i].hwchan->pre_scale_mux == pre_scale_mux &&
+ adc->chans[i].hwchan->amux_channel == amux_channel)
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info pm8xxx_xoadc_info = {
+ .fwnode_xlate = pm8xxx_fwnode_xlate,
+ .read_raw = pm8xxx_read_raw,
+};
+
+static int pm8xxx_xoadc_parse_channel(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const struct xoadc_channel *hw_channels,
+ struct iio_chan_spec *iio_chan,
+ struct pm8xxx_chan_info *ch)
+{
+ const char *name = fwnode_get_name(fwnode);
+ const struct xoadc_channel *hwchan;
+ u32 pre_scale_mux, amux_channel, reg[2];
+ u32 rsv, dec;
+ int ret;
+ int chid;
+
+ ret = fwnode_property_read_u32_array(fwnode, "reg", reg,
+ ARRAY_SIZE(reg));
+ if (ret) {
+ dev_err(dev, "invalid pre scale/mux or amux channel number %s\n",
+ name);
+ return ret;
+ }
+
+ pre_scale_mux = reg[0];
+ amux_channel = reg[1];
+
+ /* Find the right channel setting */
+ chid = 0;
+ hwchan = &hw_channels[0];
+ while (hwchan && hwchan->datasheet_name) {
+ if (hwchan->pre_scale_mux == pre_scale_mux &&
+ hwchan->amux_channel == amux_channel)
+ break;
+ hwchan++;
+ chid++;
+ }
+ /* The sentinel does not have a name assigned */
+ if (!hwchan->datasheet_name) {
+ dev_err(dev, "could not locate channel %02x/%02x\n",
+ pre_scale_mux, amux_channel);
+ return -EINVAL;
+ }
+ ch->name = name;
+ ch->hwchan = hwchan;
+ /* Everyone seems to use absolute calibration except in special cases */
+ ch->calibration = VADC_CALIB_ABSOLUTE;
+ /* Everyone seems to use default ("type 2") decimation */
+ ch->decimation = VADC_DEF_DECIMATION;
+
+ if (!fwnode_property_read_u32(fwnode, "qcom,ratiometric", &rsv)) {
+ ch->calibration = VADC_CALIB_RATIOMETRIC;
+ if (rsv > XOADC_RSV_MAX) {
+ dev_err(dev, "%s too large RSV value %d\n", name, rsv);
+ return -EINVAL;
+ }
+ if (rsv == AMUX_RSV3) {
+ dev_err(dev, "%s invalid RSV value %d\n", name, rsv);
+ return -EINVAL;
+ }
+ }
+
+ /* Optional decimation, if omitted we use the default */
+ ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &dec);
+ if (!ret) {
+ ret = qcom_vadc_decimation_from_dt(dec);
+ if (ret < 0) {
+ dev_err(dev, "%s invalid decimation %d\n",
+ name, dec);
+ return ret;
+ }
+ ch->decimation = ret;
+ }
+
+ iio_chan->channel = chid;
+ iio_chan->address = hwchan->amux_channel;
+ iio_chan->datasheet_name = hwchan->datasheet_name;
+ iio_chan->type = hwchan->type;
+ /* All channels are raw or processed */
+ iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED);
+ iio_chan->indexed = 1;
+
+ dev_dbg(dev,
+ "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" ref voltage: %d, decimation %d prescale %d/%d, scale function %d\n",
+ hwchan->pre_scale_mux, hwchan->amux_channel, ch->name,
+ ch->amux_ip_rsv, ch->decimation, hwchan->prescale.numerator,
+ hwchan->prescale.denominator, hwchan->scale_fn_type);
+
+ return 0;
+}
+
+static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc)
+{
+ struct fwnode_handle *child;
+ struct pm8xxx_chan_info *ch;
+ int ret;
+ int i;
+
+ adc->nchans = device_get_child_node_count(adc->dev);
+ if (!adc->nchans) {
+ dev_err(adc->dev, "no channel children\n");
+ return -ENODEV;
+ }
+ dev_dbg(adc->dev, "found %d ADC channels\n", adc->nchans);
+
+ adc->iio_chans = devm_kcalloc(adc->dev, adc->nchans,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chans = devm_kcalloc(adc->dev, adc->nchans,
+ sizeof(*adc->chans), GFP_KERNEL);
+ if (!adc->chans)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_child_node(adc->dev, child) {
+ ch = &adc->chans[i];
+ ret = pm8xxx_xoadc_parse_channel(adc->dev, child,
+ adc->variant->channels,
+ &adc->iio_chans[i],
+ ch);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+ i++;
+ }
+
+ /* Check for required channels */
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V);
+ if (!ch) {
+ dev_err(adc->dev, "missing 1.25V reference channel\n");
+ return -ENODEV;
+ }
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL);
+ if (!ch) {
+ dev_err(adc->dev, "missing 0.625V reference channel\n");
+ return -ENODEV;
+ }
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF);
+ if (!ch) {
+ dev_err(adc->dev, "missing MUXOFF reference channel\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int pm8xxx_xoadc_probe(struct platform_device *pdev)
+{
+ const struct xoadc_variant *variant;
+ struct pm8xxx_xoadc *adc;
+ struct iio_dev *indio_dev;
+ struct regmap *map;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ variant = device_get_match_data(dev);
+ if (!variant)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, indio_dev);
+
+ adc = iio_priv(indio_dev);
+ adc->dev = dev;
+ adc->variant = variant;
+ init_completion(&adc->complete);
+ mutex_init(&adc->lock);
+
+ ret = pm8xxx_xoadc_parse_channels(adc);
+ if (ret)
+ return ret;
+
+ map = dev_get_regmap(dev->parent, NULL);
+ if (!map) {
+ dev_err(dev, "parent regmap unavailable.\n");
+ return -ENODEV;
+ }
+ adc->map = map;
+
+ /* Bring up regulator */
+ adc->vref = devm_regulator_get(dev, "xoadc-ref");
+ if (IS_ERR(adc->vref))
+ return dev_err_probe(dev, PTR_ERR(adc->vref),
+ "failed to get XOADC VREF regulator\n");
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(dev, "failed to enable XOADC VREF regulator\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+ pm8xxx_eoc_irq, NULL, 0, variant->name, indio_dev);
+ if (ret) {
+ dev_err(dev, "unable to request IRQ\n");
+ goto out_disable_vref;
+ }
+
+ indio_dev->name = variant->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &pm8xxx_xoadc_info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchans;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto out_disable_vref;
+
+ ret = pm8xxx_calibrate_device(adc);
+ if (ret)
+ goto out_unreg_device;
+
+ dev_info(dev, "%s XOADC driver enabled\n", variant->name);
+
+ return 0;
+
+out_unreg_device:
+ iio_device_unregister(indio_dev);
+out_disable_vref:
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int pm8xxx_xoadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static const struct xoadc_variant pm8018_variant = {
+ .name = "PM8018-XOADC",
+ .channels = pm8018_xoadc_channels,
+};
+
+static const struct xoadc_variant pm8038_variant = {
+ .name = "PM8038-XOADC",
+ .channels = pm8038_xoadc_channels,
+};
+
+static const struct xoadc_variant pm8058_variant = {
+ .name = "PM8058-XOADC",
+ .channels = pm8058_xoadc_channels,
+ .broken_ratiometric = true,
+ .prescaling = true,
+};
+
+static const struct xoadc_variant pm8921_variant = {
+ .name = "PM8921-XOADC",
+ .channels = pm8921_xoadc_channels,
+ .second_level_mux = true,
+};
+
+static const struct of_device_id pm8xxx_xoadc_id_table[] = {
+ {
+ .compatible = "qcom,pm8018-adc",
+ .data = &pm8018_variant,
+ },
+ {
+ .compatible = "qcom,pm8038-adc",
+ .data = &pm8038_variant,
+ },
+ {
+ .compatible = "qcom,pm8058-adc",
+ .data = &pm8058_variant,
+ },
+ {
+ .compatible = "qcom,pm8921-adc",
+ .data = &pm8921_variant,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_xoadc_id_table);
+
+static struct platform_driver pm8xxx_xoadc_driver = {
+ .driver = {
+ .name = "pm8xxx-adc",
+ .of_match_table = pm8xxx_xoadc_id_table,
+ },
+ .probe = pm8xxx_xoadc_probe,
+ .remove = pm8xxx_xoadc_remove,
+};
+module_platform_driver(pm8xxx_xoadc_driver);
+
+MODULE_DESCRIPTION("PM8xxx XOADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pm8xxx-xoadc");
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
new file mode 100644
index 000000000..d1b865707
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -0,0 +1,942 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/adc/qcom-vadc-common.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
+#define ADC5_USR_REVISION1 0x0
+#define ADC5_USR_STATUS1 0x8
+#define ADC5_USR_STATUS1_CONV_FAULT BIT(7)
+#define ADC5_USR_STATUS1_REQ_STS BIT(1)
+#define ADC5_USR_STATUS1_EOC BIT(0)
+#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define ADC5_USR_STATUS2 0x9
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5
+
+#define ADC5_USR_IBAT_MEAS 0xf
+#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0)
+
+#define ADC5_USR_DIG_PARAM 0x42
+#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6)
+#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6
+#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30
+#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2
+
+#define ADC5_USR_FAST_AVG_CTL 0x43
+#define ADC5_USR_FAST_AVG_CTL_EN BIT(7)
+#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7
+
+#define ADC5_USR_CH_SEL_CTL 0x44
+
+#define ADC5_USR_DELAY_CTL 0x45
+#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf
+
+#define ADC5_USR_EN_CTL1 0x46
+#define ADC5_USR_EN_CTL1_ADC_EN BIT(7)
+
+#define ADC5_USR_CONV_REQ 0x47
+#define ADC5_USR_CONV_REQ_REQ BIT(7)
+
+#define ADC5_USR_DATA0 0x50
+
+#define ADC5_USR_DATA1 0x51
+
+#define ADC5_USR_IBAT_DATA0 0x52
+
+#define ADC5_USR_IBAT_DATA1 0x53
+
+#define ADC_CHANNEL_OFFSET 0x8
+#define ADC_CHANNEL_MASK GENMASK(7, 0)
+
+/*
+ * Conversion time varies based on the decimation, clock rate, fast average
+ * samples and measurements queued across different VADC peripherals.
+ * Set the timeout to a max of 100ms.
+ */
+#define ADC5_CONV_TIME_MIN_US 263
+#define ADC5_CONV_TIME_MAX_US 264
+#define ADC5_CONV_TIME_RETRY 400
+#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
+
+/* Digital version >= 5.3 supports hw_settle_2 */
+#define ADC5_HW_SETTLE_DIFF_MINOR 3
+#define ADC5_HW_SETTLE_DIFF_MAJOR 5
+
+/* For PMIC7 */
+#define ADC_APP_SID 0x40
+#define ADC_APP_SID_MASK GENMASK(3, 0)
+#define ADC7_CONV_TIMEOUT msecs_to_jiffies(10)
+
+enum adc5_cal_method {
+ ADC5_NO_CAL = 0,
+ ADC5_RATIOMETRIC_CAL,
+ ADC5_ABSOLUTE_CAL
+};
+
+enum adc5_cal_val {
+ ADC5_TIMER_CAL = 0,
+ ADC5_NEW_CAL
+};
+
+/**
+ * struct adc5_channel_prop - ADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @cal_method: calibration method.
+ * @cal_val: calibration value
+ * @decimation: sampling rate supported for the channel.
+ * @sid: slave id of PMIC owning the channel, for PMIC7.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time: the time between AMUX being configured and the
+ * start of conversion.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ * @scale_fn_type: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ * @datasheet_name: Channel name used in device tree.
+ */
+struct adc5_channel_prop {
+ unsigned int channel;
+ enum adc5_cal_method cal_method;
+ enum adc5_cal_val cal_val;
+ unsigned int decimation;
+ unsigned int sid;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+ enum vadc_scale_fn_type scale_fn_type;
+ const char *datasheet_name;
+};
+
+/**
+ * struct adc5_chip - ADC private structure.
+ * @regmap: SPMI ADC5 peripheral register map field.
+ * @dev: SPMI ADC5 device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of ADC channels.
+ * @chan_props: array of ADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: ADC result notification after interrupt is received.
+ * @lock: ADC lock for access to the peripheral.
+ * @data: software configuration data.
+ */
+struct adc5_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct adc5_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool poll_eoc;
+ struct completion complete;
+ struct mutex lock;
+ const struct adc5_data *data;
+};
+
+static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
+{
+ return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
+}
+
+static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
+{
+ int ret;
+ u8 rslt_lsb, rslt_msb;
+
+ ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ *data = (rslt_msb << 8) | rslt_lsb;
+
+ if (*data == ADC5_USR_DATA_CHECK) {
+ dev_err(adc->dev, "Invalid data:0x%x\n", *data);
+ return -EINVAL;
+ }
+
+ dev_dbg(adc->dev, "voltage raw code:0x%x\n", *data);
+
+ return 0;
+}
+
+static int adc5_poll_wait_eoc(struct adc5_chip *adc)
+{
+ unsigned int count, retry = ADC5_CONV_TIME_RETRY;
+ u8 status1;
+ int ret;
+
+ for (count = 0; count < retry; count++) {
+ ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
+ sizeof(status1));
+ if (ret)
+ return ret;
+
+ status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK;
+ if (status1 == ADC5_USR_STATUS1_EOC)
+ return 0;
+
+ usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void adc5_update_dig_param(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop, u8 *data)
+{
+ /* Update calibration value */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL;
+ *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT);
+
+ /* Update calibration select */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL;
+ *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT);
+
+ /* Update decimation ratio select */
+ *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL;
+ *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
+}
+
+static int adc5_configure(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop)
+{
+ int ret;
+ u8 buf[6];
+
+ /* Read registers 0x42 through 0x46 */
+ ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ /* Digital param selection */
+ adc5_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select ADC enable */
+ buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
+
+ /* Select CONV request */
+ buf[5] |= ADC5_USR_CONV_REQ_REQ;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+}
+
+static int adc7_configure(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop)
+{
+ int ret;
+ u8 conv_req = 0, buf[4];
+
+ ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid);
+ if (ret)
+ return ret;
+
+ ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ /* Digital param selection */
+ adc5_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select CONV request */
+ conv_req = ADC5_USR_CONV_REQ_REQ;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);
+}
+
+static int adc5_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur)
+{
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc5_configure(adc, prop);
+ if (ret) {
+ dev_err(adc->dev, "ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ if (adc->poll_eoc) {
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret) {
+ dev_err(adc->dev, "EOC bit not set\n");
+ goto unlock;
+ }
+ } else {
+ ret = wait_for_completion_timeout(&adc->complete,
+ ADC5_CONV_TIMEOUT);
+ if (!ret) {
+ dev_dbg(adc->dev, "Did not get completion timeout.\n");
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret) {
+ dev_err(adc->dev, "EOC bit not set\n");
+ goto unlock;
+ }
+ }
+ }
+
+ ret = adc5_read_voltage_data(adc, data_volt);
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static int adc7_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur)
+{
+ int ret;
+ u8 status;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc7_configure(adc, prop);
+ if (ret) {
+ dev_err(adc->dev, "ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ /* No support for polling mode at present */
+ wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT);
+
+ ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
+ if (ret)
+ goto unlock;
+
+ if (status & ADC5_USR_STATUS1_CONV_FAULT) {
+ dev_err(adc->dev, "Unexpected conversion fault\n");
+ ret = -EIO;
+ goto unlock;
+ }
+
+ ret = adc5_read_voltage_data(adc, data_volt);
+
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+typedef int (*adc_do_conversion)(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur);
+
+static irqreturn_t adc5_isr(int irq, void *dev_id)
+{
+ struct adc5_chip *adc = dev_id;
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int adc5_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ int i;
+
+ for (i = 0; i < adc->nchannels; i++)
+ if (adc->chan_props[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static int adc7_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ int i, v_channel;
+
+ for (i = 0; i < adc->nchannels; i++) {
+ v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET) |
+ adc->chan_props[i].channel;
+ if (v_channel == iiospec->args[0])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc_read_raw_common(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask, adc_do_conversion do_conv)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_prop *prop;
+ u16 adc_code_volt, adc_code_cur;
+ int ret;
+
+ prop = &adc->chan_props[chan->address];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = do_conv(adc, prop, chan,
+ &adc_code_volt, &adc_code_cur);
+ if (ret)
+ return ret;
+
+ ret = qcom_adc5_hw_scale(prop->scale_fn_type,
+ prop->prescale,
+ adc->data,
+ adc_code_volt, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adc5_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ return adc_read_raw_common(indio_dev, chan, val, val2,
+ mask, adc5_do_conversion);
+}
+
+static int adc7_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ return adc_read_raw_common(indio_dev, chan, val, val2,
+ mask, adc7_do_conversion);
+}
+
+static const struct iio_info adc5_info = {
+ .read_raw = adc5_read_raw,
+ .fwnode_xlate = adc5_fwnode_xlate,
+};
+
+static const struct iio_info adc7_info = {
+ .read_raw = adc7_read_raw,
+ .fwnode_xlate = adc7_fwnode_xlate,
+};
+
+struct adc5_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */
+#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \
+ { \
+ .datasheet_name = _dname, \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale_fn_type = _scale, \
+ }, \
+
+#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_TEMP, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 8,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 0,
+ SCALE_HW_CALIB_PM5_CHG_TEMP)
+ /* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */
+ [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0,
+ SCALE_HW_CALIB_XOTHERM)
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0,
+ SCALE_HW_CALIB_PM5_SMB_TEMP)
+ [ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+};
+
+static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
+ [ADC7_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC7_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM_PM7)
+ [ADC7_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_AMUX_THM6_100K_PU] = ADC5_CHAN_TEMP("amux_thm6_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+ [ADC7_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_pu2", 0,
+ SCALE_HW_CALIB_THERM_100K_PU_PM7)
+};
+
+static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+};
+
+static int adc5_get_fw_channel_data(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct fwnode_handle *fwnode,
+ const struct adc5_data *data)
+{
+ const char *channel_name;
+ char *name;
+ u32 chan, value, varr[2];
+ u32 sid = 0;
+ int ret;
+ struct device *dev = adc->dev;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", fwnode);
+ if (!name)
+ return -ENOMEM;
+
+ /* Cut the address part */
+ name[strchrnul(name, '@') - name] = '\0';
+
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ /* Value read from "reg" is virtual channel number */
+
+ /* virtual channel number = sid << 8 | channel number */
+
+ if (adc->data->info == &adc7_info) {
+ sid = chan >> ADC_CHANNEL_OFFSET;
+ chan = chan & ADC_CHANNEL_MASK;
+ }
+
+ if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
+ !data->adc_chans[chan].datasheet_name) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+ prop->sid = sid;
+
+ ret = fwnode_property_read_string(fwnode, "label", &channel_name);
+ if (ret)
+ channel_name = name;
+
+ prop->datasheet_name = channel_name;
+
+ ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value);
+ if (!ret) {
+ ret = qcom_adc5_decimation_from_dt(value, data->decimation);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = ADC5_DECIMATION_DEFAULT;
+ }
+
+ ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ return ret;
+ }
+ prop->prescale = ret;
+ } else {
+ prop->prescale =
+ adc->data->adc_chans[prop->channel].prescale_index;
+ }
+
+ ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value);
+ if (!ret) {
+ u8 dig_version[2];
+
+ ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
+ sizeof(dig_version));
+ if (ret) {
+ dev_err(dev, "Invalid dig version read %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "dig_ver:minor:%d, major:%d\n", dig_version[0],
+ dig_version[1]);
+ /* Digital controller >= 5.3 have hw_settle_2 option */
+ if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
+ dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
+ adc->data->info == &adc7_info)
+ ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2);
+ else
+ ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1);
+
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid hw-settle-time %d us\n",
+ chan, value);
+ return ret;
+ }
+ prop->hw_settle_time = ret;
+ } else {
+ prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
+ }
+
+ ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value);
+ if (!ret) {
+ ret = qcom_adc5_avg_samples_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid avg-samples %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->avg_samples = ret;
+ } else {
+ prop->avg_samples = VADC_DEF_AVG_SAMPLES;
+ }
+
+ if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
+ prop->cal_method = ADC5_RATIOMETRIC_CAL;
+ else
+ prop->cal_method = ADC5_ABSOLUTE_CAL;
+
+ /*
+ * Default to using timer calibration. Using a fresh calibration value
+ * for every conversion will increase the overall time for a request.
+ */
+ prop->cal_val = ADC5_TIMER_CAL;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static const struct adc5_data adc5_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .full_scale_code_cur = 0x2710,
+ .adc_chans = adc5_chans_pmic,
+ .info = &adc5_info,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {250, 420, 840},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct adc5_data adc7_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .adc_chans = adc7_chans_pmic,
+ .info = &adc7_info,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {85, 340, 1360},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1000, 2000, 4000, 8000, 16000, 32000,
+ 64000, 128000},
+};
+
+static const struct adc5_data adc5_data_pmic_rev2 = {
+ .full_scale_code_volt = 0x4000,
+ .full_scale_code_cur = 0x1800,
+ .adc_chans = adc5_chans_rev2,
+ .info = &adc5_info,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {256, 512, 1024},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {0, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct of_device_id adc5_match_table[] = {
+ {
+ .compatible = "qcom,spmi-adc5",
+ .data = &adc5_data_pmic,
+ },
+ {
+ .compatible = "qcom,spmi-adc7",
+ .data = &adc7_data_pmic,
+ },
+ {
+ .compatible = "qcom,spmi-adc-rev2",
+ .data = &adc5_data_pmic_rev2,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc5_match_table);
+
+static int adc5_get_fw_data(struct adc5_chip *adc)
+{
+ const struct adc5_channels *adc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct adc5_channel_prop prop, *chan_props;
+ struct fwnode_handle *child;
+ unsigned int index = 0;
+ int ret;
+
+ adc->nchannels = device_get_child_node_count(adc->dev);
+ if (!adc->nchannels)
+ return -EINVAL;
+
+ adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->chan_props), GFP_KERNEL);
+ if (!adc->chan_props)
+ return -ENOMEM;
+
+ chan_props = adc->chan_props;
+ iio_chan = adc->iio_chans;
+ adc->data = device_get_match_data(adc->dev);
+ if (!adc->data)
+ adc->data = &adc5_data_pmic;
+
+ device_for_each_child_node(adc->dev, child) {
+ ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+
+ prop.scale_fn_type =
+ adc->data->adc_chans[prop.channel].scale_fn_type;
+ *chan_props = prop;
+ adc_chan = &adc->data->adc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = prop.datasheet_name;
+ iio_chan->extend_name = prop.datasheet_name;
+ iio_chan->info_mask_separate = adc_chan->info_mask;
+ iio_chan->type = adc_chan->type;
+ iio_chan->address = index;
+ iio_chan++;
+ chan_props++;
+ index++;
+ }
+
+ return 0;
+}
+
+static int adc5_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct adc5_chip *adc;
+ struct regmap *regmap;
+ int ret, irq_eoc;
+ u32 reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ ret = device_property_read_u32(dev, "reg", &reg);
+ if (ret < 0)
+ return ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->regmap = regmap;
+ adc->dev = dev;
+ adc->base = reg;
+
+ init_completion(&adc->complete);
+ mutex_init(&adc->lock);
+
+ ret = adc5_get_fw_data(adc);
+ if (ret) {
+ dev_err(dev, "adc get dt data failed\n");
+ return ret;
+ }
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ adc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
+ "pm-adc5", adc);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = adc->data->info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct platform_driver adc5_driver = {
+ .driver = {
+ .name = "qcom-spmi-adc5",
+ .of_match_table = adc5_match_table,
+ },
+ .probe = adc5_probe,
+};
+module_platform_driver(adc5_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-adc5");
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
new file mode 100644
index 000000000..acbda6636
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* IADC register and bit definition */
+#define IADC_REVISION2 0x1
+#define IADC_REVISION2_SUPPORTED_IADC 1
+
+#define IADC_PERPH_TYPE 0x4
+#define IADC_PERPH_TYPE_ADC 8
+
+#define IADC_PERPH_SUBTYPE 0x5
+#define IADC_PERPH_SUBTYPE_IADC 3
+
+#define IADC_STATUS1 0x8
+#define IADC_STATUS1_OP_MODE 4
+#define IADC_STATUS1_REQ_STS BIT(1)
+#define IADC_STATUS1_EOC BIT(0)
+#define IADC_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define IADC_MODE_CTL 0x40
+#define IADC_OP_MODE_SHIFT 3
+#define IADC_OP_MODE_NORMAL 0
+#define IADC_TRIM_EN BIT(0)
+
+#define IADC_EN_CTL1 0x46
+#define IADC_EN_CTL1_SET BIT(7)
+
+#define IADC_CH_SEL_CTL 0x48
+
+#define IADC_DIG_PARAM 0x50
+#define IADC_DIG_DEC_RATIO_SEL_SHIFT 2
+
+#define IADC_HW_SETTLE_DELAY 0x51
+
+#define IADC_CONV_REQ 0x52
+#define IADC_CONV_REQ_SET BIT(7)
+
+#define IADC_FAST_AVG_CTL 0x5a
+#define IADC_FAST_AVG_EN 0x5b
+#define IADC_FAST_AVG_EN_SET BIT(7)
+
+#define IADC_PERH_RESET_CTL3 0xda
+#define IADC_FOLLOW_WARM_RB BIT(2)
+
+#define IADC_DATA 0x60 /* 16 bits */
+
+#define IADC_SEC_ACCESS 0xd0
+#define IADC_SEC_ACCESS_DATA 0xa5
+
+#define IADC_NOMINAL_RSENSE 0xf4
+#define IADC_NOMINAL_RSENSE_SIGN_MASK BIT(7)
+
+#define IADC_REF_GAIN_MICRO_VOLTS 17857
+
+#define IADC_INT_RSENSE_DEVIATION 15625 /* nano Ohms per bit */
+
+#define IADC_INT_RSENSE_IDEAL_VALUE 10000 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_VALUE 7800 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_GF 9000 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_SMIC 9700 /* micro Ohms */
+
+#define IADC_CONV_TIME_MIN_US 2000
+#define IADC_CONV_TIME_MAX_US 2100
+
+#define IADC_DEF_PRESCALING 0 /* 1:1 */
+#define IADC_DEF_DECIMATION 0 /* 512 */
+#define IADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
+#define IADC_DEF_AVG_SAMPLES 0 /* 1 sample */
+
+/* IADC channel list */
+#define IADC_INT_RSENSE 0
+#define IADC_EXT_RSENSE 1
+#define IADC_GAIN_17P857MV 3
+#define IADC_EXT_OFFSET_CSP_CSN 5
+#define IADC_INT_OFFSET_CSP2_CSN2 6
+
+/**
+ * struct iadc_chip - IADC Current ADC device structure.
+ * @regmap: regmap for register read/write.
+ * @dev: This device pointer.
+ * @base: base offset for the ADC peripheral.
+ * @rsense: Values of the internal and external sense resister in micro Ohms.
+ * @poll_eoc: Poll for end of conversion instead of waiting for IRQ.
+ * @offset: Raw offset values for the internal and external channels.
+ * @gain: Raw gain of the channels.
+ * @lock: ADC lock for access to the peripheral.
+ * @complete: ADC notification after end of conversion interrupt is received.
+ */
+struct iadc_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ bool poll_eoc;
+ u32 rsense[2];
+ u16 offset[2];
+ u16 gain;
+ struct mutex lock;
+ struct completion complete;
+};
+
+static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(iadc->regmap, iadc->base + offset, &val);
+ if (ret < 0)
+ return ret;
+
+ *data = val;
+ return 0;
+}
+
+static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data)
+{
+ return regmap_write(iadc->regmap, iadc->base + offset, data);
+}
+
+static int iadc_reset(struct iadc_chip *iadc)
+{
+ u8 data;
+ int ret;
+
+ ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ if (ret < 0)
+ return ret;
+
+ data |= IADC_FOLLOW_WARM_RB;
+
+ return iadc_write(iadc, IADC_PERH_RESET_CTL3, data);
+}
+
+static int iadc_set_state(struct iadc_chip *iadc, bool state)
+{
+ return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0);
+}
+
+static void iadc_status_show(struct iadc_chip *iadc)
+{
+ u8 mode, sta1, chan, dig, en, req;
+ int ret;
+
+ ret = iadc_read(iadc, IADC_MODE_CTL, &mode);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_DIG_PARAM, &dig);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_CONV_REQ, &req);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_EN_CTL1, &en);
+ if (ret < 0)
+ return;
+
+ dev_err(iadc->dev,
+ "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
+ mode, en, chan, dig, req, sta1);
+}
+
+static int iadc_configure(struct iadc_chip *iadc, int channel)
+{
+ u8 decim, mode;
+ int ret;
+
+ /* Mode selection */
+ mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN;
+ ret = iadc_write(iadc, IADC_MODE_CTL, mode);
+ if (ret < 0)
+ return ret;
+
+ /* Channel selection */
+ ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Digital parameter setup */
+ decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT;
+ ret = iadc_write(iadc, IADC_DIG_PARAM, decim);
+ if (ret < 0)
+ return ret;
+
+ /* HW settle time delay */
+ ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES);
+ if (ret < 0)
+ return ret;
+
+ if (IADC_DEF_AVG_SAMPLES)
+ ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET);
+ else
+ ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (!iadc->poll_eoc)
+ reinit_completion(&iadc->complete);
+
+ ret = iadc_set_state(iadc, true);
+ if (ret < 0)
+ return ret;
+
+ /* Request conversion */
+ return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET);
+}
+
+static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
+{
+ unsigned int count, retry;
+ int ret;
+ u8 sta1;
+
+ retry = interval_us / IADC_CONV_TIME_MIN_US;
+
+ for (count = 0; count < retry; count++) {
+ ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ if (ret < 0)
+ return ret;
+
+ sta1 &= IADC_STATUS1_REQ_STS_EOC_MASK;
+ if (sta1 == IADC_STATUS1_EOC)
+ return 0;
+
+ usleep_range(IADC_CONV_TIME_MIN_US, IADC_CONV_TIME_MAX_US);
+ }
+
+ iadc_status_show(iadc);
+
+ return -ETIMEDOUT;
+}
+
+static int iadc_read_result(struct iadc_chip *iadc, u16 *data)
+{
+ return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2);
+}
+
+static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
+{
+ unsigned int wait;
+ int ret;
+
+ ret = iadc_configure(iadc, chan);
+ if (ret < 0)
+ goto exit;
+
+ wait = BIT(IADC_DEF_AVG_SAMPLES) * IADC_CONV_TIME_MIN_US * 2;
+
+ if (iadc->poll_eoc) {
+ ret = iadc_poll_wait_eoc(iadc, wait);
+ } else {
+ ret = wait_for_completion_timeout(&iadc->complete,
+ usecs_to_jiffies(wait));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else
+ /* double check conversion status */
+ ret = iadc_poll_wait_eoc(iadc, IADC_CONV_TIME_MIN_US);
+ }
+
+ if (!ret)
+ ret = iadc_read_result(iadc, data);
+exit:
+ iadc_set_state(iadc, false);
+ if (ret < 0)
+ dev_err(iadc->dev, "conversion failed\n");
+
+ return ret;
+}
+
+static int iadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct iadc_chip *iadc = iio_priv(indio_dev);
+ s32 isense_ua, vsense_uv;
+ u16 adc_raw, vsense_raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&iadc->lock);
+ ret = iadc_do_conversion(iadc, chan->channel, &adc_raw);
+ mutex_unlock(&iadc->lock);
+ if (ret < 0)
+ return ret;
+
+ vsense_raw = adc_raw - iadc->offset[chan->channel];
+
+ vsense_uv = vsense_raw * IADC_REF_GAIN_MICRO_VOLTS;
+ vsense_uv /= (s32)iadc->gain - iadc->offset[chan->channel];
+
+ isense_ua = vsense_uv / iadc->rsense[chan->channel];
+
+ dev_dbg(iadc->dev, "off %d gain %d adc %d %duV I %duA\n",
+ iadc->offset[chan->channel], iadc->gain,
+ adc_raw, vsense_uv, isense_ua);
+
+ *val = isense_ua;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info iadc_info = {
+ .read_raw = iadc_read_raw,
+};
+
+static irqreturn_t iadc_isr(int irq, void *dev_id)
+{
+ struct iadc_chip *iadc = dev_id;
+
+ complete(&iadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int iadc_update_offset(struct iadc_chip *iadc)
+{
+ int ret;
+
+ ret = iadc_do_conversion(iadc, IADC_GAIN_17P857MV, &iadc->gain);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_do_conversion(iadc, IADC_INT_OFFSET_CSP2_CSN2,
+ &iadc->offset[IADC_INT_RSENSE]);
+ if (ret < 0)
+ return ret;
+
+ if (iadc->gain == iadc->offset[IADC_INT_RSENSE]) {
+ dev_err(iadc->dev, "error: internal offset == gain %d\n",
+ iadc->gain);
+ return -EINVAL;
+ }
+
+ ret = iadc_do_conversion(iadc, IADC_EXT_OFFSET_CSP_CSN,
+ &iadc->offset[IADC_EXT_RSENSE]);
+ if (ret < 0)
+ return ret;
+
+ if (iadc->gain == iadc->offset[IADC_EXT_RSENSE]) {
+ dev_err(iadc->dev, "error: external offset == gain %d\n",
+ iadc->gain);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iadc_version_check(struct iadc_chip *iadc)
+{
+ u8 val;
+ int ret;
+
+ ret = iadc_read(iadc, IADC_PERPH_TYPE, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_PERPH_TYPE_ADC) {
+ dev_err(iadc->dev, "%d is not ADC\n", val);
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_PERPH_SUBTYPE_IADC) {
+ dev_err(iadc->dev, "%d is not IADC\n", val);
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_REVISION2, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_REVISION2_SUPPORTED_IADC) {
+ dev_err(iadc->dev, "revision %d not supported\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node)
+{
+ int ret, sign, int_sense;
+ u8 deviation;
+
+ ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms",
+ &iadc->rsense[IADC_EXT_RSENSE]);
+ if (ret < 0)
+ iadc->rsense[IADC_EXT_RSENSE] = IADC_INT_RSENSE_IDEAL_VALUE;
+
+ if (!iadc->rsense[IADC_EXT_RSENSE]) {
+ dev_err(iadc->dev, "external resistor can't be zero Ohms");
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Deviation value stored is an offset from 10 mili Ohms, bit 7 is
+ * the sign, the remaining bits have an LSB of 15625 nano Ohms.
+ */
+ sign = (deviation & IADC_NOMINAL_RSENSE_SIGN_MASK) ? -1 : 1;
+
+ deviation &= ~IADC_NOMINAL_RSENSE_SIGN_MASK;
+
+ /* Scale it to nono Ohms */
+ int_sense = IADC_INT_RSENSE_IDEAL_VALUE * 1000;
+ int_sense += sign * deviation * IADC_INT_RSENSE_DEVIATION;
+ int_sense /= 1000; /* micro Ohms */
+
+ iadc->rsense[IADC_INT_RSENSE] = int_sense;
+ return 0;
+}
+
+static const struct iio_chan_spec iadc_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .datasheet_name = "INTERNAL_RSENSE",
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ },
+ {
+ .type = IIO_CURRENT,
+ .datasheet_name = "EXTERNAL_RSENSE",
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ },
+};
+
+static int iadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct iadc_chip *iadc;
+ int ret, irq_eoc;
+ u32 res;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ iadc = iio_priv(indio_dev);
+ iadc->dev = dev;
+
+ iadc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!iadc->regmap)
+ return -ENODEV;
+
+ init_completion(&iadc->complete);
+ mutex_init(&iadc->lock);
+
+ ret = of_property_read_u32(node, "reg", &res);
+ if (ret < 0)
+ return -ENODEV;
+
+ iadc->base = res;
+
+ ret = iadc_version_check(iadc);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = iadc_rsense_read(iadc, node);
+ if (ret < 0)
+ return -ENODEV;
+
+ dev_dbg(iadc->dev, "sense resistors %d and %d micro Ohm\n",
+ iadc->rsense[IADC_INT_RSENSE],
+ iadc->rsense[IADC_EXT_RSENSE]);
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc == -EPROBE_DEFER)
+ return irq_eoc;
+
+ if (irq_eoc < 0)
+ iadc->poll_eoc = true;
+
+ ret = iadc_reset(iadc);
+ if (ret < 0) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ if (!iadc->poll_eoc) {
+ ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0,
+ "spmi-iadc", iadc);
+ if (!ret)
+ enable_irq_wake(irq_eoc);
+ else
+ return ret;
+ } else {
+ device_init_wakeup(iadc->dev, 1);
+ }
+
+ ret = iadc_update_offset(iadc);
+ if (ret < 0) {
+ dev_err(dev, "failed offset calibration\n");
+ return ret;
+ }
+
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &iadc_info;
+ indio_dev->channels = iadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iadc_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id iadc_match_table[] = {
+ { .compatible = "qcom,spmi-iadc" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, iadc_match_table);
+
+static struct platform_driver iadc_driver = {
+ .driver = {
+ .name = "qcom-spmi-iadc",
+ .of_match_table = iadc_match_table,
+ },
+ .probe = iadc_probe,
+};
+
+module_platform_driver(iadc_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-iadc");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c
new file mode 100644
index 000000000..56a713766
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-rradc.c
@@ -0,0 +1,1022 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016-2017, 2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Linaro Limited.
+ * Author: Caleb Connolly <caleb.connolly@linaro.org>
+ *
+ * This driver is for the Round Robin ADC found in the pmi8998 and pm660 PMICs.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include <soc/qcom/qcom-spmi-pmic.h>
+
+#define DRIVER_NAME "qcom-spmi-rradc"
+
+#define RR_ADC_EN_CTL 0x46
+#define RR_ADC_SKIN_TEMP_LSB 0x50
+#define RR_ADC_SKIN_TEMP_MSB 0x51
+#define RR_ADC_CTL 0x52
+#define RR_ADC_CTL_CONTINUOUS_SEL BIT(3)
+#define RR_ADC_LOG 0x53
+#define RR_ADC_LOG_CLR_CTRL BIT(0)
+
+#define RR_ADC_FAKE_BATT_LOW_LSB 0x58
+#define RR_ADC_FAKE_BATT_LOW_MSB 0x59
+#define RR_ADC_FAKE_BATT_HIGH_LSB 0x5A
+#define RR_ADC_FAKE_BATT_HIGH_MSB 0x5B
+
+#define RR_ADC_BATT_ID_CTRL 0x60
+#define RR_ADC_BATT_ID_CTRL_CHANNEL_CONV BIT(0)
+#define RR_ADC_BATT_ID_TRIGGER 0x61
+#define RR_ADC_BATT_ID_STS 0x62
+#define RR_ADC_BATT_ID_CFG 0x63
+#define BATT_ID_SETTLE_MASK GENMASK(7, 5)
+#define RR_ADC_BATT_ID_5_LSB 0x66
+#define RR_ADC_BATT_ID_5_MSB 0x67
+#define RR_ADC_BATT_ID_15_LSB 0x68
+#define RR_ADC_BATT_ID_15_MSB 0x69
+#define RR_ADC_BATT_ID_150_LSB 0x6A
+#define RR_ADC_BATT_ID_150_MSB 0x6B
+
+#define RR_ADC_BATT_THERM_CTRL 0x70
+#define RR_ADC_BATT_THERM_TRIGGER 0x71
+#define RR_ADC_BATT_THERM_STS 0x72
+#define RR_ADC_BATT_THERM_CFG 0x73
+#define RR_ADC_BATT_THERM_LSB 0x74
+#define RR_ADC_BATT_THERM_MSB 0x75
+#define RR_ADC_BATT_THERM_FREQ 0x76
+
+#define RR_ADC_AUX_THERM_CTRL 0x80
+#define RR_ADC_AUX_THERM_TRIGGER 0x81
+#define RR_ADC_AUX_THERM_STS 0x82
+#define RR_ADC_AUX_THERM_CFG 0x83
+#define RR_ADC_AUX_THERM_LSB 0x84
+#define RR_ADC_AUX_THERM_MSB 0x85
+
+#define RR_ADC_SKIN_HOT 0x86
+#define RR_ADC_SKIN_TOO_HOT 0x87
+
+#define RR_ADC_AUX_THERM_C1 0x88
+#define RR_ADC_AUX_THERM_C2 0x89
+#define RR_ADC_AUX_THERM_C3 0x8A
+#define RR_ADC_AUX_THERM_HALF_RANGE 0x8B
+
+#define RR_ADC_USB_IN_V_CTRL 0x90
+#define RR_ADC_USB_IN_V_TRIGGER 0x91
+#define RR_ADC_USB_IN_V_STS 0x92
+#define RR_ADC_USB_IN_V_LSB 0x94
+#define RR_ADC_USB_IN_V_MSB 0x95
+#define RR_ADC_USB_IN_I_CTRL 0x98
+#define RR_ADC_USB_IN_I_TRIGGER 0x99
+#define RR_ADC_USB_IN_I_STS 0x9A
+#define RR_ADC_USB_IN_I_LSB 0x9C
+#define RR_ADC_USB_IN_I_MSB 0x9D
+
+#define RR_ADC_DC_IN_V_CTRL 0xA0
+#define RR_ADC_DC_IN_V_TRIGGER 0xA1
+#define RR_ADC_DC_IN_V_STS 0xA2
+#define RR_ADC_DC_IN_V_LSB 0xA4
+#define RR_ADC_DC_IN_V_MSB 0xA5
+#define RR_ADC_DC_IN_I_CTRL 0xA8
+#define RR_ADC_DC_IN_I_TRIGGER 0xA9
+#define RR_ADC_DC_IN_I_STS 0xAA
+#define RR_ADC_DC_IN_I_LSB 0xAC
+#define RR_ADC_DC_IN_I_MSB 0xAD
+
+#define RR_ADC_PMI_DIE_TEMP_CTRL 0xB0
+#define RR_ADC_PMI_DIE_TEMP_TRIGGER 0xB1
+#define RR_ADC_PMI_DIE_TEMP_STS 0xB2
+#define RR_ADC_PMI_DIE_TEMP_CFG 0xB3
+#define RR_ADC_PMI_DIE_TEMP_LSB 0xB4
+#define RR_ADC_PMI_DIE_TEMP_MSB 0xB5
+
+#define RR_ADC_CHARGER_TEMP_CTRL 0xB8
+#define RR_ADC_CHARGER_TEMP_TRIGGER 0xB9
+#define RR_ADC_CHARGER_TEMP_STS 0xBA
+#define RR_ADC_CHARGER_TEMP_CFG 0xBB
+#define RR_ADC_CHARGER_TEMP_LSB 0xBC
+#define RR_ADC_CHARGER_TEMP_MSB 0xBD
+#define RR_ADC_CHARGER_HOT 0xBE
+#define RR_ADC_CHARGER_TOO_HOT 0xBF
+
+#define RR_ADC_GPIO_CTRL 0xC0
+#define RR_ADC_GPIO_TRIGGER 0xC1
+#define RR_ADC_GPIO_STS 0xC2
+#define RR_ADC_GPIO_LSB 0xC4
+#define RR_ADC_GPIO_MSB 0xC5
+
+#define RR_ADC_ATEST_CTRL 0xC8
+#define RR_ADC_ATEST_TRIGGER 0xC9
+#define RR_ADC_ATEST_STS 0xCA
+#define RR_ADC_ATEST_LSB 0xCC
+#define RR_ADC_ATEST_MSB 0xCD
+#define RR_ADC_SEC_ACCESS 0xD0
+
+#define RR_ADC_PERPH_RESET_CTL2 0xD9
+#define RR_ADC_PERPH_RESET_CTL3 0xDA
+#define RR_ADC_PERPH_RESET_CTL4 0xDB
+#define RR_ADC_INT_TEST1 0xE0
+#define RR_ADC_INT_TEST_VAL 0xE1
+
+#define RR_ADC_TM_TRIGGER_CTRLS 0xE2
+#define RR_ADC_TM_ADC_CTRLS 0xE3
+#define RR_ADC_TM_CNL_CTRL 0xE4
+#define RR_ADC_TM_BATT_ID_CTRL 0xE5
+#define RR_ADC_TM_THERM_CTRL 0xE6
+#define RR_ADC_TM_CONV_STS 0xE7
+#define RR_ADC_TM_ADC_READ_LSB 0xE8
+#define RR_ADC_TM_ADC_READ_MSB 0xE9
+#define RR_ADC_TM_ATEST_MUX_1 0xEA
+#define RR_ADC_TM_ATEST_MUX_2 0xEB
+#define RR_ADC_TM_REFERENCES 0xED
+#define RR_ADC_TM_MISC_CTL 0xEE
+#define RR_ADC_TM_RR_CTRL 0xEF
+
+#define RR_ADC_TRIGGER_EVERY_CYCLE BIT(7)
+#define RR_ADC_TRIGGER_CTL BIT(0)
+
+#define RR_ADC_BATT_ID_RANGE 820
+
+#define RR_ADC_BITS 10
+#define RR_ADC_CHAN_MSB (1 << RR_ADC_BITS)
+#define RR_ADC_FS_VOLTAGE_MV 2500
+
+/* BATT_THERM 0.25K/LSB */
+#define RR_ADC_BATT_THERM_LSB_K 4
+
+#define RR_ADC_TEMP_FS_VOLTAGE_NUM 5000000
+#define RR_ADC_TEMP_FS_VOLTAGE_DEN 3
+#define RR_ADC_DIE_TEMP_OFFSET 601400
+#define RR_ADC_DIE_TEMP_SLOPE 2
+#define RR_ADC_DIE_TEMP_OFFSET_MILLI_DEGC 25000
+
+#define RR_ADC_CHG_TEMP_GF_OFFSET_UV 1303168
+#define RR_ADC_CHG_TEMP_GF_SLOPE_UV_PER_C 3784
+#define RR_ADC_CHG_TEMP_SMIC_OFFSET_UV 1338433
+#define RR_ADC_CHG_TEMP_SMIC_SLOPE_UV_PER_C 3655
+#define RR_ADC_CHG_TEMP_660_GF_OFFSET_UV 1309001
+#define RR_ADC_CHG_TEMP_660_GF_SLOPE_UV_PER_C 3403
+#define RR_ADC_CHG_TEMP_660_SMIC_OFFSET_UV 1295898
+#define RR_ADC_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C 3596
+#define RR_ADC_CHG_TEMP_660_MGNA_OFFSET_UV 1314779
+#define RR_ADC_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C 3496
+#define RR_ADC_CHG_TEMP_OFFSET_MILLI_DEGC 25000
+#define RR_ADC_CHG_THRESHOLD_SCALE 4
+
+#define RR_ADC_VOLT_INPUT_FACTOR 8
+#define RR_ADC_CURR_INPUT_FACTOR 2000
+#define RR_ADC_CURR_USBIN_INPUT_FACTOR_MIL 1886
+#define RR_ADC_CURR_USBIN_660_FACTOR_MIL 9
+#define RR_ADC_CURR_USBIN_660_UV_VAL 579500
+
+#define RR_ADC_GPIO_FS_RANGE 5000
+#define RR_ADC_COHERENT_CHECK_RETRY 5
+#define RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN 16
+
+#define RR_ADC_STS_CHANNEL_READING_MASK GENMASK(1, 0)
+#define RR_ADC_STS_CHANNEL_STS BIT(1)
+
+#define RR_ADC_TP_REV_VERSION1 21
+#define RR_ADC_TP_REV_VERSION2 29
+#define RR_ADC_TP_REV_VERSION3 32
+
+#define RRADC_BATT_ID_DELAY_MAX 8
+
+enum rradc_channel_id {
+ RR_ADC_BATT_ID = 0,
+ RR_ADC_BATT_THERM,
+ RR_ADC_SKIN_TEMP,
+ RR_ADC_USBIN_I,
+ RR_ADC_USBIN_V,
+ RR_ADC_DCIN_I,
+ RR_ADC_DCIN_V,
+ RR_ADC_DIE_TEMP,
+ RR_ADC_CHG_TEMP,
+ RR_ADC_GPIO,
+ RR_ADC_CHAN_MAX
+};
+
+struct rradc_chip;
+
+/**
+ * struct rradc_channel - rradc channel data
+ * @label: channel label
+ * @lsb: Channel least significant byte
+ * @status: Channel status address
+ * @size: number of bytes to read
+ * @trigger_addr: Trigger address, trigger is only used on some channels
+ * @trigger_mask: Trigger mask
+ * @scale_fn: Post process callback for channels which can't be exposed
+ * as offset + scale.
+ */
+struct rradc_channel {
+ const char *label;
+ u8 lsb;
+ u8 status;
+ int size;
+ int trigger_addr;
+ int trigger_mask;
+ int (*scale_fn)(struct rradc_chip *chip, u16 adc_code, int *result);
+};
+
+struct rradc_chip {
+ struct device *dev;
+ const struct qcom_spmi_pmic *pmic;
+ /*
+ * Lock held while doing channel conversion
+ * involving multiple register read/writes
+ */
+ struct mutex conversion_lock;
+ struct regmap *regmap;
+ u32 base;
+ int batt_id_delay;
+ u16 batt_id_data;
+};
+
+static const int batt_id_delays[] = { 0, 1, 4, 12, 20, 40, 60, 80 };
+static const struct rradc_channel rradc_chans[RR_ADC_CHAN_MAX];
+static const struct iio_chan_spec rradc_iio_chans[RR_ADC_CHAN_MAX];
+
+static int rradc_read(struct rradc_chip *chip, u16 addr, __le16 *buf, int len)
+{
+ int ret, retry_cnt = 0;
+ __le16 data_check[RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN / 2];
+
+ if (len > RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN) {
+ dev_err(chip->dev,
+ "Can't read more than %d bytes, but asked to read %d bytes.\n",
+ RR_ADC_CHAN_MAX_CONTINUOUS_BUFFER_LEN, len);
+ return -EINVAL;
+ }
+
+ while (retry_cnt < RR_ADC_COHERENT_CHECK_RETRY) {
+ ret = regmap_bulk_read(chip->regmap, chip->base + addr, buf,
+ len);
+ if (ret < 0) {
+ dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr,
+ ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_read(chip->regmap, chip->base + addr,
+ data_check, len);
+ if (ret < 0) {
+ dev_err(chip->dev, "rr_adc reg 0x%x failed :%d\n", addr,
+ ret);
+ return ret;
+ }
+
+ if (memcmp(buf, data_check, len) != 0) {
+ retry_cnt++;
+ dev_dbg(chip->dev,
+ "coherent read error, retry_cnt:%d\n",
+ retry_cnt);
+ continue;
+ }
+
+ break;
+ }
+
+ if (retry_cnt == RR_ADC_COHERENT_CHECK_RETRY)
+ dev_err(chip->dev, "Retry exceeded for coherency check\n");
+
+ return ret;
+}
+
+static int rradc_get_fab_coeff(struct rradc_chip *chip, int64_t *offset,
+ int64_t *slope)
+{
+ if (chip->pmic->subtype == PM660_SUBTYPE) {
+ switch (chip->pmic->fab_id) {
+ case PM660_FAB_ID_GF:
+ *offset = RR_ADC_CHG_TEMP_660_GF_OFFSET_UV;
+ *slope = RR_ADC_CHG_TEMP_660_GF_SLOPE_UV_PER_C;
+ return 0;
+ case PM660_FAB_ID_TSMC:
+ *offset = RR_ADC_CHG_TEMP_660_SMIC_OFFSET_UV;
+ *slope = RR_ADC_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C;
+ return 0;
+ default:
+ *offset = RR_ADC_CHG_TEMP_660_MGNA_OFFSET_UV;
+ *slope = RR_ADC_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C;
+ }
+ } else if (chip->pmic->subtype == PMI8998_SUBTYPE) {
+ switch (chip->pmic->fab_id) {
+ case PMI8998_FAB_ID_GF:
+ *offset = RR_ADC_CHG_TEMP_GF_OFFSET_UV;
+ *slope = RR_ADC_CHG_TEMP_GF_SLOPE_UV_PER_C;
+ return 0;
+ case PMI8998_FAB_ID_SMIC:
+ *offset = RR_ADC_CHG_TEMP_SMIC_OFFSET_UV;
+ *slope = RR_ADC_CHG_TEMP_SMIC_SLOPE_UV_PER_C;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * These functions explicitly cast int64_t to int.
+ * They will never overflow, as the values are small enough.
+ */
+static int rradc_post_process_batt_id(struct rradc_chip *chip, u16 adc_code,
+ int *result_ohms)
+{
+ uint32_t current_value;
+ int64_t r_id;
+
+ current_value = chip->batt_id_data;
+ r_id = ((int64_t)adc_code * RR_ADC_FS_VOLTAGE_MV);
+ r_id = div64_s64(r_id, (RR_ADC_CHAN_MSB * current_value));
+ *result_ohms = (int)(r_id * MILLI);
+
+ return 0;
+}
+
+static int rradc_enable_continuous_mode(struct rradc_chip *chip)
+{
+ int ret;
+
+ /* Clear channel log */
+ ret = regmap_update_bits(chip->regmap, chip->base + RR_ADC_LOG,
+ RR_ADC_LOG_CLR_CTRL, RR_ADC_LOG_CLR_CTRL);
+ if (ret < 0) {
+ dev_err(chip->dev, "log ctrl update to clear failed:%d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap, chip->base + RR_ADC_LOG,
+ RR_ADC_LOG_CLR_CTRL, 0);
+ if (ret < 0) {
+ dev_err(chip->dev, "log ctrl update to not clear failed:%d\n",
+ ret);
+ return ret;
+ }
+
+ /* Switch to continuous mode */
+ ret = regmap_update_bits(chip->regmap, chip->base + RR_ADC_CTL,
+ RR_ADC_CTL_CONTINUOUS_SEL,
+ RR_ADC_CTL_CONTINUOUS_SEL);
+ if (ret < 0)
+ dev_err(chip->dev, "Update to continuous mode failed:%d\n",
+ ret);
+
+ return ret;
+}
+
+static int rradc_disable_continuous_mode(struct rradc_chip *chip)
+{
+ int ret;
+
+ /* Switch to non continuous mode */
+ ret = regmap_update_bits(chip->regmap, chip->base + RR_ADC_CTL,
+ RR_ADC_CTL_CONTINUOUS_SEL, 0);
+ if (ret < 0)
+ dev_err(chip->dev, "Update to non-continuous mode failed:%d\n",
+ ret);
+
+ return ret;
+}
+
+static bool rradc_is_ready(struct rradc_chip *chip,
+ enum rradc_channel_id chan_address)
+{
+ const struct rradc_channel *chan = &rradc_chans[chan_address];
+ int ret;
+ unsigned int status, mask;
+
+ /* BATT_ID STS bit does not get set initially */
+ switch (chan_address) {
+ case RR_ADC_BATT_ID:
+ mask = RR_ADC_STS_CHANNEL_STS;
+ break;
+ default:
+ mask = RR_ADC_STS_CHANNEL_READING_MASK;
+ break;
+ }
+
+ ret = regmap_read(chip->regmap, chip->base + chan->status, &status);
+ if (ret < 0 || !(status & mask))
+ return false;
+
+ return true;
+}
+
+static int rradc_read_status_in_cont_mode(struct rradc_chip *chip,
+ enum rradc_channel_id chan_address)
+{
+ const struct rradc_channel *chan = &rradc_chans[chan_address];
+ const struct iio_chan_spec *iio_chan = &rradc_iio_chans[chan_address];
+ int ret, i;
+
+ if (chan->trigger_mask == 0) {
+ dev_err(chip->dev, "Channel doesn't have a trigger mask\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(chip->regmap, chip->base + chan->trigger_addr,
+ chan->trigger_mask, chan->trigger_mask);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Failed to apply trigger for channel '%s' ret=%d\n",
+ iio_chan->extend_name, ret);
+ return ret;
+ }
+
+ ret = rradc_enable_continuous_mode(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to switch to continuous mode\n");
+ goto disable_trigger;
+ }
+
+ /*
+ * The wait/sleep values were found through trial and error,
+ * this is mostly for the battery ID channel which takes some
+ * time to settle.
+ */
+ for (i = 0; i < 5; i++) {
+ if (rradc_is_ready(chip, chan_address))
+ break;
+ usleep_range(50000, 50000 + 500);
+ }
+
+ if (i == 5) {
+ dev_err(chip->dev, "Channel '%s' is not ready\n",
+ iio_chan->extend_name);
+ ret = -ETIMEDOUT;
+ }
+
+ rradc_disable_continuous_mode(chip);
+
+disable_trigger:
+ regmap_update_bits(chip->regmap, chip->base + chan->trigger_addr,
+ chan->trigger_mask, 0);
+
+ return ret;
+}
+
+static int rradc_prepare_batt_id_conversion(struct rradc_chip *chip,
+ enum rradc_channel_id chan_address,
+ u16 *data)
+{
+ int ret;
+
+ ret = regmap_update_bits(chip->regmap, chip->base + RR_ADC_BATT_ID_CTRL,
+ RR_ADC_BATT_ID_CTRL_CHANNEL_CONV,
+ RR_ADC_BATT_ID_CTRL_CHANNEL_CONV);
+ if (ret < 0) {
+ dev_err(chip->dev, "Enabling BATT ID channel failed:%d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(chip->regmap,
+ chip->base + RR_ADC_BATT_ID_TRIGGER,
+ RR_ADC_TRIGGER_CTL, RR_ADC_TRIGGER_CTL);
+ if (ret < 0) {
+ dev_err(chip->dev, "BATT_ID trigger set failed:%d\n", ret);
+ goto out_disable_batt_id;
+ }
+
+ ret = rradc_read_status_in_cont_mode(chip, chan_address);
+
+ /* Reset registers back to default values */
+ regmap_update_bits(chip->regmap, chip->base + RR_ADC_BATT_ID_TRIGGER,
+ RR_ADC_TRIGGER_CTL, 0);
+
+out_disable_batt_id:
+ regmap_update_bits(chip->regmap, chip->base + RR_ADC_BATT_ID_CTRL,
+ RR_ADC_BATT_ID_CTRL_CHANNEL_CONV, 0);
+
+ return ret;
+}
+
+static int rradc_do_conversion(struct rradc_chip *chip,
+ enum rradc_channel_id chan_address, u16 *data)
+{
+ const struct rradc_channel *chan = &rradc_chans[chan_address];
+ const struct iio_chan_spec *iio_chan = &rradc_iio_chans[chan_address];
+ int ret;
+ __le16 buf[3];
+
+ mutex_lock(&chip->conversion_lock);
+
+ switch (chan_address) {
+ case RR_ADC_BATT_ID:
+ ret = rradc_prepare_batt_id_conversion(chip, chan_address, data);
+ if (ret < 0) {
+ dev_err(chip->dev, "Battery ID conversion failed:%d\n",
+ ret);
+ goto unlock_out;
+ }
+ break;
+
+ case RR_ADC_USBIN_V:
+ case RR_ADC_DIE_TEMP:
+ ret = rradc_read_status_in_cont_mode(chip, chan_address);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error reading in continuous mode:%d\n", ret);
+ goto unlock_out;
+ }
+ break;
+ default:
+ if (!rradc_is_ready(chip, chan_address)) {
+ /*
+ * Usually this means the channel isn't attached, for example
+ * the in_voltage_usbin_v_input channel will not be ready if
+ * no USB cable is attached
+ */
+ dev_dbg(chip->dev, "channel '%s' is not ready\n",
+ iio_chan->extend_name);
+ ret = -ENODATA;
+ goto unlock_out;
+ }
+ break;
+ }
+
+ ret = rradc_read(chip, chan->lsb, buf, chan->size);
+ if (ret) {
+ dev_err(chip->dev, "read data failed\n");
+ goto unlock_out;
+ }
+
+ /*
+ * For the battery ID we read the register for every ID ADC and then
+ * see which one is actually connected.
+ */
+ if (chan_address == RR_ADC_BATT_ID) {
+ u16 batt_id_150 = le16_to_cpu(buf[2]);
+ u16 batt_id_15 = le16_to_cpu(buf[1]);
+ u16 batt_id_5 = le16_to_cpu(buf[0]);
+
+ if (!batt_id_150 && !batt_id_15 && !batt_id_5) {
+ dev_err(chip->dev,
+ "Invalid batt_id values with all zeros\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ if (batt_id_150 <= RR_ADC_BATT_ID_RANGE) {
+ *data = batt_id_150;
+ chip->batt_id_data = 150;
+ } else if (batt_id_15 <= RR_ADC_BATT_ID_RANGE) {
+ *data = batt_id_15;
+ chip->batt_id_data = 15;
+ } else {
+ *data = batt_id_5;
+ chip->batt_id_data = 5;
+ }
+ } else {
+ /*
+ * All of the other channels are either 1 or 2 bytes.
+ * We can rely on the second byte being 0 for 1-byte channels.
+ */
+ *data = le16_to_cpu(buf[0]);
+ }
+
+unlock_out:
+ mutex_unlock(&chip->conversion_lock);
+
+ return ret;
+}
+
+static int rradc_read_scale(struct rradc_chip *chip, int chan_address, int *val,
+ int *val2)
+{
+ int64_t fab_offset, fab_slope;
+ int ret;
+
+ ret = rradc_get_fab_coeff(chip, &fab_offset, &fab_slope);
+ if (ret < 0) {
+ dev_err(chip->dev, "Unable to get fab id coefficients\n");
+ return -EINVAL;
+ }
+
+ switch (chan_address) {
+ case RR_ADC_SKIN_TEMP:
+ *val = MILLI;
+ *val2 = RR_ADC_BATT_THERM_LSB_K;
+ return IIO_VAL_FRACTIONAL;
+ case RR_ADC_USBIN_I:
+ *val = RR_ADC_CURR_USBIN_INPUT_FACTOR_MIL *
+ RR_ADC_FS_VOLTAGE_MV;
+ *val2 = RR_ADC_CHAN_MSB;
+ return IIO_VAL_FRACTIONAL;
+ case RR_ADC_DCIN_I:
+ *val = RR_ADC_CURR_INPUT_FACTOR * RR_ADC_FS_VOLTAGE_MV;
+ *val2 = RR_ADC_CHAN_MSB;
+ return IIO_VAL_FRACTIONAL;
+ case RR_ADC_USBIN_V:
+ case RR_ADC_DCIN_V:
+ *val = RR_ADC_VOLT_INPUT_FACTOR * RR_ADC_FS_VOLTAGE_MV * MILLI;
+ *val2 = RR_ADC_CHAN_MSB;
+ return IIO_VAL_FRACTIONAL;
+ case RR_ADC_GPIO:
+ *val = RR_ADC_GPIO_FS_RANGE;
+ *val2 = RR_ADC_CHAN_MSB;
+ return IIO_VAL_FRACTIONAL;
+ case RR_ADC_CHG_TEMP:
+ /*
+ * We divide val2 by MILLI instead of multiplying val
+ * to avoid an integer overflow.
+ */
+ *val = -RR_ADC_TEMP_FS_VOLTAGE_NUM;
+ *val2 = div64_s64(RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB *
+ fab_slope,
+ MILLI);
+
+ return IIO_VAL_FRACTIONAL;
+ case RR_ADC_DIE_TEMP:
+ *val = RR_ADC_TEMP_FS_VOLTAGE_NUM;
+ *val2 = RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB *
+ RR_ADC_DIE_TEMP_SLOPE;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rradc_read_offset(struct rradc_chip *chip, int chan_address, int *val)
+{
+ int64_t fab_offset, fab_slope;
+ int64_t offset1, offset2;
+ int ret;
+
+ switch (chan_address) {
+ case RR_ADC_SKIN_TEMP:
+ /*
+ * Offset from kelvin to degC, divided by the
+ * scale factor (250). We lose some precision here.
+ * 273150 / 250 = 1092.6
+ */
+ *val = div64_s64(ABSOLUTE_ZERO_MILLICELSIUS,
+ (MILLI / RR_ADC_BATT_THERM_LSB_K));
+ return IIO_VAL_INT;
+ case RR_ADC_CHG_TEMP:
+ ret = rradc_get_fab_coeff(chip, &fab_offset, &fab_slope);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Unable to get fab id coefficients\n");
+ return -EINVAL;
+ }
+ offset1 = -(fab_offset * RR_ADC_TEMP_FS_VOLTAGE_DEN *
+ RR_ADC_CHAN_MSB);
+ offset1 += (int64_t)RR_ADC_TEMP_FS_VOLTAGE_NUM / 2ULL;
+ offset1 = div64_s64(offset1,
+ (int64_t)(RR_ADC_TEMP_FS_VOLTAGE_NUM));
+
+ offset2 = (int64_t)RR_ADC_CHG_TEMP_OFFSET_MILLI_DEGC *
+ RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB *
+ (int64_t)fab_slope;
+ offset2 += ((int64_t)MILLI * RR_ADC_TEMP_FS_VOLTAGE_NUM) / 2;
+ offset2 = div64_s64(
+ offset2, ((int64_t)MILLI * RR_ADC_TEMP_FS_VOLTAGE_NUM));
+
+ /*
+ * The -1 is to compensate for lost precision.
+ * It should actually be -0.7906976744186046.
+ * This works out to every value being off
+ * by about +0.091 degrees C after applying offset and scale.
+ */
+ *val = (int)(offset1 - offset2 - 1);
+ return IIO_VAL_INT;
+ case RR_ADC_DIE_TEMP:
+ offset1 = -RR_ADC_DIE_TEMP_OFFSET *
+ (int64_t)RR_ADC_TEMP_FS_VOLTAGE_DEN *
+ (int64_t)RR_ADC_CHAN_MSB;
+ offset1 = div64_s64(offset1, RR_ADC_TEMP_FS_VOLTAGE_NUM);
+
+ offset2 = -(int64_t)RR_ADC_CHG_TEMP_OFFSET_MILLI_DEGC *
+ RR_ADC_TEMP_FS_VOLTAGE_DEN * RR_ADC_CHAN_MSB *
+ RR_ADC_DIE_TEMP_SLOPE;
+ offset2 = div64_s64(offset2,
+ ((int64_t)RR_ADC_TEMP_FS_VOLTAGE_NUM));
+
+ /*
+ * The result is -339, it should be -338.69789, this results
+ * in the calculated die temp being off by
+ * -0.004 - -0.0175 degrees C
+ */
+ *val = (int)(offset1 - offset2);
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int rradc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan_spec, int *val,
+ int *val2, long mask)
+{
+ struct rradc_chip *chip = iio_priv(indio_dev);
+ const struct rradc_channel *chan;
+ int ret;
+ u16 adc_code;
+
+ if (chan_spec->address >= RR_ADC_CHAN_MAX) {
+ dev_err(chip->dev, "Invalid channel index:%lu\n",
+ chan_spec->address);
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return rradc_read_scale(chip, chan_spec->address, val, val2);
+ case IIO_CHAN_INFO_OFFSET:
+ return rradc_read_offset(chip, chan_spec->address, val);
+ case IIO_CHAN_INFO_RAW:
+ ret = rradc_do_conversion(chip, chan_spec->address, &adc_code);
+ if (ret < 0)
+ return ret;
+
+ *val = adc_code;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ chan = &rradc_chans[chan_spec->address];
+ if (!chan->scale_fn)
+ return -EINVAL;
+ ret = rradc_do_conversion(chip, chan_spec->address, &adc_code);
+ if (ret < 0)
+ return ret;
+
+ *val = chan->scale_fn(chip, adc_code, val);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rradc_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, char *label)
+{
+ return snprintf(label, PAGE_SIZE, "%s\n",
+ rradc_chans[chan->address].label);
+}
+
+static const struct iio_info rradc_info = {
+ .read_raw = rradc_read_raw,
+ .read_label = rradc_read_label,
+};
+
+static const struct rradc_channel rradc_chans[RR_ADC_CHAN_MAX] = {
+ {
+ .label = "batt_id",
+ .scale_fn = rradc_post_process_batt_id,
+ .lsb = RR_ADC_BATT_ID_5_LSB,
+ .status = RR_ADC_BATT_ID_STS,
+ .size = 6,
+ .trigger_addr = RR_ADC_BATT_ID_TRIGGER,
+ .trigger_mask = BIT(0),
+ }, {
+ .label = "batt",
+ .lsb = RR_ADC_BATT_THERM_LSB,
+ .status = RR_ADC_BATT_THERM_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_BATT_THERM_TRIGGER,
+ }, {
+ .label = "pmi8998_skin",
+ .lsb = RR_ADC_SKIN_TEMP_LSB,
+ .status = RR_ADC_AUX_THERM_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_AUX_THERM_TRIGGER,
+ }, {
+ .label = "usbin_i",
+ .lsb = RR_ADC_USB_IN_I_LSB,
+ .status = RR_ADC_USB_IN_I_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_USB_IN_I_TRIGGER,
+ }, {
+ .label = "usbin_v",
+ .lsb = RR_ADC_USB_IN_V_LSB,
+ .status = RR_ADC_USB_IN_V_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_USB_IN_V_TRIGGER,
+ .trigger_mask = BIT(7),
+ }, {
+ .label = "dcin_i",
+ .lsb = RR_ADC_DC_IN_I_LSB,
+ .status = RR_ADC_DC_IN_I_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_DC_IN_I_TRIGGER,
+ }, {
+ .label = "dcin_v",
+ .lsb = RR_ADC_DC_IN_V_LSB,
+ .status = RR_ADC_DC_IN_V_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_DC_IN_V_TRIGGER,
+ }, {
+ .label = "pmi8998_die",
+ .lsb = RR_ADC_PMI_DIE_TEMP_LSB,
+ .status = RR_ADC_PMI_DIE_TEMP_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_PMI_DIE_TEMP_TRIGGER,
+ .trigger_mask = RR_ADC_TRIGGER_EVERY_CYCLE,
+ }, {
+ .label = "chg",
+ .lsb = RR_ADC_CHARGER_TEMP_LSB,
+ .status = RR_ADC_CHARGER_TEMP_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_CHARGER_TEMP_TRIGGER,
+ }, {
+ .label = "gpio",
+ .lsb = RR_ADC_GPIO_LSB,
+ .status = RR_ADC_GPIO_STS,
+ .size = 2,
+ .trigger_addr = RR_ADC_GPIO_TRIGGER,
+ },
+};
+
+static const struct iio_chan_spec rradc_iio_chans[RR_ADC_CHAN_MAX] = {
+ {
+ .type = IIO_RESISTANCE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = RR_ADC_BATT_ID,
+ .channel = 0,
+ .indexed = 1,
+ }, {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = RR_ADC_BATT_THERM,
+ .channel = 0,
+ .indexed = 1,
+ }, {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = RR_ADC_SKIN_TEMP,
+ .channel = 1,
+ .indexed = 1,
+ }, {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = RR_ADC_USBIN_I,
+ .channel = 0,
+ .indexed = 1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = RR_ADC_USBIN_V,
+ .channel = 0,
+ .indexed = 1,
+ }, {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = RR_ADC_DCIN_I,
+ .channel = 1,
+ .indexed = 1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = RR_ADC_DCIN_V,
+ .channel = 1,
+ .indexed = 1,
+ }, {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = RR_ADC_DIE_TEMP,
+ .channel = 2,
+ .indexed = 1,
+ }, {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = RR_ADC_CHG_TEMP,
+ .channel = 3,
+ .indexed = 1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = RR_ADC_GPIO,
+ .channel = 2,
+ .indexed = 1,
+ },
+};
+
+static int rradc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct rradc_chip *chip;
+ int ret, i, batt_id_delay;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!chip->regmap) {
+ dev_err(dev, "Couldn't get parent's regmap\n");
+ return -EINVAL;
+ }
+
+ chip->dev = dev;
+ mutex_init(&chip->conversion_lock);
+
+ ret = device_property_read_u32(dev, "reg", &chip->base);
+ if (ret < 0) {
+ dev_err(chip->dev, "Couldn't find reg address, ret = %d\n",
+ ret);
+ return ret;
+ }
+
+ batt_id_delay = -1;
+ ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms",
+ &batt_id_delay);
+ if (!ret) {
+ for (i = 0; i < RRADC_BATT_ID_DELAY_MAX; i++) {
+ if (batt_id_delay == batt_id_delays[i])
+ break;
+ }
+ if (i == RRADC_BATT_ID_DELAY_MAX)
+ batt_id_delay = -1;
+ }
+
+ if (batt_id_delay >= 0) {
+ batt_id_delay = FIELD_PREP(BATT_ID_SETTLE_MASK, batt_id_delay);
+ ret = regmap_update_bits(chip->regmap,
+ chip->base + RR_ADC_BATT_ID_CFG,
+ batt_id_delay, batt_id_delay);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "BATT_ID settling time config failed:%d\n",
+ ret);
+ }
+ }
+
+ /* Get the PMIC revision, we need it to handle some varying coefficients */
+ chip->pmic = qcom_pmic_get(chip->dev);
+ if (IS_ERR(chip->pmic)) {
+ dev_err(chip->dev, "Unable to get reference to PMIC device\n");
+ return PTR_ERR(chip->pmic);
+ }
+
+ switch (chip->pmic->subtype) {
+ case PMI8998_SUBTYPE:
+ indio_dev->name = "pmi8998-rradc";
+ break;
+ case PM660_SUBTYPE:
+ indio_dev->name = "pm660-rradc";
+ break;
+ default:
+ indio_dev->name = DRIVER_NAME;
+ break;
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &rradc_info;
+ indio_dev->channels = rradc_iio_chans;
+ indio_dev->num_channels = ARRAY_SIZE(rradc_iio_chans);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id rradc_match_table[] = {
+ { .compatible = "qcom,pm660-rradc" },
+ { .compatible = "qcom,pmi8998-rradc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rradc_match_table);
+
+static struct platform_driver rradc_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = rradc_match_table,
+ },
+ .probe = rradc_probe,
+};
+module_platform_driver(rradc_driver);
+
+MODULE_DESCRIPTION("QCOM SPMI PMIC RR ADC driver");
+MODULE_AUTHOR("Caleb Connolly <caleb.connolly@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
new file mode 100644
index 000000000..bcff0f62b
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -0,0 +1,937 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/adc/qcom-vadc-common.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
+/* VADC register and bit definitions */
+#define VADC_REVISION2 0x1
+#define VADC_REVISION2_SUPPORTED_VADC 1
+
+#define VADC_PERPH_TYPE 0x4
+#define VADC_PERPH_TYPE_ADC 8
+
+#define VADC_PERPH_SUBTYPE 0x5
+#define VADC_PERPH_SUBTYPE_VADC 1
+
+#define VADC_STATUS1 0x8
+#define VADC_STATUS1_OP_MODE 4
+#define VADC_STATUS1_REQ_STS BIT(1)
+#define VADC_STATUS1_EOC BIT(0)
+#define VADC_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define VADC_MODE_CTL 0x40
+#define VADC_OP_MODE_SHIFT 3
+#define VADC_OP_MODE_NORMAL 0
+#define VADC_AMUX_TRIM_EN BIT(1)
+#define VADC_ADC_TRIM_EN BIT(0)
+
+#define VADC_EN_CTL1 0x46
+#define VADC_EN_CTL1_SET BIT(7)
+
+#define VADC_ADC_CH_SEL_CTL 0x48
+
+#define VADC_ADC_DIG_PARAM 0x50
+#define VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 2
+
+#define VADC_HW_SETTLE_DELAY 0x51
+
+#define VADC_CONV_REQ 0x52
+#define VADC_CONV_REQ_SET BIT(7)
+
+#define VADC_FAST_AVG_CTL 0x5a
+#define VADC_FAST_AVG_EN 0x5b
+#define VADC_FAST_AVG_EN_SET BIT(7)
+
+#define VADC_ACCESS 0xd0
+#define VADC_ACCESS_DATA 0xa5
+
+#define VADC_PERH_RESET_CTL3 0xda
+#define VADC_FOLLOW_WARM_RB BIT(2)
+
+#define VADC_DATA 0x60 /* 16 bits */
+
+#define VADC_CHAN_MIN VADC_USBIN
+#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
+
+/**
+ * struct vadc_channel_prop - VADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @calibration: calibration type.
+ * @decimation: sampling rate supported for the channel.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time: the time between AMUX being configured and the
+ * start of conversion.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ * @scale_fn_type: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ */
+struct vadc_channel_prop {
+ unsigned int channel;
+ enum vadc_calibration calibration;
+ unsigned int decimation;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+/**
+ * struct vadc_priv - VADC private structure.
+ * @regmap: pointer to struct regmap.
+ * @dev: pointer to struct device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of VADC channels.
+ * @chan_props: array of VADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @are_ref_measured: are reference points measured.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: VADC result notification after interrupt is received.
+ * @graph: store parameters for calibration.
+ * @lock: ADC lock for access to the peripheral.
+ */
+struct vadc_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct vadc_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool are_ref_measured;
+ bool poll_eoc;
+ struct completion complete;
+ struct vadc_linear_graph graph[2];
+ struct mutex lock;
+};
+
+static const struct u32_fract vadc_prescale_ratios[] = {
+ { .numerator = 1, .denominator = 1 },
+ { .numerator = 1, .denominator = 3 },
+ { .numerator = 1, .denominator = 4 },
+ { .numerator = 1, .denominator = 6 },
+ { .numerator = 1, .denominator = 20 },
+ { .numerator = 1, .denominator = 8 },
+ { .numerator = 10, .denominator = 81 },
+ { .numerator = 1, .denominator = 10 },
+};
+
+static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
+{
+ return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
+}
+
+static int vadc_write(struct vadc_priv *vadc, u16 offset, u8 data)
+{
+ return regmap_write(vadc->regmap, vadc->base + offset, data);
+}
+
+static int vadc_reset(struct vadc_priv *vadc)
+{
+ u8 data;
+ int ret;
+
+ ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
+ if (ret)
+ return ret;
+
+ ret = vadc_read(vadc, VADC_PERH_RESET_CTL3, &data);
+ if (ret)
+ return ret;
+
+ ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
+ if (ret)
+ return ret;
+
+ data |= VADC_FOLLOW_WARM_RB;
+
+ return vadc_write(vadc, VADC_PERH_RESET_CTL3, data);
+}
+
+static int vadc_set_state(struct vadc_priv *vadc, bool state)
+{
+ return vadc_write(vadc, VADC_EN_CTL1, state ? VADC_EN_CTL1_SET : 0);
+}
+
+static void vadc_show_status(struct vadc_priv *vadc)
+{
+ u8 mode, sta1, chan, dig, en, req;
+ int ret;
+
+ ret = vadc_read(vadc, VADC_MODE_CTL, &mode);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_ADC_DIG_PARAM, &dig);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_ADC_CH_SEL_CTL, &chan);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_CONV_REQ, &req);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_STATUS1, &sta1);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_EN_CTL1, &en);
+ if (ret)
+ return;
+
+ dev_err(vadc->dev,
+ "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
+ mode, en, chan, dig, req, sta1);
+}
+
+static int vadc_configure(struct vadc_priv *vadc,
+ struct vadc_channel_prop *prop)
+{
+ u8 decimation, mode_ctrl;
+ int ret;
+
+ /* Mode selection */
+ mode_ctrl = (VADC_OP_MODE_NORMAL << VADC_OP_MODE_SHIFT) |
+ VADC_ADC_TRIM_EN | VADC_AMUX_TRIM_EN;
+ ret = vadc_write(vadc, VADC_MODE_CTL, mode_ctrl);
+ if (ret)
+ return ret;
+
+ /* Channel selection */
+ ret = vadc_write(vadc, VADC_ADC_CH_SEL_CTL, prop->channel);
+ if (ret)
+ return ret;
+
+ /* Digital parameter setup */
+ decimation = prop->decimation << VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
+ ret = vadc_write(vadc, VADC_ADC_DIG_PARAM, decimation);
+ if (ret)
+ return ret;
+
+ /* HW settle time delay */
+ ret = vadc_write(vadc, VADC_HW_SETTLE_DELAY, prop->hw_settle_time);
+ if (ret)
+ return ret;
+
+ ret = vadc_write(vadc, VADC_FAST_AVG_CTL, prop->avg_samples);
+ if (ret)
+ return ret;
+
+ if (prop->avg_samples)
+ ret = vadc_write(vadc, VADC_FAST_AVG_EN, VADC_FAST_AVG_EN_SET);
+ else
+ ret = vadc_write(vadc, VADC_FAST_AVG_EN, 0);
+
+ return ret;
+}
+
+static int vadc_poll_wait_eoc(struct vadc_priv *vadc, unsigned int interval_us)
+{
+ unsigned int count, retry;
+ u8 sta1;
+ int ret;
+
+ retry = interval_us / VADC_CONV_TIME_MIN_US;
+
+ for (count = 0; count < retry; count++) {
+ ret = vadc_read(vadc, VADC_STATUS1, &sta1);
+ if (ret)
+ return ret;
+
+ sta1 &= VADC_STATUS1_REQ_STS_EOC_MASK;
+ if (sta1 == VADC_STATUS1_EOC)
+ return 0;
+
+ usleep_range(VADC_CONV_TIME_MIN_US, VADC_CONV_TIME_MAX_US);
+ }
+
+ vadc_show_status(vadc);
+
+ return -ETIMEDOUT;
+}
+
+static int vadc_read_result(struct vadc_priv *vadc, u16 *data)
+{
+ int ret;
+
+ ret = regmap_bulk_read(vadc->regmap, vadc->base + VADC_DATA, data, 2);
+ if (ret)
+ return ret;
+
+ *data = clamp_t(u16, *data, VADC_MIN_ADC_CODE, VADC_MAX_ADC_CODE);
+
+ return 0;
+}
+
+static struct vadc_channel_prop *vadc_get_channel(struct vadc_priv *vadc,
+ unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < vadc->nchannels; i++)
+ if (vadc->chan_props[i].channel == num)
+ return &vadc->chan_props[i];
+
+ dev_dbg(vadc->dev, "no such channel %02x\n", num);
+
+ return NULL;
+}
+
+static int vadc_do_conversion(struct vadc_priv *vadc,
+ struct vadc_channel_prop *prop, u16 *data)
+{
+ unsigned int timeout;
+ int ret;
+
+ mutex_lock(&vadc->lock);
+
+ ret = vadc_configure(vadc, prop);
+ if (ret)
+ goto unlock;
+
+ if (!vadc->poll_eoc)
+ reinit_completion(&vadc->complete);
+
+ ret = vadc_set_state(vadc, true);
+ if (ret)
+ goto unlock;
+
+ ret = vadc_write(vadc, VADC_CONV_REQ, VADC_CONV_REQ_SET);
+ if (ret)
+ goto err_disable;
+
+ timeout = BIT(prop->avg_samples) * VADC_CONV_TIME_MIN_US * 2;
+
+ if (vadc->poll_eoc) {
+ ret = vadc_poll_wait_eoc(vadc, timeout);
+ } else {
+ ret = wait_for_completion_timeout(&vadc->complete, timeout);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto err_disable;
+ }
+
+ /* Double check conversion status */
+ ret = vadc_poll_wait_eoc(vadc, VADC_CONV_TIME_MIN_US);
+ if (ret)
+ goto err_disable;
+ }
+
+ ret = vadc_read_result(vadc, data);
+
+err_disable:
+ vadc_set_state(vadc, false);
+ if (ret)
+ dev_err(vadc->dev, "conversion failed\n");
+unlock:
+ mutex_unlock(&vadc->lock);
+ return ret;
+}
+
+static int vadc_measure_ref_points(struct vadc_priv *vadc)
+{
+ struct vadc_channel_prop *prop;
+ u16 read_1, read_2;
+ int ret;
+
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
+ vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
+
+ prop = vadc_get_channel(vadc, VADC_REF_1250MV);
+ ret = vadc_do_conversion(vadc, prop, &read_1);
+ if (ret)
+ goto err;
+
+ /* Try with buffered 625mV channel first */
+ prop = vadc_get_channel(vadc, VADC_SPARE1);
+ if (!prop)
+ prop = vadc_get_channel(vadc, VADC_REF_625MV);
+
+ ret = vadc_do_conversion(vadc, prop, &read_2);
+ if (ret)
+ goto err;
+
+ if (read_1 == read_2) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vadc->graph[VADC_CALIB_ABSOLUTE].dy = read_1 - read_2;
+ vadc->graph[VADC_CALIB_ABSOLUTE].gnd = read_2;
+
+ /* Ratiometric calibration */
+ prop = vadc_get_channel(vadc, VADC_VDD_VADC);
+ ret = vadc_do_conversion(vadc, prop, &read_1);
+ if (ret)
+ goto err;
+
+ prop = vadc_get_channel(vadc, VADC_GND_REF);
+ ret = vadc_do_conversion(vadc, prop, &read_2);
+ if (ret)
+ goto err;
+
+ if (read_1 == read_2) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dy = read_1 - read_2;
+ vadc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_2;
+err:
+ if (ret)
+ dev_err(vadc->dev, "measure reference points failed\n");
+
+ return ret;
+}
+
+static int vadc_prescaling_from_dt(u32 numerator, u32 denominator)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++)
+ if (vadc_prescale_ratios[pre].numerator == numerator &&
+ vadc_prescale_ratios[pre].denominator == denominator)
+ break;
+
+ if (pre == ARRAY_SIZE(vadc_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+
+static int vadc_hw_settle_time_from_dt(u32 value)
+{
+ if ((value <= 1000 && value % 100) || (value > 1000 && value % 2000))
+ return -EINVAL;
+
+ if (value <= 1000)
+ value /= 100;
+ else
+ value = value / 2000 + 10;
+
+ return value;
+}
+
+static int vadc_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > VADC_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs64(value);
+}
+
+static int vadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct vadc_priv *vadc = iio_priv(indio_dev);
+ struct vadc_channel_prop *prop;
+ u16 adc_code;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ prop = &vadc->chan_props[chan->address];
+ ret = vadc_do_conversion(vadc, prop, &adc_code);
+ if (ret)
+ break;
+
+ ret = qcom_vadc_scale(prop->scale_fn_type,
+ &vadc->graph[prop->calibration],
+ &vadc_prescale_ratios[prop->prescale],
+ (prop->calibration == VADC_CALIB_ABSOLUTE),
+ adc_code, val);
+ if (ret)
+ break;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ prop = &vadc->chan_props[chan->address];
+ ret = vadc_do_conversion(vadc, prop, &adc_code);
+ if (ret)
+ break;
+
+ *val = (int)adc_code;
+ return IIO_VAL_INT;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int vadc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ struct vadc_priv *vadc = iio_priv(indio_dev);
+ unsigned int i;
+
+ for (i = 0; i < vadc->nchannels; i++)
+ if (vadc->iio_chans[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info vadc_info = {
+ .read_raw = vadc_read_raw,
+ .fwnode_xlate = vadc_fwnode_xlate,
+};
+
+struct vadc_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
+ [VADC_##_dname] = { \
+ .datasheet_name = __stringify(_dname), \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale_fn_type = _scale \
+ }, \
+
+#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
+ [VADC_##_dname] = { \
+ .datasheet_name = __stringify(_dname), \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask \
+ },
+
+#define VADC_CHAN_TEMP(_dname, _pre, _scale) \
+ VADC_CHAN(_dname, IIO_TEMP, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+#define VADC_CHAN_VOLT(_dname, _pre, _scale) \
+ VADC_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
+ _pre, _scale) \
+
+#define VADC_CHAN_NO_SCALE(_dname, _pre) \
+ VADC_NO_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_RAW), \
+ _pre) \
+
+/*
+ * The array represents all possible ADC channels found in the supported PMICs.
+ * Every index in the array is equal to the channel number per datasheet. The
+ * gaps in the array should be treated as reserved channels.
+ */
+static const struct vadc_channels vadc_chans[] = {
+ VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT)
+ VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT)
+ VADC_CHAN_NO_SCALE(VCHG_SNS, 3)
+ VADC_CHAN_NO_SCALE(SPARE1_03, 1)
+ VADC_CHAN_NO_SCALE(USB_ID_MV, 1)
+ VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT)
+ VADC_CHAN_NO_SCALE(VBAT_SNS, 1)
+ VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT)
+ VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM)
+ VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT)
+ VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT)
+ VADC_CHAN_NO_SCALE(CHG_TEMP, 0)
+ VADC_CHAN_NO_SCALE(SPARE1, 0)
+ VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP)
+ VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT)
+ VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT)
+
+ VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0)
+ VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0)
+
+ VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1)
+ VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1)
+
+ VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0, SCALE_DEFAULT)
+ VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0)
+ VADC_CHAN_NO_SCALE(AMUX_PU1, 0)
+ VADC_CHAN_NO_SCALE(AMUX_PU2, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0)
+
+ VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0)
+ VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP)
+ VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP)
+ VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP)
+ VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0)
+ VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP)
+ VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP)
+ VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0)
+ VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM)
+
+ VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0)
+
+ VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
+ VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
+};
+
+static int vadc_get_fw_channel_data(struct device *dev,
+ struct vadc_channel_prop *prop,
+ struct fwnode_handle *fwnode)
+{
+ const char *name = fwnode_get_name(fwnode);
+ u32 chan, value, varr[2];
+ int ret;
+
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ if (chan > VADC_CHAN_MAX || chan < VADC_CHAN_MIN) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+
+ ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value);
+ if (!ret) {
+ ret = qcom_vadc_decimation_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = VADC_DEF_DECIMATION;
+ }
+
+ ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = vadc_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ return ret;
+ }
+ prop->prescale = ret;
+ } else {
+ prop->prescale = vadc_chans[prop->channel].prescale_index;
+ }
+
+ ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value);
+ if (!ret) {
+ ret = vadc_hw_settle_time_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid hw-settle-time %d us\n",
+ chan, value);
+ return ret;
+ }
+ prop->hw_settle_time = ret;
+ } else {
+ prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
+ }
+
+ ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value);
+ if (!ret) {
+ ret = vadc_avg_samples_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid avg-samples %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->avg_samples = ret;
+ } else {
+ prop->avg_samples = VADC_DEF_AVG_SAMPLES;
+ }
+
+ if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
+ prop->calibration = VADC_CALIB_RATIOMETRIC;
+ else
+ prop->calibration = VADC_CALIB_ABSOLUTE;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static int vadc_get_fw_data(struct vadc_priv *vadc)
+{
+ const struct vadc_channels *vadc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct vadc_channel_prop prop;
+ struct fwnode_handle *child;
+ unsigned int index = 0;
+ int ret;
+
+ vadc->nchannels = device_get_child_node_count(vadc->dev);
+ if (!vadc->nchannels)
+ return -EINVAL;
+
+ vadc->iio_chans = devm_kcalloc(vadc->dev, vadc->nchannels,
+ sizeof(*vadc->iio_chans), GFP_KERNEL);
+ if (!vadc->iio_chans)
+ return -ENOMEM;
+
+ vadc->chan_props = devm_kcalloc(vadc->dev, vadc->nchannels,
+ sizeof(*vadc->chan_props), GFP_KERNEL);
+ if (!vadc->chan_props)
+ return -ENOMEM;
+
+ iio_chan = vadc->iio_chans;
+
+ device_for_each_child_node(vadc->dev, child) {
+ ret = vadc_get_fw_channel_data(vadc->dev, &prop, child);
+ if (ret) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+
+ prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
+ vadc->chan_props[index] = prop;
+
+ vadc_chan = &vadc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = vadc_chan->datasheet_name;
+ iio_chan->info_mask_separate = vadc_chan->info_mask;
+ iio_chan->type = vadc_chan->type;
+ iio_chan->indexed = 1;
+ iio_chan->address = index++;
+
+ iio_chan++;
+ }
+
+ /* These channels are mandatory, they are used as reference points */
+ if (!vadc_get_channel(vadc, VADC_REF_1250MV)) {
+ dev_err(vadc->dev, "Please define 1.25V channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_REF_625MV)) {
+ dev_err(vadc->dev, "Please define 0.625V channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_VDD_VADC)) {
+ dev_err(vadc->dev, "Please define VDD channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_GND_REF)) {
+ dev_err(vadc->dev, "Please define GND channel\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static irqreturn_t vadc_isr(int irq, void *dev_id)
+{
+ struct vadc_priv *vadc = dev_id;
+
+ complete(&vadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int vadc_check_revision(struct vadc_priv *vadc)
+{
+ u8 val;
+ int ret;
+
+ ret = vadc_read(vadc, VADC_PERPH_TYPE, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_PERPH_TYPE_ADC) {
+ dev_err(vadc->dev, "%d is not ADC\n", val);
+ return -ENODEV;
+ }
+
+ ret = vadc_read(vadc, VADC_PERPH_SUBTYPE, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_PERPH_SUBTYPE_VADC) {
+ dev_err(vadc->dev, "%d is not VADC\n", val);
+ return -ENODEV;
+ }
+
+ ret = vadc_read(vadc, VADC_REVISION2, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_REVISION2_SUPPORTED_VADC) {
+ dev_err(vadc->dev, "revision %d not supported\n", val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int vadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct vadc_priv *vadc;
+ struct regmap *regmap;
+ int ret, irq_eoc;
+ u32 reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ ret = device_property_read_u32(dev, "reg", &reg);
+ if (ret < 0)
+ return ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ vadc = iio_priv(indio_dev);
+ vadc->regmap = regmap;
+ vadc->dev = dev;
+ vadc->base = reg;
+ vadc->are_ref_measured = false;
+ init_completion(&vadc->complete);
+ mutex_init(&vadc->lock);
+
+ ret = vadc_check_revision(vadc);
+ if (ret)
+ return ret;
+
+ ret = vadc_get_fw_data(vadc);
+ if (ret)
+ return ret;
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ vadc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
+ "spmi-vadc", vadc);
+ if (ret)
+ return ret;
+ }
+
+ ret = vadc_reset(vadc);
+ if (ret) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ ret = vadc_measure_ref_points(vadc);
+ if (ret)
+ return ret;
+
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &vadc_info;
+ indio_dev->channels = vadc->iio_chans;
+ indio_dev->num_channels = vadc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id vadc_match_table[] = {
+ { .compatible = "qcom,spmi-vadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vadc_match_table);
+
+static struct platform_driver vadc_driver = {
+ .driver = {
+ .name = "qcom-spmi-vadc",
+ .of_match_table = vadc_match_table,
+ },
+ .probe = vadc_probe,
+};
+module_platform_driver(vadc_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-vadc");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC voltage ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
new file mode 100644
index 000000000..d5209f32a
--- /dev/null
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -0,0 +1,772 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/fixp-arith.h>
+#include <linux/iio/adc/qcom-vadc-common.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/units.h>
+
+/**
+ * struct vadc_map_pt - Map the graph representation for ADC channel
+ * @x: Represent the ADC digitized code.
+ * @y: Represent the physical data which can be temperature, voltage,
+ * resistance.
+ */
+struct vadc_map_pt {
+ s32 x;
+ s32 y;
+};
+
+/* Voltage to temperature */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
+ {1758, -40000 },
+ {1742, -35000 },
+ {1719, -30000 },
+ {1691, -25000 },
+ {1654, -20000 },
+ {1608, -15000 },
+ {1551, -10000 },
+ {1483, -5000 },
+ {1404, 0 },
+ {1315, 5000 },
+ {1218, 10000 },
+ {1114, 15000 },
+ {1007, 20000 },
+ {900, 25000 },
+ {795, 30000 },
+ {696, 35000 },
+ {605, 40000 },
+ {522, 45000 },
+ {448, 50000 },
+ {383, 55000 },
+ {327, 60000 },
+ {278, 65000 },
+ {237, 70000 },
+ {202, 75000 },
+ {172, 80000 },
+ {146, 85000 },
+ {125, 90000 },
+ {107, 95000 },
+ {92, 100000 },
+ {79, 105000 },
+ {68, 110000 },
+ {59, 115000 },
+ {51, 120000 },
+ {44, 125000 }
+};
+
+/*
+ * Voltage to temperature table for 100k pull up for NTCG104EF104 with
+ * 1.875V reference.
+ */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
+ { 1831, -40000 },
+ { 1814, -35000 },
+ { 1791, -30000 },
+ { 1761, -25000 },
+ { 1723, -20000 },
+ { 1675, -15000 },
+ { 1616, -10000 },
+ { 1545, -5000 },
+ { 1463, 0 },
+ { 1370, 5000 },
+ { 1268, 10000 },
+ { 1160, 15000 },
+ { 1049, 20000 },
+ { 937, 25000 },
+ { 828, 30000 },
+ { 726, 35000 },
+ { 630, 40000 },
+ { 544, 45000 },
+ { 467, 50000 },
+ { 399, 55000 },
+ { 340, 60000 },
+ { 290, 65000 },
+ { 247, 70000 },
+ { 209, 75000 },
+ { 179, 80000 },
+ { 153, 85000 },
+ { 130, 90000 },
+ { 112, 95000 },
+ { 96, 100000 },
+ { 82, 105000 },
+ { 71, 110000 },
+ { 62, 115000 },
+ { 53, 120000 },
+ { 46, 125000 },
+};
+
+static const struct vadc_map_pt adcmap7_die_temp[] = {
+ { 857300, 160000 },
+ { 820100, 140000 },
+ { 782500, 120000 },
+ { 744600, 100000 },
+ { 706400, 80000 },
+ { 667900, 60000 },
+ { 629300, 40000 },
+ { 590500, 20000 },
+ { 551500, 0 },
+ { 512400, -20000 },
+ { 473100, -40000 },
+ { 433700, -60000 },
+};
+
+/*
+ * Resistance to temperature table for 100k pull up for NTCG104EF104.
+ */
+static const struct vadc_map_pt adcmap7_100k[] = {
+ { 4250657, -40960 },
+ { 3962085, -39936 },
+ { 3694875, -38912 },
+ { 3447322, -37888 },
+ { 3217867, -36864 },
+ { 3005082, -35840 },
+ { 2807660, -34816 },
+ { 2624405, -33792 },
+ { 2454218, -32768 },
+ { 2296094, -31744 },
+ { 2149108, -30720 },
+ { 2012414, -29696 },
+ { 1885232, -28672 },
+ { 1766846, -27648 },
+ { 1656598, -26624 },
+ { 1553884, -25600 },
+ { 1458147, -24576 },
+ { 1368873, -23552 },
+ { 1285590, -22528 },
+ { 1207863, -21504 },
+ { 1135290, -20480 },
+ { 1067501, -19456 },
+ { 1004155, -18432 },
+ { 944935, -17408 },
+ { 889550, -16384 },
+ { 837731, -15360 },
+ { 789229, -14336 },
+ { 743813, -13312 },
+ { 701271, -12288 },
+ { 661405, -11264 },
+ { 624032, -10240 },
+ { 588982, -9216 },
+ { 556100, -8192 },
+ { 525239, -7168 },
+ { 496264, -6144 },
+ { 469050, -5120 },
+ { 443480, -4096 },
+ { 419448, -3072 },
+ { 396851, -2048 },
+ { 375597, -1024 },
+ { 355598, 0 },
+ { 336775, 1024 },
+ { 319052, 2048 },
+ { 302359, 3072 },
+ { 286630, 4096 },
+ { 271806, 5120 },
+ { 257829, 6144 },
+ { 244646, 7168 },
+ { 232209, 8192 },
+ { 220471, 9216 },
+ { 209390, 10240 },
+ { 198926, 11264 },
+ { 189040, 12288 },
+ { 179698, 13312 },
+ { 170868, 14336 },
+ { 162519, 15360 },
+ { 154622, 16384 },
+ { 147150, 17408 },
+ { 140079, 18432 },
+ { 133385, 19456 },
+ { 127046, 20480 },
+ { 121042, 21504 },
+ { 115352, 22528 },
+ { 109960, 23552 },
+ { 104848, 24576 },
+ { 100000, 25600 },
+ { 95402, 26624 },
+ { 91038, 27648 },
+ { 86897, 28672 },
+ { 82965, 29696 },
+ { 79232, 30720 },
+ { 75686, 31744 },
+ { 72316, 32768 },
+ { 69114, 33792 },
+ { 66070, 34816 },
+ { 63176, 35840 },
+ { 60423, 36864 },
+ { 57804, 37888 },
+ { 55312, 38912 },
+ { 52940, 39936 },
+ { 50681, 40960 },
+ { 48531, 41984 },
+ { 46482, 43008 },
+ { 44530, 44032 },
+ { 42670, 45056 },
+ { 40897, 46080 },
+ { 39207, 47104 },
+ { 37595, 48128 },
+ { 36057, 49152 },
+ { 34590, 50176 },
+ { 33190, 51200 },
+ { 31853, 52224 },
+ { 30577, 53248 },
+ { 29358, 54272 },
+ { 28194, 55296 },
+ { 27082, 56320 },
+ { 26020, 57344 },
+ { 25004, 58368 },
+ { 24033, 59392 },
+ { 23104, 60416 },
+ { 22216, 61440 },
+ { 21367, 62464 },
+ { 20554, 63488 },
+ { 19776, 64512 },
+ { 19031, 65536 },
+ { 18318, 66560 },
+ { 17636, 67584 },
+ { 16982, 68608 },
+ { 16355, 69632 },
+ { 15755, 70656 },
+ { 15180, 71680 },
+ { 14628, 72704 },
+ { 14099, 73728 },
+ { 13592, 74752 },
+ { 13106, 75776 },
+ { 12640, 76800 },
+ { 12192, 77824 },
+ { 11762, 78848 },
+ { 11350, 79872 },
+ { 10954, 80896 },
+ { 10574, 81920 },
+ { 10209, 82944 },
+ { 9858, 83968 },
+ { 9521, 84992 },
+ { 9197, 86016 },
+ { 8886, 87040 },
+ { 8587, 88064 },
+ { 8299, 89088 },
+ { 8023, 90112 },
+ { 7757, 91136 },
+ { 7501, 92160 },
+ { 7254, 93184 },
+ { 7017, 94208 },
+ { 6789, 95232 },
+ { 6570, 96256 },
+ { 6358, 97280 },
+ { 6155, 98304 },
+ { 5959, 99328 },
+ { 5770, 100352 },
+ { 5588, 101376 },
+ { 5412, 102400 },
+ { 5243, 103424 },
+ { 5080, 104448 },
+ { 4923, 105472 },
+ { 4771, 106496 },
+ { 4625, 107520 },
+ { 4484, 108544 },
+ { 4348, 109568 },
+ { 4217, 110592 },
+ { 4090, 111616 },
+ { 3968, 112640 },
+ { 3850, 113664 },
+ { 3736, 114688 },
+ { 3626, 115712 },
+ { 3519, 116736 },
+ { 3417, 117760 },
+ { 3317, 118784 },
+ { 3221, 119808 },
+ { 3129, 120832 },
+ { 3039, 121856 },
+ { 2952, 122880 },
+ { 2868, 123904 },
+ { 2787, 124928 },
+ { 2709, 125952 },
+ { 2633, 126976 },
+ { 2560, 128000 },
+ { 2489, 129024 },
+ { 2420, 130048 }
+};
+
+static const struct u32_fract adc5_prescale_ratios[] = {
+ { .numerator = 1, .denominator = 1 },
+ { .numerator = 1, .denominator = 3 },
+ { .numerator = 1, .denominator = 4 },
+ { .numerator = 1, .denominator = 6 },
+ { .numerator = 1, .denominator = 20 },
+ { .numerator = 1, .denominator = 8 },
+ { .numerator = 10, .denominator = 81 },
+ { .numerator = 1, .denominator = 10 },
+ { .numerator = 1, .denominator = 16 },
+};
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv);
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc7_scale_hw_calib_therm(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc7_scale_hw_calib_die_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
+static struct qcom_adc5_scale_type scale_adc5_fn[] = {
+ [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
+ [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_THERM_100K_PU_PM7] = {
+ qcom_vadc7_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
+ [SCALE_HW_CALIB_PMIC_THERM_PM7] = {
+ qcom_vadc7_scale_hw_calib_die_temp},
+ [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
+ [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
+};
+
+static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
+ u32 tablesize, s32 input, int *output)
+{
+ u32 i = 0;
+
+ if (!pts)
+ return -EINVAL;
+
+ while (i < tablesize && pts[i].x > input)
+ i++;
+
+ if (i == 0) {
+ *output = pts[0].y;
+ } else if (i == tablesize) {
+ *output = pts[tablesize - 1].y;
+ } else {
+ /* interpolate linearly */
+ *output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y,
+ pts[i].x, pts[i].y,
+ input);
+ }
+
+ return 0;
+}
+
+static s32 qcom_vadc_map_temp_voltage(const struct vadc_map_pt *pts,
+ u32 tablesize, int input)
+{
+ u32 i = 0;
+
+ /*
+ * Table must be sorted, find the interval of 'y' which contains value
+ * 'input' and map it to proper 'x' value
+ */
+ while (i < tablesize && pts[i].y < input)
+ i++;
+
+ if (i == 0)
+ return pts[0].x;
+ if (i == tablesize)
+ return pts[tablesize - 1].x;
+
+ /* interpolate linearly */
+ return fixp_linear_interpolate(pts[i - 1].y, pts[i - 1].x,
+ pts[i].y, pts[i].x, input);
+}
+
+static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
+ u16 adc_code,
+ bool absolute,
+ s64 *scale_voltage)
+{
+ *scale_voltage = (adc_code - calib_graph->gnd);
+ *scale_voltage *= calib_graph->dx;
+ *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
+ if (absolute)
+ *scale_voltage += calib_graph->dx;
+
+ if (*scale_voltage < 0)
+ *scale_voltage = 0;
+}
+
+static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
+ const struct u32_fract *prescale,
+ bool absolute, u16 adc_code,
+ int *result_uv)
+{
+ s64 voltage = 0, result = 0;
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ voltage *= prescale->denominator;
+ result = div64_s64(voltage, prescale->numerator);
+ *result_uv = result;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
+ const struct u32_fract *prescale,
+ bool absolute, u16 adc_code,
+ int *result_mdec)
+{
+ s64 voltage = 0;
+ int ret;
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ if (absolute)
+ voltage = div64_s64(voltage, 1000);
+
+ ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb),
+ voltage, result_mdec);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
+ const struct u32_fract *prescale,
+ bool absolute,
+ u16 adc_code, int *result_mdec)
+{
+ s64 voltage = 0;
+ u64 temp; /* Temporary variable for do_div */
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ if (voltage > 0) {
+ temp = voltage * prescale->denominator;
+ do_div(temp, prescale->numerator * 2);
+ voltage = temp;
+ } else {
+ voltage = 0;
+ }
+
+ *result_mdec = milli_kelvin_to_millicelsius(voltage);
+
+ return 0;
+}
+
+static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
+ const struct u32_fract *prescale,
+ bool absolute,
+ u16 adc_code, int *result_mdec)
+{
+ s64 voltage = 0, result = 0;
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ voltage *= prescale->denominator;
+ voltage = div64_s64(voltage, prescale->numerator);
+ voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
+ voltage = (voltage + PMI_CHG_SCALE_2);
+ result = div64_s64(voltage, 1000000);
+ *result_mdec = result;
+
+ return 0;
+}
+
+/* convert voltage to ADC code, using 1.875V reference */
+static u16 qcom_vadc_scale_voltage_code(s32 voltage,
+ const struct u32_fract *prescale,
+ const u32 full_scale_code_volt,
+ unsigned int factor)
+{
+ s64 volt = voltage;
+ s64 adc_vdd_ref_mv = 1875; /* reference voltage */
+
+ volt *= prescale->numerator * factor * full_scale_code_volt;
+ volt = div64_s64(volt, (s64)prescale->denominator * adc_vdd_ref_mv * 1000);
+
+ return volt;
+}
+
+static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ unsigned int factor)
+{
+ s64 voltage, temp, adc_vdd_ref_mv = 1875;
+
+ /*
+ * The normal data range is between 0V to 1.875V. On cases where
+ * we read low voltage values, the ADC code can go beyond the
+ * range and the scale result is incorrect so we clamp the values
+ * for the cases where the code represents a value below 0V
+ */
+ if (adc_code > VADC5_MAX_CODE)
+ adc_code = 0;
+
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
+ voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
+ voltage = div64_s64(voltage, data->full_scale_code_volt);
+ if (voltage > 0) {
+ voltage *= prescale->denominator;
+ temp = prescale->numerator * factor;
+ voltage = div64_s64(voltage, temp);
+ } else {
+ voltage = 0;
+ }
+
+ return (int) voltage;
+}
+
+static int qcom_vadc7_scale_hw_calib_therm(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ s64 resistance = adc_code;
+ int ret, result;
+
+ if (adc_code >= RATIO_MAX_ADC7)
+ return -EINVAL;
+
+ /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
+ resistance *= R_PU_100K;
+ resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code);
+
+ ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
+ ARRAY_SIZE(adcmap7_100k),
+ resistance, &result);
+ if (ret)
+ return ret;
+
+ *result_mdec = result;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv)
+{
+ *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1);
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ int voltage;
+
+ voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1000);
+
+ /* Map voltage to temperature from look-up table */
+ return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ voltage, result_mdec);
+}
+
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 2);
+ *result_mdec = milli_kelvin_to_millicelsius(*result_mdec);
+
+ return 0;
+}
+
+static int qcom_vadc7_scale_hw_calib_die_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+
+ int voltage;
+
+ voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1);
+
+ return qcom_vadc_map_voltage_temp(adcmap7_die_temp, ARRAY_SIZE(adcmap7_die_temp),
+ voltage, result_mdec);
+}
+
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
+ prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
+ *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct u32_fract *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 4);
+ *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
+
+ return 0;
+}
+
+int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_linear_graph *calib_graph,
+ const struct u32_fract *prescale,
+ bool absolute,
+ u16 adc_code, int *result)
+{
+ switch (scaletype) {
+ case SCALE_DEFAULT:
+ return qcom_vadc_scale_volt(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ case SCALE_THERM_100K_PULLUP:
+ case SCALE_XOTHERM:
+ return qcom_vadc_scale_therm(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ case SCALE_PMIC_THERM:
+ return qcom_vadc_scale_die_temp(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ case SCALE_PMI_CHG_TEMP:
+ return qcom_vadc_scale_chg_temp(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(qcom_vadc_scale);
+
+u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio,
+ u32 full_scale_code_volt, int temp)
+{
+ const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio];
+ s32 voltage;
+
+ voltage = qcom_vadc_map_temp_voltage(adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ temp);
+ return qcom_vadc_scale_voltage_code(voltage, prescale, full_scale_code_volt, 1000);
+}
+EXPORT_SYMBOL(qcom_adc_tm5_temp_volt_scale);
+
+u16 qcom_adc_tm5_gen2_temp_res_scale(int temp)
+{
+ int64_t resistance;
+
+ resistance = qcom_vadc_map_temp_voltage(adcmap7_100k,
+ ARRAY_SIZE(adcmap7_100k), temp);
+
+ return div64_s64(resistance * RATIO_MAX_ADC7, resistance + R_PU_100K);
+}
+EXPORT_SYMBOL(qcom_adc_tm5_gen2_temp_res_scale);
+
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ unsigned int prescale_ratio,
+ const struct adc5_data *data,
+ u16 adc_code, int *result)
+{
+ const struct u32_fract *prescale = &adc5_prescale_ratios[prescale_ratio];
+
+ if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
+ scaletype < SCALE_HW_CALIB_INVALID)) {
+ pr_err("Invalid scale type %d\n", scaletype);
+ return -EINVAL;
+ }
+
+ return scale_adc5_fn[scaletype].scale_fn(prescale, data,
+ adc_code, result);
+}
+EXPORT_SYMBOL(qcom_adc5_hw_scale);
+
+int qcom_adc5_prescaling_from_dt(u32 numerator, u32 denominator)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
+ if (adc5_prescale_ratios[pre].numerator == numerator &&
+ adc5_prescale_ratios[pre].denominator == denominator)
+ break;
+
+ if (pre == ARRAY_SIZE(adc5_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+EXPORT_SYMBOL(qcom_adc5_prescaling_from_dt);
+
+int qcom_adc5_hw_settle_time_from_dt(u32 value,
+ const unsigned int *hw_settle)
+{
+ unsigned int i;
+
+ for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
+ if (value == hw_settle[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(qcom_adc5_hw_settle_time_from_dt);
+
+int qcom_adc5_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs(value);
+}
+EXPORT_SYMBOL(qcom_adc5_avg_samples_from_dt);
+
+int qcom_adc5_decimation_from_dt(u32 value, const unsigned int *decimation)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
+ if (value == decimation[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(qcom_adc5_decimation_from_dt);
+
+int qcom_vadc_decimation_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
+ value > VADC_DECIMATION_MAX)
+ return -EINVAL;
+
+ return __ffs64(value / VADC_DECIMATION_MIN);
+}
+EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Qualcomm ADC common functionality");
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
new file mode 100644
index 000000000..27d9e147b
--- /dev/null
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Renesas R-Car GyroADC driver
+ *
+ * Copyright 2016 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+
+#define DRIVER_NAME "rcar-gyroadc"
+
+/* GyroADC registers. */
+#define RCAR_GYROADC_MODE_SELECT 0x00
+#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0
+#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1
+#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3
+
+#define RCAR_GYROADC_START_STOP 0x04
+#define RCAR_GYROADC_START_STOP_START BIT(0)
+
+#define RCAR_GYROADC_CLOCK_LENGTH 0x08
+#define RCAR_GYROADC_1_25MS_LENGTH 0x0c
+
+#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4))
+#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4))
+#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4))
+
+#define RCAR_GYROADC_FIFO_STATUS 0x70
+#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch)))
+#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch)))
+#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch)))
+
+#define RCAR_GYROADC_INTR 0x74
+#define RCAR_GYROADC_INTR_INT BIT(0)
+
+#define RCAR_GYROADC_INTENR 0x78
+#define RCAR_GYROADC_INTENR_INTEN BIT(0)
+
+#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */
+
+#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000
+
+enum rcar_gyroadc_model {
+ RCAR_GYROADC_MODEL_DEFAULT,
+ RCAR_GYROADC_MODEL_R8A7792,
+};
+
+struct rcar_gyroadc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct regulator *vref[8];
+ unsigned int num_channels;
+ enum rcar_gyroadc_model model;
+ unsigned int mode;
+ unsigned int sample_width;
+};
+
+static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv)
+{
+ const unsigned long clk_mhz = clk_get_rate(priv->clk) / 1000000;
+ const unsigned long clk_mul =
+ (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5;
+ unsigned long clk_len = clk_mhz * clk_mul;
+
+ /*
+ * According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014,
+ * page 77-7, clock length must be even number. If it's odd number,
+ * add one.
+ */
+ if (clk_len & 1)
+ clk_len++;
+
+ /* Stop the GyroADC. */
+ writel(0, priv->regs + RCAR_GYROADC_START_STOP);
+
+ /* Disable IRQ on V2H. */
+ if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
+ writel(0, priv->regs + RCAR_GYROADC_INTENR);
+
+ /* Set mode and timing. */
+ writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT);
+ writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH);
+ writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH);
+}
+
+static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv)
+{
+ /* Start sampling. */
+ writel(RCAR_GYROADC_START_STOP_START,
+ priv->regs + RCAR_GYROADC_START_STOP);
+
+ /*
+ * Wait for the first conversion to complete. This is longer than
+ * the 1.25 mS in the datasheet because 1.25 mS is not enough for
+ * the hardware to deliver the first sample and the hardware does
+ * then return zeroes instead of valid data.
+ */
+ mdelay(3);
+}
+
+static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv)
+{
+ /* Stop the GyroADC. */
+ writel(0, priv->regs + RCAR_GYROADC_START_STOP);
+}
+
+#define RCAR_GYROADC_CHAN(_idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = {
+ RCAR_GYROADC_CHAN(0),
+ RCAR_GYROADC_CHAN(1),
+ RCAR_GYROADC_CHAN(2),
+ RCAR_GYROADC_CHAN(3),
+};
+
+static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = {
+ RCAR_GYROADC_CHAN(0),
+ RCAR_GYROADC_CHAN(1),
+ RCAR_GYROADC_CHAN(2),
+ RCAR_GYROADC_CHAN(3),
+ RCAR_GYROADC_CHAN(4),
+ RCAR_GYROADC_CHAN(5),
+ RCAR_GYROADC_CHAN(6),
+ RCAR_GYROADC_CHAN(7),
+};
+
+static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
+ RCAR_GYROADC_CHAN(0),
+ RCAR_GYROADC_CHAN(1),
+ RCAR_GYROADC_CHAN(2),
+ RCAR_GYROADC_CHAN(3),
+ RCAR_GYROADC_CHAN(4),
+ RCAR_GYROADC_CHAN(5),
+ RCAR_GYROADC_CHAN(6),
+ RCAR_GYROADC_CHAN(7),
+};
+
+static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on)
+{
+ struct device *dev = priv->dev;
+
+ if (on) {
+ return pm_runtime_resume_and_get(dev);
+ } else {
+ pm_runtime_mark_last_busy(dev);
+ return pm_runtime_put_autosuspend(dev);
+ }
+}
+
+static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ struct regulator *consumer;
+ unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel);
+ unsigned int vref;
+ int ret;
+
+ /*
+ * MB88101 is special in that it has only single regulator for
+ * all four channels.
+ */
+ if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A)
+ consumer = priv->vref[0];
+ else
+ consumer = priv->vref[chan->channel];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ /* Channel not connected. */
+ if (!consumer)
+ return -EINVAL;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = rcar_gyroadc_set_power(priv, true);
+ if (ret < 0) {
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ }
+
+ *val = readl(priv->regs + datareg);
+ *val &= BIT(priv->sample_width) - 1;
+
+ ret = rcar_gyroadc_set_power(priv, false);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* Channel not connected. */
+ if (!consumer)
+ return -EINVAL;
+
+ vref = regulator_get_voltage(consumer);
+ *val = vref / 1000;
+ *val2 = 1 << priv->sample_width;
+
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = RCAR_GYROADC_SAMPLE_RATE;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS;
+
+ if (readval == NULL)
+ return -EINVAL;
+
+ if (reg % 4)
+ return -EINVAL;
+
+ /* Handle the V2H case with extra interrupt block. */
+ if (priv->model == RCAR_GYROADC_MODEL_R8A7792)
+ maxreg = RCAR_GYROADC_INTENR;
+
+ if (reg > maxreg)
+ return -EINVAL;
+
+ *readval = readl(priv->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info rcar_gyroadc_iio_info = {
+ .read_raw = rcar_gyroadc_read_raw,
+ .debugfs_reg_access = rcar_gyroadc_reg_access,
+};
+
+static const struct of_device_id rcar_gyroadc_match[] = {
+ {
+ /* R-Car compatible GyroADC */
+ .compatible = "renesas,rcar-gyroadc",
+ .data = (void *)RCAR_GYROADC_MODEL_DEFAULT,
+ }, {
+ /* R-Car V2H specialty with interrupt registers. */
+ .compatible = "renesas,r8a7792-gyroadc",
+ .data = (void *)RCAR_GYROADC_MODEL_R8A7792,
+ }, {
+ /* sentinel */
+ }
+};
+
+MODULE_DEVICE_TABLE(of, rcar_gyroadc_match);
+
+static const struct of_device_id rcar_gyroadc_child_match[] = {
+ /* Mode 1 ADCs */
+ {
+ .compatible = "fujitsu,mb88101a",
+ .data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A,
+ },
+ /* Mode 2 ADCs */
+ {
+ .compatible = "ti,adcs7476",
+ .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
+ }, {
+ .compatible = "ti,adc121",
+ .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
+ }, {
+ .compatible = "adi,ad7476",
+ .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476,
+ },
+ /* Mode 3 ADCs */
+ {
+ .compatible = "maxim,max1162",
+ .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
+ }, {
+ .compatible = "maxim,max11100",
+ .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162,
+ },
+ { /* sentinel */ }
+};
+
+static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
+{
+ const struct of_device_id *of_id;
+ const struct iio_chan_spec *channels;
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ struct device *dev = priv->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ struct regulator *vref;
+ unsigned int reg;
+ unsigned int adcmode = -1, childmode;
+ unsigned int sample_width;
+ unsigned int num_channels;
+ int ret, first = 1;
+
+ for_each_child_of_node(np, child) {
+ of_id = of_match_node(rcar_gyroadc_child_match, child);
+ if (!of_id) {
+ dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
+ child);
+ continue;
+ }
+
+ childmode = (uintptr_t)of_id->data;
+ switch (childmode) {
+ case RCAR_GYROADC_MODE_SELECT_1_MB88101A:
+ sample_width = 12;
+ channels = rcar_gyroadc_iio_channels_1;
+ num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1);
+ break;
+ case RCAR_GYROADC_MODE_SELECT_2_ADCS7476:
+ sample_width = 15;
+ channels = rcar_gyroadc_iio_channels_2;
+ num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2);
+ break;
+ case RCAR_GYROADC_MODE_SELECT_3_MAX1162:
+ sample_width = 16;
+ channels = rcar_gyroadc_iio_channels_3;
+ num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
+ break;
+ default:
+ goto err_e_inval;
+ }
+
+ /*
+ * MB88101 is special in that it's only a single chip taking
+ * up all the CHS lines. Thus, the DT binding is also special
+ * and has no reg property. If we run into such ADC, handle
+ * it here.
+ */
+ if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
+ reg = 0;
+ } else {
+ ret = of_property_read_u32(child, "reg", &reg);
+ if (ret) {
+ dev_err(dev,
+ "Failed to get child reg property of ADC \"%pOFn\".\n",
+ child);
+ goto err_of_node_put;
+ }
+
+ /* Channel number is too high. */
+ if (reg >= num_channels) {
+ dev_err(dev,
+ "Only %i channels supported with %pOFn, but reg = <%i>.\n",
+ num_channels, child, reg);
+ goto err_e_inval;
+ }
+ }
+
+ /* Child node selected different mode than the rest. */
+ if (!first && (adcmode != childmode)) {
+ dev_err(dev,
+ "Channel %i uses different ADC mode than the rest.\n",
+ reg);
+ goto err_e_inval;
+ }
+
+ /* Channel is valid, grab the regulator. */
+ dev->of_node = child;
+ vref = devm_regulator_get(dev, "vref");
+ dev->of_node = np;
+ if (IS_ERR(vref)) {
+ dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
+ reg);
+ ret = PTR_ERR(vref);
+ goto err_of_node_put;
+ }
+
+ priv->vref[reg] = vref;
+
+ if (!first)
+ continue;
+
+ /* First child node which passed sanity tests. */
+ adcmode = childmode;
+ first = 0;
+
+ priv->num_channels = num_channels;
+ priv->mode = childmode;
+ priv->sample_width = sample_width;
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = num_channels;
+
+ /*
+ * MB88101 is special and we only have one such device
+ * attached to the GyroADC at a time, so if we found it,
+ * we can stop parsing here.
+ */
+ if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
+ of_node_put(child);
+ break;
+ }
+ }
+
+ if (first) {
+ dev_err(dev, "No valid ADC channels found, aborting.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+
+err_e_inval:
+ ret = -EINVAL;
+err_of_node_put:
+ of_node_put(child);
+ return ret;
+}
+
+static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
+{
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ unsigned int i;
+
+ for (i = 0; i < priv->num_channels; i++) {
+ if (!priv->vref[i])
+ continue;
+
+ regulator_disable(priv->vref[i]);
+ }
+}
+
+static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev)
+{
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ struct device *dev = priv->dev;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < priv->num_channels; i++) {
+ if (!priv->vref[i])
+ continue;
+
+ ret = regulator_enable(priv->vref[i]);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator %i (ret=%i)\n",
+ i, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ rcar_gyroadc_deinit_supplies(indio_dev);
+ return ret;
+}
+
+static int rcar_gyroadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rcar_gyroadc *priv;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->dev = dev;
+
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ priv->clk = devm_clk_get(dev, "fck");
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk),
+ "Failed to get IF clock\n");
+
+ ret = rcar_gyroadc_parse_subdevs(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = rcar_gyroadc_init_supplies(indio_dev);
+ if (ret)
+ return ret;
+
+ priv->model = (uintptr_t)of_device_get_match_data(&pdev->dev);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = DRIVER_NAME;
+ indio_dev->info = &rcar_gyroadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret) {
+ dev_err(dev, "Could not prepare or enable the IF clock.\n");
+ goto err_clk_if_enable;
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto err_power_up;
+
+ rcar_gyroadc_hw_init(priv);
+ rcar_gyroadc_hw_start(priv);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Couldn't register IIO device.\n");
+ goto err_iio_device_register;
+ }
+
+ pm_runtime_put_sync(dev);
+
+ return 0;
+
+err_iio_device_register:
+ rcar_gyroadc_hw_stop(priv);
+ pm_runtime_put_sync(dev);
+err_power_up:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ clk_disable_unprepare(priv->clk);
+err_clk_if_enable:
+ rcar_gyroadc_deinit_supplies(indio_dev);
+
+ return ret;
+}
+
+static int rcar_gyroadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+ struct device *dev = priv->dev;
+
+ iio_device_unregister(indio_dev);
+ pm_runtime_get_sync(dev);
+ rcar_gyroadc_hw_stop(priv);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ clk_disable_unprepare(priv->clk);
+ rcar_gyroadc_deinit_supplies(indio_dev);
+
+ return 0;
+}
+
+static int rcar_gyroadc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+
+ rcar_gyroadc_hw_stop(priv);
+
+ return 0;
+}
+
+static int rcar_gyroadc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rcar_gyroadc *priv = iio_priv(indio_dev);
+
+ rcar_gyroadc_hw_start(priv);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rcar_gyroadc_pm_ops = {
+ RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL)
+};
+
+static struct platform_driver rcar_gyroadc_driver = {
+ .probe = rcar_gyroadc_probe,
+ .remove = rcar_gyroadc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = rcar_gyroadc_match,
+ .pm = pm_ptr(&rcar_gyroadc_pm_ops),
+ },
+};
+
+module_platform_driver(rcar_gyroadc_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("Renesas R-Car GyroADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c
new file mode 100644
index 000000000..6bf32907f
--- /dev/null
+++ b/drivers/iio/adc/rn5t618-adc.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADC driver for the RICOH RN5T618 power management chip family
+ *
+ * Copyright (C) 2019 Andreas Kemnade
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mfd/rn5t618.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/slab.h>
+
+#define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500))
+#define RN5T618_REFERENCE_VOLT 2500
+
+/* mask for selecting channels for single conversion */
+#define RN5T618_ADCCNT3_CHANNEL_MASK 0x7
+/* average 4-time conversion mode */
+#define RN5T618_ADCCNT3_AVG BIT(3)
+/* set for starting a single conversion, gets cleared by hw when done */
+#define RN5T618_ADCCNT3_GODONE BIT(4)
+/* automatic conversion, period is in ADCCNT2, selected channels are
+ * in ADCCNT1
+ */
+#define RN5T618_ADCCNT3_AUTO BIT(5)
+#define RN5T618_ADCEND_IRQ BIT(0)
+
+struct rn5t618_adc_data {
+ struct device *dev;
+ struct rn5t618 *rn5t618;
+ struct completion conv_completion;
+ int irq;
+};
+
+enum rn5t618_channels {
+ LIMMON = 0,
+ VBAT,
+ VADP,
+ VUSB,
+ VSYS,
+ VTHM,
+ AIN1,
+ AIN0
+};
+
+static const struct u16_fract rn5t618_ratios[8] = {
+ [LIMMON] = {50, 32}, /* measured across 20mOhm, amplified by 32 */
+ [VBAT] = {2, 1},
+ [VADP] = {3, 1},
+ [VUSB] = {3, 1},
+ [VSYS] = {3, 1},
+ [VTHM] = {1, 1},
+ [AIN1] = {1, 1},
+ [AIN0] = {1, 1},
+};
+
+static int rn5t618_read_adc_reg(struct rn5t618 *rn5t618, int reg, u16 *val)
+{
+ u8 data[2];
+ int ret;
+
+ ret = regmap_bulk_read(rn5t618->regmap, reg, data, sizeof(data));
+ if (ret < 0)
+ return ret;
+
+ *val = (data[0] << 4) | (data[1] & 0xF);
+
+ return 0;
+}
+
+static irqreturn_t rn5t618_adc_irq(int irq, void *data)
+{
+ struct rn5t618_adc_data *adc = data;
+ unsigned int r = 0;
+ int ret;
+
+ /* clear low & high threshold irqs */
+ regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC1, 0);
+ regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC2, 0);
+
+ ret = regmap_read(adc->rn5t618->regmap, RN5T618_IR_ADC3, &r);
+ if (ret < 0)
+ dev_err(adc->dev, "failed to read IRQ status: %d\n", ret);
+
+ regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC3, 0);
+
+ if (r & RN5T618_ADCEND_IRQ)
+ complete(&adc->conv_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int rn5t618_adc_read(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct rn5t618_adc_data *adc = iio_priv(iio_dev);
+ u16 raw;
+ int ret;
+
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ *val = RN5T618_REFERENCE_VOLT *
+ rn5t618_ratios[chan->channel].numerator;
+ *val2 = rn5t618_ratios[chan->channel].denominator * 4095;
+
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ /* select channel */
+ ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3,
+ RN5T618_ADCCNT3_CHANNEL_MASK,
+ chan->channel);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(adc->rn5t618->regmap, RN5T618_EN_ADCIR3,
+ RN5T618_ADCEND_IRQ);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3,
+ RN5T618_ADCCNT3_AVG,
+ mask == IIO_CHAN_INFO_AVERAGE_RAW ?
+ RN5T618_ADCCNT3_AVG : 0);
+ if (ret < 0)
+ return ret;
+
+ init_completion(&adc->conv_completion);
+ /* single conversion */
+ ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3,
+ RN5T618_ADCCNT3_GODONE,
+ RN5T618_ADCCNT3_GODONE);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&adc->conv_completion,
+ RN5T618_ADC_CONVERSION_TIMEOUT);
+ if (ret == 0) {
+ dev_warn(adc->dev, "timeout waiting for adc result\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = rn5t618_read_adc_reg(adc->rn5t618,
+ RN5T618_ILIMDATAH + 2 * chan->channel,
+ &raw);
+ if (ret < 0)
+ return ret;
+
+ *val = raw;
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info rn5t618_adc_iio_info = {
+ .read_raw = &rn5t618_adc_read,
+};
+
+#define RN5T618_ADC_CHANNEL(_channel, _type, _name) { \
+ .type = _type, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _name, \
+ .indexed = 1. \
+}
+
+static const struct iio_chan_spec rn5t618_adc_iio_channels[] = {
+ RN5T618_ADC_CHANNEL(LIMMON, IIO_CURRENT, "LIMMON"),
+ RN5T618_ADC_CHANNEL(VBAT, IIO_VOLTAGE, "VBAT"),
+ RN5T618_ADC_CHANNEL(VADP, IIO_VOLTAGE, "VADP"),
+ RN5T618_ADC_CHANNEL(VUSB, IIO_VOLTAGE, "VUSB"),
+ RN5T618_ADC_CHANNEL(VSYS, IIO_VOLTAGE, "VSYS"),
+ RN5T618_ADC_CHANNEL(VTHM, IIO_VOLTAGE, "VTHM"),
+ RN5T618_ADC_CHANNEL(AIN1, IIO_VOLTAGE, "AIN1"),
+ RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0")
+};
+
+static 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)
+{
+ int ret;
+ struct iio_dev *iio_dev;
+ struct rn5t618_adc_data *adc;
+ struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!iio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(iio_dev);
+ adc->dev = &pdev->dev;
+ adc->rn5t618 = rn5t618;
+
+ if (rn5t618->irq_data)
+ adc->irq = regmap_irq_get_virq(rn5t618->irq_data,
+ RN5T618_IRQ_ADC);
+
+ if (adc->irq <= 0) {
+ dev_err(&pdev->dev, "get virq failed\n");
+ return -EINVAL;
+ }
+
+ init_completion(&adc->conv_completion);
+
+ iio_dev->name = dev_name(&pdev->dev);
+ iio_dev->info = &rn5t618_adc_iio_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = rn5t618_adc_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(rn5t618_adc_iio_channels);
+
+ /* stop any auto-conversion */
+ ret = regmap_write(rn5t618->regmap, RN5T618_ADCCNT3, 0);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, iio_dev);
+
+ ret = devm_request_threaded_irq(adc->dev, adc->irq, NULL,
+ rn5t618_adc_irq,
+ IRQF_ONESHOT, dev_name(adc->dev),
+ adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "request irq %d failed: %d\n", adc->irq, ret);
+ return ret;
+ }
+
+ ret = devm_iio_map_array_register(adc->dev, iio_dev, rn5t618_maps);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(adc->dev, iio_dev);
+}
+
+static struct platform_driver rn5t618_adc_driver = {
+ .driver = {
+ .name = "rn5t618-adc",
+ },
+ .probe = rn5t618_adc_probe,
+};
+
+module_platform_driver(rn5t618_adc_driver);
+MODULE_ALIAS("platform:rn5t618-adc");
+MODULE_DESCRIPTION("RICOH RN5T618 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
new file mode 100644
index 000000000..b87ea7148
--- /dev/null
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Rockchip Successive Approximation Register (SAR) A/D Converter
+ * Copyright (C) 2014 ROCKCHIP, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define SARADC_DATA 0x00
+
+#define SARADC_STAS 0x04
+#define SARADC_STAS_BUSY BIT(0)
+
+#define SARADC_CTRL 0x08
+#define SARADC_CTRL_IRQ_STATUS BIT(6)
+#define SARADC_CTRL_IRQ_ENABLE BIT(5)
+#define SARADC_CTRL_POWER_CTRL BIT(3)
+#define SARADC_CTRL_CHN_MASK 0x7
+
+#define SARADC_DLY_PU_SOC 0x0c
+#define SARADC_DLY_PU_SOC_MASK 0x3f
+
+#define SARADC_TIMEOUT msecs_to_jiffies(100)
+#define SARADC_MAX_CHANNELS 8
+
+struct rockchip_saradc_data {
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ unsigned long clk_rate;
+};
+
+struct rockchip_saradc {
+ void __iomem *regs;
+ struct clk *pclk;
+ struct clk *clk;
+ struct completion completion;
+ struct regulator *vref;
+ int uv_vref;
+ struct reset_control *reset;
+ const struct rockchip_saradc_data *data;
+ u16 last_val;
+ const struct iio_chan_spec *last_chan;
+ struct notifier_block nb;
+};
+
+static void rockchip_saradc_power_down(struct rockchip_saradc *info)
+{
+ /* Clear irq & power down adc */
+ writel_relaxed(0, info->regs + SARADC_CTRL);
+}
+
+static int rockchip_saradc_conversion(struct rockchip_saradc *info,
+ struct iio_chan_spec const *chan)
+{
+ reinit_completion(&info->completion);
+
+ /* 8 clock periods as delay between power up and start cmd */
+ writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
+
+ info->last_chan = chan;
+
+ /* Select the channel to be used and trigger conversion */
+ writel(SARADC_CTRL_POWER_CTRL
+ | (chan->channel & SARADC_CTRL_CHN_MASK)
+ | SARADC_CTRL_IRQ_ENABLE,
+ info->regs + SARADC_CTRL);
+
+ if (!wait_for_completion_timeout(&info->completion, SARADC_TIMEOUT))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct rockchip_saradc *info = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+
+ ret = rockchip_saradc_conversion(info, chan);
+ if (ret) {
+ rockchip_saradc_power_down(info);
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ *val = info->last_val;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->uv_vref / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
+{
+ struct rockchip_saradc *info = dev_id;
+
+ /* Read value */
+ info->last_val = readl_relaxed(info->regs + SARADC_DATA);
+ info->last_val &= GENMASK(info->last_chan->scan_type.realbits - 1, 0);
+
+ rockchip_saradc_power_down(info);
+
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info rockchip_saradc_iio_info = {
+ .read_raw = rockchip_saradc_read_raw,
+};
+
+#define SARADC_CHANNEL(_index, _id, _res) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _id, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = _res, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
+ SARADC_CHANNEL(0, "adc0", 10),
+ SARADC_CHANNEL(1, "adc1", 10),
+ SARADC_CHANNEL(2, "adc2", 10),
+};
+
+static const struct rockchip_saradc_data saradc_data = {
+ .channels = rockchip_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
+ .clk_rate = 1000000,
+};
+
+static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
+ SARADC_CHANNEL(0, "adc0", 12),
+ SARADC_CHANNEL(1, "adc1", 12),
+};
+
+static const struct rockchip_saradc_data rk3066_tsadc_data = {
+ .channels = rockchip_rk3066_tsadc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
+ .clk_rate = 50000,
+};
+
+static const struct iio_chan_spec rockchip_rk3399_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),
+};
+
+static const struct rockchip_saradc_data rk3399_saradc_data = {
+ .channels = rockchip_rk3399_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels),
+ .clk_rate = 1000000,
+};
+
+static const struct iio_chan_spec rockchip_rk3568_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 rk3568_saradc_data = {
+ .channels = rockchip_rk3568_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3568_saradc_iio_channels),
+ .clk_rate = 1000000,
+};
+
+static const struct of_device_id rockchip_saradc_match[] = {
+ {
+ .compatible = "rockchip,saradc",
+ .data = &saradc_data,
+ }, {
+ .compatible = "rockchip,rk3066-tsadc",
+ .data = &rk3066_tsadc_data,
+ }, {
+ .compatible = "rockchip,rk3399-saradc",
+ .data = &rk3399_saradc_data,
+ }, {
+ .compatible = "rockchip,rk3568-saradc",
+ .data = &rk3568_saradc_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
+
+/*
+ * Reset SARADC Controller.
+ */
+static void rockchip_saradc_reset_controller(struct reset_control *reset)
+{
+ reset_control_assert(reset);
+ usleep_range(10, 20);
+ reset_control_deassert(reset);
+}
+
+static void rockchip_saradc_clk_disable(void *data)
+{
+ struct rockchip_saradc *info = data;
+
+ clk_disable_unprepare(info->clk);
+}
+
+static void rockchip_saradc_pclk_disable(void *data)
+{
+ struct rockchip_saradc *info = data;
+
+ clk_disable_unprepare(info->pclk);
+}
+
+static void rockchip_saradc_regulator_disable(void *data)
+{
+ struct rockchip_saradc *info = data;
+
+ regulator_disable(info->vref);
+}
+
+static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *i_dev = pf->indio_dev;
+ struct rockchip_saradc *info = iio_priv(i_dev);
+ /*
+ * @values: each channel takes an u16 value
+ * @timestamp: will be 8-byte aligned automatically
+ */
+ struct {
+ u16 values[SARADC_MAX_CHANNELS];
+ int64_t timestamp;
+ } data;
+ int ret;
+ int i, j = 0;
+
+ mutex_lock(&i_dev->mlock);
+
+ for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) {
+ const struct iio_chan_spec *chan = &i_dev->channels[i];
+
+ ret = rockchip_saradc_conversion(info, chan);
+ if (ret) {
+ rockchip_saradc_power_down(info);
+ goto out;
+ }
+
+ data.values[j] = info->last_val;
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(i_dev, &data, iio_get_time_ns(i_dev));
+out:
+ mutex_unlock(&i_dev->mlock);
+
+ iio_trigger_notify_done(i_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int rockchip_saradc_volt_notify(struct notifier_block *nb,
+ unsigned long event,
+ void *data)
+{
+ struct rockchip_saradc *info =
+ container_of(nb, struct rockchip_saradc, nb);
+
+ if (event & REGULATOR_EVENT_VOLTAGE_CHANGE)
+ info->uv_vref = (unsigned long)data;
+
+ return NOTIFY_OK;
+}
+
+static void rockchip_saradc_regulator_unreg_notifier(void *data)
+{
+ struct rockchip_saradc *info = data;
+
+ regulator_unregister_notifier(info->vref, &info->nb);
+}
+
+static int rockchip_saradc_probe(struct platform_device *pdev)
+{
+ struct rockchip_saradc *info = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ struct iio_dev *indio_dev = NULL;
+ const struct of_device_id *match;
+ int ret;
+ int irq;
+
+ if (!np)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+ info = iio_priv(indio_dev);
+
+ match = of_match_device(rockchip_saradc_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
+ info->data = match->data;
+
+ /* Sanity check for possible later IP variants with more channels */
+ if (info->data->num_channels > SARADC_MAX_CHANNELS) {
+ dev_err(&pdev->dev, "max channels exceeded");
+ return -EINVAL;
+ }
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ /*
+ * The reset should be an optional property, as it should work
+ * with old devicetrees as well
+ */
+ info->reset = devm_reset_control_get_exclusive(&pdev->dev,
+ "saradc-apb");
+ if (IS_ERR(info->reset)) {
+ ret = PTR_ERR(info->reset);
+ if (ret != -ENOENT)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to get saradc-apb\n");
+
+ dev_dbg(&pdev->dev, "no reset control found\n");
+ info->reset = NULL;
+ }
+
+ init_completion(&info->completion);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return dev_err_probe(&pdev->dev, irq, "failed to get irq\n");
+
+ ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
+ 0, dev_name(&pdev->dev), info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
+ return ret;
+ }
+
+ info->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
+ if (IS_ERR(info->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk),
+ "failed to get pclk\n");
+
+ info->clk = devm_clk_get(&pdev->dev, "saradc");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(info->clk),
+ "failed to get adc clock\n");
+
+ info->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(info->vref))
+ return dev_err_probe(&pdev->dev, PTR_ERR(info->vref),
+ "failed to get regulator\n");
+
+ 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) {
+ dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(info->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable vref regulator\n");
+ return ret;
+ }
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rockchip_saradc_regulator_disable, info);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register devm action, %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(info->vref);
+ if (ret < 0)
+ return ret;
+
+ info->uv_vref = ret;
+
+ ret = clk_prepare_enable(info->pclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable pclk\n");
+ return ret;
+ }
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rockchip_saradc_pclk_disable, info);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register devm action, %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable converter clock\n");
+ return ret;
+ }
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rockchip_saradc_clk_disable, info);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register devm action, %d\n",
+ ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &rockchip_saradc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = info->data->channels;
+ indio_dev->num_channels = info->data->num_channels;
+ ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL,
+ rockchip_saradc_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
+
+ info->nb.notifier_call = rockchip_saradc_volt_notify;
+ ret = regulator_register_notifier(info->vref, &info->nb);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&pdev->dev,
+ rockchip_saradc_regulator_unreg_notifier,
+ info);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static int rockchip_saradc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rockchip_saradc *info = iio_priv(indio_dev);
+
+ clk_disable_unprepare(info->clk);
+ clk_disable_unprepare(info->pclk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static int rockchip_saradc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rockchip_saradc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(info->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ clk_disable_unprepare(info->pclk);
+
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
+ rockchip_saradc_suspend,
+ rockchip_saradc_resume);
+
+static struct platform_driver rockchip_saradc_driver = {
+ .probe = rockchip_saradc_probe,
+ .driver = {
+ .name = "rockchip-saradc",
+ .of_match_table = rockchip_saradc_match,
+ .pm = pm_sleep_ptr(&rockchip_saradc_pm_ops),
+ },
+};
+
+module_platform_driver(rockchip_saradc_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Rockchip SARADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
new file mode 100644
index 000000000..c1b2e8dc9
--- /dev/null
+++ b/drivers/iio/adc/rtq6056.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Richtek Technology Corp.
+ *
+ * ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define RTQ6056_REG_CONFIG 0x00
+#define RTQ6056_REG_SHUNTVOLT 0x01
+#define RTQ6056_REG_BUSVOLT 0x02
+#define RTQ6056_REG_POWER 0x03
+#define RTQ6056_REG_CURRENT 0x04
+#define RTQ6056_REG_CALIBRATION 0x05
+#define RTQ6056_REG_MASKENABLE 0x06
+#define RTQ6056_REG_ALERTLIMIT 0x07
+#define RTQ6056_REG_MANUFACTID 0xFE
+#define RTQ6056_REG_DIEID 0xFF
+
+#define RTQ6056_VENDOR_ID 0x1214
+#define RTQ6056_DEFAULT_CONFIG 0x4127
+#define RTQ6056_CONT_ALLON 7
+
+enum {
+ RTQ6056_CH_VSHUNT = 0,
+ RTQ6056_CH_VBUS,
+ RTQ6056_CH_POWER,
+ RTQ6056_CH_CURRENT,
+ RTQ6056_MAX_CHANNEL
+};
+
+enum {
+ F_OPMODE = 0,
+ F_VSHUNTCT,
+ F_VBUSCT,
+ F_AVG,
+ F_RESET,
+ F_MAX_FIELDS
+};
+
+struct rtq6056_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regmap_field *rm_fields[F_MAX_FIELDS];
+ u32 shunt_resistor_uohm;
+ int vshuntct_us;
+ int vbusct_us;
+ int avg_sample;
+};
+
+static const struct reg_field rtq6056_reg_fields[F_MAX_FIELDS] = {
+ [F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2),
+ [F_VSHUNTCT] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 5),
+ [F_VBUSCT] = REG_FIELD(RTQ6056_REG_CONFIG, 6, 8),
+ [F_AVG] = REG_FIELD(RTQ6056_REG_CONFIG, 9, 11),
+ [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15),
+};
+
+static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .address = RTQ6056_REG_SHUNTVOLT,
+ .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),
+ .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 = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .address = RTQ6056_REG_BUSVOLT,
+ .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),
+ .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 = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 2,
+ .address = RTQ6056_REG_POWER,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .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 = 2,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .channel = 3,
+ .address = RTQ6056_REG_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .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 = 3,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL),
+};
+
+static int rtq6056_adc_read_channel(struct rtq6056_priv *priv,
+ struct iio_chan_spec const *ch,
+ int *val)
+{
+ struct device *dev = priv->dev;
+ unsigned int addr = ch->address;
+ unsigned int regval;
+ int ret;
+
+ pm_runtime_get_sync(dev);
+ ret = regmap_read(priv->regmap, addr, &regval);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put(dev);
+ if (ret)
+ return ret;
+
+ /* Power and VBUS is unsigned 16-bit, others are signed 16-bit */
+ if (addr == RTQ6056_REG_BUSVOLT || addr == RTQ6056_REG_POWER)
+ *val = regval;
+ else
+ *val = sign_extend32(regval, 16);
+
+ return IIO_VAL_INT;
+}
+
+static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val,
+ int *val2)
+{
+ switch (ch->address) {
+ case RTQ6056_REG_SHUNTVOLT:
+ /* VSHUNT lsb 2.5uV */
+ *val = 2500;
+ *val2 = 1000000;
+ return IIO_VAL_FRACTIONAL;
+ case RTQ6056_REG_BUSVOLT:
+ /* VBUS lsb 1.25mV */
+ *val = 1250;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ case RTQ6056_REG_POWER:
+ /* Power lsb 25mW */
+ *val = 25;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Sample frequency for channel VSHUNT and VBUS. The indices correspond
+ * with the bit value expected by the chip. And it can be found at
+ * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf
+ */
+static const int rtq6056_samp_freq_list[] = {
+ 7194, 4926, 3717, 1904, 964, 485, 243, 122,
+};
+
+static int rtq6056_adc_set_samp_freq(struct rtq6056_priv *priv,
+ struct iio_chan_spec const *ch, int val)
+{
+ struct regmap_field *rm_field;
+ unsigned int selector;
+ int *ct, ret;
+
+ if (val > 7194 || val < 122)
+ return -EINVAL;
+
+ if (ch->address == RTQ6056_REG_SHUNTVOLT) {
+ rm_field = priv->rm_fields[F_VSHUNTCT];
+ ct = &priv->vshuntct_us;
+ } else if (ch->address == RTQ6056_REG_BUSVOLT) {
+ rm_field = priv->rm_fields[F_VBUSCT];
+ ct = &priv->vbusct_us;
+ } else
+ return -EINVAL;
+
+ selector = find_closest_descending(val, rtq6056_samp_freq_list,
+ ARRAY_SIZE(rtq6056_samp_freq_list));
+
+ ret = regmap_field_write(rm_field, selector);
+ if (ret)
+ return ret;
+
+ *ct = 1000000 / rtq6056_samp_freq_list[selector];
+
+ return 0;
+}
+
+/*
+ * Available averaging rate for rtq6056. The indices correspond with the bit
+ * value expected by the chip. And it can be found at
+ * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf
+ */
+static const int rtq6056_avg_sample_list[] = {
+ 1, 4, 16, 64, 128, 256, 512, 1024,
+};
+
+static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val)
+{
+ unsigned int selector;
+ int ret;
+
+ if (val > 1024 || val < 1)
+ return -EINVAL;
+
+ selector = find_closest(val, rtq6056_avg_sample_list,
+ ARRAY_SIZE(rtq6056_avg_sample_list));
+
+ ret = regmap_field_write(priv->rm_fields[F_AVG], selector);
+ if (ret)
+ return ret;
+
+ priv->avg_sample = rtq6056_avg_sample_list[selector];
+
+ return 0;
+}
+
+static int rtq6056_adc_get_sample_freq(struct rtq6056_priv *priv,
+ struct iio_chan_spec const *ch, int *val)
+{
+ int sample_time;
+
+ if (ch->address == RTQ6056_REG_SHUNTVOLT)
+ sample_time = priv->vshuntct_us;
+ else if (ch->address == RTQ6056_REG_BUSVOLT)
+ sample_time = priv->vbusct_us;
+ else {
+ sample_time = priv->vshuntct_us + priv->vbusct_us;
+ sample_time *= priv->avg_sample;
+ }
+
+ *val = 1000000 / sample_time;
+
+ return IIO_VAL_INT;
+}
+
+static int rtq6056_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct rtq6056_priv *priv = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return rtq6056_adc_read_channel(priv, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ return rtq6056_adc_read_scale(chan, val, val2);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = priv->avg_sample;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return rtq6056_adc_get_sample_freq(priv, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rtq6056_adc_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 = rtq6056_samp_freq_list;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(rtq6056_samp_freq_list);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = rtq6056_avg_sample_list;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(rtq6056_avg_sample_list);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct rtq6056_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = rtq6056_adc_set_samp_freq(priv, chan, val);
+ break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = rtq6056_adc_set_average(priv, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = {
+ [RTQ6056_CH_VSHUNT] = "Vshunt",
+ [RTQ6056_CH_VBUS] = "Vbus",
+ [RTQ6056_CH_POWER] = "Power",
+ [RTQ6056_CH_CURRENT] = "Current",
+};
+
+static int rtq6056_adc_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ char *label)
+{
+ return sysfs_emit(label, "%s\n", rtq6056_channel_labels[chan->channel]);
+}
+
+static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv,
+ int resistor_uohm)
+{
+ unsigned int calib_val;
+ int ret;
+
+ if (resistor_uohm <= 0) {
+ dev_err(priv->dev, "Invalid resistor [%d]\n", resistor_uohm);
+ return -EINVAL;
+ }
+
+ /* calibration = 5120000 / (Rshunt (uOhm) * current lsb (1mA)) */
+ calib_val = 5120000 / resistor_uohm;
+ ret = regmap_write(priv->regmap, RTQ6056_REG_CALIBRATION, calib_val);
+ if (ret)
+ return ret;
+
+ priv->shunt_resistor_uohm = resistor_uohm;
+
+ return 0;
+}
+
+static ssize_t shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rtq6056_priv *priv = iio_priv(dev_to_iio_dev(dev));
+ int vals[2] = { priv->shunt_resistor_uohm, 1000000 };
+
+ return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals);
+}
+
+static ssize_t shunt_resistor_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(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;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
+ if (ret)
+ goto out_store;
+
+ ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract);
+
+out_store:
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret ?: len;
+}
+
+static IIO_DEVICE_ATTR_RW(shunt_resistor, 0);
+
+static struct attribute *rtq6056_attributes[] = {
+ &iio_dev_attr_shunt_resistor.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group rtq6056_attribute_group = {
+ .attrs = rtq6056_attributes,
+};
+
+static const struct iio_info rtq6056_info = {
+ .attrs = &rtq6056_attribute_group,
+ .read_raw = rtq6056_adc_read_raw,
+ .read_avail = rtq6056_adc_read_avail,
+ .write_raw = rtq6056_adc_write_raw,
+ .read_label = rtq6056_adc_read_label,
+};
+
+static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct rtq6056_priv *priv = iio_priv(indio_dev);
+ struct device *dev = priv->dev;
+ struct {
+ u16 vals[RTQ6056_MAX_CHANNEL];
+ s64 timestamp __aligned(8);
+ } data;
+ unsigned int raw;
+ int i = 0, bit, ret;
+
+ memset(&data, 0, sizeof(data));
+
+ pm_runtime_get_sync(dev);
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+ unsigned int addr = rtq6056_channels[bit].address;
+
+ ret = regmap_read(priv->regmap, addr, &raw);
+ if (ret)
+ goto out;
+
+ data.vals[i++] = raw;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev));
+
+out:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put(dev);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void rtq6056_enter_shutdown_state(void *dev)
+{
+ struct rtq6056_priv *priv = dev_get_drvdata(dev);
+
+ /* Enter shutdown state */
+ regmap_field_write(priv->rm_fields[F_OPMODE], 0);
+}
+
+static bool rtq6056_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RTQ6056_REG_CONFIG ... RTQ6056_REG_ALERTLIMIT:
+ case RTQ6056_REG_MANUFACTID ... RTQ6056_REG_DIEID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq6056_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RTQ6056_REG_CONFIG:
+ case RTQ6056_REG_CALIBRATION ... RTQ6056_REG_ALERTLIMIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rtq6056_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = RTQ6056_REG_DIEID,
+ .readable_reg = rtq6056_is_readable_reg,
+ .writeable_reg = rtq6056_is_writeable_reg,
+};
+
+static int rtq6056_probe(struct i2c_client *i2c)
+{
+ struct iio_dev *indio_dev;
+ struct rtq6056_priv *priv;
+ struct device *dev = &i2c->dev;
+ struct regmap *regmap;
+ unsigned int vendor_id, shunt_resistor_uohm;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->dev = dev;
+ priv->vshuntct_us = priv->vbusct_us = 1037;
+ priv->avg_sample = 1;
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, &rtq6056_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to init regmap\n");
+
+ priv->regmap = regmap;
+
+ ret = regmap_read(regmap, RTQ6056_REG_MANUFACTID, &vendor_id);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get manufacturer info\n");
+
+ if (vendor_id != RTQ6056_VENDOR_ID)
+ return dev_err_probe(dev, -ENODEV,
+ "Invalid vendor id 0x%04x\n", vendor_id);
+
+ ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->rm_fields,
+ rtq6056_reg_fields, F_MAX_FIELDS);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init regmap field\n");
+
+ /*
+ * By default, configure average sample as 1, bus and shunt conversion
+ * time as 1037 microsecond, and operating mode to all on.
+ */
+ ret = regmap_write(regmap, RTQ6056_REG_CONFIG, RTQ6056_DEFAULT_CONFIG);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable continuous sensing\n");
+
+ ret = devm_add_action_or_reset(dev, rtq6056_enter_shutdown_state, dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n");
+
+ /* By default, use 2000 micro-Ohm resistor */
+ shunt_resistor_uohm = 2000;
+ device_property_read_u32(dev, "shunt-resistor-micro-ohms",
+ &shunt_resistor_uohm);
+
+ ret = rtq6056_set_shunt_resistor(priv, shunt_resistor_uohm);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to init shunt resistor\n");
+
+ indio_dev->name = "rtq6056";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = rtq6056_channels;
+ indio_dev->num_channels = ARRAY_SIZE(rtq6056_channels);
+ indio_dev->info = &rtq6056_info;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ rtq6056_buffer_trigger_handler,
+ NULL);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to allocate iio trigger buffer\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int rtq6056_runtime_suspend(struct device *dev)
+{
+ struct rtq6056_priv *priv = dev_get_drvdata(dev);
+
+ /* Configure to shutdown mode */
+ return regmap_field_write(priv->rm_fields[F_OPMODE], 0);
+}
+
+static int rtq6056_runtime_resume(struct device *dev)
+{
+ struct rtq6056_priv *priv = dev_get_drvdata(dev);
+ int sample_rdy_time_us, ret;
+
+ ret = regmap_field_write(priv->rm_fields[F_OPMODE], RTQ6056_CONT_ALLON);
+ if (ret)
+ return ret;
+
+ sample_rdy_time_us = priv->vbusct_us + priv->vshuntct_us;
+ sample_rdy_time_us *= priv->avg_sample;
+
+ usleep_range(sample_rdy_time_us, sample_rdy_time_us + 100);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(rtq6056_pm_ops, rtq6056_runtime_suspend,
+ rtq6056_runtime_resume, NULL);
+
+static const struct of_device_id rtq6056_device_match[] = {
+ { .compatible = "richtek,rtq6056" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq6056_device_match);
+
+static struct i2c_driver rtq6056_driver = {
+ .driver = {
+ .name = "rtq6056",
+ .of_match_table = rtq6056_device_match,
+ .pm = pm_ptr(&rtq6056_pm_ops),
+ },
+ .probe_new = rtq6056_probe,
+};
+module_i2c_driver(rtq6056_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RTQ6056 Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
new file mode 100644
index 000000000..0921ff2d9
--- /dev/null
+++ b/drivers/iio/adc/rzg2l_adc.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G2L A/D Converter driver
+ *
+ * Copyright (c) 2021 Renesas Electronics Europe GmbH
+ *
+ * Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/reset.h>
+
+#define DRIVER_NAME "rzg2l-adc"
+
+#define RZG2L_ADM(n) ((n) * 0x4)
+#define RZG2L_ADM0_ADCE BIT(0)
+#define RZG2L_ADM0_ADBSY BIT(1)
+#define RZG2L_ADM0_PWDWNB BIT(2)
+#define RZG2L_ADM0_SRESB BIT(15)
+#define RZG2L_ADM1_TRG BIT(0)
+#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)
+#define RZG2L_ADIVC_DIVADC_4 FIELD_PREP(RZG2L_ADIVC_DIVADC_MASK, 0x4)
+
+#define RZG2L_ADFIL 0x2c
+
+#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_TIMEOUT usecs_to_jiffies(1 * 4)
+
+struct rzg2l_adc_data {
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+};
+
+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;
+ struct mutex lock;
+ u16 last_val[RZG2L_ADC_MAX_CHANNELS];
+};
+
+static const char * const rzg2l_adc_channel_name[] = {
+ "adc0",
+ "adc1",
+ "adc2",
+ "adc3",
+ "adc4",
+ "adc5",
+ "adc6",
+ "adc7",
+};
+
+static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg)
+{
+ return readl(adc->base + reg);
+}
+
+static void rzg2l_adc_writel(struct rzg2l_adc *adc, unsigned int reg, u32 val)
+{
+ writel(val, adc->base + reg);
+}
+
+static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on)
+{
+ u32 reg;
+
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ if (on)
+ reg |= RZG2L_ADM0_PWDWNB;
+ else
+ reg &= ~RZG2L_ADM0_PWDWNB;
+ rzg2l_adc_writel(adc, RZG2L_ADM(0), reg);
+ udelay(2);
+}
+
+static void rzg2l_adc_start_stop(struct rzg2l_adc *adc, bool start)
+{
+ int timeout = 5;
+ u32 reg;
+
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ if (start)
+ reg |= RZG2L_ADM0_ADCE;
+ else
+ reg &= ~RZG2L_ADM0_ADCE;
+ rzg2l_adc_writel(adc, RZG2L_ADM(0), reg);
+
+ 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)));
+}
+
+static void rzg2l_set_trigger(struct rzg2l_adc *adc)
+{
+ u32 reg;
+
+ /*
+ * Setup ADM1 for SW trigger
+ * EGA[13:12] - Set 00 to indicate hardware trigger is invalid
+ * BS[4] - Enable 1-buffer mode
+ * MS[1] - Enable Select mode
+ * TRG[0] - Enable software trigger mode
+ */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(1));
+ reg &= ~RZG2L_ADM1_EGA_MASK;
+ reg &= ~RZG2L_ADM1_BS;
+ reg &= ~RZG2L_ADM1_TRG;
+ reg |= RZG2L_ADM1_MS;
+ rzg2l_adc_writel(adc, RZG2L_ADM(1), reg);
+}
+
+static int rzg2l_adc_conversion_setup(struct rzg2l_adc *adc, u8 ch)
+{
+ u32 reg;
+
+ if (rzg2l_adc_readl(adc, RZG2L_ADM(0)) & RZG2L_ADM0_ADBSY)
+ return -EBUSY;
+
+ rzg2l_set_trigger(adc);
+
+ /* Select analog input channel subjected to conversion. */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(2));
+ reg &= ~RZG2L_ADM2_CHSEL_MASK;
+ reg |= BIT(ch);
+ rzg2l_adc_writel(adc, RZG2L_ADM(2), reg);
+
+ /*
+ * Setup ADINT
+ * INTS[31] - Select pulse signal
+ * CSEEN[16] - Enable channel select error interrupt
+ * INTEN[7:0] - Select channel interrupt
+ */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADINT);
+ reg &= ~RZG2L_ADINT_INTS;
+ reg &= ~RZG2L_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)
+{
+ int ret;
+
+ ret = rzg2l_adc_set_power(indio_dev, true);
+ if (ret)
+ return ret;
+
+ ret = rzg2l_adc_conversion_setup(adc, ch);
+ if (ret) {
+ rzg2l_adc_set_power(indio_dev, false);
+ return ret;
+ }
+
+ reinit_completion(&adc->completion);
+
+ rzg2l_adc_start_stop(adc, true);
+
+ 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;
+ }
+
+ return rzg2l_adc_set_power(indio_dev, false);
+}
+
+static int rzg2l_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct rzg2l_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u8 ch;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_VOLTAGE)
+ 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);
+ return ret;
+ }
+ *val = adc->last_val[ch];
+ mutex_unlock(&adc->lock);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+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]);
+}
+
+static const struct iio_info rzg2l_adc_iio_info = {
+ .read_raw = rzg2l_adc_read_raw,
+ .read_label = rzg2l_adc_read_label,
+};
+
+static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
+{
+ struct rzg2l_adc *adc = dev_id;
+ unsigned long intst;
+ u32 reg;
+ int ch;
+
+ reg = rzg2l_adc_readl(adc, RZG2L_ADSTS);
+
+ /* A/D conversion channel select error interrupt */
+ if (reg & RZG2L_ADSTS_CSEST) {
+ rzg2l_adc_writel(adc, RZG2L_ADSTS, reg);
+ return IRQ_HANDLED;
+ }
+
+ intst = reg & RZG2L_ADSTS_INTST_MASK;
+ if (!intst)
+ return IRQ_NONE;
+
+ for_each_set_bit(ch, &intst, RZG2L_ADC_MAX_CHANNELS)
+ adc->last_val[ch] = rzg2l_adc_readl(adc, RZG2L_ADCR(ch)) & RZG2L_ADCR_AD_MASK;
+
+ /* clear the channel interrupt */
+ rzg2l_adc_writel(adc, RZG2L_ADSTS, reg);
+
+ complete(&adc->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc)
+{
+ struct iio_chan_spec *chan_array;
+ struct fwnode_handle *fwnode;
+ 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;
+
+ i = 0;
+ device_for_each_child_node(&pdev->dev, fwnode) {
+ ret = fwnode_property_read_u32(fwnode, "reg", &channel);
+ if (ret) {
+ fwnode_handle_put(fwnode);
+ return ret;
+ }
+
+ if (channel >= RZG2L_ADC_MAX_CHANNELS) {
+ fwnode_handle_put(fwnode);
+ return -EINVAL;
+ }
+
+ 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++;
+ }
+
+ data->num_channels = num_channels;
+ data->channels = chan_array;
+ adc->data = data;
+
+ return 0;
+}
+
+static int rzg2l_adc_hw_init(struct rzg2l_adc *adc)
+{
+ int timeout = 5;
+ u32 reg;
+ int ret;
+
+ ret = clk_prepare_enable(adc->pclk);
+ if (ret)
+ return ret;
+
+ /* SW reset */
+ reg = rzg2l_adc_readl(adc, RZG2L_ADM(0));
+ 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);
+ }
+
+ /* 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
+ * ADCMP[23:16] - Should be always set to 0xe
+ * ADSMP[15:0] - Set default (0x578) sampling period
+ */
+ 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);
+ rzg2l_adc_writel(adc, RZG2L_ADM(3), reg);
+
+exit_hw_init:
+ clk_disable_unprepare(adc->pclk);
+
+ 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;
+ struct iio_dev *indio_dev;
+ struct rzg2l_adc *adc;
+ int ret;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+
+ ret = rzg2l_adc_parse_properties(pdev, adc);
+ if (ret)
+ return ret;
+
+ mutex_init(&adc->lock);
+
+ adc->base = devm_platform_ioremap_resource(pdev, 0);
+ 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->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;
+ }
+
+ 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);
+ 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;
+ }
+
+ ret = rzg2l_adc_hw_init(adc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize ADC HW, %d\n", ret);
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rzg2l_adc_isr,
+ 0, dev_name(dev), adc);
+ if (ret < 0)
+ return ret;
+
+ 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 of_device_id rzg2l_adc_match[] = {
+ { .compatible = "renesas,rzg2l-adc",},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzg2l_adc_match);
+
+static int __maybe_unused 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)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rzg2l_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = clk_prepare_enable(adc->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(adc->adclk);
+ if (ret) {
+ clk_disable_unprepare(adc->pclk);
+ return ret;
+ }
+
+ rzg2l_adc_pwr(adc, true);
+
+ return 0;
+}
+
+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)
+};
+
+static struct platform_driver rzg2l_adc_driver = {
+ .probe = rzg2l_adc_probe,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = rzg2l_adc_match,
+ .pm = &rzg2l_adc_pm_ops,
+ },
+};
+
+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");
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
new file mode 100644
index 000000000..f8421cbba
--- /dev/null
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -0,0 +1,965 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Spreadtrum Communications Inc.
+
+#include <linux/hwspinlock.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/* PMIC global registers definition */
+#define SC2730_MODULE_EN 0x1808
+#define SC2731_MODULE_EN 0xc08
+#define SC27XX_MODULE_ADC_EN BIT(5)
+#define SC2721_ARM_CLK_EN 0xc0c
+#define SC2730_ARM_CLK_EN 0x180c
+#define SC2731_ARM_CLK_EN 0xc10
+#define SC27XX_CLK_ADC_EN BIT(5)
+#define SC27XX_CLK_ADC_CLK_EN BIT(6)
+
+/* ADC controller registers definition */
+#define SC27XX_ADC_CTL 0x0
+#define SC27XX_ADC_CH_CFG 0x4
+#define SC27XX_ADC_DATA 0x4c
+#define SC27XX_ADC_INT_EN 0x50
+#define SC27XX_ADC_INT_CLR 0x54
+#define SC27XX_ADC_INT_STS 0x58
+#define SC27XX_ADC_INT_RAW 0x5c
+
+/* Bits and mask definition for SC27XX_ADC_CTL register */
+#define SC27XX_ADC_EN BIT(0)
+#define SC27XX_ADC_CHN_RUN BIT(1)
+#define SC27XX_ADC_12BIT_MODE BIT(2)
+#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4)
+#define SC27XX_ADC_RUN_NUM_SHIFT 4
+
+/* Bits and mask definition for SC27XX_ADC_CH_CFG register */
+#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0)
+#define SC27XX_ADC_SCALE_MASK GENMASK(10, 9)
+#define SC2721_ADC_SCALE_MASK BIT(5)
+#define SC27XX_ADC_SCALE_SHIFT 9
+#define SC2721_ADC_SCALE_SHIFT 5
+
+/* Bits definitions for SC27XX_ADC_INT_EN registers */
+#define SC27XX_ADC_IRQ_EN BIT(0)
+
+/* Bits definitions for SC27XX_ADC_INT_CLR registers */
+#define SC27XX_ADC_IRQ_CLR BIT(0)
+
+/* Bits definitions for SC27XX_ADC_INT_RAW registers */
+#define SC27XX_ADC_IRQ_RAW BIT(0)
+
+/* Mask definition for SC27XX_ADC_DATA register */
+#define SC27XX_ADC_DATA_MASK GENMASK(11, 0)
+
+/* Timeout (ms) for the trylock of hardware spinlocks */
+#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
+
+/* Timeout (us) for ADC data conversion according to ADC datasheet */
+#define SC27XX_ADC_RDY_TIMEOUT 1000000
+#define SC27XX_ADC_POLL_RAW_STATUS 500
+
+/* Maximum ADC channel number */
+#define SC27XX_ADC_CHANNEL_MAX 32
+
+/* ADC voltage ratio definition */
+#define SC27XX_VOLT_RATIO(n, d) \
+ (((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d))
+#define SC27XX_RATIO_NUMERATOR_OFFSET 16
+#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0)
+
+/* ADC specific channel reference voltage 3.5V */
+#define SC27XX_ADC_REFVOL_VDD35 3500000
+
+/* ADC default channel reference voltage is 2.8V */
+#define SC27XX_ADC_REFVOL_VDD28 2800000
+
+struct sc27xx_adc_data {
+ struct device *dev;
+ struct regulator *volref;
+ struct regmap *regmap;
+ /*
+ * One hardware spinlock to synchronize between the multiple
+ * subsystems which will access the unique ADC controller.
+ */
+ struct hwspinlock *hwlock;
+ int channel_scale[SC27XX_ADC_CHANNEL_MAX];
+ u32 base;
+ int irq;
+ const struct sc27xx_adc_variant_data *var_data;
+};
+
+/*
+ * Since different PMICs of SC27xx series can have different
+ * address and ratio, we should save ratio config and base
+ * in the device data structure.
+ */
+struct sc27xx_adc_variant_data {
+ u32 module_en;
+ u32 clk_en;
+ u32 scale_shift;
+ u32 scale_mask;
+ const struct sc27xx_adc_linear_graph *bscale_cal;
+ const struct sc27xx_adc_linear_graph *sscale_cal;
+ void (*init_scale)(struct sc27xx_adc_data *data);
+ int (*get_ratio)(int channel, int scale);
+ bool set_volref;
+};
+
+struct sc27xx_adc_linear_graph {
+ int volt0;
+ int adc0;
+ int volt1;
+ int adc1;
+};
+
+/*
+ * According to the datasheet, we can convert one ADC value to one voltage value
+ * through 2 points in the linear graph. If the voltage is less than 1.2v, we
+ * should use the small-scale graph, and if more than 1.2v, we should use the
+ * big-scale graph.
+ */
+static struct sc27xx_adc_linear_graph big_scale_graph = {
+ 4200, 3310,
+ 3600, 2832,
+};
+
+static struct sc27xx_adc_linear_graph small_scale_graph = {
+ 1000, 3413,
+ 100, 341,
+};
+
+static const struct sc27xx_adc_linear_graph sc2731_big_scale_graph_calib = {
+ 4200, 850,
+ 3600, 728,
+};
+
+static const struct sc27xx_adc_linear_graph sc2731_small_scale_graph_calib = {
+ 1000, 838,
+ 100, 84,
+};
+
+static const struct sc27xx_adc_linear_graph big_scale_graph_calib = {
+ 4200, 856,
+ 3600, 733,
+};
+
+static const struct sc27xx_adc_linear_graph small_scale_graph_calib = {
+ 1000, 833,
+ 100, 80,
+};
+
+static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc)
+{
+ return ((calib_data & 0xff) + calib_adc - 128) * 4;
+}
+
+/* get the adc nvmem cell calibration data */
+static int adc_nvmem_cell_calib_data(struct sc27xx_adc_data *data, const char *cell_name)
+{
+ struct nvmem_cell *cell;
+ void *buf;
+ u32 origin_calib_data = 0;
+ size_t len;
+
+ if (!data)
+ return -EINVAL;
+
+ cell = nvmem_cell_get(data->dev, cell_name);
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ buf = nvmem_cell_read(cell, &len);
+ if (IS_ERR(buf)) {
+ nvmem_cell_put(cell);
+ return PTR_ERR(buf);
+ }
+
+ memcpy(&origin_calib_data, buf, min(len, sizeof(u32)));
+
+ kfree(buf);
+ nvmem_cell_put(cell);
+ return origin_calib_data;
+}
+
+static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data,
+ bool big_scale)
+{
+ const struct sc27xx_adc_linear_graph *calib_graph;
+ struct sc27xx_adc_linear_graph *graph;
+ const char *cell_name;
+ u32 calib_data = 0;
+
+ if (big_scale) {
+ calib_graph = data->var_data->bscale_cal;
+ graph = &big_scale_graph;
+ cell_name = "big_scale_calib";
+ } else {
+ calib_graph = data->var_data->sscale_cal;
+ graph = &small_scale_graph;
+ cell_name = "small_scale_calib";
+ }
+
+ calib_data = adc_nvmem_cell_calib_data(data, cell_name);
+
+ /* Only need to calibrate the adc values in the linear graph. */
+ graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
+ graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
+ calib_graph->adc1);
+
+ return 0;
+}
+
+static int sc2720_adc_get_ratio(int channel, int scale)
+{
+ switch (channel) {
+ case 14:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(68, 900);
+ case 1:
+ return SC27XX_VOLT_RATIO(68, 1760);
+ case 2:
+ return SC27XX_VOLT_RATIO(68, 2327);
+ case 3:
+ return SC27XX_VOLT_RATIO(68, 3654);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ case 16:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(48, 100);
+ case 1:
+ return SC27XX_VOLT_RATIO(480, 1955);
+ case 2:
+ return SC27XX_VOLT_RATIO(480, 2586);
+ case 3:
+ return SC27XX_VOLT_RATIO(48, 406);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ case 21:
+ case 22:
+ case 23:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(3, 8);
+ case 1:
+ return SC27XX_VOLT_RATIO(375, 1955);
+ case 2:
+ return SC27XX_VOLT_RATIO(375, 2586);
+ case 3:
+ return SC27XX_VOLT_RATIO(300, 3248);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ default:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(1, 1);
+ case 1:
+ return SC27XX_VOLT_RATIO(1000, 1955);
+ case 2:
+ return SC27XX_VOLT_RATIO(1000, 2586);
+ case 3:
+ return SC27XX_VOLT_RATIO(100, 406);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ }
+ return SC27XX_VOLT_RATIO(1, 1);
+}
+
+static int sc2721_adc_get_ratio(int channel, int scale)
+{
+ switch (channel) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return scale ? SC27XX_VOLT_RATIO(400, 1025) :
+ SC27XX_VOLT_RATIO(1, 1);
+ case 5:
+ return SC27XX_VOLT_RATIO(7, 29);
+ case 7:
+ case 9:
+ return scale ? SC27XX_VOLT_RATIO(100, 125) :
+ SC27XX_VOLT_RATIO(1, 1);
+ case 14:
+ return SC27XX_VOLT_RATIO(68, 900);
+ case 16:
+ return SC27XX_VOLT_RATIO(48, 100);
+ case 19:
+ return SC27XX_VOLT_RATIO(1, 3);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ return SC27XX_VOLT_RATIO(1, 1);
+}
+
+static int sc2730_adc_get_ratio(int channel, int scale)
+{
+ switch (channel) {
+ case 14:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(68, 900);
+ case 1:
+ return SC27XX_VOLT_RATIO(68, 1760);
+ case 2:
+ return SC27XX_VOLT_RATIO(68, 2327);
+ case 3:
+ return SC27XX_VOLT_RATIO(68, 3654);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ case 15:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(1, 3);
+ case 1:
+ return SC27XX_VOLT_RATIO(1000, 5865);
+ case 2:
+ return SC27XX_VOLT_RATIO(500, 3879);
+ case 3:
+ return SC27XX_VOLT_RATIO(500, 6090);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ case 16:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(48, 100);
+ case 1:
+ return SC27XX_VOLT_RATIO(480, 1955);
+ case 2:
+ return SC27XX_VOLT_RATIO(480, 2586);
+ case 3:
+ return SC27XX_VOLT_RATIO(48, 406);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ case 21:
+ case 22:
+ case 23:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(3, 8);
+ case 1:
+ return SC27XX_VOLT_RATIO(375, 1955);
+ case 2:
+ return SC27XX_VOLT_RATIO(375, 2586);
+ case 3:
+ return SC27XX_VOLT_RATIO(300, 3248);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ default:
+ switch (scale) {
+ case 0:
+ return SC27XX_VOLT_RATIO(1, 1);
+ case 1:
+ return SC27XX_VOLT_RATIO(1000, 1955);
+ case 2:
+ return SC27XX_VOLT_RATIO(1000, 2586);
+ case 3:
+ return SC27XX_VOLT_RATIO(1000, 4060);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ }
+ return SC27XX_VOLT_RATIO(1, 1);
+}
+
+static int sc2731_adc_get_ratio(int channel, int scale)
+{
+ switch (channel) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return scale ? SC27XX_VOLT_RATIO(400, 1025) :
+ SC27XX_VOLT_RATIO(1, 1);
+ case 5:
+ return SC27XX_VOLT_RATIO(7, 29);
+ case 6:
+ return SC27XX_VOLT_RATIO(375, 9000);
+ case 7:
+ case 8:
+ return scale ? SC27XX_VOLT_RATIO(100, 125) :
+ SC27XX_VOLT_RATIO(1, 1);
+ case 19:
+ return SC27XX_VOLT_RATIO(1, 3);
+ default:
+ return SC27XX_VOLT_RATIO(1, 1);
+ }
+ return SC27XX_VOLT_RATIO(1, 1);
+}
+
+/*
+ * According to the datasheet set specific value on some channel.
+ */
+static void sc2720_adc_scale_init(struct sc27xx_adc_data *data)
+{
+ int i;
+
+ for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) {
+ switch (i) {
+ case 5:
+ data->channel_scale[i] = 3;
+ break;
+ case 7:
+ case 9:
+ data->channel_scale[i] = 2;
+ break;
+ case 13:
+ data->channel_scale[i] = 1;
+ break;
+ case 19:
+ case 30:
+ case 31:
+ data->channel_scale[i] = 3;
+ break;
+ default:
+ data->channel_scale[i] = 0;
+ break;
+ }
+ }
+}
+
+static void sc2730_adc_scale_init(struct sc27xx_adc_data *data)
+{
+ int i;
+
+ for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) {
+ switch (i) {
+ case 5:
+ case 10:
+ case 19:
+ case 30:
+ case 31:
+ data->channel_scale[i] = 3;
+ break;
+ case 7:
+ case 9:
+ data->channel_scale[i] = 2;
+ break;
+ case 13:
+ data->channel_scale[i] = 1;
+ break;
+ default:
+ data->channel_scale[i] = 0;
+ break;
+ }
+ }
+}
+
+static void sc2731_adc_scale_init(struct sc27xx_adc_data *data)
+{
+ int i;
+ /*
+ * In the current software design, SC2731 support 2 scales,
+ * channels 5 uses big scale, others use smale.
+ */
+ for (i = 0; i < SC27XX_ADC_CHANNEL_MAX; i++) {
+ switch (i) {
+ case 5:
+ data->channel_scale[i] = 1;
+ break;
+ default:
+ data->channel_scale[i] = 0;
+ break;
+ }
+ }
+}
+
+static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
+ int scale, int *val)
+{
+ int ret, ret_volref;
+ u32 tmp, value, status;
+
+ ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT);
+ if (ret) {
+ dev_err(data->dev, "timeout to get the hwspinlock\n");
+ return ret;
+ }
+
+ /*
+ * According to the sc2721 chip data sheet, the reference voltage of
+ * specific channel 30 and channel 31 in ADC module needs to be set from
+ * the default 2.8v to 3.5v.
+ */
+ if ((data->var_data->set_volref) && (channel == 30 || channel == 31)) {
+ ret = regulator_set_voltage(data->volref,
+ SC27XX_ADC_REFVOL_VDD35,
+ SC27XX_ADC_REFVOL_VDD35);
+ if (ret) {
+ dev_err(data->dev, "failed to set the volref 3.5v\n");
+ goto unlock_adc;
+ }
+ }
+
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
+ SC27XX_ADC_EN, SC27XX_ADC_EN);
+ if (ret)
+ goto regulator_restore;
+
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
+ SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
+ if (ret)
+ goto disable_adc;
+
+ /* Configure the channel id and scale */
+ tmp = (scale << data->var_data->scale_shift) & data->var_data->scale_mask;
+ tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG,
+ SC27XX_ADC_CHN_ID_MASK |
+ data->var_data->scale_mask,
+ tmp);
+ if (ret)
+ goto disable_adc;
+
+ /* Select 12bit conversion mode, and only sample 1 time */
+ tmp = SC27XX_ADC_12BIT_MODE;
+ tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK;
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
+ SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE,
+ tmp);
+ if (ret)
+ goto disable_adc;
+
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
+ SC27XX_ADC_CHN_RUN, SC27XX_ADC_CHN_RUN);
+ if (ret)
+ goto disable_adc;
+
+ ret = regmap_read_poll_timeout(data->regmap,
+ data->base + SC27XX_ADC_INT_RAW,
+ status, (status & SC27XX_ADC_IRQ_RAW),
+ SC27XX_ADC_POLL_RAW_STATUS,
+ SC27XX_ADC_RDY_TIMEOUT);
+ if (ret) {
+ dev_err(data->dev, "read adc timeout, status = 0x%x\n", status);
+ goto disable_adc;
+ }
+
+ ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, &value);
+ if (ret)
+ goto disable_adc;
+
+ value &= SC27XX_ADC_DATA_MASK;
+
+disable_adc:
+ regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
+ SC27XX_ADC_EN, 0);
+regulator_restore:
+ if ((data->var_data->set_volref) && (channel == 30 || channel == 31)) {
+ ret_volref = regulator_set_voltage(data->volref,
+ SC27XX_ADC_REFVOL_VDD28,
+ SC27XX_ADC_REFVOL_VDD28);
+ if (ret_volref) {
+ dev_err(data->dev, "failed to set the volref 2.8v,ret_volref = 0x%x\n",
+ ret_volref);
+ ret = ret || ret_volref;
+ }
+ }
+unlock_adc:
+ hwspin_unlock_raw(data->hwlock);
+
+ if (!ret)
+ *val = value;
+
+ return ret;
+}
+
+static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, int channel, int scale,
+ struct u32_fract *fract)
+{
+ u32 ratio;
+
+ ratio = data->var_data->get_ratio(channel, scale);
+ fract->numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET;
+ fract->denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
+}
+
+static int adc_to_volt(struct sc27xx_adc_linear_graph *graph,
+ int raw_adc)
+{
+ int tmp;
+
+ tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1);
+ tmp /= (graph->adc0 - graph->adc1);
+ tmp += graph->volt1;
+
+ return tmp;
+}
+
+static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph,
+ int raw_adc)
+{
+ int tmp;
+
+ tmp = adc_to_volt(graph, raw_adc);
+
+ return tmp < 0 ? 0 : tmp;
+}
+
+static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel,
+ int scale, int raw_adc)
+{
+ struct u32_fract fract;
+ u32 volt;
+
+ /*
+ * Convert ADC values to voltage values according to the linear graph,
+ * and channel 5 and channel 1 has been calibrated, so we can just
+ * return the voltage values calculated by the linear graph. But other
+ * channels need be calculated to the real voltage values with the
+ * voltage ratio.
+ */
+ switch (channel) {
+ case 5:
+ return sc27xx_adc_to_volt(&big_scale_graph, raw_adc);
+
+ case 1:
+ return sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
+
+ default:
+ volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc);
+ break;
+ }
+
+ sc27xx_adc_volt_ratio(data, channel, scale, &fract);
+
+ return DIV_ROUND_CLOSEST(volt * fract.denominator, fract.numerator);
+}
+
+static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data,
+ int channel, int scale, int *val)
+{
+ int ret, raw_adc;
+
+ ret = sc27xx_adc_read(data, channel, scale, &raw_adc);
+ if (ret)
+ return ret;
+
+ *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc);
+ return 0;
+}
+
+static int sc27xx_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct sc27xx_adc_data *data = iio_priv(indio_dev);
+ int scale = data->channel_scale[chan->channel];
+ int ret, tmp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = sc27xx_adc_read(data, chan->channel, scale, &tmp);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ *val = tmp;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&indio_dev->mlock);
+ ret = sc27xx_adc_read_processed(data, chan->channel, scale,
+ &tmp);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ *val = tmp;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = scale;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sc27xx_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct sc27xx_adc_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ data->channel_scale[chan->channel] = val;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info sc27xx_info = {
+ .read_raw = &sc27xx_adc_read_raw,
+ .write_raw = &sc27xx_adc_write_raw,
+};
+
+#define SC27XX_ADC_CHANNEL(index, mask) { \
+ .type = IIO_VOLTAGE, \
+ .channel = index, \
+ .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "CH##index", \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec sc27xx_channels[] = {
+ SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)),
+ SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)),
+};
+
+static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
+{
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, data->var_data->module_en,
+ SC27XX_MODULE_ADC_EN, SC27XX_MODULE_ADC_EN);
+ if (ret)
+ return ret;
+
+ /* Enable ADC work clock and controller clock */
+ ret = regmap_update_bits(data->regmap, data->var_data->clk_en,
+ SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN,
+ SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN);
+ if (ret)
+ goto disable_adc;
+
+ /* ADC channel scales' calibration from nvmem device */
+ ret = sc27xx_adc_scale_calibration(data, true);
+ if (ret)
+ goto disable_clk;
+
+ ret = sc27xx_adc_scale_calibration(data, false);
+ if (ret)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ regmap_update_bits(data->regmap, data->var_data->clk_en,
+ SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
+disable_adc:
+ regmap_update_bits(data->regmap, data->var_data->module_en,
+ SC27XX_MODULE_ADC_EN, 0);
+
+ return ret;
+}
+
+static void sc27xx_adc_disable(void *_data)
+{
+ struct sc27xx_adc_data *data = _data;
+
+ /* Disable ADC work clock and controller clock */
+ regmap_update_bits(data->regmap, data->var_data->clk_en,
+ SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
+
+ regmap_update_bits(data->regmap, data->var_data->module_en,
+ SC27XX_MODULE_ADC_EN, 0);
+}
+
+static const struct sc27xx_adc_variant_data sc2731_data = {
+ .module_en = SC2731_MODULE_EN,
+ .clk_en = SC2731_ARM_CLK_EN,
+ .scale_shift = SC27XX_ADC_SCALE_SHIFT,
+ .scale_mask = SC27XX_ADC_SCALE_MASK,
+ .bscale_cal = &sc2731_big_scale_graph_calib,
+ .sscale_cal = &sc2731_small_scale_graph_calib,
+ .init_scale = sc2731_adc_scale_init,
+ .get_ratio = sc2731_adc_get_ratio,
+ .set_volref = false,
+};
+
+static const struct sc27xx_adc_variant_data sc2730_data = {
+ .module_en = SC2730_MODULE_EN,
+ .clk_en = SC2730_ARM_CLK_EN,
+ .scale_shift = SC27XX_ADC_SCALE_SHIFT,
+ .scale_mask = SC27XX_ADC_SCALE_MASK,
+ .bscale_cal = &big_scale_graph_calib,
+ .sscale_cal = &small_scale_graph_calib,
+ .init_scale = sc2730_adc_scale_init,
+ .get_ratio = sc2730_adc_get_ratio,
+ .set_volref = false,
+};
+
+static const struct sc27xx_adc_variant_data sc2721_data = {
+ .module_en = SC2731_MODULE_EN,
+ .clk_en = SC2721_ARM_CLK_EN,
+ .scale_shift = SC2721_ADC_SCALE_SHIFT,
+ .scale_mask = SC2721_ADC_SCALE_MASK,
+ .bscale_cal = &sc2731_big_scale_graph_calib,
+ .sscale_cal = &sc2731_small_scale_graph_calib,
+ .init_scale = sc2731_adc_scale_init,
+ .get_ratio = sc2721_adc_get_ratio,
+ .set_volref = true,
+};
+
+static const struct sc27xx_adc_variant_data sc2720_data = {
+ .module_en = SC2731_MODULE_EN,
+ .clk_en = SC2721_ARM_CLK_EN,
+ .scale_shift = SC27XX_ADC_SCALE_SHIFT,
+ .scale_mask = SC27XX_ADC_SCALE_MASK,
+ .bscale_cal = &big_scale_graph_calib,
+ .sscale_cal = &small_scale_graph_calib,
+ .init_scale = sc2720_adc_scale_init,
+ .get_ratio = sc2720_adc_get_ratio,
+ .set_volref = false,
+};
+
+static int sc27xx_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct sc27xx_adc_data *sc27xx_data;
+ const struct sc27xx_adc_variant_data *pdata;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ pdata = of_device_get_match_data(dev);
+ if (!pdata) {
+ dev_err(dev, "No matching driver data found\n");
+ return -EINVAL;
+ }
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ sc27xx_data = iio_priv(indio_dev);
+
+ sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!sc27xx_data->regmap) {
+ dev_err(dev, "failed to get ADC regmap\n");
+ return -ENODEV;
+ }
+
+ ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
+ if (ret) {
+ dev_err(dev, "failed to get ADC base address\n");
+ return ret;
+ }
+
+ sc27xx_data->irq = platform_get_irq(pdev, 0);
+ if (sc27xx_data->irq < 0)
+ return sc27xx_data->irq;
+
+ ret = of_hwspin_lock_get_id(np, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to get hwspinlock id\n");
+ return ret;
+ }
+
+ sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
+ if (!sc27xx_data->hwlock) {
+ dev_err(dev, "failed to request hwspinlock\n");
+ return -ENXIO;
+ }
+
+ sc27xx_data->dev = dev;
+ if (pdata->set_volref) {
+ sc27xx_data->volref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(sc27xx_data->volref)) {
+ ret = PTR_ERR(sc27xx_data->volref);
+ return dev_err_probe(dev, ret, "failed to get ADC volref\n");
+ }
+ }
+
+ sc27xx_data->var_data = pdata;
+ sc27xx_data->var_data->init_scale(sc27xx_data);
+
+ ret = sc27xx_adc_enable(sc27xx_data);
+ if (ret) {
+ dev_err(dev, "failed to enable ADC module\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
+ if (ret) {
+ dev_err(dev, "failed to add ADC disable action\n");
+ return ret;
+ }
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &sc27xx_info;
+ indio_dev->channels = sc27xx_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ dev_err(dev, "could not register iio (ADC)");
+
+ return ret;
+}
+
+static const struct of_device_id sc27xx_adc_of_match[] = {
+ { .compatible = "sprd,sc2731-adc", .data = &sc2731_data},
+ { .compatible = "sprd,sc2730-adc", .data = &sc2730_data},
+ { .compatible = "sprd,sc2721-adc", .data = &sc2721_data},
+ { .compatible = "sprd,sc2720-adc", .data = &sc2720_data},
+ { }
+};
+MODULE_DEVICE_TABLE(of, sc27xx_adc_of_match);
+
+static struct platform_driver sc27xx_adc_driver = {
+ .probe = sc27xx_adc_probe,
+ .driver = {
+ .name = "sc27xx-adc",
+ .of_match_table = sc27xx_adc_of_match,
+ },
+};
+
+module_platform_driver(sc27xx_adc_driver);
+
+MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>");
+MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c
new file mode 100644
index 000000000..327cc2097
--- /dev/null
+++ b/drivers/iio/adc/sd_adc_modulator.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic sigma delta modulator driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+static const struct iio_info iio_sd_mod_iio_info;
+
+static const struct iio_chan_spec iio_sd_mod_ch = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 1,
+ .shift = 0,
+ },
+};
+
+static int iio_sd_mod_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *iio;
+
+ iio = devm_iio_device_alloc(dev, 0);
+ if (!iio)
+ return -ENOMEM;
+
+ iio->name = dev_name(dev);
+ iio->info = &iio_sd_mod_iio_info;
+ iio->modes = INDIO_BUFFER_HARDWARE;
+
+ iio->num_channels = 1;
+ iio->channels = &iio_sd_mod_ch;
+
+ platform_set_drvdata(pdev, iio);
+
+ return devm_iio_device_register(&pdev->dev, iio);
+}
+
+static const struct of_device_id sd_adc_of_match[] = {
+ { .compatible = "sd-modulator" },
+ { .compatible = "ads1201" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sd_adc_of_match);
+
+static struct platform_driver iio_sd_mod_adc = {
+ .driver = {
+ .name = "iio_sd_adc_mod",
+ .of_match_table = sd_adc_of_match,
+ },
+ .probe = iio_sd_mod_probe,
+};
+
+module_platform_driver(iio_sd_mod_adc);
+
+MODULE_DESCRIPTION("Basic sigma delta modulator");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
new file mode 100644
index 000000000..d93e580b3
--- /dev/null
+++ b/drivers/iio/adc/spear_adc.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ST SPEAr ADC driver
+ *
+ * Copyright 2012 Stefan Roese <sr@denx.de>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* SPEAR registers definitions */
+#define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF)
+#define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF)
+#define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0)
+#define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4)
+
+/* Bit definitions for SPEAR_ADC_STATUS */
+#define SPEAR_ADC_STATUS_START_CONVERSION BIT(0)
+#define SPEAR_ADC_STATUS_CHANNEL_NUM(x) ((x) << 1)
+#define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4)
+#define SPEAR_ADC_STATUS_AVG_SAMPLE(x) ((x) << 5)
+#define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9)
+
+#define SPEAR_ADC_DATA_MASK 0x03ff
+#define SPEAR_ADC_DATA_BITS 10
+
+#define SPEAR_ADC_MOD_NAME "spear-adc"
+
+#define SPEAR_ADC_CHANNEL_NUM 8
+
+#define SPEAR_ADC_CLK_MIN 2500000
+#define SPEAR_ADC_CLK_MAX 20000000
+
+struct adc_regs_spear3xx {
+ u32 status;
+ u32 average;
+ u32 scan_rate;
+ u32 clk; /* Not avail for 1340 & 1310 */
+ u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+ u32 ch_data[SPEAR_ADC_CHANNEL_NUM];
+};
+
+struct chan_data {
+ u32 lsb;
+ u32 msb;
+};
+
+struct adc_regs_spear6xx {
+ u32 status;
+ u32 pad[2];
+ u32 clk;
+ u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+ struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM];
+ u32 scan_rate_lo;
+ u32 scan_rate_hi;
+ struct chan_data average;
+};
+
+struct spear_adc_state {
+ struct device_node *np;
+ struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
+ struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
+ struct clk *clk;
+ struct completion completion;
+ /*
+ * Lock to protect the device state during a potential concurrent
+ * read access from userspace. Reading a raw value requires a sequence
+ * of register writes, then a wait for a completion callback,
+ * and finally a register read, during which userspace could issue
+ * another read request. This lock protects a read access from
+ * ocurring before another one has finished.
+ */
+ struct mutex lock;
+ u32 current_clk;
+ u32 sampling_freq;
+ u32 avg_samples;
+ u32 vref_external;
+ u32 value;
+};
+
+/*
+ * Functions to access some SPEAr ADC register. Abstracted into
+ * static inline functions, because of different register offsets
+ * on different SoC variants (SPEAr300 vs SPEAr600 etc).
+ */
+static void spear_adc_set_status(struct spear_adc_state *st, u32 val)
+{
+ __raw_writel(val, &st->adc_base_spear6xx->status);
+}
+
+static void spear_adc_set_clk(struct spear_adc_state *st, u32 val)
+{
+ u32 clk_high, clk_low, count;
+ u32 apb_clk = clk_get_rate(st->clk);
+
+ count = DIV_ROUND_UP(apb_clk, val);
+ clk_low = count / 2;
+ clk_high = count - clk_low;
+ st->current_clk = apb_clk / count;
+
+ __raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high),
+ &st->adc_base_spear6xx->clk);
+}
+
+static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
+ u32 val)
+{
+ __raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]);
+}
+
+static u32 spear_adc_get_average(struct spear_adc_state *st)
+{
+ if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ return __raw_readl(&st->adc_base_spear6xx->average.msb) &
+ SPEAR_ADC_DATA_MASK;
+ } else {
+ return __raw_readl(&st->adc_base_spear3xx->average) &
+ SPEAR_ADC_DATA_MASK;
+ }
+}
+
+static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
+{
+ if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
+ &st->adc_base_spear6xx->scan_rate_lo);
+ __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
+ &st->adc_base_spear6xx->scan_rate_hi);
+ } else {
+ __raw_writel(rate, &st->adc_base_spear3xx->scan_rate);
+ }
+}
+
+static int spear_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct spear_adc_state *st = iio_priv(indio_dev);
+ u32 status;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+
+ status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) |
+ SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) |
+ SPEAR_ADC_STATUS_START_CONVERSION |
+ SPEAR_ADC_STATUS_ADC_ENABLE;
+ if (st->vref_external == 0)
+ status |= SPEAR_ADC_STATUS_VREF_INTERNAL;
+
+ spear_adc_set_status(st, status);
+ wait_for_completion(&st->completion); /* set by ISR */
+ *val = st->value;
+
+ mutex_unlock(&st->lock);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_external;
+ *val2 = SPEAR_ADC_DATA_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->current_clk;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int spear_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct spear_adc_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+
+ if ((val < SPEAR_ADC_CLK_MIN) ||
+ (val > SPEAR_ADC_CLK_MAX) ||
+ (val2 != 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spear_adc_set_clk(st, val);
+
+out:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+#define SPEAR_ADC_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .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),\
+ .channel = idx, \
+}
+
+static const struct iio_chan_spec spear_adc_iio_channels[] = {
+ SPEAR_ADC_CHAN(0),
+ SPEAR_ADC_CHAN(1),
+ SPEAR_ADC_CHAN(2),
+ SPEAR_ADC_CHAN(3),
+ SPEAR_ADC_CHAN(4),
+ SPEAR_ADC_CHAN(5),
+ SPEAR_ADC_CHAN(6),
+ SPEAR_ADC_CHAN(7),
+};
+
+static irqreturn_t spear_adc_isr(int irq, void *dev_id)
+{
+ struct spear_adc_state *st = dev_id;
+
+ /* Read value to clear IRQ */
+ st->value = spear_adc_get_average(st);
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int spear_adc_configure(struct spear_adc_state *st)
+{
+ int i;
+
+ /* Reset ADC core */
+ spear_adc_set_status(st, 0);
+ __raw_writel(0, &st->adc_base_spear6xx->clk);
+ for (i = 0; i < 8; i++)
+ spear_adc_set_ctrl(st, i, 0);
+ spear_adc_set_scanrate(st, 0);
+
+ spear_adc_set_clk(st, st->sampling_freq);
+
+ return 0;
+}
+
+static const struct iio_info spear_adc_info = {
+ .read_raw = &spear_adc_read_raw,
+ .write_raw = &spear_adc_write_raw,
+};
+
+static int spear_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct spear_adc_state *st;
+ struct iio_dev *indio_dev = NULL;
+ int ret = -ENODEV;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state));
+ if (!indio_dev) {
+ dev_err(dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+
+ mutex_init(&st->lock);
+
+ st->np = np;
+
+ /*
+ * SPEAr600 has a different register layout than other SPEAr SoC's
+ * (e.g. SPEAr3xx). Let's provide two register base addresses
+ * to support multi-arch kernels.
+ */
+ st->adc_base_spear6xx = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(st->adc_base_spear6xx))
+ return PTR_ERR(st->adc_base_spear6xx);
+
+ st->adc_base_spear3xx =
+ (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx;
+
+ st->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(st->clk)) {
+ dev_err(dev, "failed getting clock\n");
+ return PTR_ERR(st->clk);
+ }
+
+ ret = clk_prepare_enable(st->clk);
+ if (ret) {
+ dev_err(dev, "failed enabling clock\n");
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto errout2;
+ }
+
+ ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME,
+ st);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto errout2;
+ }
+
+ if (of_property_read_u32(np, "sampling-frequency",
+ &st->sampling_freq)) {
+ dev_err(dev, "sampling-frequency missing in DT\n");
+ ret = -EINVAL;
+ goto errout2;
+ }
+
+ /*
+ * Optional avg_samples defaults to 0, resulting in single data
+ * conversion
+ */
+ of_property_read_u32(np, "average-samples", &st->avg_samples);
+
+ /*
+ * Optional vref_external defaults to 0, resulting in internal vref
+ * selection
+ */
+ of_property_read_u32(np, "vref-external", &st->vref_external);
+
+ spear_adc_configure(st);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&st->completion);
+
+ indio_dev->name = SPEAR_ADC_MOD_NAME;
+ indio_dev->info = &spear_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = spear_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto errout2;
+
+ dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
+
+ return 0;
+
+errout2:
+ clk_disable_unprepare(st->clk);
+ return ret;
+}
+
+static int spear_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct spear_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(st->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_adc_dt_ids[] = {
+ { .compatible = "st,spear600-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
+#endif
+
+static struct platform_driver spear_adc_driver = {
+ .probe = spear_adc_probe,
+ .remove = spear_adc_remove,
+ .driver = {
+ .name = SPEAR_ADC_MOD_NAME,
+ .of_match_table = of_match_ptr(spear_adc_dt_ids),
+ },
+};
+
+module_platform_driver(spear_adc_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr@denx.de>");
+MODULE_DESCRIPTION("SPEAr ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
new file mode 100644
index 000000000..dee47b899
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -0,0 +1,905 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * Inspired from: fsl-imx25-tsadc
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include "stm32-adc-core.h"
+
+#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
+
+/* SYSCFG registers */
+#define STM32MP1_SYSCFG_PMCSETR 0x04
+#define STM32MP1_SYSCFG_PMCCLRR 0x44
+
+/* SYSCFG bit fields */
+#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9)
+
+/* SYSCFG capability flags */
+#define HAS_VBOOSTER BIT(0)
+#define HAS_ANASWVDD BIT(1)
+
+/**
+ * struct stm32_adc_common_regs - stm32 common registers
+ * @csr: common status register offset
+ * @ccr: common control register offset
+ * @eoc_msk: array of eoc (end of conversion flag) masks in csr for adc1..n
+ * @ovr_msk: array of ovr (overrun flag) masks in csr for adc1..n
+ * @ier: interrupt enable register offset for each adc
+ * @eocie_msk: end of conversion interrupt enable mask in @ier
+ */
+struct stm32_adc_common_regs {
+ u32 csr;
+ u32 ccr;
+ u32 eoc_msk[STM32_ADC_MAX_ADCS];
+ u32 ovr_msk[STM32_ADC_MAX_ADCS];
+ u32 ier;
+ u32 eocie_msk;
+};
+
+struct stm32_adc_priv;
+
+/**
+ * struct stm32_adc_priv_cfg - stm32 core compatible configuration data
+ * @regs: common registers for all instances
+ * @clk_sel: clock selection routine
+ * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @ipid: adc identification number
+ * @has_syscfg: SYSCFG capability flags
+ * @num_irqs: number of interrupt lines
+ * @num_adcs: maximum number of ADC instances in the common registers
+ */
+struct stm32_adc_priv_cfg {
+ const struct stm32_adc_common_regs *regs;
+ int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
+ u32 max_clk_rate_hz;
+ u32 ipid;
+ unsigned int has_syscfg;
+ unsigned int num_irqs;
+ unsigned int num_adcs;
+};
+
+/**
+ * struct stm32_adc_priv - stm32 ADC core private data
+ * @irq: irq(s) for ADC block
+ * @nb_adc_max: actual maximum number of instance per ADC block
+ * @domain: irq domain reference
+ * @aclk: clock reference for the analog circuitry
+ * @bclk: bus clock common for all ADCs, depends on part used
+ * @max_clk_rate: desired maximum clock rate
+ * @booster: booster supply reference
+ * @vdd: vdd supply reference
+ * @vdda: vdda analog supply reference
+ * @vref: regulator reference
+ * @vdd_uv: vdd supply voltage (microvolts)
+ * @vdda_uv: vdda supply voltage (microvolts)
+ * @cfg: compatible configuration data
+ * @common: common data for all ADC instances
+ * @ccr_bak: backup CCR in low power mode
+ * @syscfg: reference to syscon, system control registers
+ */
+struct stm32_adc_priv {
+ int irq[STM32_ADC_MAX_ADCS];
+ unsigned int nb_adc_max;
+ struct irq_domain *domain;
+ struct clk *aclk;
+ struct clk *bclk;
+ u32 max_clk_rate;
+ struct regulator *booster;
+ struct regulator *vdd;
+ struct regulator *vdda;
+ struct regulator *vref;
+ int vdd_uv;
+ int vdda_uv;
+ const struct stm32_adc_priv_cfg *cfg;
+ struct stm32_adc_common common;
+ u32 ccr_bak;
+ struct regmap *syscfg;
+};
+
+static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
+{
+ return container_of(com, struct stm32_adc_priv, common);
+}
+
+/* STM32F4 ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler
+ * @pdev: platform device
+ * @priv: stm32 ADC core private data
+ * Select clock prescaler used for analog conversions, before using ADC.
+ */
+static int stm32f4_adc_clk_sel(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ unsigned long rate;
+ u32 val;
+ int i;
+
+ /* stm32f4 has one clk input for analog (mandatory), enforce it here */
+ if (!priv->aclk) {
+ dev_err(&pdev->dev, "No 'adc' clock found\n");
+ return -ENOENT;
+ }
+
+ rate = clk_get_rate(priv->aclk);
+ if (!rate) {
+ dev_err(&pdev->dev, "Invalid clock rate: 0\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+ if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate)
+ break;
+ }
+ if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
+ dev_err(&pdev->dev, "adc clk selection failed\n");
+ return -EINVAL;
+ }
+
+ priv->common.rate = rate / stm32f4_pclk_div[i];
+ val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
+ val &= ~STM32F4_ADC_ADCPRE_MASK;
+ val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+ writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR);
+
+ dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n",
+ priv->common.rate / 1000);
+
+ return 0;
+}
+
+/**
+ * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
+ * @ckmode: ADC clock mode, Async or sync with prescaler.
+ * @presc: prescaler bitfield for async clock mode
+ * @div: prescaler division ratio
+ */
+struct stm32h7_adc_ck_spec {
+ u32 ckmode;
+ u32 presc;
+ int div;
+};
+
+static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
+ /* 00: CK_ADC[1..3]: Asynchronous clock modes */
+ { 0, 0, 1 },
+ { 0, 1, 2 },
+ { 0, 2, 4 },
+ { 0, 3, 6 },
+ { 0, 4, 8 },
+ { 0, 5, 10 },
+ { 0, 6, 12 },
+ { 0, 7, 16 },
+ { 0, 8, 32 },
+ { 0, 9, 64 },
+ { 0, 10, 128 },
+ { 0, 11, 256 },
+ /* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
+ { 1, 0, 1 },
+ { 2, 0, 2 },
+ { 3, 0, 4 },
+};
+
+static int stm32h7_adc_clk_sel(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ u32 ckmode, presc, val;
+ unsigned long rate;
+ int i, div, duty;
+
+ /* stm32h7 bus clock is common for all ADC instances (mandatory) */
+ if (!priv->bclk) {
+ dev_err(&pdev->dev, "No 'bus' clock found\n");
+ return -ENOENT;
+ }
+
+ /*
+ * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
+ * So, choice is to have bus clock mandatory and adc clock optional.
+ * If optional 'adc' clock has been found, then try to use it first.
+ */
+ if (priv->aclk) {
+ /*
+ * Asynchronous clock modes (e.g. ckmode == 0)
+ * From spec: PLL output musn't exceed max rate
+ */
+ rate = clk_get_rate(priv->aclk);
+ if (!rate) {
+ dev_err(&pdev->dev, "Invalid adc clock rate: 0\n");
+ return -EINVAL;
+ }
+
+ /* If duty is an error, kindly use at least /2 divider */
+ duty = clk_get_scaled_duty_cycle(priv->aclk, 100);
+ if (duty < 0)
+ dev_warn(&pdev->dev, "adc clock duty: %d\n", duty);
+
+ for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
+ ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
+ presc = stm32h7_adc_ckmodes_spec[i].presc;
+ div = stm32h7_adc_ckmodes_spec[i].div;
+
+ if (ckmode)
+ continue;
+
+ /*
+ * For proper operation, clock duty cycle range is 49%
+ * to 51%. Apply at least /2 prescaler otherwise.
+ */
+ if (div == 1 && (duty < 49 || duty > 51))
+ continue;
+
+ if ((rate / div) <= priv->max_clk_rate)
+ goto out;
+ }
+ }
+
+ /* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
+ rate = clk_get_rate(priv->bclk);
+ if (!rate) {
+ dev_err(&pdev->dev, "Invalid bus clock rate: 0\n");
+ return -EINVAL;
+ }
+
+ duty = clk_get_scaled_duty_cycle(priv->bclk, 100);
+ if (duty < 0)
+ dev_warn(&pdev->dev, "bus clock duty: %d\n", duty);
+
+ for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
+ ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
+ presc = stm32h7_adc_ckmodes_spec[i].presc;
+ div = stm32h7_adc_ckmodes_spec[i].div;
+
+ if (!ckmode)
+ continue;
+
+ if (div == 1 && (duty < 49 || duty > 51))
+ continue;
+
+ if ((rate / div) <= priv->max_clk_rate)
+ goto out;
+ }
+
+ dev_err(&pdev->dev, "adc clk selection failed\n");
+ return -EINVAL;
+
+out:
+ /* rate used later by each ADC instance to control BOOST mode */
+ priv->common.rate = rate / div;
+
+ /* Set common clock mode and prescaler */
+ val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR);
+ val &= ~(STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK);
+ val |= ckmode << STM32H7_CKMODE_SHIFT;
+ val |= presc << STM32H7_PRESC_SHIFT;
+ writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR);
+
+ dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n",
+ ckmode ? "bus" : "adc", div, priv->common.rate / 1000);
+
+ return 0;
+}
+
+/* STM32F4 common registers definitions */
+static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
+ .csr = STM32F4_ADC_CSR,
+ .ccr = STM32F4_ADC_CCR,
+ .eoc_msk = { STM32F4_EOC1, STM32F4_EOC2, STM32F4_EOC3},
+ .ovr_msk = { STM32F4_OVR1, STM32F4_OVR2, STM32F4_OVR3},
+ .ier = STM32F4_ADC_CR1,
+ .eocie_msk = STM32F4_EOCIE,
+};
+
+/* STM32H7 common registers definitions */
+static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
+ .csr = STM32H7_ADC_CSR,
+ .ccr = STM32H7_ADC_CCR,
+ .eoc_msk = { STM32H7_EOC_MST, STM32H7_EOC_SLV},
+ .ovr_msk = { STM32H7_OVR_MST, STM32H7_OVR_SLV},
+ .ier = STM32H7_ADC_IER,
+ .eocie_msk = STM32H7_EOCIE,
+};
+
+static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
+ 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
+};
+
+static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv,
+ unsigned int adc)
+{
+ u32 ier, offset = stm32_adc_offset[adc];
+
+ ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier);
+
+ return ier & priv->cfg->regs->eocie_msk;
+}
+
+/* ADC common interrupt for all instances */
+static void stm32_adc_irq_handler(struct irq_desc *desc)
+{
+ struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ int i;
+ u32 status;
+
+ chained_irq_enter(chip, desc);
+ status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
+
+ /*
+ * End of conversion may be handled by using IRQ or DMA. There may be a
+ * race here when two conversions complete at the same time on several
+ * ADCs. EOC may be read 'set' for several ADCs, with:
+ * - an ADC configured to use DMA (EOC triggers the DMA request, and
+ * is then automatically cleared by DR read in hardware)
+ * - an ADC configured to use IRQs (EOCIE bit is set. The handler must
+ * be called in this case)
+ * So both EOC status bit in CSR and EOCIE control bit must be checked
+ * before invoking the interrupt handler (e.g. call ISR only for
+ * IRQ-enabled ADCs).
+ */
+ for (i = 0; i < priv->nb_adc_max; i++) {
+ if ((status & priv->cfg->regs->eoc_msk[i] &&
+ stm32_adc_eoc_enabled(priv, i)) ||
+ (status & priv->cfg->regs->ovr_msk[i]))
+ generic_handle_domain_irq(priv->domain, i);
+ }
+
+ chained_irq_exit(chip, desc);
+};
+
+static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops stm32_adc_domain_ops = {
+ .map = stm32_adc_domain_map,
+ .unmap = stm32_adc_domain_unmap,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static int stm32_adc_irq_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ unsigned int i;
+
+ /*
+ * Interrupt(s) must be provided, depending on the compatible:
+ * - stm32f4/h7 shares a common interrupt line.
+ * - stm32mp1, has one line per ADC
+ */
+ for (i = 0; i < priv->cfg->num_irqs; i++) {
+ priv->irq[i] = platform_get_irq(pdev, i);
+ if (priv->irq[i] < 0)
+ return priv->irq[i];
+ }
+
+ priv->domain = irq_domain_add_simple(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);
+ }
+
+ return 0;
+}
+
+static void stm32_adc_irq_remove(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ int hwirq;
+ unsigned int i;
+
+ for (hwirq = 0; hwirq < priv->nb_adc_max; hwirq++)
+ irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
+ irq_domain_remove(priv->domain);
+
+ for (i = 0; i < priv->cfg->num_irqs; i++)
+ irq_set_chained_handler(priv->irq[i], NULL);
+}
+
+static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
+ struct device *dev)
+{
+ int ret;
+
+ /*
+ * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+ * switches (via PCSEL) which have reduced performances when their
+ * supply is below 2.7V (vdda by default):
+ * - Voltage booster can be used, to get full ADC performances
+ * (increases power consumption).
+ * - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
+ *
+ * Recommended settings for ANASWVDD and EN_BOOSTER:
+ * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+ * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+ * - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default)
+ */
+ if (priv->vdda_uv < 2700000) {
+ if (priv->syscfg && priv->vdd_uv > 2700000) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(priv->syscfg,
+ STM32MP1_SYSCFG_PMCSETR,
+ STM32MP1_SYSCFG_ANASWVDD_MASK);
+ if (ret < 0) {
+ regulator_disable(priv->vdd);
+ dev_err(dev, "vdd select failed, %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "analog switches supplied by vdd\n");
+
+ return 0;
+ }
+
+ if (priv->booster) {
+ /*
+ * This is optional, as this is a trade-off between
+ * analog performance and power consumption.
+ */
+ ret = regulator_enable(priv->booster);
+ if (ret < 0) {
+ dev_err(dev, "booster enable failed %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "analog switches supplied by booster\n");
+
+ return 0;
+ }
+ }
+
+ /* Fallback using vdda (default), nothing to do */
+ dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
+ priv->vdda_uv);
+
+ return 0;
+}
+
+static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
+{
+ if (priv->vdda_uv < 2700000) {
+ if (priv->syscfg && priv->vdd_uv > 2700000) {
+ regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
+ STM32MP1_SYSCFG_ANASWVDD_MASK);
+ regulator_disable(priv->vdd);
+ return;
+ }
+ if (priv->booster)
+ regulator_disable(priv->booster);
+ }
+}
+
+static int stm32_adc_core_hw_start(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ int ret;
+
+ ret = regulator_enable(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda get voltage failed, %d\n", ret);
+ goto err_vdda_disable;
+ }
+ priv->vdda_uv = ret;
+
+ ret = stm32_adc_core_switches_supply_en(priv, dev);
+ if (ret < 0)
+ goto err_vdda_disable;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed\n");
+ goto err_switches_dis;
+ }
+
+ ret = clk_prepare_enable(priv->bclk);
+ if (ret < 0) {
+ dev_err(dev, "bus clk enable failed\n");
+ goto err_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(dev, "adc clk enable failed\n");
+ goto err_bclk_disable;
+ }
+
+ writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
+
+ return 0;
+
+err_bclk_disable:
+ clk_disable_unprepare(priv->bclk);
+err_regulator_disable:
+ regulator_disable(priv->vref);
+err_switches_dis:
+ stm32_adc_core_switches_supply_dis(priv);
+err_vdda_disable:
+ regulator_disable(priv->vdda);
+
+ return ret;
+}
+
+static void stm32_adc_core_hw_stop(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ /* Backup CCR that may be lost (depends on power state to achieve) */
+ priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
+ clk_disable_unprepare(priv->aclk);
+ clk_disable_unprepare(priv->bclk);
+ regulator_disable(priv->vref);
+ stm32_adc_core_switches_supply_dis(priv);
+ regulator_disable(priv->vdda);
+}
+
+static int stm32_adc_core_switches_probe(struct device *dev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ /* Analog switches supply can be controlled by syscfg (optional) */
+ priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(priv->syscfg)) {
+ ret = PTR_ERR(priv->syscfg);
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret, "Can't probe syscfg\n");
+
+ priv->syscfg = NULL;
+ }
+
+ /* Booster can be used to supply analog switches (optional) */
+ if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
+ of_property_read_bool(np, "booster-supply")) {
+ priv->booster = devm_regulator_get_optional(dev, "booster");
+ if (IS_ERR(priv->booster)) {
+ ret = PTR_ERR(priv->booster);
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret, "can't get booster\n");
+
+ priv->booster = NULL;
+ }
+ }
+
+ /* Vdd can be used to supply analog switches (optional) */
+ if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
+ of_property_read_bool(np, "vdd-supply")) {
+ priv->vdd = devm_regulator_get_optional(dev, "vdd");
+ if (IS_ERR(priv->vdd)) {
+ ret = PTR_ERR(priv->vdd);
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret, "can't get vdd\n");
+
+ priv->vdd = NULL;
+ }
+ }
+
+ if (priv->vdd) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd get voltage failed %d\n", ret);
+ regulator_disable(priv->vdd);
+ return ret;
+ }
+ priv->vdd_uv = ret;
+
+ regulator_disable(priv->vdd);
+ }
+
+ return 0;
+}
+
+static int stm32_adc_probe_identification(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ const char *compat;
+ int ret, count = 0;
+ u32 id, val;
+
+ if (!priv->cfg->ipid)
+ return 0;
+
+ id = FIELD_GET(STM32MP1_IPIDR_MASK,
+ readl_relaxed(priv->common.base + STM32MP1_ADC_IPDR));
+ if (id != priv->cfg->ipid) {
+ dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(np, child) {
+ ret = of_property_read_string(child, "compatible", &compat);
+ if (ret)
+ continue;
+ /* Count child nodes with stm32 adc compatible */
+ if (strstr(compat, "st,stm32") && strstr(compat, "adc"))
+ count++;
+ }
+
+ val = readl_relaxed(priv->common.base + STM32MP1_ADC_HWCFGR0);
+ priv->nb_adc_max = FIELD_GET(STM32MP1_ADCNUM_MASK, val);
+ if (count > priv->nb_adc_max) {
+ dev_err(&pdev->dev, "Unexpected child number: %d", count);
+ return -EINVAL;
+ }
+
+ val = readl_relaxed(priv->common.base + STM32MP1_ADC_VERR);
+ dev_dbg(&pdev->dev, "ADC version: %lu.%lu\n",
+ FIELD_GET(STM32MP1_MAJREV_MASK, val),
+ FIELD_GET(STM32MP1_MINREV_MASK, val));
+
+ return 0;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct stm32_adc_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id;
+
+ struct resource *res;
+ u32 max_rate;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, &priv->common);
+
+ of_id = of_match_device(dev->driver->of_match_table, dev);
+ if (!of_id)
+ return -ENODEV;
+
+ priv->cfg = (const struct stm32_adc_priv_cfg *)of_id->data;
+ priv->nb_adc_max = priv->cfg->num_adcs;
+ spin_lock_init(&priv->common.lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->common.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->common.base))
+ return PTR_ERR(priv->common.base);
+ priv->common.phys_base = res->start;
+
+ priv->vdda = devm_regulator_get(&pdev->dev, "vdda");
+ if (IS_ERR(priv->vdda))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->vdda),
+ "vdda get failed\n");
+
+ priv->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(priv->vref))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->vref),
+ "vref get failed\n");
+
+ priv->aclk = devm_clk_get_optional(&pdev->dev, "adc");
+ if (IS_ERR(priv->aclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->aclk),
+ "Can't get 'adc' clock\n");
+
+ priv->bclk = devm_clk_get_optional(&pdev->dev, "bus");
+ if (IS_ERR(priv->bclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->bclk),
+ "Can't get 'bus' clock\n");
+
+ ret = stm32_adc_core_switches_probe(dev, priv);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_core_hw_start(dev);
+ if (ret)
+ goto err_pm_stop;
+
+ ret = stm32_adc_probe_identification(pdev, priv);
+ if (ret < 0)
+ goto err_hw_stop;
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_hw_stop;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "st,max-clk-rate-hz",
+ &max_rate);
+ if (!ret)
+ priv->max_clk_rate = min(max_rate, priv->cfg->max_clk_rate_hz);
+ else
+ priv->max_clk_rate = priv->cfg->max_clk_rate_hz;
+
+ ret = priv->cfg->clk_sel(pdev, priv);
+ if (ret < 0)
+ goto err_hw_stop;
+
+ ret = stm32_adc_irq_probe(pdev, priv);
+ if (ret < 0)
+ goto err_hw_stop;
+
+ ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to populate DT children\n");
+ goto err_irq_remove;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_irq_remove:
+ stm32_adc_irq_remove(pdev, priv);
+err_hw_stop:
+ stm32_adc_core_hw_stop(dev);
+err_pm_stop:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = platform_get_drvdata(pdev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ stm32_adc_irq_remove(pdev, priv);
+ stm32_adc_core_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+static int stm32_adc_core_runtime_suspend(struct device *dev)
+{
+ stm32_adc_core_hw_stop(dev);
+
+ return 0;
+}
+
+static int stm32_adc_core_runtime_resume(struct device *dev)
+{
+ return stm32_adc_core_hw_start(dev);
+}
+
+static int stm32_adc_core_runtime_idle(struct device *dev)
+{
+ pm_runtime_mark_last_busy(dev);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(stm32_adc_core_pm_ops,
+ stm32_adc_core_runtime_suspend,
+ stm32_adc_core_runtime_resume,
+ stm32_adc_core_runtime_idle);
+
+static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
+ .regs = &stm32f4_adc_common_regs,
+ .clk_sel = stm32f4_adc_clk_sel,
+ .max_clk_rate_hz = 36000000,
+ .num_irqs = 1,
+ .num_adcs = 3,
+};
+
+static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
+ .regs = &stm32h7_adc_common_regs,
+ .clk_sel = stm32h7_adc_clk_sel,
+ .max_clk_rate_hz = 36000000,
+ .has_syscfg = HAS_VBOOSTER,
+ .num_irqs = 1,
+ .num_adcs = 2,
+};
+
+static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
+ .regs = &stm32h7_adc_common_regs,
+ .clk_sel = stm32h7_adc_clk_sel,
+ .max_clk_rate_hz = 36000000,
+ .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
+ .ipid = STM32MP15_IPIDR_NUMBER,
+ .num_irqs = 2,
+};
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ {
+ .compatible = "st,stm32f4-adc-core",
+ .data = (void *)&stm32f4_adc_priv_cfg
+ }, {
+ .compatible = "st,stm32h7-adc-core",
+ .data = (void *)&stm32h7_adc_priv_cfg
+ }, {
+ .compatible = "st,stm32mp1-adc-core",
+ .data = (void *)&stm32mp1_adc_priv_cfg
+ }, {
+ },
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc-core",
+ .of_match_table = stm32_adc_of_match,
+ .pm = pm_ptr(&stm32_adc_core_pm_ops),
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc-core");
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
new file mode 100644
index 000000000..2118ef638
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset | Register |
+ * --------------------------------------------------------
+ * | 0x000 | Master ADC1 |
+ * --------------------------------------------------------
+ * | 0x100 | Slave ADC2 |
+ * --------------------------------------------------------
+ * | 0x200 | Slave ADC3 |
+ * --------------------------------------------------------
+ * | 0x300 | Master & Slave common regs |
+ * --------------------------------------------------------
+ */
+/* Maximum ADC instances number per ADC block for all supported SoCs */
+#define STM32_ADC_MAX_ADCS 3
+#define STM32_ADC_OFFSET 0x100
+#define STM32_ADCX_COMN_OFFSET 0x300
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR 0x00
+#define STM32F4_ADC_CR1 0x04
+#define STM32F4_ADC_CR2 0x08
+#define STM32F4_ADC_SMPR1 0x0C
+#define STM32F4_ADC_SMPR2 0x10
+#define STM32F4_ADC_HTR 0x24
+#define STM32F4_ADC_LTR 0x28
+#define STM32F4_ADC_SQR1 0x2C
+#define STM32F4_ADC_SQR2 0x30
+#define STM32F4_ADC_SQR3 0x34
+#define STM32F4_ADC_JSQR 0x38
+#define STM32F4_ADC_JDR1 0x3C
+#define STM32F4_ADC_JDR2 0x40
+#define STM32F4_ADC_JDR3 0x44
+#define STM32F4_ADC_JDR4 0x48
+#define STM32F4_ADC_DR 0x4C
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_OVR BIT(5)
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_OVRIE BIT(26)
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_SHIFT 28
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT 24
+#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_DDS BIT(9)
+#define STM32F4_DMA BIT(8)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_OVR3 BIT(21)
+#define STM32F4_EOC3 BIT(17)
+#define STM32F4_OVR2 BIT(13)
+#define STM32F4_EOC2 BIT(9)
+#define STM32F4_OVR1 BIT(5)
+#define STM32F4_EOC1 BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/* STM32H7 - Registers for each ADC instance */
+#define STM32H7_ADC_ISR 0x00
+#define STM32H7_ADC_IER 0x04
+#define STM32H7_ADC_CR 0x08
+#define STM32H7_ADC_CFGR 0x0C
+#define STM32H7_ADC_SMPR1 0x14
+#define STM32H7_ADC_SMPR2 0x18
+#define STM32H7_ADC_PCSEL 0x1C
+#define STM32H7_ADC_SQR1 0x30
+#define STM32H7_ADC_SQR2 0x34
+#define STM32H7_ADC_SQR3 0x38
+#define STM32H7_ADC_SQR4 0x3C
+#define STM32H7_ADC_DR 0x40
+#define STM32H7_ADC_DIFSEL 0xC0
+#define STM32H7_ADC_CALFACT 0xC4
+#define STM32H7_ADC_CALFACT2 0xC8
+
+/* STM32MP1 - ADC2 instance option register */
+#define STM32MP1_ADC2_OR 0xD0
+
+/* STM32MP1 - Identification registers */
+#define STM32MP1_ADC_HWCFGR0 0x3F0
+#define STM32MP1_ADC_VERR 0x3F4
+#define STM32MP1_ADC_IPDR 0x3F8
+#define STM32MP1_ADC_SIDR 0x3FC
+
+/* STM32H7 - common registers for all ADC instances */
+#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32H7_ADC_ISR - bit fields */
+#define STM32MP1_VREGREADY BIT(12)
+#define STM32H7_OVR BIT(4)
+#define STM32H7_EOC BIT(2)
+#define STM32H7_ADRDY BIT(0)
+
+/* STM32H7_ADC_IER - bit fields */
+#define STM32H7_OVRIE STM32H7_OVR
+#define STM32H7_EOCIE STM32H7_EOC
+
+/* STM32H7_ADC_CR - bit fields */
+#define STM32H7_ADCAL BIT(31)
+#define STM32H7_ADCALDIF BIT(30)
+#define STM32H7_DEEPPWD BIT(29)
+#define STM32H7_ADVREGEN BIT(28)
+#define STM32H7_LINCALRDYW6 BIT(27)
+#define STM32H7_LINCALRDYW5 BIT(26)
+#define STM32H7_LINCALRDYW4 BIT(25)
+#define STM32H7_LINCALRDYW3 BIT(24)
+#define STM32H7_LINCALRDYW2 BIT(23)
+#define STM32H7_LINCALRDYW1 BIT(22)
+#define STM32H7_ADCALLIN BIT(16)
+#define STM32H7_BOOST BIT(8)
+#define STM32H7_ADSTP BIT(4)
+#define STM32H7_ADSTART BIT(2)
+#define STM32H7_ADDIS BIT(1)
+#define STM32H7_ADEN BIT(0)
+
+/* STM32H7_ADC_CFGR bit fields */
+#define STM32H7_EXTEN_SHIFT 10
+#define STM32H7_EXTEN_MASK GENMASK(11, 10)
+#define STM32H7_EXTSEL_SHIFT 5
+#define STM32H7_EXTSEL_MASK GENMASK(9, 5)
+#define STM32H7_RES_SHIFT 2
+#define STM32H7_RES_MASK GENMASK(4, 2)
+#define STM32H7_DMNGT_SHIFT 0
+#define STM32H7_DMNGT_MASK GENMASK(1, 0)
+
+enum stm32h7_adc_dmngt {
+ STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */
+ STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */
+ STM32H7_DMNGT_DFSDM, /* DFSDM mode */
+ STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */
+};
+
+/* STM32H7_ADC_CALFACT - bit fields */
+#define STM32H7_CALFACT_D_SHIFT 16
+#define STM32H7_CALFACT_D_MASK GENMASK(26, 16)
+#define STM32H7_CALFACT_S_SHIFT 0
+#define STM32H7_CALFACT_S_MASK GENMASK(10, 0)
+
+/* STM32H7_ADC_CALFACT2 - bit fields */
+#define STM32H7_LINCALFACT_SHIFT 0
+#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
+
+/* STM32H7_ADC_CSR - bit fields */
+#define STM32H7_OVR_SLV BIT(20)
+#define STM32H7_EOC_SLV BIT(18)
+#define STM32H7_OVR_MST BIT(4)
+#define STM32H7_EOC_MST BIT(2)
+
+/* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_VBATEN BIT(24)
+#define STM32H7_VREFEN BIT(22)
+#define STM32H7_PRESC_SHIFT 18
+#define STM32H7_PRESC_MASK GENMASK(21, 18)
+#define STM32H7_CKMODE_SHIFT 16
+#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+
+/* STM32MP1_ADC2_OR - bit fields */
+#define STM32MP1_VDDCOREEN BIT(0)
+
+/* STM32MP1_ADC_HWCFGR0 - bit fields */
+#define STM32MP1_ADCNUM_SHIFT 0
+#define STM32MP1_ADCNUM_MASK GENMASK(3, 0)
+#define STM32MP1_MULPIPE_SHIFT 4
+#define STM32MP1_MULPIPE_MASK GENMASK(7, 4)
+#define STM32MP1_OPBITS_SHIFT 8
+#define STM32MP1_OPBITS_MASK GENMASK(11, 8)
+#define STM32MP1_IDLEVALUE_SHIFT 12
+#define STM32MP1_IDLEVALUE_MASK GENMASK(15, 12)
+
+/* STM32MP1_ADC_VERR - bit fields */
+#define STM32MP1_MINREV_SHIFT 0
+#define STM32MP1_MINREV_MASK GENMASK(3, 0)
+#define STM32MP1_MAJREV_SHIFT 4
+#define STM32MP1_MAJREV_MASK GENMASK(7, 4)
+
+/* STM32MP1_ADC_IPDR - bit fields */
+#define STM32MP1_IPIDR_MASK GENMASK(31, 0)
+
+/* STM32MP1_ADC_SIDR - bit fields */
+#define STM32MP1_SIDR_MASK GENMASK(31, 0)
+
+#define STM32MP15_IPIDR_NUMBER 0x00110005
+
+/**
+ * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
+ * @base: control registers base cpu addr
+ * @phys_base: control registers base physical addr
+ * @rate: clock rate used for analog circuitry
+ * @vref_mv: vref voltage (mv)
+ * @lock: spinlock
+ */
+struct stm32_adc_common {
+ void __iomem *base;
+ phys_addr_t phys_base;
+ unsigned long rate;
+ int vref_mv;
+ spinlock_t lock; /* lock for common register */
+};
+
+#endif
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
new file mode 100644
index 000000000..a5d5b7b38
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc.c
@@ -0,0 +1,2485 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/timer/stm32-timer-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+
+#include "stm32-adc-core.h"
+
+/* Number of linear calibration shadow registers / LINCALRDYW control bits */
+#define STM32H7_LINCALFACT_NUM 6
+
+/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
+#define STM32H7_BOOST_CLKRATE 20000000UL
+
+#define STM32_ADC_CH_MAX 20 /* max number of channels */
+#define STM32_ADC_CH_SZ 16 /* max channel name size */
+#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
+#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+#define STM32_ADC_HW_STOP_DELAY_MS 100
+#define STM32_ADC_VREFINT_VOLTAGE 3300
+
+#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
+
+/* External trigger enable */
+enum stm32_adc_exten {
+ STM32_EXTEN_SWTRIG,
+ STM32_EXTEN_HWTRIG_RISING_EDGE,
+ STM32_EXTEN_HWTRIG_FALLING_EDGE,
+ STM32_EXTEN_HWTRIG_BOTH_EDGES,
+};
+
+/* extsel - trigger mux selection value */
+enum stm32_adc_extsel {
+ STM32_EXT0,
+ STM32_EXT1,
+ STM32_EXT2,
+ STM32_EXT3,
+ STM32_EXT4,
+ STM32_EXT5,
+ STM32_EXT6,
+ STM32_EXT7,
+ STM32_EXT8,
+ STM32_EXT9,
+ STM32_EXT10,
+ STM32_EXT11,
+ STM32_EXT12,
+ STM32_EXT13,
+ STM32_EXT14,
+ STM32_EXT15,
+ STM32_EXT16,
+ STM32_EXT17,
+ STM32_EXT18,
+ STM32_EXT19,
+ STM32_EXT20,
+};
+
+enum stm32_adc_int_ch {
+ STM32_ADC_INT_CH_NONE = -1,
+ STM32_ADC_INT_CH_VDDCORE,
+ STM32_ADC_INT_CH_VREFINT,
+ STM32_ADC_INT_CH_VBAT,
+ STM32_ADC_INT_CH_NB,
+};
+
+/**
+ * struct stm32_adc_ic - ADC internal channels
+ * @name: name of the internal channel
+ * @idx: internal channel enum index
+ */
+struct stm32_adc_ic {
+ const char *name;
+ u32 idx;
+};
+
+static const struct stm32_adc_ic stm32_adc_ic[STM32_ADC_INT_CH_NB] = {
+ { "vddcore", STM32_ADC_INT_CH_VDDCORE },
+ { "vrefint", STM32_ADC_INT_CH_VREFINT },
+ { "vbat", STM32_ADC_INT_CH_VBAT },
+};
+
+/**
+ * struct stm32_adc_trig_info - ADC trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @extsel: trigger selection
+ */
+struct stm32_adc_trig_info {
+ const char *name;
+ enum stm32_adc_extsel extsel;
+};
+
+/**
+ * struct stm32_adc_calib - optional adc calibration data
+ * @calfact_s: Calibration offset for single ended channels
+ * @calfact_d: Calibration offset in differential
+ * @lincalfact: Linearity calibration factor
+ * @calibrated: Indicates calibration status
+ */
+struct stm32_adc_calib {
+ u32 calfact_s;
+ u32 calfact_d;
+ u32 lincalfact[STM32H7_LINCALFACT_NUM];
+ bool calibrated;
+};
+
+/**
+ * struct stm32_adc_regs - stm32 ADC misc registers & bitfield desc
+ * @reg: register offset
+ * @mask: bitfield mask
+ * @shift: left shift
+ */
+struct stm32_adc_regs {
+ int reg;
+ int mask;
+ int shift;
+};
+
+/**
+ * struct stm32_adc_vrefint - stm32 ADC internal reference voltage data
+ * @vrefint_cal: vrefint calibration value from nvmem
+ * @vrefint_data: vrefint actual value
+ */
+struct stm32_adc_vrefint {
+ u32 vrefint_cal;
+ u32 vrefint_data;
+};
+
+/**
+ * struct stm32_adc_regspec - stm32 registers definition
+ * @dr: data register offset
+ * @ier_eoc: interrupt enable register & eocie bitfield
+ * @ier_ovr: interrupt enable register & overrun bitfield
+ * @isr_eoc: interrupt status register & eoc bitfield
+ * @isr_ovr: interrupt status register & overrun bitfield
+ * @sqr: reference to sequence registers array
+ * @exten: trigger control register & bitfield
+ * @extsel: trigger selection register & bitfield
+ * @res: resolution selection register & bitfield
+ * @smpr: smpr1 & smpr2 registers offset array
+ * @smp_bits: smpr1 & smpr2 index and bitfields
+ * @or_vdd: option register & vddcore bitfield
+ * @ccr_vbat: common register & vbat bitfield
+ * @ccr_vref: common register & vrefint bitfield
+ */
+struct stm32_adc_regspec {
+ const u32 dr;
+ const struct stm32_adc_regs ier_eoc;
+ const struct stm32_adc_regs ier_ovr;
+ const struct stm32_adc_regs isr_eoc;
+ const struct stm32_adc_regs isr_ovr;
+ const struct stm32_adc_regs *sqr;
+ const struct stm32_adc_regs exten;
+ const struct stm32_adc_regs extsel;
+ const struct stm32_adc_regs res;
+ const u32 smpr[2];
+ const struct stm32_adc_regs *smp_bits;
+ const struct stm32_adc_regs or_vdd;
+ const struct stm32_adc_regs ccr_vbat;
+ const struct stm32_adc_regs ccr_vref;
+};
+
+struct stm32_adc;
+
+/**
+ * struct stm32_adc_cfg - stm32 compatible configuration data
+ * @regs: registers descriptions
+ * @adc_info: per instance input channels definitions
+ * @trigs: external trigger sources
+ * @clk_required: clock is required
+ * @has_vregready: vregready status flag presence
+ * @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
+ * @smp_cycles: programmable sampling time (ADC clock cycles)
+ * @ts_vrefint_ns: vrefint minimum sampling time in ns
+ */
+struct stm32_adc_cfg {
+ const struct stm32_adc_regspec *regs;
+ const struct stm32_adc_info *adc_info;
+ struct stm32_adc_trig_info *trigs;
+ bool clk_required;
+ bool has_vregready;
+ 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);
+ const unsigned int *smp_cycles;
+ const unsigned int ts_vrefint_ns;
+};
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @offset: ADC instance register offset in ADC block
+ * @cfg: compatible configuration data
+ * @completion: end of single conversion completion
+ * @buffer: data buffer + 8 bytes for timestamp if enabled
+ * @clk: clock for this adc instance
+ * @irq: interrupt for this adc instance
+ * @lock: spinlock
+ * @bufi: data buffer index
+ * @num_conv: expected number of scan conversions
+ * @res: data resolution (e.g. RES bitfield value)
+ * @trigger_polarity: external trigger polarity (e.g. exten)
+ * @dma_chan: dma channel
+ * @rx_buf: dma rx buffer cpu address
+ * @rx_dma_buf: dma rx buffer bus address
+ * @rx_buf_sz: dma rx buffer size
+ * @difsel: bitmask to set single-ended/differential channel
+ * @pcsel: bitmask to preselect channels on some devices
+ * @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
+ * @cal: optional calibration data on some devices
+ * @vrefint: internal reference voltage data
+ * @chan_name: channel name array
+ * @num_diff: number of differential channels
+ * @int_ch: internal channel indexes array
+ * @nsmps: number of channels with optional sample time
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ u32 offset;
+ const struct stm32_adc_cfg *cfg;
+ struct completion completion;
+ u16 buffer[STM32_ADC_MAX_SQ + 4] __aligned(8);
+ struct clk *clk;
+ int irq;
+ spinlock_t lock; /* interrupt lock */
+ unsigned int bufi;
+ unsigned int num_conv;
+ u32 res;
+ u32 trigger_polarity;
+ struct dma_chan *dma_chan;
+ u8 *rx_buf;
+ dma_addr_t rx_dma_buf;
+ unsigned int rx_buf_sz;
+ u32 difsel;
+ u32 pcsel;
+ u32 smpr_val[2];
+ struct stm32_adc_calib cal;
+ struct stm32_adc_vrefint vrefint;
+ char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
+ u32 num_diff;
+ int int_ch[STM32_ADC_INT_CH_NB];
+ int nsmps;
+};
+
+struct stm32_adc_diff_channel {
+ u32 vinp;
+ u32 vinn;
+};
+
+/**
+ * struct stm32_adc_info - stm32 ADC, per instance config data
+ * @max_channels: Number of channels
+ * @resolutions: available resolutions
+ * @num_res: number of available resolutions
+ */
+struct stm32_adc_info {
+ int max_channels;
+ const unsigned int *resolutions;
+ const unsigned int num_res;
+};
+
+static const unsigned int stm32f4_adc_resolutions[] = {
+ /* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */
+ 12, 10, 8, 6,
+};
+
+/* stm32f4 can have up to 16 channels */
+static const struct stm32_adc_info stm32f4_adc_info = {
+ .max_channels = 16,
+ .resolutions = stm32f4_adc_resolutions,
+ .num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
+};
+
+static const unsigned int stm32h7_adc_resolutions[] = {
+ /* sorted values so the index matches RES[2:0] in STM32H7_ADC_CFGR */
+ 16, 14, 12, 10, 8,
+};
+
+/* stm32h7 can have up to 20 channels */
+static const struct stm32_adc_info stm32h7_adc_info = {
+ .max_channels = STM32_ADC_CH_MAX,
+ .resolutions = stm32h7_adc_resolutions,
+ .num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
+};
+
+/*
+ * stm32f4_sq - describe regular sequence registers
+ * - L: sequence len (register & bit field)
+ * - SQ1..SQ16: sequence entries (register & bit field)
+ */
+static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = {
+ /* L: len bit field description to be kept as first element */
+ { STM32F4_ADC_SQR1, GENMASK(23, 20), 20 },
+ /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */
+ { STM32F4_ADC_SQR3, GENMASK(4, 0), 0 },
+ { STM32F4_ADC_SQR3, GENMASK(9, 5), 5 },
+ { STM32F4_ADC_SQR3, GENMASK(14, 10), 10 },
+ { STM32F4_ADC_SQR3, GENMASK(19, 15), 15 },
+ { STM32F4_ADC_SQR3, GENMASK(24, 20), 20 },
+ { STM32F4_ADC_SQR3, GENMASK(29, 25), 25 },
+ { STM32F4_ADC_SQR2, GENMASK(4, 0), 0 },
+ { STM32F4_ADC_SQR2, GENMASK(9, 5), 5 },
+ { STM32F4_ADC_SQR2, GENMASK(14, 10), 10 },
+ { STM32F4_ADC_SQR2, GENMASK(19, 15), 15 },
+ { STM32F4_ADC_SQR2, GENMASK(24, 20), 20 },
+ { STM32F4_ADC_SQR2, GENMASK(29, 25), 25 },
+ { STM32F4_ADC_SQR1, GENMASK(4, 0), 0 },
+ { STM32F4_ADC_SQR1, GENMASK(9, 5), 5 },
+ { STM32F4_ADC_SQR1, GENMASK(14, 10), 10 },
+ { STM32F4_ADC_SQR1, GENMASK(19, 15), 15 },
+};
+
+/* STM32F4 external trigger sources for all instances */
+static struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
+ { TIM1_CH1, STM32_EXT0 },
+ { TIM1_CH2, STM32_EXT1 },
+ { TIM1_CH3, STM32_EXT2 },
+ { TIM2_CH2, STM32_EXT3 },
+ { TIM2_CH3, STM32_EXT4 },
+ { TIM2_CH4, STM32_EXT5 },
+ { TIM2_TRGO, STM32_EXT6 },
+ { TIM3_CH1, STM32_EXT7 },
+ { TIM3_TRGO, STM32_EXT8 },
+ { TIM4_CH4, STM32_EXT9 },
+ { TIM5_CH1, STM32_EXT10 },
+ { TIM5_CH2, STM32_EXT11 },
+ { TIM5_CH3, STM32_EXT12 },
+ { TIM8_CH1, STM32_EXT13 },
+ { TIM8_TRGO, STM32_EXT14 },
+ {}, /* sentinel */
+};
+
+/*
+ * stm32f4_smp_bits[] - describe sampling time register index & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32f4_smp_bits[] = {
+ /* STM32F4_ADC_SMPR2: smpr[] index, mask, shift for SMP0 to SMP9 */
+ { 1, GENMASK(2, 0), 0 },
+ { 1, GENMASK(5, 3), 3 },
+ { 1, GENMASK(8, 6), 6 },
+ { 1, GENMASK(11, 9), 9 },
+ { 1, GENMASK(14, 12), 12 },
+ { 1, GENMASK(17, 15), 15 },
+ { 1, GENMASK(20, 18), 18 },
+ { 1, GENMASK(23, 21), 21 },
+ { 1, GENMASK(26, 24), 24 },
+ { 1, GENMASK(29, 27), 27 },
+ /* STM32F4_ADC_SMPR1, smpr[] index, mask, shift for SMP10 to SMP18 */
+ { 0, GENMASK(2, 0), 0 },
+ { 0, GENMASK(5, 3), 3 },
+ { 0, GENMASK(8, 6), 6 },
+ { 0, GENMASK(11, 9), 9 },
+ { 0, GENMASK(14, 12), 12 },
+ { 0, GENMASK(17, 15), 15 },
+ { 0, GENMASK(20, 18), 18 },
+ { 0, GENMASK(23, 21), 21 },
+ { 0, GENMASK(26, 24), 24 },
+};
+
+/* STM32F4 programmable sampling time (ADC clock cycles) */
+static const unsigned int stm32f4_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+ 3, 15, 28, 56, 84, 112, 144, 480,
+};
+
+static const struct stm32_adc_regspec stm32f4_adc_regspec = {
+ .dr = STM32F4_ADC_DR,
+ .ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
+ .ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE },
+ .isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
+ .isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR },
+ .sqr = stm32f4_sq,
+ .exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
+ .extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK,
+ STM32F4_EXTSEL_SHIFT },
+ .res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT },
+ .smpr = { STM32F4_ADC_SMPR1, STM32F4_ADC_SMPR2 },
+ .smp_bits = stm32f4_smp_bits,
+};
+
+static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = {
+ /* L: len bit field description to be kept as first element */
+ { STM32H7_ADC_SQR1, GENMASK(3, 0), 0 },
+ /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */
+ { STM32H7_ADC_SQR1, GENMASK(10, 6), 6 },
+ { STM32H7_ADC_SQR1, GENMASK(16, 12), 12 },
+ { STM32H7_ADC_SQR1, GENMASK(22, 18), 18 },
+ { STM32H7_ADC_SQR1, GENMASK(28, 24), 24 },
+ { STM32H7_ADC_SQR2, GENMASK(4, 0), 0 },
+ { STM32H7_ADC_SQR2, GENMASK(10, 6), 6 },
+ { STM32H7_ADC_SQR2, GENMASK(16, 12), 12 },
+ { STM32H7_ADC_SQR2, GENMASK(22, 18), 18 },
+ { STM32H7_ADC_SQR2, GENMASK(28, 24), 24 },
+ { STM32H7_ADC_SQR3, GENMASK(4, 0), 0 },
+ { STM32H7_ADC_SQR3, GENMASK(10, 6), 6 },
+ { STM32H7_ADC_SQR3, GENMASK(16, 12), 12 },
+ { STM32H7_ADC_SQR3, GENMASK(22, 18), 18 },
+ { STM32H7_ADC_SQR3, GENMASK(28, 24), 24 },
+ { STM32H7_ADC_SQR4, GENMASK(4, 0), 0 },
+ { STM32H7_ADC_SQR4, GENMASK(10, 6), 6 },
+};
+
+/* STM32H7 external trigger sources for all instances */
+static struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
+ { TIM1_CH1, STM32_EXT0 },
+ { TIM1_CH2, STM32_EXT1 },
+ { TIM1_CH3, STM32_EXT2 },
+ { TIM2_CH2, STM32_EXT3 },
+ { TIM3_TRGO, STM32_EXT4 },
+ { TIM4_CH4, STM32_EXT5 },
+ { TIM8_TRGO, STM32_EXT7 },
+ { TIM8_TRGO2, STM32_EXT8 },
+ { TIM1_TRGO, STM32_EXT9 },
+ { TIM1_TRGO2, STM32_EXT10 },
+ { TIM2_TRGO, STM32_EXT11 },
+ { TIM4_TRGO, STM32_EXT12 },
+ { TIM6_TRGO, STM32_EXT13 },
+ { TIM15_TRGO, STM32_EXT14 },
+ { TIM3_CH4, STM32_EXT15 },
+ { LPTIM1_OUT, STM32_EXT18 },
+ { LPTIM2_OUT, STM32_EXT19 },
+ { LPTIM3_OUT, STM32_EXT20 },
+ {},
+};
+
+/*
+ * stm32h7_smp_bits - describe sampling time register index & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32h7_smp_bits[] = {
+ /* STM32H7_ADC_SMPR1, smpr[] index, mask, shift for SMP0 to SMP9 */
+ { 0, GENMASK(2, 0), 0 },
+ { 0, GENMASK(5, 3), 3 },
+ { 0, GENMASK(8, 6), 6 },
+ { 0, GENMASK(11, 9), 9 },
+ { 0, GENMASK(14, 12), 12 },
+ { 0, GENMASK(17, 15), 15 },
+ { 0, GENMASK(20, 18), 18 },
+ { 0, GENMASK(23, 21), 21 },
+ { 0, GENMASK(26, 24), 24 },
+ { 0, GENMASK(29, 27), 27 },
+ /* STM32H7_ADC_SMPR2, smpr[] index, mask, shift for SMP10 to SMP19 */
+ { 1, GENMASK(2, 0), 0 },
+ { 1, GENMASK(5, 3), 3 },
+ { 1, GENMASK(8, 6), 6 },
+ { 1, GENMASK(11, 9), 9 },
+ { 1, GENMASK(14, 12), 12 },
+ { 1, GENMASK(17, 15), 15 },
+ { 1, GENMASK(20, 18), 18 },
+ { 1, GENMASK(23, 21), 21 },
+ { 1, GENMASK(26, 24), 24 },
+ { 1, GENMASK(29, 27), 27 },
+};
+
+/* STM32H7 programmable sampling time (ADC clock cycles, rounded down) */
+static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+ 1, 2, 8, 16, 32, 64, 387, 810,
+};
+
+static const struct stm32_adc_regspec stm32h7_adc_regspec = {
+ .dr = STM32H7_ADC_DR,
+ .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+ .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+ .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+ .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+ .sqr = stm32h7_sq,
+ .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+ .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+ STM32H7_EXTSEL_SHIFT },
+ .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+ .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+ .smp_bits = stm32h7_smp_bits,
+};
+
+static const struct stm32_adc_regspec stm32mp1_adc_regspec = {
+ .dr = STM32H7_ADC_DR,
+ .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+ .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
+ .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+ .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
+ .sqr = stm32h7_sq,
+ .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+ .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+ STM32H7_EXTSEL_SHIFT },
+ .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+ .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
+ .smp_bits = stm32h7_smp_bits,
+ .or_vdd = { STM32MP1_ADC2_OR, STM32MP1_VDDCOREEN },
+ .ccr_vbat = { STM32H7_ADC_CCR, STM32H7_VBATEN },
+ .ccr_vref = { STM32H7_ADC_CCR, STM32H7_VREFEN },
+};
+
+/*
+ * STM32 ADC registers access routines
+ * @adc: stm32 adc instance
+ * @reg: reg offset in adc instance
+ *
+ * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
+ * for adc1, adc2 and adc3.
+ */
+static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ return readl_relaxed(adc->common->base + adc->offset + reg);
+}
+
+#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr)
+
+#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
+ readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
+ cond, sleep_us, timeout_us)
+
+static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
+{
+ return readw_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void stm32_adc_set_bits_common(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ spin_lock(&adc->common->lock);
+ writel_relaxed(readl_relaxed(adc->common->base + reg) | bits,
+ adc->common->base + reg);
+ spin_unlock(&adc->common->lock);
+}
+
+static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+ spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static void stm32_adc_clr_bits_common(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+ spin_lock(&adc->common->lock);
+ writel_relaxed(readl_relaxed(adc->common->base + reg) & ~bits,
+ adc->common->base + reg);
+ spin_unlock(&adc->common->lock);
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg,
+ adc->cfg->regs->ier_eoc.mask);
+};
+
+/**
+ * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg,
+ adc->cfg->regs->ier_eoc.mask);
+}
+
+static void stm32_adc_ovr_irq_enable(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, adc->cfg->regs->ier_ovr.reg,
+ adc->cfg->regs->ier_ovr.mask);
+}
+
+static void stm32_adc_ovr_irq_disable(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, adc->cfg->regs->ier_ovr.reg,
+ adc->cfg->regs->ier_ovr.mask);
+}
+
+static void stm32_adc_set_res(struct stm32_adc *adc)
+{
+ const struct stm32_adc_regs *res = &adc->cfg->regs->res;
+ u32 val;
+
+ val = stm32_adc_readl(adc, res->reg);
+ val = (val & ~res->mask) | (adc->res << res->shift);
+ stm32_adc_writel(adc, res->reg, val);
+}
+
+static int stm32_adc_hw_stop(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ if (adc->cfg->unprepare)
+ adc->cfg->unprepare(indio_dev);
+
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static int stm32_adc_hw_start(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret)
+ return ret;
+
+ stm32_adc_set_res(adc);
+
+ if (adc->cfg->prepare) {
+ ret = adc->cfg->prepare(indio_dev);
+ if (ret)
+ goto err_clk_dis;
+ }
+
+ return 0;
+
+err_clk_dis:
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
+static void stm32_adc_int_ch_enable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ u32 i;
+
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+ if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE)
+ continue;
+
+ switch (i) {
+ case STM32_ADC_INT_CH_VDDCORE:
+ dev_dbg(&indio_dev->dev, "Enable VDDCore\n");
+ stm32_adc_set_bits(adc, adc->cfg->regs->or_vdd.reg,
+ adc->cfg->regs->or_vdd.mask);
+ break;
+ case STM32_ADC_INT_CH_VREFINT:
+ dev_dbg(&indio_dev->dev, "Enable VREFInt\n");
+ stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vref.reg,
+ adc->cfg->regs->ccr_vref.mask);
+ break;
+ case STM32_ADC_INT_CH_VBAT:
+ dev_dbg(&indio_dev->dev, "Enable VBAT\n");
+ stm32_adc_set_bits_common(adc, adc->cfg->regs->ccr_vbat.reg,
+ adc->cfg->regs->ccr_vbat.mask);
+ break;
+ }
+ }
+}
+
+static void stm32_adc_int_ch_disable(struct stm32_adc *adc)
+{
+ u32 i;
+
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+ if (adc->int_ch[i] == STM32_ADC_INT_CH_NONE)
+ continue;
+
+ switch (i) {
+ case STM32_ADC_INT_CH_VDDCORE:
+ stm32_adc_clr_bits(adc, adc->cfg->regs->or_vdd.reg,
+ adc->cfg->regs->or_vdd.mask);
+ break;
+ case STM32_ADC_INT_CH_VREFINT:
+ stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vref.reg,
+ adc->cfg->regs->ccr_vref.mask);
+ break;
+ case STM32_ADC_INT_CH_VBAT:
+ stm32_adc_clr_bits_common(adc, adc->cfg->regs->ccr_vbat.reg,
+ adc->cfg->regs->ccr_vbat.mask);
+ break;
+ }
+ }
+}
+
+/**
+ * stm32f4_adc_start_conv() - Start conversions for regular channels.
+ * @indio_dev: IIO device instance
+ * @dma: use dma to transfer conversion result
+ *
+ * Start conversions for regular channels.
+ * Also take care of normal or DMA mode. Circular DMA may be used for regular
+ * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct
+ * DR read instead (e.g. read_raw, or triggered buffer mode without DMA).
+ */
+static void stm32f4_adc_start_conv(struct iio_dev *indio_dev, bool dma)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+
+ if (dma)
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2,
+ STM32F4_DMA | STM32F4_DDS);
+
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
+}
+
+static void stm32f4_adc_stop_conv(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2,
+ STM32F4_ADON | STM32F4_DMA | STM32F4_DDS);
+}
+
+static void stm32f4_adc_irq_clear(struct iio_dev *indio_dev, u32 msk)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_clr_bits(adc, adc->cfg->regs->isr_eoc.reg, msk);
+}
+
+static void stm32h7_adc_start_conv(struct iio_dev *indio_dev, bool dma)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ enum stm32h7_adc_dmngt dmngt;
+ unsigned long flags;
+ u32 val;
+
+ if (dma)
+ dmngt = STM32H7_DMNGT_DMA_CIRC;
+ else
+ dmngt = STM32H7_DMNGT_DR_ONLY;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, STM32H7_ADC_CFGR);
+ val = (val & ~STM32H7_DMNGT_MASK) | (dmngt << STM32H7_DMNGT_SHIFT);
+ stm32_adc_writel(adc, STM32H7_ADC_CFGR, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART);
+}
+
+static void stm32h7_adc_stop_conv(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP);
+
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & (STM32H7_ADSTART)),
+ 100, STM32_ADC_TIMEOUT_US);
+ if (ret)
+ dev_warn(&indio_dev->dev, "stop failed\n");
+
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
+}
+
+static void stm32h7_adc_irq_clear(struct iio_dev *indio_dev, u32 msk)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ /* On STM32H7 IRQs are cleared by writing 1 into ISR register */
+ stm32_adc_set_bits(adc, adc->cfg->regs->isr_eoc.reg, msk);
+}
+
+static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ /* Exit deep power down, then enable ADC voltage regulator */
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
+
+ if (adc->common->rate > STM32H7_BOOST_CLKRATE)
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
+
+ /* Wait for startup time */
+ if (!adc->cfg->has_vregready) {
+ usleep_range(10, 20);
+ return 0;
+ }
+
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
+ val & STM32MP1_VREGREADY, 100,
+ STM32_ADC_TIMEOUT_US);
+ if (ret) {
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
+ dev_err(&indio_dev->dev, "Failed to exit power down\n");
+ }
+
+ return ret;
+}
+
+static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
+
+ /* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
+}
+
+static int stm32h7_adc_enable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN);
+
+ /* Poll for ADRDY to be set (after adc startup time) */
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
+ val & STM32H7_ADRDY,
+ 100, STM32_ADC_TIMEOUT_US);
+ if (ret) {
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS);
+ dev_err(&indio_dev->dev, "Failed to enable ADC\n");
+ } else {
+ /* Clear ADRDY by writing one */
+ stm32_adc_set_bits(adc, STM32H7_ADC_ISR, STM32H7_ADRDY);
+ }
+
+ return ret;
+}
+
+static void stm32h7_adc_disable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ if (!(stm32_adc_readl(adc, STM32H7_ADC_CR) & STM32H7_ADEN))
+ return;
+
+ /* Disable ADC and wait until it's effectively disabled */
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS);
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & STM32H7_ADEN), 100,
+ STM32_ADC_TIMEOUT_US);
+ if (ret)
+ dev_warn(&indio_dev->dev, "Failed to disable\n");
+}
+
+/**
+ * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result
+ * @indio_dev: IIO device instance
+ * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable
+ */
+static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int i, ret;
+ u32 lincalrdyw_mask, val;
+
+ /* Read linearity calibration */
+ lincalrdyw_mask = STM32H7_LINCALRDYW6;
+ for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
+ /* Clear STM32H7_LINCALRDYW[6..1]: transfer calib to CALFACT2 */
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
+
+ /* Poll: wait calib data to be ready in CALFACT2 register */
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & lincalrdyw_mask),
+ 100, STM32_ADC_TIMEOUT_US);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to read calfact\n");
+ return ret;
+ }
+
+ val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
+ adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK);
+ adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT;
+
+ lincalrdyw_mask >>= 1;
+ }
+
+ /* Read offset calibration */
+ val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
+ adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
+ adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
+ adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
+ adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+ adc->cal.calibrated = true;
+
+ return 0;
+}
+
+/**
+ * stm32h7_adc_restore_selfcalib() - Restore saved self-calibration result
+ * @indio_dev: IIO device instance
+ * Note: ADC must be enabled, with no on-going conversions.
+ */
+static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int i, ret;
+ u32 lincalrdyw_mask, val;
+
+ val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
+ (adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
+ stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
+
+ lincalrdyw_mask = STM32H7_LINCALRDYW6;
+ for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
+ /*
+ * Write saved calibration data to shadow registers:
+ * Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger
+ * data write. Then poll to wait for complete transfer.
+ */
+ val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT;
+ stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val);
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ val & lincalrdyw_mask,
+ 100, STM32_ADC_TIMEOUT_US);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to write calfact\n");
+ return ret;
+ }
+
+ /*
+ * Read back calibration data, has two effects:
+ * - It ensures bits LINCALRDYW[6..1] are kept cleared
+ * for next time calibration needs to be restored.
+ * - BTW, bit clear triggers a read, then check data has been
+ * correctly written.
+ */
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & lincalrdyw_mask),
+ 100, STM32_ADC_TIMEOUT_US);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to read calfact\n");
+ return ret;
+ }
+ val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
+ if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) {
+ dev_err(&indio_dev->dev, "calfact not consistent\n");
+ return -EIO;
+ }
+
+ lincalrdyw_mask >>= 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Fixed timeout value for ADC calibration.
+ * worst cases:
+ * - low clock frequency
+ * - maximum prescalers
+ * Calibration requires:
+ * - 131,072 ADC clock cycle for the linear calibration
+ * - 20 ADC clock cycle for the offset calibration
+ *
+ * Set to 100ms for now
+ */
+#define STM32H7_ADC_CALIB_TIMEOUT_US 100000
+
+/**
+ * stm32h7_adc_selfcalib() - Procedure to calibrate ADC
+ * @indio_dev: IIO device instance
+ * Note: Must be called once ADC is out of power down.
+ */
+static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int ret;
+ u32 val;
+
+ if (adc->cal.calibrated)
+ return true;
+
+ /* ADC must be disabled for calibration */
+ stm32h7_adc_disable(indio_dev);
+
+ /*
+ * Select calibration mode:
+ * - Offset calibration for single ended inputs
+ * - No linearity calibration (do it later, before reading it)
+ */
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF);
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN);
+
+ /* Start calibration, then wait for completion */
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & STM32H7_ADCAL), 100,
+ STM32H7_ADC_CALIB_TIMEOUT_US);
+ if (ret) {
+ dev_err(&indio_dev->dev, "calibration failed\n");
+ goto out;
+ }
+
+ /*
+ * Select calibration mode, then start calibration:
+ * - Offset calibration for differential input
+ * - Linearity calibration (needs to be done only once for single/diff)
+ * will run simultaneously with offset calibration.
+ */
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR,
+ STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & STM32H7_ADCAL), 100,
+ STM32H7_ADC_CALIB_TIMEOUT_US);
+ if (ret) {
+ dev_err(&indio_dev->dev, "calibration failed\n");
+ goto out;
+ }
+
+out:
+ stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
+ STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+
+ return ret;
+}
+
+/**
+ * stm32h7_adc_prepare() - Leave power down mode to enable ADC.
+ * @indio_dev: IIO device instance
+ * Leave power down mode.
+ * Configure channels as single ended or differential before enabling ADC.
+ * Enable ADC.
+ * Restore calibration data.
+ * Pre-select channels that may be used in PCSEL (required by input MUX / IO):
+ * - Only one input is selected for single ended (e.g. 'vinp')
+ * - Two inputs are selected for differential channels (e.g. 'vinp' & 'vinn')
+ */
+static int stm32h7_adc_prepare(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int calib, ret;
+
+ ret = stm32h7_adc_exit_pwr_down(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = stm32h7_adc_selfcalib(indio_dev);
+ if (ret < 0)
+ goto pwr_dwn;
+ calib = ret;
+
+ stm32_adc_int_ch_enable(indio_dev);
+
+ stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
+
+ ret = stm32h7_adc_enable(indio_dev);
+ if (ret)
+ goto ch_disable;
+
+ /* Either restore or read calibration result for future reference */
+ if (calib)
+ ret = stm32h7_adc_restore_selfcalib(indio_dev);
+ else
+ ret = stm32h7_adc_read_selfcalib(indio_dev);
+ if (ret)
+ goto disable;
+
+ stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
+
+ return 0;
+
+disable:
+ stm32h7_adc_disable(indio_dev);
+ch_disable:
+ stm32_adc_int_ch_disable(adc);
+pwr_dwn:
+ stm32h7_adc_enter_pwr_down(adc);
+
+ return ret;
+}
+
+static void stm32h7_adc_unprepare(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0);
+ stm32h7_adc_disable(indio_dev);
+ stm32_adc_int_ch_disable(adc);
+ stm32h7_adc_enter_pwr_down(adc);
+}
+
+/**
+ * stm32_adc_conf_scan_seq() - Build regular channels scan sequence
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ *
+ * Conversion sequence :
+ * Apply sampling time settings for all channels.
+ * Configure ADC scan sequence based on selected channels in scan_mask.
+ * Add channels to SQR registers, from scan_mask LSB to MSB, then
+ * program sequence len.
+ */
+static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regs *sqr = adc->cfg->regs->sqr;
+ const struct iio_chan_spec *chan;
+ u32 val, bit;
+ int i = 0;
+
+ /* Apply sampling time settings */
+ stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]);
+ stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]);
+
+ for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+ chan = indio_dev->channels + bit;
+ /*
+ * Assign one channel per SQ entry in regular
+ * sequence, starting with SQ1.
+ */
+ i++;
+ if (i > STM32_ADC_MAX_SQ)
+ return -EINVAL;
+
+ dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n",
+ __func__, chan->channel, i);
+
+ val = stm32_adc_readl(adc, sqr[i].reg);
+ val &= ~sqr[i].mask;
+ val |= chan->channel << sqr[i].shift;
+ stm32_adc_writel(adc, sqr[i].reg, val);
+ }
+
+ if (!i)
+ return -EINVAL;
+
+ /* Sequence len */
+ val = stm32_adc_readl(adc, sqr[0].reg);
+ val &= ~sqr[0].mask;
+ val |= ((i - 1) << sqr[0].shift);
+ stm32_adc_writel(adc, sqr[0].reg, val);
+
+ return 0;
+}
+
+/**
+ * stm32_adc_get_trig_extsel() - Get external trigger selection
+ * @indio_dev: IIO device structure
+ * @trig: trigger
+ *
+ * Returns trigger extsel value, if trig matches, -EINVAL otherwise.
+ */
+static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int i;
+
+ /* lookup triggers registered by stm32 timer trigger driver */
+ for (i = 0; adc->cfg->trigs[i].name; i++) {
+ /**
+ * Checking both stm32 timer trigger type and trig name
+ * should be safe against arbitrary trigger names.
+ */
+ if ((is_stm32_timer_trigger(trig) ||
+ is_stm32_lptim_trigger(trig)) &&
+ !strcmp(adc->cfg->trigs[i].name, trig->name)) {
+ return adc->cfg->trigs[i].extsel;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_set_trig() - Set a regular trigger
+ * @indio_dev: IIO device
+ * @trig: IIO trigger
+ *
+ * Set trigger source/polarity (e.g. SW, or HW with polarity) :
+ * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
+ * - if HW trigger enabled, set source & polarity
+ */
+static int stm32_adc_set_trig(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
+ unsigned long flags;
+ int ret;
+
+ if (trig) {
+ ret = stm32_adc_get_trig_extsel(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* set trigger source and polarity (default to rising edge) */
+ extsel = ret;
+ exten = adc->trigger_polarity + STM32_EXTEN_HWTRIG_RISING_EDGE;
+ }
+
+ spin_lock_irqsave(&adc->lock, flags);
+ val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg);
+ val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask);
+ val |= exten << adc->cfg->regs->exten.shift;
+ val |= extsel << adc->cfg->regs->extsel.shift;
+ stm32_adc_writel(adc, adc->cfg->regs->exten.reg, val);
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+ return 0;
+}
+
+static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int type)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ adc->trigger_polarity = type;
+
+ return 0;
+}
+
+static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ return adc->trigger_polarity;
+}
+
+static const char * const stm32_trig_pol_items[] = {
+ "rising-edge", "falling-edge", "both-edges",
+};
+
+static const struct iio_enum stm32_adc_trig_pol = {
+ .items = stm32_trig_pol_items,
+ .num_items = ARRAY_SIZE(stm32_trig_pol_items),
+ .get = stm32_adc_get_trig_pol,
+ .set = stm32_adc_set_trig_pol,
+};
+
+/**
+ * stm32_adc_single_conv() - Performs a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @res: conversion result
+ *
+ * The function performs a single conversion on a given channel:
+ * - Apply sampling time settings
+ * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
+ * - Use SW trigger
+ * - Start conversion, then wait for interrupt completion.
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *res)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ const struct stm32_adc_regspec *regs = adc->cfg->regs;
+ long timeout;
+ u32 val;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ adc->bufi = 0;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ /* Apply sampling time settings */
+ stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]);
+ stm32_adc_writel(adc, regs->smpr[1], adc->smpr_val[1]);
+
+ /* Program chan number in regular sequence (SQ1) */
+ val = stm32_adc_readl(adc, regs->sqr[1].reg);
+ val &= ~regs->sqr[1].mask;
+ val |= chan->channel << regs->sqr[1].shift;
+ stm32_adc_writel(adc, regs->sqr[1].reg, val);
+
+ /* Set regular sequence len (0 for 1 conversion) */
+ stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask);
+
+ /* Trigger detection disabled (conversion can be launched in SW) */
+ stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask);
+
+ stm32_adc_conv_irq_enable(adc);
+
+ adc->cfg->start_conv(indio_dev, false);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ ret = timeout;
+ } else {
+ *res = adc->buffer[0];
+ ret = IIO_VAL_INT;
+ }
+
+ adc->cfg->stop_conv(indio_dev);
+
+ stm32_adc_conv_irq_disable(adc);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int stm32_adc_read_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);
+ int ret;
+
+ 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 (chan->type == IIO_VOLTAGE)
+ ret = stm32_adc_single_conv(indio_dev, chan, val);
+ else
+ ret = -EINVAL;
+
+ if (mask == IIO_CHAN_INFO_PROCESSED)
+ *val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val;
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->differential) {
+ *val = adc->common->vref_mv * 2;
+ *val2 = chan->scan_type.realbits;
+ } else {
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ }
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->differential)
+ /* ADC_full_scale / 2 */
+ *val = -((1 << chan->scan_type.realbits) / 2);
+ else
+ *val = 0;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void stm32_adc_irq_clear(struct iio_dev *indio_dev, u32 msk)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ adc->cfg->irq_clear(indio_dev, msk);
+}
+
+static irqreturn_t stm32_adc_threaded_isr(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regspec *regs = adc->cfg->regs;
+ u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
+
+ /* Check ovr status right now, as ovr mask should be already disabled */
+ if (status & regs->isr_ovr.mask) {
+ /*
+ * Clear ovr bit to avoid subsequent calls to IRQ handler.
+ * This requires to stop ADC first. OVR bit state in ISR,
+ * is propaged to CSR register by hardware.
+ */
+ adc->cfg->stop_conv(indio_dev);
+ stm32_adc_irq_clear(indio_dev, regs->isr_ovr.mask);
+ dev_err(&indio_dev->dev, "Overrun, stopping: restart needed\n");
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t stm32_adc_isr(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_regspec *regs = adc->cfg->regs;
+ u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
+
+ if (status & regs->isr_ovr.mask) {
+ /*
+ * Overrun occurred on regular conversions: data for wrong
+ * channel may be read. Unconditionally disable interrupts
+ * to stop processing data and print error message.
+ * Restarting the capture can be done by disabling, then
+ * re-enabling it (e.g. write 0, then 1 to buffer/enable).
+ */
+ stm32_adc_ovr_irq_disable(adc);
+ stm32_adc_conv_irq_disable(adc);
+ return IRQ_WAKE_THREAD;
+ }
+
+ if (status & regs->isr_eoc.mask) {
+ /* Reading DR also clears EOC status flag */
+ adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr);
+ if (iio_buffer_enabled(indio_dev)) {
+ adc->bufi++;
+ if (adc->bufi >= adc->num_conv) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ }
+ } else {
+ complete(&adc->completion);
+ }
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/**
+ * stm32_adc_validate_trigger() - validate trigger for stm32 adc
+ * @indio_dev: IIO device
+ * @trig: new trigger
+ *
+ * Returns: 0 if trig matches one of the triggers registered by stm32 adc
+ * driver, -EINVAL otherwise.
+ */
+static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_adc_get_trig_extsel(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
+static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2;
+ unsigned int rx_buf_sz = STM32_DMA_BUFFER_SIZE;
+
+ /*
+ * dma cyclic transfers are used, buffer is split into two periods.
+ * There should be :
+ * - always one buffer (period) dma is working on
+ * - one buffer (period) driver can push data.
+ */
+ watermark = min(watermark, val * (unsigned)(sizeof(u16)));
+ adc->rx_buf_sz = min(rx_buf_sz, watermark * 2 * adc->num_conv);
+
+ return 0;
+}
+
+static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
+
+ ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int stm32_adc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
+{
+ int i;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ * @indio_dev: IIO device structure
+ * @reg: register offset
+ * @writeval: value to write
+ * @readval: value to read
+ *
+ * To read a value from an ADC register:
+ * echo [ADC reg offset] > direct_reg_access
+ * cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ * echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ if (!readval)
+ stm32_adc_writel(adc, reg, writeval);
+ else
+ *readval = stm32_adc_readl(adc, reg);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+ .read_raw = stm32_adc_read_raw,
+ .validate_trigger = stm32_adc_validate_trigger,
+ .hwfifo_set_watermark = stm32_adc_set_watermark,
+ .update_scan_mode = stm32_adc_update_scan_mode,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .fwnode_xlate = stm32_adc_fwnode_xlate,
+};
+
+static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(adc->dma_chan,
+ adc->dma_chan->cookie,
+ &state);
+ if (status == DMA_IN_PROGRESS) {
+ /* Residue is size in bytes from end of buffer */
+ unsigned int i = adc->rx_buf_sz - state.residue;
+ unsigned int size;
+
+ /* Return available bytes */
+ if (i >= adc->bufi)
+ size = i - adc->bufi;
+ else
+ size = adc->rx_buf_sz + i - adc->bufi;
+
+ return size;
+ }
+
+ return 0;
+}
+
+static void stm32_adc_dma_buffer_done(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ int residue = stm32_adc_dma_residue(adc);
+
+ /*
+ * In DMA mode the trigger services of IIO are not used
+ * (e.g. no call to iio_trigger_poll).
+ * Calling irq handler associated to the hardware trigger is not
+ * relevant as the conversions have already been done. Data
+ * transfers are performed directly in DMA callback instead.
+ * This implementation avoids to call trigger irq handler that
+ * may sleep, in an atomic context (DMA irq handler context).
+ */
+ dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
+
+ while (residue >= indio_dev->scan_bytes) {
+ u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi];
+
+ iio_push_to_buffers(indio_dev, buffer);
+
+ residue -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->rx_buf_sz)
+ adc->bufi = 0;
+ }
+}
+
+static int stm32_adc_dma_start(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+ int ret;
+
+ if (!adc->dma_chan)
+ return 0;
+
+ dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
+ adc->rx_buf_sz, adc->rx_buf_sz / 2);
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
+ adc->rx_dma_buf,
+ adc->rx_buf_sz, adc->rx_buf_sz / 2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = stm32_adc_dma_buffer_done;
+ desc->callback_param = indio_dev;
+
+ cookie = dmaengine_submit(desc);
+ ret = dma_submit_error(cookie);
+ if (ret) {
+ dmaengine_terminate_sync(adc->dma_chan);
+ return ret;
+ }
+
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(adc->dma_chan);
+
+ return 0;
+}
+
+static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't set trigger\n");
+ goto err_pm_put;
+ }
+
+ ret = stm32_adc_dma_start(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start dma\n");
+ goto err_clr_trig;
+ }
+
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ stm32_adc_ovr_irq_enable(adc);
+
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_enable(adc);
+
+ adc->cfg->start_conv(indio_dev, !!adc->dma_chan);
+
+ return 0;
+
+err_clr_trig:
+ stm32_adc_set_trig(indio_dev, NULL);
+err_pm_put:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+
+ adc->cfg->stop_conv(indio_dev);
+ if (!adc->dma_chan)
+ stm32_adc_conv_irq_disable(adc);
+
+ stm32_adc_ovr_irq_disable(adc);
+
+ if (adc->dma_chan)
+ dmaengine_terminate_sync(adc->dma_chan);
+
+ if (stm32_adc_set_trig(indio_dev, NULL))
+ dev_err(&indio_dev->dev, "Can't clear trigger\n");
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_adc_buffer_setup_ops = {
+ .postenable = &stm32_adc_buffer_postenable,
+ .predisable = &stm32_adc_buffer_predisable,
+};
+
+static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
+
+ /* reset buffer index */
+ adc->bufi = 0;
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+ pf->timestamp);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ /* re-enable eoc irq */
+ stm32_adc_conv_irq_enable(adc);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
+ IIO_ENUM("trigger_polarity", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
+ {
+ .name = "trigger_polarity_available",
+ .shared = IIO_SHARED_BY_ALL,
+ .read = iio_enum_available_read,
+ .private = (uintptr_t)&stm32_adc_trig_pol,
+ },
+ {},
+};
+
+static int stm32_adc_fw_get_resolution(struct iio_dev *indio_dev)
+{
+ struct device *dev = &indio_dev->dev;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ unsigned int i;
+ u32 res;
+
+ if (device_property_read_u32(dev, "assigned-resolution-bits", &res))
+ res = adc->cfg->adc_info->resolutions[0];
+
+ for (i = 0; i < adc->cfg->adc_info->num_res; i++)
+ if (res == adc->cfg->adc_info->resolutions[i])
+ break;
+ if (i >= adc->cfg->adc_info->num_res) {
+ dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
+ return -EINVAL;
+ }
+
+ dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res);
+ adc->res = i;
+
+ return 0;
+}
+
+static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
+{
+ const struct stm32_adc_regs *smpr = &adc->cfg->regs->smp_bits[channel];
+ u32 period_ns, shift = smpr->shift, mask = smpr->mask;
+ unsigned int smp, r = smpr->reg;
+
+ /*
+ * For vrefint channel, ensure that the sampling time cannot
+ * be lower than the one specified in the datasheet
+ */
+ if (channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
+ smp_ns = max(smp_ns, adc->cfg->ts_vrefint_ns);
+
+ /* Determine sampling time (ADC clock cycles) */
+ period_ns = NSEC_PER_SEC / adc->common->rate;
+ for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++)
+ if ((period_ns * adc->cfg->smp_cycles[smp]) >= smp_ns)
+ break;
+ if (smp > STM32_ADC_MAX_SMP)
+ smp = STM32_ADC_MAX_SMP;
+
+ /* pre-build sampling time registers (e.g. smpr1, smpr2) */
+ adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift);
+}
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct iio_chan_spec *chan, u32 vinp,
+ u32 vinn, int scan_index, bool differential)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ char *name = adc->chan_name[vinp];
+
+ chan->type = IIO_VOLTAGE;
+ chan->channel = vinp;
+ if (differential) {
+ chan->differential = 1;
+ chan->channel2 = vinn;
+ snprintf(name, STM32_ADC_CH_SZ, "in%d-in%d", vinp, vinn);
+ } else {
+ snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp);
+ }
+ chan->datasheet_name = name;
+ chan->scan_index = scan_index;
+ chan->indexed = 1;
+ if (chan->channel == adc->int_ch[STM32_ADC_INT_CH_VREFINT])
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED);
+ else
+ 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);
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
+ chan->scan_type.storagebits = 16;
+ chan->ext_info = stm32_adc_ext_info;
+
+ /* pre-build selected channels mask */
+ adc->pcsel |= BIT(chan->channel);
+ if (differential) {
+ /* pre-build diff channels mask */
+ adc->difsel |= BIT(chan->channel);
+ /* Also add negative input to pre-selected channels */
+ adc->pcsel |= BIT(chan->channel2);
+ }
+}
+
+static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc)
+{
+ struct device *dev = &indio_dev->dev;
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+ int num_channels = 0, ret;
+
+ ret = device_property_count_u32(dev, "st,adc-channels");
+ if (ret > adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+ return -EINVAL;
+ } else if (ret > 0) {
+ num_channels += ret;
+ }
+
+ /*
+ * each st,adc-diff-channels is a group of 2 u32 so we divide @ret
+ * to get the *real* number of channels.
+ */
+ ret = device_property_count_u32(dev, "st,adc-diff-channels");
+ if (ret > 0) {
+ ret /= (int)(sizeof(struct stm32_adc_diff_channel) / sizeof(u32));
+ if (ret > adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
+ return -EINVAL;
+ } else if (ret > 0) {
+ adc->num_diff = ret;
+ num_channels += ret;
+ }
+ }
+
+ /* Optional sample time is provided either for each, or all channels */
+ adc->nsmps = device_property_count_u32(dev, "st,min-sample-time-nsecs");
+ if (adc->nsmps > 1 && adc->nsmps != num_channels) {
+ dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
+ return -EINVAL;
+ }
+
+ return num_channels;
+}
+
+static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
+ struct stm32_adc *adc,
+ struct iio_chan_spec *channels,
+ int nchans)
+{
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+ struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
+ struct device *dev = &indio_dev->dev;
+ u32 num_diff = adc->num_diff;
+ int num_se = nchans - num_diff;
+ int size = num_diff * sizeof(*diff) / sizeof(u32);
+ int scan_index = 0, ret, i, c;
+ u32 smp = 0, smps[STM32_ADC_CH_MAX], chans[STM32_ADC_CH_MAX];
+
+ if (num_diff) {
+ ret = device_property_read_u32_array(dev, "st,adc-diff-channels",
+ (u32 *)diff, size);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < num_diff; i++) {
+ if (diff[i].vinp >= adc_info->max_channels ||
+ diff[i].vinn >= adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+ diff[i].vinp, diff[i].vinn);
+ return -EINVAL;
+ }
+
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ diff[i].vinp, diff[i].vinn,
+ scan_index, true);
+ scan_index++;
+ }
+ }
+ if (num_se > 0) {
+ ret = device_property_read_u32_array(dev, "st,adc-channels", chans, num_se);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to get st,adc-channels %d\n", ret);
+ return ret;
+ }
+
+ for (c = 0; c < num_se; c++) {
+ if (chans[c] >= adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n",
+ chans[c]);
+ return -EINVAL;
+ }
+
+ /* Channel can't be configured both as single-ended & diff */
+ for (i = 0; i < num_diff; i++) {
+ if (chans[c] == diff[i].vinp) {
+ dev_err(&indio_dev->dev, "channel %d misconfigured\n",
+ chans[c]);
+ return -EINVAL;
+ }
+ }
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ chans[c], 0, scan_index, false);
+ scan_index++;
+ }
+ }
+
+ if (adc->nsmps > 0) {
+ ret = device_property_read_u32_array(dev, "st,min-sample-time-nsecs",
+ smps, adc->nsmps);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < scan_index; i++) {
+ /*
+ * This check is used with the above logic so that smp value
+ * will only be modified if valid u32 value can be decoded. This
+ * allows to get either no value, 1 shared value for all indexes,
+ * or one value per channel. The point is to have the same
+ * behavior as 'of_property_read_u32_index()'.
+ */
+ if (i < adc->nsmps)
+ smp = smps[i];
+
+ /* Prepare sampling time settings */
+ stm32_adc_smpr_init(adc, channels[i].channel, smp);
+ }
+
+ return scan_index;
+}
+
+static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_name,
+ int chan)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ u16 vrefint;
+ int i, ret;
+
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
+ if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+ if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) {
+ adc->int_ch[i] = chan;
+ break;
+ }
+
+ /* Get calibration data for vrefint channel */
+ ret = nvmem_cell_read_u16(&indio_dev->dev, "vrefint", &vrefint);
+ if (ret && ret != -ENOENT) {
+ return dev_err_probe(indio_dev->dev.parent, ret,
+ "nvmem access error\n");
+ }
+ if (ret == -ENOENT) {
+ dev_dbg(&indio_dev->dev, "vrefint calibration not found. Skip vrefint channel\n");
+ return ret;
+ } else if (!vrefint) {
+ dev_dbg(&indio_dev->dev, "Null vrefint calibration value. Skip vrefint channel\n");
+ return -ENOENT;
+ }
+ adc->int_ch[i] = chan;
+ adc->vrefint.vrefint_cal = vrefint;
+ }
+ }
+
+ return 0;
+}
+
+static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
+ struct stm32_adc *adc,
+ struct iio_chan_spec *channels)
+{
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+ struct fwnode_handle *child;
+ const char *name;
+ int val, scan_index = 0, ret;
+ bool differential;
+ u32 vin[2];
+
+ device_for_each_child_node(&indio_dev->dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &val);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
+ goto err;
+ }
+
+ ret = fwnode_property_read_string(child, "label", &name);
+ /* label is optional */
+ if (!ret) {
+ if (strlen(name) >= STM32_ADC_CH_SZ) {
+ dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
+ name, STM32_ADC_CH_SZ);
+ ret = -EINVAL;
+ goto err;
+ }
+ strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
+ ret = stm32_adc_populate_int_ch(indio_dev, name, val);
+ if (ret == -ENOENT)
+ continue;
+ else if (ret)
+ goto err;
+ } else if (ret != -EINVAL) {
+ dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
+ goto err;
+ }
+
+ if (val >= adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ differential = false;
+ ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2);
+ /* diff-channels is optional */
+ if (!ret) {
+ differential = true;
+ if (vin[0] != val || vin[1] >= adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
+ vin[0], vin[1]);
+ goto err;
+ }
+ } else if (ret != -EINVAL) {
+ dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
+ goto err;
+ }
+
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
+ vin[1], scan_index, differential);
+
+ val = 0;
+ ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val);
+ /* st,min-sample-time-ns is optional */
+ if (ret && ret != -EINVAL) {
+ dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n",
+ ret);
+ goto err;
+ }
+
+ stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
+ if (differential)
+ stm32_adc_smpr_init(adc, vin[1], val);
+
+ scan_index++;
+ }
+
+ return scan_index;
+
+err:
+ fwnode_handle_put(child);
+
+ return ret;
+}
+
+static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
+ struct iio_chan_spec *channels;
+ int scan_index = 0, num_channels = 0, ret, i;
+ bool legacy = false;
+
+ for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
+ adc->int_ch[i] = STM32_ADC_INT_CH_NONE;
+
+ num_channels = device_get_child_node_count(&indio_dev->dev);
+ /* If no channels have been found, fallback to channels legacy properties. */
+ if (!num_channels) {
+ legacy = true;
+
+ ret = stm32_adc_get_legacy_chan_count(indio_dev, adc);
+ if (!ret) {
+ dev_err(indio_dev->dev.parent, "No channel found\n");
+ return -ENODATA;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ num_channels = ret;
+ }
+
+ if (num_channels > adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Channel number [%d] exceeds %d\n",
+ num_channels, adc_info->max_channels);
+ return -EINVAL;
+ }
+
+ if (timestamping)
+ num_channels++;
+
+ channels = devm_kcalloc(&indio_dev->dev, num_channels,
+ sizeof(struct iio_chan_spec), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ if (legacy)
+ ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels,
+ timestamping ? num_channels - 1 : num_channels);
+ else
+ ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
+ if (ret < 0)
+ return ret;
+ scan_index = ret;
+
+ if (timestamping) {
+ struct iio_chan_spec *timestamp = &channels[scan_index];
+
+ timestamp->type = IIO_TIMESTAMP;
+ timestamp->channel = -1;
+ timestamp->scan_index = scan_index;
+ timestamp->scan_type.sign = 's';
+ timestamp->scan_type.realbits = 64;
+ timestamp->scan_type.storagebits = 64;
+
+ scan_index++;
+ }
+
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct dma_slave_config config;
+ int ret;
+
+ adc->dma_chan = dma_request_chan(dev, "rx");
+ if (IS_ERR(adc->dma_chan)) {
+ ret = PTR_ERR(adc->dma_chan);
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "DMA channel request failed with\n");
+
+ /* DMA is optional: fall back to IRQ mode */
+ adc->dma_chan = NULL;
+ return 0;
+ }
+
+ adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
+ STM32_DMA_BUFFER_SIZE,
+ &adc->rx_dma_buf, GFP_KERNEL);
+ if (!adc->rx_buf) {
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ /* Configure DMA channel to read data register */
+ memset(&config, 0, sizeof(config));
+ config.src_addr = (dma_addr_t)adc->common->phys_base;
+ config.src_addr += adc->offset + adc->cfg->regs->dr;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ goto err_free;
+
+ return 0;
+
+err_free:
+ dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE,
+ adc->rx_buf, adc->rx_dma_buf);
+err_release:
+ dma_release_channel(adc->dma_chan);
+
+ return ret;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ irqreturn_t (*handler)(int irq, void *p) = NULL;
+ struct stm32_adc *adc;
+ bool timestamping = false;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->common = dev_get_drvdata(pdev->dev.parent);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+ adc->cfg = device_get_match_data(dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ device_set_node(&indio_dev->dev, dev_fwnode(&pdev->dev));
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = device_property_read_u32(dev, "reg", &adc->offset);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0)
+ return adc->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+ stm32_adc_threaded_isr,
+ 0, pdev->name, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ adc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ ret = PTR_ERR(adc->clk);
+ if (ret == -ENOENT && !adc->cfg->clk_required) {
+ adc->clk = NULL;
+ } else {
+ dev_err(&pdev->dev, "Can't get clock\n");
+ return ret;
+ }
+ }
+
+ ret = stm32_adc_fw_get_resolution(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = stm32_adc_dma_request(dev, indio_dev);
+ if (ret < 0)
+ return ret;
+
+ if (!adc->dma_chan) {
+ /* For PIO mode only, iio_pollfunc_store_time stores a timestamp
+ * in the primary trigger IRQ handler and stm32_adc_trigger_handler
+ * runs in the IRQ thread to push out buffer along with timestamp.
+ */
+ handler = &stm32_adc_trigger_handler;
+ timestamping = true;
+ }
+
+ ret = stm32_adc_chan_fw_init(indio_dev, timestamping);
+ if (ret < 0)
+ goto err_dma_disable;
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time, handler,
+ &stm32_adc_buffer_setup_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "buffer setup failed\n");
+ goto err_dma_disable;
+ }
+
+ /* Get stm32-adc-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_hw_start(dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio dev register failed\n");
+ goto err_hw_stop;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_hw_stop:
+ stm32_adc_hw_stop(dev);
+
+err_buffer_cleanup:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+err_dma_disable:
+ if (adc->dma_chan) {
+ dma_free_coherent(adc->dma_chan->device->dev,
+ STM32_DMA_BUFFER_SIZE,
+ adc->rx_buf, adc->rx_dma_buf);
+ dma_release_channel(adc->dma_chan);
+ }
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ iio_device_unregister(indio_dev);
+ stm32_adc_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (adc->dma_chan) {
+ dma_free_coherent(adc->dma_chan->device->dev,
+ STM32_DMA_BUFFER_SIZE,
+ adc->rx_buf, adc->rx_dma_buf);
+ dma_release_channel(adc->dma_chan);
+ }
+
+ return 0;
+}
+
+static int stm32_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ if (iio_buffer_enabled(indio_dev))
+ stm32_adc_buffer_predisable(indio_dev);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int stm32_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ ret = stm32_adc_update_scan_mode(indio_dev,
+ indio_dev->active_scan_mask);
+ if (ret < 0)
+ return ret;
+
+ return stm32_adc_buffer_postenable(indio_dev);
+}
+
+static int stm32_adc_runtime_suspend(struct device *dev)
+{
+ return stm32_adc_hw_stop(dev);
+}
+
+static int stm32_adc_runtime_resume(struct device *dev)
+{
+ return stm32_adc_hw_start(dev);
+}
+
+static const struct dev_pm_ops stm32_adc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume)
+ RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume,
+ NULL)
+};
+
+static const struct stm32_adc_cfg stm32f4_adc_cfg = {
+ .regs = &stm32f4_adc_regspec,
+ .adc_info = &stm32f4_adc_info,
+ .trigs = stm32f4_adc_trigs,
+ .clk_required = true,
+ .start_conv = stm32f4_adc_start_conv,
+ .stop_conv = stm32f4_adc_stop_conv,
+ .smp_cycles = stm32f4_adc_smp_cycles,
+ .irq_clear = stm32f4_adc_irq_clear,
+};
+
+static const struct stm32_adc_cfg stm32h7_adc_cfg = {
+ .regs = &stm32h7_adc_regspec,
+ .adc_info = &stm32h7_adc_info,
+ .trigs = stm32h7_adc_trigs,
+ .start_conv = stm32h7_adc_start_conv,
+ .stop_conv = stm32h7_adc_stop_conv,
+ .prepare = stm32h7_adc_prepare,
+ .unprepare = stm32h7_adc_unprepare,
+ .smp_cycles = stm32h7_adc_smp_cycles,
+ .irq_clear = stm32h7_adc_irq_clear,
+};
+
+static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
+ .regs = &stm32mp1_adc_regspec,
+ .adc_info = &stm32h7_adc_info,
+ .trigs = stm32h7_adc_trigs,
+ .has_vregready = true,
+ .start_conv = stm32h7_adc_start_conv,
+ .stop_conv = stm32h7_adc_stop_conv,
+ .prepare = stm32h7_adc_prepare,
+ .unprepare = stm32h7_adc_unprepare,
+ .smp_cycles = stm32h7_adc_smp_cycles,
+ .irq_clear = stm32h7_adc_irq_clear,
+ .ts_vrefint_ns = 4300,
+};
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
+ { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
+ { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc",
+ .of_match_table = stm32_adc_of_match,
+ .pm = pm_ptr(&stm32_adc_pm_ops),
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc");
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
new file mode 100644
index 000000000..a428bdb56
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,1685 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is the ADC part of the STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/iio/adc/stm32-dfsdm-adc.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/hw-consumer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/timer/stm32-timer-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "stm32-dfsdm.h"
+
+#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE)
+
+/* Conversion timeout */
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+/* Oversampling attribute default */
+#define DFSDM_DEFAULT_OVERSAMPLING 100
+
+/* Oversampling max values */
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */
+#define DFSDM_DATA_MAX BIT(30)
+/*
+ * Data are output as two's complement data in a 24 bit field.
+ * Data from filters are in the range +/-2^(n-1)
+ * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits
+ * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1)
+ * So, the resolution of samples from filter is actually limited to 23 bits
+ */
+#define DFSDM_DATA_RES 24
+
+/* Filter configuration */
+#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
+ DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \
+ DFSDM_CR1_JSCAN_MASK)
+
+enum sd_converter_type {
+ DFSDM_AUDIO,
+ DFSDM_IIO,
+};
+
+struct stm32_dfsdm_dev_data {
+ int type;
+ int (*init)(struct device *dev, struct iio_dev *indio_dev);
+ unsigned int num_channels;
+ const struct regmap_config *regmap_cfg;
+};
+
+struct stm32_dfsdm_adc {
+ struct stm32_dfsdm *dfsdm;
+ const struct stm32_dfsdm_dev_data *dev_data;
+ unsigned int fl_id;
+ unsigned int nconv;
+ unsigned long smask;
+
+ /* ADC specific */
+ unsigned int oversamp;
+ struct iio_hw_consumer *hwc;
+ struct completion completion;
+ u32 *buffer;
+
+ /* Audio specific */
+ unsigned int spi_freq; /* SPI bus clock frequency */
+ unsigned int sample_freq; /* Sample frequency after filter decimation */
+ int (*cb)(const void *data, size_t size, void *cb_priv);
+ void *cb_priv;
+
+ /* DMA */
+ u8 *rx_buf;
+ unsigned int bufi; /* Buffer current position */
+ unsigned int buf_sz; /* Buffer size */
+ struct dma_chan *dma_chan;
+ dma_addr_t dma_buf;
+};
+
+struct stm32_dfsdm_str2field {
+ const char *name;
+ unsigned int val;
+};
+
+/* DFSDM channel serial interface type */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
+ { "SPI_R", 0 }, /* SPI with data on rising edge */
+ { "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 */
+static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
+ /* External SPI clock (CLKIN x) */
+ { "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
+ /* Internal SPI clock (CLKOUT) */
+ { "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
+ /* Internal SPI clock divided by 2 (falling edge) */
+ { "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,
+ const struct stm32_dfsdm_str2field *list)
+{
+ const struct stm32_dfsdm_str2field *p = list;
+
+ for (p = list; p && p->name; p++)
+ if (!strcmp(p->name, str))
+ return p->val;
+
+ return -EINVAL;
+}
+
+/**
+ * struct stm32_dfsdm_trig_info - DFSDM trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @jextsel: trigger signal selection
+ */
+struct stm32_dfsdm_trig_info {
+ const char *name;
+ unsigned int jextsel;
+};
+
+/* hardware injected trigger enable, edge selection */
+enum stm32_dfsdm_jexten {
+ STM32_DFSDM_JEXTEN_DISABLED,
+ STM32_DFSDM_JEXTEN_RISING_EDGE,
+ STM32_DFSDM_JEXTEN_FALLING_EDGE,
+ STM32_DFSDM_EXTEN_BOTH_EDGES,
+};
+
+static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
+ { TIM1_TRGO, 0 },
+ { TIM1_TRGO2, 1 },
+ { TIM8_TRGO, 2 },
+ { TIM8_TRGO2, 3 },
+ { TIM3_TRGO, 4 },
+ { TIM4_TRGO, 5 },
+ { TIM16_OC1, 6 },
+ { TIM6_TRGO, 7 },
+ { TIM7_TRGO, 8 },
+ { LPTIM1_OUT, 26 },
+ { LPTIM2_OUT, 27 },
+ { LPTIM3_OUT, 28 },
+ {},
+};
+
+static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ int i;
+
+ /* lookup triggers registered by stm32 timer trigger driver */
+ for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
+ /**
+ * Checking both stm32 timer trigger type and trig name
+ * should be safe against arbitrary trigger names.
+ */
+ if ((is_stm32_timer_trigger(trig) ||
+ is_stm32_lptim_trigger(trig)) &&
+ !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
+ return stm32_dfsdm_trigs[i].jextsel;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
+ unsigned int fast, unsigned int oversamp)
+{
+ unsigned int i, d, fosr, iosr;
+ u64 res, max;
+ int bits, shift;
+ unsigned int m = 1; /* multiplication factor */
+ unsigned int p = fl->ford; /* filter order (ford) */
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast];
+
+ pr_debug("Requested oversampling: %d\n", oversamp);
+ /*
+ * This function tries to compute filter oversampling and integrator
+ * oversampling, base on oversampling ratio requested by user.
+ *
+ * Decimation d depends on the filter order and the oversampling ratios.
+ * ford: filter order
+ * fosr: filter over sampling ratio
+ * iosr: integrator over sampling ratio
+ */
+ if (fl->ford == DFSDM_FASTSINC_ORDER) {
+ m = 2;
+ p = 2;
+ }
+
+ /*
+ * Look for filter and integrator oversampling ratios which allows
+ * to maximize data output resolution.
+ */
+ for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
+ for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
+ if (fast)
+ d = fosr * iosr;
+ else if (fl->ford == DFSDM_FASTSINC_ORDER)
+ d = fosr * (iosr + 3) + 2;
+ else
+ d = fosr * (iosr - 1 + p) + p;
+
+ if (d > oversamp)
+ break;
+ else if (d != oversamp)
+ continue;
+ /*
+ * Check resolution (limited to signed 32 bits)
+ * res <= 2^31
+ * Sincx filters:
+ * res = m * fosr^p x iosr (with m=1, p=ford)
+ * FastSinc filter
+ * res = m * fosr^p x iosr (with m=2, p=2)
+ */
+ res = fosr;
+ for (i = p - 1; i > 0; i--) {
+ res = res * (u64)fosr;
+ if (res > DFSDM_DATA_MAX)
+ break;
+ }
+ if (res > DFSDM_DATA_MAX)
+ continue;
+
+ res = res * (u64)m * (u64)iosr;
+ if (res > DFSDM_DATA_MAX)
+ continue;
+
+ if (res >= flo->res) {
+ flo->res = res;
+ flo->fosr = fosr;
+ flo->iosr = iosr;
+
+ bits = fls(flo->res);
+ /* 8 LBSs in data register contain chan info */
+ max = flo->res << 8;
+
+ /* if resolution is not a power of two */
+ if (flo->res > BIT(bits - 1))
+ bits++;
+ else
+ max--;
+
+ shift = DFSDM_DATA_RES - bits;
+ /*
+ * Compute right/left shift
+ * Right shift is performed by hardware
+ * when transferring samples to data register.
+ * Left shift is done by software on buffer
+ */
+ if (shift > 0) {
+ /* Resolution is lower than 24 bits */
+ flo->rshift = 0;
+ flo->lshift = shift;
+ } else {
+ /*
+ * If resolution is 24 bits or more,
+ * max positive value may be ambiguous
+ * (equal to max negative value as sign
+ * bit is dropped).
+ * Reduce resolution to 23 bits (rshift)
+ * to keep the sign on bit 23 and treat
+ * saturation before rescaling on 24
+ * bits (lshift).
+ */
+ flo->rshift = 1 - shift;
+ flo->lshift = 1;
+ max >>= flo->rshift;
+ }
+ flo->max = (s32)max;
+ flo->bits = bits;
+
+ pr_debug("fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
+ fast, flo->fosr, flo->iosr,
+ flo->res, bits, flo->rshift,
+ flo->lshift);
+ }
+ }
+ }
+
+ if (!flo->res)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev,
+ unsigned int oversamp)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ int ret0, ret1;
+
+ memset(&fl->flo[0], 0, sizeof(fl->flo[0]));
+ memset(&fl->flo[1], 0, sizeof(fl->flo[1]));
+
+ ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp);
+ ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp);
+ if (ret0 < 0 && ret1 < 0) {
+ dev_err(&indio_dev->dev,
+ "Filter parameters not found: errors %d/%d\n",
+ ret0, ret1);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stm32_dfsdm_start_channel(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(1));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void stm32_dfsdm_stop_channel(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(0));
+ }
+}
+
+static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
+ struct stm32_dfsdm_channel *ch)
+{
+ unsigned int id = ch->id;
+ struct regmap *regmap = dfsdm->regmap;
+ int ret;
+
+ ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+ DFSDM_CHCFGR1_SITP_MASK,
+ DFSDM_CHCFGR1_SITP(ch->type));
+ if (ret < 0)
+ return ret;
+ ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+ DFSDM_CHCFGR1_SPICKSEL_MASK,
+ DFSDM_CHCFGR1_SPICKSEL(ch->src));
+ if (ret < 0)
+ return ret;
+ return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
+ DFSDM_CHCFGR1_CHINSEL_MASK,
+ DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
+}
+
+static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct stm32_dfsdm *dfsdm = adc->dfsdm;
+ int ret;
+
+ /* Enable filter */
+ ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
+ if (ret < 0)
+ return ret;
+
+ /* Nothing more to do for injected (scan mode/triggered) conversions */
+ if (adc->nconv > 1 || trig)
+ return 0;
+
+ /* Software start (single or continuous) regular conversion */
+ return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_RSWSTART_MASK,
+ DFSDM_CR1_RSWSTART(1));
+}
+
+static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
+ unsigned int fl_id)
+{
+ /* Disable conversion */
+ regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
+}
+
+static int stm32_dfsdm_filter_set_trig(struct iio_dev *indio_dev,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
+ int ret;
+
+ if (trig) {
+ ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* set trigger source and polarity (default to rising edge) */
+ jextsel = ret;
+ jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
+ }
+
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
+ DFSDM_CR1_JEXTSEL(jextsel) |
+ DFSDM_CR1_JEXTEN(jexten));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_dfsdm_channels_configure(struct iio_dev *indio_dev,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[0];
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ fl->fast = 0;
+
+ /*
+ * In continuous mode, use fast mode configuration,
+ * if it provides a better resolution.
+ */
+ if (adc->nconv == 1 && !trig && iio_buffer_enabled(indio_dev)) {
+ if (fl->flo[1].res >= fl->flo[0].res) {
+ fl->fast = 1;
+ flo = &fl->flo[1];
+ }
+ }
+
+ if (!flo->res)
+ return -EINVAL;
+
+ dev_dbg(&indio_dev->dev, "Samples actual resolution: %d bits",
+ min(flo->bits, (u32)DFSDM_DATA_RES - 1));
+
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+
+ ret = regmap_update_bits(regmap,
+ DFSDM_CHCFGR2(chan->channel),
+ DFSDM_CHCFGR2_DTRBS_MASK,
+ DFSDM_CHCFGR2_DTRBS(flo->rshift));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_dfsdm_filter_configure(struct iio_dev *indio_dev,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ u32 cr1;
+ const struct iio_chan_spec *chan;
+ unsigned int bit, jchg = 0;
+ int ret;
+
+ /* Average integrator oversampling */
+ ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+ DFSDM_FCR_IOSR(flo->iosr - 1));
+ if (ret)
+ return ret;
+
+ /* Filter order and Oversampling */
+ ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
+ DFSDM_FCR_FOSR(flo->fosr - 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
+ DFSDM_FCR_FORD(fl->ford));
+ if (ret)
+ return ret;
+
+ ret = stm32_dfsdm_filter_set_trig(indio_dev, fl_id, trig);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_FAST_MASK,
+ DFSDM_CR1_FAST(fl->fast));
+ if (ret)
+ return ret;
+
+ /*
+ * DFSDM modes configuration W.R.T audio/iio type modes
+ * ----------------------------------------------------------------
+ * Modes | regular | regular | injected | injected |
+ * | | continuous | | + scan |
+ * --------------|---------|--------------|----------|------------|
+ * single conv | x | | | |
+ * (1 chan) | | | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 Audio chan | | sample freq | | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 IIO chan | | sample freq | trigger | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 2+ IIO chans | | | | trigger or |
+ * | | | | sync_mode |
+ * ----------------------------------------------------------------
+ */
+ if (adc->nconv == 1 && !trig) {
+ bit = __ffs(adc->smask);
+ chan = indio_dev->channels + bit;
+
+ /* Use regular conversion for single channel without trigger */
+ cr1 = DFSDM_CR1_RCH(chan->channel);
+
+ /* Continuous conversions triggered by SPI clk in buffer mode */
+ if (iio_buffer_enabled(indio_dev))
+ cr1 |= DFSDM_CR1_RCONT(1);
+
+ cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
+ } else {
+ /* Use injected conversion for multiple channels */
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ jchg |= BIT(chan->channel);
+ }
+ ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg);
+ if (ret < 0)
+ return ret;
+
+ /* Use scan mode for multiple channels */
+ cr1 = DFSDM_CR1_JSCAN((adc->nconv > 1) ? 1 : 0);
+
+ /*
+ * Continuous conversions not supported in injected mode,
+ * either use:
+ * - conversions in sync with filter 0
+ * - triggered conversions
+ */
+ if (!fl->sync_mode && !trig)
+ return -EINVAL;
+ cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
+ }
+
+ return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
+ cr1);
+}
+
+static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
+ struct iio_dev *indio_dev,
+ struct iio_chan_spec *ch)
+{
+ struct stm32_dfsdm_channel *df_ch;
+ const char *of_str;
+ int chan_idx = ch->scan_index;
+ int ret, val;
+
+ ret = of_property_read_u32_index(indio_dev->dev.of_node,
+ "st,adc-channels", chan_idx,
+ &ch->channel);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ " Error parsing 'st,adc-channels' for idx %d\n",
+ chan_idx);
+ return ret;
+ }
+ if (ch->channel >= dfsdm->num_chs) {
+ dev_err(&indio_dev->dev,
+ " Error bad channel number %d (max = %d)\n",
+ ch->channel, dfsdm->num_chs);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_string_index(indio_dev->dev.of_node,
+ "st,adc-channel-names", chan_idx,
+ &ch->datasheet_name);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ " Error parsing 'st,adc-channel-names' for idx %d\n",
+ chan_idx);
+ return ret;
+ }
+
+ df_ch = &dfsdm->ch_list[ch->channel];
+ df_ch->id = ch->channel;
+
+ ret = of_property_read_string_index(indio_dev->dev.of_node,
+ "st,adc-channel-types", chan_idx,
+ &of_str);
+ if (!ret) {
+ val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
+ if (val < 0)
+ return val;
+ } else {
+ val = 0;
+ }
+ df_ch->type = val;
+
+ ret = of_property_read_string_index(indio_dev->dev.of_node,
+ "st,adc-channel-clk-src", chan_idx,
+ &of_str);
+ if (!ret) {
+ val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
+ if (val < 0)
+ return val;
+ } else {
+ val = 0;
+ }
+ df_ch->src = val;
+
+ ret = of_property_read_u32_index(indio_dev->dev.of_node,
+ "st,adc-alt-channel", chan_idx,
+ &df_ch->alt_si);
+ if (ret < 0)
+ df_ch->alt_si = 0;
+
+ return 0;
+}
+
+static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
+ uintptr_t priv,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
+}
+
+static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
+ unsigned int sample_freq,
+ unsigned int spi_freq)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ unsigned int oversamp;
+ int ret;
+
+ oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
+ if (spi_freq % sample_freq)
+ dev_dbg(&indio_dev->dev,
+ "Rate not accurate. requested (%u), actual (%u)\n",
+ sample_freq, spi_freq / oversamp);
+
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp);
+ if (ret < 0)
+ return ret;
+
+ adc->sample_freq = spi_freq / oversamp;
+ adc->oversamp = oversamp;
+
+ return 0;
+}
+
+static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
+ uintptr_t priv,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
+ unsigned int sample_freq = adc->sample_freq;
+ unsigned int spi_freq;
+ int ret;
+
+ dev_err(&indio_dev->dev, "enter %s\n", __func__);
+ /* If DFSDM is master on SPI, SPI freq can not be updated */
+ if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+ return -EPERM;
+
+ ret = kstrtoint(buf, 0, &spi_freq);
+ if (ret)
+ return ret;
+
+ if (!spi_freq)
+ return -EINVAL;
+
+ if (sample_freq) {
+ ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
+ if (ret < 0)
+ return ret;
+ }
+ adc->spi_freq = spi_freq;
+
+ return len;
+}
+
+static int stm32_dfsdm_start_conv(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ int ret;
+
+ ret = stm32_dfsdm_channels_configure(indio_dev, adc->fl_id, trig);
+ if (ret < 0)
+ return ret;
+
+ ret = stm32_dfsdm_start_channel(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = stm32_dfsdm_filter_configure(indio_dev, adc->fl_id, trig);
+ if (ret < 0)
+ goto stop_channels;
+
+ ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
+ if (ret < 0)
+ goto filter_unconfigure;
+
+ return 0;
+
+filter_unconfigure:
+ regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_CFG_MASK, 0);
+stop_channels:
+ stm32_dfsdm_stop_channel(indio_dev);
+
+ return ret;
+}
+
+static void stm32_dfsdm_stop_conv(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+
+ stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+
+ regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_CFG_MASK, 0);
+
+ stm32_dfsdm_stop_channel(indio_dev);
+}
+
+static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
+ unsigned int val)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+ unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;
+
+ /*
+ * DMA cyclic transfers are used, buffer is split into two periods.
+ * There should be :
+ * - always one buffer (period) DMA is working on
+ * - one buffer (period) driver pushed to ASoC side.
+ */
+ watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
+ adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
+
+ return 0;
+}
+
+static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
+{
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(adc->dma_chan,
+ adc->dma_chan->cookie,
+ &state);
+ if (status == DMA_IN_PROGRESS) {
+ /* Residue is size in bytes from end of buffer */
+ unsigned int i = adc->buf_sz - state.residue;
+ unsigned int size;
+
+ /* Return available bytes */
+ if (i >= adc->bufi)
+ size = i - adc->bufi;
+ else
+ size = adc->buf_sz + i - adc->bufi;
+
+ return size;
+ }
+
+ return 0;
+}
+
+static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc,
+ s32 *buffer)
+{
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ unsigned int i = adc->nconv;
+ s32 *ptr = buffer;
+
+ while (i--) {
+ /* Mask 8 LSB that contains the channel ID */
+ *ptr &= 0xFFFFFF00;
+ /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */
+ if (*ptr > flo->max)
+ *ptr -= 1;
+ /*
+ * Samples from filter are retrieved with 23 bits resolution
+ * or less. Shift left to align MSB on 24 bits.
+ */
+ *ptr <<= flo->lshift;
+
+ ptr++;
+ }
+}
+
+static void stm32_dfsdm_dma_buffer_done(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int available = stm32_dfsdm_adc_dma_residue(adc);
+ size_t old_pos;
+
+ /*
+ * FIXME: In Kernel interface does not support cyclic DMA buffer,and
+ * offers only an interface to push data samples per samples.
+ * For this reason IIO buffer interface is not used and interface is
+ * bypassed using a private callback registered by ASoC.
+ * This should be a temporary solution waiting a cyclic DMA engine
+ * support in IIO.
+ */
+
+ dev_dbg(&indio_dev->dev, "pos = %d, available = %d\n",
+ adc->bufi, available);
+ old_pos = adc->bufi;
+
+ while (available >= indio_dev->scan_bytes) {
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
+
+ stm32_dfsdm_process_data(adc, buffer);
+
+ available -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->buf_sz) {
+ if (adc->cb)
+ adc->cb(&adc->rx_buf[old_pos],
+ adc->buf_sz - old_pos, adc->cb_priv);
+ adc->bufi = 0;
+ old_pos = 0;
+ }
+ /*
+ * In DMA mode the trigger services of IIO are not used
+ * (e.g. no call to iio_trigger_poll).
+ * Calling irq handler associated to the hardware trigger is not
+ * relevant as the conversions have already been done. Data
+ * transfers are performed directly in DMA callback instead.
+ * This implementation avoids to call trigger irq handler that
+ * may sleep, in an atomic context (DMA irq handler context).
+ */
+ if (adc->dev_data->type == DFSDM_IIO)
+ iio_push_to_buffers(indio_dev, buffer);
+ }
+ if (adc->cb)
+ adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
+ adc->cb_priv);
+}
+
+static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ /*
+ * The DFSDM supports half-word transfers. However, for 16 bits record,
+ * 4 bytes buswidth is kept, to avoid losing samples LSBs when left
+ * shift is required.
+ */
+ struct dma_slave_config config = {
+ .src_addr = (dma_addr_t)adc->dfsdm->phys_base,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ };
+ struct dma_async_tx_descriptor *desc;
+ dma_cookie_t cookie;
+ int ret;
+
+ if (!adc->dma_chan)
+ return -EINVAL;
+
+ dev_dbg(&indio_dev->dev, "size=%d watermark=%d\n",
+ adc->buf_sz, adc->buf_sz / 2);
+
+ if (adc->nconv == 1 && !indio_dev->trig)
+ config.src_addr += DFSDM_RDATAR(adc->fl_id);
+ else
+ config.src_addr += DFSDM_JDATAR(adc->fl_id);
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ return ret;
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
+ adc->dma_buf,
+ adc->buf_sz, adc->buf_sz / 2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = stm32_dfsdm_dma_buffer_done;
+ desc->callback_param = indio_dev;
+
+ cookie = dmaengine_submit(desc);
+ ret = dma_submit_error(cookie);
+ if (ret)
+ goto err_stop_dma;
+
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(adc->dma_chan);
+
+ if (adc->nconv == 1 && !indio_dev->trig) {
+ /* Enable regular DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK,
+ DFSDM_CR1_RDMAEN_MASK);
+ } else {
+ /* Enable injected DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_JDMAEN_MASK,
+ DFSDM_CR1_JDMAEN_MASK);
+ }
+
+ if (ret < 0)
+ goto err_stop_dma;
+
+ return 0;
+
+err_stop_dma:
+ dmaengine_terminate_all(adc->dma_chan);
+
+ return ret;
+}
+
+static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan)
+ return;
+
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0);
+ dmaengine_terminate_all(adc->dma_chan);
+}
+
+static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
+ adc->smask = *scan_mask;
+
+ dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
+
+ return 0;
+}
+
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ /* Reset adc buffer index */
+ adc->bufi = 0;
+
+ if (adc->hwc) {
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
+ if (ret < 0)
+ goto err_stop_hwc;
+
+ ret = stm32_dfsdm_adc_dma_start(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start DMA\n");
+ goto stop_dfsdm;
+ }
+
+ ret = stm32_dfsdm_start_conv(indio_dev, indio_dev->trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start conversion\n");
+ goto err_stop_dma;
+ }
+
+ return 0;
+
+err_stop_dma:
+ stm32_dfsdm_adc_dma_stop(indio_dev);
+stop_dfsdm:
+ stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+err_stop_hwc:
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+
+ return ret;
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ stm32_dfsdm_stop_conv(indio_dev);
+
+ stm32_dfsdm_adc_dma_stop(indio_dev);
+
+ stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
+ .postenable = &stm32_dfsdm_postenable,
+ .predisable = &stm32_dfsdm_predisable,
+};
+
+/**
+ * stm32_dfsdm_get_buff_cb() - register a callback that will be called when
+ * DMA transfer period is achieved.
+ *
+ * @iio_dev: Handle to IIO device.
+ * @cb: Pointer to callback function:
+ * - data: pointer to data buffer
+ * - size: size in byte of the data buffer
+ * - private: pointer to consumer private structure.
+ * @private: Pointer to consumer private structure.
+ */
+int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev,
+ int (*cb)(const void *data, size_t size,
+ void *private),
+ void *private)
+{
+ struct stm32_dfsdm_adc *adc;
+
+ if (!iio_dev)
+ return -EINVAL;
+ adc = iio_priv(iio_dev);
+
+ adc->cb = cb;
+ adc->cb_priv = private;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb);
+
+/**
+ * stm32_dfsdm_release_buff_cb - unregister buffer callback
+ *
+ * @iio_dev: Handle to IIO device.
+ */
+int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev)
+{
+ struct stm32_dfsdm_adc *adc;
+
+ if (!iio_dev)
+ return -EINVAL;
+ adc = iio_priv(iio_dev);
+
+ adc->cb = NULL;
+ adc->cb_priv = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb);
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *res)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ long timeout;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ adc->buffer = res;
+
+ ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+ DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
+ if (ret < 0)
+ goto stop_dfsdm;
+
+ adc->nconv = 1;
+ adc->smask = BIT(chan->scan_index);
+ ret = stm32_dfsdm_start_conv(indio_dev, NULL);
+ if (ret < 0) {
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+ DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+ goto stop_dfsdm;
+ }
+
+ timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+ DFSDM_TIMEOUT);
+
+ /* Mask IRQ for regular conversion achievement*/
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
+ DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
+
+ if (timeout == 0)
+ ret = -ETIMEDOUT;
+ else if (timeout < 0)
+ ret = timeout;
+ else
+ ret = IIO_VAL_INT;
+
+ stm32_dfsdm_stop_conv(indio_dev);
+
+ stm32_dfsdm_process_data(adc, res);
+
+stop_dfsdm:
+ stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+
+ return ret;
+}
+
+static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
+ unsigned int spi_freq;
+ int ret = -EINVAL;
+
+ switch (ch->src) {
+ case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
+ spi_freq = adc->dfsdm->spi_master_freq;
+ break;
+ case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING:
+ case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING:
+ spi_freq = adc->dfsdm->spi_master_freq / 2;
+ break;
+ default:
+ spi_freq = adc->spi_freq;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
+ if (!ret) {
+ dev_dbg(&indio_dev->dev,
+ "Sampling rate changed from (%u) to (%u)\n",
+ adc->sample_freq, spi_freq / val);
+ adc->oversamp = val;
+ adc->sample_freq = spi_freq / val;
+ }
+ iio_device_release_direct_mode(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;
+
+ ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = iio_hw_consumer_enable(adc->hwc);
+ 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);
+ iio_hw_consumer_disable(adc->hwc);
+ 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:
+ *val = adc->oversamp;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = adc->sample_freq;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
+static const struct iio_info stm32_dfsdm_info_audio = {
+ .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
+ .read_raw = stm32_dfsdm_read_raw,
+ .write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
+};
+
+static const struct iio_info stm32_dfsdm_info_adc = {
+ .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
+ .read_raw = stm32_dfsdm_read_raw,
+ .write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
+ .validate_trigger = stm32_dfsdm_validate_trigger,
+};
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+ struct iio_dev *indio_dev = arg;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ unsigned int status, int_en;
+
+ regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
+ regmap_read(regmap, DFSDM_CR2(adc->fl_id), &int_en);
+
+ if (status & DFSDM_ISR_REOCF_MASK) {
+ /* Read the data register clean the IRQ status */
+ regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
+ complete(&adc->completion);
+ }
+
+ if (status & DFSDM_ISR_ROVRF_MASK) {
+ if (int_en & DFSDM_CR2_ROVRIE_MASK)
+ dev_warn(&indio_dev->dev, "Overrun detected\n");
+ regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
+ DFSDM_ICR_CLRROVRF_MASK,
+ DFSDM_ICR_CLRROVRF_MASK);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Define external info for SPI Frequency and audio sampling rate that can be
+ * configured by ASoC driver through consumer.h API
+ */
+static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = {
+ /* spi_clk_freq : clock freq on SPI/manchester bus used by channel */
+ {
+ .name = "spi_clk_freq",
+ .shared = IIO_SHARED_BY_TYPE,
+ .read = dfsdm_adc_audio_get_spiclk,
+ .write = dfsdm_adc_audio_set_spiclk,
+ },
+ {},
+};
+
+static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (adc->dma_chan) {
+ dma_free_coherent(adc->dma_chan->device->dev,
+ DFSDM_DMA_BUFFER_SIZE,
+ adc->rx_buf, adc->dma_buf);
+ dma_release_channel(adc->dma_chan);
+ }
+}
+
+static int stm32_dfsdm_dma_request(struct device *dev,
+ struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ adc->dma_chan = dma_request_chan(dev, "rx");
+ if (IS_ERR(adc->dma_chan)) {
+ int ret = PTR_ERR(adc->dma_chan);
+
+ adc->dma_chan = NULL;
+ return ret;
+ }
+
+ adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev,
+ DFSDM_DMA_BUFFER_SIZE,
+ &adc->dma_buf, GFP_KERNEL);
+ if (!adc->rx_buf) {
+ dma_release_channel(adc->dma_chan);
+ return -ENOMEM;
+ }
+
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
+
+ return 0;
+}
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+ struct iio_chan_spec *ch)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch);
+ if (ret < 0)
+ return ret;
+
+ ch->type = IIO_VOLTAGE;
+ ch->indexed = 1;
+
+ /*
+ * IIO_CHAN_INFO_RAW: used to compute regular conversion
+ * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
+ */
+ ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
+
+ if (adc->dev_data->type == DFSDM_AUDIO) {
+ ch->ext_info = dfsdm_adc_audio_ext_info;
+ } else {
+ ch->scan_type.shift = 8;
+ }
+ ch->scan_type.sign = 's';
+ ch->scan_type.realbits = 24;
+ ch->scan_type.storagebits = 32;
+
+ return stm32_dfsdm_chan_configure(adc->dfsdm,
+ &adc->dfsdm->ch_list[ch->channel]);
+}
+
+static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev)
+{
+ struct iio_chan_spec *ch;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_channel *d_ch;
+ int ret;
+
+ ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
+ if (!ch)
+ return -ENOMEM;
+
+ ch->scan_index = 0;
+
+ ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Channels init failed\n");
+ return ret;
+ }
+ ch->info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ);
+
+ d_ch = &adc->dfsdm->ch_list[ch->channel];
+ if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL)
+ adc->spi_freq = adc->dfsdm->spi_master_freq;
+
+ indio_dev->num_channels = 1;
+ indio_dev->channels = ch;
+
+ return stm32_dfsdm_dma_request(dev, indio_dev);
+}
+
+static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
+{
+ struct iio_chan_spec *ch;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int num_ch;
+ int ret, chan_idx;
+
+ adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
+ if (ret < 0)
+ return ret;
+
+ num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
+ "st,adc-channels");
+ if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels\n");
+ return num_ch < 0 ? num_ch : -EINVAL;
+ }
+
+ /* Bind to SD modulator IIO device */
+ adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev);
+ if (IS_ERR(adc->hwc))
+ return -EPROBE_DEFER;
+
+ ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch),
+ GFP_KERNEL);
+ if (!ch)
+ return -ENOMEM;
+
+ for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
+ ch[chan_idx].scan_index = chan_idx;
+ ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Channels init failed\n");
+ return ret;
+ }
+ }
+
+ indio_dev->num_channels = num_ch;
+ indio_dev->channels = ch;
+
+ init_completion(&adc->completion);
+
+ /* Optionally request DMA */
+ ret = stm32_dfsdm_dma_request(dev, indio_dev);
+ if (ret) {
+ if (ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "DMA channel request failed with\n");
+
+ dev_dbg(dev, "No DMA support\n");
+ return 0;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time, NULL,
+ &stm32_dfsdm_buffer_setup_ops);
+ if (ret) {
+ stm32_dfsdm_dma_release(indio_dev);
+ dev_err(&indio_dev->dev, "buffer setup failed\n");
+ return ret;
+ }
+
+ /* lptimer/timer hardware triggers */
+ indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
+
+ return 0;
+}
+
+static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = {
+ .type = DFSDM_IIO,
+ .init = stm32_dfsdm_adc_init,
+};
+
+static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_audio_data = {
+ .type = DFSDM_AUDIO,
+ .init = stm32_dfsdm_audio_init,
+};
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+ {
+ .compatible = "st,stm32-dfsdm-adc",
+ .data = &stm32h7_dfsdm_adc_data,
+ },
+ {
+ .compatible = "st,stm32-dfsdm-dmic",
+ .data = &stm32h7_dfsdm_audio_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match);
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct stm32_dfsdm_adc *adc;
+ struct device_node *np = dev->of_node;
+ const struct stm32_dfsdm_dev_data *dev_data;
+ struct iio_dev *iio;
+ char *name;
+ int ret, irq, val;
+
+ dev_data = of_device_get_match_data(dev);
+ iio = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!iio) {
+ dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(iio);
+ adc->dfsdm = dev_get_drvdata(dev->parent);
+
+ iio->dev.of_node = np;
+ iio->modes = INDIO_DIRECT_MODE;
+
+ platform_set_drvdata(pdev, iio);
+
+ ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
+ if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) {
+ dev_err(dev, "Missing or bad reg property\n");
+ return -EINVAL;
+ }
+
+ name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ if (dev_data->type == DFSDM_AUDIO) {
+ iio->info = &stm32_dfsdm_info_audio;
+ snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", adc->fl_id);
+ } else {
+ iio->info = &stm32_dfsdm_info_adc;
+ snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
+ }
+ iio->name = name;
+
+ /*
+ * In a first step IRQs generated for channels are not treated.
+ * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
+ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
+ 0, pdev->name, iio);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request IRQ\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set filter order\n");
+ return ret;
+ }
+
+ adc->dfsdm->fl_list[adc->fl_id].ford = val;
+
+ ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
+ if (!ret)
+ adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
+
+ adc->dev_data = dev_data;
+ ret = dev_data->init(dev, iio);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(iio);
+ if (ret < 0)
+ goto err_cleanup;
+
+ if (dev_data->type == DFSDM_AUDIO) {
+ ret = of_platform_populate(np, NULL, NULL, dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to find an audio DAI\n");
+ goto err_unregister;
+ }
+ }
+
+ return 0;
+
+err_unregister:
+ iio_device_unregister(iio);
+err_cleanup:
+ stm32_dfsdm_dma_release(iio);
+
+ return ret;
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (adc->dev_data->type == DFSDM_AUDIO)
+ of_platform_depopulate(&pdev->dev);
+ iio_device_unregister(indio_dev);
+ stm32_dfsdm_dma_release(indio_dev);
+
+ return 0;
+}
+
+static int stm32_dfsdm_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ if (iio_buffer_enabled(indio_dev))
+ stm32_dfsdm_predisable(indio_dev);
+
+ return 0;
+}
+
+static int stm32_dfsdm_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ const struct iio_chan_spec *chan;
+ struct stm32_dfsdm_channel *ch;
+ int i, ret;
+
+ /* restore channels configuration */
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ chan = indio_dev->channels + i;
+ ch = &adc->dfsdm->ch_list[chan->channel];
+ ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch);
+ if (ret)
+ return ret;
+ }
+
+ if (iio_buffer_enabled(indio_dev))
+ stm32_dfsdm_postenable(indio_dev);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
+ stm32_dfsdm_adc_suspend,
+ stm32_dfsdm_adc_resume);
+
+static struct platform_driver stm32_dfsdm_adc_driver = {
+ .driver = {
+ .name = "stm32-dfsdm-adc",
+ .of_match_table = stm32_dfsdm_adc_match,
+ .pm = pm_sleep_ptr(&stm32_dfsdm_adc_pm_ops),
+ },
+ .probe = stm32_dfsdm_adc_probe,
+ .remove = stm32_dfsdm_adc_remove,
+};
+module_platform_driver(stm32_dfsdm_adc_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta ADC");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
new file mode 100644
index 000000000..a3d4de6ba
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part the core part STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include "stm32-dfsdm.h"
+
+struct stm32_dfsdm_dev_data {
+ unsigned int num_filters;
+ unsigned int num_channels;
+ const struct regmap_config *regmap_cfg;
+};
+
+#define STM32H7_DFSDM_NUM_FILTERS 4
+#define STM32H7_DFSDM_NUM_CHANNELS 8
+#define STM32MP1_DFSDM_NUM_FILTERS 6
+#define STM32MP1_DFSDM_NUM_CHANNELS 8
+
+static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg < DFSDM_FILTER_BASE_ADR)
+ return false;
+
+ /*
+ * Mask is done on register to avoid to list registers of all
+ * filter instances.
+ */
+ switch (reg & DFSDM_FILTER_REG_MASK) {
+ case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
+ case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
+ case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
+ case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x2B8,
+ .volatile_reg = stm32_dfsdm_volatile_reg,
+ .fast_io = true,
+};
+
+static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
+ .num_filters = STM32H7_DFSDM_NUM_FILTERS,
+ .num_channels = STM32H7_DFSDM_NUM_CHANNELS,
+ .regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x7fc,
+ .volatile_reg = stm32_dfsdm_volatile_reg,
+ .fast_io = true,
+};
+
+static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = {
+ .num_filters = STM32MP1_DFSDM_NUM_FILTERS,
+ .num_channels = STM32MP1_DFSDM_NUM_CHANNELS,
+ .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg,
+};
+
+struct dfsdm_priv {
+ struct platform_device *pdev; /* platform device */
+
+ struct stm32_dfsdm dfsdm; /* common data exported for all instances */
+
+ unsigned int spi_clk_out_div; /* SPI clkout divider value */
+ atomic_t n_active_ch; /* number of current active channels */
+
+ struct clk *clk; /* DFSDM clock */
+ struct clk *aclk; /* audio clock */
+};
+
+static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm)
+{
+ return container_of(dfsdm, struct dfsdm_priv, dfsdm);
+}
+
+static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret || !priv->aclk)
+ return ret;
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret)
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+
+ clk_disable_unprepare(priv->aclk);
+ clk_disable_unprepare(priv->clk);
+}
+
+/**
+ * stm32_dfsdm_start_dfsdm - start global dfsdm interface.
+ *
+ * Enable interface if n_active_ch is not null.
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ struct device *dev = &priv->pdev->dev;
+ unsigned int clk_div = priv->spi_clk_out_div, clk_src;
+ int ret;
+
+ if (atomic_inc_return(&priv->n_active_ch) == 1) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ goto error_ret;
+
+ /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
+ clk_src = priv->aclk ? 1 : 0;
+ ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+ DFSDM_CHCFGR1_CKOUTSRC_MASK,
+ DFSDM_CHCFGR1_CKOUTSRC(clk_src));
+ if (ret < 0)
+ goto pm_put;
+
+ /* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
+ ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+ DFSDM_CHCFGR1_CKOUTDIV_MASK,
+ DFSDM_CHCFGR1_CKOUTDIV(clk_div));
+ if (ret < 0)
+ goto pm_put;
+
+ /* Global enable of DFSDM interface */
+ ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+ DFSDM_CHCFGR1_DFSDMEN_MASK,
+ DFSDM_CHCFGR1_DFSDMEN(1));
+ if (ret < 0)
+ goto pm_put;
+ }
+
+ dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
+ atomic_read(&priv->n_active_ch));
+
+ return 0;
+
+pm_put:
+ pm_runtime_put_sync(dev);
+error_ret:
+ atomic_dec(&priv->n_active_ch);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm);
+
+/**
+ * stm32_dfsdm_stop_dfsdm - stop global DFSDM interface.
+ *
+ * Disable interface if n_active_ch is null
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ */
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ if (atomic_dec_and_test(&priv->n_active_ch)) {
+ /* Global disable of DFSDM interface */
+ ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+ DFSDM_CHCFGR1_DFSDMEN_MASK,
+ DFSDM_CHCFGR1_DFSDMEN(0));
+ if (ret < 0)
+ return ret;
+
+ /* Stop SPI CLKOUT */
+ ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
+ DFSDM_CHCFGR1_CKOUTDIV_MASK,
+ DFSDM_CHCFGR1_CKOUTDIV(0));
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_put_sync(&priv->pdev->dev);
+ }
+ dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+ atomic_read(&priv->n_active_ch));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm);
+
+static int stm32_dfsdm_parse_of(struct platform_device *pdev,
+ struct dfsdm_priv *priv)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct resource *res;
+ unsigned long clk_freq, divider;
+ unsigned int spi_freq, rem;
+ int ret;
+
+ if (!node)
+ return -EINVAL;
+
+ priv->dfsdm.base = devm_platform_get_and_ioremap_resource(pdev, 0,
+ &res);
+ if (IS_ERR(priv->dfsdm.base))
+ return PTR_ERR(priv->dfsdm.base);
+
+ priv->dfsdm.phys_base = res->start;
+
+ /*
+ * "dfsdm" clock is mandatory for DFSDM peripheral clocking.
+ * "dfsdm" or "audio" clocks can be used as source clock for
+ * the SPI clock out signal and internal processing, depending
+ * on use case.
+ */
+ priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk),
+ "Failed to get clock\n");
+
+ priv->aclk = devm_clk_get(&pdev->dev, "audio");
+ if (IS_ERR(priv->aclk))
+ priv->aclk = NULL;
+
+ if (priv->aclk)
+ clk_freq = clk_get_rate(priv->aclk);
+ else
+ clk_freq = clk_get_rate(priv->clk);
+
+ /* SPI clock out frequency */
+ ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
+ &spi_freq);
+ if (ret < 0) {
+ /* No SPI master mode */
+ return 0;
+ }
+
+ divider = div_u64_rem(clk_freq, spi_freq, &rem);
+ /* Round up divider when ckout isn't precise, not to exceed spi_freq */
+ if (rem)
+ divider++;
+
+ /* programmable divider is in range of [2:256] */
+ if (divider < 2 || divider > 256) {
+ dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
+ return -EINVAL;
+ }
+
+ /* SPI clock output divider is: divider = CKOUTDIV + 1 */
+ priv->spi_clk_out_div = divider - 1;
+ priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);
+
+ if (rem) {
+ dev_warn(&pdev->dev, "SPI clock not accurate\n");
+ dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
+ clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
+ }
+
+ return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+ {
+ .compatible = "st,stm32h7-dfsdm",
+ .data = &stm32h7_dfsdm_data,
+ },
+ {
+ .compatible = "st,stm32mp1-dfsdm",
+ .data = &stm32mp1_dfsdm_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+
+static int stm32_dfsdm_probe(struct platform_device *pdev)
+{
+ struct dfsdm_priv *priv;
+ const struct stm32_dfsdm_dev_data *dev_data;
+ struct stm32_dfsdm *dfsdm;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->pdev = pdev;
+
+ dev_data = of_device_get_match_data(&pdev->dev);
+
+ dfsdm = &priv->dfsdm;
+ dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
+ sizeof(*dfsdm->fl_list), GFP_KERNEL);
+ if (!dfsdm->fl_list)
+ return -ENOMEM;
+
+ dfsdm->num_fls = dev_data->num_filters;
+ dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
+ sizeof(*dfsdm->ch_list),
+ GFP_KERNEL);
+ if (!dfsdm->ch_list)
+ return -ENOMEM;
+ dfsdm->num_chs = dev_data->num_channels;
+
+ ret = stm32_dfsdm_parse_of(pdev, priv);
+ if (ret < 0)
+ return ret;
+
+ dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm",
+ dfsdm->base,
+ dev_data->regmap_cfg);
+ if (IS_ERR(dfsdm->regmap)) {
+ ret = PTR_ERR(dfsdm->regmap);
+ dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, dfsdm);
+
+ ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to start clock\n");
+ return ret;
+ }
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ goto pm_put;
+
+ pm_runtime_put(&pdev->dev);
+
+ return 0;
+
+pm_put:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return ret;
+}
+
+static int stm32_dfsdm_core_remove(struct platform_device *pdev)
+{
+ struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int stm32_dfsdm_core_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Balance devm_regmap_init_mmio_clk() clk_prepare() */
+ clk_unprepare(priv->clk);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int stm32_dfsdm_core_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare(priv->clk);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_resume(dev);
+}
+
+static int stm32_dfsdm_core_runtime_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int stm32_dfsdm_core_runtime_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ return stm32_dfsdm_clk_prepare_enable(dfsdm);
+}
+
+static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend, stm32_dfsdm_core_resume)
+ RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
+ stm32_dfsdm_core_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver stm32_dfsdm_driver = {
+ .probe = stm32_dfsdm_probe,
+ .remove = stm32_dfsdm_core_remove,
+ .driver = {
+ .name = "stm32-dfsdm",
+ .of_match_table = stm32_dfsdm_of_match,
+ .pm = pm_ptr(&stm32_dfsdm_core_pm_ops),
+ },
+};
+
+module_platform_driver(stm32_dfsdm_driver);
+
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
new file mode 100644
index 000000000..4afc1f528
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -0,0 +1,326 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of STM32 DFSDM driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ */
+
+#ifndef MDF_STM32_DFSDM__H
+#define MDF_STM32_DFSDM__H
+
+#include <linux/bitfield.h>
+
+/*
+ * STM32 DFSDM - global register map
+ * ________________________________________________________
+ * | Offset | Registers block |
+ * --------------------------------------------------------
+ * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
+ * --------------------------------------------------------
+ * | 0x020 | CHANNEL 1 |
+ * --------------------------------------------------------
+ * | ... | ..... |
+ * --------------------------------------------------------
+ * | 0x0E0 | CHANNEL 7 |
+ * --------------------------------------------------------
+ * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
+ * --------------------------------------------------------
+ * | 0x200 | FILTER 1 |
+ * --------------------------------------------------------
+ * | 0x300 | FILTER 2 |
+ * --------------------------------------------------------
+ * | 0x400 | FILTER 3 |
+ * --------------------------------------------------------
+ */
+
+/*
+ * Channels register definitions
+ */
+#define DFSDM_CHCFGR1(y) ((y) * 0x20 + 0x00)
+#define DFSDM_CHCFGR2(y) ((y) * 0x20 + 0x04)
+#define DFSDM_AWSCDR(y) ((y) * 0x20 + 0x08)
+#define DFSDM_CHWDATR(y) ((y) * 0x20 + 0x0C)
+#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
+
+/* CHCFGR1: Channel configuration register 1 */
+#define DFSDM_CHCFGR1_SITP_MASK GENMASK(1, 0)
+#define DFSDM_CHCFGR1_SITP(v) FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
+#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
+#define DFSDM_CHCFGR1_SPICKSEL(v) FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
+#define DFSDM_CHCFGR1_SCDEN_MASK BIT(5)
+#define DFSDM_CHCFGR1_SCDEN(v) FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
+#define DFSDM_CHCFGR1_CKABEN_MASK BIT(6)
+#define DFSDM_CHCFGR1_CKABEN(v) FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
+#define DFSDM_CHCFGR1_CHEN_MASK BIT(7)
+#define DFSDM_CHCFGR1_CHEN(v) FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
+#define DFSDM_CHCFGR1_CHINSEL_MASK BIT(8)
+#define DFSDM_CHCFGR1_CHINSEL(v) FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
+#define DFSDM_CHCFGR1_DATMPX_MASK GENMASK(13, 12)
+#define DFSDM_CHCFGR1_DATMPX(v) FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
+#define DFSDM_CHCFGR1_DATPACK_MASK GENMASK(15, 14)
+#define DFSDM_CHCFGR1_DATPACK(v) FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
+#define DFSDM_CHCFGR1_CKOUTDIV(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
+#define DFSDM_CHCFGR1_CKOUTSRC(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
+#define DFSDM_CHCFGR1_DFSDMEN_MASK BIT(31)
+#define DFSDM_CHCFGR1_DFSDMEN(v) FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
+
+/* CHCFGR2: Channel configuration register 2 */
+#define DFSDM_CHCFGR2_DTRBS_MASK GENMASK(7, 3)
+#define DFSDM_CHCFGR2_DTRBS(v) FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
+#define DFSDM_CHCFGR2_OFFSET_MASK GENMASK(31, 8)
+#define DFSDM_CHCFGR2_OFFSET(v) FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
+
+/* AWSCDR: Channel analog watchdog and short circuit detector */
+#define DFSDM_AWSCDR_SCDT_MASK GENMASK(7, 0)
+#define DFSDM_AWSCDR_SCDT(v) FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
+#define DFSDM_AWSCDR_BKSCD_MASK GENMASK(15, 12)
+#define DFSDM_AWSCDR_BKSCD(v) FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
+#define DFSDM_AWSCDR_AWFOSR_MASK GENMASK(20, 16)
+#define DFSDM_AWSCDR_AWFOSR(v) FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
+#define DFSDM_AWSCDR_AWFORD_MASK GENMASK(23, 22)
+#define DFSDM_AWSCDR_AWFORD(v) FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
+
+/*
+ * Filters register definitions
+ */
+#define DFSDM_FILTER_BASE_ADR 0x100
+#define DFSDM_FILTER_REG_MASK 0x7F
+#define DFSDM_FILTER_X_BASE_ADR(x) ((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
+
+#define DFSDM_CR1(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x00)
+#define DFSDM_CR2(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x04)
+#define DFSDM_ISR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x08)
+#define DFSDM_ICR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x0C)
+#define DFSDM_JCHGR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x10)
+#define DFSDM_FCR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x14)
+#define DFSDM_JDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x18)
+#define DFSDM_RDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x1C)
+#define DFSDM_AWHTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x20)
+#define DFSDM_AWLTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x24)
+#define DFSDM_AWSR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x28)
+#define DFSDM_AWCFR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x2C)
+#define DFSDM_EXMAX(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x30)
+#define DFSDM_EXMIN(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x34)
+#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x38)
+
+/* CR1 Control register 1 */
+#define DFSDM_CR1_DFEN_MASK BIT(0)
+#define DFSDM_CR1_DFEN(v) FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
+#define DFSDM_CR1_JSWSTART_MASK BIT(1)
+#define DFSDM_CR1_JSWSTART(v) FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
+#define DFSDM_CR1_JSYNC_MASK BIT(3)
+#define DFSDM_CR1_JSYNC(v) FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
+#define DFSDM_CR1_JSCAN_MASK BIT(4)
+#define DFSDM_CR1_JSCAN(v) FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
+#define DFSDM_CR1_JDMAEN_MASK BIT(5)
+#define DFSDM_CR1_JDMAEN(v) FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
+#define DFSDM_CR1_JEXTSEL_MASK GENMASK(12, 8)
+#define DFSDM_CR1_JEXTSEL(v) FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
+#define DFSDM_CR1_JEXTEN_MASK GENMASK(14, 13)
+#define DFSDM_CR1_JEXTEN(v) FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
+#define DFSDM_CR1_RSWSTART_MASK BIT(17)
+#define DFSDM_CR1_RSWSTART(v) FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
+#define DFSDM_CR1_RCONT_MASK BIT(18)
+#define DFSDM_CR1_RCONT(v) FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
+#define DFSDM_CR1_RSYNC_MASK BIT(19)
+#define DFSDM_CR1_RSYNC(v) FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
+#define DFSDM_CR1_RDMAEN_MASK BIT(21)
+#define DFSDM_CR1_RDMAEN(v) FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
+#define DFSDM_CR1_RCH_MASK GENMASK(26, 24)
+#define DFSDM_CR1_RCH(v) FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
+#define DFSDM_CR1_FAST_MASK BIT(29)
+#define DFSDM_CR1_FAST(v) FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
+#define DFSDM_CR1_AWFSEL_MASK BIT(30)
+#define DFSDM_CR1_AWFSEL(v) FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
+
+/* CR2: Control register 2 */
+#define DFSDM_CR2_IE_MASK GENMASK(6, 0)
+#define DFSDM_CR2_IE(v) FIELD_PREP(DFSDM_CR2_IE_MASK, v)
+#define DFSDM_CR2_JEOCIE_MASK BIT(0)
+#define DFSDM_CR2_JEOCIE(v) FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
+#define DFSDM_CR2_REOCIE_MASK BIT(1)
+#define DFSDM_CR2_REOCIE(v) FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
+#define DFSDM_CR2_JOVRIE_MASK BIT(2)
+#define DFSDM_CR2_JOVRIE(v) FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
+#define DFSDM_CR2_ROVRIE_MASK BIT(3)
+#define DFSDM_CR2_ROVRIE(v) FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
+#define DFSDM_CR2_AWDIE_MASK BIT(4)
+#define DFSDM_CR2_AWDIE(v) FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
+#define DFSDM_CR2_SCDIE_MASK BIT(5)
+#define DFSDM_CR2_SCDIE(v) FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
+#define DFSDM_CR2_CKABIE_MASK BIT(6)
+#define DFSDM_CR2_CKABIE(v) FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
+#define DFSDM_CR2_EXCH_MASK GENMASK(15, 8)
+#define DFSDM_CR2_EXCH(v) FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
+#define DFSDM_CR2_AWDCH_MASK GENMASK(23, 16)
+#define DFSDM_CR2_AWDCH(v) FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
+
+/* ISR: Interrupt status register */
+#define DFSDM_ISR_JEOCF_MASK BIT(0)
+#define DFSDM_ISR_JEOCF(v) FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
+#define DFSDM_ISR_REOCF_MASK BIT(1)
+#define DFSDM_ISR_REOCF(v) FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
+#define DFSDM_ISR_JOVRF_MASK BIT(2)
+#define DFSDM_ISR_JOVRF(v) FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
+#define DFSDM_ISR_ROVRF_MASK BIT(3)
+#define DFSDM_ISR_ROVRF(v) FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
+#define DFSDM_ISR_AWDF_MASK BIT(4)
+#define DFSDM_ISR_AWDF(v) FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
+#define DFSDM_ISR_JCIP_MASK BIT(13)
+#define DFSDM_ISR_JCIP(v) FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
+#define DFSDM_ISR_RCIP_MASK BIT(14)
+#define DFSDM_ISR_RCIP(v) FIELD_PREP(DFSDM_ISR_RCIP, v)
+#define DFSDM_ISR_CKABF_MASK GENMASK(23, 16)
+#define DFSDM_ISR_CKABF(v) FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
+#define DFSDM_ISR_SCDF_MASK GENMASK(31, 24)
+#define DFSDM_ISR_SCDF(v) FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
+
+/* ICR: Interrupt flag clear register */
+#define DFSDM_ICR_CLRJOVRF_MASK BIT(2)
+#define DFSDM_ICR_CLRJOVRF(v) FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
+#define DFSDM_ICR_CLRROVRF_MASK BIT(3)
+#define DFSDM_ICR_CLRROVRF(v) FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_MASK GENMASK(23, 16)
+#define DFSDM_ICR_CLRCKABF(v) FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
+#define DFSDM_ICR_CLRCKABF_CH(v, y) \
+ (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
+#define DFSDM_ICR_CLRSCDF_MASK GENMASK(31, 24)
+#define DFSDM_ICR_CLRSCDF(v) FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
+#define DFSDM_ICR_CLRSCDF_CH_MASK(y) BIT(24 + (y))
+#define DFSDM_ICR_CLRSCDF_CH(v, y) \
+ (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
+
+/* FCR: Filter control register */
+#define DFSDM_FCR_IOSR_MASK GENMASK(7, 0)
+#define DFSDM_FCR_IOSR(v) FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
+#define DFSDM_FCR_FOSR_MASK GENMASK(25, 16)
+#define DFSDM_FCR_FOSR(v) FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
+#define DFSDM_FCR_FORD_MASK GENMASK(31, 29)
+#define DFSDM_FCR_FORD(v) FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
+
+/* RDATAR: Filter data register for regular channel */
+#define DFSDM_DATAR_CH_MASK GENMASK(2, 0)
+#define DFSDM_DATAR_DATA_OFFSET 8
+#define DFSDM_DATAR_DATA_MASK GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
+
+/* AWLTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWLTR_BKAWL_MASK GENMASK(3, 0)
+#define DFSDM_AWLTR_BKAWL(v) FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
+#define DFSDM_AWLTR_AWLT_MASK GENMASK(31, 8)
+#define DFSDM_AWLTR_AWLT(v) FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
+
+/* AWHTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWHTR_BKAWH_MASK GENMASK(3, 0)
+#define DFSDM_AWHTR_BKAWH(v) FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
+#define DFSDM_AWHTR_AWHT_MASK GENMASK(31, 8)
+#define DFSDM_AWHTR_AWHT(v) FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
+
+/* AWSR: Filter watchdog status register */
+#define DFSDM_AWSR_AWLTF_MASK GENMASK(7, 0)
+#define DFSDM_AWSR_AWLTF(v) FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
+#define DFSDM_AWSR_AWHTF_MASK GENMASK(15, 8)
+#define DFSDM_AWSR_AWHTF(v) FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
+
+/* AWCFR: Filter watchdog status register */
+#define DFSDM_AWCFR_AWLTF_MASK GENMASK(7, 0)
+#define DFSDM_AWCFR_AWLTF(v) FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
+#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8)
+#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+
+/* DFSDM filter order */
+enum stm32_dfsdm_sinc_order {
+ DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
+ DFSDM_SINC1_ORDER, /* Sinc 1 filter type */
+ DFSDM_SINC2_ORDER, /* Sinc 2 filter type */
+ DFSDM_SINC3_ORDER, /* Sinc 3 filter type */
+ DFSDM_SINC4_ORDER, /* Sinc 4 filter type (N.A. for watchdog) */
+ DFSDM_SINC5_ORDER, /* Sinc 5 filter type (N.A. for watchdog) */
+ DFSDM_NB_SINC_ORDER,
+};
+
+/**
+ * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling
+ * @iosr: integrator oversampling
+ * @fosr: filter oversampling
+ * @rshift: output sample right shift (hardware shift)
+ * @lshift: output sample left shift (software shift)
+ * @res: output sample resolution
+ * @bits: output sample resolution in bits
+ * @max: output sample maximum positive value
+ */
+struct stm32_dfsdm_filter_osr {
+ unsigned int iosr;
+ unsigned int fosr;
+ unsigned int rshift;
+ unsigned int lshift;
+ u64 res;
+ u32 bits;
+ s32 max;
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * @ford: filter order
+ * @flo: filter oversampling data table indexed by fast mode flag
+ * @sync_mode: filter synchronized with filter 0
+ * @fast: filter fast mode
+ */
+struct stm32_dfsdm_filter {
+ enum stm32_dfsdm_sinc_order ford;
+ struct stm32_dfsdm_filter_osr flo[2];
+ unsigned int sync_mode;
+ unsigned int fast;
+};
+
+/**
+ * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
+ * @id: id of the channel
+ * @type: interface type linked to stm32_dfsdm_chan_type
+ * @src: interface type linked to stm32_dfsdm_chan_src
+ * @alt_si: alternative serial input interface
+ */
+struct stm32_dfsdm_channel {
+ unsigned int id;
+ unsigned int type;
+ unsigned int src;
+ unsigned int alt_si;
+};
+
+/**
+ * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
+ * @base: control registers base cpu addr
+ * @phys_base: DFSDM IP register physical address
+ * @regmap: regmap for register read/write
+ * @fl_list: filter resources list
+ * @num_fls: number of filter resources available
+ * @ch_list: channel resources list
+ * @num_chs: number of channel resources available
+ * @spi_master_freq: SPI clock out frequency
+ */
+struct stm32_dfsdm {
+ void __iomem *base;
+ phys_addr_t phys_base;
+ struct regmap *regmap;
+ struct stm32_dfsdm_filter *fl_list;
+ unsigned int num_fls;
+ struct stm32_dfsdm_channel *ch_list;
+ unsigned int num_chs;
+ unsigned int spi_master_freq;
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+ DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+ DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+ DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+ DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
+int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
+
+#endif
diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c
new file mode 100644
index 000000000..67518e460
--- /dev/null
+++ b/drivers/iio/adc/stmpe-adc.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STMPE811 IIO ADC Driver
+ *
+ * 4 channel, 10/12-bit ADC
+ *
+ * Copyright (C) 2013-2018 Toradex AG <stefan.agner@toradex.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_INT_EN 0x0E
+#define STMPE_REG_ADC_INT_STA 0x0F
+
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_ADC_CAPT 0x22
+#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel))
+
+#define STMPE_REG_TEMP_CTRL 0x60
+#define STMPE_TEMP_CTRL_ENABLE BIT(0)
+#define STMPE_TEMP_CTRL_ACQ BIT(1)
+#define STMPE_TEMP_CTRL_THRES_EN BIT(3)
+#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \
+ STMPE_TEMP_CTRL_ACQ | \
+ STMPE_TEMP_CTRL_THRES_EN)
+#define STMPE_REG_TEMP_DATA 0x61
+#define STMPE_REG_TEMP_TH 0x63
+#define STMPE_ADC_LAST_NR 7
+#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1)
+
+#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff)
+
+#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000)
+
+struct stmpe_adc {
+ struct stmpe *stmpe;
+ struct clk *clk;
+ struct device *dev;
+ struct mutex lock;
+
+ /* We are allocating plus one for the temperature channel */
+ struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2];
+
+ struct completion completion;
+
+ u8 channel;
+ u32 value;
+};
+
+static int stmpe_read_voltage(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ unsigned long ret;
+
+ mutex_lock(&info->lock);
+
+ reinit_completion(&info->completion);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel > STMPE_ADC_LAST_NR) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT,
+ STMPE_ADC_CH(info->channel));
+
+ ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret == 0) {
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
+ STMPE_ADC_CH(info->channel));
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+
+ *val = info->value;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_temp(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ unsigned long ret;
+
+ mutex_lock(&info->lock);
+
+ reinit_completion(&info->completion);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel != STMPE_TEMP_CHANNEL) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL,
+ STMPE_START_ONE_TEMP_CONV);
+
+ ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret == 0) {
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * absolute temp = +V3.3 * value /7.51 [K]
+ * scale to [milli °C]
+ */
+ *val = ((449960l * info->value) / 1024l) - 273150;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct stmpe_adc *info = iio_priv(indio_dev);
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = stmpe_read_voltage(info, chan, val);
+ break;
+
+ case IIO_TEMP:
+ ret = stmpe_read_temp(info, chan, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 3300;
+ *val2 = info->stmpe->mod_12b ? 12 : 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
+{
+ struct stmpe_adc *info = (struct stmpe_adc *)dev_id;
+ __be16 data;
+
+ if (info->channel <= STMPE_ADC_LAST_NR) {
+ int int_sta;
+
+ int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA);
+
+ /* Is the interrupt relevant */
+ if (!(int_sta & STMPE_ADC_CH(info->channel)))
+ return IRQ_NONE;
+
+ /* Read value */
+ stmpe_block_read(info->stmpe,
+ STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data);
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta);
+ } else if (info->channel == STMPE_TEMP_CHANNEL) {
+ /* Read value */
+ stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2,
+ (u8 *) &data);
+ } else {
+ return IRQ_NONE;
+ }
+
+ info->value = (u32) be16_to_cpu(data);
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info stmpe_adc_iio_info = {
+ .read_raw = &stmpe_read_raw,
+};
+
+static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_VOLTAGE;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_TEMP;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static int stmpe_adc_init_hw(struct stmpe_adc *adc)
+{
+ int ret;
+ struct stmpe *stmpe = adc->stmpe;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(stmpe->dev, "Could not enable clock for ADC\n");
+ return ret;
+ }
+
+ ret = stmpe811_adc_common_init(stmpe);
+ if (ret) {
+ stmpe_disable(stmpe, STMPE_BLOCK_ADC);
+ return ret;
+ }
+
+ /* use temp irq for each conversion completion */
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0);
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0);
+
+ return 0;
+}
+
+static int stmpe_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stmpe_adc *info;
+ struct device_node *np;
+ u32 norequest_mask = 0;
+ unsigned long bits;
+ int irq_temp, irq_adc;
+ int num_chan = 0;
+ int i = 0;
+ int ret;
+
+ irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC");
+ if (irq_adc < 0)
+ return irq_adc;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ mutex_init(&info->lock);
+
+ init_completion(&info->completion);
+ ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+ irq_adc);
+ return ret;
+ }
+
+ irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS");
+ if (irq_temp >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed requesting irq for"
+ " temp sensor, irq = %d\n", irq_temp);
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &stmpe_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ info->stmpe = dev_get_drvdata(pdev->dev.parent);
+
+ np = pdev->dev.of_node;
+
+ if (!np)
+ dev_err(&pdev->dev, "no device tree node found\n");
+
+ of_property_read_u32(np, "st,norequest-mask", &norequest_mask);
+
+ bits = norequest_mask;
+ for_each_clear_bit(i, &bits, (STMPE_ADC_LAST_NR + 1)) {
+ stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ }
+ stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ indio_dev->channels = info->stmpe_adc_iio_channels;
+ indio_dev->num_channels = num_chan;
+
+ ret = stmpe_adc_init_hw(info);
+ if (ret)
+ return ret;
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN,
+ ~(norequest_mask & 0xFF));
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
+ ~(norequest_mask & 0xFF));
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static int stmpe_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stmpe_adc *info = iio_priv(indio_dev);
+
+ stmpe_adc_init_hw(info);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume);
+
+static const struct of_device_id stmpe_adc_ids[] = {
+ { .compatible = "st,stmpe-adc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stmpe_adc_ids);
+
+static struct platform_driver stmpe_adc_driver = {
+ .probe = stmpe_adc_probe,
+ .driver = {
+ .name = "stmpe-adc",
+ .pm = pm_sleep_ptr(&stmpe_adc_pm_ops),
+ .of_match_table = stmpe_adc_ids,
+ },
+};
+module_platform_driver(stmpe_adc_driver);
+
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
+MODULE_DESCRIPTION("STMPEXXX ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stmpe-adc");
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
new file mode 100644
index 000000000..a6ade70de
--- /dev/null
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
+ *
+ * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * The Allwinner SoCs all have an ADC that can also act as a touchscreen
+ * controller and a thermal sensor.
+ * The thermal sensor works only when the ADC acts as a touchscreen controller
+ * and is configured to throw an interrupt every fixed periods of time (let say
+ * every X seconds).
+ * One would be tempted to disable the IP on the hardware side rather than
+ * disabling interrupts to save some power but that resets the internal clock of
+ * the IP, resulting in having to wait X seconds every time we want to read the
+ * value of the thermal sensor.
+ * This is also the reason of using autosuspend in pm_runtime. If there was no
+ * autosuspend, the thermal sensor would need X seconds after every
+ * pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the
+ * thermal sensor to be requested again in a certain time span before it gets
+ * shutdown for not being used.
+ */
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/sun4i-gpadc.h>
+
+static unsigned int sun4i_gpadc_chan_select(unsigned int chan)
+{
+ return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
+}
+
+static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
+{
+ return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
+}
+
+struct gpadc_data {
+ int temp_offset;
+ int temp_scale;
+ unsigned int tp_mode_en;
+ unsigned int tp_adc_select;
+ unsigned int (*adc_chan_select)(unsigned int chan);
+ unsigned int adc_chan_mask;
+};
+
+static const struct gpadc_data sun4i_gpadc_data = {
+ .temp_offset = -1932,
+ .temp_scale = 133,
+ .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
+ .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
+ .adc_chan_select = &sun4i_gpadc_chan_select,
+ .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
+};
+
+static const struct gpadc_data sun5i_gpadc_data = {
+ .temp_offset = -1447,
+ .temp_scale = 100,
+ .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
+ .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
+ .adc_chan_select = &sun4i_gpadc_chan_select,
+ .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
+};
+
+static const struct gpadc_data sun6i_gpadc_data = {
+ .temp_offset = -1623,
+ .temp_scale = 167,
+ .tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
+ .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
+ .adc_chan_select = &sun6i_gpadc_chan_select,
+ .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
+};
+
+static const struct gpadc_data sun8i_a33_gpadc_data = {
+ .temp_offset = -1662,
+ .temp_scale = 162,
+ .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
+};
+
+struct sun4i_gpadc_iio {
+ struct iio_dev *indio_dev;
+ struct completion completion;
+ int temp_data;
+ u32 adc_data;
+ struct regmap *regmap;
+ unsigned int fifo_data_irq;
+ atomic_t ignore_fifo_data_irq;
+ unsigned int temp_data_irq;
+ atomic_t ignore_temp_data_irq;
+ const struct gpadc_data *data;
+ bool no_irq;
+ /* prevents concurrent reads of temperature and ADC */
+ struct mutex mutex;
+ struct thermal_zone_device *tzd;
+ struct device *sensor_device;
+};
+
+#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _name, \
+}
+
+static struct iio_map sun4i_gpadc_hwmon_maps[] = {
+ {
+ .adc_channel_label = "temp_adc",
+ .consumer_dev_name = "iio_hwmon.0",
+ },
+ { /* sentinel */ },
+};
+
+static const struct iio_chan_spec sun4i_gpadc_channels[] = {
+ SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
+ SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
+ SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
+ SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "temp_adc",
+ },
+};
+
+static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
+ SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
+ SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
+ SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
+ SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
+};
+
+static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "temp_adc",
+ },
+};
+
+static const struct regmap_config sun4i_gpadc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
+ unsigned int irq)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ int ret;
+ u32 reg;
+
+ pm_runtime_get_sync(indio_dev->dev.parent);
+
+ reinit_completion(&info->completion);
+
+ ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
+ SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) |
+ SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, &reg);
+ if (ret)
+ return ret;
+
+ if (irq == info->fifo_data_irq) {
+ ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
+ info->data->tp_mode_en |
+ info->data->tp_adc_select |
+ info->data->adc_chan_select(channel));
+ /*
+ * When the IP changes channel, it needs a bit of time to get
+ * correct values.
+ */
+ if ((reg & info->data->adc_chan_mask) !=
+ info->data->adc_chan_select(channel))
+ mdelay(10);
+
+ } else {
+ /*
+ * The temperature sensor returns valid data only when the ADC
+ * operates in touchscreen mode.
+ */
+ ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
+ info->data->tp_mode_en);
+ }
+
+ if (ret)
+ return ret;
+
+ /*
+ * When the IP changes mode between ADC or touchscreen, it
+ * needs a bit of time to get correct values.
+ */
+ if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select)
+ mdelay(100);
+
+ return 0;
+}
+
+static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
+ unsigned int irq)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&info->mutex);
+
+ ret = sun4i_prepare_for_irq(indio_dev, channel, irq);
+ if (ret)
+ goto err;
+
+ enable_irq(irq);
+
+ /*
+ * The temperature sensor throws an interruption periodically (currently
+ * set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay
+ * makes sure an interruption occurs in normal conditions. If it doesn't
+ * occur, then there is a timeout.
+ */
+ if (!wait_for_completion_timeout(&info->completion,
+ msecs_to_jiffies(1000))) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (irq == info->fifo_data_irq)
+ *val = info->adc_data;
+ else
+ *val = info->temp_data;
+
+ ret = 0;
+ pm_runtime_mark_last_busy(indio_dev->dev.parent);
+
+err:
+ pm_runtime_put_autosuspend(indio_dev->dev.parent);
+ disable_irq(irq);
+ mutex_unlock(&info->mutex);
+
+ return ret;
+}
+
+static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
+ int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
+}
+
+static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ if (info->no_irq) {
+ pm_runtime_get_sync(indio_dev->dev.parent);
+
+ regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
+
+ pm_runtime_mark_last_busy(indio_dev->dev.parent);
+ pm_runtime_put_autosuspend(indio_dev->dev.parent);
+
+ return 0;
+ }
+
+ return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
+}
+
+static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ *val = info->data->temp_offset;
+
+ return 0;
+}
+
+static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ *val = info->data->temp_scale;
+
+ return 0;
+}
+
+static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ ret = sun4i_gpadc_temp_offset(indio_dev, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_VOLTAGE)
+ ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
+ val);
+ else
+ ret = sun4i_gpadc_temp_read(indio_dev, val);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLTAGE) {
+ /* 3000mV / 4096 * raw */
+ *val = 0;
+ *val2 = 732421875;
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ ret = sun4i_gpadc_temp_scale(indio_dev, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info sun4i_gpadc_iio_info = {
+ .read_raw = sun4i_gpadc_read_raw,
+};
+
+static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id)
+{
+ struct sun4i_gpadc_iio *info = dev_id;
+
+ if (atomic_read(&info->ignore_temp_data_irq))
+ goto out;
+
+ if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data))
+ complete(&info->completion);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
+{
+ struct sun4i_gpadc_iio *info = dev_id;
+
+ if (atomic_read(&info->ignore_fifo_data_irq))
+ goto out;
+
+ if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data))
+ complete(&info->completion);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int sun4i_gpadc_runtime_suspend(struct device *dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+
+ /* Disable the ADC on IP */
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
+ /* Disable temperature sensor on IP */
+ regmap_write(info->regmap, SUN4I_GPADC_TPR, 0);
+
+ return 0;
+}
+
+static int sun4i_gpadc_runtime_resume(struct device *dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+
+ /* clkin = 6MHz */
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
+ SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
+ SUN4I_GPADC_CTRL0_FS_DIV(7) |
+ SUN4I_GPADC_CTRL0_T_ACQ(63));
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
+ SUN4I_GPADC_CTRL3_FILTER_EN |
+ SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
+ /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
+ regmap_write(info->regmap, SUN4I_GPADC_TPR,
+ SUN4I_GPADC_TPR_TEMP_ENABLE |
+ SUN4I_GPADC_TPR_TEMP_PERIOD(800));
+
+ return 0;
+}
+
+static int sun4i_gpadc_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ struct sun4i_gpadc_iio *info = tz->devdata;
+ int val, scale, offset;
+
+ if (sun4i_gpadc_temp_read(info->indio_dev, &val))
+ return -ETIMEDOUT;
+
+ sun4i_gpadc_temp_scale(info->indio_dev, &scale);
+ sun4i_gpadc_temp_offset(info->indio_dev, &offset);
+
+ *temp = (val + offset) * scale;
+
+ return 0;
+}
+
+static const struct thermal_zone_device_ops sun4i_ts_tz_ops = {
+ .get_temp = &sun4i_gpadc_get_temp,
+};
+
+static const struct dev_pm_ops sun4i_gpadc_pm_ops = {
+ .runtime_suspend = &sun4i_gpadc_runtime_suspend,
+ .runtime_resume = &sun4i_gpadc_runtime_resume,
+};
+
+static int sun4i_irq_init(struct platform_device *pdev, const char *name,
+ irq_handler_t handler, const char *devname,
+ unsigned int *irq, atomic_t *atomic)
+{
+ int ret;
+ struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
+
+ /*
+ * Once the interrupt is activated, the IP continuously performs
+ * conversions thus throws interrupts. The interrupt is activated right
+ * after being requested but we want to control when these interrupts
+ * occur thus we disable it right after being requested. However, an
+ * interrupt might occur between these two instructions and we have to
+ * make sure that does not happen, by using atomic flags. We set the
+ * flag before requesting the interrupt and unset it right after
+ * disabling the interrupt. When an interrupt occurs between these two
+ * instructions, reading the atomic flag will tell us to ignore the
+ * interrupt.
+ */
+ atomic_set(atomic, 1);
+
+ ret = platform_get_irq_byname(pdev, name);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get virq for irq %s\n", name);
+ return ret;
+ }
+
+ *irq = ret;
+ ret = devm_request_any_context_irq(&pdev->dev, *irq, handler,
+ IRQF_NO_AUTOEN,
+ devname, info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not request %s interrupt: %d\n",
+ name, ret);
+ return ret;
+ }
+
+ atomic_set(atomic, 0);
+
+ return 0;
+}
+
+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,
+ struct iio_dev *indio_dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ void __iomem *base;
+ int ret;
+
+ info->data = of_device_get_match_data(&pdev->dev);
+ if (!info->data)
+ return -ENODEV;
+
+ info->no_irq = true;
+ indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
+ indio_dev->channels = sun8i_a33_gpadc_channels;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sun4i_gpadc_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ ret = PTR_ERR(info->regmap);
+ dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF))
+ info->sensor_device = &pdev->dev;
+
+ return 0;
+}
+
+static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
+ struct iio_dev *indio_dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ struct sun4i_gpadc_dev *sun4i_gpadc_dev =
+ dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ info->no_irq = false;
+ info->regmap = sun4i_gpadc_dev->regmap;
+
+ indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
+ indio_dev->channels = sun4i_gpadc_channels;
+
+ info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data;
+
+ /*
+ * Since the controller needs to be in touchscreen mode for its thermal
+ * sensor to operate properly, and that switching between the two modes
+ * needs a delay, always registering in the thermal framework will
+ * significantly slow down the conversion rate of the ADCs.
+ *
+ * Therefore, instead of depending on THERMAL_OF in Kconfig, we only
+ * register the sensor if that option is enabled, eventually leaving
+ * that choice to the user.
+ */
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ /*
+ * This driver is a child of an MFD which has a node in the DT
+ * but not its children, because of DT backward compatibility
+ * for A10, A13 and A31 SoCs. Therefore, the resulting devices
+ * of this driver do not have an of_node variable.
+ * However, its parent (the MFD driver) has an of_node variable
+ * and since devm_thermal_zone_of_sensor_register uses its first
+ * argument to match the phandle defined in the node of the
+ * thermal driver with the of_node of the device passed as first
+ * argument and the third argument to call ops from
+ * thermal_zone_of_device_ops, the solution is to use the parent
+ * device as first argument to match the phandle with its
+ * of_node, and the device from this driver as third argument to
+ * return the temperature.
+ */
+ info->sensor_device = pdev->dev.parent;
+ } else {
+ indio_dev->num_channels =
+ ARRAY_SIZE(sun4i_gpadc_channels_no_temp);
+ indio_dev->channels = sun4i_gpadc_channels_no_temp;
+ }
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
+ sun4i_gpadc_temp_data_irq_handler,
+ "temp_data", &info->temp_data_irq,
+ &info->ignore_temp_data_irq);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
+ sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
+ &info->fifo_data_irq, &info->ignore_fifo_data_irq);
+ if (ret < 0)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to register iio map array\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sun4i_gpadc_probe(struct platform_device *pdev)
+{
+ struct sun4i_gpadc_iio *info;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ platform_set_drvdata(pdev, indio_dev);
+
+ mutex_init(&info->mutex);
+ info->indio_dev = indio_dev;
+ init_completion(&info->completion);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &sun4i_gpadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (pdev->dev.of_node)
+ ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
+ else
+ ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
+
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ SUN4I_GPADC_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ info->tzd = devm_thermal_of_zone_register(info->sensor_device,
+ 0, info,
+ &sun4i_ts_tz_ops);
+ /*
+ * Do not fail driver probing when failing to register in
+ * thermal because no thermal DT node is found.
+ */
+ if (IS_ERR(info->tzd) && PTR_ERR(info->tzd) != -ENODEV) {
+ dev_err(&pdev->dev,
+ "could not register thermal sensor: %ld\n",
+ PTR_ERR(info->tzd));
+ return PTR_ERR(info->tzd);
+ }
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register the device\n");
+ goto err_map;
+ }
+
+ return 0;
+
+err_map:
+ if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
+ iio_map_array_unregister(indio_dev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int sun4i_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ if (!IS_ENABLED(CONFIG_THERMAL_OF))
+ return 0;
+
+ if (!info->no_irq)
+ iio_map_array_unregister(indio_dev);
+
+ return 0;
+}
+
+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);
+
+static struct platform_driver sun4i_gpadc_driver = {
+ .driver = {
+ .name = "sun4i-gpadc-iio",
+ .of_match_table = sun4i_gpadc_of_id,
+ .pm = &sun4i_gpadc_pm_ops,
+ },
+ .id_table = sun4i_gpadc_id,
+ .probe = sun4i_gpadc_probe,
+ .remove = sun4i_gpadc_remove,
+};
+MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_id);
+
+module_platform_driver(sun4i_gpadc_driver);
+
+MODULE_DESCRIPTION("ADC driver for sunxi platforms");
+MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
new file mode 100644
index 000000000..bd48b073e
--- /dev/null
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
+ *
+ * Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2016 Intel
+ *
+ * Datasheets:
+ * https://www.ti.com/lit/ds/symlink/adc081c021.pdf
+ * https://www.ti.com/lit/ds/symlink/adc101c021.pdf
+ * https://www.ti.com/lit/ds/symlink/adc121c021.pdf
+ *
+ * The devices have a very similar interface and differ mostly in the number of
+ * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2
+ * bits of value registers are reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regulator/consumer.h>
+
+struct adc081c {
+ struct i2c_client *i2c;
+ struct regulator *ref;
+
+ /* 8, 10 or 12 */
+ int bits;
+
+ /* Ensure natural alignment of buffer elements */
+ struct {
+ u16 channel;
+ s64 ts __aligned(8);
+ } scan;
+};
+
+#define REG_CONV_RES 0x00
+
+static int adc081c_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct adc081c *adc = iio_priv(iio);
+ int err;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
+ if (err < 0)
+ return err;
+
+ *value = (err & 0xFFF) >> (12 - adc->bits);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ err = regulator_get_voltage(adc->ref);
+ if (err < 0)
+ return err;
+
+ *value = err / 1000;
+ *shift = adc->bits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+#define ADCxx1C_CHAN(_bits) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 12 - (_bits), \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define DEFINE_ADCxx1C_CHANNELS(_name, _bits) \
+ static const struct iio_chan_spec _name ## _channels[] = { \
+ ADCxx1C_CHAN((_bits)), \
+ IIO_CHAN_SOFT_TIMESTAMP(1), \
+ }; \
+
+#define ADC081C_NUM_CHANNELS 2
+
+struct adcxx1c_model {
+ const struct iio_chan_spec* channels;
+ int bits;
+};
+
+#define ADCxx1C_MODEL(_name, _bits) \
+ { \
+ .channels = _name ## _channels, \
+ .bits = (_bits), \
+ }
+
+DEFINE_ADCxx1C_CHANNELS(adc081c, 8);
+DEFINE_ADCxx1C_CHANNELS(adc101c, 10);
+DEFINE_ADCxx1C_CHANNELS(adc121c, 12);
+
+/* Model ids are indexes in _models array */
+enum adcxx1c_model_id {
+ ADC081C = 0,
+ ADC101C = 1,
+ ADC121C = 2,
+};
+
+static struct adcxx1c_model adcxx1c_models[] = {
+ ADCxx1C_MODEL(adc081c, 8),
+ ADCxx1C_MODEL(adc101c, 10),
+ ADCxx1C_MODEL(adc121c, 12),
+};
+
+static const struct iio_info adc081c_info = {
+ .read_raw = adc081c_read_raw,
+};
+
+static irqreturn_t adc081c_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc081c *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
+ 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));
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static void adc081c_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int adc081c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *iio;
+ struct adc081c *adc;
+ const struct adcxx1c_model *model;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ if (dev_fwnode(&client->dev))
+ model = device_get_match_data(&client->dev);
+ else
+ model = &adcxx1c_models[id->driver_data];
+
+ iio = devm_iio_device_alloc(&client->dev, sizeof(*adc));
+ if (!iio)
+ return -ENOMEM;
+
+ adc = iio_priv(iio);
+ adc->i2c = client;
+ adc->bits = model->bits;
+
+ adc->ref = devm_regulator_get(&client->dev, "vref");
+ if (IS_ERR(adc->ref))
+ return PTR_ERR(adc->ref);
+
+ err = regulator_enable(adc->ref);
+ if (err < 0)
+ return err;
+
+ err = devm_add_action_or_reset(&client->dev, adc081c_reg_disable,
+ adc->ref);
+ if (err)
+ return err;
+
+ iio->name = dev_name(&client->dev);
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->info = &adc081c_info;
+
+ iio->channels = model->channels;
+ iio->num_channels = ADC081C_NUM_CHANNELS;
+
+ err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL,
+ adc081c_trigger_handler, NULL);
+ if (err < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ return err;
+ }
+
+ return devm_iio_device_register(&client->dev, iio);
+}
+
+static const struct i2c_device_id adc081c_id[] = {
+ { "adc081c", ADC081C },
+ { "adc101c", ADC101C },
+ { "adc121c", ADC121C },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adc081c_id);
+
+static const struct acpi_device_id adc081c_acpi_match[] = {
+ /* Used on some AAEON boards */
+ { "ADC081C", (kernel_ulong_t)&adcxx1c_models[ADC081C] },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match);
+
+static const struct of_device_id adc081c_of_match[] = {
+ { .compatible = "ti,adc081c", .data = &adcxx1c_models[ADC081C] },
+ { .compatible = "ti,adc101c", .data = &adcxx1c_models[ADC101C] },
+ { .compatible = "ti,adc121c", .data = &adcxx1c_models[ADC121C] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc081c_of_match);
+
+static struct i2c_driver adc081c_driver = {
+ .driver = {
+ .name = "adc081c",
+ .of_match_table = adc081c_of_match,
+ .acpi_match_table = adc081c_acpi_match,
+ },
+ .probe = adc081c_probe,
+ .id_table = adc081c_id,
+};
+module_i2c_driver(adc081c_driver);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
new file mode 100644
index 000000000..b11ce555b
--- /dev/null
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver
+ *
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/adc0832-n.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+enum {
+ adc0831,
+ adc0832,
+ adc0834,
+ adc0838,
+};
+
+struct adc0832 {
+ struct spi_device *spi;
+ struct regulator *reg;
+ struct mutex lock;
+ u8 mux_bits;
+ /*
+ * Max size needed: 16x 1 byte ADC data + 8 bytes timestamp
+ * May be shorter if not all channels are enabled subject
+ * to the timestamp remaining 8 byte aligned.
+ */
+ u8 data[24] __aligned(8);
+
+ u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[2];
+};
+
+#define ADC0832_VOLTAGE_CHANNEL(chan) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = chan, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+ }
+
+#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = si, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+ }
+
+static const struct iio_chan_spec adc0831_channels[] = {
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_chan_spec adc0832_channels[] = {
+ ADC0832_VOLTAGE_CHANNEL(0),
+ ADC0832_VOLTAGE_CHANNEL(1),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct iio_chan_spec adc0834_channels[] = {
+ ADC0832_VOLTAGE_CHANNEL(0),
+ ADC0832_VOLTAGE_CHANNEL(1),
+ ADC0832_VOLTAGE_CHANNEL(2),
+ ADC0832_VOLTAGE_CHANNEL(3),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const struct iio_chan_spec adc0838_channels[] = {
+ ADC0832_VOLTAGE_CHANNEL(0),
+ ADC0832_VOLTAGE_CHANNEL(1),
+ ADC0832_VOLTAGE_CHANNEL(2),
+ ADC0832_VOLTAGE_CHANNEL(3),
+ ADC0832_VOLTAGE_CHANNEL(4),
+ ADC0832_VOLTAGE_CHANNEL(5),
+ ADC0832_VOLTAGE_CHANNEL(6),
+ ADC0832_VOLTAGE_CHANNEL(7),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+};
+
+static int adc0831_adc_conversion(struct adc0832 *adc)
+{
+ struct spi_device *spi = adc->spi;
+ int ret;
+
+ ret = spi_read(spi, &adc->rx_buf, 2);
+ if (ret)
+ return ret;
+
+ /*
+ * Skip TRI-STATE and a leading zero
+ */
+ return (adc->rx_buf[0] << 2 & 0xff) | (adc->rx_buf[1] >> 6);
+}
+
+static int adc0832_adc_conversion(struct adc0832 *adc, int channel,
+ bool differential)
+{
+ struct spi_device *spi = adc->spi;
+ struct spi_transfer xfer = {
+ .tx_buf = adc->tx_buf,
+ .rx_buf = adc->rx_buf,
+ .len = 2,
+ };
+ int ret;
+
+ if (!adc->mux_bits)
+ return adc0831_adc_conversion(adc);
+
+ /* start bit */
+ adc->tx_buf[0] = 1 << (adc->mux_bits + 1);
+ /* single-ended or differential */
+ adc->tx_buf[0] |= differential ? 0 : (1 << adc->mux_bits);
+ /* odd / sign */
+ adc->tx_buf[0] |= (channel % 2) << (adc->mux_bits - 1);
+ /* select */
+ if (adc->mux_bits > 1)
+ adc->tx_buf[0] |= channel / 2;
+
+ /* align Data output BIT7 (MSB) to 8-bit boundary */
+ adc->tx_buf[0] <<= 1;
+
+ ret = spi_sync_transfer(spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ return adc->rx_buf[1];
+}
+
+static int adc0832_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct adc0832 *adc = iio_priv(iio);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *value = adc0832_adc_conversion(adc, channel->channel,
+ channel->differential);
+ mutex_unlock(&adc->lock);
+ if (*value < 0)
+ return *value;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *value = regulator_get_voltage(adc->reg);
+ if (*value < 0)
+ return *value;
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+ *shift = 8;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info adc0832_info = {
+ .read_raw = adc0832_read_raw,
+};
+
+static irqreturn_t adc0832_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc0832 *adc = iio_priv(indio_dev);
+ int scan_index;
+ int i = 0;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan =
+ &indio_dev->channels[scan_index];
+ int ret = adc0832_adc_conversion(adc, scan_chan->channel,
+ scan_chan->differential);
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ adc->data[i] = ret;
+ i++;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->data,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void adc0832_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int adc0832_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct adc0832 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &adc0832_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ switch (spi_get_device_id(spi)->driver_data) {
+ case adc0831:
+ adc->mux_bits = 0;
+ indio_dev->channels = adc0831_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc0831_channels);
+ break;
+ case adc0832:
+ adc->mux_bits = 1;
+ indio_dev->channels = adc0832_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc0832_channels);
+ break;
+ case adc0834:
+ adc->mux_bits = 2;
+ indio_dev->channels = adc0834_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc0834_channels);
+ break;
+ case adc0838:
+ adc->mux_bits = 3;
+ indio_dev->channels = adc0838_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc0838_channels);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return PTR_ERR(adc->reg);
+
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, adc0832_reg_disable,
+ adc->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ adc0832_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id adc0832_dt_ids[] = {
+ { .compatible = "ti,adc0831", },
+ { .compatible = "ti,adc0832", },
+ { .compatible = "ti,adc0834", },
+ { .compatible = "ti,adc0838", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adc0832_dt_ids);
+
+static const struct spi_device_id adc0832_id[] = {
+ { "adc0831", adc0831 },
+ { "adc0832", adc0832 },
+ { "adc0834", adc0834 },
+ { "adc0838", adc0838 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adc0832_id);
+
+static struct spi_driver adc0832_driver = {
+ .driver = {
+ .name = "adc0832",
+ .of_match_table = adc0832_dt_ids,
+ },
+ .probe = adc0832_probe,
+ .id_table = adc0832_id,
+};
+module_spi_driver(adc0832_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("ADC0831/ADC0832/ADC0834/ADC0838 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
new file mode 100644
index 000000000..1f6e53832
--- /dev/null
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017 Axis Communications AB
+ *
+ * Driver for Texas Instruments' ADC084S021 ADC chip.
+ * Datasheets can be found here:
+ * https://www.ti.com/lit/ds/symlink/adc084s021.pdf
+ */
+
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define ADC084S021_DRIVER_NAME "adc084s021"
+
+struct adc084s021 {
+ struct spi_device *spi;
+ struct spi_message message;
+ struct spi_transfer spi_trans;
+ struct regulator *reg;
+ struct mutex lock;
+ /* Buffer used to align data */
+ struct {
+ __be16 channels[4];
+ s64 ts __aligned(8);
+ } scan;
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache line.
+ */
+ u16 tx_buf[4] __aligned(IIO_DMA_MINALIGN);
+ __be16 rx_buf[5]; /* First 16-bits are trash */
+};
+
+#define ADC084S021_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .channel = (num), \
+ .indexed = 1, \
+ .scan_index = (num), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 16, \
+ .shift = 4, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ }
+
+static const struct iio_chan_spec adc084s021_channels[] = {
+ ADC084S021_VOLTAGE_CHANNEL(0),
+ ADC084S021_VOLTAGE_CHANNEL(1),
+ ADC084S021_VOLTAGE_CHANNEL(2),
+ ADC084S021_VOLTAGE_CHANNEL(3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+/**
+ * adc084s021_adc_conversion() - Read an ADC channel and return its value.
+ *
+ * @adc: The ADC SPI data.
+ * @data: Buffer for converted data.
+ */
+static int adc084s021_adc_conversion(struct adc084s021 *adc, __be16 *data)
+{
+ int n_words = (adc->spi_trans.len >> 1) - 1; /* Discard first word */
+ int ret, i = 0;
+
+ /* Do the transfer */
+ ret = spi_sync(adc->spi, &adc->message);
+ if (ret < 0)
+ return ret;
+
+ for (; i < n_words; i++)
+ *(data + i) = adc->rx_buf[i + 1];
+
+ return ret;
+}
+
+static int adc084s021_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct adc084s021 *adc = iio_priv(indio_dev);
+ int ret;
+ __be16 be_val;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(adc->reg);
+ if (ret) {
+ iio_device_release_direct_mode(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);
+ regulator_disable(adc->reg);
+ if (ret < 0)
+ return ret;
+
+ *val = be16_to_cpu(be_val);
+ *val = (*val >> channel->scan_type.shift) & 0xff;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(adc->reg);
+ regulator_disable(adc->reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * adc084s021_buffer_trigger_handler() - Read ADC channels and push to buffer.
+ *
+ * @irq: The interrupt number (not used).
+ * @pollfunc: Pointer to the poll func.
+ */
+static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc)
+{
+ struct iio_poll_func *pf = pollfunc;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc084s021 *adc = iio_priv(indio_dev);
+
+ mutex_lock(&adc->lock);
+
+ 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));
+ mutex_unlock(&adc->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int adc084s021_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct adc084s021 *adc = iio_priv(indio_dev);
+ int scan_index;
+ int i = 0;
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *channel =
+ &indio_dev->channels[scan_index];
+ adc->tx_buf[i++] = channel->channel << 3;
+ }
+ adc->spi_trans.len = 2 + (i * sizeof(__be16)); /* Trash + channels */
+
+ return regulator_enable(adc->reg);
+}
+
+static int adc084s021_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct adc084s021 *adc = iio_priv(indio_dev);
+
+ adc->spi_trans.len = 4; /* Trash + single channel */
+
+ return regulator_disable(adc->reg);
+}
+
+static const struct iio_info adc084s021_info = {
+ .read_raw = adc084s021_read_raw,
+};
+
+static const struct iio_buffer_setup_ops adc084s021_buffer_setup_ops = {
+ .preenable = adc084s021_buffer_preenable,
+ .postdisable = adc084s021_buffer_postdisable,
+};
+
+static int adc084s021_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct adc084s021 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(&spi->dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ /* Initiate the Industrial I/O device */
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &adc084s021_info;
+ indio_dev->channels = adc084s021_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc084s021_channels);
+
+ /* Create SPI transfer for channel reads */
+ adc->spi_trans.tx_buf = adc->tx_buf;
+ adc->spi_trans.rx_buf = adc->rx_buf;
+ adc->spi_trans.len = 4; /* Trash + single channel */
+ spi_message_init_with_transfers(&adc->message, &adc->spi_trans, 1);
+
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return PTR_ERR(adc->reg);
+
+ mutex_init(&adc->lock);
+
+ /* Setup triggered buffer with pollfunction */
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ adc084s021_buffer_trigger_handler,
+ &adc084s021_buffer_setup_ops);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to setup triggered buffer\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id adc084s021_of_match[] = {
+ { .compatible = "ti,adc084s021", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, adc084s021_of_match);
+
+static const struct spi_device_id adc084s021_id[] = {
+ { ADC084S021_DRIVER_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adc084s021_id);
+
+static struct spi_driver adc084s021_driver = {
+ .driver = {
+ .name = ADC084S021_DRIVER_NAME,
+ .of_match_table = adc084s021_of_match,
+ },
+ .probe = adc084s021_probe,
+ .id_table = adc084s021_id,
+};
+module_spi_driver(adc084s021_driver);
+
+MODULE_AUTHOR("MÃ¥rten Lindahl <martenli@axis.com>");
+MODULE_DESCRIPTION("Texas Instruments ADC084S021");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c
new file mode 100644
index 000000000..c82a16163
--- /dev/null
+++ b/drivers/iio/adc/ti-adc108s102.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI ADC108S102 SPI ADC driver
+ *
+ * Copyright (c) 2013-2015 Intel Corporation.
+ * Copyright (c) 2017 Siemens AG
+ *
+ * This IIO device driver is designed to work with the following
+ * analog to digital converters from Texas Instruments:
+ * ADC108S102
+ * ADC128S102
+ * The communication with ADC chip is via the SPI bus (mode 3).
+ */
+
+#include <linux/acpi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/types.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+/*
+ * In case of ACPI, we use the hard-wired 5000 mV of the Galileo and IOT2000
+ * boards as default for the reference pin VA. Device tree users encode that
+ * via the vref-supply regulator.
+ */
+#define ADC108S102_VA_MV_ACPI_DEFAULT 5000
+
+/*
+ * Defining the ADC resolution being 12 bits, we can use the same driver for
+ * both ADC108S102 (10 bits resolution) and ADC128S102 (12 bits resolution)
+ * chips. The ADC108S102 effectively returns a 12-bit result with the 2
+ * least-significant bits unset.
+ */
+#define ADC108S102_BITS 12
+#define ADC108S102_MAX_CHANNELS 8
+
+/*
+ * 16-bit SPI command format:
+ * [15:14] Ignored
+ * [13:11] 3-bit channel address
+ * [10:0] Ignored
+ */
+#define ADC108S102_CMD(ch) ((u16)(ch) << 11)
+
+/*
+ * 16-bit SPI response format:
+ * [15:12] Zeros
+ * [11:0] 12-bit ADC sample (for ADC108S102, [1:0] will always be 0).
+ */
+#define ADC108S102_RES_DATA(res) ((u16)res & GENMASK(11, 0))
+
+struct adc108s102_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ u32 va_millivolt;
+ /* SPI transfer used by triggered buffer handler*/
+ struct spi_transfer ring_xfer;
+ /* SPI transfer used by direct scan */
+ struct spi_transfer scan_single_xfer;
+ /* SPI message used by ring_xfer SPI transfer */
+ struct spi_message ring_msg;
+ /* SPI message used by scan_single_xfer SPI transfer */
+ struct spi_message scan_single_msg;
+
+ /*
+ * SPI message buffers:
+ * tx_buf: |C0|C1|C2|C3|C4|C5|C6|C7|XX|
+ * rx_buf: |XX|R0|R1|R2|R3|R4|R5|R6|R7|tt|tt|tt|tt|
+ *
+ * tx_buf: 8 channel read commands, plus 1 dummy command
+ * rx_buf: 1 dummy response, 8 channel responses
+ */
+ __be16 rx_buf[9] __aligned(IIO_DMA_MINALIGN);
+ __be16 tx_buf[9] __aligned(IIO_DMA_MINALIGN);
+};
+
+#define ADC108S102_V_CHAN(index) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = ADC108S102_BITS, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec adc108s102_channels[] = {
+ ADC108S102_V_CHAN(0),
+ ADC108S102_V_CHAN(1),
+ ADC108S102_V_CHAN(2),
+ ADC108S102_V_CHAN(3),
+ ADC108S102_V_CHAN(4),
+ ADC108S102_V_CHAN(5),
+ ADC108S102_V_CHAN(6),
+ ADC108S102_V_CHAN(7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static int adc108s102_update_scan_mode(struct iio_dev *indio_dev,
+ unsigned long const *active_scan_mask)
+{
+ struct adc108s102_state *st = iio_priv(indio_dev);
+ unsigned int bit, cmds;
+
+ /*
+ * Fill in the first x shorts of tx_buf with the number of channels
+ * enabled for sampling by the triggered buffer.
+ */
+ cmds = 0;
+ for_each_set_bit(bit, active_scan_mask, ADC108S102_MAX_CHANNELS)
+ st->tx_buf[cmds++] = cpu_to_be16(ADC108S102_CMD(bit));
+
+ /* One dummy command added, to clock in the last response */
+ st->tx_buf[cmds++] = 0x00;
+
+ /* build SPI ring message */
+ st->ring_xfer.tx_buf = &st->tx_buf[0];
+ st->ring_xfer.rx_buf = &st->rx_buf[0];
+ st->ring_xfer.len = cmds * sizeof(st->tx_buf[0]);
+
+ spi_message_init_with_transfers(&st->ring_msg, &st->ring_xfer, 1);
+
+ return 0;
+}
+
+static irqreturn_t adc108s102_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc108s102_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_sync(st->spi, &st->ring_msg);
+ if (ret < 0)
+ goto out_notify;
+
+ /* Skip the dummy response in the first slot */
+ iio_push_to_buffers_with_ts_unaligned(indio_dev,
+ &st->rx_buf[1],
+ st->ring_xfer.len - sizeof(st->rx_buf[1]),
+ iio_get_time_ns(indio_dev));
+
+out_notify:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int adc108s102_scan_direct(struct adc108s102_state *st, unsigned int ch)
+{
+ int ret;
+
+ st->tx_buf[0] = cpu_to_be16(ADC108S102_CMD(ch));
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ return ret;
+
+ /* Skip the dummy response in the first slot */
+ return be16_to_cpu(st->rx_buf[1]);
+}
+
+static int adc108s102_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct adc108s102_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = adc108s102_scan_direct(st, chan->address);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ADC108S102_RES_DATA(ret);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_VOLTAGE)
+ break;
+
+ *val = st->va_millivolt;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info adc108s102_info = {
+ .read_raw = &adc108s102_read_raw,
+ .update_scan_mode = &adc108s102_update_scan_mode,
+};
+
+static void adc108s102_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int adc108s102_probe(struct spi_device *spi)
+{
+ struct adc108s102_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ if (ACPI_COMPANION(&spi->dev)) {
+ st->va_millivolt = ADC108S102_VA_MV_ACPI_DEFAULT;
+ } else {
+ st->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Cannot enable vref regulator\n");
+ return ret;
+ }
+ ret = devm_add_action_or_reset(&spi->dev, adc108s102_reg_disable,
+ st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "vref get voltage failed\n");
+ return ret;
+ }
+
+ st->va_millivolt = ret / 1000;
+ }
+
+ st->spi = spi;
+
+ indio_dev->name = spi->modalias;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = adc108s102_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc108s102_channels);
+ indio_dev->info = &adc108s102_info;
+
+ /* Setup default message */
+ st->scan_single_xfer.tx_buf = st->tx_buf;
+ st->scan_single_xfer.rx_buf = st->rx_buf;
+ st->scan_single_xfer.len = 2 * sizeof(st->tx_buf[0]);
+
+ spi_message_init_with_transfers(&st->scan_single_msg,
+ &st->scan_single_xfer, 1);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ &adc108s102_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
+ if (ret)
+ dev_err(&spi->dev, "Failed to register IIO device\n");
+ return ret;
+}
+
+static const struct of_device_id adc108s102_of_match[] = {
+ { .compatible = "ti,adc108s102" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc108s102_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id adc108s102_acpi_ids[] = {
+ { "INT3495", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids);
+#endif
+
+static const struct spi_device_id adc108s102_id[] = {
+ { "adc108s102", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adc108s102_id);
+
+static struct spi_driver adc108s102_driver = {
+ .driver = {
+ .name = "adc108s102",
+ .of_match_table = adc108s102_of_match,
+ .acpi_match_table = ACPI_PTR(adc108s102_acpi_ids),
+ },
+ .probe = adc108s102_probe,
+ .id_table = adc108s102_id,
+};
+module_spi_driver(adc108s102_driver);
+
+MODULE_AUTHOR("Bogdan Pricop <bogdan.pricop@emutex.com>");
+MODULE_DESCRIPTION("Texas Instruments ADC108S102 and ADC128S102 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
new file mode 100644
index 000000000..c0a72d72f
--- /dev/null
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADC12130/ADC12132/ADC12138 12-bit plus sign ADC driver
+ *
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/clk.h>
+#include <linux/property.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define ADC12138_MODE_AUTO_CAL 0x08
+#define ADC12138_MODE_READ_STATUS 0x0c
+#define ADC12138_MODE_ACQUISITION_TIME_6 0x0e
+#define ADC12138_MODE_ACQUISITION_TIME_10 0x4e
+#define ADC12138_MODE_ACQUISITION_TIME_18 0x8e
+#define ADC12138_MODE_ACQUISITION_TIME_34 0xce
+
+#define ADC12138_STATUS_CAL BIT(6)
+
+enum {
+ adc12130,
+ adc12132,
+ adc12138,
+};
+
+struct adc12138 {
+ struct spi_device *spi;
+ unsigned int id;
+ /* conversion clock */
+ struct clk *cclk;
+ /* positive analog voltage reference */
+ struct regulator *vref_p;
+ /* negative analog voltage reference */
+ struct regulator *vref_n;
+ struct mutex lock;
+ struct completion complete;
+ /* The number of cclk periods for the S/H's acquisition time */
+ unsigned int acquisition_time;
+ /*
+ * Maximum size needed: 16x 2 bytes ADC data + 8 bytes timestamp.
+ * Less may be need if not all channels are enabled, as long as
+ * the 8 byte alignment of the timestamp is maintained.
+ */
+ __be16 data[20] __aligned(8);
+
+ u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[2];
+};
+
+#define ADC12138_VOLTAGE_CHANNEL(chan) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = chan, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 13, \
+ .storagebits = 16, \
+ .shift = 3, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define ADC12138_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = si, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 13, \
+ .storagebits = 16, \
+ .shift = 3, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec adc12132_channels[] = {
+ ADC12138_VOLTAGE_CHANNEL(0),
+ ADC12138_VOLTAGE_CHANNEL(1),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct iio_chan_spec adc12138_channels[] = {
+ ADC12138_VOLTAGE_CHANNEL(0),
+ ADC12138_VOLTAGE_CHANNEL(1),
+ ADC12138_VOLTAGE_CHANNEL(2),
+ ADC12138_VOLTAGE_CHANNEL(3),
+ ADC12138_VOLTAGE_CHANNEL(4),
+ ADC12138_VOLTAGE_CHANNEL(5),
+ ADC12138_VOLTAGE_CHANNEL(6),
+ ADC12138_VOLTAGE_CHANNEL(7),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+};
+
+static int adc12138_mode_programming(struct adc12138 *adc, u8 mode,
+ void *rx_buf, int len)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = adc->tx_buf,
+ .rx_buf = adc->rx_buf,
+ .len = len,
+ };
+ int ret;
+
+ /* Skip unused bits for ADC12130 and ADC12132 */
+ if (adc->id != adc12138)
+ mode = (mode & 0xc0) | ((mode & 0x0f) << 2);
+
+ adc->tx_buf[0] = mode;
+
+ ret = spi_sync_transfer(adc->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ memcpy(rx_buf, adc->rx_buf, len);
+
+ return 0;
+}
+
+static int adc12138_read_status(struct adc12138 *adc)
+{
+ u8 rx_buf[2];
+ int ret;
+
+ ret = adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS,
+ rx_buf, 2);
+ if (ret)
+ return ret;
+
+ return (rx_buf[0] << 1) | (rx_buf[1] >> 7);
+}
+
+static int __adc12138_start_conv(struct adc12138 *adc,
+ struct iio_chan_spec const *channel,
+ void *data, int len)
+
+{
+ static const u8 ch_to_mux[] = { 0, 4, 1, 5, 2, 6, 3, 7 };
+ u8 mode = (ch_to_mux[channel->channel] << 4) |
+ (channel->differential ? 0 : 0x80);
+
+ return adc12138_mode_programming(adc, mode, data, len);
+}
+
+static int adc12138_start_conv(struct adc12138 *adc,
+ struct iio_chan_spec const *channel)
+{
+ u8 trash;
+
+ return __adc12138_start_conv(adc, channel, &trash, 1);
+}
+
+static int adc12138_start_and_read_conv(struct adc12138 *adc,
+ struct iio_chan_spec const *channel,
+ __be16 *data)
+{
+ return __adc12138_start_conv(adc, channel, data, 2);
+}
+
+static int adc12138_read_conv_data(struct adc12138 *adc, __be16 *value)
+{
+ /* Issue a read status instruction and read previous conversion data */
+ return adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS,
+ value, sizeof(*value));
+}
+
+static int adc12138_wait_eoc(struct adc12138 *adc, unsigned long timeout)
+{
+ if (!wait_for_completion_timeout(&adc->complete, timeout))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int adc12138_adc_conversion(struct adc12138 *adc,
+ struct iio_chan_spec const *channel,
+ __be16 *value)
+{
+ int ret;
+
+ reinit_completion(&adc->complete);
+
+ ret = adc12138_start_conv(adc, channel);
+ if (ret)
+ return ret;
+
+ ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100));
+ if (ret)
+ return ret;
+
+ return adc12138_read_conv_data(adc, value);
+}
+
+static int adc12138_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct adc12138 *adc = iio_priv(iio);
+ int ret;
+ __be16 data;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ ret = adc12138_adc_conversion(adc, channel, &data);
+ mutex_unlock(&adc->lock);
+ if (ret)
+ return ret;
+
+ *value = sign_extend32(be16_to_cpu(data) >> channel->scan_type.shift,
+ channel->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc->vref_p);
+ if (ret < 0)
+ return ret;
+ *value = ret;
+
+ if (!IS_ERR(adc->vref_n)) {
+ ret = regulator_get_voltage(adc->vref_n);
+ if (ret < 0)
+ return ret;
+ *value -= ret;
+ }
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+ *shift = channel->scan_type.realbits - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (!IS_ERR(adc->vref_n)) {
+ *value = regulator_get_voltage(adc->vref_n);
+ if (*value < 0)
+ return *value;
+ } else {
+ *value = 0;
+ }
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info adc12138_info = {
+ .read_raw = adc12138_read_raw,
+};
+
+static int adc12138_init(struct adc12138 *adc)
+{
+ int ret;
+ int status;
+ u8 mode;
+ u8 trash;
+
+ reinit_completion(&adc->complete);
+
+ ret = adc12138_mode_programming(adc, ADC12138_MODE_AUTO_CAL, &trash, 1);
+ if (ret)
+ return ret;
+
+ /* data output at this time has no significance */
+ status = adc12138_read_status(adc);
+ if (status < 0)
+ return status;
+
+ adc12138_wait_eoc(adc, msecs_to_jiffies(100));
+
+ status = adc12138_read_status(adc);
+ if (status & ADC12138_STATUS_CAL) {
+ dev_warn(&adc->spi->dev,
+ "Auto Cal sequence is still in progress: %#x\n",
+ status);
+ return -EIO;
+ }
+
+ switch (adc->acquisition_time) {
+ case 6:
+ mode = ADC12138_MODE_ACQUISITION_TIME_6;
+ break;
+ case 10:
+ mode = ADC12138_MODE_ACQUISITION_TIME_10;
+ break;
+ case 18:
+ mode = ADC12138_MODE_ACQUISITION_TIME_18;
+ break;
+ case 34:
+ mode = ADC12138_MODE_ACQUISITION_TIME_34;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adc12138_mode_programming(adc, mode, &trash, 1);
+}
+
+static irqreturn_t adc12138_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc12138 *adc = iio_priv(indio_dev);
+ __be16 trash;
+ int ret;
+ int scan_index;
+ int i = 0;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan =
+ &indio_dev->channels[scan_index];
+
+ reinit_completion(&adc->complete);
+
+ ret = adc12138_start_and_read_conv(adc, scan_chan,
+ i ? &adc->data[i - 1] : &trash);
+ if (ret) {
+ dev_warn(&adc->spi->dev,
+ "failed to start conversion\n");
+ goto out;
+ }
+
+ ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100));
+ if (ret) {
+ dev_warn(&adc->spi->dev, "wait eoc timeout\n");
+ goto out;
+ }
+
+ i++;
+ }
+
+ if (i) {
+ ret = adc12138_read_conv_data(adc, &adc->data[i - 1]);
+ if (ret) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, adc->data,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t adc12138_eoc_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct adc12138 *adc = iio_priv(indio_dev);
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int adc12138_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct adc12138 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ adc->id = spi_get_device_id(spi)->driver_data;
+ mutex_init(&adc->lock);
+ init_completion(&adc->complete);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &adc12138_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ switch (adc->id) {
+ case adc12130:
+ case adc12132:
+ indio_dev->channels = adc12132_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc12132_channels);
+ break;
+ case adc12138:
+ indio_dev->channels = adc12138_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc12138_channels);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32(&spi->dev, "ti,acquisition-time",
+ &adc->acquisition_time);
+ if (ret)
+ adc->acquisition_time = 10;
+
+ adc->cclk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(adc->cclk))
+ return PTR_ERR(adc->cclk);
+
+ adc->vref_p = devm_regulator_get(&spi->dev, "vref-p");
+ if (IS_ERR(adc->vref_p))
+ return PTR_ERR(adc->vref_p);
+
+ adc->vref_n = devm_regulator_get_optional(&spi->dev, "vref-n");
+ if (IS_ERR(adc->vref_n)) {
+ /*
+ * Assume vref_n is 0V if an optional regulator is not
+ * specified, otherwise return the error code.
+ */
+ ret = PTR_ERR(adc->vref_n);
+ if (ret != -ENODEV)
+ return ret;
+ }
+
+ ret = devm_request_irq(&spi->dev, spi->irq, adc12138_eoc_handler,
+ IRQF_TRIGGER_RISING, indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(adc->cclk);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(adc->vref_p);
+ if (ret)
+ goto err_clk_disable;
+
+ if (!IS_ERR(adc->vref_n)) {
+ ret = regulator_enable(adc->vref_n);
+ if (ret)
+ goto err_vref_p_disable;
+ }
+
+ ret = adc12138_init(adc);
+ if (ret)
+ goto err_vref_n_disable;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ adc12138_trigger_handler, NULL);
+ if (ret)
+ goto err_vref_n_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
+ return 0;
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_vref_n_disable:
+ if (!IS_ERR(adc->vref_n))
+ regulator_disable(adc->vref_n);
+err_vref_p_disable:
+ regulator_disable(adc->vref_p);
+err_clk_disable:
+ clk_disable_unprepare(adc->cclk);
+
+ return ret;
+}
+
+static void adc12138_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adc12138 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (!IS_ERR(adc->vref_n))
+ regulator_disable(adc->vref_n);
+ regulator_disable(adc->vref_p);
+ clk_disable_unprepare(adc->cclk);
+}
+
+static const struct of_device_id adc12138_dt_ids[] = {
+ { .compatible = "ti,adc12130", },
+ { .compatible = "ti,adc12132", },
+ { .compatible = "ti,adc12138", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adc12138_dt_ids);
+
+static const struct spi_device_id adc12138_id[] = {
+ { "adc12130", adc12130 },
+ { "adc12132", adc12132 },
+ { "adc12138", adc12138 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adc12138_id);
+
+static struct spi_driver adc12138_driver = {
+ .driver = {
+ .name = "adc12138",
+ .of_match_table = adc12138_dt_ids,
+ },
+ .probe = adc12138_probe,
+ .remove = adc12138_remove,
+ .id_table = adc12138_id,
+};
+module_spi_driver(adc12138_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("ADC12130/ADC12132/ADC12138 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
new file mode 100644
index 000000000..b3d5b9b72
--- /dev/null
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
+ *
+ * Driver for Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip.
+ * Datasheets can be found here:
+ * https://www.ti.com/lit/ds/symlink/adc128s052.pdf
+ * https://www.ti.com/lit/ds/symlink/adc122s021.pdf
+ * https://www.ti.com/lit/ds/symlink/adc124s021.pdf
+ */
+
+#include <linux/acpi.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/iio/iio.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+struct adc128_configuration {
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+};
+
+struct adc128 {
+ struct spi_device *spi;
+
+ struct regulator *reg;
+ struct mutex lock;
+
+ u8 buffer[2] __aligned(IIO_DMA_MINALIGN);
+};
+
+static int adc128_adc_conversion(struct adc128 *adc, u8 channel)
+{
+ int ret;
+
+ mutex_lock(&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);
+ return ret;
+ }
+
+ ret = spi_read(adc->spi, &adc->buffer, 2);
+
+ mutex_unlock(&adc->lock);
+
+ if (ret < 0)
+ return ret;
+
+ return ((adc->buffer[0] << 8 | adc->buffer[1]) & 0xFFF);
+}
+
+static int adc128_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct adc128 *adc = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+
+ ret = adc128_adc_conversion(adc, channel->channel);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+
+ ret = regulator_get_voltage(adc->reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+
+}
+
+#define ADC128_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (num), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ }
+
+static const struct iio_chan_spec adc128s052_channels[] = {
+ ADC128_VOLTAGE_CHANNEL(0),
+ ADC128_VOLTAGE_CHANNEL(1),
+ ADC128_VOLTAGE_CHANNEL(2),
+ ADC128_VOLTAGE_CHANNEL(3),
+ ADC128_VOLTAGE_CHANNEL(4),
+ ADC128_VOLTAGE_CHANNEL(5),
+ ADC128_VOLTAGE_CHANNEL(6),
+ ADC128_VOLTAGE_CHANNEL(7),
+};
+
+static const struct iio_chan_spec adc122s021_channels[] = {
+ ADC128_VOLTAGE_CHANNEL(0),
+ ADC128_VOLTAGE_CHANNEL(1),
+};
+
+static const struct iio_chan_spec adc124s021_channels[] = {
+ ADC128_VOLTAGE_CHANNEL(0),
+ ADC128_VOLTAGE_CHANNEL(1),
+ ADC128_VOLTAGE_CHANNEL(2),
+ ADC128_VOLTAGE_CHANNEL(3),
+};
+
+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) },
+};
+
+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)
+{
+ struct iio_dev *indio_dev;
+ unsigned int config;
+ struct adc128 *adc;
+ int ret;
+
+ if (dev_fwnode(&spi->dev))
+ config = (unsigned long) device_get_match_data(&spi->dev);
+ else
+ config = spi_get_device_id(spi)->driver_data;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &adc128_info;
+
+ indio_dev->channels = adc128_config[config].channels;
+ indio_dev->num_channels = adc128_config[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);
+ if (ret < 0)
+ return ret;
+ ret = devm_add_action_or_reset(&spi->dev, adc128_disable_regulator,
+ adc->reg);
+ if (ret)
+ return ret;
+
+ mutex_init(&adc->lock);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id adc128_of_match[] = {
+ { .compatible = "ti,adc128s052", .data = (void*)0L, },
+ { .compatible = "ti,adc122s021", .data = (void*)1L, },
+ { .compatible = "ti,adc122s051", .data = (void*)1L, },
+ { .compatible = "ti,adc122s101", .data = (void*)1L, },
+ { .compatible = "ti,adc124s021", .data = (void*)2L, },
+ { .compatible = "ti,adc124s051", .data = (void*)2L, },
+ { .compatible = "ti,adc124s101", .data = (void*)2L, },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, adc128_of_match);
+
+static const struct spi_device_id adc128_id[] = {
+ { "adc128s052", 0 }, /* index into adc128_config */
+ { "adc122s021", 1 },
+ { "adc122s051", 1 },
+ { "adc122s101", 1 },
+ { "adc124s021", 2 },
+ { "adc124s051", 2 },
+ { "adc124s101", 2 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, adc128_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id adc128_acpi_match[] = {
+ { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adc128_acpi_match);
+#endif
+
+static struct spi_driver adc128_driver = {
+ .driver = {
+ .name = "adc128s052",
+ .of_match_table = adc128_of_match,
+ .acpi_match_table = ACPI_PTR(adc128_acpi_match),
+ },
+ .probe = adc128_probe,
+ .id_table = adc128_id,
+};
+module_spi_driver(adc128_driver);
+
+MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments ADC128S052");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
new file mode 100644
index 000000000..b789891dc
--- /dev/null
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
+ *
+ * ADC Devices Supported:
+ * adc141s626 - 14-bit ADC
+ * adc161s626 - 16-bit ADC
+ *
+ * Copyright (C) 2016-2018
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ */
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regulator/consumer.h>
+
+#define TI_ADC_DRV_NAME "ti-adc161s626"
+
+enum {
+ TI_ADC141S626,
+ TI_ADC161S626,
+};
+
+static const struct iio_chan_spec ti_adc141s626_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 16,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_chan_spec ti_adc161s626_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+struct ti_adc_data {
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ struct regulator *ref;
+
+ u8 read_size;
+ u8 shift;
+
+ u8 buffer[16] __aligned(IIO_DMA_MINALIGN);
+};
+
+static int ti_adc_read_measurement(struct ti_adc_data *data,
+ struct iio_chan_spec const *chan, int *val)
+{
+ int ret;
+
+ switch (data->read_size) {
+ case 2: {
+ __be16 buf;
+
+ ret = spi_read(data->spi, (void *) &buf, 2);
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(buf);
+ break;
+ }
+ case 3: {
+ __be32 buf;
+
+ ret = spi_read(data->spi, (void *) &buf, 3);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(buf) >> 8;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1);
+
+ return 0;
+}
+
+static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ti_adc_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = ti_adc_read_measurement(data, &indio_dev->channels[0],
+ (int *) &data->buffer);
+ if (!ret)
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ data->buffer,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ti_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ti_adc_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ti_adc_read_measurement(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(data->ref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 1 << (chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ }
+
+ return 0;
+}
+
+static const struct iio_info ti_adc_info = {
+ .read_raw = ti_adc_read_raw,
+};
+
+static void ti_adc_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int ti_adc_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ti_adc_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &ti_adc_info;
+ indio_dev->name = TI_ADC_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data = iio_priv(indio_dev);
+ data->spi = spi;
+
+ switch (spi_get_device_id(spi)->driver_data) {
+ case TI_ADC141S626:
+ indio_dev->channels = ti_adc141s626_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels);
+ data->shift = 0;
+ data->read_size = 2;
+ break;
+ case TI_ADC161S626:
+ indio_dev->channels = ti_adc161s626_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels);
+ data->shift = 6;
+ data->read_size = 3;
+ break;
+ }
+
+ data->ref = devm_regulator_get(&spi->dev, "vdda");
+ if (IS_ERR(data->ref))
+ return PTR_ERR(data->ref);
+
+ ret = regulator_enable(data->ref);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable,
+ data->ref);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ ti_adc_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ti_adc_dt_ids[] = {
+ { .compatible = "ti,adc141s626", },
+ { .compatible = "ti,adc161s626", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
+
+static const struct spi_device_id ti_adc_id[] = {
+ {"adc141s626", TI_ADC141S626},
+ {"adc161s626", TI_ADC161S626},
+ {},
+};
+MODULE_DEVICE_TABLE(spi, ti_adc_id);
+
+static struct spi_driver ti_adc_driver = {
+ .driver = {
+ .name = TI_ADC_DRV_NAME,
+ .of_match_table = ti_adc_dt_ids,
+ },
+ .probe = ti_adc_probe,
+ .id_table = ti_adc_id,
+};
+module_spi_driver(ti_adc_driver);
+
+MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
+MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
new file mode 100644
index 000000000..8bceba694
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -0,0 +1,1207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ADS1015 - Texas Instruments Analog-to-Digital Converter
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * IIO driver for ADS1015 ADC 7-bit I2C slave address:
+ * * 0x48 - ADDR connected to Ground
+ * * 0x49 - ADDR connected to Vdd
+ * * 0x4A - ADDR connected to SDA
+ * * 0x4B - ADDR connected to SCL
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define ADS1015_DRV_NAME "ads1015"
+
+#define ADS1015_CHANNELS 8
+
+#define ADS1015_CONV_REG 0x00
+#define ADS1015_CFG_REG 0x01
+#define ADS1015_LO_THRESH_REG 0x02
+#define ADS1015_HI_THRESH_REG 0x03
+
+#define ADS1015_CFG_COMP_QUE_SHIFT 0
+#define ADS1015_CFG_COMP_LAT_SHIFT 2
+#define ADS1015_CFG_COMP_POL_SHIFT 3
+#define ADS1015_CFG_COMP_MODE_SHIFT 4
+#define ADS1015_CFG_DR_SHIFT 5
+#define ADS1015_CFG_MOD_SHIFT 8
+#define ADS1015_CFG_PGA_SHIFT 9
+#define ADS1015_CFG_MUX_SHIFT 12
+
+#define ADS1015_CFG_COMP_QUE_MASK GENMASK(1, 0)
+#define ADS1015_CFG_COMP_LAT_MASK BIT(2)
+#define ADS1015_CFG_COMP_POL_MASK BIT(3)
+#define ADS1015_CFG_COMP_MODE_MASK BIT(4)
+#define ADS1015_CFG_DR_MASK GENMASK(7, 5)
+#define ADS1015_CFG_MOD_MASK BIT(8)
+#define ADS1015_CFG_PGA_MASK GENMASK(11, 9)
+#define ADS1015_CFG_MUX_MASK GENMASK(14, 12)
+
+/* Comparator queue and disable field */
+#define ADS1015_CFG_COMP_DISABLE 3
+
+/* Comparator polarity field */
+#define ADS1015_CFG_COMP_POL_LOW 0
+#define ADS1015_CFG_COMP_POL_HIGH 1
+
+/* Comparator mode field */
+#define ADS1015_CFG_COMP_MODE_TRAD 0
+#define ADS1015_CFG_COMP_MODE_WINDOW 1
+
+/* device operating modes */
+#define ADS1015_CONTINUOUS 0
+#define ADS1015_SINGLESHOT 1
+
+#define ADS1015_SLEEP_DELAY_MS 2000
+#define ADS1015_DEFAULT_PGA 2
+#define ADS1015_DEFAULT_DATA_RATE 4
+#define ADS1015_DEFAULT_CHAN 0
+
+struct ads1015_chip_data {
+ struct iio_chan_spec const *channels;
+ int num_channels;
+ const struct iio_info *info;
+ const int *data_rate;
+ const int data_rate_len;
+ const int *scale;
+ const int scale_len;
+ bool has_comparator;
+};
+
+enum ads1015_channels {
+ ADS1015_AIN0_AIN1 = 0,
+ ADS1015_AIN0_AIN3,
+ ADS1015_AIN1_AIN3,
+ ADS1015_AIN2_AIN3,
+ ADS1015_AIN0,
+ ADS1015_AIN1,
+ ADS1015_AIN2,
+ ADS1015_AIN3,
+ ADS1015_TIMESTAMP,
+};
+
+static const int ads1015_data_rate[] = {
+ 128, 250, 490, 920, 1600, 2400, 3300, 3300
+};
+
+static const int ads1115_data_rate[] = {
+ 8, 16, 32, 64, 128, 250, 475, 860
+};
+
+/*
+ * Translation from PGA bits to full-scale positive and negative input voltage
+ * range in mV
+ */
+static const int ads1015_fullscale_range[] = {
+ 6144, 4096, 2048, 1024, 512, 256, 256, 256
+};
+
+static const int ads1015_scale[] = { /* 12bit ADC */
+ 256, 11,
+ 512, 11,
+ 1024, 11,
+ 2048, 11,
+ 4096, 11,
+ 6144, 11
+};
+
+static const int ads1115_scale[] = { /* 16bit ADC */
+ 256, 15,
+ 512, 15,
+ 1024, 15,
+ 2048, 15,
+ 4096, 15,
+ 6144, 15
+};
+
+/*
+ * Translation from COMP_QUE field value to the number of successive readings
+ * exceed the threshold values before an interrupt is generated
+ */
+static const int ads1015_comp_queue[] = { 1, 2, 4 };
+
+static const struct iio_event_spec ads1015_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),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD),
+ },
+};
+
+/*
+ * Compile-time check whether _fitbits can accommodate up to _testbits
+ * bits. Returns _fitbits on success, fails to compile otherwise.
+ *
+ * The test works such that it multiplies constant _fitbits by constant
+ * double-negation of size of a non-empty structure, i.e. it multiplies
+ * constant _fitbits by constant 1 in each successful compilation case.
+ * The non-empty structure may contain C11 _Static_assert(), make use of
+ * this and place the kernel variant of static assert in there, so that
+ * it performs the compile-time check for _testbits <= _fitbits. Note
+ * that it is not possible to directly use static_assert in compound
+ * statements, hence this convoluted construct.
+ */
+#define FIT_CHECK(_testbits, _fitbits) \
+ ( \
+ (_fitbits) * \
+ !!sizeof(struct { \
+ static_assert((_testbits) <= (_fitbits)); \
+ int pad; \
+ }) \
+ )
+
+#define ADS1015_V_CHAN(_chan, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .address = _addr, \
+ .channel = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _addr, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (_realbits), \
+ .storagebits = FIT_CHECK((_realbits) + (_shift), 16), \
+ .shift = (_shift), \
+ .endianness = IIO_CPU, \
+ }, \
+ .event_spec = (_event_spec), \
+ .num_event_specs = (_num_event_specs), \
+ .datasheet_name = "AIN"#_chan, \
+}
+
+#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .indexed = 1, \
+ .address = _addr, \
+ .channel = _chan, \
+ .channel2 = _chan2, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _addr, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (_realbits), \
+ .storagebits = FIT_CHECK((_realbits) + (_shift), 16), \
+ .shift = (_shift), \
+ .endianness = IIO_CPU, \
+ }, \
+ .event_spec = (_event_spec), \
+ .num_event_specs = (_num_event_specs), \
+ .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \
+}
+
+struct ads1015_channel_data {
+ bool enabled;
+ unsigned int pga;
+ unsigned int data_rate;
+};
+
+struct ads1015_thresh_data {
+ unsigned int comp_queue;
+ int high_thresh;
+ int low_thresh;
+};
+
+struct ads1015_data {
+ struct regmap *regmap;
+ /*
+ * Protects ADC ops, e.g: concurrent sysfs/buffered
+ * data reads, configuration updates
+ */
+ struct mutex lock;
+ struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
+
+ unsigned int event_channel;
+ unsigned int comp_mode;
+ struct ads1015_thresh_data thresh_data[ADS1015_CHANNELS];
+
+ const struct ads1015_chip_data *chip;
+ /*
+ * Set to true when the ADC is switched to the continuous-conversion
+ * mode and exits from a power-down state. This flag is used to avoid
+ * getting the stale result from the conversion register.
+ */
+ bool conv_invalid;
+};
+
+static bool ads1015_event_channel_enabled(struct ads1015_data *data)
+{
+ return (data->event_channel != ADS1015_CHANNELS);
+}
+
+static void ads1015_event_channel_enable(struct ads1015_data *data, int chan,
+ int comp_mode)
+{
+ WARN_ON(ads1015_event_channel_enabled(data));
+
+ data->event_channel = chan;
+ data->comp_mode = comp_mode;
+}
+
+static void ads1015_event_channel_disable(struct ads1015_data *data, int chan)
+{
+ data->event_channel = ADS1015_CHANNELS;
+}
+
+static const struct regmap_range ads1015_writeable_ranges[] = {
+ regmap_reg_range(ADS1015_CFG_REG, ADS1015_HI_THRESH_REG),
+};
+
+static const struct regmap_access_table ads1015_writeable_table = {
+ .yes_ranges = ads1015_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ads1015_writeable_ranges),
+};
+
+static const struct regmap_config ads1015_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = ADS1015_HI_THRESH_REG,
+ .wr_table = &ads1015_writeable_table,
+};
+
+static const struct regmap_range tla2024_writeable_ranges[] = {
+ regmap_reg_range(ADS1015_CFG_REG, ADS1015_CFG_REG),
+};
+
+static const struct regmap_access_table tla2024_writeable_table = {
+ .yes_ranges = tla2024_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(tla2024_writeable_ranges),
+};
+
+static const struct regmap_config tla2024_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = ADS1015_CFG_REG,
+ .wr_table = &tla2024_writeable_table,
+};
+
+static const struct iio_chan_spec ads1015_channels[] = {
+ ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(0, ADS1015_AIN0, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(1, ADS1015_AIN1, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(2, ADS1015_AIN2, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(3, ADS1015_AIN3, 12, 4,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
+};
+
+static const struct iio_chan_spec ads1115_channels[] = {
+ ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(0, ADS1015_AIN0, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(1, ADS1015_AIN1, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(2, ADS1015_AIN2, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ ADS1015_V_CHAN(3, ADS1015_AIN3, 16, 0,
+ ads1015_events, ARRAY_SIZE(ads1015_events)),
+ IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
+};
+
+static const struct iio_chan_spec tla2024_channels[] = {
+ ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1, 12, 4, NULL, 0),
+ ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3, 12, 4, NULL, 0),
+ ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3, 12, 4, NULL, 0),
+ ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3, 12, 4, NULL, 0),
+ ADS1015_V_CHAN(0, ADS1015_AIN0, 12, 4, NULL, 0),
+ ADS1015_V_CHAN(1, ADS1015_AIN1, 12, 4, NULL, 0),
+ ADS1015_V_CHAN(2, ADS1015_AIN2, 12, 4, NULL, 0),
+ ADS1015_V_CHAN(3, ADS1015_AIN3, 12, 4, NULL, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
+};
+
+
+#ifdef CONFIG_PM
+static int ads1015_set_power_state(struct ads1015_data *data, bool on)
+{
+ int ret;
+ struct device *dev = regmap_get_device(data->regmap);
+
+ if (on) {
+ ret = pm_runtime_resume_and_get(dev);
+ } else {
+ pm_runtime_mark_last_busy(dev);
+ ret = pm_runtime_put_autosuspend(dev);
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+#else /* !CONFIG_PM */
+
+static int ads1015_set_power_state(struct ads1015_data *data, bool on)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_PM */
+
+static
+int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
+{
+ const int *data_rate = data->chip->data_rate;
+ int ret, pga, dr, dr_old, conv_time;
+ unsigned int old, mask, cfg;
+
+ if (chan < 0 || chan >= ADS1015_CHANNELS)
+ return -EINVAL;
+
+ ret = regmap_read(data->regmap, ADS1015_CFG_REG, &old);
+ if (ret)
+ return ret;
+
+ pga = data->channel_data[chan].pga;
+ dr = data->channel_data[chan].data_rate;
+ mask = ADS1015_CFG_MUX_MASK | ADS1015_CFG_PGA_MASK |
+ ADS1015_CFG_DR_MASK;
+ cfg = chan << ADS1015_CFG_MUX_SHIFT | pga << ADS1015_CFG_PGA_SHIFT |
+ dr << ADS1015_CFG_DR_SHIFT;
+
+ if (ads1015_event_channel_enabled(data)) {
+ mask |= ADS1015_CFG_COMP_QUE_MASK | ADS1015_CFG_COMP_MODE_MASK;
+ cfg |= data->thresh_data[chan].comp_queue <<
+ ADS1015_CFG_COMP_QUE_SHIFT |
+ data->comp_mode <<
+ ADS1015_CFG_COMP_MODE_SHIFT;
+ }
+
+ cfg = (old & ~mask) | (cfg & mask);
+ if (old != cfg) {
+ ret = regmap_write(data->regmap, ADS1015_CFG_REG, cfg);
+ if (ret)
+ return ret;
+ data->conv_invalid = true;
+ }
+ if (data->conv_invalid) {
+ dr_old = (old & ADS1015_CFG_DR_MASK) >> ADS1015_CFG_DR_SHIFT;
+ conv_time = DIV_ROUND_UP(USEC_PER_SEC, data_rate[dr_old]);
+ conv_time += DIV_ROUND_UP(USEC_PER_SEC, data_rate[dr]);
+ conv_time += conv_time / 10; /* 10% internal clock inaccuracy */
+ usleep_range(conv_time, conv_time + 1);
+ data->conv_invalid = false;
+ }
+
+ return regmap_read(data->regmap, ADS1015_CONV_REG, val);
+}
+
+static irqreturn_t ads1015_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1015_data *data = iio_priv(indio_dev);
+ /* Ensure natural alignment of timestamp */
+ struct {
+ s16 chan;
+ s64 timestamp __aligned(8);
+ } scan;
+ int chan, ret, res;
+
+ memset(&scan, 0, sizeof(scan));
+
+ mutex_lock(&data->lock);
+ chan = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ ret = ads1015_get_adc_result(data, chan, &res);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto err;
+ }
+
+ scan.chan = res;
+ mutex_unlock(&data->lock);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &scan,
+ iio_get_time_ns(indio_dev));
+
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1015_set_scale(struct ads1015_data *data,
+ struct iio_chan_spec const *chan,
+ int scale, int uscale)
+{
+ int i;
+ int fullscale = div_s64((scale * 1000000LL + uscale) <<
+ (chan->scan_type.realbits - 1), 1000000);
+
+ for (i = 0; i < ARRAY_SIZE(ads1015_fullscale_range); i++) {
+ if (ads1015_fullscale_range[i] == fullscale) {
+ data->channel_data[chan->address].pga = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate)
+{
+ int i;
+
+ for (i = 0; i < data->chip->data_rate_len; i++) {
+ if (data->chip->data_rate[i] == rate) {
+ data->channel_data[chan].data_rate = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int ads1015_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct ads1015_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_FRACTIONAL_LOG2;
+ *vals = data->chip->scale;
+ *length = data->chip->scale_len;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT;
+ *vals = data->chip->data_rate;
+ *length = data->chip->data_rate_len;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads1015_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret, idx;
+ struct ads1015_data *data = iio_priv(indio_dev);
+
+ mutex_lock(&data->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(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;
+
+ ret = IIO_VAL_INT;
+release_direct:
+ iio_device_release_direct_mode(indio_dev);
+ break;
+ 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;
+ 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;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int ads1015_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct ads1015_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = ads1015_set_scale(data, chan, val, val2);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = ads1015_set_data_rate(data, chan->address, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int ads1015_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 ads1015_data *data = iio_priv(indio_dev);
+ int ret;
+ unsigned int comp_queue;
+ int period;
+ int dr;
+
+ mutex_lock(&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;
+ case IIO_EV_INFO_PERIOD:
+ dr = data->channel_data[chan->address].data_rate;
+ comp_queue = data->thresh_data[chan->address].comp_queue;
+ period = ads1015_comp_queue[comp_queue] *
+ USEC_PER_SEC / data->chip->data_rate[dr];
+
+ *val = period / USEC_PER_SEC;
+ *val2 = period % USEC_PER_SEC;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int ads1015_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 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);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val >= 1 << (realbits - 1) || val < -1 << (realbits - 1)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dir == IIO_EV_DIR_RISING)
+ data->thresh_data[chan->address].high_thresh = val;
+ else
+ data->thresh_data[chan->address].low_thresh = val;
+ break;
+ case IIO_EV_INFO_PERIOD:
+ dr = data->channel_data[chan->address].data_rate;
+ period = val * USEC_PER_SEC + val2;
+
+ for (i = 0; i < ARRAY_SIZE(ads1015_comp_queue) - 1; i++) {
+ if (period <= ads1015_comp_queue[i] *
+ USEC_PER_SEC / data_rate[dr])
+ break;
+ }
+ data->thresh_data[chan->address].comp_queue = i;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int ads1015_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 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);
+
+ return ret;
+}
+
+static int ads1015_enable_event_config(struct ads1015_data *data,
+ const struct iio_chan_spec *chan, int comp_mode)
+{
+ int low_thresh = data->thresh_data[chan->address].low_thresh;
+ int high_thresh = data->thresh_data[chan->address].high_thresh;
+ int ret;
+ unsigned int val;
+
+ if (ads1015_event_channel_enabled(data)) {
+ if (data->event_channel != chan->address ||
+ (data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD &&
+ comp_mode == ADS1015_CFG_COMP_MODE_WINDOW))
+ return -EBUSY;
+
+ return 0;
+ }
+
+ if (comp_mode == ADS1015_CFG_COMP_MODE_TRAD) {
+ low_thresh = max(-1 << (chan->scan_type.realbits - 1),
+ high_thresh - 1);
+ }
+ ret = regmap_write(data->regmap, ADS1015_LO_THRESH_REG,
+ low_thresh << chan->scan_type.shift);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, ADS1015_HI_THRESH_REG,
+ high_thresh << chan->scan_type.shift);
+ if (ret)
+ return ret;
+
+ ret = ads1015_set_power_state(data, true);
+ if (ret < 0)
+ return ret;
+
+ ads1015_event_channel_enable(data, chan->address, comp_mode);
+
+ ret = ads1015_get_adc_result(data, chan->address, &val);
+ if (ret) {
+ ads1015_event_channel_disable(data, chan->address);
+ ads1015_set_power_state(data, false);
+ }
+
+ return ret;
+}
+
+static int ads1015_disable_event_config(struct ads1015_data *data,
+ const struct iio_chan_spec *chan, int comp_mode)
+{
+ int ret;
+
+ if (!ads1015_event_channel_enabled(data))
+ return 0;
+
+ if (data->event_channel != chan->address)
+ return 0;
+
+ if (data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD &&
+ comp_mode == ADS1015_CFG_COMP_MODE_WINDOW)
+ return 0;
+
+ ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
+ ADS1015_CFG_COMP_QUE_MASK,
+ ADS1015_CFG_COMP_DISABLE <<
+ ADS1015_CFG_COMP_QUE_SHIFT);
+ if (ret)
+ return ret;
+
+ ads1015_event_channel_disable(data, chan->address);
+
+ return ads1015_set_power_state(data, false);
+}
+
+static int ads1015_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct ads1015_data *data = iio_priv(indio_dev);
+ int ret;
+ int comp_mode = (dir == IIO_EV_DIR_EITHER) ?
+ ADS1015_CFG_COMP_MODE_WINDOW : ADS1015_CFG_COMP_MODE_TRAD;
+
+ mutex_lock(&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 (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);
+
+ return ret;
+}
+
+static irqreturn_t ads1015_event_handler(int irq, void *priv)
+{
+ struct iio_dev *indio_dev = priv;
+ struct ads1015_data *data = iio_priv(indio_dev);
+ int val;
+ int ret;
+
+ /* Clear the latched ALERT/RDY pin */
+ ret = regmap_read(data->regmap, ADS1015_CONV_REG, &val);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if (ads1015_event_channel_enabled(data)) {
+ enum iio_event_direction dir;
+ u64 code;
+
+ dir = data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD ?
+ IIO_EV_DIR_RISING : IIO_EV_DIR_EITHER;
+ code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, data->event_channel,
+ IIO_EV_TYPE_THRESH, dir);
+ iio_push_event(indio_dev, code, iio_get_time_ns(indio_dev));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ads1015_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1015_data *data = iio_priv(indio_dev);
+
+ /* Prevent from enabling both buffer and event at a time */
+ if (ads1015_event_channel_enabled(data))
+ return -EBUSY;
+
+ return ads1015_set_power_state(iio_priv(indio_dev), true);
+}
+
+static int ads1015_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ return ads1015_set_power_state(iio_priv(indio_dev), false);
+}
+
+static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
+ .preenable = ads1015_buffer_preenable,
+ .postdisable = ads1015_buffer_postdisable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static const struct iio_info ads1015_info = {
+ .read_avail = ads1015_read_avail,
+ .read_raw = ads1015_read_raw,
+ .write_raw = ads1015_write_raw,
+ .read_event_value = ads1015_read_event,
+ .write_event_value = ads1015_write_event,
+ .read_event_config = ads1015_read_event_config,
+ .write_event_config = ads1015_write_event_config,
+};
+
+static const struct iio_info tla2024_info = {
+ .read_avail = ads1015_read_avail,
+ .read_raw = ads1015_read_raw,
+ .write_raw = ads1015_write_raw,
+};
+
+static int ads1015_client_get_channels_config(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ads1015_data *data = iio_priv(indio_dev);
+ struct device *dev = &client->dev;
+ struct fwnode_handle *node;
+ int i = -1;
+
+ device_for_each_child_node(dev, node) {
+ u32 pval;
+ unsigned int channel;
+ unsigned int pga = ADS1015_DEFAULT_PGA;
+ unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
+
+ if (fwnode_property_read_u32(node, "reg", &pval)) {
+ dev_err(dev, "invalid reg on %pfw\n", node);
+ continue;
+ }
+
+ channel = pval;
+ if (channel >= ADS1015_CHANNELS) {
+ dev_err(dev, "invalid channel index %d on %pfw\n",
+ channel, node);
+ continue;
+ }
+
+ if (!fwnode_property_read_u32(node, "ti,gain", &pval)) {
+ pga = pval;
+ if (pga > 6) {
+ dev_err(dev, "invalid gain on %pfw\n", node);
+ fwnode_handle_put(node);
+ return -EINVAL;
+ }
+ }
+
+ if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) {
+ data_rate = pval;
+ if (data_rate > 7) {
+ dev_err(dev, "invalid data_rate on %pfw\n", node);
+ fwnode_handle_put(node);
+ return -EINVAL;
+ }
+ }
+
+ data->channel_data[channel].pga = pga;
+ data->channel_data[channel].data_rate = data_rate;
+
+ i++;
+ }
+
+ return i < 0 ? -EINVAL : 0;
+}
+
+static void ads1015_get_channels_config(struct i2c_client *client)
+{
+ unsigned int k;
+
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ads1015_data *data = iio_priv(indio_dev);
+
+ if (!ads1015_client_get_channels_config(client))
+ return;
+
+ /* fallback on default configuration */
+ for (k = 0; k < ADS1015_CHANNELS; ++k) {
+ data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
+ data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE;
+ }
+}
+
+static int ads1015_set_conv_mode(struct ads1015_data *data, int mode)
+{
+ return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
+ ADS1015_CFG_MOD_MASK,
+ mode << ADS1015_CFG_MOD_SHIFT);
+}
+
+static int ads1015_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct ads1015_chip_data *chip;
+ struct iio_dev *indio_dev;
+ struct ads1015_data *data;
+ int ret;
+ int i;
+
+ chip = device_get_match_data(&client->dev);
+ if (!chip)
+ chip = (const struct ads1015_chip_data *)id->driver_data;
+ if (!chip)
+ return dev_err_probe(&client->dev, -EINVAL, "Unknown chip\n");
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+
+ mutex_init(&data->lock);
+
+ indio_dev->name = ADS1015_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = chip->channels;
+ indio_dev->num_channels = chip->num_channels;
+ indio_dev->info = chip->info;
+ data->chip = chip;
+ data->event_channel = ADS1015_CHANNELS;
+
+ /*
+ * Set default lower and upper threshold to min and max value
+ * respectively.
+ */
+ for (i = 0; i < ADS1015_CHANNELS; i++) {
+ int realbits = indio_dev->channels[i].scan_type.realbits;
+
+ data->thresh_data[i].low_thresh = -1 << (realbits - 1);
+ data->thresh_data[i].high_thresh = (1 << (realbits - 1)) - 1;
+ }
+
+ /* we need to keep this ABI the same as used by hwmon ADS1015 driver */
+ ads1015_get_channels_config(client);
+
+ data->regmap = devm_regmap_init_i2c(client, chip->has_comparator ?
+ &ads1015_regmap_config :
+ &tla2024_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "Failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
+ ads1015_trigger_handler,
+ &ads1015_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+
+ if (client->irq && chip->has_comparator) {
+ unsigned long irq_trig =
+ irqd_get_trigger_type(irq_get_irq_data(client->irq));
+ unsigned int cfg_comp_mask = ADS1015_CFG_COMP_QUE_MASK |
+ ADS1015_CFG_COMP_LAT_MASK | ADS1015_CFG_COMP_POL_MASK;
+ unsigned int cfg_comp =
+ ADS1015_CFG_COMP_DISABLE << ADS1015_CFG_COMP_QUE_SHIFT |
+ 1 << ADS1015_CFG_COMP_LAT_SHIFT;
+
+ switch (irq_trig) {
+ case IRQF_TRIGGER_LOW:
+ cfg_comp |= ADS1015_CFG_COMP_POL_LOW <<
+ ADS1015_CFG_COMP_POL_SHIFT;
+ break;
+ case IRQF_TRIGGER_HIGH:
+ cfg_comp |= ADS1015_CFG_COMP_POL_HIGH <<
+ ADS1015_CFG_COMP_POL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
+ cfg_comp_mask, cfg_comp);
+ if (ret)
+ return ret;
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, ads1015_event_handler,
+ irq_trig | IRQF_ONESHOT,
+ client->name, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = ads1015_set_conv_mode(data, ADS1015_CONTINUOUS);
+ if (ret)
+ return ret;
+
+ data->conv_invalid = true;
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret)
+ return ret;
+ pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_enable(&client->dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register IIO device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ads1015_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ads1015_data *data = iio_priv(indio_dev);
+ int ret;
+
+ iio_device_unregister(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ /* power down single shot mode */
+ ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT);
+ if (ret)
+ dev_warn(&client->dev, "Failed to power down (%pe)\n",
+ ERR_PTR(ret));
+}
+
+#ifdef CONFIG_PM
+static int ads1015_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ads1015_data *data = iio_priv(indio_dev);
+
+ return ads1015_set_conv_mode(data, ADS1015_SINGLESHOT);
+}
+
+static int ads1015_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ads1015_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = ads1015_set_conv_mode(data, ADS1015_CONTINUOUS);
+ if (!ret)
+ data->conv_invalid = true;
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops ads1015_pm_ops = {
+ SET_RUNTIME_PM_OPS(ads1015_runtime_suspend,
+ ads1015_runtime_resume, NULL)
+};
+
+static const struct ads1015_chip_data ads1015_data = {
+ .channels = ads1015_channels,
+ .num_channels = ARRAY_SIZE(ads1015_channels),
+ .info = &ads1015_info,
+ .data_rate = ads1015_data_rate,
+ .data_rate_len = ARRAY_SIZE(ads1015_data_rate),
+ .scale = ads1015_scale,
+ .scale_len = ARRAY_SIZE(ads1015_scale),
+ .has_comparator = true,
+};
+
+static const struct ads1015_chip_data ads1115_data = {
+ .channels = ads1115_channels,
+ .num_channels = ARRAY_SIZE(ads1115_channels),
+ .info = &ads1015_info,
+ .data_rate = ads1115_data_rate,
+ .data_rate_len = ARRAY_SIZE(ads1115_data_rate),
+ .scale = ads1115_scale,
+ .scale_len = ARRAY_SIZE(ads1115_scale),
+ .has_comparator = true,
+};
+
+static const struct ads1015_chip_data tla2024_data = {
+ .channels = tla2024_channels,
+ .num_channels = ARRAY_SIZE(tla2024_channels),
+ .info = &tla2024_info,
+ .data_rate = ads1015_data_rate,
+ .data_rate_len = ARRAY_SIZE(ads1015_data_rate),
+ .scale = ads1015_scale,
+ .scale_len = ARRAY_SIZE(ads1015_scale),
+ .has_comparator = false,
+};
+
+static const struct i2c_device_id ads1015_id[] = {
+ { "ads1015", (kernel_ulong_t)&ads1015_data },
+ { "ads1115", (kernel_ulong_t)&ads1115_data },
+ { "tla2024", (kernel_ulong_t)&tla2024_data },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ads1015_id);
+
+static const struct of_device_id ads1015_of_match[] = {
+ { .compatible = "ti,ads1015", .data = &ads1015_data },
+ { .compatible = "ti,ads1115", .data = &ads1115_data },
+ { .compatible = "ti,tla2024", .data = &tla2024_data },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads1015_of_match);
+
+static struct i2c_driver ads1015_driver = {
+ .driver = {
+ .name = ADS1015_DRV_NAME,
+ .of_match_table = ads1015_of_match,
+ .pm = &ads1015_pm_ops,
+ },
+ .probe = ads1015_probe,
+ .remove = ads1015_remove,
+ .id_table = ads1015_id,
+};
+
+module_i2c_driver(ads1015_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
new file mode 100644
index 000000000..4ca62121f
--- /dev/null
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0
+/* TI ADS124S0X chip family driver
+ * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+
+#include <asm/unaligned.h>
+
+/* Commands */
+#define ADS124S08_CMD_NOP 0x00
+#define ADS124S08_CMD_WAKEUP 0x02
+#define ADS124S08_CMD_PWRDWN 0x04
+#define ADS124S08_CMD_RESET 0x06
+#define ADS124S08_CMD_START 0x08
+#define ADS124S08_CMD_STOP 0x0a
+#define ADS124S08_CMD_SYOCAL 0x16
+#define ADS124S08_CMD_SYGCAL 0x17
+#define ADS124S08_CMD_SFOCAL 0x19
+#define ADS124S08_CMD_RDATA 0x12
+#define ADS124S08_CMD_RREG 0x20
+#define ADS124S08_CMD_WREG 0x40
+
+/* Registers */
+#define ADS124S08_ID_REG 0x00
+#define ADS124S08_STATUS 0x01
+#define ADS124S08_INPUT_MUX 0x02
+#define ADS124S08_PGA 0x03
+#define ADS124S08_DATA_RATE 0x04
+#define ADS124S08_REF 0x05
+#define ADS124S08_IDACMAG 0x06
+#define ADS124S08_IDACMUX 0x07
+#define ADS124S08_VBIAS 0x08
+#define ADS124S08_SYS 0x09
+#define ADS124S08_OFCAL0 0x0a
+#define ADS124S08_OFCAL1 0x0b
+#define ADS124S08_OFCAL2 0x0c
+#define ADS124S08_FSCAL0 0x0d
+#define ADS124S08_FSCAL1 0x0e
+#define ADS124S08_FSCAL2 0x0f
+#define ADS124S08_GPIODAT 0x10
+#define ADS124S08_GPIOCON 0x11
+
+/* ADS124S0x common channels */
+#define ADS124S08_AIN0 0x00
+#define ADS124S08_AIN1 0x01
+#define ADS124S08_AIN2 0x02
+#define ADS124S08_AIN3 0x03
+#define ADS124S08_AIN4 0x04
+#define ADS124S08_AIN5 0x05
+#define ADS124S08_AINCOM 0x0c
+/* ADS124S08 only channels */
+#define ADS124S08_AIN6 0x06
+#define ADS124S08_AIN7 0x07
+#define ADS124S08_AIN8 0x08
+#define ADS124S08_AIN9 0x09
+#define ADS124S08_AIN10 0x0a
+#define ADS124S08_AIN11 0x0b
+#define ADS124S08_MAX_CHANNELS 12
+
+#define ADS124S08_POS_MUX_SHIFT 0x04
+#define ADS124S08_INT_REF 0x09
+
+#define ADS124S08_START_REG_MASK 0x1f
+#define ADS124S08_NUM_BYTES_MASK 0x1f
+
+#define ADS124S08_START_CONV 0x01
+#define ADS124S08_STOP_CONV 0x00
+
+enum ads124s_id {
+ ADS124S08_ID,
+ ADS124S06_ID,
+};
+
+struct ads124s_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct ads124s_private {
+ const struct ads124s_chip_info *chip_info;
+ struct gpio_desc *reset_gpio;
+ struct spi_device *spi;
+ struct mutex lock;
+ /*
+ * Used to correctly align data.
+ * Ensure timestamp is naturally aligned.
+ * Note that the full buffer length may not be needed if not
+ * all channels are enabled, as long as the alignment of the
+ * timestamp is maintained.
+ */
+ u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u32)] __aligned(8);
+ u8 data[5] __aligned(IIO_DMA_MINALIGN);
+};
+
+#define ADS124S08_CHAN(index) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ }, \
+}
+
+static const struct iio_chan_spec ads124s06_channels[] = {
+ ADS124S08_CHAN(0),
+ ADS124S08_CHAN(1),
+ ADS124S08_CHAN(2),
+ ADS124S08_CHAN(3),
+ ADS124S08_CHAN(4),
+ ADS124S08_CHAN(5),
+};
+
+static const struct iio_chan_spec ads124s08_channels[] = {
+ ADS124S08_CHAN(0),
+ ADS124S08_CHAN(1),
+ ADS124S08_CHAN(2),
+ ADS124S08_CHAN(3),
+ ADS124S08_CHAN(4),
+ ADS124S08_CHAN(5),
+ ADS124S08_CHAN(6),
+ ADS124S08_CHAN(7),
+ ADS124S08_CHAN(8),
+ ADS124S08_CHAN(9),
+ ADS124S08_CHAN(10),
+ ADS124S08_CHAN(11),
+};
+
+static const struct ads124s_chip_info ads124s_chip_info_tbl[] = {
+ [ADS124S08_ID] = {
+ .channels = ads124s08_channels,
+ .num_channels = ARRAY_SIZE(ads124s08_channels),
+ },
+ [ADS124S06_ID] = {
+ .channels = ads124s06_channels,
+ .num_channels = ARRAY_SIZE(ads124s06_channels),
+ },
+};
+
+static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ priv->data[0] = command;
+
+ return spi_write(priv->spi, &priv->data[0], 1);
+}
+
+static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ priv->data[0] = ADS124S08_CMD_WREG | reg;
+ priv->data[1] = 0x0;
+ priv->data[2] = data;
+
+ return spi_write(priv->spi, &priv->data[0], 3);
+}
+
+static int ads124s_reset(struct iio_dev *indio_dev)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ if (priv->reset_gpio) {
+ gpiod_set_value(priv->reset_gpio, 0);
+ udelay(200);
+ gpiod_set_value(priv->reset_gpio, 1);
+ } else {
+ return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);
+ }
+
+ return 0;
+};
+
+static int ads124s_read(struct iio_dev *indio_dev)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &priv->data[0],
+ .len = 4,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &priv->data[1],
+ .rx_buf = &priv->data[1],
+ .len = 4,
+ },
+ };
+
+ priv->data[0] = ADS124S08_CMD_RDATA;
+ memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data) - 1);
+
+ ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return get_unaligned_be24(&priv->data[2]);
+}
+
+static int ads124s_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&priv->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
+ chan->channel);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Set ADC CH failed\n");
+ goto out;
+ }
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Start conversions failed\n");
+ goto out;
+ }
+
+ ret = ads124s_read(indio_dev);
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "Read ADC failed\n");
+ goto out;
+ }
+
+ *val = ret;
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Stop conversions failed\n");
+ goto out;
+ }
+
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct iio_info ads124s_info = {
+ .read_raw = &ads124s_read_raw,
+};
+
+static irqreturn_t ads124s_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int scan_index, j = 0;
+ int ret;
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
+ scan_index);
+ if (ret)
+ dev_err(&priv->spi->dev, "Set ADC CH failed\n");
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
+ if (ret)
+ dev_err(&priv->spi->dev, "Start ADC conversions failed\n");
+
+ priv->buffer[j] = ads124s_read(indio_dev);
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
+ if (ret)
+ dev_err(&priv->spi->dev, "Stop ADC conversions failed\n");
+
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads124s_probe(struct spi_device *spi)
+{
+ struct ads124s_private *ads124s_priv;
+ struct iio_dev *indio_dev;
+ const struct spi_device_id *spi_id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ ads124s_priv = iio_priv(indio_dev);
+
+ ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ads124s_priv->reset_gpio))
+ dev_info(&spi->dev, "Reset GPIO not defined\n");
+
+ ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
+
+ ads124s_priv->spi = spi;
+
+ indio_dev->name = spi_id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads124s_priv->chip_info->channels;
+ indio_dev->num_channels = ads124s_priv->chip_info->num_channels;
+ indio_dev->info = &ads124s_info;
+
+ mutex_init(&ads124s_priv->lock);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ ads124s_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&spi->dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+
+ ads124s_reset(indio_dev);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ads124s_id[] = {
+ { "ads124s06", ADS124S06_ID },
+ { "ads124s08", ADS124S08_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads124s_id);
+
+static const struct of_device_id ads124s_of_table[] = {
+ { .compatible = "ti,ads124s06" },
+ { .compatible = "ti,ads124s08" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads124s_of_table);
+
+static struct spi_driver ads124s_driver = {
+ .driver = {
+ .name = "ads124s08",
+ .of_match_table = ads124s_of_table,
+ },
+ .probe = ads124s_probe,
+ .id_table = ads124s_id,
+};
+module_spi_driver(ads124s_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>");
+MODULE_DESCRIPTION("TI TI_ADS12S0X ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
new file mode 100644
index 000000000..5235a93f2
--- /dev/null
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -0,0 +1,940 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs
+ *
+ * Copyright (c) 2020 AVL DiTEST GmbH
+ * Tomislav Denis <tomislav.denis@avl.com>
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <asm/unaligned.h>
+
+/* Commands */
+#define ADS131E08_CMD_RESET 0x06
+#define ADS131E08_CMD_START 0x08
+#define ADS131E08_CMD_STOP 0x0A
+#define ADS131E08_CMD_OFFSETCAL 0x1A
+#define ADS131E08_CMD_SDATAC 0x11
+#define ADS131E08_CMD_RDATA 0x12
+#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0)))
+#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0)))
+
+/* Registers */
+#define ADS131E08_ADR_CFG1R 0x01
+#define ADS131E08_ADR_CFG3R 0x03
+#define ADS131E08_ADR_CH0R 0x05
+
+/* Configuration register 1 */
+#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0)
+
+/* Configuration register 3 */
+#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7)
+#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5)
+
+/* Channel settings register */
+#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4)
+#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0)
+#define ADS131E08_CHR_PWD_MASK BIT(7)
+
+/* ADC misc */
+#define ADS131E08_DEFAULT_DATA_RATE 1
+#define ADS131E08_DEFAULT_PGA_GAIN 1
+#define ADS131E08_DEFAULT_MUX 0
+
+#define ADS131E08_VREF_2V4_mV 2400
+#define ADS131E08_VREF_4V_mV 4000
+
+#define ADS131E08_WAIT_RESET_CYCLES 18
+#define ADS131E08_WAIT_SDECODE_CYCLES 4
+#define ADS131E08_WAIT_OFFSETCAL_MS 153
+#define ADS131E08_MAX_SETTLING_TIME_MS 6
+
+#define ADS131E08_NUM_STATUS_BYTES 3
+#define ADS131E08_NUM_DATA_BYTES_MAX 24
+#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3)
+#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8)
+#define ADS131E08_NUM_STORAGE_BYTES 4
+
+enum ads131e08_ids {
+ ads131e04,
+ ads131e06,
+ ads131e08,
+};
+
+struct ads131e08_info {
+ unsigned int max_channels;
+ const char *name;
+};
+
+struct ads131e08_channel_config {
+ unsigned int pga_gain;
+ unsigned int mux;
+};
+
+struct ads131e08_state {
+ const struct ads131e08_info *info;
+ struct spi_device *spi;
+ struct iio_trigger *trig;
+ struct clk *adc_clk;
+ struct regulator *vref_reg;
+ struct ads131e08_channel_config *channel_config;
+ unsigned int data_rate;
+ unsigned int vref_mv;
+ unsigned int sdecode_delay_us;
+ unsigned int reset_delay_us;
+ unsigned int readback_len;
+ struct completion completion;
+ struct {
+ u8 data[ADS131E08_NUM_DATA_BYTES_MAX];
+ s64 ts __aligned(8);
+ } tmp_buf;
+
+ u8 tx_buf[3] __aligned(IIO_DMA_MINALIGN);
+ /*
+ * Add extra one padding byte to be able to access the last channel
+ * value using u32 pointer
+ */
+ u8 rx_buf[ADS131E08_NUM_STATUS_BYTES +
+ ADS131E08_NUM_DATA_BYTES_MAX + 1];
+};
+
+static const struct ads131e08_info ads131e08_info_tbl[] = {
+ [ads131e04] = {
+ .max_channels = 4,
+ .name = "ads131e04",
+ },
+ [ads131e06] = {
+ .max_channels = 6,
+ .name = "ads131e06",
+ },
+ [ads131e08] = {
+ .max_channels = 8,
+ .name = "ads131e08",
+ },
+};
+
+struct ads131e08_data_rate_desc {
+ unsigned int rate; /* data rate in kSPS */
+ u8 reg; /* reg value */
+};
+
+static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = {
+ { .rate = 64, .reg = 0x00 },
+ { .rate = 32, .reg = 0x01 },
+ { .rate = 16, .reg = 0x02 },
+ { .rate = 8, .reg = 0x03 },
+ { .rate = 4, .reg = 0x04 },
+ { .rate = 2, .reg = 0x05 },
+ { .rate = 1, .reg = 0x06 },
+};
+
+struct ads131e08_pga_gain_desc {
+ unsigned int gain; /* PGA gain value */
+ u8 reg; /* field value */
+};
+
+static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = {
+ { .gain = 1, .reg = 0x01 },
+ { .gain = 2, .reg = 0x02 },
+ { .gain = 4, .reg = 0x04 },
+ { .gain = 8, .reg = 0x05 },
+ { .gain = 12, .reg = 0x06 },
+};
+
+static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 };
+
+static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd)
+{
+ int ret;
+
+ ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0);
+ if (ret)
+ dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd);
+
+ return ret;
+}
+
+static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg)
+{
+ int ret;
+ struct spi_transfer transfer[] = {
+ {
+ .tx_buf = &st->tx_buf,
+ .len = 2,
+ .delay = {
+ .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
+ }, {
+ .rx_buf = &st->rx_buf,
+ .len = 1,
+ },
+ };
+
+ st->tx_buf[0] = ADS131E08_CMD_RREG(reg);
+ st->tx_buf[1] = 0;
+
+ ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+ if (ret) {
+ dev_err(&st->spi->dev, "Read register failed\n");
+ return ret;
+ }
+
+ return st->rx_buf[0];
+}
+
+static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value)
+{
+ int ret;
+ struct spi_transfer transfer[] = {
+ {
+ .tx_buf = &st->tx_buf,
+ .len = 3,
+ .delay = {
+ .value = st->sdecode_delay_us,
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
+ }
+ };
+
+ st->tx_buf[0] = ADS131E08_CMD_WREG(reg);
+ st->tx_buf[1] = 0;
+ st->tx_buf[2] = value;
+
+ ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+ if (ret)
+ dev_err(&st->spi->dev, "Write register failed\n");
+
+ return ret;
+}
+
+static int ads131e08_read_data(struct ads131e08_state *st, int rx_len)
+{
+ int ret;
+ struct spi_transfer transfer[] = {
+ {
+ .tx_buf = &st->tx_buf,
+ .len = 1,
+ }, {
+ .rx_buf = &st->rx_buf,
+ .len = rx_len,
+ },
+ };
+
+ st->tx_buf[0] = ADS131E08_CMD_RDATA;
+
+ ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer));
+ if (ret)
+ dev_err(&st->spi->dev, "Read data failed\n");
+
+ return ret;
+}
+
+static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate)
+{
+ int i, reg, ret;
+
+ for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) {
+ if (ads131e08_data_rate_tbl[i].rate == data_rate)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) {
+ dev_err(&st->spi->dev, "invalid data rate value\n");
+ return -EINVAL;
+ }
+
+ reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~ADS131E08_CFG1R_DR_MASK;
+ reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK,
+ ads131e08_data_rate_tbl[i].reg);
+
+ ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg);
+ if (ret)
+ return ret;
+
+ st->data_rate = data_rate;
+ st->readback_len = ADS131E08_NUM_STATUS_BYTES +
+ ADS131E08_NUM_DATA_BYTES(st->data_rate) *
+ st->info->max_channels;
+
+ return 0;
+}
+
+static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st,
+ unsigned int pga_gain)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); i++) {
+ if (ads131e08_pga_gain_tbl[i].gain == pga_gain)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) {
+ dev_err(&st->spi->dev, "invalid PGA gain value\n");
+ return -EINVAL;
+ }
+
+ return ads131e08_pga_gain_tbl[i].reg;
+}
+
+static int ads131e08_set_pga_gain(struct ads131e08_state *st,
+ unsigned int channel, unsigned int pga_gain)
+{
+ int field_value, reg;
+
+ field_value = ads131e08_pga_gain_to_field_value(st, pga_gain);
+ if (field_value < 0)
+ return field_value;
+
+ reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~ADS131E08_CHR_GAIN_MASK;
+ reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value);
+
+ return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
+}
+
+static int ads131e08_validate_channel_mux(struct ads131e08_state *st,
+ unsigned int mux)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); i++) {
+ if (ads131e08_valid_channel_mux_values[i] == mux)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) {
+ dev_err(&st->spi->dev, "invalid channel mux value\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ads131e08_set_channel_mux(struct ads131e08_state *st,
+ unsigned int channel, unsigned int mux)
+{
+ int reg;
+
+ reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~ADS131E08_CHR_MUX_MASK;
+ reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux);
+
+ return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
+}
+
+static int ads131e08_power_down_channel(struct ads131e08_state *st,
+ unsigned int channel, bool value)
+{
+ int reg;
+
+ reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~ADS131E08_CHR_PWD_MASK;
+ reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value);
+
+ return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg);
+}
+
+static int ads131e08_config_reference_voltage(struct ads131e08_state *st)
+{
+ int reg;
+
+ reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK;
+ if (!st->vref_reg) {
+ reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1);
+ reg &= ~ADS131E08_CFG3R_VREF_4V_MASK;
+ reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK,
+ st->vref_mv == ADS131E08_VREF_4V_mV);
+ }
+
+ return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg);
+}
+
+static int ads131e08_initial_config(struct iio_dev *indio_dev)
+{
+ const struct iio_chan_spec *channel = indio_dev->channels;
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ unsigned long active_channels = 0;
+ int ret, i;
+
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET);
+ if (ret)
+ return ret;
+
+ udelay(st->reset_delay_us);
+
+ /* Disable read data in continuous mode (enabled by default) */
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_config_reference_voltage(st);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ ret = ads131e08_set_pga_gain(st, channel->channel,
+ st->channel_config[i].pga_gain);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_set_channel_mux(st, channel->channel,
+ st->channel_config[i].mux);
+ if (ret)
+ return ret;
+
+ active_channels |= BIT(channel->channel);
+ channel++;
+ }
+
+ /* Power down unused channels */
+ for_each_clear_bit(i, &active_channels, st->info->max_channels) {
+ ret = ads131e08_power_down_channel(st, i, true);
+ if (ret)
+ return ret;
+ }
+
+ /* Request channel offset calibration */
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL);
+ if (ret)
+ return ret;
+
+ /*
+ * Channel offset calibration is triggered with the first START
+ * command. Since calibration takes more time than settling operation,
+ * this causes timeout error when command START is sent first
+ * time (e.g. first call of the ads131e08_read_direct method).
+ * To avoid this problem offset calibration is triggered here.
+ */
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
+ if (ret)
+ return ret;
+
+ msleep(ADS131E08_WAIT_OFFSETCAL_MS);
+
+ return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
+}
+
+static int ads131e08_pool_data(struct ads131e08_state *st)
+{
+ unsigned long timeout;
+ int ret;
+
+ reinit_completion(&st->completion);
+
+ ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START);
+ if (ret)
+ return ret;
+
+ timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS);
+ ret = wait_for_completion_timeout(&st->completion, timeout);
+ if (!ret)
+ return -ETIMEDOUT;
+
+ ret = ads131e08_read_data(st, st->readback_len);
+ if (ret)
+ return ret;
+
+ return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP);
+}
+
+static int ads131e08_read_direct(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *value)
+{
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ u8 num_bits, *src;
+ int ret;
+
+ ret = ads131e08_pool_data(st);
+ if (ret)
+ return ret;
+
+ src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES +
+ channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate);
+
+ num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate);
+ *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1);
+
+ return 0;
+}
+
+static int ads131e08_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *value,
+ int *value2, long mask)
+{
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_read_direct(indio_dev, channel, value);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (st->vref_reg) {
+ ret = regulator_get_voltage(st->vref_reg);
+ if (ret < 0)
+ return ret;
+
+ *value = ret / 1000;
+ } else {
+ *value = st->vref_mv;
+ }
+
+ *value /= st->channel_config[channel->address].pga_gain;
+ *value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *value = st->data_rate;
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads131e08_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int value,
+ int value2, long mask)
+{
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ads131e08_set_data_rate(st, value);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64");
+
+static struct attribute *ads131e08_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ads131e08_attribute_group = {
+ .attrs = ads131e08_attributes,
+};
+
+static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval, unsigned int *readval)
+{
+ struct ads131e08_state *st = iio_priv(indio_dev);
+
+ if (readval) {
+ int ret = ads131e08_read_reg(st, reg);
+ *readval = ret;
+ return ret;
+ }
+
+ return ads131e08_write_reg(st, reg, writeval);
+}
+
+static const struct iio_info ads131e08_iio_info = {
+ .read_raw = ads131e08_read_raw,
+ .write_raw = ads131e08_write_raw,
+ .attrs = &ads131e08_attribute_group,
+ .debugfs_reg_access = &ads131e08_debugfs_reg_access,
+};
+
+static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP;
+
+ return ads131e08_exec_cmd(st, cmd);
+}
+
+static const struct iio_trigger_ops ads131e08_trigger_ops = {
+ .set_trigger_state = &ads131e08_set_trigger_state,
+ .validate_device = &iio_trigger_validate_own_device,
+};
+
+static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ unsigned int chn, i = 0;
+ u8 *src, *dest;
+ int ret;
+
+ /*
+ * The number of data bits per channel depends on the data rate.
+ * For 32 and 64 ksps data rates, number of data bits per channel
+ * is 16. This case is not compliant with used (fixed) scan element
+ * type (be:s24/32>>8). So we use a little tweak to pack properly
+ * 16 bits of data into the buffer.
+ */
+ unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate);
+ u8 tweek_offset = num_bytes == 2 ? 1 : 0;
+
+ if (iio_trigger_using_own(indio_dev))
+ ret = ads131e08_read_data(st, st->readback_len);
+ else
+ ret = ads131e08_pool_data(st);
+
+ if (ret)
+ goto out;
+
+ for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) {
+ src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes;
+ dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
+
+ /*
+ * Tweek offset is 0:
+ * +---+---+---+---+
+ * |D0 |D1 |D2 | X | (3 data bytes)
+ * +---+---+---+---+
+ * a+0 a+1 a+2 a+3
+ *
+ * Tweek offset is 1:
+ * +---+---+---+---+
+ * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes)
+ * +---+---+---+---+
+ * a+0 a+1 a+2 a+3
+ */
+ memcpy(dest + tweek_offset, src, num_bytes);
+
+ /*
+ * Data conversion from 16 bits of data to 24 bits of data
+ * is done by sign extension (properly filling padding byte).
+ */
+ if (tweek_offset)
+ *dest = *src & BIT(7) ? 0xff : 0x00;
+
+ i++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data,
+ iio_get_time_ns(indio_dev));
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ads131e08_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ads131e08_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev))
+ iio_trigger_poll(st->trig);
+ else
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
+{
+ struct ads131e08_state *st = iio_priv(indio_dev);
+ struct ads131e08_channel_config *channel_config;
+ struct device *dev = &st->spi->dev;
+ struct iio_chan_spec *channels;
+ struct fwnode_handle *node;
+ unsigned int channel, tmp;
+ int num_channels, i, ret;
+
+ ret = device_property_read_u32(dev, "ti,vref-internal", &tmp);
+ if (ret)
+ tmp = 0;
+
+ switch (tmp) {
+ case 0:
+ st->vref_mv = ADS131E08_VREF_2V4_mV;
+ break;
+ case 1:
+ st->vref_mv = ADS131E08_VREF_4V_mV;
+ break;
+ default:
+ dev_err(&st->spi->dev, "invalid internal voltage reference\n");
+ return -EINVAL;
+ }
+
+ num_channels = device_get_child_node_count(dev);
+ if (num_channels == 0) {
+ dev_err(&st->spi->dev, "no channel children\n");
+ return -ENODEV;
+ }
+
+ if (num_channels > st->info->max_channels) {
+ dev_err(&st->spi->dev, "num of channel children out of range\n");
+ return -EINVAL;
+ }
+
+ channels = devm_kcalloc(&st->spi->dev, num_channels,
+ sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ channel_config = devm_kcalloc(&st->spi->dev, num_channels,
+ sizeof(*channel_config), GFP_KERNEL);
+ if (!channel_config)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_child_node(dev, node) {
+ ret = fwnode_property_read_u32(node, "reg", &channel);
+ if (ret)
+ goto err_child_out;
+
+ ret = fwnode_property_read_u32(node, "ti,gain", &tmp);
+ if (ret) {
+ channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN;
+ } else {
+ ret = ads131e08_pga_gain_to_field_value(st, tmp);
+ if (ret < 0)
+ goto err_child_out;
+
+ channel_config[i].pga_gain = tmp;
+ }
+
+ ret = fwnode_property_read_u32(node, "ti,mux", &tmp);
+ if (ret) {
+ channel_config[i].mux = ADS131E08_DEFAULT_MUX;
+ } else {
+ ret = ads131e08_validate_channel_mux(st, tmp);
+ if (ret)
+ goto err_child_out;
+
+ channel_config[i].mux = tmp;
+ }
+
+ channels[i].type = IIO_VOLTAGE;
+ channels[i].indexed = 1;
+ channels[i].channel = channel;
+ channels[i].address = i;
+ channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE);
+ channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ channels[i].scan_index = channel;
+ channels[i].scan_type.sign = 's';
+ channels[i].scan_type.realbits = 24;
+ channels[i].scan_type.storagebits = 32;
+ channels[i].scan_type.shift = 8;
+ channels[i].scan_type.endianness = IIO_BE;
+ i++;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = num_channels;
+ st->channel_config = channel_config;
+
+ return 0;
+
+err_child_out:
+ fwnode_handle_put(node);
+ return ret;
+}
+
+static void ads131e08_regulator_disable(void *data)
+{
+ struct ads131e08_state *st = data;
+
+ regulator_disable(st->vref_reg);
+}
+
+static int ads131e08_probe(struct spi_device *spi)
+{
+ const struct ads131e08_info *info;
+ struct ads131e08_state *st;
+ struct iio_dev *indio_dev;
+ unsigned long adc_clk_hz;
+ unsigned long adc_clk_ns;
+ int ret;
+
+ info = device_get_match_data(&spi->dev);
+ if (!info) {
+ dev_err(&spi->dev, "failed to get match data\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev) {
+ dev_err(&spi->dev, "failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ st->info = info;
+ st->spi = spi;
+
+ ret = ads131e08_alloc_channels(indio_dev);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->info = &ads131e08_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ init_completion(&st->completion);
+
+ if (spi->irq) {
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ ads131e08_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ spi->dev.driver->name, indio_dev);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret,
+ "request irq failed\n");
+ } else {
+ dev_err(&spi->dev, "data ready IRQ missing\n");
+ return -ENODEV;
+ }
+
+ st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name, iio_device_id(indio_dev));
+ if (!st->trig) {
+ dev_err(&spi->dev, "failed to allocate IIO trigger\n");
+ return -ENOMEM;
+ }
+
+ st->trig->ops = &ads131e08_trigger_ops;
+ st->trig->dev.parent = &spi->dev;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(&spi->dev, st->trig);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register IIO trigger\n");
+ return -ENOMEM;
+ }
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ NULL, &ads131e08_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&spi->dev, "failed to setup IIO buffer\n");
+ return ret;
+ }
+
+ st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->vref_reg)) {
+ ret = regulator_enable(st->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev,
+ "failed to enable external vref supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st);
+ if (ret)
+ return ret;
+ } else {
+ if (PTR_ERR(st->vref_reg) != -ENODEV)
+ return PTR_ERR(st->vref_reg);
+
+ st->vref_reg = NULL;
+ }
+
+ st->adc_clk = devm_clk_get_enabled(&spi->dev, "adc-clk");
+ if (IS_ERR(st->adc_clk))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk),
+ "failed to get the ADC clock\n");
+
+ adc_clk_hz = clk_get_rate(st->adc_clk);
+ if (!adc_clk_hz) {
+ dev_err(&spi->dev, "failed to get the ADC clock rate\n");
+ return -EINVAL;
+ }
+
+ adc_clk_ns = NSEC_PER_SEC / adc_clk_hz;
+ st->sdecode_delay_us = DIV_ROUND_UP(
+ ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, NSEC_PER_USEC);
+ st->reset_delay_us = DIV_ROUND_UP(
+ ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, NSEC_PER_USEC);
+
+ ret = ads131e08_initial_config(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "initial configuration failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ads131e08_of_match[] = {
+ { .compatible = "ti,ads131e04",
+ .data = &ads131e08_info_tbl[ads131e04], },
+ { .compatible = "ti,ads131e06",
+ .data = &ads131e08_info_tbl[ads131e06], },
+ { .compatible = "ti,ads131e08",
+ .data = &ads131e08_info_tbl[ads131e08], },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads131e08_of_match);
+
+static struct spi_driver ads131e08_driver = {
+ .driver = {
+ .name = "ads131e08",
+ .of_match_table = ads131e08_of_match,
+ },
+ .probe = ads131e08_probe,
+};
+module_spi_driver(ads131e08_driver);
+
+MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
+MODULE_DESCRIPTION("Driver for ADS131E0x ADC family");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
new file mode 100644
index 000000000..263fc3a1b
--- /dev/null
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments ADS7950 SPI ADC driver
+ *
+ * Copyright 2016 David Lechner <david@lechnology.com>
+ *
+ * Based on iio/ad7923.c:
+ * Copyright 2011 Analog Devices Inc
+ * Copyright 2012 CS Systemes d'Information
+ *
+ * And also on hwmon/ads79xx.c
+ * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/
+ * Nishanth Menon
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/*
+ * In case of ACPI, we use the 5000 mV as default for the reference pin.
+ * Device tree users encode that via the vref-supply regulator.
+ */
+#define TI_ADS7950_VA_MV_ACPI_DEFAULT 5000
+
+#define TI_ADS7950_CR_GPIO BIT(14)
+#define TI_ADS7950_CR_MANUAL BIT(12)
+#define TI_ADS7950_CR_WRITE BIT(11)
+#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7)
+#define TI_ADS7950_CR_RANGE_5V BIT(6)
+#define TI_ADS7950_CR_GPIO_DATA BIT(4)
+
+#define TI_ADS7950_MAX_CHAN 16
+#define TI_ADS7950_NUM_GPIOS 4
+
+#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16))
+
+/* val = value, dec = left shift, bits = number of bits of the mask */
+#define TI_ADS7950_EXTRACT(val, dec, bits) \
+ (((val) >> (dec)) & ((1 << (bits)) - 1))
+
+#define TI_ADS7950_MAN_CMD(cmd) (TI_ADS7950_CR_MANUAL | (cmd))
+#define TI_ADS7950_GPIO_CMD(cmd) (TI_ADS7950_CR_GPIO | (cmd))
+
+/* Manual mode configuration */
+#define TI_ADS7950_MAN_CMD_SETTINGS(st) \
+ (TI_ADS7950_MAN_CMD(TI_ADS7950_CR_WRITE | st->cmd_settings_bitmask))
+/* GPIO mode configuration */
+#define TI_ADS7950_GPIO_CMD_SETTINGS(st) \
+ (TI_ADS7950_GPIO_CMD(st->gpio_cmd_settings_bitmask))
+
+struct ti_ads7950_state {
+ struct spi_device *spi;
+ struct spi_transfer ring_xfer;
+ struct spi_transfer scan_single_xfer[3];
+ struct spi_message ring_msg;
+ struct spi_message scan_single_msg;
+
+ /* Lock to protect the spi xfer buffers */
+ struct mutex slock;
+ struct gpio_chip chip;
+
+ struct regulator *reg;
+ unsigned int vref_mv;
+
+ /*
+ * Bitmask of lower 7 bits used for configuration
+ * These bits only can be written when TI_ADS7950_CR_WRITE
+ * is set, otherwise it retains its original state.
+ * [0-3] GPIO signal
+ * [4] Set following frame to return GPIO signal values
+ * [5] Powers down device
+ * [6] Sets Vref range1(2.5v) or range2(5v)
+ *
+ * Bits present on Manual/Auto1/Auto2 commands
+ */
+ unsigned int cmd_settings_bitmask;
+
+ /*
+ * Bitmask of GPIO command
+ * [0-3] GPIO direction
+ * [4-6] Different GPIO alarm mode configurations
+ * [7] GPIO 2 as device range input
+ * [8] GPIO 3 as device power down input
+ * [9] Reset all registers
+ * [10-11] N/A
+ */
+ unsigned int gpio_cmd_settings_bitmask;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE]
+ __aligned(IIO_DMA_MINALIGN);
+ u16 tx_buf[TI_ADS7950_MAX_CHAN + 2];
+ u16 single_tx;
+ u16 single_rx;
+
+};
+
+struct ti_ads7950_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+enum ti_ads7950_id {
+ TI_ADS7950,
+ TI_ADS7951,
+ TI_ADS7952,
+ TI_ADS7953,
+ TI_ADS7954,
+ TI_ADS7955,
+ TI_ADS7956,
+ TI_ADS7957,
+ TI_ADS7958,
+ TI_ADS7959,
+ TI_ADS7960,
+ TI_ADS7961,
+};
+
+#define TI_ADS7950_V_CHAN(index, bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .datasheet_name = "CH##index", \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = bits, \
+ .storagebits = 16, \
+ .shift = 12 - (bits), \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define DECLARE_TI_ADS7950_4_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ TI_ADS7950_V_CHAN(0, bits), \
+ TI_ADS7950_V_CHAN(1, bits), \
+ TI_ADS7950_V_CHAN(2, bits), \
+ TI_ADS7950_V_CHAN(3, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DECLARE_TI_ADS7950_8_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ TI_ADS7950_V_CHAN(0, bits), \
+ TI_ADS7950_V_CHAN(1, bits), \
+ TI_ADS7950_V_CHAN(2, bits), \
+ TI_ADS7950_V_CHAN(3, bits), \
+ TI_ADS7950_V_CHAN(4, bits), \
+ TI_ADS7950_V_CHAN(5, bits), \
+ TI_ADS7950_V_CHAN(6, bits), \
+ TI_ADS7950_V_CHAN(7, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(8), \
+}
+
+#define DECLARE_TI_ADS7950_12_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ TI_ADS7950_V_CHAN(0, bits), \
+ TI_ADS7950_V_CHAN(1, bits), \
+ TI_ADS7950_V_CHAN(2, bits), \
+ TI_ADS7950_V_CHAN(3, bits), \
+ TI_ADS7950_V_CHAN(4, bits), \
+ TI_ADS7950_V_CHAN(5, bits), \
+ TI_ADS7950_V_CHAN(6, bits), \
+ TI_ADS7950_V_CHAN(7, bits), \
+ TI_ADS7950_V_CHAN(8, bits), \
+ TI_ADS7950_V_CHAN(9, bits), \
+ TI_ADS7950_V_CHAN(10, bits), \
+ TI_ADS7950_V_CHAN(11, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(12), \
+}
+
+#define DECLARE_TI_ADS7950_16_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ TI_ADS7950_V_CHAN(0, bits), \
+ TI_ADS7950_V_CHAN(1, bits), \
+ TI_ADS7950_V_CHAN(2, bits), \
+ TI_ADS7950_V_CHAN(3, bits), \
+ TI_ADS7950_V_CHAN(4, bits), \
+ TI_ADS7950_V_CHAN(5, bits), \
+ TI_ADS7950_V_CHAN(6, bits), \
+ TI_ADS7950_V_CHAN(7, bits), \
+ TI_ADS7950_V_CHAN(8, bits), \
+ TI_ADS7950_V_CHAN(9, bits), \
+ TI_ADS7950_V_CHAN(10, bits), \
+ TI_ADS7950_V_CHAN(11, bits), \
+ TI_ADS7950_V_CHAN(12, bits), \
+ TI_ADS7950_V_CHAN(13, bits), \
+ TI_ADS7950_V_CHAN(14, bits), \
+ TI_ADS7950_V_CHAN(15, bits), \
+ IIO_CHAN_SOFT_TIMESTAMP(16), \
+}
+
+static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7950, 12);
+static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7951, 12);
+static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7952, 12);
+static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7953, 12);
+static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7954, 10);
+static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7955, 10);
+static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7956, 10);
+static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7957, 10);
+static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7958, 8);
+static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8);
+static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8);
+static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8);
+
+static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = {
+ [TI_ADS7950] = {
+ .channels = ti_ads7950_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7950_channels),
+ },
+ [TI_ADS7951] = {
+ .channels = ti_ads7951_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7951_channels),
+ },
+ [TI_ADS7952] = {
+ .channels = ti_ads7952_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7952_channels),
+ },
+ [TI_ADS7953] = {
+ .channels = ti_ads7953_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7953_channels),
+ },
+ [TI_ADS7954] = {
+ .channels = ti_ads7954_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7954_channels),
+ },
+ [TI_ADS7955] = {
+ .channels = ti_ads7955_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7955_channels),
+ },
+ [TI_ADS7956] = {
+ .channels = ti_ads7956_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7956_channels),
+ },
+ [TI_ADS7957] = {
+ .channels = ti_ads7957_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7957_channels),
+ },
+ [TI_ADS7958] = {
+ .channels = ti_ads7958_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7958_channels),
+ },
+ [TI_ADS7959] = {
+ .channels = ti_ads7959_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7959_channels),
+ },
+ [TI_ADS7960] = {
+ .channels = ti_ads7960_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7960_channels),
+ },
+ [TI_ADS7961] = {
+ .channels = ti_ads7961_channels,
+ .num_channels = ARRAY_SIZE(ti_ads7961_channels),
+ },
+};
+
+/*
+ * ti_ads7950_update_scan_mode() setup the spi transfer buffer for the new
+ * scan mask
+ */
+static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct ti_ads7950_state *st = iio_priv(indio_dev);
+ int i, cmd, len;
+
+ len = 0;
+ for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
+ cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i));
+ st->tx_buf[len++] = cmd;
+ }
+
+ /* Data for the 1st channel is not returned until the 3rd transfer */
+ st->tx_buf[len++] = 0;
+ st->tx_buf[len++] = 0;
+
+ st->ring_xfer.len = len * 2;
+
+ return 0;
+}
+
+static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ti_ads7950_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->slock);
+ ret = spi_sync(st->spi, &st->ring_msg);
+ if (ret < 0)
+ goto out;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2],
+ iio_get_time_ns(indio_dev));
+
+out:
+ mutex_unlock(&st->slock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
+{
+ struct ti_ads7950_state *st = iio_priv(indio_dev);
+ int ret, cmd;
+
+ mutex_lock(&st->slock);
+ cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(ch));
+ st->single_tx = cmd;
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ ret = st->single_rx;
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_get_range(struct ti_ads7950_state *st)
+{
+ int vref;
+
+ if (st->vref_mv) {
+ vref = st->vref_mv;
+ } else {
+ vref = regulator_get_voltage(st->reg);
+ if (vref < 0)
+ return vref;
+
+ vref /= 1000;
+ }
+
+ if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V)
+ vref *= 2;
+
+ return vref;
+}
+
+static int ti_ads7950_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ti_ads7950_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ti_ads7950_scan_direct(indio_dev, chan->address);
+ if (ret < 0)
+ return ret;
+
+ if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4))
+ return -EIO;
+
+ *val = TI_ADS7950_EXTRACT(ret, chan->scan_type.shift,
+ chan->scan_type.realbits);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = ti_ads7950_get_range(st);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ *val2 = (1 << chan->scan_type.realbits) - 1;
+
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ti_ads7950_info = {
+ .read_raw = &ti_ads7950_read_raw,
+ .update_scan_mode = ti_ads7950_update_scan_mode,
+};
+
+static void ti_ads7950_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+
+ mutex_lock(&st->slock);
+
+ if (value)
+ st->cmd_settings_bitmask |= BIT(offset);
+ else
+ st->cmd_settings_bitmask &= ~BIT(offset);
+
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ spi_sync(st->spi, &st->scan_single_msg);
+
+ mutex_unlock(&st->slock);
+}
+
+static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ int ret;
+
+ mutex_lock(&st->slock);
+
+ /* If set as output, return the output */
+ if (st->gpio_cmd_settings_bitmask & BIT(offset)) {
+ ret = st->cmd_settings_bitmask & BIT(offset);
+ goto out;
+ }
+
+ /* GPIO data bit sets SDO bits 12-15 to GPIO input */
+ st->cmd_settings_bitmask |= TI_ADS7950_CR_GPIO_DATA;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0;
+
+ /* Revert back to original settings */
+ st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+
+ /* Bitmask is inverted from GPIO framework 0=input/1=output */
+ return !(st->gpio_cmd_settings_bitmask & BIT(offset));
+}
+
+static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset,
+ int input)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ int ret = 0;
+
+ mutex_lock(&st->slock);
+
+ /* Only change direction if needed */
+ if (input && (st->gpio_cmd_settings_bitmask & BIT(offset)))
+ st->gpio_cmd_settings_bitmask &= ~BIT(offset);
+ else if (!input && !(st->gpio_cmd_settings_bitmask & BIT(offset)))
+ st->gpio_cmd_settings_bitmask |= BIT(offset);
+ else
+ goto out;
+
+ st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return _ti_ads7950_set_direction(chip, offset, 1);
+}
+
+static int ti_ads7950_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ ti_ads7950_set(chip, offset, value);
+
+ return _ti_ads7950_set_direction(chip, offset, 0);
+}
+
+static int ti_ads7950_init_hw(struct ti_ads7950_state *st)
+{
+ int ret = 0;
+
+ mutex_lock(&st->slock);
+
+ /* Settings for Manual/Auto1/Auto2 commands */
+ /* Default to 5v ref */
+ st->cmd_settings_bitmask = TI_ADS7950_CR_RANGE_5V;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ /* Settings for GPIO command */
+ st->gpio_cmd_settings_bitmask = 0x0;
+ st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_probe(struct spi_device *spi)
+{
+ struct ti_ads7950_state *st;
+ struct iio_dev *indio_dev;
+ const struct ti_ads7950_chip_info *info;
+ int ret;
+
+ spi->bits_per_word = 16;
+ spi->mode |= SPI_CS_WORD;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Error in spi setup\n");
+ return ret;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = info->channels;
+ indio_dev->num_channels = info->num_channels;
+ indio_dev->info = &ti_ads7950_info;
+
+ /* build spi ring message */
+ spi_message_init(&st->ring_msg);
+
+ st->ring_xfer.tx_buf = &st->tx_buf[0];
+ st->ring_xfer.rx_buf = &st->rx_buf[0];
+ /* len will be set later */
+
+ spi_message_add_tail(&st->ring_xfer, &st->ring_msg);
+
+ /*
+ * Setup default message. The sample is read at the end of the first
+ * transfer, then it takes one full cycle to convert the sample and one
+ * more cycle to send the value. The conversion process is driven by
+ * the SPI clock, which is why we have 3 transfers. The middle one is
+ * just dummy data sent while the chip is converting the sample that
+ * was read at the end of the first transfer.
+ */
+
+ st->scan_single_xfer[0].tx_buf = &st->single_tx;
+ st->scan_single_xfer[0].len = 2;
+ st->scan_single_xfer[0].cs_change = 1;
+ st->scan_single_xfer[1].tx_buf = &st->single_tx;
+ st->scan_single_xfer[1].len = 2;
+ st->scan_single_xfer[1].cs_change = 1;
+ st->scan_single_xfer[2].rx_buf = &st->single_rx;
+ st->scan_single_xfer[2].len = 2;
+
+ spi_message_init_with_transfers(&st->scan_single_msg,
+ st->scan_single_xfer, 3);
+
+ /* Use hard coded value for reference voltage in ACPI case */
+ if (ACPI_COMPANION(&spi->dev))
+ st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT;
+
+ mutex_init(&st->slock);
+
+ st->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->reg)) {
+ ret = dev_err_probe(&spi->dev, PTR_ERR(st->reg),
+ "Failed to get regulator \"vref\"\n");
+ goto error_destroy_mutex;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
+ goto error_destroy_mutex;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ti_ads7950_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to setup triggered buffer\n");
+ goto error_disable_reg;
+ }
+
+ ret = ti_ads7950_init_hw(st);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to init adc chip\n");
+ goto error_cleanup_ring;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device\n");
+ goto error_cleanup_ring;
+ }
+
+ /* Add GPIO chip */
+ st->chip.label = dev_name(&st->spi->dev);
+ st->chip.parent = &st->spi->dev;
+ st->chip.owner = THIS_MODULE;
+ st->chip.can_sleep = true;
+ st->chip.base = -1;
+ st->chip.ngpio = TI_ADS7950_NUM_GPIOS;
+ st->chip.get_direction = ti_ads7950_get_direction;
+ 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;
+
+ ret = gpiochip_add_data(&st->chip, st);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to init GPIOs\n");
+ goto error_iio_device;
+ }
+
+ return 0;
+
+error_iio_device:
+ iio_device_unregister(indio_dev);
+error_cleanup_ring:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+error_destroy_mutex:
+ mutex_destroy(&st->slock);
+
+ return ret;
+}
+
+static void ti_ads7950_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ti_ads7950_state *st = iio_priv(indio_dev);
+
+ gpiochip_remove(&st->chip);
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ regulator_disable(st->reg);
+ mutex_destroy(&st->slock);
+}
+
+static const struct spi_device_id ti_ads7950_id[] = {
+ { "ads7950", TI_ADS7950 },
+ { "ads7951", TI_ADS7951 },
+ { "ads7952", TI_ADS7952 },
+ { "ads7953", TI_ADS7953 },
+ { "ads7954", TI_ADS7954 },
+ { "ads7955", TI_ADS7955 },
+ { "ads7956", TI_ADS7956 },
+ { "ads7957", TI_ADS7957 },
+ { "ads7958", TI_ADS7958 },
+ { "ads7959", TI_ADS7959 },
+ { "ads7960", TI_ADS7960 },
+ { "ads7961", TI_ADS7961 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
+
+static const struct of_device_id ads7950_of_table[] = {
+ { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info[TI_ADS7950] },
+ { .compatible = "ti,ads7951", .data = &ti_ads7950_chip_info[TI_ADS7951] },
+ { .compatible = "ti,ads7952", .data = &ti_ads7950_chip_info[TI_ADS7952] },
+ { .compatible = "ti,ads7953", .data = &ti_ads7950_chip_info[TI_ADS7953] },
+ { .compatible = "ti,ads7954", .data = &ti_ads7950_chip_info[TI_ADS7954] },
+ { .compatible = "ti,ads7955", .data = &ti_ads7950_chip_info[TI_ADS7955] },
+ { .compatible = "ti,ads7956", .data = &ti_ads7950_chip_info[TI_ADS7956] },
+ { .compatible = "ti,ads7957", .data = &ti_ads7950_chip_info[TI_ADS7957] },
+ { .compatible = "ti,ads7958", .data = &ti_ads7950_chip_info[TI_ADS7958] },
+ { .compatible = "ti,ads7959", .data = &ti_ads7950_chip_info[TI_ADS7959] },
+ { .compatible = "ti,ads7960", .data = &ti_ads7950_chip_info[TI_ADS7960] },
+ { .compatible = "ti,ads7961", .data = &ti_ads7950_chip_info[TI_ADS7961] },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads7950_of_table);
+
+static struct spi_driver ti_ads7950_driver = {
+ .driver = {
+ .name = "ads7950",
+ .of_match_table = ads7950_of_table,
+ },
+ .probe = ti_ads7950_probe,
+ .remove = ti_ads7950_remove,
+ .id_table = ti_ads7950_id,
+};
+module_spi_driver(ti_ads7950_driver);
+
+MODULE_AUTHOR("David Lechner <david@lechnology.com>");
+MODULE_DESCRIPTION("TI TI_ADS7950 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c
new file mode 100644
index 000000000..bbd85cb47
--- /dev/null
+++ b/drivers/iio/adc/ti-ads8344.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADS8344 16-bit 8-Channel ADC driver
+ *
+ * Author: Gregory CLEMENT <gregory.clement@bootlin.com>
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/ads8344.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define ADS8344_START BIT(7)
+#define ADS8344_SINGLE_END BIT(2)
+#define ADS8344_CHANNEL(channel) ((channel) << 4)
+#define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */
+
+struct ads8344 {
+ struct spi_device *spi;
+ struct regulator *reg;
+ /*
+ * Lock protecting access to adc->tx_buff and rx_buff,
+ * especially from concurrent read on sysfs file.
+ */
+ struct mutex lock;
+
+ u8 tx_buf __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[3];
+};
+
+#define ADS8344_VOLTAGE_CHANNEL(chan, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = addr, \
+ }
+
+#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, addr) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = addr, \
+ }
+
+static const struct iio_chan_spec ads8344_channels[] = {
+ ADS8344_VOLTAGE_CHANNEL(0, 0),
+ ADS8344_VOLTAGE_CHANNEL(1, 4),
+ ADS8344_VOLTAGE_CHANNEL(2, 1),
+ ADS8344_VOLTAGE_CHANNEL(3, 5),
+ ADS8344_VOLTAGE_CHANNEL(4, 2),
+ ADS8344_VOLTAGE_CHANNEL(5, 6),
+ ADS8344_VOLTAGE_CHANNEL(6, 3),
+ ADS8344_VOLTAGE_CHANNEL(7, 7),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+};
+
+static int ads8344_adc_conversion(struct ads8344 *adc, int channel,
+ bool differential)
+{
+ struct spi_device *spi = adc->spi;
+ int ret;
+
+ adc->tx_buf = ADS8344_START;
+ if (!differential)
+ adc->tx_buf |= ADS8344_SINGLE_END;
+ adc->tx_buf |= ADS8344_CHANNEL(channel);
+ adc->tx_buf |= ADS8344_CLOCK_INTERNAL;
+
+ ret = spi_write(spi, &adc->tx_buf, 1);
+ if (ret)
+ return ret;
+
+ udelay(9);
+
+ ret = spi_read(spi, adc->rx_buf, sizeof(adc->rx_buf));
+ if (ret)
+ return ret;
+
+ return adc->rx_buf[0] << 9 | adc->rx_buf[1] << 1 | adc->rx_buf[2] >> 7;
+}
+
+static int ads8344_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct ads8344 *adc = iio_priv(iio);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *value = ads8344_adc_conversion(adc, channel->address,
+ channel->differential);
+ mutex_unlock(&adc->lock);
+ if (*value < 0)
+ return *value;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *value = regulator_get_voltage(adc->reg);
+ if (*value < 0)
+ return *value;
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+ *shift = 16;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ads8344_info = {
+ .read_raw = ads8344_read_raw,
+};
+
+static void ads8344_reg_disable(void *data)
+{
+ regulator_disable(data);
+}
+
+static int ads8344_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ads8344 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ indio_dev->name = dev_name(&spi->dev);
+ indio_dev->info = &ads8344_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads8344_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ads8344_channels);
+
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return PTR_ERR(adc->reg);
+
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ads8344_reg_disable, adc->reg);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ads8344_of_match[] = {
+ { .compatible = "ti,ads8344", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads8344_of_match);
+
+static struct spi_driver ads8344_driver = {
+ .driver = {
+ .name = "ads8344",
+ .of_match_table = ads8344_of_match,
+ },
+ .probe = ads8344_probe,
+};
+module_spi_driver(ads8344_driver);
+
+MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@bootlin.com>");
+MODULE_DESCRIPTION("ADS8344 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
new file mode 100644
index 000000000..ef06a8974
--- /dev/null
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -0,0 +1,521 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015 Prevas A/S
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+
+#define ADS8688_CMD_REG(x) (x << 8)
+#define ADS8688_CMD_REG_NOOP 0x00
+#define ADS8688_CMD_REG_RST 0x85
+#define ADS8688_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan))
+#define ADS8688_CMD_DONT_CARE_BITS 16
+
+#define ADS8688_PROG_REG(x) (x << 9)
+#define ADS8688_PROG_REG_RANGE_CH(chan) (0x05 + chan)
+#define ADS8688_PROG_WR_BIT BIT(8)
+#define ADS8688_PROG_DONT_CARE_BITS 8
+
+#define ADS8688_REG_PLUSMINUS25VREF 0
+#define ADS8688_REG_PLUSMINUS125VREF 1
+#define ADS8688_REG_PLUSMINUS0625VREF 2
+#define ADS8688_REG_PLUS25VREF 5
+#define ADS8688_REG_PLUS125VREF 6
+
+#define ADS8688_VREF_MV 4096
+#define ADS8688_REALBITS 16
+#define ADS8688_MAX_CHANNELS 8
+
+/*
+ * enum ads8688_range - ADS8688 reference voltage range
+ * @ADS8688_PLUSMINUS25VREF: Device is configured for input range ±2.5 * VREF
+ * @ADS8688_PLUSMINUS125VREF: Device is configured for input range ±1.25 * VREF
+ * @ADS8688_PLUSMINUS0625VREF: Device is configured for input range ±0.625 * VREF
+ * @ADS8688_PLUS25VREF: Device is configured for input range 0 - 2.5 * VREF
+ * @ADS8688_PLUS125VREF: Device is configured for input range 0 - 1.25 * VREF
+ */
+enum ads8688_range {
+ ADS8688_PLUSMINUS25VREF,
+ ADS8688_PLUSMINUS125VREF,
+ ADS8688_PLUSMINUS0625VREF,
+ ADS8688_PLUS25VREF,
+ ADS8688_PLUS125VREF,
+};
+
+struct ads8688_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct ads8688_state {
+ struct mutex lock;
+ const struct ads8688_chip_info *chip_info;
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned int vref_mv;
+ enum ads8688_range range[8];
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data[2] __aligned(IIO_DMA_MINALIGN);
+};
+
+enum ads8688_id {
+ ID_ADS8684,
+ ID_ADS8688,
+};
+
+struct ads8688_ranges {
+ enum ads8688_range range;
+ unsigned int scale;
+ int offset;
+ u8 reg;
+};
+
+static const struct ads8688_ranges ads8688_range_def[5] = {
+ {
+ .range = ADS8688_PLUSMINUS25VREF,
+ .scale = 76295,
+ .offset = -(1 << (ADS8688_REALBITS - 1)),
+ .reg = ADS8688_REG_PLUSMINUS25VREF,
+ }, {
+ .range = ADS8688_PLUSMINUS125VREF,
+ .scale = 38148,
+ .offset = -(1 << (ADS8688_REALBITS - 1)),
+ .reg = ADS8688_REG_PLUSMINUS125VREF,
+ }, {
+ .range = ADS8688_PLUSMINUS0625VREF,
+ .scale = 19074,
+ .offset = -(1 << (ADS8688_REALBITS - 1)),
+ .reg = ADS8688_REG_PLUSMINUS0625VREF,
+ }, {
+ .range = ADS8688_PLUS25VREF,
+ .scale = 38148,
+ .offset = 0,
+ .reg = ADS8688_REG_PLUS25VREF,
+ }, {
+ .range = ADS8688_PLUS125VREF,
+ .scale = 19074,
+ .offset = 0,
+ .reg = ADS8688_REG_PLUS125VREF,
+ }
+};
+
+static ssize_t ads8688_show_scales(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ads8688_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "0.%09u 0.%09u 0.%09u\n",
+ ads8688_range_def[0].scale * st->vref_mv,
+ ads8688_range_def[1].scale * st->vref_mv,
+ ads8688_range_def[2].scale * st->vref_mv);
+}
+
+static ssize_t ads8688_show_offsets(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d %d\n", ads8688_range_def[0].offset,
+ ads8688_range_def[3].offset);
+}
+
+static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
+ ads8688_show_scales, NULL, 0);
+static IIO_DEVICE_ATTR(in_voltage_offset_available, S_IRUGO,
+ ads8688_show_offsets, NULL, 0);
+
+static struct attribute *ads8688_attributes[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage_offset_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ads8688_attribute_group = {
+ .attrs = ads8688_attributes,
+};
+
+#define ADS8688_CHAN(index) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec ads8684_channels[] = {
+ ADS8688_CHAN(0),
+ ADS8688_CHAN(1),
+ ADS8688_CHAN(2),
+ ADS8688_CHAN(3),
+};
+
+static const struct iio_chan_spec ads8688_channels[] = {
+ ADS8688_CHAN(0),
+ ADS8688_CHAN(1),
+ ADS8688_CHAN(2),
+ ADS8688_CHAN(3),
+ ADS8688_CHAN(4),
+ ADS8688_CHAN(5),
+ ADS8688_CHAN(6),
+ ADS8688_CHAN(7),
+};
+
+static int ads8688_prog_write(struct iio_dev *indio_dev, unsigned int addr,
+ unsigned int val)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ u32 tmp;
+
+ tmp = ADS8688_PROG_REG(addr) | ADS8688_PROG_WR_BIT | val;
+ tmp <<= ADS8688_PROG_DONT_CARE_BITS;
+ st->data[0].d32 = cpu_to_be32(tmp);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ads8688_reset(struct iio_dev *indio_dev)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ u32 tmp;
+
+ tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_RST);
+ tmp <<= ADS8688_CMD_DONT_CARE_BITS;
+ st->data[0].d32 = cpu_to_be32(tmp);
+
+ return spi_write(st->spi, &st->data[0].d8[0], 4);
+}
+
+static int ads8688_read(struct iio_dev *indio_dev, unsigned int chan)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ int ret;
+ u32 tmp;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[0],
+ .len = 4,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[0],
+ .rx_buf = &st->data[1].d8[0],
+ .len = 4,
+ },
+ };
+
+ tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_MAN_CH(chan));
+ tmp <<= ADS8688_CMD_DONT_CARE_BITS;
+ st->data[0].d32 = cpu_to_be32(tmp);
+
+ tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_NOOP);
+ tmp <<= ADS8688_CMD_DONT_CARE_BITS;
+ st->data[1].d32 = cpu_to_be32(tmp);
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be32_to_cpu(st->data[1].d32) & 0xffff;
+}
+
+static int ads8688_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ int ret, offset;
+ unsigned long scale_mv;
+
+ struct ads8688_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads8688_read(indio_dev, chan->channel);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ scale_mv = st->vref_mv;
+ scale_mv *= ads8688_range_def[st->range[chan->channel]].scale;
+ *val = 0;
+ *val2 = scale_mv;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ offset = ads8688_range_def[st->range[chan->channel]].offset;
+ *val = offset;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ }
+ mutex_unlock(&st->lock);
+
+ return -EINVAL;
+}
+
+static int ads8688_write_reg_range(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ enum ads8688_range range)
+{
+ unsigned int tmp;
+
+ tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel);
+
+ return ads8688_prog_write(indio_dev, tmp, range);
+}
+
+static int ads8688_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ads8688_state *st = iio_priv(indio_dev);
+ unsigned int scale = 0;
+ int ret = -EINVAL, i, offset = 0;
+
+ mutex_lock(&st->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ /* If the offset is 0 the ±2.5 * VREF mode is not available */
+ offset = ads8688_range_def[st->range[chan->channel]].offset;
+ if (offset == 0 && val2 == ads8688_range_def[0].scale * st->vref_mv) {
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+ /* Lookup new mode */
+ for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++)
+ if (val2 == ads8688_range_def[i].scale * st->vref_mv &&
+ offset == ads8688_range_def[i].offset) {
+ ret = ads8688_write_reg_range(indio_dev, chan,
+ ads8688_range_def[i].reg);
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * There are only two available offsets:
+ * 0 and -(1 << (ADS8688_REALBITS - 1))
+ */
+ if (!(ads8688_range_def[0].offset == val ||
+ ads8688_range_def[3].offset == val)) {
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+ /*
+ * If the device are in ±2.5 * VREF mode, it's not allowed to
+ * switch to a mode where the offset is 0
+ */
+ if (val == 0 &&
+ st->range[chan->channel] == ADS8688_PLUSMINUS25VREF) {
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+ scale = ads8688_range_def[st->range[chan->channel]].scale;
+
+ /* Lookup new mode */
+ for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++)
+ if (val == ads8688_range_def[i].offset &&
+ scale == ads8688_range_def[i].scale) {
+ ret = ads8688_write_reg_range(indio_dev, chan,
+ ads8688_range_def[i].reg);
+ break;
+ }
+ break;
+ }
+
+ if (!ret)
+ st->range[chan->channel] = ads8688_range_def[i].range;
+
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ads8688_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_OFFSET:
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ads8688_info = {
+ .read_raw = &ads8688_read_raw,
+ .write_raw = &ads8688_write_raw,
+ .write_raw_get_fmt = &ads8688_write_raw_get_fmt,
+ .attrs = &ads8688_attribute_group,
+};
+
+static irqreturn_t ads8688_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ /* Ensure naturally aligned timestamp */
+ u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)] __aligned(8);
+ int i, j = 0;
+
+ for (i = 0; i < indio_dev->masklength; i++) {
+ if (!test_bit(i, indio_dev->active_scan_mask))
+ continue;
+ buffer[j] = ads8688_read(indio_dev, i);
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct ads8688_chip_info ads8688_chip_info_tbl[] = {
+ [ID_ADS8684] = {
+ .channels = ads8684_channels,
+ .num_channels = ARRAY_SIZE(ads8684_channels),
+ },
+ [ID_ADS8688] = {
+ .channels = ads8688_channels,
+ .num_channels = ARRAY_SIZE(ads8688_channels),
+ },
+};
+
+static int ads8688_probe(struct spi_device *spi)
+{
+ struct ads8688_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(st->reg);
+ if (ret < 0)
+ goto err_regulator_disable;
+
+ st->vref_mv = ret / 1000;
+ } else {
+ /* Use internal reference */
+ st->vref_mv = ADS8688_VREF_MV;
+ }
+
+ st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ spi->mode = SPI_MODE_1;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = &ads8688_info;
+
+ ads8688_reset(indio_dev);
+
+ mutex_init(&st->lock);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL, ads8688_trigger_handler, NULL);
+ if (ret < 0) {
+ dev_err(&spi->dev, "iio triggered buffer setup failed\n");
+ goto err_regulator_disable;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+err_regulator_disable:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static void ads8688_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ads8688_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+}
+
+static const struct spi_device_id ads8688_id[] = {
+ {"ads8684", ID_ADS8684},
+ {"ads8688", ID_ADS8688},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ads8688_id);
+
+static const struct of_device_id ads8688_of_match[] = {
+ { .compatible = "ti,ads8684" },
+ { .compatible = "ti,ads8688" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads8688_of_match);
+
+static struct spi_driver ads8688_driver = {
+ .driver = {
+ .name = "ads8688",
+ .of_match_table = ads8688_of_match,
+ },
+ .probe = ads8688_probe,
+ .remove = ads8688_remove,
+ .id_table = ads8688_id,
+};
+module_spi_driver(ads8688_driver);
+
+MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
+MODULE_DESCRIPTION("Texas Instruments ADS8688 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
new file mode 100644
index 000000000..30f629a55
--- /dev/null
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI tlc4541 ADC Driver
+ *
+ * Copyright (C) 2017 Phil Reid
+ *
+ * Datasheets can be found here:
+ * https://www.ti.com/lit/gpn/tlc3541
+ * https://www.ti.com/lit/gpn/tlc4541
+ *
+ * The tlc4541 requires 24 clock cycles to start a transfer.
+ * Conversion then takes 2.94us to complete before data is ready
+ * Data is returned MSB first.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+
+struct tlc4541_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ struct spi_transfer scan_single_xfer[3];
+ struct spi_message scan_single_msg;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ * 2 bytes data + 6 bytes padding + 8 bytes timestamp when
+ * call iio_push_to_buffers_with_timestamp.
+ */
+ __be16 rx_buf[8] __aligned(IIO_DMA_MINALIGN);
+};
+
+struct tlc4541_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+enum tlc4541_id {
+ TLC3541,
+ TLC4541,
+};
+
+#define TLC4541_V_CHAN(bits, bitshift) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = (bitshift), \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \
+const struct iio_chan_spec name ## _channels[] = { \
+ TLC4541_V_CHAN(bits, bitshift), \
+ IIO_CHAN_SOFT_TIMESTAMP(1), \
+}
+
+static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2);
+static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0);
+
+static const struct tlc4541_chip_info tlc4541_chip_info[] = {
+ [TLC3541] = {
+ .channels = tlc3541_channels,
+ .num_channels = ARRAY_SIZE(tlc3541_channels),
+ },
+ [TLC4541] = {
+ .channels = tlc4541_channels,
+ .num_channels = ARRAY_SIZE(tlc4541_channels),
+ },
+};
+
+static irqreturn_t tlc4541_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct tlc4541_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret < 0)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int tlc4541_get_range(struct tlc4541_state *st)
+{
+ int vref;
+
+ vref = regulator_get_voltage(st->reg);
+ if (vref < 0)
+ return vref;
+
+ vref /= 1000;
+
+ return vref;
+}
+
+static int tlc4541_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret = 0;
+ struct tlc4541_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+ *val = be16_to_cpu(st->rx_buf[0]);
+ *val = *val >> chan->scan_type.shift;
+ *val &= GENMASK(chan->scan_type.realbits - 1, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = tlc4541_get_range(st);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info tlc4541_info = {
+ .read_raw = &tlc4541_read_raw,
+};
+
+static int tlc4541_probe(struct spi_device *spi)
+{
+ struct tlc4541_state *st;
+ struct iio_dev *indio_dev;
+ const struct tlc4541_chip_info *info;
+ int ret;
+ int8_t device_init = 0;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st->spi = spi;
+
+ info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data];
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = info->channels;
+ indio_dev->num_channels = info->num_channels;
+ indio_dev->info = &tlc4541_info;
+
+ /* perform reset */
+ spi_write(spi, &device_init, 1);
+
+ /* Setup default message */
+ st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
+ st->scan_single_xfer[0].len = 3;
+ st->scan_single_xfer[1].delay.value = 3;
+ st->scan_single_xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
+ st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
+ st->scan_single_xfer[2].len = 2;
+
+ spi_message_init_with_transfers(&st->scan_single_msg,
+ st->scan_single_xfer, 3);
+
+ st->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &tlc4541_trigger_handler, NULL);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer;
+
+ return 0;
+
+error_cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static void tlc4541_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct tlc4541_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ regulator_disable(st->reg);
+}
+
+static const struct of_device_id tlc4541_dt_ids[] = {
+ { .compatible = "ti,tlc3541", },
+ { .compatible = "ti,tlc4541", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
+
+static const struct spi_device_id tlc4541_id[] = {
+ {"tlc3541", TLC3541},
+ {"tlc4541", TLC4541},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, tlc4541_id);
+
+static struct spi_driver tlc4541_driver = {
+ .driver = {
+ .name = "tlc4541",
+ .of_match_table = tlc4541_dt_ids,
+ },
+ .probe = tlc4541_probe,
+ .remove = tlc4541_remove,
+ .id_table = tlc4541_id,
+};
+module_spi_driver(tlc4541_driver);
+
+MODULE_AUTHOR("Phil Reid <preid@electromag.com.au>");
+MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
new file mode 100644
index 000000000..1bbb51a66
--- /dev/null
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -0,0 +1,912 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Texas Instruments TSC2046 SPI ADC driver
+ *
+ * Copyright (c) 2021 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <asm/unaligned.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger.h>
+
+/*
+ * The PENIRQ of TSC2046 controller is implemented as level shifter attached to
+ * the X+ line. If voltage of the X+ line reaches a specific level the IRQ will
+ * be activated or deactivated.
+ * To make this kind of IRQ reusable as trigger following additions were
+ * implemented:
+ * - rate limiting:
+ * For typical touchscreen use case, we need to trigger about each 10ms.
+ * - hrtimer:
+ * Continue triggering at least once after the IRQ was deactivated. Then
+ * deactivate this trigger to stop sampling in order to reduce power
+ * consumption.
+ */
+
+#define TI_TSC2046_NAME "tsc2046"
+
+/* This driver doesn't aim at the peak continuous sample rate */
+#define TI_TSC2046_MAX_SAMPLE_RATE 125000
+#define TI_TSC2046_SAMPLE_BITS \
+ BITS_PER_TYPE(struct tsc2046_adc_atom)
+#define TI_TSC2046_MAX_CLK_FREQ \
+ (TI_TSC2046_MAX_SAMPLE_RATE * TI_TSC2046_SAMPLE_BITS)
+
+#define TI_TSC2046_SAMPLE_INTERVAL_US 10000
+
+#define TI_TSC2046_START BIT(7)
+#define TI_TSC2046_ADDR GENMASK(6, 4)
+#define TI_TSC2046_ADDR_TEMP1 7
+#define TI_TSC2046_ADDR_AUX 6
+#define TI_TSC2046_ADDR_X 5
+#define TI_TSC2046_ADDR_Z2 4
+#define TI_TSC2046_ADDR_Z1 3
+#define TI_TSC2046_ADDR_VBAT 2
+#define TI_TSC2046_ADDR_Y 1
+#define TI_TSC2046_ADDR_TEMP0 0
+
+/*
+ * The mode bit sets the resolution of the ADC. With this bit low, the next
+ * conversion has 12-bit resolution, whereas with this bit high, the next
+ * conversion has 8-bit resolution. This driver is optimized for 12-bit mode.
+ * So, for this driver, this bit should stay zero.
+ */
+#define TI_TSC2046_8BIT_MODE BIT(3)
+
+/*
+ * SER/DFR - The SER/DFR bit controls the reference mode, either single-ended
+ * (high) or differential (low).
+ */
+#define TI_TSC2046_SER BIT(2)
+
+/*
+ * If VREF_ON and ADC_ON are both zero, then the chip operates in
+ * auto-wake/suspend mode. In most case this bits should stay zero.
+ */
+#define TI_TSC2046_PD1_VREF_ON BIT(1)
+#define TI_TSC2046_PD0_ADC_ON BIT(0)
+
+/*
+ * All supported devices can do 8 or 12bit resolution. This driver
+ * supports only 12bit mode, here we have a 16bit data transfer, where
+ * the MSB and the 3 LSB are 0.
+ */
+#define TI_TSC2046_DATA_12BIT GENMASK(14, 3)
+
+#define TI_TSC2046_MAX_CHAN 8
+#define TI_TSC2046_MIN_POLL_CNT 3
+#define TI_TSC2046_EXT_POLL_CNT 3
+#define TI_TSC2046_POLL_CNT \
+ (TI_TSC2046_MIN_POLL_CNT + TI_TSC2046_EXT_POLL_CNT)
+#define TI_TSC2046_INT_VREF 2500
+
+/* Represents a HW sample */
+struct tsc2046_adc_atom {
+ /*
+ * Command transmitted to the controller. This field is empty on the RX
+ * buffer.
+ */
+ u8 cmd;
+ /*
+ * Data received from the controller. This field is empty for the TX
+ * buffer
+ */
+ __be16 data;
+} __packed;
+
+/* Layout of atomic buffers within big buffer */
+struct tsc2046_adc_group_layout {
+ /* Group offset within the SPI RX buffer */
+ unsigned int offset;
+ /*
+ * Amount of tsc2046_adc_atom structs within the same command gathered
+ * within same group.
+ */
+ unsigned int count;
+ /*
+ * Settling samples (tsc2046_adc_atom structs) which should be skipped
+ * before good samples will start.
+ */
+ unsigned int skip;
+};
+
+struct tsc2046_adc_dcfg {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct tsc2046_adc_ch_cfg {
+ unsigned int settling_time_us;
+ unsigned int oversampling_ratio;
+};
+
+enum tsc2046_state {
+ TSC2046_STATE_SHUTDOWN,
+ TSC2046_STATE_STANDBY,
+ TSC2046_STATE_POLL,
+ TSC2046_STATE_POLL_IRQ_DISABLE,
+ TSC2046_STATE_ENABLE_IRQ,
+};
+
+struct tsc2046_adc_priv {
+ struct spi_device *spi;
+ const struct tsc2046_adc_dcfg *dcfg;
+ struct regulator *vref_reg;
+
+ struct iio_trigger *trig;
+ struct hrtimer trig_timer;
+ enum tsc2046_state state;
+ int poll_cnt;
+ spinlock_t state_lock;
+
+ struct spi_transfer xfer;
+ struct spi_message msg;
+
+ struct {
+ /* Scan data for each channel */
+ u16 data[TI_TSC2046_MAX_CHAN];
+ /* Timestamp */
+ s64 ts __aligned(8);
+ } scan_buf;
+
+ /*
+ * Lock to protect the layout and the SPI transfer buffer.
+ * tsc2046_adc_group_layout can be changed within update_scan_mode(),
+ * in this case the l[] and tx/rx buffer will be out of sync to each
+ * other.
+ */
+ struct mutex slock;
+ struct tsc2046_adc_group_layout l[TI_TSC2046_MAX_CHAN];
+ struct tsc2046_adc_atom *rx;
+ struct tsc2046_adc_atom *tx;
+
+ unsigned int count;
+ unsigned int groups;
+ u32 effective_speed_hz;
+ u32 scan_interval_us;
+ u32 time_per_scan_us;
+ u32 time_per_bit_ns;
+ unsigned int vref_mv;
+
+ struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN];
+};
+
+#define TI_TSC2046_V_CHAN(index, bits, name) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "#name", \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = bits, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define DECLARE_TI_TSC2046_8_CHANNELS(name, bits) \
+const struct iio_chan_spec name ## _channels[] = { \
+ TI_TSC2046_V_CHAN(0, bits, TEMP0), \
+ TI_TSC2046_V_CHAN(1, bits, Y), \
+ TI_TSC2046_V_CHAN(2, bits, VBAT), \
+ TI_TSC2046_V_CHAN(3, bits, Z1), \
+ TI_TSC2046_V_CHAN(4, bits, Z2), \
+ TI_TSC2046_V_CHAN(5, bits, X), \
+ TI_TSC2046_V_CHAN(6, bits, AUX), \
+ TI_TSC2046_V_CHAN(7, bits, TEMP1), \
+ IIO_CHAN_SOFT_TIMESTAMP(8), \
+}
+
+static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12);
+
+static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = {
+ .channels = tsc2046_adc_channels,
+ .num_channels = ARRAY_SIZE(tsc2046_adc_channels),
+};
+
+/*
+ * Convert time to a number of samples which can be transferred within this
+ * time.
+ */
+static unsigned int tsc2046_adc_time_to_count(struct tsc2046_adc_priv *priv,
+ unsigned long time)
+{
+ unsigned int bit_count, sample_count;
+
+ bit_count = DIV_ROUND_UP(time * NSEC_PER_USEC, priv->time_per_bit_ns);
+ sample_count = DIV_ROUND_UP(bit_count, TI_TSC2046_SAMPLE_BITS);
+
+ dev_dbg(&priv->spi->dev, "Effective speed %u, time per bit: %u, count bits: %u, count samples: %u\n",
+ priv->effective_speed_hz, priv->time_per_bit_ns,
+ bit_count, sample_count);
+
+ return sample_count;
+}
+
+static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx,
+ bool keep_power)
+{
+ u32 pd;
+
+ /*
+ * if PD bits are 0, controller will automatically disable ADC, VREF and
+ * enable IRQ.
+ */
+ if (keep_power)
+ pd = TI_TSC2046_PD0_ADC_ON;
+ else
+ pd = 0;
+
+ switch (ch_idx) {
+ case TI_TSC2046_ADDR_TEMP1:
+ case TI_TSC2046_ADDR_AUX:
+ case TI_TSC2046_ADDR_VBAT:
+ case TI_TSC2046_ADDR_TEMP0:
+ pd |= TI_TSC2046_SER;
+ if (!priv->vref_reg)
+ pd |= TI_TSC2046_PD1_VREF_ON;
+ }
+
+ return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd;
+}
+
+static u16 tsc2046_adc_get_value(struct tsc2046_adc_atom *buf)
+{
+ return FIELD_GET(TI_TSC2046_DATA_12BIT, get_unaligned_be16(&buf->data));
+}
+
+static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
+ u32 *effective_speed_hz)
+{
+ struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx];
+ struct tsc2046_adc_atom *rx_buf, *tx_buf;
+ unsigned int val, val_normalized = 0;
+ int ret, i, count_skip = 0, max_count;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ u8 cmd;
+
+ if (!effective_speed_hz) {
+ count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us);
+ max_count = count_skip + ch->oversampling_ratio;
+ } else {
+ max_count = 1;
+ }
+
+ if (sizeof(*tx_buf) * max_count > PAGE_SIZE)
+ return -ENOSPC;
+
+ tx_buf = kcalloc(max_count, sizeof(*tx_buf), GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
+
+ rx_buf = kcalloc(max_count, sizeof(*rx_buf), GFP_KERNEL);
+ if (!rx_buf) {
+ ret = -ENOMEM;
+ goto free_tx;
+ }
+
+ /*
+ * Do not enable automatic power down on working samples. Otherwise the
+ * plates will never be completely charged.
+ */
+ cmd = tsc2046_adc_get_cmd(priv, ch_idx, true);
+
+ for (i = 0; i < max_count - 1; i++)
+ tx_buf[i].cmd = cmd;
+
+ /* automatically power down on last sample */
+ tx_buf[i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
+
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.tx_buf = tx_buf;
+ xfer.rx_buf = rx_buf;
+ xfer.len = sizeof(*tx_buf) * max_count;
+ spi_message_init_with_transfers(&msg, &xfer, 1);
+
+ /*
+ * We aren't using spi_write_then_read() because we need to be able
+ * to get hold of the effective_speed_hz from the xfer
+ */
+ ret = spi_sync(priv->spi, &msg);
+ if (ret) {
+ dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n",
+ ERR_PTR(ret));
+ goto free_bufs;
+ }
+
+ if (effective_speed_hz)
+ *effective_speed_hz = xfer.effective_speed_hz;
+
+ for (i = 0; i < max_count - count_skip; i++) {
+ val = tsc2046_adc_get_value(&rx_buf[count_skip + i]);
+ val_normalized += val;
+ }
+
+ ret = DIV_ROUND_UP(val_normalized, max_count - count_skip);
+
+free_bufs:
+ kfree(rx_buf);
+free_tx:
+ kfree(tx_buf);
+
+ return ret;
+}
+
+static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv,
+ unsigned int group,
+ unsigned int ch_idx)
+{
+ struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx];
+ struct tsc2046_adc_group_layout *cur;
+ unsigned int max_count, count_skip;
+ unsigned int offset = 0;
+
+ if (group)
+ offset = priv->l[group - 1].offset + priv->l[group - 1].count;
+
+ count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us);
+ max_count = count_skip + ch->oversampling_ratio;
+
+ cur = &priv->l[group];
+ cur->offset = offset;
+ cur->count = max_count;
+ cur->skip = count_skip;
+
+ return sizeof(*priv->tx) * max_count;
+}
+
+static void tsc2046_adc_group_set_cmd(struct tsc2046_adc_priv *priv,
+ unsigned int group, int ch_idx)
+{
+ struct tsc2046_adc_group_layout *l = &priv->l[group];
+ unsigned int i;
+ u8 cmd;
+
+ /*
+ * Do not enable automatic power down on working samples. Otherwise the
+ * plates will never be completely charged.
+ */
+ cmd = tsc2046_adc_get_cmd(priv, ch_idx, true);
+
+ for (i = 0; i < l->count - 1; i++)
+ priv->tx[l->offset + i].cmd = cmd;
+
+ /* automatically power down on last sample */
+ priv->tx[l->offset + i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
+}
+
+static u16 tsc2046_adc_get_val(struct tsc2046_adc_priv *priv, int group)
+{
+ struct tsc2046_adc_group_layout *l;
+ unsigned int val, val_normalized = 0;
+ int valid_count, i;
+
+ l = &priv->l[group];
+ valid_count = l->count - l->skip;
+
+ for (i = 0; i < valid_count; i++) {
+ val = tsc2046_adc_get_value(&priv->rx[l->offset + l->skip + i]);
+ val_normalized += val;
+ }
+
+ return DIV_ROUND_UP(val_normalized, valid_count);
+}
+
+static int tsc2046_adc_scan(struct iio_dev *indio_dev)
+{
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ struct device *dev = &priv->spi->dev;
+ int group;
+ int ret;
+
+ ret = spi_sync(priv->spi, &priv->msg);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI transfer failed: %pe\n", ERR_PTR(ret));
+ return ret;
+ }
+
+ 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));
+ /* 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",
+ ERR_PTR(ret));
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t tsc2046_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+
+ mutex_lock(&priv->slock);
+ tsc2046_adc_scan(indio_dev);
+ mutex_unlock(&priv->slock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int tsc2046_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = tsc2046_adc_read_one(priv, chan->channel, NULL);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * Note: the TSC2046 has internal voltage divider on the VBAT
+ * line. This divider can be influenced by external divider.
+ * So, it is better to use external voltage-divider driver
+ * instead, which is calculating complete chain.
+ */
+ *val = priv->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *active_scan_mask)
+{
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ unsigned int ch_idx, group = 0;
+ size_t size;
+
+ mutex_lock(&priv->slock);
+
+ size = 0;
+ for_each_set_bit(ch_idx, active_scan_mask, ARRAY_SIZE(priv->l)) {
+ size += tsc2046_adc_group_set_layout(priv, group, ch_idx);
+ tsc2046_adc_group_set_cmd(priv, group, ch_idx);
+ group++;
+ }
+
+ priv->groups = group;
+ priv->xfer.len = size;
+ priv->time_per_scan_us = size * 8 * priv->time_per_bit_ns / NSEC_PER_USEC;
+
+ if (priv->scan_interval_us < priv->time_per_scan_us)
+ dev_warn(&priv->spi->dev, "The scan interval (%d) is less then calculated scan time (%d)\n",
+ priv->scan_interval_us, priv->time_per_scan_us);
+
+ mutex_unlock(&priv->slock);
+
+ return 0;
+}
+
+static const struct iio_info tsc2046_adc_info = {
+ .read_raw = tsc2046_adc_read_raw,
+ .update_scan_mode = tsc2046_adc_update_scan_mode,
+};
+
+static enum hrtimer_restart tsc2046_adc_timer(struct hrtimer *hrtimer)
+{
+ struct tsc2046_adc_priv *priv = container_of(hrtimer,
+ struct tsc2046_adc_priv,
+ trig_timer);
+ unsigned long flags;
+
+ /*
+ * This state machine should address following challenges :
+ * - the interrupt source is based on level shifter attached to the X
+ * channel of ADC. It will change the state every time we switch
+ * between channels. So, we need to disable IRQ if we do
+ * iio_trigger_poll().
+ * - we should do iio_trigger_poll() at some reduced sample rate
+ * - we should still trigger for some amount of time after last
+ * interrupt with enabled IRQ was processed.
+ */
+
+ spin_lock_irqsave(&priv->state_lock, flags);
+ switch (priv->state) {
+ case TSC2046_STATE_ENABLE_IRQ:
+ if (priv->poll_cnt < TI_TSC2046_POLL_CNT) {
+ priv->poll_cnt++;
+ hrtimer_start(&priv->trig_timer,
+ ns_to_ktime(priv->scan_interval_us *
+ NSEC_PER_USEC),
+ HRTIMER_MODE_REL_SOFT);
+
+ if (priv->poll_cnt >= TI_TSC2046_MIN_POLL_CNT) {
+ priv->state = TSC2046_STATE_POLL_IRQ_DISABLE;
+ enable_irq(priv->spi->irq);
+ } else {
+ priv->state = TSC2046_STATE_POLL;
+ }
+ } else {
+ priv->state = TSC2046_STATE_STANDBY;
+ enable_irq(priv->spi->irq);
+ }
+ break;
+ case TSC2046_STATE_POLL_IRQ_DISABLE:
+ disable_irq_nosync(priv->spi->irq);
+ fallthrough;
+ case TSC2046_STATE_POLL:
+ priv->state = TSC2046_STATE_ENABLE_IRQ;
+ /* iio_trigger_poll() starts hrtimer */
+ iio_trigger_poll(priv->trig);
+ break;
+ case TSC2046_STATE_SHUTDOWN:
+ break;
+ case TSC2046_STATE_STANDBY:
+ fallthrough;
+ default:
+ dev_warn(&priv->spi->dev, "Got unexpected state: %i\n",
+ priv->state);
+ break;
+ }
+ spin_unlock_irqrestore(&priv->state_lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t tsc2046_adc_irq(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ unsigned long flags;
+
+ hrtimer_try_to_cancel(&priv->trig_timer);
+
+ spin_lock_irqsave(&priv->state_lock, flags);
+ if (priv->state != TSC2046_STATE_SHUTDOWN) {
+ priv->state = TSC2046_STATE_ENABLE_IRQ;
+ priv->poll_cnt = 0;
+
+ /* iio_trigger_poll() starts hrtimer */
+ disable_irq_nosync(priv->spi->irq);
+ iio_trigger_poll(priv->trig);
+ }
+ spin_unlock_irqrestore(&priv->state_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void tsc2046_adc_reenable_trigger(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ ktime_t tim;
+
+ /*
+ * We can sample it as fast as we can, but usually we do not need so
+ * many samples. Reduce the sample rate for default (touchscreen) use
+ * case.
+ */
+ tim = ns_to_ktime((priv->scan_interval_us - priv->time_per_scan_us) *
+ NSEC_PER_USEC);
+ hrtimer_start(&priv->trig_timer, tim, HRTIMER_MODE_REL_SOFT);
+}
+
+static int tsc2046_adc_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct tsc2046_adc_priv *priv = iio_priv(indio_dev);
+ unsigned long flags;
+
+ if (enable) {
+ spin_lock_irqsave(&priv->state_lock, flags);
+ if (priv->state == TSC2046_STATE_SHUTDOWN) {
+ priv->state = TSC2046_STATE_STANDBY;
+ enable_irq(priv->spi->irq);
+ }
+ spin_unlock_irqrestore(&priv->state_lock, flags);
+ } else {
+ spin_lock_irqsave(&priv->state_lock, flags);
+
+ if (priv->state == TSC2046_STATE_STANDBY ||
+ priv->state == TSC2046_STATE_POLL_IRQ_DISABLE)
+ disable_irq_nosync(priv->spi->irq);
+
+ priv->state = TSC2046_STATE_SHUTDOWN;
+ spin_unlock_irqrestore(&priv->state_lock, flags);
+
+ hrtimer_cancel(&priv->trig_timer);
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops tsc2046_adc_trigger_ops = {
+ .set_trigger_state = tsc2046_adc_set_trigger_state,
+ .reenable = tsc2046_adc_reenable_trigger,
+};
+
+static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv)
+{
+ unsigned int ch_idx;
+ size_t size;
+ int ret;
+
+ /*
+ * Make dummy read to set initial power state and get real SPI clock
+ * freq. It seems to be not important which channel is used for this
+ * case.
+ */
+ ret = tsc2046_adc_read_one(priv, TI_TSC2046_ADDR_TEMP0,
+ &priv->effective_speed_hz);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * In case SPI controller do not report effective_speed_hz, use
+ * configure value and hope it will match.
+ */
+ if (!priv->effective_speed_hz)
+ priv->effective_speed_hz = priv->spi->max_speed_hz;
+
+
+ priv->scan_interval_us = TI_TSC2046_SAMPLE_INTERVAL_US;
+ priv->time_per_bit_ns = DIV_ROUND_UP(NSEC_PER_SEC,
+ priv->effective_speed_hz);
+
+ /*
+ * Calculate and allocate maximal size buffer if all channels are
+ * enabled.
+ */
+ size = 0;
+ for (ch_idx = 0; ch_idx < ARRAY_SIZE(priv->l); ch_idx++)
+ size += tsc2046_adc_group_set_layout(priv, ch_idx, ch_idx);
+
+ if (size > PAGE_SIZE) {
+ dev_err(&priv->spi->dev,
+ "Calculated scan buffer is too big. Try to reduce spi-max-frequency, settling-time-us or oversampling-ratio\n");
+ return -ENOSPC;
+ }
+
+ priv->tx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL);
+ if (!priv->tx)
+ return -ENOMEM;
+
+ priv->rx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL);
+ if (!priv->rx)
+ return -ENOMEM;
+
+ priv->xfer.tx_buf = priv->tx;
+ priv->xfer.rx_buf = priv->rx;
+ priv->xfer.len = size;
+ spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
+
+ return 0;
+}
+
+static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv)
+{
+ struct fwnode_handle *child;
+ struct device *dev = &priv->spi->dev;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->ch_cfg); i++) {
+ priv->ch_cfg[i].settling_time_us = 1;
+ priv->ch_cfg[i].oversampling_ratio = 1;
+ }
+
+ device_for_each_child_node(dev, child) {
+ u32 stl, overs, reg;
+ int ret;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret) {
+ dev_err(dev, "invalid reg on %pfw, err: %pe\n", child,
+ ERR_PTR(ret));
+ continue;
+ }
+
+ if (reg >= ARRAY_SIZE(priv->ch_cfg)) {
+ dev_err(dev, "%pfw: Unsupported reg value: %i, max supported is: %zu.\n",
+ child, reg, ARRAY_SIZE(priv->ch_cfg));
+ continue;
+ }
+
+ ret = fwnode_property_read_u32(child, "settling-time-us", &stl);
+ if (!ret)
+ priv->ch_cfg[reg].settling_time_us = stl;
+
+ ret = fwnode_property_read_u32(child, "oversampling-ratio",
+ &overs);
+ if (!ret)
+ priv->ch_cfg[reg].oversampling_ratio = overs;
+ }
+}
+
+static void tsc2046_adc_regulator_disable(void *data)
+{
+ struct tsc2046_adc_priv *priv = data;
+
+ regulator_disable(priv->vref_reg);
+}
+
+static int tsc2046_adc_configure_regulator(struct tsc2046_adc_priv *priv)
+{
+ struct device *dev = &priv->spi->dev;
+ int ret;
+
+ priv->vref_reg = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(priv->vref_reg)) {
+ /* If regulator exists but can't be get, return an error */
+ if (PTR_ERR(priv->vref_reg) != -ENODEV)
+ return PTR_ERR(priv->vref_reg);
+ priv->vref_reg = NULL;
+ }
+ if (!priv->vref_reg) {
+ /* Use internal reference */
+ priv->vref_mv = TI_TSC2046_INT_VREF;
+ return 0;
+ }
+
+ ret = regulator_enable(priv->vref_reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, tsc2046_adc_regulator_disable,
+ priv);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(priv->vref_reg);
+ if (ret < 0)
+ return ret;
+
+ priv->vref_mv = ret / MILLI;
+
+ return 0;
+}
+
+static int tsc2046_adc_probe(struct spi_device *spi)
+{
+ const struct tsc2046_adc_dcfg *dcfg;
+ struct device *dev = &spi->dev;
+ struct tsc2046_adc_priv *priv;
+ struct iio_dev *indio_dev;
+ struct iio_trigger *trig;
+ int ret;
+
+ if (spi->max_speed_hz > TI_TSC2046_MAX_CLK_FREQ) {
+ dev_err(dev, "SPI max_speed_hz is too high: %d Hz. Max supported freq is %zu Hz\n",
+ spi->max_speed_hz, TI_TSC2046_MAX_CLK_FREQ);
+ return -EINVAL;
+ }
+
+ dcfg = device_get_match_data(dev);
+ if (!dcfg) {
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ dcfg = (const struct tsc2046_adc_dcfg *)id->driver_data;
+ }
+ if (!dcfg)
+ return -EINVAL;
+
+ spi->bits_per_word = 8;
+ spi->mode &= ~SPI_MODE_X_MASK;
+ spi->mode |= SPI_MODE_0;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error in SPI setup\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->dcfg = dcfg;
+
+ priv->spi = spi;
+
+ indio_dev->name = TI_TSC2046_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dcfg->channels;
+ indio_dev->num_channels = dcfg->num_channels;
+ indio_dev->info = &tsc2046_adc_info;
+
+ ret = tsc2046_adc_configure_regulator(priv);
+ if (ret)
+ return ret;
+
+ tsc2046_adc_parse_fwnode(priv);
+
+ ret = tsc2046_adc_setup_spi_msg(priv);
+ if (ret)
+ return ret;
+
+ mutex_init(&priv->slock);
+
+ ret = devm_request_irq(dev, spi->irq, &tsc2046_adc_irq,
+ IRQF_NO_AUTOEN, indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ trig = devm_iio_trigger_alloc(dev, "touchscreen-%s", indio_dev->name);
+ if (!trig)
+ return -ENOMEM;
+
+ priv->trig = trig;
+ iio_trigger_set_drvdata(trig, indio_dev);
+ trig->ops = &tsc2046_adc_trigger_ops;
+
+ 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;
+
+ ret = devm_iio_trigger_register(dev, trig);
+ if (ret) {
+ dev_err(dev, "failed to register trigger\n");
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ &tsc2046_adc_trigger_handler, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to setup triggered buffer\n");
+ return ret;
+ }
+
+ /* set default trigger */
+ indio_dev->trig = iio_trigger_get(priv->trig);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ads7950_of_table[] = {
+ { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads7950_of_table);
+
+static const struct spi_device_id tsc2046_adc_spi_ids[] = {
+ { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids);
+
+static struct spi_driver tsc2046_adc_driver = {
+ .driver = {
+ .name = "tsc2046",
+ .of_match_table = ads7950_of_table,
+ },
+ .id_table = tsc2046_adc_spi_ids,
+ .probe = tsc2046_adc_probe,
+};
+module_spi_driver(tsc2046_adc_driver);
+
+MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
+MODULE_DESCRIPTION("TI TSC2046 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
new file mode 100644
index 000000000..3ac253a27
--- /dev/null
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -0,0 +1,759 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TI ADC MFD driver
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/iopoll.h>
+
+#include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE SZ_2K
+
+struct tiadc_dma {
+ struct dma_slave_config conf;
+ struct dma_chan *chan;
+ dma_addr_t addr;
+ dma_cookie_t cookie;
+ u8 *buf;
+ int current_period;
+ int period_size;
+ u8 fifo_thresh;
+};
+
+struct tiadc_device {
+ struct ti_tscadc_dev *mfd_tscadc;
+ struct tiadc_dma dma;
+ struct mutex fifo1_lock; /* to protect fifo access */
+ int channels;
+ int total_ch_enabled;
+ u8 channel_line[8];
+ u8 channel_step[8];
+ int buffer_en_ch_steps;
+ u16 data[8];
+ u32 open_delay[8], sample_delay[8], step_avg[8];
+};
+
+static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
+{
+ return readl(adc->mfd_tscadc->tscadc_base + reg);
+}
+
+static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, adc->mfd_tscadc->tscadc_base + reg);
+}
+
+static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
+{
+ u32 step_en;
+
+ step_en = ((1 << adc_dev->channels) - 1);
+ step_en <<= TOTAL_STEPS - adc_dev->channels + 1;
+ return step_en;
+}
+
+static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev,
+ struct iio_chan_spec const *chan)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
+ if (chan->channel == adc_dev->channel_line[i]) {
+ u32 step;
+
+ step = adc_dev->channel_step[i];
+ /* +1 for the charger */
+ return 1 << (step + 1);
+ }
+ }
+ WARN_ON(1);
+ return 0;
+}
+
+static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
+{
+ return 1 << adc_dev->channel_step[chan];
+}
+
+static int tiadc_wait_idle(struct tiadc_device *adc_dev)
+{
+ u32 val;
+
+ return readl_poll_timeout(adc_dev->mfd_tscadc->tscadc_base + REG_ADCFSM,
+ val, !(val & SEQ_STATUS), 10,
+ IDLE_TIMEOUT_MS * 1000 * adc_dev->channels);
+}
+
+static void tiadc_step_config(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int stepconfig;
+ int i, steps = 0;
+
+ /*
+ * There are 16 configurable steps and 8 analog input
+ * lines available which are shared between Touchscreen and ADC.
+ *
+ * Steps forwards i.e. from 0 towards 16 are used by ADC
+ * depending on number of input lines needed.
+ * Channel would represent which analog input
+ * needs to be given to ADC to digitalize data.
+ */
+ for (i = 0; i < adc_dev->channels; i++) {
+ int chan;
+
+ chan = adc_dev->channel_line[i];
+
+ if (adc_dev->step_avg[i])
+ stepconfig = STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) |
+ STEPCONFIG_FIFO1;
+ else
+ stepconfig = STEPCONFIG_FIFO1;
+
+ if (iio_buffer_enabled(indio_dev))
+ stepconfig |= STEPCONFIG_MODE_SWCNT;
+
+ tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
+ stepconfig | STEPCONFIG_INP(chan) |
+ STEPCONFIG_INM_ADCREFM | STEPCONFIG_RFP_VREFP |
+ STEPCONFIG_RFM_VREFN);
+
+ tiadc_writel(adc_dev, REG_STEPDELAY(steps),
+ STEPDELAY_OPEN(adc_dev->open_delay[i]) |
+ STEPDELAY_SAMPLE(adc_dev->sample_delay[i]));
+
+ adc_dev->channel_step[i] = steps;
+ steps++;
+ }
+}
+
+static irqreturn_t tiadc_irq_h(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int status, config, adc_fsm;
+ unsigned short count = 0;
+
+ status = tiadc_readl(adc_dev, REG_IRQSTATUS);
+
+ /*
+ * ADC and touchscreen share the IRQ line.
+ * FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only
+ */
+ if (status & IRQENB_FIFO1OVRRUN) {
+ /* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
+ config = tiadc_readl(adc_dev, REG_CTRL);
+ config &= ~(CNTRLREG_SSENB);
+ tiadc_writel(adc_dev, REG_CTRL, config);
+ tiadc_writel(adc_dev, REG_IRQSTATUS,
+ IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW |
+ IRQENB_FIFO1THRES);
+
+ /*
+ * Wait for the idle state.
+ * ADC needs to finish the current conversion
+ * before disabling the module
+ */
+ do {
+ adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM);
+ } while (adc_fsm != 0x10 && count++ < 100);
+
+ tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_SSENB));
+ return IRQ_HANDLED;
+ } else if (status & IRQENB_FIFO1THRES) {
+ /* Disable irq and wake worker thread */
+ tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t tiadc_worker_h(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int i, k, fifo1count, read;
+ u16 *data = adc_dev->data;
+
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (k = 0; k < fifo1count; k = k + i) {
+ for (i = 0; i < indio_dev->scan_bytes / 2; i++) {
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ data[i] = read & FIFOREAD_DATA_MASK;
+ }
+ iio_push_to_buffers(indio_dev, (u8 *)data);
+ }
+
+ tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
+ tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
+
+ return IRQ_HANDLED;
+}
+
+static void tiadc_dma_rx_complete(void *param)
+{
+ struct iio_dev *indio_dev = param;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ u8 *data;
+ int i;
+
+ data = dma->buf + dma->current_period * dma->period_size;
+ dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
+
+ for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
+ iio_push_to_buffers(indio_dev, data);
+ data += indio_dev->scan_bytes;
+ }
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ struct dma_async_tx_descriptor *desc;
+
+ dma->current_period = 0; /* We start to fill period 0 */
+
+ /*
+ * Make the fifo thresh as the multiple of total number of
+ * channels enabled, so make sure that cyclic DMA period
+ * length is also a multiple of total number of channels
+ * enabled. This ensures that no invalid data is reported
+ * to the stack via iio_push_to_buffers().
+ */
+ dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
+ adc_dev->total_ch_enabled) - 1;
+
+ /* Make sure that period length is multiple of fifo thresh level */
+ dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
+ (dma->fifo_thresh + 1) * sizeof(u16));
+
+ dma->conf.src_maxburst = dma->fifo_thresh + 1;
+ dmaengine_slave_config(dma->chan, &dma->conf);
+
+ desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+ dma->period_size * 2,
+ dma->period_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = tiadc_dma_rx_complete;
+ desc->callback_param = indio_dev;
+
+ dma->cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dma->chan);
+
+ tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+ return 0;
+}
+
+static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int i, fifo1count;
+ int ret;
+
+ ret = tiadc_wait_idle(adc_dev);
+ if (ret)
+ return ret;
+
+ tiadc_writel(adc_dev, REG_IRQCLR,
+ IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW);
+
+ /* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ tiadc_readl(adc_dev, REG_FIFO1);
+
+ return 0;
+}
+
+static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ unsigned int irq_enable;
+ unsigned int enb = 0;
+ u8 bit;
+
+ tiadc_step_config(indio_dev);
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
+ enb |= (get_adc_step_bit(adc_dev, bit) << 1);
+ adc_dev->total_ch_enabled++;
+ }
+ adc_dev->buffer_en_ch_steps = enb;
+
+ if (dma->chan)
+ tiadc_start_dma(indio_dev);
+
+ am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
+
+ tiadc_writel(adc_dev, REG_IRQSTATUS,
+ IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW);
+
+ irq_enable = IRQENB_FIFO1OVRRUN;
+ if (!dma->chan)
+ irq_enable |= IRQENB_FIFO1THRES;
+ tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
+
+ return 0;
+}
+
+static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ int fifo1count, i;
+
+ tiadc_writel(adc_dev, REG_IRQCLR,
+ IRQENB_FIFO1THRES | IRQENB_FIFO1OVRRUN |
+ IRQENB_FIFO1UNDRFLW);
+ am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
+ adc_dev->buffer_en_ch_steps = 0;
+ adc_dev->total_ch_enabled = 0;
+ if (dma->chan) {
+ tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+ dmaengine_terminate_async(dma->chan);
+ }
+
+ /* Flush FIFO of leftover data in the time it takes to disable adc */
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ for (i = 0; i < fifo1count; i++)
+ tiadc_readl(adc_dev, REG_FIFO1);
+
+ return 0;
+}
+
+static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ tiadc_step_config(indio_dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
+ .preenable = &tiadc_buffer_preenable,
+ .postenable = &tiadc_buffer_postenable,
+ .predisable = &tiadc_buffer_predisable,
+ .postdisable = &tiadc_buffer_postdisable,
+};
+
+static int tiadc_iio_buffered_hardware_setup(struct device *dev,
+ struct iio_dev *indio_dev,
+ irqreturn_t (*pollfunc_bh)(int irq, void *p),
+ irqreturn_t (*pollfunc_th)(int irq, void *p),
+ int irq, unsigned long flags,
+ const struct iio_buffer_setup_ops *setup_ops)
+{
+ int ret;
+
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, setup_ops);
+ if (ret)
+ return ret;
+
+ return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
+ flags, indio_dev->name, indio_dev);
+}
+
+static const char * const chan_name_ain[] = {
+ "AIN0",
+ "AIN1",
+ "AIN2",
+ "AIN3",
+ "AIN4",
+ "AIN5",
+ "AIN6",
+ "AIN7",
+};
+
+static int tiadc_channel_init(struct device *dev, struct iio_dev *indio_dev,
+ int channels)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct iio_chan_spec *chan_array;
+ struct iio_chan_spec *chan;
+ int i;
+
+ indio_dev->num_channels = channels;
+ chan_array = devm_kcalloc(dev, channels, sizeof(*chan_array),
+ GFP_KERNEL);
+ if (!chan_array)
+ return -ENOMEM;
+
+ chan = chan_array;
+ for (i = 0; i < channels; i++, chan++) {
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = adc_dev->channel_line[i];
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->datasheet_name = chan_name_ain[chan->channel];
+ chan->scan_index = i;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 12;
+ chan->scan_type.storagebits = 16;
+ }
+
+ indio_dev->channels = chan_array;
+
+ return 0;
+}
+
+static int tiadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ int i, map_val;
+ unsigned int fifo1count, read, stepid;
+ bool found = false;
+ u32 step_en;
+ unsigned long timeout;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = 1800;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ step_en = get_adc_chan_step_mask(adc_dev, chan);
+ if (!step_en)
+ return -EINVAL;
+
+ mutex_lock(&adc_dev->fifo1_lock);
+
+ ret = tiadc_wait_idle(adc_dev);
+ if (ret)
+ goto err_unlock;
+
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ while (fifo1count--)
+ tiadc_readl(adc_dev, REG_FIFO1);
+
+ am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
+
+ /* Wait for Fifo threshold interrupt */
+ timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT_MS * adc_dev->channels);
+ while (1) {
+ fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
+ if (fifo1count)
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
+ ret = -EAGAIN;
+ goto err_unlock;
+ }
+ }
+
+ map_val = adc_dev->channel_step[chan->scan_index];
+
+ /*
+ * We check the complete FIFO. We programmed just one entry but in case
+ * something went wrong we left empty handed (-EAGAIN previously) and
+ * then the value apeared somehow in the FIFO we would have two entries.
+ * Therefore we read every item and keep only the latest version of the
+ * requested channel.
+ */
+ for (i = 0; i < fifo1count; i++) {
+ read = tiadc_readl(adc_dev, REG_FIFO1);
+ stepid = read & FIFOREAD_CHNLID_MASK;
+ stepid = stepid >> 0x10;
+
+ if (stepid == map_val) {
+ read = read & FIFOREAD_DATA_MASK;
+ found = true;
+ *val = (u16)read;
+ }
+ }
+
+ am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
+
+ if (!found)
+ ret = -EBUSY;
+
+err_unlock:
+ mutex_unlock(&adc_dev->fifo1_lock);
+ return ret ? ret : IIO_VAL_INT;
+}
+
+static const struct iio_info tiadc_info = {
+ .read_raw = &tiadc_read_raw,
+};
+
+static int tiadc_request_dma(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct tiadc_dma *dma = &adc_dev->dma;
+ dma_cap_mask_t mask;
+
+ /* Default slave configuration parameters */
+ dma->conf.direction = DMA_DEV_TO_MEM;
+ dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get a channel for RX */
+ dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
+ if (IS_ERR(dma->chan)) {
+ int ret = PTR_ERR(dma->chan);
+
+ dma->chan = NULL;
+ return ret;
+ }
+
+ /* RX buffer */
+ dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ &dma->addr, GFP_KERNEL);
+ if (!dma->buf)
+ goto err;
+
+ return 0;
+
+err:
+ dma_release_channel(dma->chan);
+ return -ENOMEM;
+}
+
+static int tiadc_parse_dt(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
+ int channels = 0;
+ u32 val;
+ int i;
+
+ of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
+ adc_dev->channel_line[channels] = val;
+
+ /* Set Default values for optional DT parameters */
+ adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY;
+ adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY;
+ adc_dev->step_avg[channels] = 16;
+
+ channels++;
+ }
+
+ adc_dev->channels = channels;
+
+ of_property_read_u32_array(node, "ti,chan-step-avg",
+ adc_dev->step_avg, channels);
+ of_property_read_u32_array(node, "ti,chan-step-opendelay",
+ adc_dev->open_delay, channels);
+ of_property_read_u32_array(node, "ti,chan-step-sampledelay",
+ adc_dev->sample_delay, channels);
+
+ for (i = 0; i < adc_dev->channels; i++) {
+ int chan;
+
+ chan = adc_dev->channel_line[i];
+
+ if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) {
+ dev_warn(&pdev->dev,
+ "chan %d: wrong step avg, truncated to %ld\n",
+ chan, STEPCONFIG_AVG_16);
+ adc_dev->step_avg[i] = STEPCONFIG_AVG_16;
+ }
+
+ if (adc_dev->open_delay[i] > STEPCONFIG_MAX_OPENDLY) {
+ dev_warn(&pdev->dev,
+ "chan %d: wrong open delay, truncated to 0x%lX\n",
+ chan, STEPCONFIG_MAX_OPENDLY);
+ adc_dev->open_delay[i] = STEPCONFIG_MAX_OPENDLY;
+ }
+
+ if (adc_dev->sample_delay[i] > STEPCONFIG_MAX_SAMPLE) {
+ dev_warn(&pdev->dev,
+ "chan %d: wrong sample delay, truncated to 0x%lX\n",
+ chan, STEPCONFIG_MAX_SAMPLE);
+ adc_dev->sample_delay[i] = STEPCONFIG_MAX_SAMPLE;
+ }
+ }
+
+ return 0;
+}
+
+static int tiadc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct tiadc_device *adc_dev;
+ struct device_node *node = pdev->dev.of_node;
+ int err;
+
+ if (!node) {
+ dev_err(&pdev->dev, "Could not find valid DT data.\n");
+ return -EINVAL;
+ }
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+ adc_dev = iio_priv(indio_dev);
+
+ adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
+ tiadc_parse_dt(pdev, adc_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &tiadc_info;
+
+ tiadc_step_config(indio_dev);
+ tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
+ mutex_init(&adc_dev->fifo1_lock);
+
+ err = tiadc_channel_init(&pdev->dev, indio_dev, adc_dev->channels);
+ if (err < 0)
+ return err;
+
+ err = tiadc_iio_buffered_hardware_setup(&pdev->dev, indio_dev,
+ &tiadc_worker_h,
+ &tiadc_irq_h,
+ adc_dev->mfd_tscadc->irq,
+ IRQF_SHARED,
+ &tiadc_buffer_setup_ops);
+ if (err)
+ return err;
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ return err;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ err = tiadc_request_dma(pdev, adc_dev);
+ if (err && err != -ENODEV) {
+ dev_err_probe(&pdev->dev, err, "DMA request failed\n");
+ goto err_dma;
+ }
+
+ return 0;
+
+err_dma:
+ iio_device_unregister(indio_dev);
+
+ return err;
+}
+
+static int tiadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ u32 step_en;
+
+ if (dma->chan) {
+ dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ dma->buf, dma->addr);
+ dma_release_channel(dma->chan);
+ }
+ iio_device_unregister(indio_dev);
+
+ step_en = get_adc_step_mask(adc_dev);
+ am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en);
+
+ return 0;
+}
+
+static int tiadc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int idle;
+
+ idle = tiadc_readl(adc_dev, REG_CTRL);
+ idle &= ~(CNTRLREG_SSENB);
+ tiadc_writel(adc_dev, REG_CTRL, idle | CNTRLREG_POWERDOWN);
+
+ return 0;
+}
+
+static int tiadc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int restore;
+
+ /* Make sure ADC is powered up */
+ restore = tiadc_readl(adc_dev, REG_CTRL);
+ restore &= ~CNTRLREG_POWERDOWN;
+ tiadc_writel(adc_dev, REG_CTRL, restore);
+
+ tiadc_step_config(indio_dev);
+ am335x_tsc_se_set_cache(adc_dev->mfd_tscadc,
+ adc_dev->buffer_en_ch_steps);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(tiadc_pm_ops, tiadc_suspend, tiadc_resume);
+
+static const struct of_device_id ti_adc_dt_ids[] = {
+ { .compatible = "ti,am3359-adc", },
+ { .compatible = "ti,am4372-adc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
+
+static struct platform_driver tiadc_driver = {
+ .driver = {
+ .name = "TI-am335x-adc",
+ .pm = pm_sleep_ptr(&tiadc_pm_ops),
+ .of_match_table = ti_adc_dt_ids,
+ },
+ .probe = tiadc_probe,
+ .remove = tiadc_remove,
+};
+module_platform_driver(tiadc_driver);
+
+MODULE_DESCRIPTION("TI ADC controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
new file mode 100644
index 000000000..f8f8aea15
--- /dev/null
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -0,0 +1,931 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * TWL4030 MADC module driver-This driver monitors the real time
+ * conversion of analog signals like battery temperature,
+ * battery type, battery level etc.
+ *
+ * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
+ * J Keerthy <j-keerthy@ti.com>
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * Amit Kucheria <amit.kucheria@canonical.com>
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/twl.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/jiffies.h>
+#include <linux/types.h>
+#include <linux/gfp.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+
+#define TWL4030_MADC_MAX_CHANNELS 16
+
+#define TWL4030_MADC_CTRL1 0x00
+#define TWL4030_MADC_CTRL2 0x01
+
+#define TWL4030_MADC_RTSELECT_LSB 0x02
+#define TWL4030_MADC_SW1SELECT_LSB 0x06
+#define TWL4030_MADC_SW2SELECT_LSB 0x0A
+
+#define TWL4030_MADC_RTAVERAGE_LSB 0x04
+#define TWL4030_MADC_SW1AVERAGE_LSB 0x08
+#define TWL4030_MADC_SW2AVERAGE_LSB 0x0C
+
+#define TWL4030_MADC_CTRL_SW1 0x12
+#define TWL4030_MADC_CTRL_SW2 0x13
+
+#define TWL4030_MADC_RTCH0_LSB 0x17
+#define TWL4030_MADC_GPCH0_LSB 0x37
+
+#define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */
+#define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */
+/* MADC conversion completion */
+#define TWL4030_MADC_EOC_SW (1 << 1)
+/* MADC SWx start conversion */
+#define TWL4030_MADC_SW_START (1 << 5)
+#define TWL4030_MADC_ADCIN0 (1 << 0)
+#define TWL4030_MADC_ADCIN1 (1 << 1)
+#define TWL4030_MADC_ADCIN2 (1 << 2)
+#define TWL4030_MADC_ADCIN3 (1 << 3)
+#define TWL4030_MADC_ADCIN4 (1 << 4)
+#define TWL4030_MADC_ADCIN5 (1 << 5)
+#define TWL4030_MADC_ADCIN6 (1 << 6)
+#define TWL4030_MADC_ADCIN7 (1 << 7)
+#define TWL4030_MADC_ADCIN8 (1 << 8)
+#define TWL4030_MADC_ADCIN9 (1 << 9)
+#define TWL4030_MADC_ADCIN10 (1 << 10)
+#define TWL4030_MADC_ADCIN11 (1 << 11)
+#define TWL4030_MADC_ADCIN12 (1 << 12)
+#define TWL4030_MADC_ADCIN13 (1 << 13)
+#define TWL4030_MADC_ADCIN14 (1 << 14)
+#define TWL4030_MADC_ADCIN15 (1 << 15)
+
+/* Fixed channels */
+#define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1
+#define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8
+#define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9
+#define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10
+#define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11
+#define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12
+
+/* Step size and prescaler ratio */
+#define TEMP_STEP_SIZE 147
+#define TEMP_PSR_R 100
+#define CURR_STEP_SIZE 147
+#define CURR_PSR_R1 44
+#define CURR_PSR_R2 88
+
+#define TWL4030_BCI_BCICTL1 0x23
+#define TWL4030_BCI_CGAIN 0x020
+#define TWL4030_BCI_MESBAT (1 << 1)
+#define TWL4030_BCI_TYPEN (1 << 4)
+#define TWL4030_BCI_ITHEN (1 << 3)
+
+#define REG_BCICTL2 0x024
+#define TWL4030_BCI_ITHSENS 0x007
+
+/* Register and bits for GPBR1 register */
+#define TWL4030_REG_GPBR1 0x0c
+#define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7)
+
+#define TWL4030_USB_SEL_MADC_MCPC (1<<3)
+#define TWL4030_USB_CARKIT_ANA_CTRL 0xBB
+
+struct twl4030_madc_conversion_method {
+ u8 sel;
+ u8 avg;
+ u8 rbase;
+ u8 ctrl;
+};
+
+/**
+ * struct twl4030_madc_request - madc request packet for channel conversion
+ * @channels: 16 bit bitmap for individual channels
+ * @do_avg: sample the input channel for 4 consecutive cycles
+ * @method: RT, SW1, SW2
+ * @type: Polling or interrupt based method
+ * @active: Flag if request is active
+ * @result_pending: Flag from irq handler, that result is ready
+ * @raw: Return raw value, do not convert it
+ * @rbuf: Result buffer
+ */
+struct twl4030_madc_request {
+ unsigned long channels;
+ bool do_avg;
+ u16 method;
+ u16 type;
+ bool active;
+ bool result_pending;
+ bool raw;
+ int rbuf[TWL4030_MADC_MAX_CHANNELS];
+};
+
+enum conversion_methods {
+ TWL4030_MADC_RT,
+ TWL4030_MADC_SW1,
+ TWL4030_MADC_SW2,
+ TWL4030_MADC_NUM_METHODS
+};
+
+enum sample_type {
+ TWL4030_MADC_WAIT,
+ TWL4030_MADC_IRQ_ONESHOT,
+ TWL4030_MADC_IRQ_REARM
+};
+
+/**
+ * struct twl4030_madc_data - a container for madc info
+ * @dev: Pointer to device structure for madc
+ * @lock: Mutex protecting this data structure
+ * @usb3v1: Pointer to bias regulator for madc
+ * @requests: Array of request struct corresponding to SW1, SW2 and RT
+ * @use_second_irq: IRQ selection (main or co-processor)
+ * @imr: Interrupt mask register of MADC
+ * @isr: Interrupt status register of MADC
+ */
+struct twl4030_madc_data {
+ struct device *dev;
+ struct mutex lock;
+ struct regulator *usb3v1;
+ struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
+ bool use_second_irq;
+ u8 imr;
+ u8 isr;
+};
+
+static int twl4030_madc_conversion(struct twl4030_madc_request *req);
+
+static int twl4030_madc_read(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct twl4030_madc_data *madc = iio_priv(iio_dev);
+ struct twl4030_madc_request req;
+ int ret;
+
+ req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
+
+ req.channels = BIT(chan->channel);
+ req.active = false;
+ req.type = TWL4030_MADC_WAIT;
+ req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
+ req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
+
+ ret = twl4030_madc_conversion(&req);
+ if (ret < 0)
+ return ret;
+
+ *val = req.rbuf[chan->channel];
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info twl4030_madc_iio_info = {
+ .read_raw = &twl4030_madc_read,
+};
+
+#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
+ .type = _type, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ .datasheet_name = _name, \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
+ TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
+ TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
+ TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
+ TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
+ TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
+ TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
+ TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
+ TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
+ TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
+ TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
+ TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
+ TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
+ TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
+ TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
+ TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
+ TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
+};
+
+static struct twl4030_madc_data *twl4030_madc;
+
+static const struct s16_fract twl4030_divider_ratios[16] = {
+ {1, 1}, /* CHANNEL 0 No Prescaler */
+ {1, 1}, /* CHANNEL 1 No Prescaler */
+ {6, 10}, /* CHANNEL 2 */
+ {6, 10}, /* CHANNEL 3 */
+ {6, 10}, /* CHANNEL 4 */
+ {6, 10}, /* CHANNEL 5 */
+ {6, 10}, /* CHANNEL 6 */
+ {6, 10}, /* CHANNEL 7 */
+ {3, 14}, /* CHANNEL 8 */
+ {1, 3}, /* CHANNEL 9 */
+ {1, 1}, /* CHANNEL 10 No Prescaler */
+ {15, 100}, /* CHANNEL 11 */
+ {1, 4}, /* CHANNEL 12 */
+ {1, 1}, /* CHANNEL 13 Reserved channels */
+ {1, 1}, /* CHANNEL 14 Reseved channels */
+ {5, 11}, /* CHANNEL 15 */
+};
+
+/* Conversion table from -3 to 55 degrees Celcius */
+static int twl4030_therm_tbl[] = {
+ 30800, 29500, 28300, 27100,
+ 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
+ 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
+ 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
+ 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
+ 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
+ 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
+ 3550
+};
+
+/*
+ * Structure containing the registers
+ * of different conversion methods supported by MADC.
+ * Hardware or RT real time conversion request initiated by external host
+ * processor for RT Signal conversions.
+ * External host processors can also request for non RT conversions
+ * SW1 and SW2 software conversions also called asynchronous or GPC request.
+ */
+static
+const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
+ [TWL4030_MADC_RT] = {
+ .sel = TWL4030_MADC_RTSELECT_LSB,
+ .avg = TWL4030_MADC_RTAVERAGE_LSB,
+ .rbase = TWL4030_MADC_RTCH0_LSB,
+ },
+ [TWL4030_MADC_SW1] = {
+ .sel = TWL4030_MADC_SW1SELECT_LSB,
+ .avg = TWL4030_MADC_SW1AVERAGE_LSB,
+ .rbase = TWL4030_MADC_GPCH0_LSB,
+ .ctrl = TWL4030_MADC_CTRL_SW1,
+ },
+ [TWL4030_MADC_SW2] = {
+ .sel = TWL4030_MADC_SW2SELECT_LSB,
+ .avg = TWL4030_MADC_SW2AVERAGE_LSB,
+ .rbase = TWL4030_MADC_GPCH0_LSB,
+ .ctrl = TWL4030_MADC_CTRL_SW2,
+ },
+};
+
+/**
+ * twl4030_madc_channel_raw_read() - Function to read a particular channel value
+ * @madc: pointer to struct twl4030_madc_data
+ * @reg: lsb of ADC Channel
+ *
+ * Return: 0 on success, an error code otherwise.
+ */
+static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
+{
+ u16 val;
+ int ret;
+ /*
+ * For each ADC channel, we have MSB and LSB register pair. MSB address
+ * is always LSB address+1. reg parameter is the address of LSB register
+ */
+ ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
+ if (ret) {
+ dev_err(madc->dev, "unable to read register 0x%X\n", reg);
+ return ret;
+ }
+
+ return (int)(val >> 6);
+}
+
+/*
+ * Return battery temperature in degrees Celsius
+ * Or < 0 on failure.
+ */
+static int twl4030battery_temperature(int raw_volt)
+{
+ u8 val;
+ int temp, curr, volt, res, ret;
+
+ volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
+ /* Getting and calculating the supply current in micro amperes */
+ ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
+ REG_BCICTL2);
+ if (ret < 0)
+ return ret;
+
+ curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10;
+ /* Getting and calculating the thermistor resistance in ohms */
+ res = volt * 1000 / curr;
+ /* calculating temperature */
+ for (temp = 58; temp >= 0; temp--) {
+ int actual = twl4030_therm_tbl[temp];
+ if ((actual - res) >= 0)
+ break;
+ }
+
+ return temp + 1;
+}
+
+static int twl4030battery_current(int raw_volt)
+{
+ int ret;
+ u8 val;
+
+ ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
+ TWL4030_BCI_BCICTL1);
+ if (ret)
+ return ret;
+ if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
+ return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
+ else /* slope of 0.88 mV/mA */
+ return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
+}
+
+/*
+ * Function to read channel values
+ * @madc - pointer to twl4030_madc_data struct
+ * @reg_base - Base address of the first channel
+ * @Channels - 16 bit bitmap. If the bit is set, channel's value is read
+ * @buf - The channel values are stored here. if read fails error
+ * @raw - Return raw values without conversion
+ * value is stored
+ * Returns the number of successfully read channels.
+ */
+static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
+ u8 reg_base, unsigned
+ long channels, int *buf,
+ bool raw)
+{
+ int count = 0;
+ int i;
+ u8 reg;
+
+ for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
+ reg = reg_base + (2 * i);
+ buf[i] = twl4030_madc_channel_raw_read(madc, reg);
+ if (buf[i] < 0) {
+ dev_err(madc->dev, "Unable to read register 0x%X\n",
+ reg);
+ return buf[i];
+ }
+ if (raw) {
+ count++;
+ continue;
+ }
+ switch (i) {
+ case 10:
+ buf[i] = twl4030battery_current(buf[i]);
+ if (buf[i] < 0) {
+ dev_err(madc->dev, "err reading current\n");
+ return buf[i];
+ } else {
+ count++;
+ buf[i] = buf[i] - 750;
+ }
+ break;
+ case 1:
+ buf[i] = twl4030battery_temperature(buf[i]);
+ if (buf[i] < 0) {
+ dev_err(madc->dev, "err reading temperature\n");
+ return buf[i];
+ } else {
+ buf[i] -= 3;
+ count++;
+ }
+ break;
+ default:
+ count++;
+ /* Analog Input (V) = conv_result * step_size / R
+ * conv_result = decimal value of 10-bit conversion
+ * result
+ * step size = 1.5 / (2 ^ 10 -1)
+ * R = Prescaler ratio for input channels.
+ * Result given in mV hence multiplied by 1000.
+ */
+ buf[i] = (buf[i] * 3 * 1000 *
+ twl4030_divider_ratios[i].denominator)
+ / (2 * 1023 *
+ twl4030_divider_ratios[i].numerator);
+ }
+ }
+
+ return count;
+}
+
+/*
+ * Disables irq.
+ * @madc - pointer to twl4030_madc_data struct
+ * @id - irq number to be disabled
+ * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
+ * corresponding to RT, SW1, SW2 conversion requests.
+ * Returns error if i2c read/write fails.
+ */
+static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
+{
+ u8 val;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
+ if (ret) {
+ dev_err(madc->dev, "unable to read imr register 0x%X\n",
+ madc->imr);
+ return ret;
+ }
+ val |= (1 << id);
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
+ if (ret) {
+ dev_err(madc->dev,
+ "unable to write imr register 0x%X\n", madc->imr);
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
+{
+ struct twl4030_madc_data *madc = _madc;
+ const struct twl4030_madc_conversion_method *method;
+ u8 isr_val, imr_val;
+ int i, ret;
+ struct twl4030_madc_request *r;
+
+ mutex_lock(&madc->lock);
+ ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
+ if (ret) {
+ dev_err(madc->dev, "unable to read isr register 0x%X\n",
+ madc->isr);
+ goto err_i2c;
+ }
+ ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
+ if (ret) {
+ dev_err(madc->dev, "unable to read imr register 0x%X\n",
+ madc->imr);
+ goto err_i2c;
+ }
+ isr_val &= ~imr_val;
+ for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+ if (!(isr_val & (1 << i)))
+ continue;
+ ret = twl4030_madc_disable_irq(madc, i);
+ if (ret < 0)
+ dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
+ madc->requests[i].result_pending = true;
+ }
+ for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+ r = &madc->requests[i];
+ /* No pending results for this method, move to next one */
+ if (!r->result_pending)
+ continue;
+ method = &twl4030_conversion_methods[r->method];
+ /* Read results */
+ twl4030_madc_read_channels(madc, method->rbase,
+ r->channels, r->rbuf, r->raw);
+ /* Free request */
+ r->result_pending = false;
+ r->active = false;
+ }
+ mutex_unlock(&madc->lock);
+
+ return IRQ_HANDLED;
+
+err_i2c:
+ /*
+ * In case of error check whichever request is active
+ * and service the same.
+ */
+ for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+ r = &madc->requests[i];
+ if (!r->active)
+ continue;
+ method = &twl4030_conversion_methods[r->method];
+ /* Read results */
+ twl4030_madc_read_channels(madc, method->rbase,
+ r->channels, r->rbuf, r->raw);
+ /* Free request */
+ r->result_pending = false;
+ r->active = false;
+ }
+ mutex_unlock(&madc->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Function which enables the madc conversion
+ * by writing to the control register.
+ * @madc - pointer to twl4030_madc_data struct
+ * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
+ * corresponding to RT SW1 or SW2 conversion methods.
+ * Returns 0 if succeeds else a negative error value
+ */
+static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
+ int conv_method)
+{
+ const struct twl4030_madc_conversion_method *method;
+ int ret = 0;
+
+ if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
+ return -ENOTSUPP;
+
+ method = &twl4030_conversion_methods[conv_method];
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
+ method->ctrl);
+ if (ret) {
+ dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
+ method->ctrl);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Function that waits for conversion to be ready
+ * @madc - pointer to twl4030_madc_data struct
+ * @timeout_ms - timeout value in milliseconds
+ * @status_reg - ctrl register
+ * returns 0 if succeeds else a negative error value
+ */
+static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
+ unsigned int timeout_ms,
+ u8 status_reg)
+{
+ unsigned long timeout;
+ int ret;
+
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ do {
+ u8 reg;
+
+ ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg);
+ if (ret) {
+ dev_err(madc->dev,
+ "unable to read status register 0x%X\n",
+ status_reg);
+ return ret;
+ }
+ if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
+ return 0;
+ usleep_range(500, 2000);
+ } while (!time_after(jiffies, timeout));
+ dev_err(madc->dev, "conversion timeout!\n");
+
+ return -EAGAIN;
+}
+
+/*
+ * An exported function which can be called from other kernel drivers.
+ * @req twl4030_madc_request structure
+ * req->rbuf will be filled with read values of channels based on the
+ * channel index. If a particular channel reading fails there will
+ * be a negative error value in the corresponding array element.
+ * returns 0 if succeeds else error value
+ */
+static int twl4030_madc_conversion(struct twl4030_madc_request *req)
+{
+ const struct twl4030_madc_conversion_method *method;
+ int ret;
+
+ if (!req || !twl4030_madc)
+ return -EINVAL;
+
+ mutex_lock(&twl4030_madc->lock);
+ if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /* Do we have a conversion request ongoing */
+ if (twl4030_madc->requests[req->method].active) {
+ ret = -EBUSY;
+ goto out;
+ }
+ method = &twl4030_conversion_methods[req->method];
+ /* Select channels to be converted */
+ ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
+ if (ret) {
+ dev_err(twl4030_madc->dev,
+ "unable to write sel register 0x%X\n", method->sel);
+ goto out;
+ }
+ /* Select averaging for all channels if do_avg is set */
+ if (req->do_avg) {
+ ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
+ method->avg);
+ if (ret) {
+ dev_err(twl4030_madc->dev,
+ "unable to write avg register 0x%X\n",
+ method->avg);
+ goto out;
+ }
+ }
+ /* With RT method we should not be here anymore */
+ if (req->method == TWL4030_MADC_RT) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
+ if (ret < 0)
+ goto out;
+ twl4030_madc->requests[req->method].active = true;
+ /* Wait until conversion is ready (ctrl register returns EOC) */
+ ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
+ if (ret) {
+ twl4030_madc->requests[req->method].active = false;
+ goto out;
+ }
+ ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
+ req->channels, req->rbuf, req->raw);
+ twl4030_madc->requests[req->method].active = false;
+
+out:
+ mutex_unlock(&twl4030_madc->lock);
+
+ return ret;
+}
+
+/**
+ * twl4030_madc_set_current_generator() - setup bias current
+ *
+ * @madc: pointer to twl4030_madc_data struct
+ * @chan: can be one of the two values:
+ * 0 - Enables bias current for main battery type reading
+ * 1 - Enables bias current for main battery temperature sensing
+ * @on: enable or disable chan.
+ *
+ * Function to enable or disable bias current for
+ * main battery type reading or temperature sensing
+ */
+static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
+ int chan, int on)
+{
+ int ret;
+ int regmask;
+ u8 regval;
+
+ ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
+ &regval, TWL4030_BCI_BCICTL1);
+ if (ret) {
+ dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
+ TWL4030_BCI_BCICTL1);
+ return ret;
+ }
+
+ regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
+ if (on)
+ regval |= regmask;
+ else
+ regval &= ~regmask;
+
+ ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ regval, TWL4030_BCI_BCICTL1);
+ if (ret) {
+ dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
+ TWL4030_BCI_BCICTL1);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Function that sets MADC software power on bit to enable MADC
+ * @madc - pointer to twl4030_madc_data struct
+ * @on - Enable or disable MADC software power on bit.
+ * returns error if i2c read/write fails else 0
+ */
+static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
+{
+ u8 regval;
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
+ &regval, TWL4030_MADC_CTRL1);
+ if (ret) {
+ dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
+ TWL4030_MADC_CTRL1);
+ return ret;
+ }
+ if (on)
+ regval |= TWL4030_MADC_MADCON;
+ else
+ regval &= ~TWL4030_MADC_MADCON;
+ ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
+ if (ret) {
+ dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
+ TWL4030_MADC_CTRL1);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize MADC and request for threaded irq
+ */
+static int twl4030_madc_probe(struct platform_device *pdev)
+{
+ struct twl4030_madc_data *madc;
+ struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ int irq, ret;
+ u8 regval;
+ struct iio_dev *iio_dev = NULL;
+
+ if (!pdata && !np) {
+ dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
+ return -EINVAL;
+ }
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
+ if (!iio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ madc = iio_priv(iio_dev);
+ madc->dev = &pdev->dev;
+
+ iio_dev->name = dev_name(&pdev->dev);
+ iio_dev->info = &twl4030_madc_iio_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = twl4030_madc_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
+
+ /*
+ * Phoenix provides 2 interrupt lines. The first one is connected to
+ * the OMAP. The other one can be connected to the other processor such
+ * as modem. Hence two separate ISR and IMR registers.
+ */
+ if (pdata)
+ madc->use_second_irq = (pdata->irq_line != 1);
+ else
+ madc->use_second_irq = of_property_read_bool(np,
+ "ti,system-uses-second-madc-irq");
+
+ madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
+ TWL4030_MADC_IMR1;
+ madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
+ TWL4030_MADC_ISR1;
+
+ ret = twl4030_madc_set_power(madc, 1);
+ if (ret < 0)
+ return ret;
+ ret = twl4030_madc_set_current_generator(madc, 0, 1);
+ if (ret < 0)
+ goto err_current_generator;
+
+ ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
+ &regval, TWL4030_BCI_BCICTL1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
+ TWL4030_BCI_BCICTL1);
+ goto err_i2c;
+ }
+ regval |= TWL4030_BCI_MESBAT;
+ ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ regval, TWL4030_BCI_BCICTL1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
+ TWL4030_BCI_BCICTL1);
+ goto err_i2c;
+ }
+
+ /* Check that MADC clock is on */
+ ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &regval, TWL4030_REG_GPBR1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
+ TWL4030_REG_GPBR1);
+ goto err_i2c;
+ }
+
+ /* If MADC clk is not on, turn it on */
+ if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
+ dev_info(&pdev->dev, "clk disabled, enabling\n");
+ regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
+ ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
+ TWL4030_REG_GPBR1);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
+ TWL4030_REG_GPBR1);
+ goto err_i2c;
+ }
+ }
+
+ platform_set_drvdata(pdev, iio_dev);
+ mutex_init(&madc->lock);
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ twl4030_madc_threaded_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "twl4030_madc", madc);
+ if (ret) {
+ dev_err(&pdev->dev, "could not request irq\n");
+ goto err_i2c;
+ }
+ twl4030_madc = madc;
+
+ /* Configure MADC[3:6] */
+ ret = twl_i2c_read_u8(TWL_MODULE_USB, &regval,
+ TWL4030_USB_CARKIT_ANA_CTRL);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to read reg CARKIT_ANA_CTRL 0x%X\n",
+ TWL4030_USB_CARKIT_ANA_CTRL);
+ goto err_i2c;
+ }
+ regval |= TWL4030_USB_SEL_MADC_MCPC;
+ ret = twl_i2c_write_u8(TWL_MODULE_USB, regval,
+ TWL4030_USB_CARKIT_ANA_CTRL);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to write reg CARKIT_ANA_CTRL 0x%X\n",
+ TWL4030_USB_CARKIT_ANA_CTRL);
+ goto err_i2c;
+ }
+
+ /* Enable 3v1 bias regulator for MADC[3:6] */
+ madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1");
+ if (IS_ERR(madc->usb3v1)) {
+ ret = -ENODEV;
+ goto err_i2c;
+ }
+
+ ret = regulator_enable(madc->usb3v1);
+ if (ret) {
+ dev_err(madc->dev, "could not enable 3v1 bias regulator\n");
+ goto err_i2c;
+ }
+
+ ret = iio_device_register(iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register iio device\n");
+ goto err_usb3v1;
+ }
+
+ return 0;
+
+err_usb3v1:
+ regulator_disable(madc->usb3v1);
+err_i2c:
+ twl4030_madc_set_current_generator(madc, 0, 0);
+err_current_generator:
+ twl4030_madc_set_power(madc, 0);
+ return ret;
+}
+
+static int twl4030_madc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *iio_dev = platform_get_drvdata(pdev);
+ struct twl4030_madc_data *madc = iio_priv(iio_dev);
+
+ iio_device_unregister(iio_dev);
+
+ twl4030_madc_set_current_generator(madc, 0, 0);
+ twl4030_madc_set_power(madc, 0);
+
+ regulator_disable(madc->usb3v1);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl_madc_of_match[] = {
+ { .compatible = "ti,twl4030-madc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_madc_of_match);
+#endif
+
+static struct platform_driver twl4030_madc_driver = {
+ .probe = twl4030_madc_probe,
+ .remove = twl4030_madc_remove,
+ .driver = {
+ .name = "twl4030_madc",
+ .of_match_table = of_match_ptr(twl_madc_of_match),
+ },
+};
+
+module_platform_driver(twl4030_madc_driver);
+
+MODULE_DESCRIPTION("TWL4030 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("J Keerthy");
+MODULE_ALIAS("platform:twl4030_madc");
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
new file mode 100644
index 000000000..32873fb5f
--- /dev/null
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -0,0 +1,1025 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TWL6030 GPADC module driver
+ *
+ * Copyright (C) 2009-2013 Texas Instruments Inc.
+ * Nishant Kamat <nskamat@ti.com>
+ * Balaji T K <balajitk@ti.com>
+ * Graeme Gregory <gg@slimlogic.co.uk>
+ * Girish S Ghongdemath <girishsg@ti.com>
+ * Ambresh K <ambresh@ti.com>
+ * Oleksandr Kozaruk <oleksandr.kozaruk@ti.com
+ *
+ * Based on twl4030-madc.c
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ */
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/mfd/twl.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define DRIVER_NAME "twl6030_gpadc"
+
+/*
+ * twl6030 per TRM has 17 channels, and twl6032 has 19 channels
+ * 2 test network channels are not used,
+ * 2 die temperature channels are not used either, as it is not
+ * defined how to convert ADC value to temperature
+ */
+#define TWL6030_GPADC_USED_CHANNELS 13
+#define TWL6030_GPADC_MAX_CHANNELS 15
+#define TWL6032_GPADC_USED_CHANNELS 15
+#define TWL6032_GPADC_MAX_CHANNELS 19
+#define TWL6030_GPADC_NUM_TRIM_REGS 16
+
+#define TWL6030_GPADC_CTRL_P1 0x05
+
+#define TWL6032_GPADC_GPSELECT_ISB 0x07
+#define TWL6032_GPADC_CTRL_P1 0x08
+
+#define TWL6032_GPADC_GPCH0_LSB 0x0d
+#define TWL6032_GPADC_GPCH0_MSB 0x0e
+
+#define TWL6030_GPADC_CTRL_P1_SP1 BIT(3)
+
+#define TWL6030_GPADC_GPCH0_LSB (0x29)
+
+#define TWL6030_GPADC_RT_SW1_EOC_MASK BIT(5)
+
+#define TWL6030_GPADC_TRIM1 0xCD
+
+#define TWL6030_REG_TOGGLE1 0x90
+#define TWL6030_GPADCS BIT(1)
+#define TWL6030_GPADCR BIT(0)
+
+#define USB_VBUS_CTRL_SET 0x04
+#define USB_ID_CTRL_SET 0x06
+
+#define TWL6030_MISC1 0xE4
+#define VBUS_MEAS 0x01
+#define ID_MEAS 0x01
+
+#define VAC_MEAS 0x04
+#define VBAT_MEAS 0x02
+#define BB_MEAS 0x01
+
+
+/**
+ * struct twl6030_chnl_calib - channel calibration
+ * @gain: slope coefficient for ideal curve
+ * @gain_error: gain error
+ * @offset_error: offset of the real curve
+ */
+struct twl6030_chnl_calib {
+ s32 gain;
+ s32 gain_error;
+ s32 offset_error;
+};
+
+/**
+ * struct twl6030_ideal_code - GPADC calibration parameters
+ * GPADC is calibrated in two points: close to the beginning and
+ * to the and of the measurable input range
+ *
+ * @channel: channel number
+ * @code1: ideal code for the input at the beginning
+ * @code2: ideal code for at the end of the range
+ * @volt1: voltage input at the beginning(low voltage)
+ * @volt2: voltage input at the end(high voltage)
+ */
+struct twl6030_ideal_code {
+ int channel;
+ u16 code1;
+ u16 code2;
+ u16 volt1;
+ u16 volt2;
+};
+
+struct twl6030_gpadc_data;
+
+/**
+ * struct twl6030_gpadc_platform_data - platform specific data
+ * @nchannels: number of GPADC channels
+ * @iio_channels: iio channels
+ * @ideal: pointer to calibration parameters
+ * @start_conversion: pointer to ADC start conversion function
+ * @channel_to_reg: pointer to ADC function to convert channel to
+ * register address for reading conversion result
+ * @calibrate: pointer to calibration function
+ */
+struct twl6030_gpadc_platform_data {
+ const int nchannels;
+ const struct iio_chan_spec *iio_channels;
+ const struct twl6030_ideal_code *ideal;
+ int (*start_conversion)(int channel);
+ u8 (*channel_to_reg)(int channel);
+ int (*calibrate)(struct twl6030_gpadc_data *gpadc);
+};
+
+/**
+ * struct twl6030_gpadc_data - GPADC data
+ * @dev: device pointer
+ * @lock: mutual exclusion lock for the structure
+ * @irq_complete: completion to signal end of conversion
+ * @twl6030_cal_tbl: pointer to calibration data for each
+ * channel with gain error and offset
+ * @pdata: pointer to device specific data
+ */
+struct twl6030_gpadc_data {
+ struct device *dev;
+ struct mutex lock;
+ struct completion irq_complete;
+ struct twl6030_chnl_calib *twl6030_cal_tbl;
+ const struct twl6030_gpadc_platform_data *pdata;
+};
+
+/*
+ * channels 11, 12, 13, 15 and 16 have no calibration data
+ * calibration offset is same for channels 1, 3, 4, 5
+ *
+ * The data is taken from GPADC_TRIM registers description.
+ * GPADC_TRIM registers keep difference between the code measured
+ * at volt1 and volt2 input voltages and corresponding code1 and code2
+ */
+static const struct twl6030_ideal_code
+ twl6030_ideal[TWL6030_GPADC_USED_CHANNELS] = {
+ [0] = { /* ch 0, external, battery type, resistor value */
+ .channel = 0,
+ .code1 = 116,
+ .code2 = 745,
+ .volt1 = 141,
+ .volt2 = 910,
+ },
+ [1] = { /* ch 1, external, battery temperature, NTC resistor value */
+ .channel = 1,
+ .code1 = 82,
+ .code2 = 900,
+ .volt1 = 100,
+ .volt2 = 1100,
+ },
+ [2] = { /* ch 2, external, audio accessory/general purpose */
+ .channel = 2,
+ .code1 = 55,
+ .code2 = 818,
+ .volt1 = 101,
+ .volt2 = 1499,
+ },
+ [3] = { /* ch 3, external, general purpose */
+ .channel = 3,
+ .code1 = 82,
+ .code2 = 900,
+ .volt1 = 100,
+ .volt2 = 1100,
+ },
+ [4] = { /* ch 4, external, temperature measurement/general purpose */
+ .channel = 4,
+ .code1 = 82,
+ .code2 = 900,
+ .volt1 = 100,
+ .volt2 = 1100,
+ },
+ [5] = { /* ch 5, external, general purpose */
+ .channel = 5,
+ .code1 = 82,
+ .code2 = 900,
+ .volt1 = 100,
+ .volt2 = 1100,
+ },
+ [6] = { /* ch 6, external, general purpose */
+ .channel = 6,
+ .code1 = 82,
+ .code2 = 900,
+ .volt1 = 100,
+ .volt2 = 1100,
+ },
+ [7] = { /* ch 7, internal, main battery */
+ .channel = 7,
+ .code1 = 614,
+ .code2 = 941,
+ .volt1 = 3001,
+ .volt2 = 4599,
+ },
+ [8] = { /* ch 8, internal, backup battery */
+ .channel = 8,
+ .code1 = 82,
+ .code2 = 688,
+ .volt1 = 501,
+ .volt2 = 4203,
+ },
+ [9] = { /* ch 9, internal, external charger input */
+ .channel = 9,
+ .code1 = 182,
+ .code2 = 818,
+ .volt1 = 2001,
+ .volt2 = 8996,
+ },
+ [10] = { /* ch 10, internal, VBUS */
+ .channel = 10,
+ .code1 = 149,
+ .code2 = 818,
+ .volt1 = 1001,
+ .volt2 = 5497,
+ },
+ [11] = { /* ch 11, internal, VBUS charging current */
+ .channel = 11,
+ },
+ /* ch 12, internal, Die temperature */
+ /* ch 13, internal, Die temperature */
+ [12] = { /* ch 14, internal, USB ID line */
+ .channel = 14,
+ .code1 = 48,
+ .code2 = 714,
+ .volt1 = 323,
+ .volt2 = 4800,
+ },
+};
+
+static const struct twl6030_ideal_code
+ twl6032_ideal[TWL6032_GPADC_USED_CHANNELS] = {
+ [0] = { /* ch 0, external, battery type, resistor value */
+ .channel = 0,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 440,
+ .volt2 = 1000,
+ },
+ [1] = { /* ch 1, external, battery temperature, NTC resistor value */
+ .channel = 1,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 440,
+ .volt2 = 1000,
+ },
+ [2] = { /* ch 2, external, audio accessory/general purpose */
+ .channel = 2,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 660,
+ .volt2 = 1500,
+ },
+ [3] = { /* ch 3, external, temperature with external diode/general
+ purpose */
+ .channel = 3,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 440,
+ .volt2 = 1000,
+ },
+ [4] = { /* ch 4, external, temperature measurement/general purpose */
+ .channel = 4,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 440,
+ .volt2 = 1000,
+ },
+ [5] = { /* ch 5, external, general purpose */
+ .channel = 5,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 440,
+ .volt2 = 1000,
+ },
+ [6] = { /* ch 6, external, general purpose */
+ .channel = 6,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 440,
+ .volt2 = 1000,
+ },
+ [7] = { /* ch7, internal, system supply */
+ .channel = 7,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 2200,
+ .volt2 = 5000,
+ },
+ [8] = { /* ch8, internal, backup battery */
+ .channel = 8,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 2200,
+ .volt2 = 5000,
+ },
+ [9] = { /* ch 9, internal, external charger input */
+ .channel = 9,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 3960,
+ .volt2 = 9000,
+ },
+ [10] = { /* ch10, internal, VBUS */
+ .channel = 10,
+ .code1 = 150,
+ .code2 = 751,
+ .volt1 = 1000,
+ .volt2 = 5000,
+ },
+ [11] = { /* ch 11, internal, VBUS DC-DC output current */
+ .channel = 11,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 660,
+ .volt2 = 1500,
+ },
+ /* ch 12, internal, Die temperature */
+ /* ch 13, internal, Die temperature */
+ [12] = { /* ch 14, internal, USB ID line */
+ .channel = 14,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 2420,
+ .volt2 = 5500,
+ },
+ /* ch 15, internal, test network */
+ /* ch 16, internal, test network */
+ [13] = { /* ch 17, internal, battery charging current */
+ .channel = 17,
+ },
+ [14] = { /* ch 18, internal, battery voltage */
+ .channel = 18,
+ .code1 = 1441,
+ .code2 = 3276,
+ .volt1 = 2200,
+ .volt2 = 5000,
+ },
+};
+
+static inline int twl6030_gpadc_write(u8 reg, u8 val)
+{
+ return twl_i2c_write_u8(TWL6030_MODULE_GPADC, val, reg);
+}
+
+static inline int twl6030_gpadc_read(u8 reg, u8 *val)
+{
+
+ return twl_i2c_read(TWL6030_MODULE_GPADC, val, reg, 2);
+}
+
+static int twl6030_gpadc_enable_irq(u8 mask)
+{
+ int ret;
+
+ ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_LINE_B);
+ if (ret < 0)
+ return ret;
+
+ ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_STS_B);
+
+ return ret;
+}
+
+static void twl6030_gpadc_disable_irq(u8 mask)
+{
+ twl6030_interrupt_mask(mask, REG_INT_MSK_LINE_B);
+ twl6030_interrupt_mask(mask, REG_INT_MSK_STS_B);
+}
+
+static irqreturn_t twl6030_gpadc_irq_handler(int irq, void *indio_dev)
+{
+ struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev);
+
+ complete(&gpadc->irq_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int twl6030_start_conversion(int channel)
+{
+ return twl6030_gpadc_write(TWL6030_GPADC_CTRL_P1,
+ TWL6030_GPADC_CTRL_P1_SP1);
+}
+
+static int twl6032_start_conversion(int channel)
+{
+ int ret;
+
+ ret = twl6030_gpadc_write(TWL6032_GPADC_GPSELECT_ISB, channel);
+ if (ret)
+ return ret;
+
+ return twl6030_gpadc_write(TWL6032_GPADC_CTRL_P1,
+ TWL6030_GPADC_CTRL_P1_SP1);
+}
+
+static u8 twl6030_channel_to_reg(int channel)
+{
+ return TWL6030_GPADC_GPCH0_LSB + 2 * channel;
+}
+
+static u8 twl6032_channel_to_reg(int channel)
+{
+ /*
+ * for any prior chosen channel, when the conversion is ready
+ * the result is avalable in GPCH0_LSB, GPCH0_MSB.
+ */
+
+ return TWL6032_GPADC_GPCH0_LSB;
+}
+
+static int twl6030_gpadc_lookup(const struct twl6030_ideal_code *ideal,
+ int channel, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (ideal[i].channel == channel)
+ break;
+
+ return i;
+}
+
+static int twl6030_channel_calibrated(const struct twl6030_gpadc_platform_data
+ *pdata, int channel)
+{
+ const struct twl6030_ideal_code *ideal = pdata->ideal;
+ int i;
+
+ i = twl6030_gpadc_lookup(ideal, channel, pdata->nchannels);
+ /* not calibrated channels have 0 in all structure members */
+ return pdata->ideal[i].code2;
+}
+
+static int twl6030_gpadc_make_correction(struct twl6030_gpadc_data *gpadc,
+ int channel, int raw_code)
+{
+ const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal;
+ int corrected_code;
+ int i;
+
+ i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels);
+ corrected_code = ((raw_code * 1000) -
+ gpadc->twl6030_cal_tbl[i].offset_error) /
+ gpadc->twl6030_cal_tbl[i].gain_error;
+
+ return corrected_code;
+}
+
+static int twl6030_gpadc_get_raw(struct twl6030_gpadc_data *gpadc,
+ int channel, int *res)
+{
+ u8 reg = gpadc->pdata->channel_to_reg(channel);
+ __le16 val;
+ int raw_code;
+ int ret;
+
+ ret = twl6030_gpadc_read(reg, (u8 *)&val);
+ if (ret) {
+ dev_dbg(gpadc->dev, "unable to read register 0x%X\n", reg);
+ return ret;
+ }
+
+ raw_code = le16_to_cpu(val);
+ dev_dbg(gpadc->dev, "GPADC raw code: %d", raw_code);
+
+ if (twl6030_channel_calibrated(gpadc->pdata, channel))
+ *res = twl6030_gpadc_make_correction(gpadc, channel, raw_code);
+ else
+ *res = raw_code;
+
+ return ret;
+}
+
+static int twl6030_gpadc_get_processed(struct twl6030_gpadc_data *gpadc,
+ int channel, int *val)
+{
+ const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal;
+ int corrected_code;
+ int channel_value;
+ int i;
+ int ret;
+
+ ret = twl6030_gpadc_get_raw(gpadc, channel, &corrected_code);
+ if (ret)
+ return ret;
+
+ i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels);
+ channel_value = corrected_code *
+ gpadc->twl6030_cal_tbl[i].gain;
+
+ /* Shift back into mV range */
+ channel_value /= 1000;
+
+ dev_dbg(gpadc->dev, "GPADC corrected code: %d", corrected_code);
+ dev_dbg(gpadc->dev, "GPADC value: %d", channel_value);
+
+ *val = channel_value;
+
+ return ret;
+}
+
+static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev);
+ int ret;
+ long timeout;
+
+ mutex_lock(&gpadc->lock);
+
+ ret = gpadc->pdata->start_conversion(chan->channel);
+ if (ret) {
+ dev_err(gpadc->dev, "failed to start conversion\n");
+ goto err;
+ }
+ /* wait for conversion to complete */
+ timeout = wait_for_completion_interruptible_timeout(
+ &gpadc->irq_complete, msecs_to_jiffies(5000));
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ } else if (timeout < 0) {
+ ret = -EINTR;
+ goto err;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = twl6030_gpadc_get_raw(gpadc, chan->channel, val);
+ ret = ret ? -EIO : IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = twl6030_gpadc_get_processed(gpadc, chan->channel, val);
+ ret = ret ? -EIO : IIO_VAL_INT;
+ break;
+
+ default:
+ break;
+ }
+err:
+ mutex_unlock(&gpadc->lock);
+
+ return ret;
+}
+
+/*
+ * The GPADC channels are calibrated using a two point calibration method.
+ * The channels measured with two known values: volt1 and volt2, and
+ * ideal corresponding output codes are known: code1, code2.
+ * The difference(d1, d2) between ideal and measured codes stored in trim
+ * registers.
+ * The goal is to find offset and gain of the real curve for each calibrated
+ * channel.
+ * gain: k = 1 + ((d2 - d1) / (x2 - x1))
+ * offset: b = d1 + (k - 1) * x1
+ */
+static void twl6030_calibrate_channel(struct twl6030_gpadc_data *gpadc,
+ int channel, int d1, int d2)
+{
+ int b, k, gain, x1, x2, i;
+ const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal;
+
+ i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels);
+
+ /* Gain */
+ gain = ((ideal[i].volt2 - ideal[i].volt1) * 1000) /
+ (ideal[i].code2 - ideal[i].code1);
+
+ x1 = ideal[i].code1;
+ x2 = ideal[i].code2;
+
+ /* k - real curve gain */
+ k = 1000 + (((d2 - d1) * 1000) / (x2 - x1));
+
+ /* b - offset of the real curve gain */
+ b = (d1 * 1000) - (k - 1000) * x1;
+
+ gpadc->twl6030_cal_tbl[i].gain = gain;
+ gpadc->twl6030_cal_tbl[i].gain_error = k;
+ gpadc->twl6030_cal_tbl[i].offset_error = b;
+
+ dev_dbg(gpadc->dev, "GPADC d1 for Chn: %d = %d\n", channel, d1);
+ dev_dbg(gpadc->dev, "GPADC d2 for Chn: %d = %d\n", channel, d2);
+ dev_dbg(gpadc->dev, "GPADC x1 for Chn: %d = %d\n", channel, x1);
+ dev_dbg(gpadc->dev, "GPADC x2 for Chn: %d = %d\n", channel, x2);
+ dev_dbg(gpadc->dev, "GPADC Gain for Chn: %d = %d\n", channel, gain);
+ dev_dbg(gpadc->dev, "GPADC k for Chn: %d = %d\n", channel, k);
+ dev_dbg(gpadc->dev, "GPADC b for Chn: %d = %d\n", channel, b);
+}
+
+static inline int twl6030_gpadc_get_trim_offset(s8 d)
+{
+ /*
+ * XXX NOTE!
+ * bit 0 - sign, bit 7 - reserved, 6..1 - trim value
+ * though, the documentation states that trim value
+ * is absolute value, the correct conversion results are
+ * obtained if the value is interpreted as 2's complement.
+ */
+ __u32 temp = ((d & 0x7f) >> 1) | ((d & 1) << 6);
+
+ return sign_extend32(temp, 6);
+}
+
+static int twl6030_calibration(struct twl6030_gpadc_data *gpadc)
+{
+ int ret;
+ int chn;
+ u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS];
+ s8 d1, d2;
+
+ /*
+ * for calibration two measurements have been performed at
+ * factory, for some channels, during the production test and
+ * have been stored in registers. This two stored values are
+ * used to correct the measurements. The values represent
+ * offsets for the given input from the output on ideal curve.
+ */
+ ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs,
+ TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "calibration failed\n");
+ return ret;
+ }
+
+ for (chn = 0; chn < TWL6030_GPADC_MAX_CHANNELS; chn++) {
+
+ switch (chn) {
+ case 0:
+ d1 = trim_regs[0];
+ d2 = trim_regs[1];
+ break;
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ d1 = trim_regs[4];
+ d2 = trim_regs[5];
+ break;
+ case 2:
+ d1 = trim_regs[12];
+ d2 = trim_regs[13];
+ break;
+ case 7:
+ d1 = trim_regs[6];
+ d2 = trim_regs[7];
+ break;
+ case 8:
+ d1 = trim_regs[2];
+ d2 = trim_regs[3];
+ break;
+ case 9:
+ d1 = trim_regs[8];
+ d2 = trim_regs[9];
+ break;
+ case 10:
+ d1 = trim_regs[10];
+ d2 = trim_regs[11];
+ break;
+ case 14:
+ d1 = trim_regs[14];
+ d2 = trim_regs[15];
+ break;
+ default:
+ continue;
+ }
+
+ d1 = twl6030_gpadc_get_trim_offset(d1);
+ d2 = twl6030_gpadc_get_trim_offset(d2);
+
+ twl6030_calibrate_channel(gpadc, chn, d1, d2);
+ }
+
+ return 0;
+}
+
+static int twl6032_get_trim_value(u8 *trim_regs, unsigned int reg0,
+ unsigned int reg1, unsigned int mask0, unsigned int mask1,
+ unsigned int shift0)
+{
+ int val;
+
+ val = (trim_regs[reg0] & mask0) << shift0;
+ val |= (trim_regs[reg1] & mask1) >> 1;
+ if (trim_regs[reg1] & 0x01)
+ val = -val;
+
+ return val;
+}
+
+static int twl6032_calibration(struct twl6030_gpadc_data *gpadc)
+{
+ int chn, d1 = 0, d2 = 0, temp;
+ u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS];
+ int ret;
+
+ ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs,
+ TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "calibration failed\n");
+ return ret;
+ }
+
+ /*
+ * Loop to calculate the value needed for returning voltages from
+ * GPADC not values.
+ *
+ * gain is calculated to 3 decimal places fixed point.
+ */
+ for (chn = 0; chn < TWL6032_GPADC_MAX_CHANNELS; chn++) {
+
+ switch (chn) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 11:
+ case 14:
+ d1 = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f,
+ 0x06, 2);
+ d2 = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f,
+ 0x06, 2);
+ break;
+ case 8:
+ temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f,
+ 0x06, 2);
+ d1 = temp + twl6032_get_trim_value(trim_regs, 7, 6,
+ 0x18, 0x1E, 1);
+
+ temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3F,
+ 0x06, 2);
+ d2 = temp + twl6032_get_trim_value(trim_regs, 9, 7,
+ 0x1F, 0x06, 2);
+ break;
+ case 9:
+ temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f,
+ 0x06, 2);
+ d1 = temp + twl6032_get_trim_value(trim_regs, 13, 11,
+ 0x18, 0x1E, 1);
+
+ temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f,
+ 0x06, 2);
+ d2 = temp + twl6032_get_trim_value(trim_regs, 15, 13,
+ 0x1F, 0x06, 1);
+ break;
+ case 10:
+ d1 = twl6032_get_trim_value(trim_regs, 10, 8, 0x0f,
+ 0x0E, 3);
+ d2 = twl6032_get_trim_value(trim_regs, 14, 12, 0x0f,
+ 0x0E, 3);
+ break;
+ case 7:
+ case 18:
+ temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f,
+ 0x06, 2);
+
+ d1 = (trim_regs[4] & 0x7E) >> 1;
+ if (trim_regs[4] & 0x01)
+ d1 = -d1;
+ d1 += temp;
+
+ temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f,
+ 0x06, 2);
+
+ d2 = (trim_regs[5] & 0xFE) >> 1;
+ if (trim_regs[5] & 0x01)
+ d2 = -d2;
+
+ d2 += temp;
+ break;
+ default:
+ /* No data for other channels */
+ continue;
+ }
+
+ twl6030_calibrate_channel(gpadc, chn, d1, d2);
+ }
+
+ return 0;
+}
+
+#define TWL6030_GPADC_CHAN(chn, _type, chan_info) { \
+ .type = _type, \
+ .channel = chn, \
+ .info_mask_separate = BIT(chan_info), \
+ .indexed = 1, \
+}
+
+static const struct iio_chan_spec twl6030_gpadc_iio_channels[] = {
+ TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+};
+
+static const struct iio_chan_spec twl6032_gpadc_iio_channels[] = {
+ TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW),
+ TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+ TWL6030_GPADC_CHAN(17, IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+ TWL6030_GPADC_CHAN(18, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
+};
+
+static const struct iio_info twl6030_gpadc_iio_info = {
+ .read_raw = &twl6030_gpadc_read_raw,
+};
+
+static const struct twl6030_gpadc_platform_data twl6030_pdata = {
+ .iio_channels = twl6030_gpadc_iio_channels,
+ .nchannels = TWL6030_GPADC_USED_CHANNELS,
+ .ideal = twl6030_ideal,
+ .start_conversion = twl6030_start_conversion,
+ .channel_to_reg = twl6030_channel_to_reg,
+ .calibrate = twl6030_calibration,
+};
+
+static const struct twl6030_gpadc_platform_data twl6032_pdata = {
+ .iio_channels = twl6032_gpadc_iio_channels,
+ .nchannels = TWL6032_GPADC_USED_CHANNELS,
+ .ideal = twl6032_ideal,
+ .start_conversion = twl6032_start_conversion,
+ .channel_to_reg = twl6032_channel_to_reg,
+ .calibrate = twl6032_calibration,
+};
+
+static const struct of_device_id of_twl6030_match_tbl[] = {
+ {
+ .compatible = "ti,twl6030-gpadc",
+ .data = &twl6030_pdata,
+ },
+ {
+ .compatible = "ti,twl6032-gpadc",
+ .data = &twl6032_pdata,
+ },
+ { /* end */ }
+};
+MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl);
+
+static int twl6030_gpadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct twl6030_gpadc_data *gpadc;
+ const struct twl6030_gpadc_platform_data *pdata;
+ const struct of_device_id *match;
+ struct iio_dev *indio_dev;
+ int irq;
+ int ret;
+
+ match = of_match_device(of_twl6030_match_tbl, dev);
+ if (!match)
+ return -EINVAL;
+
+ pdata = match->data;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ gpadc = iio_priv(indio_dev);
+
+ gpadc->twl6030_cal_tbl = devm_kcalloc(dev,
+ pdata->nchannels,
+ sizeof(*gpadc->twl6030_cal_tbl),
+ GFP_KERNEL);
+ if (!gpadc->twl6030_cal_tbl)
+ return -ENOMEM;
+
+ gpadc->dev = dev;
+ gpadc->pdata = pdata;
+
+ platform_set_drvdata(pdev, indio_dev);
+ mutex_init(&gpadc->lock);
+ init_completion(&gpadc->irq_complete);
+
+ ret = pdata->calibrate(gpadc);
+ if (ret < 0) {
+ dev_err(dev, "failed to read calibration registers\n");
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ twl6030_gpadc_irq_handler,
+ IRQF_ONESHOT, "twl6030_gpadc", indio_dev);
+ if (ret)
+ return ret;
+
+ ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable GPADC interrupt\n");
+ return ret;
+ }
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS,
+ TWL6030_REG_TOGGLE1);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable GPADC module\n");
+ return ret;
+ }
+
+ ret = twl_i2c_write_u8(TWL_MODULE_USB, VBUS_MEAS, USB_VBUS_CTRL_SET);
+ if (ret < 0) {
+ dev_err(dev, "failed to wire up inputs\n");
+ return ret;
+ }
+
+ ret = twl_i2c_write_u8(TWL_MODULE_USB, ID_MEAS, USB_ID_CTRL_SET);
+ if (ret < 0) {
+ dev_err(dev, "failed to wire up inputs\n");
+ return ret;
+ }
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID0,
+ VBAT_MEAS | BB_MEAS | VAC_MEAS,
+ TWL6030_MISC1);
+ if (ret < 0) {
+ dev_err(dev, "failed to wire up inputs\n");
+ return ret;
+ }
+
+ indio_dev->name = DRIVER_NAME;
+ indio_dev->info = &twl6030_gpadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = pdata->iio_channels;
+ indio_dev->num_channels = pdata->nchannels;
+
+ return iio_device_register(indio_dev);
+}
+
+static int twl6030_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ twl6030_gpadc_disable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK);
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static int twl6030_gpadc_suspend(struct device *pdev)
+{
+ int ret;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCR,
+ TWL6030_REG_TOGGLE1);
+ if (ret)
+ dev_err(pdev, "error resetting GPADC (%d)!\n", ret);
+
+ return 0;
+};
+
+static int twl6030_gpadc_resume(struct device *pdev)
+{
+ int ret;
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS,
+ TWL6030_REG_TOGGLE1);
+ if (ret)
+ dev_err(pdev, "error setting GPADC (%d)!\n", ret);
+
+ return 0;
+};
+
+static DEFINE_SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend,
+ twl6030_gpadc_resume);
+
+static struct platform_driver twl6030_gpadc_driver = {
+ .probe = twl6030_gpadc_probe,
+ .remove = twl6030_gpadc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = pm_sleep_ptr(&twl6030_gpadc_pm_ops),
+ .of_match_table = of_twl6030_match_tbl,
+ },
+};
+
+module_platform_driver(twl6030_gpadc_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
+MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
+MODULE_AUTHOR("Oleksandr Kozaruk <oleksandr.kozaruk@ti.com");
+MODULE_DESCRIPTION("twl6030 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
new file mode 100644
index 000000000..c6b16cf6e
--- /dev/null
+++ b/drivers/iio/adc/vf610_adc.c
@@ -0,0 +1,969 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale Vybrid vf610 ADC driver
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* This will be the driver name the kernel reports */
+#define DRIVER_NAME "vf610-adc"
+
+/* Vybrid/IMX ADC registers */
+#define VF610_REG_ADC_HC0 0x00
+#define VF610_REG_ADC_HC1 0x04
+#define VF610_REG_ADC_HS 0x08
+#define VF610_REG_ADC_R0 0x0c
+#define VF610_REG_ADC_R1 0x10
+#define VF610_REG_ADC_CFG 0x14
+#define VF610_REG_ADC_GC 0x18
+#define VF610_REG_ADC_GS 0x1c
+#define VF610_REG_ADC_CV 0x20
+#define VF610_REG_ADC_OFS 0x24
+#define VF610_REG_ADC_CAL 0x28
+#define VF610_REG_ADC_PCTL 0x30
+
+/* Configuration register field define */
+#define VF610_ADC_MODE_BIT8 0x00
+#define VF610_ADC_MODE_BIT10 0x04
+#define VF610_ADC_MODE_BIT12 0x08
+#define VF610_ADC_MODE_MASK 0x0c
+#define VF610_ADC_BUSCLK2_SEL 0x01
+#define VF610_ADC_ALTCLK_SEL 0x02
+#define VF610_ADC_ADACK_SEL 0x03
+#define VF610_ADC_ADCCLK_MASK 0x03
+#define VF610_ADC_CLK_DIV2 0x20
+#define VF610_ADC_CLK_DIV4 0x40
+#define VF610_ADC_CLK_DIV8 0x60
+#define VF610_ADC_CLK_MASK 0x60
+#define VF610_ADC_ADLSMP_LONG 0x10
+#define VF610_ADC_ADSTS_SHORT 0x100
+#define VF610_ADC_ADSTS_NORMAL 0x200
+#define VF610_ADC_ADSTS_LONG 0x300
+#define VF610_ADC_ADSTS_MASK 0x300
+#define VF610_ADC_ADLPC_EN 0x80
+#define VF610_ADC_ADHSC_EN 0x400
+#define VF610_ADC_REFSEL_VALT 0x800
+#define VF610_ADC_REFSEL_VBG 0x1000
+#define VF610_ADC_ADTRG_HARD 0x2000
+#define VF610_ADC_AVGS_8 0x4000
+#define VF610_ADC_AVGS_16 0x8000
+#define VF610_ADC_AVGS_32 0xC000
+#define VF610_ADC_AVGS_MASK 0xC000
+#define VF610_ADC_OVWREN 0x10000
+
+/* General control register field define */
+#define VF610_ADC_ADACKEN 0x1
+#define VF610_ADC_DMAEN 0x2
+#define VF610_ADC_ACREN 0x4
+#define VF610_ADC_ACFGT 0x8
+#define VF610_ADC_ACFE 0x10
+#define VF610_ADC_AVGEN 0x20
+#define VF610_ADC_ADCON 0x40
+#define VF610_ADC_CAL 0x80
+
+/* Other field define */
+#define VF610_ADC_ADCHC(x) ((x) & 0x1F)
+#define VF610_ADC_AIEN (0x1 << 7)
+#define VF610_ADC_CONV_DISABLE 0x1F
+#define VF610_ADC_HS_COCO0 0x1
+#define VF610_ADC_CALF 0x2
+#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
+
+#define DEFAULT_SAMPLE_TIME 1000
+
+/* V at 25°C of 696 mV */
+#define VF610_VTEMP25_3V0 950
+/* V at 25°C of 699 mV */
+#define VF610_VTEMP25_3V3 867
+/* Typical sensor slope coefficient at all temperatures */
+#define VF610_TEMP_SLOPE_COEFF 1840
+
+enum clk_sel {
+ VF610_ADCIOC_BUSCLK_SET,
+ VF610_ADCIOC_ALTCLK_SET,
+ VF610_ADCIOC_ADACK_SET,
+};
+
+enum vol_ref {
+ VF610_ADCIOC_VR_VREF_SET,
+ VF610_ADCIOC_VR_VALT_SET,
+ VF610_ADCIOC_VR_VBG_SET,
+};
+
+enum average_sel {
+ VF610_ADC_SAMPLE_1,
+ VF610_ADC_SAMPLE_4,
+ VF610_ADC_SAMPLE_8,
+ VF610_ADC_SAMPLE_16,
+ VF610_ADC_SAMPLE_32,
+};
+
+enum conversion_mode_sel {
+ VF610_ADC_CONV_NORMAL,
+ VF610_ADC_CONV_HIGH_SPEED,
+ VF610_ADC_CONV_LOW_POWER,
+};
+
+enum lst_adder_sel {
+ VF610_ADCK_CYCLES_3,
+ VF610_ADCK_CYCLES_5,
+ VF610_ADCK_CYCLES_7,
+ VF610_ADCK_CYCLES_9,
+ VF610_ADCK_CYCLES_13,
+ VF610_ADCK_CYCLES_17,
+ VF610_ADCK_CYCLES_21,
+ VF610_ADCK_CYCLES_25,
+};
+
+struct vf610_adc_feature {
+ enum clk_sel clk_sel;
+ enum vol_ref vol_ref;
+ enum conversion_mode_sel conv_mode;
+
+ int clk_div;
+ int sample_rate;
+ int res_mode;
+ u32 lst_adder_index;
+ u32 default_sample_time;
+
+ bool calibration;
+ bool ovwren;
+};
+
+struct vf610_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+
+ u32 vref_uv;
+ u32 value;
+ struct regulator *vref;
+
+ u32 max_adck_rate[3];
+ struct vf610_adc_feature adc_feature;
+
+ u32 sample_freq_avail[5];
+
+ struct completion completion;
+ /* Ensure the timestamp is naturally aligned */
+ struct {
+ u16 chan;
+ s64 timestamp __aligned(8);
+ } scan;
+};
+
+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 };
+
+static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+ unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
+ u32 adck_period, lst_addr_min;
+ int divisor, i;
+
+ adck_rate = info->max_adck_rate[adc_feature->conv_mode];
+
+ if (adck_rate) {
+ /* calculate clk divider which is within specification */
+ divisor = ipg_rate / adck_rate;
+ adc_feature->clk_div = 1 << fls(divisor + 1);
+ } else {
+ /* fall-back value using a safe divisor */
+ adc_feature->clk_div = 8;
+ }
+
+ adck_rate = ipg_rate / adc_feature->clk_div;
+
+ /*
+ * Determine the long sample time adder value to be used based
+ * on the default minimum sample time provided.
+ */
+ adck_period = NSEC_PER_SEC / adck_rate;
+ lst_addr_min = adc_feature->default_sample_time / adck_period;
+ for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) {
+ if (vf610_lst_adder[i] > lst_addr_min) {
+ adc_feature->lst_adder_index = i;
+ break;
+ }
+ }
+
+ /*
+ * Calculate ADC sample frequencies
+ * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
+ * which is the same as bus clock.
+ *
+ * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
+ * SFCAdder: fixed to 6 ADCK cycles
+ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
+ * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
+ * LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles
+ */
+ for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
+ info->sample_freq_avail[i] =
+ adck_rate / (6 + vf610_hw_avgs[i] *
+ (25 + vf610_lst_adder[adc_feature->lst_adder_index]));
+}
+
+static inline void vf610_adc_cfg_init(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+
+ /* set default Configuration for ADC controller */
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
+
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
+
+ adc_feature->res_mode = 12;
+ adc_feature->sample_rate = 1;
+
+ adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
+
+ vf610_adc_calculate_rates(info);
+}
+
+static void vf610_adc_cfg_post_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+ int cfg_data = 0;
+ int gc_data = 0;
+
+ switch (adc_feature->clk_sel) {
+ case VF610_ADCIOC_ALTCLK_SET:
+ cfg_data |= VF610_ADC_ALTCLK_SEL;
+ break;
+ case VF610_ADCIOC_ADACK_SET:
+ cfg_data |= VF610_ADC_ADACK_SEL;
+ break;
+ default:
+ break;
+ }
+
+ /* low power set for calibration */
+ cfg_data |= VF610_ADC_ADLPC_EN;
+
+ /* enable high speed for calibration */
+ cfg_data |= VF610_ADC_ADHSC_EN;
+
+ /* voltage reference */
+ switch (adc_feature->vol_ref) {
+ case VF610_ADCIOC_VR_VREF_SET:
+ break;
+ case VF610_ADCIOC_VR_VALT_SET:
+ cfg_data |= VF610_ADC_REFSEL_VALT;
+ break;
+ case VF610_ADCIOC_VR_VBG_SET:
+ cfg_data |= VF610_ADC_REFSEL_VBG;
+ break;
+ default:
+ dev_err(info->dev, "error voltage reference\n");
+ }
+
+ /* data overwrite enable */
+ if (adc_feature->ovwren)
+ cfg_data |= VF610_ADC_OVWREN;
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+ writel(gc_data, info->regs + VF610_REG_ADC_GC);
+}
+
+static void vf610_adc_calibration(struct vf610_adc *info)
+{
+ int adc_gc, hc_cfg;
+
+ if (!info->adc_feature.calibration)
+ return;
+
+ /* enable calibration interrupt */
+ hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE;
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ adc_gc = readl(info->regs + VF610_REG_ADC_GC);
+ writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
+
+ if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT))
+ dev_err(info->dev, "Timeout for adc calibration\n");
+
+ adc_gc = readl(info->regs + VF610_REG_ADC_GS);
+ if (adc_gc & VF610_ADC_CALF)
+ dev_err(info->dev, "ADC calibration failed\n");
+
+ info->adc_feature.calibration = false;
+}
+
+static void vf610_adc_cfg_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &(info->adc_feature);
+ int cfg_data;
+
+ cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
+
+ cfg_data &= ~VF610_ADC_ADLPC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
+ cfg_data |= VF610_ADC_ADLPC_EN;
+
+ cfg_data &= ~VF610_ADC_ADHSC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
+ cfg_data |= VF610_ADC_ADHSC_EN;
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+}
+
+static void vf610_adc_sample_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &(info->adc_feature);
+ int cfg_data, gc_data;
+
+ cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
+ gc_data = readl(info->regs + VF610_REG_ADC_GC);
+
+ /* resolution mode */
+ cfg_data &= ~VF610_ADC_MODE_MASK;
+ switch (adc_feature->res_mode) {
+ case 8:
+ cfg_data |= VF610_ADC_MODE_BIT8;
+ break;
+ case 10:
+ cfg_data |= VF610_ADC_MODE_BIT10;
+ break;
+ case 12:
+ cfg_data |= VF610_ADC_MODE_BIT12;
+ break;
+ default:
+ dev_err(info->dev, "error resolution mode\n");
+ break;
+ }
+
+ /* clock select and clock divider */
+ cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
+ switch (adc_feature->clk_div) {
+ case 1:
+ break;
+ case 2:
+ cfg_data |= VF610_ADC_CLK_DIV2;
+ break;
+ case 4:
+ cfg_data |= VF610_ADC_CLK_DIV4;
+ break;
+ case 8:
+ cfg_data |= VF610_ADC_CLK_DIV8;
+ break;
+ case 16:
+ switch (adc_feature->clk_sel) {
+ case VF610_ADCIOC_BUSCLK_SET:
+ cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
+ break;
+ default:
+ dev_err(info->dev, "error clk divider\n");
+ break;
+ }
+ break;
+ }
+
+ /*
+ * Set ADLSMP and ADSTS based on the Long Sample Time Adder value
+ * determined.
+ */
+ switch (adc_feature->lst_adder_index) {
+ case VF610_ADCK_CYCLES_3:
+ break;
+ case VF610_ADCK_CYCLES_5:
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_7:
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_9:
+ cfg_data |= VF610_ADC_ADSTS_LONG;
+ break;
+ case VF610_ADCK_CYCLES_13:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ break;
+ case VF610_ADCK_CYCLES_17:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_21:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_25:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ default:
+ dev_err(info->dev, "error in sample time select\n");
+ }
+
+ /* update hardware average selection */
+ cfg_data &= ~VF610_ADC_AVGS_MASK;
+ gc_data &= ~VF610_ADC_AVGEN;
+ switch (adc_feature->sample_rate) {
+ case VF610_ADC_SAMPLE_1:
+ break;
+ case VF610_ADC_SAMPLE_4:
+ gc_data |= VF610_ADC_AVGEN;
+ break;
+ case VF610_ADC_SAMPLE_8:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_8;
+ break;
+ case VF610_ADC_SAMPLE_16:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_16;
+ break;
+ case VF610_ADC_SAMPLE_32:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_32;
+ break;
+ default:
+ dev_err(info->dev,
+ "error hardware sample average select\n");
+ }
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+ writel(gc_data, info->regs + VF610_REG_ADC_GC);
+}
+
+static void vf610_adc_hw_init(struct vf610_adc *info)
+{
+ /* CFG: Feature set */
+ vf610_adc_cfg_post_set(info);
+ vf610_adc_sample_set(info);
+
+ /* adc calibration */
+ vf610_adc_calibration(info);
+
+ /* CFG: power and speed set */
+ vf610_adc_cfg_set(info);
+}
+
+static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ info->adc_feature.conv_mode = mode;
+ vf610_adc_calculate_rates(info);
+ vf610_adc_hw_init(info);
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ return info->adc_feature.conv_mode;
+}
+
+static const char * const vf610_conv_modes[] = { "normal", "high-speed",
+ "low-power" };
+
+static const struct iio_enum vf610_conversion_mode = {
+ .items = vf610_conv_modes,
+ .num_items = ARRAY_SIZE(vf610_conv_modes),
+ .get = vf610_get_conversion_mode,
+ .set = vf610_set_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) { \
+ .type = (_chan_type), \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .ext_info = vf610_ext_info, \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+}
+
+#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+}
+
+static const struct iio_chan_spec vf610_adc_iio_channels[] = {
+ VF610_ADC_CHAN(0, IIO_VOLTAGE),
+ VF610_ADC_CHAN(1, IIO_VOLTAGE),
+ VF610_ADC_CHAN(2, IIO_VOLTAGE),
+ VF610_ADC_CHAN(3, IIO_VOLTAGE),
+ VF610_ADC_CHAN(4, IIO_VOLTAGE),
+ VF610_ADC_CHAN(5, IIO_VOLTAGE),
+ VF610_ADC_CHAN(6, IIO_VOLTAGE),
+ VF610_ADC_CHAN(7, IIO_VOLTAGE),
+ VF610_ADC_CHAN(8, IIO_VOLTAGE),
+ VF610_ADC_CHAN(9, IIO_VOLTAGE),
+ VF610_ADC_CHAN(10, IIO_VOLTAGE),
+ VF610_ADC_CHAN(11, IIO_VOLTAGE),
+ VF610_ADC_CHAN(12, IIO_VOLTAGE),
+ VF610_ADC_CHAN(13, IIO_VOLTAGE),
+ VF610_ADC_CHAN(14, IIO_VOLTAGE),
+ VF610_ADC_CHAN(15, IIO_VOLTAGE),
+ VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(32),
+ /* sentinel */
+};
+
+static int vf610_adc_read_data(struct vf610_adc *info)
+{
+ int result;
+
+ result = readl(info->regs + VF610_REG_ADC_R0);
+
+ switch (info->adc_feature.res_mode) {
+ case 8:
+ result &= 0xFF;
+ break;
+ case 10:
+ result &= 0x3FF;
+ break;
+ case 12:
+ result &= 0xFFF;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct vf610_adc *info = iio_priv(indio_dev);
+ int coco;
+
+ coco = readl(info->regs + VF610_REG_ADC_HS);
+ if (coco & VF610_ADC_HS_COCO0) {
+ 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_trigger_notify_done(indio_dev->trig);
+ } else
+ complete(&info->completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t vf610_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%u ", info->sample_freq_avail[i]);
+
+ /* replace trailing space by newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
+
+static struct attribute *vf610_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group vf610_attribute_group = {
+ .attrs = vf610_attributes,
+};
+
+static int vf610_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+ unsigned int hc_cfg;
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+
+ 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) {
+ mutex_unlock(&indio_dev->mlock);
+ return -ETIMEDOUT;
+ }
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = info->value;
+ break;
+ case IIO_TEMP:
+ /*
+ * Calculate in degree Celsius times 1000
+ * Using the typical sensor slope of 1.84 mV/°C
+ * and VREFH_ADC at 3.3V, V at 25°C of 699 mV
+ */
+ *val = 25000 - ((int)info->value - VF610_VTEMP25_3V3) *
+ 1000000 / VF610_TEMP_SLOPE_COEFF;
+
+ break;
+ default:
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->vref_uv / 1000;
+ *val2 = info->adc_feature.res_mode;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = info->sample_freq_avail[info->adc_feature.sample_rate];
+ *val2 = 0;
+ return IIO_VAL_INT;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int vf610_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0;
+ i < ARRAY_SIZE(info->sample_freq_avail);
+ i++)
+ if (val == info->sample_freq_avail[i]) {
+ info->adc_feature.sample_rate = i;
+ vf610_adc_sample_set(info);
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+ unsigned int channel;
+ int val;
+
+ val = readl(info->regs + VF610_REG_ADC_GC);
+ val |= VF610_ADC_ADCON;
+ writel(val, info->regs + VF610_REG_ADC_GC);
+
+ channel = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+
+ val = VF610_ADC_ADCHC(channel);
+ val |= VF610_ADC_AIEN;
+
+ writel(val, info->regs + VF610_REG_ADC_HC0);
+
+ return 0;
+}
+
+static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+ unsigned int hc_cfg = 0;
+ int val;
+
+ val = readl(info->regs + VF610_REG_ADC_GC);
+ val &= ~VF610_ADC_ADCON;
+ writel(val, info->regs + VF610_REG_ADC_GC);
+
+ hc_cfg |= VF610_ADC_CONV_DISABLE;
+ hc_cfg &= ~VF610_ADC_AIEN;
+
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &vf610_adc_buffer_postenable,
+ .predisable = &vf610_adc_buffer_predisable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static int vf610_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ if ((readval == NULL) ||
+ ((reg % 4) || (reg > VF610_REG_ADC_PCTL)))
+ return -EINVAL;
+
+ *readval = readl(info->regs + reg);
+
+ return 0;
+}
+
+static const struct iio_info vf610_adc_iio_info = {
+ .read_raw = &vf610_read_raw,
+ .write_raw = &vf610_write_raw,
+ .debugfs_reg_access = &vf610_adc_reg_access,
+ .attrs = &vf610_attribute_group,
+};
+
+static const struct of_device_id vf610_adc_match[] = {
+ { .compatible = "fsl,vf610-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vf610_adc_match);
+
+static int vf610_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vf610_adc *info;
+ struct iio_dev *indio_dev;
+ int irq;
+ 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;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = &pdev->dev;
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ 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;
+ }
+
+ 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->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(info->vref))
+ return PTR_ERR(info->vref);
+
+ ret = regulator_enable(info->vref);
+ 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);
+
+ info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME;
+ device_property_read_u32(dev, "min-sample-time", &info->adc_feature.default_sample_time);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ 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;
+ }
+
+ 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 = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto error_adc_buffer_init;
+ }
+
+ 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 int 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);
+
+ return 0;
+}
+
+static int vf610_adc_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct vf610_adc *info = iio_priv(indio_dev);
+ int hc_cfg;
+
+ /* ADC controller enters to stop mode */
+ hc_cfg = readl(info->regs + VF610_REG_ADC_HC0);
+ hc_cfg |= VF610_ADC_CONV_DISABLE;
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static int vf610_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct vf610_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ goto disable_reg;
+
+ vf610_adc_hw_init(info);
+
+ return 0;
+
+disable_reg:
+ regulator_disable(info->vref);
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend,
+ vf610_adc_resume);
+
+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,
+ .pm = pm_sleep_ptr(&vf610_adc_pm_ops),
+ },
+};
+
+module_platform_driver(vf610_adc_driver);
+
+MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
+MODULE_DESCRIPTION("Freescale VF610 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c
new file mode 100644
index 000000000..1028b101c
--- /dev/null
+++ b/drivers/iio/adc/viperboard_adc.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nano River Technologies viperboard IIO ADC driver
+ *
+ * (C) 2012 by Lemonage GmbH
+ * Author: Lars Poeschel <poeschel@lemonage.de>
+ * All rights reserved.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb.h>
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/viperboard.h>
+
+#define VPRBRD_ADC_CMD_GET 0x00
+
+struct vprbrd_adc_msg {
+ u8 cmd;
+ u8 chan;
+ u8 val;
+} __packed;
+
+struct vprbrd_adc {
+ struct vprbrd *vb;
+};
+
+#define VPRBRD_ADC_CHANNEL(_index) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+static struct iio_chan_spec const vprbrd_adc_iio_channels[] = {
+ VPRBRD_ADC_CHANNEL(0),
+ VPRBRD_ADC_CHANNEL(1),
+ VPRBRD_ADC_CHANNEL(2),
+ VPRBRD_ADC_CHANNEL(3),
+};
+
+static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long info)
+{
+ int ret, error = 0;
+ struct vprbrd_adc *adc = iio_priv(iio_dev);
+ struct vprbrd *vb = adc->vb;
+ struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&vb->lock);
+
+ admsg->cmd = VPRBRD_ADC_CMD_GET;
+ admsg->chan = chan->channel;
+ admsg->val = 0x00;
+
+ ret = usb_control_msg(vb->usb_dev,
+ usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
+ VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
+ sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
+ if (ret != sizeof(struct vprbrd_adc_msg)) {
+ dev_err(&iio_dev->dev, "usb send error on adc read\n");
+ error = -EREMOTEIO;
+ }
+
+ ret = usb_control_msg(vb->usb_dev,
+ usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
+ VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg,
+ sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
+
+ *val = admsg->val;
+
+ mutex_unlock(&vb->lock);
+
+ if (ret != sizeof(struct vprbrd_adc_msg)) {
+ dev_err(&iio_dev->dev, "usb recv error on adc read\n");
+ error = -EREMOTEIO;
+ }
+
+ if (error)
+ goto error;
+
+ return IIO_VAL_INT;
+ default:
+ error = -EINVAL;
+ break;
+ }
+error:
+ return error;
+}
+
+static const struct iio_info vprbrd_adc_iio_info = {
+ .read_raw = &vprbrd_iio_read_raw,
+};
+
+static int vprbrd_adc_probe(struct platform_device *pdev)
+{
+ struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
+ struct vprbrd_adc *adc;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ /* registering iio */
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ adc = iio_priv(indio_dev);
+ adc->vb = vb;
+ indio_dev->name = "viperboard adc";
+ indio_dev->info = &vprbrd_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = vprbrd_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels);
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register iio (adc)");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver vprbrd_adc_driver = {
+ .driver = {
+ .name = "viperboard-adc",
+ },
+ .probe = vprbrd_adc_probe,
+};
+
+module_platform_driver(vprbrd_adc_driver);
+
+MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
+MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:viperboard-adc");
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
new file mode 100644
index 000000000..a507d2e17
--- /dev/null
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -0,0 +1,1440 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx AMS driver
+ *
+ * Copyright (C) 2021 Xilinx, Inc.
+ *
+ * Manish Narani <mnarani@xilinx.com>
+ * Rajnikant Bhojani <rajnikant.bhojani@xilinx.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/devm-helpers.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+
+/* AMS registers definitions */
+#define AMS_ISR_0 0x010
+#define AMS_ISR_1 0x014
+#define AMS_IER_0 0x020
+#define AMS_IER_1 0x024
+#define AMS_IDR_0 0x028
+#define AMS_IDR_1 0x02C
+#define AMS_PS_CSTS 0x040
+#define AMS_PL_CSTS 0x044
+
+#define AMS_VCC_PSPLL0 0x060
+#define AMS_VCC_PSPLL3 0x06C
+#define AMS_VCCINT 0x078
+#define AMS_VCCBRAM 0x07C
+#define AMS_VCCAUX 0x080
+#define AMS_PSDDRPLL 0x084
+#define AMS_PSINTFPDDR 0x09C
+
+#define AMS_VCC_PSPLL0_CH 48
+#define AMS_VCC_PSPLL3_CH 51
+#define AMS_VCCINT_CH 54
+#define AMS_VCCBRAM_CH 55
+#define AMS_VCCAUX_CH 56
+#define AMS_PSDDRPLL_CH 57
+#define AMS_PSINTFPDDR_CH 63
+
+#define AMS_REG_CONFIG0 0x100
+#define AMS_REG_CONFIG1 0x104
+#define AMS_REG_CONFIG3 0x10C
+#define AMS_REG_CONFIG4 0x110
+#define AMS_REG_SEQ_CH0 0x120
+#define AMS_REG_SEQ_CH1 0x124
+#define AMS_REG_SEQ_CH2 0x118
+
+#define AMS_VUSER0_MASK BIT(0)
+#define AMS_VUSER1_MASK BIT(1)
+#define AMS_VUSER2_MASK BIT(2)
+#define AMS_VUSER3_MASK BIT(3)
+
+#define AMS_TEMP 0x000
+#define AMS_SUPPLY1 0x004
+#define AMS_SUPPLY2 0x008
+#define AMS_VP_VN 0x00C
+#define AMS_VREFP 0x010
+#define AMS_VREFN 0x014
+#define AMS_SUPPLY3 0x018
+#define AMS_SUPPLY4 0x034
+#define AMS_SUPPLY5 0x038
+#define AMS_SUPPLY6 0x03C
+#define AMS_SUPPLY7 0x200
+#define AMS_SUPPLY8 0x204
+#define AMS_SUPPLY9 0x208
+#define AMS_SUPPLY10 0x20C
+#define AMS_VCCAMS 0x210
+#define AMS_TEMP_REMOTE 0x214
+
+#define AMS_REG_VAUX(x) (0x40 + 4 * (x))
+
+#define AMS_PS_RESET_VALUE 0xFFFF
+#define AMS_PL_RESET_VALUE 0xFFFF
+
+#define AMS_CONF0_CHANNEL_NUM_MASK GENMASK(6, 0)
+
+#define AMS_CONF1_SEQ_MASK GENMASK(15, 12)
+#define AMS_CONF1_SEQ_DEFAULT FIELD_PREP(AMS_CONF1_SEQ_MASK, 0)
+#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 2)
+#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 3)
+
+#define AMS_REG_SEQ0_MASK GENMASK(15, 0)
+#define AMS_REG_SEQ2_MASK GENMASK(21, 16)
+#define AMS_REG_SEQ1_MASK GENMASK_ULL(37, 22)
+
+#define AMS_PS_SEQ_MASK GENMASK(21, 0)
+#define AMS_PL_SEQ_MASK GENMASK_ULL(59, 22)
+
+#define AMS_ALARM_TEMP 0x140
+#define AMS_ALARM_SUPPLY1 0x144
+#define AMS_ALARM_SUPPLY2 0x148
+#define AMS_ALARM_SUPPLY3 0x160
+#define AMS_ALARM_SUPPLY4 0x164
+#define AMS_ALARM_SUPPLY5 0x168
+#define AMS_ALARM_SUPPLY6 0x16C
+#define AMS_ALARM_SUPPLY7 0x180
+#define AMS_ALARM_SUPPLY8 0x184
+#define AMS_ALARM_SUPPLY9 0x188
+#define AMS_ALARM_SUPPLY10 0x18C
+#define AMS_ALARM_VCCAMS 0x190
+#define AMS_ALARM_TEMP_REMOTE 0x194
+#define AMS_ALARM_THRESHOLD_OFF_10 0x10
+#define AMS_ALARM_THRESHOLD_OFF_20 0x20
+
+#define AMS_ALARM_THR_DIRECT_MASK BIT(1)
+#define AMS_ALARM_THR_MIN 0x0000
+#define AMS_ALARM_THR_MAX (BIT(16) - 1)
+
+#define AMS_ALARM_MASK GENMASK_ULL(63, 0)
+#define AMS_NO_OF_ALARMS 32
+#define AMS_PL_ALARM_START 16
+#define AMS_PL_ALARM_MASK GENMASK(31, 16)
+#define AMS_ISR0_ALARM_MASK GENMASK(31, 0)
+#define AMS_ISR1_ALARM_MASK (GENMASK(31, 29) | GENMASK(4, 0))
+#define AMS_ISR1_EOC_MASK BIT(3)
+#define AMS_ISR1_INTR_MASK GENMASK_ULL(63, 32)
+#define AMS_ISR0_ALARM_2_TO_0_MASK GENMASK(2, 0)
+#define AMS_ISR0_ALARM_6_TO_3_MASK GENMASK(6, 3)
+#define AMS_ISR0_ALARM_12_TO_7_MASK GENMASK(13, 8)
+#define AMS_CONF1_ALARM_2_TO_0_MASK GENMASK(3, 1)
+#define AMS_CONF1_ALARM_6_TO_3_MASK GENMASK(11, 8)
+#define AMS_CONF1_ALARM_12_TO_7_MASK GENMASK(5, 0)
+#define AMS_REGCFG1_ALARM_MASK \
+ (AMS_CONF1_ALARM_2_TO_0_MASK | AMS_CONF1_ALARM_6_TO_3_MASK | BIT(0))
+#define AMS_REGCFG3_ALARM_MASK AMS_CONF1_ALARM_12_TO_7_MASK
+
+#define AMS_PS_CSTS_PS_READY (BIT(27) | BIT(16))
+#define AMS_PL_CSTS_ACCESS_MASK BIT(1)
+
+#define AMS_PL_MAX_FIXED_CHANNEL 10
+#define AMS_PL_MAX_EXT_CHANNEL 20
+
+#define AMS_INIT_POLL_TIME_US 200
+#define AMS_INIT_TIMEOUT_US 10000
+#define AMS_UNMASK_TIMEOUT_MS 500
+
+/*
+ * Following scale and offset value is derived from
+ * UG580 (v1.7) December 20, 2016
+ */
+#define AMS_SUPPLY_SCALE_1VOLT_mV 1000
+#define AMS_SUPPLY_SCALE_3VOLT_mV 3000
+#define AMS_SUPPLY_SCALE_6VOLT_mV 6000
+#define AMS_SUPPLY_SCALE_DIV_BIT 16
+
+#define AMS_TEMP_SCALE 509314
+#define AMS_TEMP_SCALE_DIV_BIT 16
+#define AMS_TEMP_OFFSET -((280230LL << 16) / 509314)
+
+enum ams_alarm_bit {
+ AMS_ALARM_BIT_TEMP = 0,
+ AMS_ALARM_BIT_SUPPLY1 = 1,
+ AMS_ALARM_BIT_SUPPLY2 = 2,
+ AMS_ALARM_BIT_SUPPLY3 = 3,
+ AMS_ALARM_BIT_SUPPLY4 = 4,
+ AMS_ALARM_BIT_SUPPLY5 = 5,
+ AMS_ALARM_BIT_SUPPLY6 = 6,
+ AMS_ALARM_BIT_RESERVED = 7,
+ AMS_ALARM_BIT_SUPPLY7 = 8,
+ AMS_ALARM_BIT_SUPPLY8 = 9,
+ AMS_ALARM_BIT_SUPPLY9 = 10,
+ AMS_ALARM_BIT_SUPPLY10 = 11,
+ AMS_ALARM_BIT_VCCAMS = 12,
+ AMS_ALARM_BIT_TEMP_REMOTE = 13,
+};
+
+enum ams_seq {
+ AMS_SEQ_VCC_PSPLL = 0,
+ AMS_SEQ_VCC_PSBATT = 1,
+ AMS_SEQ_VCCINT = 2,
+ AMS_SEQ_VCCBRAM = 3,
+ AMS_SEQ_VCCAUX = 4,
+ AMS_SEQ_PSDDRPLL = 5,
+ AMS_SEQ_INTDDR = 6,
+};
+
+enum ams_ps_pl_seq {
+ AMS_SEQ_CALIB = 0,
+ AMS_SEQ_RSVD_1 = 1,
+ AMS_SEQ_RSVD_2 = 2,
+ AMS_SEQ_TEST = 3,
+ AMS_SEQ_RSVD_4 = 4,
+ AMS_SEQ_SUPPLY4 = 5,
+ AMS_SEQ_SUPPLY5 = 6,
+ AMS_SEQ_SUPPLY6 = 7,
+ AMS_SEQ_TEMP = 8,
+ AMS_SEQ_SUPPLY2 = 9,
+ AMS_SEQ_SUPPLY1 = 10,
+ AMS_SEQ_VP_VN = 11,
+ AMS_SEQ_VREFP = 12,
+ AMS_SEQ_VREFN = 13,
+ AMS_SEQ_SUPPLY3 = 14,
+ AMS_SEQ_CURRENT_MON = 15,
+ AMS_SEQ_SUPPLY7 = 16,
+ AMS_SEQ_SUPPLY8 = 17,
+ AMS_SEQ_SUPPLY9 = 18,
+ AMS_SEQ_SUPPLY10 = 19,
+ AMS_SEQ_VCCAMS = 20,
+ AMS_SEQ_TEMP_REMOTE = 21,
+ AMS_SEQ_MAX = 22
+};
+
+#define AMS_PS_SEQ_MAX AMS_SEQ_MAX
+#define AMS_SEQ(x) (AMS_SEQ_MAX + (x))
+#define PS_SEQ(x) (x)
+#define PL_SEQ(x) (AMS_PS_SEQ_MAX + (x))
+#define AMS_CTRL_SEQ_BASE (AMS_PS_SEQ_MAX * 3)
+
+#define AMS_CHAN_TEMP(_scan_index, _addr) { \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .address = (_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .event_spec = ams_temp_events, \
+ .scan_index = _scan_index, \
+ .num_event_specs = ARRAY_SIZE(ams_temp_events), \
+}
+
+#define AMS_CHAN_VOLTAGE(_scan_index, _addr, _alarm) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .address = (_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .event_spec = (_alarm) ? ams_voltage_events : NULL, \
+ .scan_index = _scan_index, \
+ .num_event_specs = (_alarm) ? ARRAY_SIZE(ams_voltage_events) : 0, \
+}
+
+#define AMS_PS_CHAN_TEMP(_scan_index, _addr) \
+ AMS_CHAN_TEMP(PS_SEQ(_scan_index), _addr)
+#define AMS_PS_CHAN_VOLTAGE(_scan_index, _addr) \
+ AMS_CHAN_VOLTAGE(PS_SEQ(_scan_index), _addr, true)
+
+#define AMS_PL_CHAN_TEMP(_scan_index, _addr) \
+ AMS_CHAN_TEMP(PL_SEQ(_scan_index), _addr)
+#define AMS_PL_CHAN_VOLTAGE(_scan_index, _addr, _alarm) \
+ AMS_CHAN_VOLTAGE(PL_SEQ(_scan_index), _addr, _alarm)
+#define AMS_PL_AUX_CHAN_VOLTAGE(_auxno) \
+ AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(_auxno)), AMS_REG_VAUX(_auxno), false)
+#define AMS_CTRL_CHAN_VOLTAGE(_scan_index, _addr) \
+ AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(AMS_SEQ(_scan_index))), _addr, false)
+
+/**
+ * struct ams - This structure contains necessary state for xilinx-ams to operate
+ * @base: physical base address of device
+ * @ps_base: physical base address of PS device
+ * @pl_base: physical base address of PL device
+ * @clk: clocks associated with the device
+ * @dev: pointer to device struct
+ * @lock: to handle multiple user interaction
+ * @intr_lock: to protect interrupt mask values
+ * @alarm_mask: alarm configuration
+ * @current_masked_alarm: currently masked due to alarm
+ * @intr_mask: interrupt configuration
+ * @ams_unmask_work: re-enables event once the event condition disappears
+ *
+ */
+struct ams {
+ void __iomem *base;
+ void __iomem *ps_base;
+ void __iomem *pl_base;
+ struct clk *clk;
+ struct device *dev;
+ struct mutex lock;
+ spinlock_t intr_lock;
+ unsigned int alarm_mask;
+ unsigned int current_masked_alarm;
+ u64 intr_mask;
+ struct delayed_work ams_unmask_work;
+};
+
+static inline void ams_ps_update_reg(struct ams *ams, unsigned int offset,
+ u32 mask, u32 data)
+{
+ u32 val, regval;
+
+ val = readl(ams->ps_base + offset);
+ regval = (val & ~mask) | (data & mask);
+ writel(regval, ams->ps_base + offset);
+}
+
+static inline void ams_pl_update_reg(struct ams *ams, unsigned int offset,
+ u32 mask, u32 data)
+{
+ u32 val, regval;
+
+ val = readl(ams->pl_base + offset);
+ regval = (val & ~mask) | (data & mask);
+ writel(regval, ams->pl_base + offset);
+}
+
+static void ams_update_intrmask(struct ams *ams, u64 mask, u64 val)
+{
+ u32 regval;
+
+ ams->intr_mask = (ams->intr_mask & ~mask) | (val & mask);
+
+ regval = ~(ams->intr_mask | ams->current_masked_alarm);
+ writel(regval, ams->base + AMS_IER_0);
+
+ regval = ~(FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask));
+ writel(regval, ams->base + AMS_IER_1);
+
+ regval = ams->intr_mask | ams->current_masked_alarm;
+ writel(regval, ams->base + AMS_IDR_0);
+
+ regval = FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask);
+ writel(regval, ams->base + AMS_IDR_1);
+}
+
+static void ams_disable_all_alarms(struct ams *ams)
+{
+ /* disable PS module alarm */
+ if (ams->ps_base) {
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK,
+ AMS_REGCFG1_ALARM_MASK);
+ ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK,
+ AMS_REGCFG3_ALARM_MASK);
+ }
+
+ /* disable PL module alarm */
+ if (ams->pl_base) {
+ ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK,
+ AMS_REGCFG1_ALARM_MASK);
+ ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK,
+ AMS_REGCFG3_ALARM_MASK);
+ }
+}
+
+static void ams_update_ps_alarm(struct ams *ams, unsigned long alarm_mask)
+{
+ u32 cfg;
+ u32 val;
+
+ val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, alarm_mask);
+ cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val));
+
+ val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, alarm_mask);
+ cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val));
+
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg);
+
+ val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, alarm_mask);
+ cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val));
+ ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg);
+}
+
+static void ams_update_pl_alarm(struct ams *ams, unsigned long alarm_mask)
+{
+ unsigned long pl_alarm_mask;
+ u32 cfg;
+ u32 val;
+
+ pl_alarm_mask = FIELD_GET(AMS_PL_ALARM_MASK, alarm_mask);
+
+ val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, pl_alarm_mask);
+ cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val));
+
+ val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, pl_alarm_mask);
+ cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val));
+
+ ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg);
+
+ val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, pl_alarm_mask);
+ cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val));
+ ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg);
+}
+
+static void ams_update_alarm(struct ams *ams, unsigned long alarm_mask)
+{
+ unsigned long flags;
+
+ if (ams->ps_base)
+ ams_update_ps_alarm(ams, alarm_mask);
+
+ if (ams->pl_base)
+ ams_update_pl_alarm(ams, alarm_mask);
+
+ spin_lock_irqsave(&ams->intr_lock, flags);
+ ams_update_intrmask(ams, AMS_ISR0_ALARM_MASK, ~alarm_mask);
+ spin_unlock_irqrestore(&ams->intr_lock, flags);
+}
+
+static void ams_enable_channel_sequence(struct iio_dev *indio_dev)
+{
+ struct ams *ams = iio_priv(indio_dev);
+ unsigned long long scan_mask;
+ int i;
+ u32 regval;
+
+ /*
+ * Enable channel sequence. First 22 bits of scan_mask represent
+ * PS channels, and next remaining bits represent PL channels.
+ */
+
+ /* Run calibration of PS & PL as part of the sequence */
+ scan_mask = BIT(0) | BIT(AMS_PS_SEQ_MAX);
+ for (i = 0; i < indio_dev->num_channels; i++)
+ scan_mask |= BIT_ULL(indio_dev->channels[i].scan_index);
+
+ if (ams->ps_base) {
+ /* put sysmon in a soft reset to change the sequence */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_DEFAULT);
+
+ /* configure basic channels */
+ regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask);
+ writel(regval, ams->ps_base + AMS_REG_SEQ_CH0);
+
+ regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask);
+ writel(regval, ams->ps_base + AMS_REG_SEQ_CH2);
+
+ /* set continuous sequence mode */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_CONTINUOUS);
+ }
+
+ if (ams->pl_base) {
+ /* put sysmon in a soft reset to change the sequence */
+ ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_DEFAULT);
+
+ /* configure basic channels */
+ scan_mask = FIELD_GET(AMS_PL_SEQ_MASK, scan_mask);
+
+ regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask);
+ writel(regval, ams->pl_base + AMS_REG_SEQ_CH0);
+
+ regval = FIELD_GET(AMS_REG_SEQ1_MASK, scan_mask);
+ writel(regval, ams->pl_base + AMS_REG_SEQ_CH1);
+
+ regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask);
+ writel(regval, ams->pl_base + AMS_REG_SEQ_CH2);
+
+ /* set continuous sequence mode */
+ ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_CONTINUOUS);
+ }
+}
+
+static int ams_init_device(struct ams *ams)
+{
+ u32 expect = AMS_PS_CSTS_PS_READY;
+ u32 reg, value;
+ int ret;
+
+ /* reset AMS */
+ if (ams->ps_base) {
+ writel(AMS_PS_RESET_VALUE, ams->ps_base + AMS_VP_VN);
+
+ ret = readl_poll_timeout(ams->base + AMS_PS_CSTS, reg, (reg & expect),
+ AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /* put sysmon in a default state */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_DEFAULT);
+ }
+
+ if (ams->pl_base) {
+ value = readl(ams->base + AMS_PL_CSTS);
+ if (value == 0)
+ return 0;
+
+ writel(AMS_PL_RESET_VALUE, ams->pl_base + AMS_VP_VN);
+
+ /* put sysmon in a default state */
+ ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_DEFAULT);
+ }
+
+ ams_disable_all_alarms(ams);
+
+ /* Disable interrupt */
+ ams_update_intrmask(ams, AMS_ALARM_MASK, AMS_ALARM_MASK);
+
+ /* Clear any pending interrupt */
+ writel(AMS_ISR0_ALARM_MASK, ams->base + AMS_ISR_0);
+ writel(AMS_ISR1_ALARM_MASK, ams->base + AMS_ISR_1);
+
+ return 0;
+}
+
+static int ams_enable_single_channel(struct ams *ams, unsigned int offset)
+{
+ u8 channel_num;
+
+ switch (offset) {
+ case AMS_VCC_PSPLL0:
+ channel_num = AMS_VCC_PSPLL0_CH;
+ break;
+ case AMS_VCC_PSPLL3:
+ channel_num = AMS_VCC_PSPLL3_CH;
+ break;
+ case AMS_VCCINT:
+ channel_num = AMS_VCCINT_CH;
+ break;
+ case AMS_VCCBRAM:
+ channel_num = AMS_VCCBRAM_CH;
+ break;
+ case AMS_VCCAUX:
+ channel_num = AMS_VCCAUX_CH;
+ break;
+ case AMS_PSDDRPLL:
+ channel_num = AMS_PSDDRPLL_CH;
+ break;
+ case AMS_PSINTFPDDR:
+ channel_num = AMS_PSINTFPDDR_CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* put sysmon in a soft reset to change the sequence */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_DEFAULT);
+
+ /* write the channel number */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG0, AMS_CONF0_CHANNEL_NUM_MASK,
+ channel_num);
+
+ /* set single channel, sequencer off mode */
+ ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK,
+ AMS_CONF1_SEQ_SINGLE_CHANNEL);
+
+ return 0;
+}
+
+static int ams_read_vcc_reg(struct ams *ams, unsigned int offset, u32 *data)
+{
+ u32 expect = AMS_ISR1_EOC_MASK;
+ u32 reg;
+ int ret;
+
+ ret = ams_enable_single_channel(ams, offset);
+ if (ret)
+ return ret;
+
+ /* clear end-of-conversion flag, wait for next conversion to complete */
+ writel(expect, ams->base + AMS_ISR_1);
+ ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg, (reg & expect),
+ AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ *data = readl(ams->base + offset);
+
+ return 0;
+}
+
+static int ams_get_ps_scale(int address)
+{
+ int val;
+
+ switch (address) {
+ case AMS_SUPPLY1:
+ case AMS_SUPPLY2:
+ case AMS_SUPPLY3:
+ case AMS_SUPPLY4:
+ case AMS_SUPPLY9:
+ case AMS_SUPPLY10:
+ case AMS_VCCAMS:
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ case AMS_SUPPLY5:
+ case AMS_SUPPLY6:
+ case AMS_SUPPLY7:
+ case AMS_SUPPLY8:
+ val = AMS_SUPPLY_SCALE_6VOLT_mV;
+ break;
+ default:
+ val = AMS_SUPPLY_SCALE_1VOLT_mV;
+ break;
+ }
+
+ return val;
+}
+
+static int ams_get_pl_scale(struct ams *ams, int address)
+{
+ int val, regval;
+
+ switch (address) {
+ case AMS_SUPPLY1:
+ case AMS_SUPPLY2:
+ case AMS_SUPPLY3:
+ case AMS_SUPPLY4:
+ case AMS_SUPPLY5:
+ case AMS_SUPPLY6:
+ case AMS_VCCAMS:
+ case AMS_VREFP:
+ case AMS_VREFN:
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ case AMS_SUPPLY7:
+ regval = readl(ams->pl_base + AMS_REG_CONFIG4);
+ if (FIELD_GET(AMS_VUSER0_MASK, regval))
+ val = AMS_SUPPLY_SCALE_6VOLT_mV;
+ else
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ case AMS_SUPPLY8:
+ regval = readl(ams->pl_base + AMS_REG_CONFIG4);
+ if (FIELD_GET(AMS_VUSER1_MASK, regval))
+ val = AMS_SUPPLY_SCALE_6VOLT_mV;
+ else
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ case AMS_SUPPLY9:
+ regval = readl(ams->pl_base + AMS_REG_CONFIG4);
+ if (FIELD_GET(AMS_VUSER2_MASK, regval))
+ val = AMS_SUPPLY_SCALE_6VOLT_mV;
+ else
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ case AMS_SUPPLY10:
+ regval = readl(ams->pl_base + AMS_REG_CONFIG4);
+ if (FIELD_GET(AMS_VUSER3_MASK, regval))
+ val = AMS_SUPPLY_SCALE_6VOLT_mV;
+ else
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ case AMS_VP_VN:
+ case AMS_REG_VAUX(0) ... AMS_REG_VAUX(15):
+ val = AMS_SUPPLY_SCALE_1VOLT_mV;
+ break;
+ default:
+ val = AMS_SUPPLY_SCALE_1VOLT_mV;
+ break;
+ }
+
+ return val;
+}
+
+static int ams_get_ctrl_scale(int address)
+{
+ int val;
+
+ switch (address) {
+ case AMS_VCC_PSPLL0:
+ case AMS_VCC_PSPLL3:
+ case AMS_VCCINT:
+ case AMS_VCCBRAM:
+ case AMS_VCCAUX:
+ case AMS_PSDDRPLL:
+ case AMS_PSINTFPDDR:
+ val = AMS_SUPPLY_SCALE_3VOLT_mV;
+ break;
+ default:
+ val = AMS_SUPPLY_SCALE_1VOLT_mV;
+ break;
+ }
+
+ return val;
+}
+
+static int ams_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ams *ams = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&ams->lock);
+ if (chan->scan_index >= AMS_CTRL_SEQ_BASE) {
+ ret = ams_read_vcc_reg(ams, chan->address, val);
+ if (ret)
+ goto unlock_mutex;
+ ams_enable_channel_sequence(indio_dev);
+ } else if (chan->scan_index >= AMS_PS_SEQ_MAX)
+ *val = readl(ams->pl_base + chan->address);
+ else
+ *val = readl(ams->ps_base + chan->address);
+
+ ret = IIO_VAL_INT;
+unlock_mutex:
+ mutex_unlock(&ams->lock);
+ return ret;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->scan_index < AMS_PS_SEQ_MAX)
+ *val = ams_get_ps_scale(chan->address);
+ else if (chan->scan_index >= AMS_PS_SEQ_MAX &&
+ chan->scan_index < AMS_CTRL_SEQ_BASE)
+ *val = ams_get_pl_scale(ams, chan->address);
+ else
+ *val = ams_get_ctrl_scale(chan->address);
+
+ *val2 = AMS_SUPPLY_SCALE_DIV_BIT;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ *val = AMS_TEMP_SCALE;
+ *val2 = AMS_TEMP_SCALE_DIV_BIT;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ /* Only the temperature channel has an offset */
+ *val = AMS_TEMP_OFFSET;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ams_get_alarm_offset(int scan_index, enum iio_event_direction dir)
+{
+ int offset;
+
+ if (scan_index >= AMS_PS_SEQ_MAX)
+ scan_index -= AMS_PS_SEQ_MAX;
+
+ if (dir == IIO_EV_DIR_FALLING) {
+ if (scan_index < AMS_SEQ_SUPPLY7)
+ offset = AMS_ALARM_THRESHOLD_OFF_10;
+ else
+ offset = AMS_ALARM_THRESHOLD_OFF_20;
+ } else {
+ offset = 0;
+ }
+
+ switch (scan_index) {
+ case AMS_SEQ_TEMP:
+ return AMS_ALARM_TEMP + offset;
+ case AMS_SEQ_SUPPLY1:
+ return AMS_ALARM_SUPPLY1 + offset;
+ case AMS_SEQ_SUPPLY2:
+ return AMS_ALARM_SUPPLY2 + offset;
+ case AMS_SEQ_SUPPLY3:
+ return AMS_ALARM_SUPPLY3 + offset;
+ case AMS_SEQ_SUPPLY4:
+ return AMS_ALARM_SUPPLY4 + offset;
+ case AMS_SEQ_SUPPLY5:
+ return AMS_ALARM_SUPPLY5 + offset;
+ case AMS_SEQ_SUPPLY6:
+ return AMS_ALARM_SUPPLY6 + offset;
+ case AMS_SEQ_SUPPLY7:
+ return AMS_ALARM_SUPPLY7 + offset;
+ case AMS_SEQ_SUPPLY8:
+ return AMS_ALARM_SUPPLY8 + offset;
+ case AMS_SEQ_SUPPLY9:
+ return AMS_ALARM_SUPPLY9 + offset;
+ case AMS_SEQ_SUPPLY10:
+ return AMS_ALARM_SUPPLY10 + offset;
+ case AMS_SEQ_VCCAMS:
+ return AMS_ALARM_VCCAMS + offset;
+ case AMS_SEQ_TEMP_REMOTE:
+ return AMS_ALARM_TEMP_REMOTE + offset;
+ default:
+ return 0;
+ }
+}
+
+static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev,
+ u32 event)
+{
+ int scan_index = 0, i;
+
+ if (event >= AMS_PL_ALARM_START) {
+ event -= AMS_PL_ALARM_START;
+ scan_index = AMS_PS_SEQ_MAX;
+ }
+
+ switch (event) {
+ case AMS_ALARM_BIT_TEMP:
+ scan_index += AMS_SEQ_TEMP;
+ break;
+ case AMS_ALARM_BIT_SUPPLY1:
+ scan_index += AMS_SEQ_SUPPLY1;
+ break;
+ case AMS_ALARM_BIT_SUPPLY2:
+ scan_index += AMS_SEQ_SUPPLY2;
+ break;
+ case AMS_ALARM_BIT_SUPPLY3:
+ scan_index += AMS_SEQ_SUPPLY3;
+ break;
+ case AMS_ALARM_BIT_SUPPLY4:
+ scan_index += AMS_SEQ_SUPPLY4;
+ break;
+ case AMS_ALARM_BIT_SUPPLY5:
+ scan_index += AMS_SEQ_SUPPLY5;
+ break;
+ case AMS_ALARM_BIT_SUPPLY6:
+ scan_index += AMS_SEQ_SUPPLY6;
+ break;
+ case AMS_ALARM_BIT_SUPPLY7:
+ scan_index += AMS_SEQ_SUPPLY7;
+ break;
+ case AMS_ALARM_BIT_SUPPLY8:
+ scan_index += AMS_SEQ_SUPPLY8;
+ break;
+ case AMS_ALARM_BIT_SUPPLY9:
+ scan_index += AMS_SEQ_SUPPLY9;
+ break;
+ case AMS_ALARM_BIT_SUPPLY10:
+ scan_index += AMS_SEQ_SUPPLY10;
+ break;
+ case AMS_ALARM_BIT_VCCAMS:
+ scan_index += AMS_SEQ_VCCAMS;
+ break;
+ case AMS_ALARM_BIT_TEMP_REMOTE:
+ scan_index += AMS_SEQ_TEMP_REMOTE;
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < dev->num_channels; i++)
+ if (dev->channels[i].scan_index == scan_index)
+ break;
+
+ return &dev->channels[i];
+}
+
+static int ams_get_alarm_mask(int scan_index)
+{
+ int bit = 0;
+
+ if (scan_index >= AMS_PS_SEQ_MAX) {
+ bit = AMS_PL_ALARM_START;
+ scan_index -= AMS_PS_SEQ_MAX;
+ }
+
+ switch (scan_index) {
+ case AMS_SEQ_TEMP:
+ return BIT(AMS_ALARM_BIT_TEMP + bit);
+ case AMS_SEQ_SUPPLY1:
+ return BIT(AMS_ALARM_BIT_SUPPLY1 + bit);
+ case AMS_SEQ_SUPPLY2:
+ return BIT(AMS_ALARM_BIT_SUPPLY2 + bit);
+ case AMS_SEQ_SUPPLY3:
+ return BIT(AMS_ALARM_BIT_SUPPLY3 + bit);
+ case AMS_SEQ_SUPPLY4:
+ return BIT(AMS_ALARM_BIT_SUPPLY4 + bit);
+ case AMS_SEQ_SUPPLY5:
+ return BIT(AMS_ALARM_BIT_SUPPLY5 + bit);
+ case AMS_SEQ_SUPPLY6:
+ return BIT(AMS_ALARM_BIT_SUPPLY6 + bit);
+ case AMS_SEQ_SUPPLY7:
+ return BIT(AMS_ALARM_BIT_SUPPLY7 + bit);
+ case AMS_SEQ_SUPPLY8:
+ return BIT(AMS_ALARM_BIT_SUPPLY8 + bit);
+ case AMS_SEQ_SUPPLY9:
+ return BIT(AMS_ALARM_BIT_SUPPLY9 + bit);
+ case AMS_SEQ_SUPPLY10:
+ return BIT(AMS_ALARM_BIT_SUPPLY10 + bit);
+ case AMS_SEQ_VCCAMS:
+ return BIT(AMS_ALARM_BIT_VCCAMS + bit);
+ case AMS_SEQ_TEMP_REMOTE:
+ return BIT(AMS_ALARM_BIT_TEMP_REMOTE + bit);
+ default:
+ return 0;
+ }
+}
+
+static int ams_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 ams *ams = iio_priv(indio_dev);
+
+ return !!(ams->alarm_mask & ams_get_alarm_mask(chan->scan_index));
+}
+
+static int ams_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct ams *ams = iio_priv(indio_dev);
+ unsigned int alarm;
+
+ alarm = ams_get_alarm_mask(chan->scan_index);
+
+ mutex_lock(&ams->lock);
+
+ if (state)
+ ams->alarm_mask |= alarm;
+ else
+ ams->alarm_mask &= ~alarm;
+
+ ams_update_alarm(ams, ams->alarm_mask);
+
+ mutex_unlock(&ams->lock);
+
+ return 0;
+}
+
+static int ams_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 ams *ams = iio_priv(indio_dev);
+ unsigned int offset = ams_get_alarm_offset(chan->scan_index, dir);
+
+ mutex_lock(&ams->lock);
+
+ if (chan->scan_index >= AMS_PS_SEQ_MAX)
+ *val = readl(ams->pl_base + offset);
+ else
+ *val = readl(ams->ps_base + offset);
+
+ mutex_unlock(&ams->lock);
+
+ return IIO_VAL_INT;
+}
+
+static int ams_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 ams *ams = iio_priv(indio_dev);
+ unsigned int offset;
+
+ mutex_lock(&ams->lock);
+
+ /* Set temperature channel threshold to direct threshold */
+ if (chan->type == IIO_TEMP) {
+ offset = ams_get_alarm_offset(chan->scan_index, IIO_EV_DIR_FALLING);
+
+ if (chan->scan_index >= AMS_PS_SEQ_MAX)
+ ams_pl_update_reg(ams, offset,
+ AMS_ALARM_THR_DIRECT_MASK,
+ AMS_ALARM_THR_DIRECT_MASK);
+ else
+ ams_ps_update_reg(ams, offset,
+ AMS_ALARM_THR_DIRECT_MASK,
+ AMS_ALARM_THR_DIRECT_MASK);
+ }
+
+ offset = ams_get_alarm_offset(chan->scan_index, dir);
+ if (chan->scan_index >= AMS_PS_SEQ_MAX)
+ writel(val, ams->pl_base + offset);
+ else
+ writel(val, ams->ps_base + offset);
+
+ mutex_unlock(&ams->lock);
+
+ return 0;
+}
+
+static void ams_handle_event(struct iio_dev *indio_dev, u32 event)
+{
+ const struct iio_chan_spec *chan;
+
+ chan = ams_event_to_channel(indio_dev, event);
+
+ if (chan->type == IIO_TEMP) {
+ /*
+ * The temperature channel only supports over-temperature
+ * events.
+ */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ } else {
+ /*
+ * For other channels we don't know whether it is a upper or
+ * lower threshold event. Userspace will have to check the
+ * channel value if it wants to know.
+ */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ }
+}
+
+static void ams_handle_events(struct iio_dev *indio_dev, unsigned long events)
+{
+ unsigned int bit;
+
+ for_each_set_bit(bit, &events, AMS_NO_OF_ALARMS)
+ ams_handle_event(indio_dev, bit);
+}
+
+/**
+ * ams_unmask_worker - ams alarm interrupt unmask worker
+ * @work: work to be done
+ *
+ * The ZynqMP threshold interrupts are level sensitive. Since we can't make the
+ * threshold condition go way from within the interrupt handler, this means as
+ * soon as a threshold condition is present we would enter the interrupt handler
+ * again and again. To work around this we mask all active threshold interrupts
+ * in the interrupt handler and start a timer. In this timer we poll the
+ * interrupt status and only if the interrupt is inactive we unmask it again.
+ */
+static void ams_unmask_worker(struct work_struct *work)
+{
+ struct ams *ams = container_of(work, struct ams, ams_unmask_work.work);
+ unsigned int status, unmask;
+
+ spin_lock_irq(&ams->intr_lock);
+
+ status = readl(ams->base + AMS_ISR_0);
+
+ /* Clear those bits which are not active anymore */
+ unmask = (ams->current_masked_alarm ^ status) & ams->current_masked_alarm;
+
+ /* Clear status of disabled alarm */
+ unmask |= ams->intr_mask;
+
+ ams->current_masked_alarm &= status;
+
+ /* Also clear those which are masked out anyway */
+ ams->current_masked_alarm &= ~ams->intr_mask;
+
+ /* Clear the interrupts before we unmask them */
+ writel(unmask, ams->base + AMS_ISR_0);
+
+ ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK);
+
+ spin_unlock_irq(&ams->intr_lock);
+
+ /* If still pending some alarm re-trigger the timer */
+ if (ams->current_masked_alarm)
+ schedule_delayed_work(&ams->ams_unmask_work,
+ msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS));
+}
+
+static irqreturn_t ams_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct ams *ams = iio_priv(indio_dev);
+ u32 isr0;
+
+ spin_lock(&ams->intr_lock);
+
+ isr0 = readl(ams->base + AMS_ISR_0);
+
+ /* Only process alarms that are not masked */
+ isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) | ams->current_masked_alarm);
+ if (!isr0) {
+ spin_unlock(&ams->intr_lock);
+ return IRQ_NONE;
+ }
+
+ /* Clear interrupt */
+ writel(isr0, ams->base + AMS_ISR_0);
+
+ /* Mask the alarm interrupts until cleared */
+ ams->current_masked_alarm |= isr0;
+ ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK);
+
+ ams_handle_events(indio_dev, isr0);
+
+ schedule_delayed_work(&ams->ams_unmask_work,
+ msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS));
+
+ spin_unlock(&ams->intr_lock);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_event_spec ams_temp_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+static const struct iio_event_spec ams_voltage_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_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec ams_ps_channels[] = {
+ AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP),
+ AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP_REMOTE, AMS_TEMP_REMOTE),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10),
+ AMS_PS_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS),
+};
+
+static const struct iio_chan_spec ams_pl_channels[] = {
+ AMS_PL_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFP, AMS_VREFP, false),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFN, AMS_VREFN, false),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VP_VN, AMS_VP_VN, false),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9, true),
+ AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10, true),
+ AMS_PL_AUX_CHAN_VOLTAGE(0),
+ AMS_PL_AUX_CHAN_VOLTAGE(1),
+ AMS_PL_AUX_CHAN_VOLTAGE(2),
+ AMS_PL_AUX_CHAN_VOLTAGE(3),
+ AMS_PL_AUX_CHAN_VOLTAGE(4),
+ AMS_PL_AUX_CHAN_VOLTAGE(5),
+ AMS_PL_AUX_CHAN_VOLTAGE(6),
+ AMS_PL_AUX_CHAN_VOLTAGE(7),
+ AMS_PL_AUX_CHAN_VOLTAGE(8),
+ AMS_PL_AUX_CHAN_VOLTAGE(9),
+ AMS_PL_AUX_CHAN_VOLTAGE(10),
+ AMS_PL_AUX_CHAN_VOLTAGE(11),
+ AMS_PL_AUX_CHAN_VOLTAGE(12),
+ AMS_PL_AUX_CHAN_VOLTAGE(13),
+ AMS_PL_AUX_CHAN_VOLTAGE(14),
+ AMS_PL_AUX_CHAN_VOLTAGE(15),
+};
+
+static const struct iio_chan_spec ams_ctrl_channels[] = {
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSPLL, AMS_VCC_PSPLL0),
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSBATT, AMS_VCC_PSPLL3),
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCINT, AMS_VCCINT),
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCBRAM, AMS_VCCBRAM),
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCAUX, AMS_VCCAUX),
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_PSDDRPLL, AMS_PSDDRPLL),
+ AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_INTDDR, AMS_PSINTFPDDR),
+};
+
+static int ams_get_ext_chan(struct fwnode_handle *chan_node,
+ struct iio_chan_spec *channels, int num_channels)
+{
+ struct iio_chan_spec *chan;
+ struct fwnode_handle *child;
+ unsigned int reg, ext_chan;
+ int ret;
+
+ fwnode_for_each_child_node(chan_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret || reg > AMS_PL_MAX_EXT_CHANNEL + 30)
+ continue;
+
+ chan = &channels[num_channels];
+ ext_chan = reg + AMS_PL_MAX_FIXED_CHANNEL - 30;
+ memcpy(chan, &ams_pl_channels[ext_chan], sizeof(*channels));
+
+ if (fwnode_property_read_bool(child, "xlnx,bipolar"))
+ chan->scan_type.sign = 's';
+
+ num_channels++;
+ }
+
+ return num_channels;
+}
+
+static void ams_iounmap_ps(void *data)
+{
+ struct ams *ams = data;
+
+ iounmap(ams->ps_base);
+}
+
+static void ams_iounmap_pl(void *data)
+{
+ struct ams *ams = data;
+
+ iounmap(ams->pl_base);
+}
+
+static int ams_init_module(struct iio_dev *indio_dev,
+ struct fwnode_handle *fwnode,
+ struct iio_chan_spec *channels)
+{
+ struct device *dev = indio_dev->dev.parent;
+ struct ams *ams = iio_priv(indio_dev);
+ int num_channels = 0;
+ int ret;
+
+ if (fwnode_property_match_string(fwnode, "compatible",
+ "xlnx,zynqmp-ams-ps") == 0) {
+ ams->ps_base = fwnode_iomap(fwnode, 0);
+ if (!ams->ps_base)
+ return -ENXIO;
+ ret = devm_add_action_or_reset(dev, ams_iounmap_ps, ams);
+ if (ret < 0)
+ return ret;
+
+ /* add PS channels to iio device channels */
+ memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels));
+ num_channels = ARRAY_SIZE(ams_ps_channels);
+ } else if (fwnode_property_match_string(fwnode, "compatible",
+ "xlnx,zynqmp-ams-pl") == 0) {
+ ams->pl_base = fwnode_iomap(fwnode, 0);
+ if (!ams->pl_base)
+ return -ENXIO;
+
+ ret = devm_add_action_or_reset(dev, ams_iounmap_pl, ams);
+ if (ret < 0)
+ return ret;
+
+ /* Copy only first 10 fix channels */
+ memcpy(channels, ams_pl_channels, AMS_PL_MAX_FIXED_CHANNEL * sizeof(*channels));
+ num_channels += AMS_PL_MAX_FIXED_CHANNEL;
+ num_channels = ams_get_ext_chan(fwnode, channels,
+ num_channels);
+ } else if (fwnode_property_match_string(fwnode, "compatible",
+ "xlnx,zynqmp-ams") == 0) {
+ /* add AMS channels to iio device channels */
+ memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels));
+ num_channels += ARRAY_SIZE(ams_ctrl_channels);
+ } else {
+ return -EINVAL;
+ }
+
+ return num_channels;
+}
+
+static int ams_parse_firmware(struct iio_dev *indio_dev)
+{
+ struct ams *ams = iio_priv(indio_dev);
+ struct iio_chan_spec *ams_channels, *dev_channels;
+ struct device *dev = indio_dev->dev.parent;
+ struct fwnode_handle *child = NULL;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ size_t ams_size, dev_size;
+ int ret, ch_cnt = 0, i, rising_off, falling_off;
+ unsigned int num_channels = 0;
+
+ ams_size = ARRAY_SIZE(ams_ps_channels) + ARRAY_SIZE(ams_pl_channels) +
+ ARRAY_SIZE(ams_ctrl_channels);
+
+ /* Initialize buffer for channel specification */
+ ams_channels = devm_kcalloc(dev, ams_size, sizeof(*ams_channels), GFP_KERNEL);
+ if (!ams_channels)
+ return -ENOMEM;
+
+ if (fwnode_device_is_available(fwnode)) {
+ ret = ams_init_module(indio_dev, fwnode, ams_channels);
+ if (ret < 0)
+ return ret;
+
+ num_channels += ret;
+ }
+
+ fwnode_for_each_child_node(fwnode, child) {
+ if (fwnode_device_is_available(child)) {
+ ret = ams_init_module(indio_dev, child, ams_channels + num_channels);
+ if (ret < 0) {
+ fwnode_handle_put(child);
+ return ret;
+ }
+
+ num_channels += ret;
+ }
+ }
+
+ for (i = 0; i < num_channels; i++) {
+ ams_channels[i].channel = ch_cnt++;
+
+ if (ams_channels[i].scan_index < AMS_CTRL_SEQ_BASE) {
+ /* set threshold to max and min for each channel */
+ falling_off =
+ ams_get_alarm_offset(ams_channels[i].scan_index,
+ IIO_EV_DIR_FALLING);
+ rising_off =
+ ams_get_alarm_offset(ams_channels[i].scan_index,
+ IIO_EV_DIR_RISING);
+ if (ams_channels[i].scan_index >= AMS_PS_SEQ_MAX) {
+ writel(AMS_ALARM_THR_MIN,
+ ams->pl_base + falling_off);
+ writel(AMS_ALARM_THR_MAX,
+ ams->pl_base + rising_off);
+ } else {
+ writel(AMS_ALARM_THR_MIN,
+ ams->ps_base + falling_off);
+ writel(AMS_ALARM_THR_MAX,
+ ams->ps_base + rising_off);
+ }
+ }
+ }
+
+ dev_size = array_size(sizeof(*dev_channels), num_channels);
+ if (dev_size == SIZE_MAX)
+ return -ENOMEM;
+
+ dev_channels = devm_krealloc(dev, ams_channels, dev_size, GFP_KERNEL);
+ if (!dev_channels)
+ return -ENOMEM;
+
+ indio_dev->channels = dev_channels;
+ indio_dev->num_channels = num_channels;
+
+ return 0;
+}
+
+static const struct iio_info iio_ams_info = {
+ .read_raw = &ams_read_raw,
+ .read_event_config = &ams_read_event_config,
+ .write_event_config = &ams_write_event_config,
+ .read_event_value = &ams_read_event_value,
+ .write_event_value = &ams_write_event_value,
+};
+
+static const struct of_device_id ams_of_match_table[] = {
+ { .compatible = "xlnx,zynqmp-ams" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ams_of_match_table);
+
+static int ams_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct ams *ams;
+ int ret;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ams));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ams = iio_priv(indio_dev);
+ mutex_init(&ams->lock);
+ spin_lock_init(&ams->intr_lock);
+
+ indio_dev->name = "xilinx-ams";
+
+ indio_dev->info = &iio_ams_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ams->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ams->base))
+ return PTR_ERR(ams->base);
+
+ ams->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(ams->clk))
+ return PTR_ERR(ams->clk);
+
+ ret = devm_delayed_work_autocancel(&pdev->dev, &ams->ams_unmask_work,
+ ams_unmask_worker);
+ if (ret < 0)
+ return ret;
+
+ ret = ams_parse_firmware(indio_dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failure in parsing DT\n");
+
+ ret = ams_init_device(ams);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to initialize AMS\n");
+
+ ams_enable_channel_sequence(indio_dev);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(&pdev->dev, irq, &ams_irq, 0, "ams-irq",
+ indio_dev);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "failed to register interrupt\n");
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static int ams_suspend(struct device *dev)
+{
+ struct ams *ams = iio_priv(dev_get_drvdata(dev));
+
+ clk_disable_unprepare(ams->clk);
+
+ return 0;
+}
+
+static int ams_resume(struct device *dev)
+{
+ struct ams *ams = iio_priv(dev_get_drvdata(dev));
+
+ return clk_prepare_enable(ams->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ams_pm_ops, ams_suspend, ams_resume);
+
+static struct platform_driver ams_driver = {
+ .probe = ams_probe,
+ .driver = {
+ .name = "xilinx-ams",
+ .pm = pm_sleep_ptr(&ams_pm_ops),
+ .of_match_table = ams_of_match_table,
+ },
+};
+module_platform_driver(ams_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Xilinx, Inc.");
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
new file mode 100644
index 000000000..abb8891b9
--- /dev/null
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -0,0 +1,1457 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Xilinx XADC driver
+ *
+ * Copyright 2013-2014 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Documentation for the parts can be found at:
+ * - XADC hardmacro: Xilinx UG480
+ * - ZYNQ XADC interface: Xilinx UG585
+ * - AXI XADC interface: Xilinx PG019
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "xilinx-xadc.h"
+
+static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500;
+
+/* ZYNQ register definitions */
+#define XADC_ZYNQ_REG_CFG 0x00
+#define XADC_ZYNQ_REG_INTSTS 0x04
+#define XADC_ZYNQ_REG_INTMSK 0x08
+#define XADC_ZYNQ_REG_STATUS 0x0c
+#define XADC_ZYNQ_REG_CFIFO 0x10
+#define XADC_ZYNQ_REG_DFIFO 0x14
+#define XADC_ZYNQ_REG_CTL 0x18
+
+#define XADC_ZYNQ_CFG_ENABLE BIT(31)
+#define XADC_ZYNQ_CFG_CFIFOTH_MASK (0xf << 20)
+#define XADC_ZYNQ_CFG_CFIFOTH_OFFSET 20
+#define XADC_ZYNQ_CFG_DFIFOTH_MASK (0xf << 16)
+#define XADC_ZYNQ_CFG_DFIFOTH_OFFSET 16
+#define XADC_ZYNQ_CFG_WEDGE BIT(13)
+#define XADC_ZYNQ_CFG_REDGE BIT(12)
+#define XADC_ZYNQ_CFG_TCKRATE_MASK (0x3 << 8)
+#define XADC_ZYNQ_CFG_TCKRATE_DIV2 (0x0 << 8)
+#define XADC_ZYNQ_CFG_TCKRATE_DIV4 (0x1 << 8)
+#define XADC_ZYNQ_CFG_TCKRATE_DIV8 (0x2 << 8)
+#define XADC_ZYNQ_CFG_TCKRATE_DIV16 (0x3 << 8)
+#define XADC_ZYNQ_CFG_IGAP_MASK 0x1f
+#define XADC_ZYNQ_CFG_IGAP(x) (x)
+
+#define XADC_ZYNQ_INT_CFIFO_LTH BIT(9)
+#define XADC_ZYNQ_INT_DFIFO_GTH BIT(8)
+#define XADC_ZYNQ_INT_ALARM_MASK 0xff
+#define XADC_ZYNQ_INT_ALARM_OFFSET 0
+
+#define XADC_ZYNQ_STATUS_CFIFO_LVL_MASK (0xf << 16)
+#define XADC_ZYNQ_STATUS_CFIFO_LVL_OFFSET 16
+#define XADC_ZYNQ_STATUS_DFIFO_LVL_MASK (0xf << 12)
+#define XADC_ZYNQ_STATUS_DFIFO_LVL_OFFSET 12
+#define XADC_ZYNQ_STATUS_CFIFOF BIT(11)
+#define XADC_ZYNQ_STATUS_CFIFOE BIT(10)
+#define XADC_ZYNQ_STATUS_DFIFOF BIT(9)
+#define XADC_ZYNQ_STATUS_DFIFOE BIT(8)
+#define XADC_ZYNQ_STATUS_OT BIT(7)
+#define XADC_ZYNQ_STATUS_ALM(x) BIT(x)
+
+#define XADC_ZYNQ_CTL_RESET BIT(4)
+
+#define XADC_ZYNQ_CMD_NOP 0x00
+#define XADC_ZYNQ_CMD_READ 0x01
+#define XADC_ZYNQ_CMD_WRITE 0x02
+
+#define XADC_ZYNQ_CMD(cmd, addr, data) (((cmd) << 26) | ((addr) << 16) | (data))
+
+/* AXI register definitions */
+#define XADC_AXI_REG_RESET 0x00
+#define XADC_AXI_REG_STATUS 0x04
+#define XADC_AXI_REG_ALARM_STATUS 0x08
+#define XADC_AXI_REG_CONVST 0x0c
+#define XADC_AXI_REG_XADC_RESET 0x10
+#define XADC_AXI_REG_GIER 0x5c
+#define XADC_AXI_REG_IPISR 0x60
+#define XADC_AXI_REG_IPIER 0x68
+
+/* 7 Series */
+#define XADC_7S_AXI_ADC_REG_OFFSET 0x200
+
+/* UltraScale */
+#define XADC_US_AXI_ADC_REG_OFFSET 0x400
+
+#define XADC_AXI_RESET_MAGIC 0xa
+#define XADC_AXI_GIER_ENABLE BIT(31)
+
+#define XADC_AXI_INT_EOS BIT(4)
+#define XADC_AXI_INT_ALARM_MASK 0x3c0f
+
+#define XADC_FLAGS_BUFFERED BIT(0)
+#define XADC_FLAGS_IRQ_OPTIONAL BIT(1)
+
+/*
+ * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does
+ * not have a hardware FIFO. Which means an interrupt is generated for each
+ * conversion sequence. At 1MSPS sample rate the CPU in ZYNQ7000 is completely
+ * overloaded by the interrupts that it soft-lockups. For this reason the driver
+ * limits the maximum samplerate 150kSPS. At this rate the CPU is fairly busy,
+ * but still responsive.
+ */
+#define XADC_MAX_SAMPLERATE 150000
+
+static void xadc_write_reg(struct xadc *xadc, unsigned int reg,
+ uint32_t val)
+{
+ writel(val, xadc->base + reg);
+}
+
+static void xadc_read_reg(struct xadc *xadc, unsigned int reg,
+ uint32_t *val)
+{
+ *val = readl(xadc->base + reg);
+}
+
+/*
+ * The ZYNQ interface uses two asynchronous FIFOs for communication with the
+ * XADC. Reads and writes to the XADC register are performed by submitting a
+ * request to the command FIFO (CFIFO), once the request has been completed the
+ * result can be read from the data FIFO (DFIFO). The method currently used in
+ * this driver is to submit the request for a read/write operation, then go to
+ * sleep and wait for an interrupt that signals that a response is available in
+ * the data FIFO.
+ */
+
+static void xadc_zynq_write_fifo(struct xadc *xadc, uint32_t *cmd,
+ unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < n; i++)
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_CFIFO, cmd[i]);
+}
+
+static void xadc_zynq_drain_fifo(struct xadc *xadc)
+{
+ uint32_t status, tmp;
+
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status);
+
+ while (!(status & XADC_ZYNQ_STATUS_DFIFOE)) {
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp);
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status);
+ }
+}
+
+static void xadc_zynq_update_intmsk(struct xadc *xadc, unsigned int mask,
+ unsigned int val)
+{
+ xadc->zynq_intmask &= ~mask;
+ xadc->zynq_intmask |= val;
+
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK,
+ xadc->zynq_intmask | xadc->zynq_masked_alarm);
+}
+
+static int xadc_zynq_write_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t val)
+{
+ uint32_t cmd[1];
+ uint32_t tmp;
+ int ret;
+
+ spin_lock_irq(&xadc->lock);
+ xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH,
+ XADC_ZYNQ_INT_DFIFO_GTH);
+
+ reinit_completion(&xadc->completion);
+
+ cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_WRITE, reg, val);
+ xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd));
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp);
+ tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK;
+ tmp |= 0 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET;
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp);
+
+ xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0);
+ spin_unlock_irq(&xadc->lock);
+
+ ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ);
+ if (ret == 0)
+ ret = -EIO;
+ else
+ ret = 0;
+
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp);
+
+ return ret;
+}
+
+static int xadc_zynq_read_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t *val)
+{
+ uint32_t cmd[2];
+ uint32_t resp, tmp;
+ int ret;
+
+ cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_READ, reg, 0);
+ cmd[1] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_NOP, 0, 0);
+
+ spin_lock_irq(&xadc->lock);
+ xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH,
+ XADC_ZYNQ_INT_DFIFO_GTH);
+ xadc_zynq_drain_fifo(xadc);
+ reinit_completion(&xadc->completion);
+
+ xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd));
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp);
+ tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK;
+ tmp |= 1 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET;
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp);
+
+ xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0);
+ spin_unlock_irq(&xadc->lock);
+ ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ);
+ if (ret == 0)
+ ret = -EIO;
+ if (ret < 0)
+ return ret;
+
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp);
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp);
+
+ *val = resp & 0xffff;
+
+ return 0;
+}
+
+static unsigned int xadc_zynq_transform_alarm(unsigned int alarm)
+{
+ return ((alarm & 0x80) >> 4) |
+ ((alarm & 0x78) << 1) |
+ (alarm & 0x07);
+}
+
+/*
+ * The ZYNQ threshold interrupts are level sensitive. Since we can't make the
+ * threshold condition go way from within the interrupt handler, this means as
+ * soon as a threshold condition is present we would enter the interrupt handler
+ * again and again. To work around this we mask all active thresholds interrupts
+ * in the interrupt handler and start a timer. In this timer we poll the
+ * interrupt status and only if the interrupt is inactive we unmask it again.
+ */
+static void xadc_zynq_unmask_worker(struct work_struct *work)
+{
+ struct xadc *xadc = container_of(work, struct xadc, zynq_unmask_work.work);
+ unsigned int misc_sts, unmask;
+
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &misc_sts);
+
+ misc_sts &= XADC_ZYNQ_INT_ALARM_MASK;
+
+ spin_lock_irq(&xadc->lock);
+
+ /* Clear those bits which are not active anymore */
+ unmask = (xadc->zynq_masked_alarm ^ misc_sts) & xadc->zynq_masked_alarm;
+ xadc->zynq_masked_alarm &= misc_sts;
+
+ /* Also clear those which are masked out anyway */
+ xadc->zynq_masked_alarm &= ~xadc->zynq_intmask;
+
+ /* Clear the interrupts before we unmask them */
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, unmask);
+
+ xadc_zynq_update_intmsk(xadc, 0, 0);
+
+ spin_unlock_irq(&xadc->lock);
+
+ /* if still pending some alarm re-trigger the timer */
+ if (xadc->zynq_masked_alarm) {
+ schedule_delayed_work(&xadc->zynq_unmask_work,
+ msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
+ }
+
+}
+
+static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
+{
+ struct iio_dev *indio_dev = devid;
+ struct xadc *xadc = iio_priv(indio_dev);
+ uint32_t status;
+
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status);
+
+ status &= ~(xadc->zynq_intmask | xadc->zynq_masked_alarm);
+
+ if (!status)
+ return IRQ_NONE;
+
+ spin_lock(&xadc->lock);
+
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status);
+
+ if (status & XADC_ZYNQ_INT_DFIFO_GTH) {
+ xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH,
+ XADC_ZYNQ_INT_DFIFO_GTH);
+ complete(&xadc->completion);
+ }
+
+ status &= XADC_ZYNQ_INT_ALARM_MASK;
+ if (status) {
+ xadc->zynq_masked_alarm |= status;
+ /*
+ * mask the current event interrupt,
+ * unmask it when the interrupt is no more active.
+ */
+ xadc_zynq_update_intmsk(xadc, 0, 0);
+
+ xadc_handle_events(indio_dev,
+ xadc_zynq_transform_alarm(status));
+
+ /* unmask the required interrupts in timer. */
+ schedule_delayed_work(&xadc->zynq_unmask_work,
+ msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
+ }
+ spin_unlock(&xadc->lock);
+
+ return IRQ_HANDLED;
+}
+
+#define XADC_ZYNQ_TCK_RATE_MAX 50000000
+#define XADC_ZYNQ_IGAP_DEFAULT 20
+#define XADC_ZYNQ_PCAP_RATE_MAX 200000000
+
+static int xadc_zynq_setup(struct platform_device *pdev,
+ struct iio_dev *indio_dev, int irq)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+ unsigned long pcap_rate;
+ unsigned int tck_div;
+ unsigned int div;
+ unsigned int igap;
+ unsigned int tck_rate;
+ int ret;
+
+ /* TODO: Figure out how to make igap and tck_rate configurable */
+ igap = XADC_ZYNQ_IGAP_DEFAULT;
+ tck_rate = XADC_ZYNQ_TCK_RATE_MAX;
+
+ xadc->zynq_intmask = ~0;
+
+ pcap_rate = clk_get_rate(xadc->clk);
+ if (!pcap_rate)
+ return -EINVAL;
+
+ if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) {
+ ret = clk_set_rate(xadc->clk,
+ (unsigned long)XADC_ZYNQ_PCAP_RATE_MAX);
+ if (ret)
+ return ret;
+ }
+
+ if (tck_rate > pcap_rate / 2) {
+ div = 2;
+ } else {
+ div = pcap_rate / tck_rate;
+ if (pcap_rate / div > XADC_ZYNQ_TCK_RATE_MAX)
+ div++;
+ }
+
+ if (div <= 3)
+ tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV2;
+ else if (div <= 7)
+ tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV4;
+ else if (div <= 15)
+ tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV8;
+ else
+ tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV16;
+
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, XADC_ZYNQ_CTL_RESET);
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, 0);
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, ~0);
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, xadc->zynq_intmask);
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, XADC_ZYNQ_CFG_ENABLE |
+ XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE |
+ tck_div | XADC_ZYNQ_CFG_IGAP(igap));
+
+ if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) {
+ ret = clk_set_rate(xadc->clk, pcap_rate);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static unsigned long xadc_zynq_get_dclk_rate(struct xadc *xadc)
+{
+ unsigned int div;
+ uint32_t val;
+
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &val);
+
+ switch (val & XADC_ZYNQ_CFG_TCKRATE_MASK) {
+ case XADC_ZYNQ_CFG_TCKRATE_DIV4:
+ div = 4;
+ break;
+ case XADC_ZYNQ_CFG_TCKRATE_DIV8:
+ div = 8;
+ break;
+ case XADC_ZYNQ_CFG_TCKRATE_DIV16:
+ div = 16;
+ break;
+ default:
+ div = 2;
+ break;
+ }
+
+ return clk_get_rate(xadc->clk) / div;
+}
+
+static void xadc_zynq_update_alarm(struct xadc *xadc, unsigned int alarm)
+{
+ unsigned long flags;
+ uint32_t status;
+
+ /* Move OT to bit 7 */
+ alarm = ((alarm & 0x08) << 4) | ((alarm & 0xf0) >> 1) | (alarm & 0x07);
+
+ spin_lock_irqsave(&xadc->lock, flags);
+
+ /* Clear previous interrupts if any. */
+ xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status);
+ xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status & alarm);
+
+ xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_ALARM_MASK,
+ ~alarm & XADC_ZYNQ_INT_ALARM_MASK);
+
+ spin_unlock_irqrestore(&xadc->lock, flags);
+}
+
+static const struct xadc_ops xadc_zynq_ops = {
+ .read = xadc_zynq_read_adc_reg,
+ .write = xadc_zynq_write_adc_reg,
+ .setup = xadc_zynq_setup,
+ .get_dclk_rate = xadc_zynq_get_dclk_rate,
+ .interrupt_handler = xadc_zynq_interrupt_handler,
+ .update_alarm = xadc_zynq_update_alarm,
+ .type = XADC_TYPE_S7,
+ /* Temp in C = (val * 503.975) / 2**bits - 273.15 */
+ .temp_scale = 503975,
+ .temp_offset = 273150,
+};
+
+static const unsigned int xadc_axi_reg_offsets[] = {
+ [XADC_TYPE_S7] = XADC_7S_AXI_ADC_REG_OFFSET,
+ [XADC_TYPE_US] = XADC_US_AXI_ADC_REG_OFFSET,
+};
+
+static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t *val)
+{
+ uint32_t val32;
+
+ xadc_read_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
+ &val32);
+ *val = val32 & 0xffff;
+
+ return 0;
+}
+
+static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t val)
+{
+ xadc_write_reg(xadc, xadc_axi_reg_offsets[xadc->ops->type] + reg * 4,
+ val);
+
+ return 0;
+}
+
+static int xadc_axi_setup(struct platform_device *pdev,
+ struct iio_dev *indio_dev, int irq)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+
+ xadc_write_reg(xadc, XADC_AXI_REG_RESET, XADC_AXI_RESET_MAGIC);
+ xadc_write_reg(xadc, XADC_AXI_REG_GIER, XADC_AXI_GIER_ENABLE);
+
+ return 0;
+}
+
+static irqreturn_t xadc_axi_interrupt_handler(int irq, void *devid)
+{
+ struct iio_dev *indio_dev = devid;
+ struct xadc *xadc = iio_priv(indio_dev);
+ uint32_t status, mask;
+ unsigned int events;
+
+ xadc_read_reg(xadc, XADC_AXI_REG_IPISR, &status);
+ xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &mask);
+ status &= mask;
+
+ if (!status)
+ return IRQ_NONE;
+
+ if ((status & XADC_AXI_INT_EOS) && xadc->trigger)
+ iio_trigger_poll(xadc->trigger);
+
+ if (status & XADC_AXI_INT_ALARM_MASK) {
+ /*
+ * The order of the bits in the AXI-XADC status register does
+ * not match the order of the bits in the XADC alarm enable
+ * register. xadc_handle_events() expects the events to be in
+ * the same order as the XADC alarm enable register.
+ */
+ events = (status & 0x000e) >> 1;
+ events |= (status & 0x0001) << 3;
+ events |= (status & 0x3c00) >> 6;
+ xadc_handle_events(indio_dev, events);
+ }
+
+ xadc_write_reg(xadc, XADC_AXI_REG_IPISR, status);
+
+ return IRQ_HANDLED;
+}
+
+static void xadc_axi_update_alarm(struct xadc *xadc, unsigned int alarm)
+{
+ uint32_t val;
+ unsigned long flags;
+
+ /*
+ * The order of the bits in the AXI-XADC status register does not match
+ * the order of the bits in the XADC alarm enable register. We get
+ * passed the alarm mask in the same order as in the XADC alarm enable
+ * register.
+ */
+ alarm = ((alarm & 0x07) << 1) | ((alarm & 0x08) >> 3) |
+ ((alarm & 0xf0) << 6);
+
+ spin_lock_irqsave(&xadc->lock, flags);
+ xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val);
+ val &= ~XADC_AXI_INT_ALARM_MASK;
+ val |= alarm;
+ xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val);
+ spin_unlock_irqrestore(&xadc->lock, flags);
+}
+
+static unsigned long xadc_axi_get_dclk(struct xadc *xadc)
+{
+ return clk_get_rate(xadc->clk);
+}
+
+static const struct xadc_ops xadc_7s_axi_ops = {
+ .read = xadc_axi_read_adc_reg,
+ .write = xadc_axi_write_adc_reg,
+ .setup = xadc_axi_setup,
+ .get_dclk_rate = xadc_axi_get_dclk,
+ .update_alarm = xadc_axi_update_alarm,
+ .interrupt_handler = xadc_axi_interrupt_handler,
+ .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL,
+ .type = XADC_TYPE_S7,
+ /* Temp in C = (val * 503.975) / 2**bits - 273.15 */
+ .temp_scale = 503975,
+ .temp_offset = 273150,
+};
+
+static const struct xadc_ops xadc_us_axi_ops = {
+ .read = xadc_axi_read_adc_reg,
+ .write = xadc_axi_write_adc_reg,
+ .setup = xadc_axi_setup,
+ .get_dclk_rate = xadc_axi_get_dclk,
+ .update_alarm = xadc_axi_update_alarm,
+ .interrupt_handler = xadc_axi_interrupt_handler,
+ .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL,
+ .type = XADC_TYPE_US,
+ /**
+ * Values below are for UltraScale+ (SYSMONE4) using internal reference.
+ * See https://docs.xilinx.com/v/u/en-US/ug580-ultrascale-sysmon
+ */
+ .temp_scale = 509314,
+ .temp_offset = 280231,
+};
+
+static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t mask, uint16_t val)
+{
+ uint16_t tmp;
+ int ret;
+
+ ret = _xadc_read_adc_reg(xadc, reg, &tmp);
+ if (ret)
+ return ret;
+
+ return _xadc_write_adc_reg(xadc, reg, (tmp & ~mask) | val);
+}
+
+static int xadc_update_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t mask, uint16_t val)
+{
+ int ret;
+
+ mutex_lock(&xadc->mutex);
+ ret = _xadc_update_adc_reg(xadc, reg, mask, val);
+ mutex_unlock(&xadc->mutex);
+
+ return ret;
+}
+
+static unsigned long xadc_get_dclk_rate(struct xadc *xadc)
+{
+ return xadc->ops->get_dclk_rate(xadc);
+}
+
+static int xadc_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *mask)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+ size_t new_size, n;
+ void *data;
+
+ n = bitmap_weight(mask, indio_dev->masklength);
+
+ if (check_mul_overflow(n, sizeof(*xadc->data), &new_size))
+ return -ENOMEM;
+
+ data = devm_krealloc(indio_dev->dev.parent, xadc->data,
+ new_size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, new_size);
+ xadc->data = data;
+
+ return 0;
+}
+
+static unsigned int xadc_scan_index_to_channel(unsigned int scan_index)
+{
+ switch (scan_index) {
+ case 5:
+ return XADC_REG_VCCPINT;
+ case 6:
+ return XADC_REG_VCCPAUX;
+ case 7:
+ return XADC_REG_VCCO_DDR;
+ case 8:
+ return XADC_REG_TEMP;
+ case 9:
+ return XADC_REG_VCCINT;
+ case 10:
+ return XADC_REG_VCCAUX;
+ case 11:
+ return XADC_REG_VPVN;
+ case 12:
+ return XADC_REG_VREFP;
+ case 13:
+ return XADC_REG_VREFN;
+ case 14:
+ return XADC_REG_VCCBRAM;
+ default:
+ return XADC_REG_VAUX(scan_index - 16);
+ }
+}
+
+static irqreturn_t xadc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct xadc *xadc = iio_priv(indio_dev);
+ unsigned int chan;
+ int i, j;
+
+ if (!xadc->data)
+ goto out;
+
+ j = 0;
+ for_each_set_bit(i, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ chan = xadc_scan_index_to_channel(i);
+ xadc_read_adc_reg(xadc, chan, &xadc->data[j]);
+ j++;
+ }
+
+ iio_push_to_buffers(indio_dev, xadc->data);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int xadc_trigger_set_state(struct iio_trigger *trigger, bool state)
+{
+ struct xadc *xadc = iio_trigger_get_drvdata(trigger);
+ unsigned long flags;
+ unsigned int convst;
+ unsigned int val;
+ int ret = 0;
+
+ mutex_lock(&xadc->mutex);
+
+ if (state) {
+ /* Only one of the two triggers can be active at a time. */
+ if (xadc->trigger != NULL) {
+ ret = -EBUSY;
+ goto err_out;
+ } else {
+ xadc->trigger = trigger;
+ if (trigger == xadc->convst_trigger)
+ convst = XADC_CONF0_EC;
+ else
+ convst = 0;
+ }
+ ret = _xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF0_EC,
+ convst);
+ if (ret)
+ goto err_out;
+ } else {
+ xadc->trigger = NULL;
+ }
+
+ spin_lock_irqsave(&xadc->lock, flags);
+ xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val);
+ xadc_write_reg(xadc, XADC_AXI_REG_IPISR, XADC_AXI_INT_EOS);
+ if (state)
+ val |= XADC_AXI_INT_EOS;
+ else
+ val &= ~XADC_AXI_INT_EOS;
+ xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val);
+ spin_unlock_irqrestore(&xadc->lock, flags);
+
+err_out:
+ mutex_unlock(&xadc->mutex);
+
+ return ret;
+}
+
+static const struct iio_trigger_ops xadc_trigger_ops = {
+ .set_trigger_state = &xadc_trigger_set_state,
+};
+
+static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev,
+ const char *name)
+{
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name,
+ iio_device_id(indio_dev), name);
+ if (trig == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ trig->ops = &xadc_trigger_ops;
+ iio_trigger_set_drvdata(trig, iio_priv(indio_dev));
+
+ ret = devm_iio_trigger_register(dev, trig);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return trig;
+}
+
+static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode)
+{
+ uint16_t val;
+
+ /*
+ * As per datasheet the power-down bits are don't care in the
+ * UltraScale, but as per reality setting the power-down bit for the
+ * non-existing ADC-B powers down the main ADC, so just return and don't
+ * do anything.
+ */
+ if (xadc->ops->type == XADC_TYPE_US)
+ return 0;
+
+ /* Powerdown the ADC-B when it is not needed. */
+ switch (seq_mode) {
+ case XADC_CONF1_SEQ_SIMULTANEOUS:
+ case XADC_CONF1_SEQ_INDEPENDENT:
+ val = 0;
+ break;
+ default:
+ val = XADC_CONF2_PD_ADC_B;
+ break;
+ }
+
+ return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_PD_MASK,
+ val);
+}
+
+static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode)
+{
+ unsigned int aux_scan_mode = scan_mode >> 16;
+
+ /* UltraScale has only one ADC and supports only continuous mode */
+ if (xadc->ops->type == XADC_TYPE_US)
+ return XADC_CONF1_SEQ_CONTINUOUS;
+
+ if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL)
+ return XADC_CONF1_SEQ_SIMULTANEOUS;
+
+ if ((aux_scan_mode & 0xff00) == 0 ||
+ (aux_scan_mode & 0x00ff) == 0)
+ return XADC_CONF1_SEQ_CONTINUOUS;
+
+ return XADC_CONF1_SEQ_SIMULTANEOUS;
+}
+
+static int xadc_postdisable(struct iio_dev *indio_dev)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+ unsigned long scan_mask;
+ int ret;
+ int i;
+
+ scan_mask = 1; /* Run calibration as part of the sequence */
+ for (i = 0; i < indio_dev->num_channels; i++)
+ scan_mask |= BIT(indio_dev->channels[i].scan_index);
+
+ /* Enable all channels and calibration */
+ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff);
+ if (ret)
+ return ret;
+
+ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16);
+ if (ret)
+ return ret;
+
+ ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK,
+ XADC_CONF1_SEQ_CONTINUOUS);
+ if (ret)
+ return ret;
+
+ return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS);
+}
+
+static int xadc_preenable(struct iio_dev *indio_dev)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+ unsigned long scan_mask;
+ int seq_mode;
+ int ret;
+
+ ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK,
+ XADC_CONF1_SEQ_DEFAULT);
+ if (ret)
+ goto err;
+
+ scan_mask = *indio_dev->active_scan_mask;
+ seq_mode = xadc_get_seq_mode(xadc, scan_mask);
+
+ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff);
+ if (ret)
+ goto err;
+
+ /*
+ * In simultaneous mode the upper and lower aux channels are samples at
+ * the same time. In this mode the upper 8 bits in the sequencer
+ * register are don't care and the lower 8 bits control two channels
+ * each. As such we must set the bit if either the channel in the lower
+ * group or the upper group is enabled.
+ */
+ if (seq_mode == XADC_CONF1_SEQ_SIMULTANEOUS)
+ scan_mask = ((scan_mask >> 8) | scan_mask) & 0xff0000;
+
+ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16);
+ if (ret)
+ goto err;
+
+ ret = xadc_power_adc_b(xadc, seq_mode);
+ if (ret)
+ goto err;
+
+ ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK,
+ seq_mode);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ xadc_postdisable(indio_dev);
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops xadc_buffer_ops = {
+ .preenable = &xadc_preenable,
+ .postdisable = &xadc_postdisable,
+};
+
+static int xadc_read_samplerate(struct xadc *xadc)
+{
+ unsigned int div;
+ uint16_t val16;
+ int ret;
+
+ ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16);
+ if (ret)
+ return ret;
+
+ div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET;
+ if (div < 2)
+ div = 2;
+
+ return xadc_get_dclk_rate(xadc) / div / 26;
+}
+
+static int xadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+ unsigned int bits = chan->scan_type.realbits;
+ uint16_t val16;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ ret = xadc_read_adc_reg(xadc, chan->address, &val16);
+ if (ret < 0)
+ return ret;
+
+ val16 >>= chan->scan_type.shift;
+ if (chan->scan_type.sign == 'u')
+ *val = val16;
+ else
+ *val = sign_extend32(val16, bits - 1);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ /* V = (val * 3.0) / 2**bits */
+ switch (chan->address) {
+ case XADC_REG_VCCINT:
+ case XADC_REG_VCCAUX:
+ case XADC_REG_VREFP:
+ case XADC_REG_VREFN:
+ case XADC_REG_VCCBRAM:
+ case XADC_REG_VCCPINT:
+ case XADC_REG_VCCPAUX:
+ case XADC_REG_VCCO_DDR:
+ *val = 3000;
+ break;
+ default:
+ *val = 1000;
+ break;
+ }
+ *val2 = bits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ *val = xadc->ops->temp_scale;
+ *val2 = bits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ /* Only the temperature channel has an offset */
+ *val = -((xadc->ops->temp_offset << bits) / xadc->ops->temp_scale);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = xadc_read_samplerate(xadc);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int xadc_write_samplerate(struct xadc *xadc, int val)
+{
+ unsigned long clk_rate = xadc_get_dclk_rate(xadc);
+ unsigned int div;
+
+ if (!clk_rate)
+ return -EINVAL;
+
+ if (val <= 0)
+ return -EINVAL;
+
+ /* Max. 150 kSPS */
+ if (val > XADC_MAX_SAMPLERATE)
+ val = XADC_MAX_SAMPLERATE;
+
+ val *= 26;
+
+ /* Min 1MHz */
+ if (val < 1000000)
+ val = 1000000;
+
+ /*
+ * We want to round down, but only if we do not exceed the 150 kSPS
+ * limit.
+ */
+ div = clk_rate / val;
+ if (clk_rate / div / 26 > XADC_MAX_SAMPLERATE)
+ div++;
+ if (div < 2)
+ div = 2;
+ else if (div > 0xff)
+ div = 0xff;
+
+ return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_DIV_MASK,
+ div << XADC_CONF2_DIV_OFFSET);
+}
+
+static int xadc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+ struct xadc *xadc = iio_priv(indio_dev);
+
+ if (info != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ return xadc_write_samplerate(xadc, val);
+}
+
+static const struct iio_event_spec xadc_temp_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+};
+
+/* Separate values for upper and lower thresholds, but only a shared enabled */
+static const struct iio_event_spec xadc_voltage_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_ENABLE),
+ },
+};
+
+#define XADC_CHAN_TEMP(_chan, _scan_index, _addr, _bits) { \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .channel = (_chan), \
+ .address = (_addr), \
+ .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), \
+ .event_spec = xadc_temp_events, \
+ .num_event_specs = ARRAY_SIZE(xadc_temp_events), \
+ .scan_index = (_scan_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _bits, _ext, _alarm) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan), \
+ .address = (_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .event_spec = (_alarm) ? xadc_voltage_events : NULL, \
+ .num_event_specs = (_alarm) ? ARRAY_SIZE(xadc_voltage_events) : 0, \
+ .scan_index = (_scan_index), \
+ .scan_type = { \
+ .sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 16 - (_bits), \
+ .endianness = IIO_CPU, \
+ }, \
+ .extend_name = _ext, \
+}
+
+/* 7 Series */
+#define XADC_7S_CHAN_TEMP(_chan, _scan_index, _addr) \
+ XADC_CHAN_TEMP(_chan, _scan_index, _addr, 12)
+#define XADC_7S_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
+ XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 12, _ext, _alarm)
+
+static const struct iio_chan_spec xadc_7s_channels[] = {
+ XADC_7S_CHAN_TEMP(0, 8, XADC_REG_TEMP),
+ XADC_7S_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
+ XADC_7S_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
+ XADC_7S_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
+ XADC_7S_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true),
+ XADC_7S_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true),
+ XADC_7S_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true),
+ XADC_7S_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
+ XADC_7S_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
+ XADC_7S_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
+ XADC_7S_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
+ XADC_7S_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
+};
+
+/* UltraScale */
+#define XADC_US_CHAN_TEMP(_chan, _scan_index, _addr) \
+ XADC_CHAN_TEMP(_chan, _scan_index, _addr, 10)
+#define XADC_US_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) \
+ XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, 10, _ext, _alarm)
+
+static const struct iio_chan_spec xadc_us_channels[] = {
+ XADC_US_CHAN_TEMP(0, 8, XADC_REG_TEMP),
+ XADC_US_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true),
+ XADC_US_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true),
+ XADC_US_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true),
+ XADC_US_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpsintlp", true),
+ XADC_US_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpsintfp", true),
+ XADC_US_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccpsaux", true),
+ XADC_US_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false),
+ XADC_US_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false),
+ XADC_US_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false),
+ XADC_US_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false),
+ XADC_US_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false),
+ XADC_US_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false),
+ XADC_US_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false),
+ XADC_US_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false),
+ XADC_US_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false),
+ XADC_US_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false),
+ XADC_US_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false),
+ XADC_US_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false),
+ XADC_US_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false),
+ XADC_US_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false),
+ XADC_US_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false),
+ XADC_US_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false),
+ XADC_US_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false),
+ XADC_US_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false),
+ XADC_US_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false),
+};
+
+static const struct iio_info xadc_info = {
+ .read_raw = &xadc_read_raw,
+ .write_raw = &xadc_write_raw,
+ .read_event_config = &xadc_read_event_config,
+ .write_event_config = &xadc_write_event_config,
+ .read_event_value = &xadc_read_event_value,
+ .write_event_value = &xadc_write_event_value,
+ .update_scan_mode = &xadc_update_scan_mode,
+};
+
+static const struct of_device_id xadc_of_match_table[] = {
+ {
+ .compatible = "xlnx,zynq-xadc-1.00.a",
+ .data = &xadc_zynq_ops
+ }, {
+ .compatible = "xlnx,axi-xadc-1.00.a",
+ .data = &xadc_7s_axi_ops
+ }, {
+ .compatible = "xlnx,system-management-wiz-1.3",
+ .data = &xadc_us_axi_ops
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, xadc_of_match_table);
+
+static int xadc_parse_dt(struct iio_dev *indio_dev, unsigned int *conf, int irq)
+{
+ struct device *dev = indio_dev->dev.parent;
+ struct xadc *xadc = iio_priv(indio_dev);
+ const struct iio_chan_spec *channel_templates;
+ struct iio_chan_spec *channels, *chan;
+ struct fwnode_handle *chan_node, *child;
+ unsigned int max_channels;
+ unsigned int num_channels;
+ const char *external_mux;
+ u32 ext_mux_chan;
+ u32 reg;
+ int ret;
+ int i;
+
+ *conf = 0;
+
+ ret = device_property_read_string(dev, "xlnx,external-mux", &external_mux);
+ if (ret < 0 || strcasecmp(external_mux, "none") == 0)
+ xadc->external_mux_mode = XADC_EXTERNAL_MUX_NONE;
+ else if (strcasecmp(external_mux, "single") == 0)
+ xadc->external_mux_mode = XADC_EXTERNAL_MUX_SINGLE;
+ else if (strcasecmp(external_mux, "dual") == 0)
+ xadc->external_mux_mode = XADC_EXTERNAL_MUX_DUAL;
+ else
+ return -EINVAL;
+
+ if (xadc->external_mux_mode != XADC_EXTERNAL_MUX_NONE) {
+ ret = device_property_read_u32(dev, "xlnx,external-mux-channel", &ext_mux_chan);
+ if (ret < 0)
+ return ret;
+
+ if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_SINGLE) {
+ if (ext_mux_chan == 0)
+ ext_mux_chan = XADC_REG_VPVN;
+ else if (ext_mux_chan <= 16)
+ ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1);
+ else
+ return -EINVAL;
+ } else {
+ if (ext_mux_chan > 0 && ext_mux_chan <= 8)
+ ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1);
+ else
+ return -EINVAL;
+ }
+
+ *conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan);
+ }
+ if (xadc->ops->type == XADC_TYPE_S7) {
+ channel_templates = xadc_7s_channels;
+ max_channels = ARRAY_SIZE(xadc_7s_channels);
+ } else {
+ 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);
+ if (!channels)
+ return -ENOMEM;
+
+ num_channels = 9;
+ chan = &channels[9];
+
+ chan_node = device_get_named_child_node(dev, "xlnx,channels");
+ fwnode_for_each_child_node(chan_node, child) {
+ if (num_channels >= max_channels) {
+ fwnode_handle_put(child);
+ break;
+ }
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret || reg > 16)
+ continue;
+
+ if (fwnode_property_read_bool(child, "xlnx,bipolar"))
+ chan->scan_type.sign = 's';
+
+ if (reg == 0) {
+ chan->scan_index = 11;
+ chan->address = XADC_REG_VPVN;
+ } else {
+ chan->scan_index = 15 + reg;
+ chan->address = XADC_REG_VAUX(reg - 1);
+ }
+ num_channels++;
+ chan++;
+ }
+ fwnode_handle_put(chan_node);
+
+ /* No IRQ => no events */
+ if (irq <= 0) {
+ for (i = 0; i < num_channels; i++) {
+ channels[i].event_spec = NULL;
+ channels[i].num_event_specs = 0;
+ }
+ }
+
+ indio_dev->num_channels = num_channels;
+ indio_dev->channels = devm_krealloc(dev, channels,
+ sizeof(*channels) * num_channels,
+ GFP_KERNEL);
+ /* If we can't resize the channels array, just use the original */
+ if (!indio_dev->channels)
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static const char * const xadc_type_names[] = {
+ [XADC_TYPE_S7] = "xadc",
+ [XADC_TYPE_US] = "xilinx-system-monitor",
+};
+
+static void xadc_cancel_delayed_work(void *data)
+{
+ struct delayed_work *work = data;
+
+ cancel_delayed_work_sync(work);
+}
+
+static int xadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct xadc_ops *ops;
+ struct iio_dev *indio_dev;
+ unsigned int bipolar_mask;
+ unsigned int conf0;
+ struct xadc *xadc;
+ int ret;
+ int irq;
+ int i;
+
+ ops = device_get_match_data(dev);
+ if (!ops)
+ return -EINVAL;
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq < 0 &&
+ (irq != -ENXIO || !(ops->flags & XADC_FLAGS_IRQ_OPTIONAL)))
+ return irq;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ xadc = iio_priv(indio_dev);
+ xadc->ops = ops;
+ init_completion(&xadc->completion);
+ mutex_init(&xadc->mutex);
+ spin_lock_init(&xadc->lock);
+ INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker);
+
+ xadc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(xadc->base))
+ return PTR_ERR(xadc->base);
+
+ indio_dev->name = xadc_type_names[xadc->ops->type];
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &xadc_info;
+
+ ret = xadc_parse_dt(indio_dev, &conf0, irq);
+ if (ret)
+ return ret;
+
+ if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &xadc_trigger_handler,
+ &xadc_buffer_ops);
+ if (ret)
+ return ret;
+
+ if (irq > 0) {
+ xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst");
+ if (IS_ERR(xadc->convst_trigger))
+ return PTR_ERR(xadc->convst_trigger);
+
+ xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev,
+ "samplerate");
+ if (IS_ERR(xadc->samplerate_trigger))
+ return PTR_ERR(xadc->samplerate_trigger);
+ }
+ }
+
+ xadc->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(xadc->clk))
+ return PTR_ERR(xadc->clk);
+
+ /*
+ * Make sure not to exceed the maximum samplerate since otherwise the
+ * resulting interrupt storm will soft-lock the system.
+ */
+ if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
+ ret = xadc_read_samplerate(xadc);
+ if (ret < 0)
+ return ret;
+
+ if (ret > XADC_MAX_SAMPLERATE) {
+ ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (irq > 0) {
+ ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler,
+ 0, dev_name(dev), indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
+ &xadc->zynq_unmask_work);
+ if (ret)
+ return ret;
+ }
+
+ ret = xadc->ops->setup(pdev, indio_dev, irq);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 16; i++)
+ xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i),
+ &xadc->threshold[i]);
+
+ ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0);
+ if (ret)
+ return ret;
+
+ bipolar_mask = 0;
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ if (indio_dev->channels[i].scan_type.sign == 's')
+ bipolar_mask |= BIT(indio_dev->channels[i].scan_index);
+ }
+
+ ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask);
+ if (ret)
+ return ret;
+
+ ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1),
+ bipolar_mask >> 16);
+ if (ret)
+ return ret;
+
+ /* Go to non-buffered mode */
+ xadc_postdisable(indio_dev);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct platform_driver xadc_driver = {
+ .probe = xadc_probe,
+ .driver = {
+ .name = "xadc",
+ .of_match_table = xadc_of_match_table,
+ },
+};
+module_platform_driver(xadc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Xilinx XADC IIO driver");
diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c
new file mode 100644
index 000000000..1bd375fb1
--- /dev/null
+++ b/drivers/iio/adc/xilinx-xadc-events.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Xilinx XADC driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/kernel.h>
+
+#include "xilinx-xadc.h"
+
+static const struct iio_chan_spec *xadc_event_to_channel(
+ struct iio_dev *indio_dev, unsigned int event)
+{
+ switch (event) {
+ case XADC_THRESHOLD_OT_MAX:
+ case XADC_THRESHOLD_TEMP_MAX:
+ return &indio_dev->channels[0];
+ case XADC_THRESHOLD_VCCINT_MAX:
+ case XADC_THRESHOLD_VCCAUX_MAX:
+ return &indio_dev->channels[event];
+ default:
+ return &indio_dev->channels[event-1];
+ }
+}
+
+static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event)
+{
+ const struct iio_chan_spec *chan;
+
+ /* Temperature threshold error, we don't handle this yet */
+ if (event == 0)
+ return;
+
+ chan = xadc_event_to_channel(indio_dev, event);
+
+ if (chan->type == IIO_TEMP) {
+ /*
+ * The temperature channel only supports over-temperature
+ * events.
+ */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ } else {
+ /*
+ * For other channels we don't know whether it is a upper or
+ * lower threshold event. Userspace will have to check the
+ * channel value if it wants to know.
+ */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
+ IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ }
+}
+
+void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events)
+{
+ unsigned int i;
+
+ for_each_set_bit(i, &events, 8)
+ xadc_handle_event(indio_dev, i);
+}
+
+static unsigned int xadc_get_threshold_offset(const struct iio_chan_spec *chan,
+ enum iio_event_direction dir)
+{
+ unsigned int offset;
+
+ if (chan->type == IIO_TEMP) {
+ offset = XADC_THRESHOLD_OT_MAX;
+ } else {
+ if (chan->channel < 2)
+ offset = chan->channel + 1;
+ else
+ offset = chan->channel + 6;
+ }
+
+ if (dir == IIO_EV_DIR_FALLING)
+ offset += 4;
+
+ return offset;
+}
+
+static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan)
+{
+ if (chan->type == IIO_TEMP)
+ return XADC_ALARM_OT_MASK;
+ switch (chan->channel) {
+ case 0:
+ return XADC_ALARM_VCCINT_MASK;
+ case 1:
+ return XADC_ALARM_VCCAUX_MASK;
+ case 2:
+ return XADC_ALARM_VCCBRAM_MASK;
+ case 3:
+ return XADC_ALARM_VCCPINT_MASK;
+ case 4:
+ return XADC_ALARM_VCCPAUX_MASK;
+ case 5:
+ return XADC_ALARM_VCCODDR_MASK;
+ default:
+ /* We will never get here */
+ return 0;
+ }
+}
+
+int xadc_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 xadc *xadc = iio_priv(indio_dev);
+
+ return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan));
+}
+
+int xadc_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ unsigned int alarm = xadc_get_alarm_mask(chan);
+ struct xadc *xadc = iio_priv(indio_dev);
+ uint16_t cfg, old_cfg;
+ int ret;
+
+ mutex_lock(&xadc->mutex);
+
+ if (state)
+ xadc->alarm_mask |= alarm;
+ else
+ xadc->alarm_mask &= ~alarm;
+
+ xadc->ops->update_alarm(xadc, xadc->alarm_mask);
+
+ ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg);
+ if (ret)
+ goto err_out;
+
+ old_cfg = cfg;
+ cfg |= XADC_CONF1_ALARM_MASK;
+ cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */
+ cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */
+ cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */
+ if (old_cfg != cfg)
+ ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg);
+
+err_out:
+ mutex_unlock(&xadc->mutex);
+
+ return ret;
+}
+
+int xadc_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)
+{
+ unsigned int offset = xadc_get_threshold_offset(chan, dir);
+ struct xadc *xadc = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ *val = xadc->threshold[offset];
+ break;
+ case IIO_EV_INFO_HYSTERESIS:
+ *val = xadc->temp_hysteresis;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* MSB aligned */
+ *val >>= 16 - chan->scan_type.realbits;
+
+ return IIO_VAL_INT;
+}
+
+int xadc_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)
+{
+ unsigned int offset = xadc_get_threshold_offset(chan, dir);
+ struct xadc *xadc = iio_priv(indio_dev);
+ int ret = 0;
+
+ /* MSB aligned */
+ val <<= 16 - chan->scan_type.realbits;
+
+ if (val < 0 || val > 0xffff)
+ return -EINVAL;
+
+ mutex_lock(&xadc->mutex);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ xadc->threshold[offset] = val;
+ break;
+ case IIO_EV_INFO_HYSTERESIS:
+ xadc->temp_hysteresis = val;
+ break;
+ default:
+ mutex_unlock(&xadc->mutex);
+ return -EINVAL;
+ }
+
+ if (chan->type == IIO_TEMP) {
+ /*
+ * According to the datasheet we need to set the lower 4 bits to
+ * 0x3, otherwise 125 degree celsius will be used as the
+ * threshold.
+ */
+ val |= 0x3;
+
+ /*
+ * Since we store the hysteresis as relative (to the threshold)
+ * value, but the hardware expects an absolute value we need to
+ * recalcualte this value whenever the hysteresis or the
+ * threshold changes.
+ */
+ if (xadc->threshold[offset] < xadc->temp_hysteresis)
+ xadc->threshold[offset + 4] = 0;
+ else
+ xadc->threshold[offset + 4] = xadc->threshold[offset] -
+ xadc->temp_hysteresis;
+ ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4),
+ xadc->threshold[offset + 4]);
+ if (ret)
+ goto out_unlock;
+ }
+
+ if (info == IIO_EV_INFO_VALUE)
+ ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val);
+
+out_unlock:
+ mutex_unlock(&xadc->mutex);
+
+ return ret;
+}
diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h
new file mode 100644
index 000000000..3036f4d61
--- /dev/null
+++ b/drivers/iio/adc/xilinx-xadc.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Xilinx XADC driver
+ *
+ * Copyright 2013 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef __IIO_XILINX_XADC__
+#define __IIO_XILINX_XADC__
+
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+struct iio_dev;
+struct clk;
+struct xadc_ops;
+struct platform_device;
+
+void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events);
+
+int xadc_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 xadc_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state);
+int xadc_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 xadc_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);
+
+enum xadc_external_mux_mode {
+ XADC_EXTERNAL_MUX_NONE,
+ XADC_EXTERNAL_MUX_SINGLE,
+ XADC_EXTERNAL_MUX_DUAL,
+};
+
+struct xadc {
+ void __iomem *base;
+ struct clk *clk;
+
+ const struct xadc_ops *ops;
+
+ uint16_t threshold[16];
+ uint16_t temp_hysteresis;
+ unsigned int alarm_mask;
+
+ uint16_t *data;
+
+ struct iio_trigger *trigger;
+ struct iio_trigger *convst_trigger;
+ struct iio_trigger *samplerate_trigger;
+
+ enum xadc_external_mux_mode external_mux_mode;
+
+ unsigned int zynq_masked_alarm;
+ unsigned int zynq_intmask;
+ struct delayed_work zynq_unmask_work;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ struct completion completion;
+};
+
+enum xadc_type {
+ XADC_TYPE_S7, /* Series 7 */
+ XADC_TYPE_US, /* UltraScale and UltraScale+ */
+};
+
+struct xadc_ops {
+ int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val);
+ int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val);
+ int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev,
+ int irq);
+ void (*update_alarm)(struct xadc *xadc, unsigned int alarm);
+ unsigned long (*get_dclk_rate)(struct xadc *xadc);
+ irqreturn_t (*interrupt_handler)(int irq, void *devid);
+
+ unsigned int flags;
+ enum xadc_type type;
+ int temp_scale;
+ int temp_offset;
+};
+
+static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t *val)
+{
+ lockdep_assert_held(&xadc->mutex);
+ return xadc->ops->read(xadc, reg, val);
+}
+
+static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t val)
+{
+ lockdep_assert_held(&xadc->mutex);
+ return xadc->ops->write(xadc, reg, val);
+}
+
+static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t *val)
+{
+ int ret;
+
+ mutex_lock(&xadc->mutex);
+ ret = _xadc_read_adc_reg(xadc, reg, val);
+ mutex_unlock(&xadc->mutex);
+ return ret;
+}
+
+static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
+ uint16_t val)
+{
+ int ret;
+
+ mutex_lock(&xadc->mutex);
+ ret = _xadc_write_adc_reg(xadc, reg, val);
+ mutex_unlock(&xadc->mutex);
+ return ret;
+}
+
+/* XADC hardmacro register definitions */
+#define XADC_REG_TEMP 0x00
+#define XADC_REG_VCCINT 0x01
+#define XADC_REG_VCCAUX 0x02
+#define XADC_REG_VPVN 0x03
+#define XADC_REG_VREFP 0x04
+#define XADC_REG_VREFN 0x05
+#define XADC_REG_VCCBRAM 0x06
+
+#define XADC_REG_VCCPINT 0x0d
+#define XADC_REG_VCCPAUX 0x0e
+#define XADC_REG_VCCO_DDR 0x0f
+#define XADC_REG_VAUX(x) (0x10 + (x))
+
+#define XADC_REG_MAX_TEMP 0x20
+#define XADC_REG_MAX_VCCINT 0x21
+#define XADC_REG_MAX_VCCAUX 0x22
+#define XADC_REG_MAX_VCCBRAM 0x23
+#define XADC_REG_MIN_TEMP 0x24
+#define XADC_REG_MIN_VCCINT 0x25
+#define XADC_REG_MIN_VCCAUX 0x26
+#define XADC_REG_MIN_VCCBRAM 0x27
+#define XADC_REG_MAX_VCCPINT 0x28
+#define XADC_REG_MAX_VCCPAUX 0x29
+#define XADC_REG_MAX_VCCO_DDR 0x2a
+#define XADC_REG_MIN_VCCPINT 0x2c
+#define XADC_REG_MIN_VCCPAUX 0x2d
+#define XADC_REG_MIN_VCCO_DDR 0x2e
+
+#define XADC_REG_CONF0 0x40
+#define XADC_REG_CONF1 0x41
+#define XADC_REG_CONF2 0x42
+#define XADC_REG_SEQ(x) (0x48 + (x))
+#define XADC_REG_INPUT_MODE(x) (0x4c + (x))
+#define XADC_REG_THRESHOLD(x) (0x50 + (x))
+
+#define XADC_REG_FLAG 0x3f
+
+#define XADC_CONF0_EC BIT(9)
+#define XADC_CONF0_ACQ BIT(8)
+#define XADC_CONF0_MUX BIT(11)
+#define XADC_CONF0_CHAN(x) (x)
+
+#define XADC_CONF1_SEQ_MASK (0xf << 12)
+#define XADC_CONF1_SEQ_DEFAULT (0 << 12)
+#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12)
+#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12)
+#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12)
+#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12)
+#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12)
+#define XADC_CONF1_ALARM_MASK 0x0f0f
+
+#define XADC_CONF2_DIV_MASK 0xff00
+#define XADC_CONF2_DIV_OFFSET 8
+
+#define XADC_CONF2_PD_MASK (0x3 << 4)
+#define XADC_CONF2_PD_NONE (0x0 << 4)
+#define XADC_CONF2_PD_ADC_B (0x2 << 4)
+#define XADC_CONF2_PD_BOTH (0x3 << 4)
+
+#define XADC_ALARM_TEMP_MASK BIT(0)
+#define XADC_ALARM_VCCINT_MASK BIT(1)
+#define XADC_ALARM_VCCAUX_MASK BIT(2)
+#define XADC_ALARM_OT_MASK BIT(3)
+#define XADC_ALARM_VCCBRAM_MASK BIT(4)
+#define XADC_ALARM_VCCPINT_MASK BIT(5)
+#define XADC_ALARM_VCCPAUX_MASK BIT(6)
+#define XADC_ALARM_VCCODDR_MASK BIT(7)
+
+#define XADC_THRESHOLD_TEMP_MAX 0x0
+#define XADC_THRESHOLD_VCCINT_MAX 0x1
+#define XADC_THRESHOLD_VCCAUX_MAX 0x2
+#define XADC_THRESHOLD_OT_MAX 0x3
+#define XADC_THRESHOLD_TEMP_MIN 0x4
+#define XADC_THRESHOLD_VCCINT_MIN 0x5
+#define XADC_THRESHOLD_VCCAUX_MIN 0x6
+#define XADC_THRESHOLD_OT_MIN 0x7
+#define XADC_THRESHOLD_VCCBRAM_MAX 0x8
+#define XADC_THRESHOLD_VCCPINT_MAX 0x9
+#define XADC_THRESHOLD_VCCPAUX_MAX 0xa
+#define XADC_THRESHOLD_VCCODDR_MAX 0xb
+#define XADC_THRESHOLD_VCCBRAM_MIN 0xc
+#define XADC_THRESHOLD_VCCPINT_MIN 0xd
+#define XADC_THRESHOLD_VCCPAUX_MIN 0xe
+#define XADC_THRESHOLD_VCCODDR_MIN 0xf
+
+#endif