summaryrefslogtreecommitdiff
path: root/drivers/iio/adc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-25 09:38:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-25 09:38:50 -0700
commit93e4b3076b5f2d853462b9777d083c77fc0b7b23 (patch)
tree9e4eb974354d490ba5b74f05dfb1a3f5e4dc5469 /drivers/iio/adc
parent5f5ef9c407cfdc524a1aaeb4196d933f61ecf21a (diff)
parent8992f32c57607bdfaf5de2a2cd26b3b71f3a9d55 (diff)
downloadlinux-next-93e4b3076b5f2d853462b9777d083c77fc0b7b23.tar.gz
linux-next-93e4b3076b5f2d853462b9777d083c77fc0b7b23.zip
Merge tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc/IIO/etc driver updates from Greg KH: "Here is the big set of char, misc, iio, counter, fpga, and other small driver subsystems for 7.3-rc1. Overall, due to some driver removals we only added a bit more code than removed, which was a nice change. Highlights in this merge request are: - Loads of IIO driver updates and additions - binder driver updates (more on that below...) - Removal of the SGI XP and GRU drivers as they are not used anymore and turn out to be pretty insecure overall - Removal of the obsolete ibmasm driver as it's not being used anymore - Coresight driver updates and additions - Mei driver udpates - Counter driver updates - FPGA driver updates - ICC driver updates - lots and lots of other tiny driver updates to resolve reported issues All of these have been in linux-next for a while" * tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (513 commits) iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF iio: adc: pac1921: fix wrong channel used in trigger handler read iio: light: gp2ap002: re-enable irq if runtime suspend fails iio: light: gp2ap002: Fix unbalanced runtime PM on repeated event writes iio: light: apds9306: fix PM reference leak in apds9306_read_data() iio: gyro: mpu3050: fix sign of raw angular velocity readings iio: srf04: fix pm_runtime handling on probe error path iio: adc: ad4080: configure backend data size iio: adc: adi-axi-adc: add data size support for AD408X backend iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable iio: dac: ad5446: fix OF module device table iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask iio: light: opt4001: Reject integration times with a non-zero seconds part iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem() iio: light: opt4001: Fix power down clearing bits of the wrong register iio: light: opt4060: Fix incorrect register name in threshold read error message iio: light: opt4060: Fix pointer type passed to div_u64_rem() iio: light: opt4060: Reject integration times with a non-zero seconds part iio: light: ltrf216a: fix runtime PM reference leak in error path iio: pressure: dps310: fix NULL pointer dereference on ACPI probe ...
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/88pm886-gpadc.c2
-rw-r--r--drivers/iio/adc/Kconfig80
-rw-r--r--drivers/iio/adc/Makefile6
-rw-r--r--drivers/iio/adc/ad4000.c62
-rw-r--r--drivers/iio/adc/ad4030.c14
-rw-r--r--drivers/iio/adc/ad4080.c45
-rw-r--r--drivers/iio/adc/ad4130.c12
-rw-r--r--drivers/iio/adc/ad4134.c2
-rw-r--r--drivers/iio/adc/ad4170-4.c6
-rw-r--r--drivers/iio/adc/ad4851.c18
-rw-r--r--drivers/iio/adc/ad7091r8.c6
-rw-r--r--drivers/iio/adc/ad7124.c4
-rw-r--r--drivers/iio/adc/ad7173.c26
-rw-r--r--drivers/iio/adc/ad7191.c2
-rw-r--r--drivers/iio/adc/ad7192.c10
-rw-r--r--drivers/iio/adc/ad7266.c4
-rw-r--r--drivers/iio/adc/ad7280a.c2
-rw-r--r--drivers/iio/adc/ad7292.c2
-rw-r--r--drivers/iio/adc/ad7298.c2
-rw-r--r--drivers/iio/adc/ad7380.c36
-rw-r--r--drivers/iio/adc/ad7476.c60
-rw-r--r--drivers/iio/adc/ad7606_spi.c22
-rw-r--r--drivers/iio/adc/ad7766.c12
-rw-r--r--drivers/iio/adc/ad7768-1.c8
-rw-r--r--drivers/iio/adc/ad7779.c6
-rw-r--r--drivers/iio/adc/ad7780.c8
-rw-r--r--drivers/iio/adc/ad7791.c10
-rw-r--r--drivers/iio/adc/ad7793.c18
-rw-r--r--drivers/iio/adc/ad7887.c2
-rw-r--r--drivers/iio/adc/ad7923.c14
-rw-r--r--drivers/iio/adc/ad7944.c6
-rw-r--r--drivers/iio/adc/ad7949.c6
-rw-r--r--drivers/iio/adc/ad9467.c14
-rw-r--r--drivers/iio/adc/ade9000.c4
-rw-r--r--drivers/iio/adc/adi-axi-adc.c30
-rw-r--r--drivers/iio/adc/at91_adc.c4
-rw-r--r--drivers/iio/adc/envelope-detector.c1
-rw-r--r--drivers/iio/adc/hi8435.c2
-rw-r--r--drivers/iio/adc/hx711.c98
-rw-r--r--drivers/iio/adc/ltc2378.c857
-rw-r--r--drivers/iio/adc/max1027.c12
-rw-r--r--drivers/iio/adc/max1118.c6
-rw-r--r--drivers/iio/adc/max11205.c4
-rw-r--r--drivers/iio/adc/max11410.c2
-rw-r--r--drivers/iio/adc/max1241.c9
-rw-r--r--drivers/iio/adc/max14001.c4
-rw-r--r--drivers/iio/adc/mcp320x.c26
-rw-r--r--drivers/iio/adc/mcp3564.c24
-rw-r--r--drivers/iio/adc/mcp3911.c14
-rw-r--r--drivers/iio/adc/mt6323-auxadc.c313
-rw-r--r--drivers/iio/adc/pac1921.c2
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c14
-rw-r--r--drivers/iio/adc/rohm-bd79112.c2
-rw-r--r--drivers/iio/adc/rtq6056.c4
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c12
-rw-r--r--drivers/iio/adc/ti-adc081c.c6
-rw-r--r--drivers/iio/adc/ti-adc0832.c8
-rw-r--r--drivers/iio/adc/ti-adc084s021.c8
-rw-r--r--drivers/iio/adc/ti-adc108s102.c2
-rw-r--r--drivers/iio/adc/ti-adc12138.c6
-rw-r--r--drivers/iio/adc/ti-adc128s052.c24
-rw-r--r--drivers/iio/adc/ti-adc161s626.c4
-rw-r--r--drivers/iio/adc/ti-ads1015.c44
-rw-r--r--drivers/iio/adc/ti-ads1018.c4
-rw-r--r--drivers/iio/adc/ti-ads112c14.c1454
-rw-r--r--drivers/iio/adc/ti-ads124s08.c4
-rw-r--r--drivers/iio/adc/ti-ads1298.c2
-rw-r--r--drivers/iio/adc/ti-ads131e08.c6
-rw-r--r--drivers/iio/adc/ti-ads131m02.c10
-rw-r--r--drivers/iio/adc/ti-ads7138.c42
-rw-r--r--drivers/iio/adc/ti-ads7950.c24
-rw-r--r--drivers/iio/adc/ti-ads8688.c4
-rw-r--r--drivers/iio/adc/ti-lmp92064.c2
-rw-r--r--drivers/iio/adc/ti-tlc4541.c4
-rw-r--r--drivers/iio/adc/ti-tsc2046.c25
-rw-r--r--drivers/iio/adc/versal-sysmon-core.c1052
-rw-r--r--drivers/iio/adc/versal-sysmon-i2c.c134
-rw-r--r--drivers/iio/adc/versal-sysmon.c92
-rw-r--r--drivers/iio/adc/versal-sysmon.h120
79 files changed, 4619 insertions, 433 deletions
diff --git a/drivers/iio/adc/88pm886-gpadc.c b/drivers/iio/adc/88pm886-gpadc.c
index ff9bc5f06c18..938ba43c2c35 100644
--- a/drivers/iio/adc/88pm886-gpadc.c
+++ b/drivers/iio/adc/88pm886-gpadc.c
@@ -279,7 +279,7 @@ static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const
static int pm886_gpadc_hw_enable(struct regmap *map)
{
- const u8 config[] = {
+ static const u8 config[] = {
PM886_GPADC_CONFIG1_EN_ALL,
PM886_GPADC_CONFIG2_EN_ALL,
PM886_GPADC_GND_DET2_EN,
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3755a81c1efd..415e519ad4eb 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -33,7 +33,8 @@ config AB8500_GPADC
and USB voltages integral to the U8500 platform.
config AD_SIGMA_DELTA
- tristate
+ tristate "Analog Devices Sigma-Delta Modulator support" if COMPILE_TEST
+ depends on SPI
select IIO_BUFFER
select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
@@ -945,6 +946,25 @@ config LTC2309
This driver can also be built as a module. If so, the module will
be called ltc2309.
+config LTC2378
+ tristate "Analog Devices LTC2378 ADC driver"
+ depends on SPI
+ depends on REGULATOR || COMPILE_TEST
+ depends on GPIOLIB
+ depends on PWM
+ select IIO_BUFFER
+ select IIO_BUFFER_DMA
+ select IIO_BUFFER_DMAENGINE
+ select IIO_TRIGGERED_BUFFER
+ select SPI_OFFLOAD
+ select SPI_OFFLOAD_TRIGGER_PWM
+ help
+ Say yes here to build support for Analog Devices LTC2378-20 and
+ similar analog to digital converters.
+
+ This driver can also be built as a module. If so, the module will
+ be called ltc2378.
+
config LTC2471
tristate "Linear Technology LTC2471 and LTC2473 ADC driver"
depends on I2C
@@ -1076,6 +1096,7 @@ config MAX1363
config MAX14001
tristate "Analog Devices MAX14001/MAX14002 ADC driver"
depends on SPI
+ select REGMAP
help
Say yes here to build support for Analog Devices MAX14001/MAX14002
Configurable, Isolated 10-bit ADCs for Multi-Range Binary Inputs.
@@ -1086,6 +1107,7 @@ config MAX14001
config MAX34408
tristate "Maxim max34408/max344089 ADC driver"
depends on I2C
+ select REGMAP_I2C
help
Say yes here to build support for Maxim max34408/max34409 current sense
monitor with 8-bits ADC interface with overcurrent delay/threshold and
@@ -1165,6 +1187,17 @@ config MCP3911
This driver can also be built as a module. If so, the module will be
called mcp3911.
+config MEDIATEK_MT6323_AUXADC
+ tristate "MediaTek MT6323 PMIC AUXADC driver"
+ depends on MFD_MT6397
+ help
+ Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC.
+ This driver provides multiple channels for system monitoring,
+ such as battery voltage, PMIC temperature, and others.
+
+ This driver can also be built as a module. If so, the module will be
+ called mt6323-auxadc.
+
config MEDIATEK_MT6359_AUXADC
tristate "MediaTek MT6359 PMIC AUXADC driver"
depends on MFD_MT6397
@@ -1789,6 +1822,18 @@ config TI_ADS1119
This driver can also be built as a module. If so, the module will be
called ti-ads1119.
+config TI_ADS112C14
+ tristate "Texas Instruments ADS112C14/ADS122C14"
+ depends on I2C
+ select CRC8
+ select REGMAP
+ help
+ If you say yes here you get support for Texas Instruments ADS112C14,
+ ADS122C14 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads112c14.
+
config TI_ADS124S08
tristate "Texas Instruments ADS124S08"
depends on SPI
@@ -1964,6 +2009,39 @@ config TWL6030_GPADC
This driver can also be built as a module. If so, the module will be
called twl6030-gpadc.
+config VERSAL_SYSMON_CORE
+ tristate
+ select REGMAP
+
+config VERSAL_SYSMON
+ tristate "AMD Versal SysMon driver"
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ depends on HAS_IOMEM
+ select VERSAL_SYSMON_CORE
+ help
+ Say yes here to have support for the AMD/Xilinx Versal System
+ Monitor (SysMon). This driver provides voltage and temperature
+ monitoring through the IIO subsystem.
+
+ The SysMon measures up to 160 supply voltages and reads up to
+ 64 temperature satellites distributed across the SoC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called versal-sysmon.
+
+config VERSAL_SYSMON_I2C
+ tristate "AMD Versal SysMon I2C driver"
+ depends on I2C
+ select VERSAL_SYSMON_CORE
+ help
+ Say yes here to have support for the AMD/Xilinx Versal System
+ Monitor (SysMon) via I2C interface. This driver enables voltage
+ and temperature monitoring when the Versal chip has SysMon
+ configured with I2C access.
+
+ To compile this driver as a module, choose M here: the module
+ will be called versal-sysmon-i2c.
+
config VF610_ADC
tristate "Freescale vf610 ADC driver"
depends on HAS_IOMEM
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 707dd708912f..dcec0abb03b7 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
obj-$(CONFIG_LTC2309) += ltc2309.o
+obj-$(CONFIG_LTC2378) += ltc2378.o
obj-$(CONFIG_LTC2471) += ltc2471.o
obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
@@ -100,6 +101,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MCP3564) += mcp3564.o
obj-$(CONFIG_MCP3911) += mcp3911.o
+obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o
obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o
obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o
@@ -153,6 +155,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
+obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
@@ -168,6 +171,9 @@ 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_VERSAL_SYSMON_CORE) += versal-sysmon-core.o
+obj-$(CONFIG_VERSAL_SYSMON) += versal-sysmon.o
+obj-$(CONFIG_VERSAL_SYSMON_I2C) += versal-sysmon-i2c.o
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c
index c2f6f5f9812c..a33896b2cf97 100644
--- a/drivers/iio/adc/ad4000.c
+++ b/drivers/iio/adc/ad4000.c
@@ -1176,37 +1176,37 @@ static int ad4000_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4000_id[] = {
- { "ad4000", (kernel_ulong_t)&ad4000_chip_info },
- { "ad4001", (kernel_ulong_t)&ad4001_chip_info },
- { "ad4002", (kernel_ulong_t)&ad4002_chip_info },
- { "ad4003", (kernel_ulong_t)&ad4003_chip_info },
- { "ad4004", (kernel_ulong_t)&ad4004_chip_info },
- { "ad4005", (kernel_ulong_t)&ad4005_chip_info },
- { "ad4006", (kernel_ulong_t)&ad4006_chip_info },
- { "ad4007", (kernel_ulong_t)&ad4007_chip_info },
- { "ad4008", (kernel_ulong_t)&ad4008_chip_info },
- { "ad4010", (kernel_ulong_t)&ad4010_chip_info },
- { "ad4011", (kernel_ulong_t)&ad4011_chip_info },
- { "ad4020", (kernel_ulong_t)&ad4020_chip_info },
- { "ad4021", (kernel_ulong_t)&ad4021_chip_info },
- { "ad4022", (kernel_ulong_t)&ad4022_chip_info },
- { "adaq4001", (kernel_ulong_t)&adaq4001_chip_info },
- { "adaq4003", (kernel_ulong_t)&adaq4003_chip_info },
- { "ad7685", (kernel_ulong_t)&ad7685_chip_info },
- { "ad7686", (kernel_ulong_t)&ad7686_chip_info },
- { "ad7687", (kernel_ulong_t)&ad7687_chip_info },
- { "ad7688", (kernel_ulong_t)&ad7688_chip_info },
- { "ad7690", (kernel_ulong_t)&ad7690_chip_info },
- { "ad7691", (kernel_ulong_t)&ad7691_chip_info },
- { "ad7693", (kernel_ulong_t)&ad7693_chip_info },
- { "ad7942", (kernel_ulong_t)&ad7942_chip_info },
- { "ad7946", (kernel_ulong_t)&ad7946_chip_info },
- { "ad7980", (kernel_ulong_t)&ad7980_chip_info },
- { "ad7982", (kernel_ulong_t)&ad7982_chip_info },
- { "ad7983", (kernel_ulong_t)&ad7983_chip_info },
- { "ad7984", (kernel_ulong_t)&ad7984_chip_info },
- { "ad7988-1", (kernel_ulong_t)&ad7988_1_chip_info },
- { "ad7988-5", (kernel_ulong_t)&ad7988_5_chip_info },
+ { .name = "ad4000", .driver_data = (kernel_ulong_t)&ad4000_chip_info },
+ { .name = "ad4001", .driver_data = (kernel_ulong_t)&ad4001_chip_info },
+ { .name = "ad4002", .driver_data = (kernel_ulong_t)&ad4002_chip_info },
+ { .name = "ad4003", .driver_data = (kernel_ulong_t)&ad4003_chip_info },
+ { .name = "ad4004", .driver_data = (kernel_ulong_t)&ad4004_chip_info },
+ { .name = "ad4005", .driver_data = (kernel_ulong_t)&ad4005_chip_info },
+ { .name = "ad4006", .driver_data = (kernel_ulong_t)&ad4006_chip_info },
+ { .name = "ad4007", .driver_data = (kernel_ulong_t)&ad4007_chip_info },
+ { .name = "ad4008", .driver_data = (kernel_ulong_t)&ad4008_chip_info },
+ { .name = "ad4010", .driver_data = (kernel_ulong_t)&ad4010_chip_info },
+ { .name = "ad4011", .driver_data = (kernel_ulong_t)&ad4011_chip_info },
+ { .name = "ad4020", .driver_data = (kernel_ulong_t)&ad4020_chip_info },
+ { .name = "ad4021", .driver_data = (kernel_ulong_t)&ad4021_chip_info },
+ { .name = "ad4022", .driver_data = (kernel_ulong_t)&ad4022_chip_info },
+ { .name = "adaq4001", .driver_data = (kernel_ulong_t)&adaq4001_chip_info },
+ { .name = "adaq4003", .driver_data = (kernel_ulong_t)&adaq4003_chip_info },
+ { .name = "ad7685", .driver_data = (kernel_ulong_t)&ad7685_chip_info },
+ { .name = "ad7686", .driver_data = (kernel_ulong_t)&ad7686_chip_info },
+ { .name = "ad7687", .driver_data = (kernel_ulong_t)&ad7687_chip_info },
+ { .name = "ad7688", .driver_data = (kernel_ulong_t)&ad7688_chip_info },
+ { .name = "ad7690", .driver_data = (kernel_ulong_t)&ad7690_chip_info },
+ { .name = "ad7691", .driver_data = (kernel_ulong_t)&ad7691_chip_info },
+ { .name = "ad7693", .driver_data = (kernel_ulong_t)&ad7693_chip_info },
+ { .name = "ad7942", .driver_data = (kernel_ulong_t)&ad7942_chip_info },
+ { .name = "ad7946", .driver_data = (kernel_ulong_t)&ad7946_chip_info },
+ { .name = "ad7980", .driver_data = (kernel_ulong_t)&ad7980_chip_info },
+ { .name = "ad7982", .driver_data = (kernel_ulong_t)&ad7982_chip_info },
+ { .name = "ad7983", .driver_data = (kernel_ulong_t)&ad7983_chip_info },
+ { .name = "ad7984", .driver_data = (kernel_ulong_t)&ad7984_chip_info },
+ { .name = "ad7988-1", .driver_data = (kernel_ulong_t)&ad7988_1_chip_info },
+ { .name = "ad7988-5", .driver_data = (kernel_ulong_t)&ad7988_5_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4000_id);
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index 9c5f19321e3b..e97400a1aa48 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -1763,13 +1763,13 @@ static const struct ad4030_chip_info adaq4224_chip_info = {
};
static const struct spi_device_id ad4030_id_table[] = {
- { "ad4030-24", (kernel_ulong_t)&ad4030_24_chip_info },
- { "ad4630-16", (kernel_ulong_t)&ad4630_16_chip_info },
- { "ad4630-24", (kernel_ulong_t)&ad4630_24_chip_info },
- { "ad4632-16", (kernel_ulong_t)&ad4632_16_chip_info },
- { "ad4632-24", (kernel_ulong_t)&ad4632_24_chip_info },
- { "adaq4216", (kernel_ulong_t)&adaq4216_chip_info },
- { "adaq4224", (kernel_ulong_t)&adaq4224_chip_info },
+ { .name = "ad4030-24", .driver_data = (kernel_ulong_t)&ad4030_24_chip_info },
+ { .name = "ad4630-16", .driver_data = (kernel_ulong_t)&ad4630_16_chip_info },
+ { .name = "ad4630-24", .driver_data = (kernel_ulong_t)&ad4630_24_chip_info },
+ { .name = "ad4632-16", .driver_data = (kernel_ulong_t)&ad4632_16_chip_info },
+ { .name = "ad4632-24", .driver_data = (kernel_ulong_t)&ad4632_24_chip_info },
+ { .name = "adaq4216", .driver_data = (kernel_ulong_t)&adaq4216_chip_info },
+ { .name = "adaq4224", .driver_data = (kernel_ulong_t)&adaq4224_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4030_id_table);
diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
index 0797d64aeaec..04cd6628ebff 100644
--- a/drivers/iio/adc/ad4080.c
+++ b/drivers/iio/adc/ad4080.c
@@ -135,6 +135,7 @@
#define AD4087_CHIP_ID 0x0057
#define AD4088_CHIP_ID 0x0058
#define AD4880_CHIP_ID 0x0059
+#define AD4883_CHIP_ID 0x005B
#define AD4884_CHIP_ID 0x005C
#define AD4080_MAX_CHANNELS 2
@@ -541,6 +542,11 @@ static const struct iio_chan_spec ad4880_channels[] = {
AD4880_CHANNEL_DEFINE(20, 32, 1),
};
+static const struct iio_chan_spec ad4883_channels[] = {
+ AD4880_CHANNEL_DEFINE(16, 16, 0),
+ AD4880_CHANNEL_DEFINE(16, 16, 1),
+};
+
static const struct iio_chan_spec ad4884_channels[] = {
AD4880_CHANNEL_DEFINE(16, 16, 0),
AD4880_CHANNEL_DEFINE(16, 16, 1),
@@ -646,6 +652,16 @@ static const struct ad4080_chip_info ad4880_chip_info = {
.lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX,
};
+static const struct ad4080_chip_info ad4883_chip_info = {
+ .name = "ad4883",
+ .product_id = AD4883_CHIP_ID,
+ .scale_table = ad4080_scale_table,
+ .num_scales = ARRAY_SIZE(ad4080_scale_table),
+ .num_channels = 2,
+ .channels = ad4883_channels,
+ .lvds_cnv_clk_cnt_max = 5,
+};
+
static const struct ad4080_chip_info ad4884_chip_info = {
.name = "ad4884",
.product_id = AD4884_CHIP_ID,
@@ -697,6 +713,11 @@ static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch)
if (ret)
return ret;
+ ret = iio_backend_data_size_set(st->back[ch],
+ st->info->channels[0].scan_type.realbits);
+ if (ret)
+ return ret;
+
if (!st->lvds_cnv_en)
return 0;
@@ -848,17 +869,18 @@ static int ad4080_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4080_id[] = {
- { "ad4080", (kernel_ulong_t)&ad4080_chip_info },
- { "ad4081", (kernel_ulong_t)&ad4081_chip_info },
- { "ad4082", (kernel_ulong_t)&ad4082_chip_info },
- { "ad4083", (kernel_ulong_t)&ad4083_chip_info },
- { "ad4084", (kernel_ulong_t)&ad4084_chip_info },
- { "ad4085", (kernel_ulong_t)&ad4085_chip_info },
- { "ad4086", (kernel_ulong_t)&ad4086_chip_info },
- { "ad4087", (kernel_ulong_t)&ad4087_chip_info },
- { "ad4088", (kernel_ulong_t)&ad4088_chip_info },
- { "ad4880", (kernel_ulong_t)&ad4880_chip_info },
- { "ad4884", (kernel_ulong_t)&ad4884_chip_info },
+ { .name = "ad4080", .driver_data = (kernel_ulong_t)&ad4080_chip_info },
+ { .name = "ad4081", .driver_data = (kernel_ulong_t)&ad4081_chip_info },
+ { .name = "ad4082", .driver_data = (kernel_ulong_t)&ad4082_chip_info },
+ { .name = "ad4083", .driver_data = (kernel_ulong_t)&ad4083_chip_info },
+ { .name = "ad4084", .driver_data = (kernel_ulong_t)&ad4084_chip_info },
+ { .name = "ad4085", .driver_data = (kernel_ulong_t)&ad4085_chip_info },
+ { .name = "ad4086", .driver_data = (kernel_ulong_t)&ad4086_chip_info },
+ { .name = "ad4087", .driver_data = (kernel_ulong_t)&ad4087_chip_info },
+ { .name = "ad4088", .driver_data = (kernel_ulong_t)&ad4088_chip_info },
+ { .name = "ad4880", .driver_data = (kernel_ulong_t)&ad4880_chip_info },
+ { .name = "ad4883", .driver_data = (kernel_ulong_t)&ad4883_chip_info },
+ { .name = "ad4884", .driver_data = (kernel_ulong_t)&ad4884_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4080_id);
@@ -874,6 +896,7 @@ static const struct of_device_id ad4080_of_match[] = {
{ .compatible = "adi,ad4087", &ad4087_chip_info },
{ .compatible = "adi,ad4088", &ad4088_chip_info },
{ .compatible = "adi,ad4880", &ad4880_chip_info },
+ { .compatible = "adi,ad4883", &ad4883_chip_info },
{ .compatible = "adi,ad4884", &ad4884_chip_info },
{ }
};
diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c
index 7f39f3484062..3ffc4b765e0e 100644
--- a/drivers/iio/adc/ad4130.c
+++ b/drivers/iio/adc/ad4130.c
@@ -2425,12 +2425,12 @@ static const struct of_device_id ad4130_of_match[] = {
MODULE_DEVICE_TABLE(of, ad4130_of_match);
static const struct spi_device_id ad4130_id_table[] = {
- { "ad4129-4", (kernel_ulong_t)&ad4129_4_chip_info },
- { "ad4129-8", (kernel_ulong_t)&ad4129_8_chip_info },
- { "ad4130-4", (kernel_ulong_t)&ad4130_4_chip_info },
- { "ad4130", (kernel_ulong_t)&ad4130_8_chip_info },
- { "ad4131-4", (kernel_ulong_t)&ad4131_4_chip_info },
- { "ad4131-8", (kernel_ulong_t)&ad4131_8_chip_info },
+ { .name = "ad4129-4", .driver_data = (kernel_ulong_t)&ad4129_4_chip_info },
+ { .name = "ad4129-8", .driver_data = (kernel_ulong_t)&ad4129_8_chip_info },
+ { .name = "ad4130-4", .driver_data = (kernel_ulong_t)&ad4130_4_chip_info },
+ { .name = "ad4130", .driver_data = (kernel_ulong_t)&ad4130_8_chip_info },
+ { .name = "ad4131-4", .driver_data = (kernel_ulong_t)&ad4131_4_chip_info },
+ { .name = "ad4131-8", .driver_data = (kernel_ulong_t)&ad4131_8_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4130_id_table);
diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c
index cb9d74316f6a..0490218bb0e9 100644
--- a/drivers/iio/adc/ad4134.c
+++ b/drivers/iio/adc/ad4134.c
@@ -472,7 +472,7 @@ static int ad4134_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4134_id[] = {
- { "ad4134" },
+ { .name = "ad4134" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4134_id);
diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
index 627cbf5a37b0..f7757b3a931c 100644
--- a/drivers/iio/adc/ad4170-4.c
+++ b/drivers/iio/adc/ad4170-4.c
@@ -2979,9 +2979,9 @@ static int ad4170_probe(struct spi_device *spi)
}
static const struct spi_device_id ad4170_id_table[] = {
- { "ad4170-4", (kernel_ulong_t)&ad4170_chip_info },
- { "ad4190-4", (kernel_ulong_t)&ad4190_chip_info },
- { "ad4195-4", (kernel_ulong_t)&ad4195_chip_info },
+ { .name = "ad4170-4", .driver_data = (kernel_ulong_t)&ad4170_chip_info },
+ { .name = "ad4190-4", .driver_data = (kernel_ulong_t)&ad4190_chip_info },
+ { .name = "ad4195-4", .driver_data = (kernel_ulong_t)&ad4195_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4170_id_table);
diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c
index 940b042d743a..480d1798f17b 100644
--- a/drivers/iio/adc/ad4851.c
+++ b/drivers/iio/adc/ad4851.c
@@ -1285,15 +1285,15 @@ static const struct of_device_id ad4851_of_match[] = {
};
static const struct spi_device_id ad4851_spi_id[] = {
- { "ad4851", (kernel_ulong_t)&ad4851_info },
- { "ad4852", (kernel_ulong_t)&ad4852_info },
- { "ad4853", (kernel_ulong_t)&ad4853_info },
- { "ad4854", (kernel_ulong_t)&ad4854_info },
- { "ad4855", (kernel_ulong_t)&ad4855_info },
- { "ad4856", (kernel_ulong_t)&ad4856_info },
- { "ad4857", (kernel_ulong_t)&ad4857_info },
- { "ad4858", (kernel_ulong_t)&ad4858_info },
- { "ad4858i", (kernel_ulong_t)&ad4858i_info },
+ { .name = "ad4851", .driver_data = (kernel_ulong_t)&ad4851_info },
+ { .name = "ad4852", .driver_data = (kernel_ulong_t)&ad4852_info },
+ { .name = "ad4853", .driver_data = (kernel_ulong_t)&ad4853_info },
+ { .name = "ad4854", .driver_data = (kernel_ulong_t)&ad4854_info },
+ { .name = "ad4855", .driver_data = (kernel_ulong_t)&ad4855_info },
+ { .name = "ad4856", .driver_data = (kernel_ulong_t)&ad4856_info },
+ { .name = "ad4857", .driver_data = (kernel_ulong_t)&ad4857_info },
+ { .name = "ad4858", .driver_data = (kernel_ulong_t)&ad4858_info },
+ { .name = "ad4858i", .driver_data = (kernel_ulong_t)&ad4858i_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4851_spi_id);
diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c
index e93b8bb60e8e..465a8ba01a22 100644
--- a/drivers/iio/adc/ad7091r8.c
+++ b/drivers/iio/adc/ad7091r8.c
@@ -249,9 +249,9 @@ static const struct of_device_id ad7091r8_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7091r8_of_match);
static const struct spi_device_id ad7091r8_spi_id[] = {
- { "ad7091r2", (kernel_ulong_t)&ad7091r2_init_info },
- { "ad7091r4", (kernel_ulong_t)&ad7091r4_init_info },
- { "ad7091r8", (kernel_ulong_t)&ad7091r8_init_info },
+ { .name = "ad7091r2", .driver_data = (kernel_ulong_t)&ad7091r2_init_info },
+ { .name = "ad7091r4", .driver_data = (kernel_ulong_t)&ad7091r4_init_info },
+ { .name = "ad7091r8", .driver_data = (kernel_ulong_t)&ad7091r8_init_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7091r8_spi_id);
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 19058d081418..fc5dbc624856 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -1683,8 +1683,8 @@ static const struct of_device_id ad7124_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7124_of_match);
static const struct spi_device_id ad71124_ids[] = {
- { "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info },
- { "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info },
+ { .name = "ad7124-4", .driver_data = (kernel_ulong_t)&ad7124_4_chip_info },
+ { .name = "ad7124-8", .driver_data = (kernel_ulong_t)&ad7124_8_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad71124_ids);
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index f78e758706ed..eb47175a0528 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -2065,19 +2065,19 @@ static const struct of_device_id ad7173_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7173_of_match);
static const struct spi_device_id ad7173_id_table[] = {
- { "ad4111", (kernel_ulong_t)&ad4111_device_info },
- { "ad4112", (kernel_ulong_t)&ad4112_device_info },
- { "ad4113", (kernel_ulong_t)&ad4113_device_info },
- { "ad4114", (kernel_ulong_t)&ad4114_device_info },
- { "ad4115", (kernel_ulong_t)&ad4115_device_info },
- { "ad4116", (kernel_ulong_t)&ad4116_device_info },
- { "ad7172-2", (kernel_ulong_t)&ad7172_2_device_info },
- { "ad7172-4", (kernel_ulong_t)&ad7172_4_device_info },
- { "ad7173-8", (kernel_ulong_t)&ad7173_8_device_info },
- { "ad7175-2", (kernel_ulong_t)&ad7175_2_device_info },
- { "ad7175-8", (kernel_ulong_t)&ad7175_8_device_info },
- { "ad7176-2", (kernel_ulong_t)&ad7176_2_device_info },
- { "ad7177-2", (kernel_ulong_t)&ad7177_2_device_info },
+ { .name = "ad4111", .driver_data = (kernel_ulong_t)&ad4111_device_info },
+ { .name = "ad4112", .driver_data = (kernel_ulong_t)&ad4112_device_info },
+ { .name = "ad4113", .driver_data = (kernel_ulong_t)&ad4113_device_info },
+ { .name = "ad4114", .driver_data = (kernel_ulong_t)&ad4114_device_info },
+ { .name = "ad4115", .driver_data = (kernel_ulong_t)&ad4115_device_info },
+ { .name = "ad4116", .driver_data = (kernel_ulong_t)&ad4116_device_info },
+ { .name = "ad7172-2", .driver_data = (kernel_ulong_t)&ad7172_2_device_info },
+ { .name = "ad7172-4", .driver_data = (kernel_ulong_t)&ad7172_4_device_info },
+ { .name = "ad7173-8", .driver_data = (kernel_ulong_t)&ad7173_8_device_info },
+ { .name = "ad7175-2", .driver_data = (kernel_ulong_t)&ad7175_2_device_info },
+ { .name = "ad7175-8", .driver_data = (kernel_ulong_t)&ad7175_8_device_info },
+ { .name = "ad7176-2", .driver_data = (kernel_ulong_t)&ad7176_2_device_info },
+ { .name = "ad7177-2", .driver_data = (kernel_ulong_t)&ad7177_2_device_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7173_id_table);
diff --git a/drivers/iio/adc/ad7191.c b/drivers/iio/adc/ad7191.c
index 94b172dce866..071d26191b3c 100644
--- a/drivers/iio/adc/ad7191.c
+++ b/drivers/iio/adc/ad7191.c
@@ -535,7 +535,7 @@ static const struct of_device_id ad7191_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7191_of_match);
static const struct spi_device_id ad7191_id_table[] = {
- { "ad7191" },
+ { .name = "ad7191" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7191_id_table);
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index 2cfad0bda752..d8f5886fcf69 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -1448,11 +1448,11 @@ static const struct of_device_id ad7192_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7192_of_match);
static const struct spi_device_id ad7192_ids[] = {
- { "ad7190", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] },
- { "ad7192", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] },
- { "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] },
- { "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] },
- { "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] },
+ { .name = "ad7190", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] },
+ { .name = "ad7192", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] },
+ { .name = "ad7193", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] },
+ { .name = "ad7194", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] },
+ { .name = "ad7195", .driver_data = (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7192_ids);
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 0ef36c249ab8..594a070c3745 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -453,8 +453,8 @@ static int ad7266_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7266_id[] = {
- { "ad7265", 0 },
- { "ad7266", 0 },
+ { .name = "ad7265" },
+ { .name = "ad7266" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7266_id);
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
index 2972e706de92..505fa68ee539 100644
--- a/drivers/iio/adc/ad7280a.c
+++ b/drivers/iio/adc/ad7280a.c
@@ -1082,7 +1082,7 @@ static int ad7280_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7280_id[] = {
- { "ad7280a", 0 },
+ { .name = "ad7280a" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7280_id);
diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c
index 0ba0fbad4f70..1d7bf46b80e2 100644
--- a/drivers/iio/adc/ad7292.c
+++ b/drivers/iio/adc/ad7292.c
@@ -299,7 +299,7 @@ static int ad7292_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7292_id_table[] = {
- { "ad7292", 0 },
+ { .name = "ad7292" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7292_id_table);
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index 5fa76aec360e..c669022b5dec 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -353,7 +353,7 @@ static const struct acpi_device_id ad7298_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids);
static const struct spi_device_id ad7298_id[] = {
- { "ad7298", 0 },
+ { .name = "ad7298" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7298_id);
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 9f77990a03f9..2e90f125a797 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -2116,24 +2116,24 @@ static const struct of_device_id ad7380_of_match_table[] = {
};
static const struct spi_device_id ad7380_id_table[] = {
- { "ad7380", (kernel_ulong_t)&ad7380_chip_info },
- { "ad7381", (kernel_ulong_t)&ad7381_chip_info },
- { "ad7383", (kernel_ulong_t)&ad7383_chip_info },
- { "ad7384", (kernel_ulong_t)&ad7384_chip_info },
- { "ad7386", (kernel_ulong_t)&ad7386_chip_info },
- { "ad7387", (kernel_ulong_t)&ad7387_chip_info },
- { "ad7388", (kernel_ulong_t)&ad7388_chip_info },
- { "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info },
- { "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info },
- { "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info },
- { "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info },
- { "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info },
- { "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info },
- { "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
- { "ad7389-4", (kernel_ulong_t)&ad7389_4_chip_info },
- { "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info },
- { "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info },
- { "adaq4381-4", (kernel_ulong_t)&adaq4381_4_chip_info },
+ { .name = "ad7380", .driver_data = (kernel_ulong_t)&ad7380_chip_info },
+ { .name = "ad7381", .driver_data = (kernel_ulong_t)&ad7381_chip_info },
+ { .name = "ad7383", .driver_data = (kernel_ulong_t)&ad7383_chip_info },
+ { .name = "ad7384", .driver_data = (kernel_ulong_t)&ad7384_chip_info },
+ { .name = "ad7386", .driver_data = (kernel_ulong_t)&ad7386_chip_info },
+ { .name = "ad7387", .driver_data = (kernel_ulong_t)&ad7387_chip_info },
+ { .name = "ad7388", .driver_data = (kernel_ulong_t)&ad7388_chip_info },
+ { .name = "ad7380-4", .driver_data = (kernel_ulong_t)&ad7380_4_chip_info },
+ { .name = "ad7381-4", .driver_data = (kernel_ulong_t)&ad7381_4_chip_info },
+ { .name = "ad7383-4", .driver_data = (kernel_ulong_t)&ad7383_4_chip_info },
+ { .name = "ad7384-4", .driver_data = (kernel_ulong_t)&ad7384_4_chip_info },
+ { .name = "ad7386-4", .driver_data = (kernel_ulong_t)&ad7386_4_chip_info },
+ { .name = "ad7387-4", .driver_data = (kernel_ulong_t)&ad7387_4_chip_info },
+ { .name = "ad7388-4", .driver_data = (kernel_ulong_t)&ad7388_4_chip_info },
+ { .name = "ad7389-4", .driver_data = (kernel_ulong_t)&ad7389_4_chip_info },
+ { .name = "adaq4370-4", .driver_data = (kernel_ulong_t)&adaq4370_4_chip_info },
+ { .name = "adaq4380-4", .driver_data = (kernel_ulong_t)&adaq4380_4_chip_info },
+ { .name = "adaq4381-4", .driver_data = (kernel_ulong_t)&adaq4381_4_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7380_id_table);
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index 21d3f6aae972..c007f453ae18 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -411,42 +411,42 @@ static int ad7476_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7476_id[] = {
- { "ad7091", (kernel_ulong_t)&ad7091_chip_info },
- { "ad7091r", (kernel_ulong_t)&ad7091r_chip_info },
- { "ad7273", (kernel_ulong_t)&ad7273_chip_info },
- { "ad7274", (kernel_ulong_t)&ad7274_chip_info },
- { "ad7276", (kernel_ulong_t)&ad7276_chip_info },
- { "ad7277", (kernel_ulong_t)&ad7277_chip_info },
- { "ad7278", (kernel_ulong_t)&ad7278_chip_info },
- { "ad7466", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7467", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7468", (kernel_ulong_t)&ad7468_chip_info },
- { "ad7475", (kernel_ulong_t)&ad7475_chip_info },
- { "ad7476", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7476a", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7477", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7477a", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7478", (kernel_ulong_t)&ad7468_chip_info },
- { "ad7478a", (kernel_ulong_t)&ad7468_chip_info },
- { "ad7495", (kernel_ulong_t)&ad7495_chip_info },
- { "ad7910", (kernel_ulong_t)&ad7467_chip_info },
- { "ad7920", (kernel_ulong_t)&ad7466_chip_info },
- { "ad7940", (kernel_ulong_t)&ad7940_chip_info },
- { "adc081s", (kernel_ulong_t)&adc081s_chip_info },
- { "adc101s", (kernel_ulong_t)&adc101s_chip_info },
- { "adc121s", (kernel_ulong_t)&adc121s_chip_info },
- { "ads7866", (kernel_ulong_t)&ads7866_chip_info },
- { "ads7867", (kernel_ulong_t)&ads7867_chip_info },
- { "ads7868", (kernel_ulong_t)&ads7868_chip_info },
- { "bd79105", (kernel_ulong_t)&bd79105_chip_info },
+ { .name = "ad7091", .driver_data = (kernel_ulong_t)&ad7091_chip_info },
+ { .name = "ad7091r", .driver_data = (kernel_ulong_t)&ad7091r_chip_info },
+ { .name = "ad7273", .driver_data = (kernel_ulong_t)&ad7273_chip_info },
+ { .name = "ad7274", .driver_data = (kernel_ulong_t)&ad7274_chip_info },
+ { .name = "ad7276", .driver_data = (kernel_ulong_t)&ad7276_chip_info },
+ { .name = "ad7277", .driver_data = (kernel_ulong_t)&ad7277_chip_info },
+ { .name = "ad7278", .driver_data = (kernel_ulong_t)&ad7278_chip_info },
+ { .name = "ad7466", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7467", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7468", .driver_data = (kernel_ulong_t)&ad7468_chip_info },
+ { .name = "ad7475", .driver_data = (kernel_ulong_t)&ad7475_chip_info },
+ { .name = "ad7476", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7476a", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7477", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7477a", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7478", .driver_data = (kernel_ulong_t)&ad7468_chip_info },
+ { .name = "ad7478a", .driver_data = (kernel_ulong_t)&ad7468_chip_info },
+ { .name = "ad7495", .driver_data = (kernel_ulong_t)&ad7495_chip_info },
+ { .name = "ad7910", .driver_data = (kernel_ulong_t)&ad7467_chip_info },
+ { .name = "ad7920", .driver_data = (kernel_ulong_t)&ad7466_chip_info },
+ { .name = "ad7940", .driver_data = (kernel_ulong_t)&ad7940_chip_info },
+ { .name = "adc081s", .driver_data = (kernel_ulong_t)&adc081s_chip_info },
+ { .name = "adc101s", .driver_data = (kernel_ulong_t)&adc101s_chip_info },
+ { .name = "adc121s", .driver_data = (kernel_ulong_t)&adc121s_chip_info },
+ { .name = "ads7866", .driver_data = (kernel_ulong_t)&ads7866_chip_info },
+ { .name = "ads7867", .driver_data = (kernel_ulong_t)&ads7867_chip_info },
+ { .name = "ads7868", .driver_data = (kernel_ulong_t)&ads7868_chip_info },
+ { .name = "bd79105", .driver_data = (kernel_ulong_t)&bd79105_chip_info },
/*
* The ROHM BU79100G is identical to the TI's ADS7866 from the software
* point of view. The binding document mandates the ADS7866 to be
* marked as a fallback for the BU79100G, but we still need the SPI ID
* here to make the module loading work.
*/
- { "bu79100g", (kernel_ulong_t)&ads7866_chip_info },
- { "ltc2314-14", (kernel_ulong_t)&ltc2314_14_chip_info },
+ { .name = "bu79100g", .driver_data = (kernel_ulong_t)&ads7866_chip_info },
+ { .name = "ltc2314-14", .driver_data = (kernel_ulong_t)&ltc2314_14_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index 7e17ccbcedd0..1ebdfb8580ab 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -463,17 +463,17 @@ static int ad7606_spi_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7606_id_table[] = {
- { "ad7605-4", (kernel_ulong_t)&ad7605_4_bus_info },
- { "ad7606-4", (kernel_ulong_t)&ad7606_4_bus_info },
- { "ad7606-6", (kernel_ulong_t)&ad7606_6_bus_info },
- { "ad7606-8", (kernel_ulong_t)&ad7606_8_bus_info },
- { "ad7606b", (kernel_ulong_t)&ad7606b_bus_info },
- { "ad7606c-16", (kernel_ulong_t)&ad7606c_16_bus_info },
- { "ad7606c-18", (kernel_ulong_t)&ad7606c_18_bus_info },
- { "ad7607", (kernel_ulong_t)&ad7607_bus_info },
- { "ad7608", (kernel_ulong_t)&ad7608_bus_info },
- { "ad7609", (kernel_ulong_t)&ad7609_bus_info },
- { "ad7616", (kernel_ulong_t)&ad7616_bus_info },
+ { .name = "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_bus_info },
+ { .name = "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_bus_info },
+ { .name = "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_bus_info },
+ { .name = "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_bus_info },
+ { .name = "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_bus_info },
+ { .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_bus_info },
+ { .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_bus_info },
+ { .name = "ad7607", .driver_data = (kernel_ulong_t)&ad7607_bus_info },
+ { .name = "ad7608", .driver_data = (kernel_ulong_t)&ad7608_bus_info },
+ { .name = "ad7609", .driver_data = (kernel_ulong_t)&ad7609_bus_info },
+ { .name = "ad7616", .driver_data = (kernel_ulong_t)&ad7616_bus_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 9e4a66477d2d..92957196de59 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -285,12 +285,12 @@ static int ad7766_probe(struct spi_device *spi)
}
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 },
+ { .name = "ad7766", .driver_data = ID_AD7766 },
+ { .name = "ad7766-1", .driver_data = ID_AD7766_1 },
+ { .name = "ad7766-2", .driver_data = ID_AD7766_2 },
+ { .name = "ad7767", .driver_data = ID_AD7766 },
+ { .name = "ad7767-1", .driver_data = ID_AD7766_1 },
+ { .name = "ad7767-2", .driver_data = ID_AD7766_2 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7766_id);
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 598936e47fd2..e9060c1bbe6f 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -1918,10 +1918,10 @@ static int ad7768_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7768_id_table[] = {
- { "ad7768-1", (kernel_ulong_t)&ad7768_chip_info },
- { "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info },
- { "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info },
- { "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info },
+ { .name = "ad7768-1", .driver_data = (kernel_ulong_t)&ad7768_chip_info },
+ { .name = "adaq7767-1", .driver_data = (kernel_ulong_t)&adaq7767_chip_info },
+ { .name = "adaq7768-1", .driver_data = (kernel_ulong_t)&adaq7768_chip_info },
+ { .name = "adaq7769-1", .driver_data = (kernel_ulong_t)&adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c
index 003e23d6e242..79fb38d0f400 100644
--- a/drivers/iio/adc/ad7779.c
+++ b/drivers/iio/adc/ad7779.c
@@ -142,7 +142,6 @@ struct ad7779_state {
const struct ad7779_chip_info *chip_info;
struct clk *mclk;
struct iio_trigger *trig;
- struct completion completion;
unsigned int sampling_freq;
enum ad7779_filter filter_enabled;
struct iio_backend *back;
@@ -842,8 +841,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
- return dev_err_probe(dev, ret, "request IRQ %d failed\n",
- st->spi->irq);
+ return ret;
ret = devm_iio_trigger_register(dev, st->trig);
if (ret)
@@ -851,8 +849,6 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
indio_dev->trig = iio_trigger_get(st->trig);
- init_completion(&st->completion);
-
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad7779_trigger_handler,
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
index 26382b1f8c9e..1cfad70320fe 100644
--- a/drivers/iio/adc/ad7780.c
+++ b/drivers/iio/adc/ad7780.c
@@ -347,10 +347,10 @@ static int ad7780_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7780_id[] = {
- { "ad7170", ID_AD7170 },
- { "ad7171", ID_AD7171 },
- { "ad7780", ID_AD7780 },
- { "ad7781", ID_AD7781 },
+ { .name = "ad7170", .driver_data = ID_AD7170 },
+ { .name = "ad7171", .driver_data = ID_AD7171 },
+ { .name = "ad7780", .driver_data = ID_AD7780 },
+ { .name = "ad7781", .driver_data = ID_AD7781 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7780_id);
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
index bcdc19e799aa..c2688307bbba 100644
--- a/drivers/iio/adc/ad7791.c
+++ b/drivers/iio/adc/ad7791.c
@@ -458,11 +458,11 @@ static int ad7791_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7791_spi_ids[] = {
- { "ad7787", AD7787 },
- { "ad7788", AD7788 },
- { "ad7789", AD7789 },
- { "ad7790", AD7790 },
- { "ad7791", AD7791 },
+ { .name = "ad7787", .driver_data = AD7787 },
+ { .name = "ad7788", .driver_data = AD7788 },
+ { .name = "ad7789", .driver_data = AD7789 },
+ { .name = "ad7790", .driver_data = AD7790 },
+ { .name = "ad7791", .driver_data = AD7791 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index ea1c192e2142..b0f44c144f20 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -829,15 +829,15 @@ static int ad7793_probe(struct spi_device *spi)
}
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 },
+ { .name = "ad7785", .driver_data = ID_AD7785 },
+ { .name = "ad7792", .driver_data = ID_AD7792 },
+ { .name = "ad7793", .driver_data = ID_AD7793 },
+ { .name = "ad7794", .driver_data = ID_AD7794 },
+ { .name = "ad7795", .driver_data = ID_AD7795 },
+ { .name = "ad7796", .driver_data = ID_AD7796 },
+ { .name = "ad7797", .driver_data = ID_AD7797 },
+ { .name = "ad7798", .driver_data = ID_AD7798 },
+ { .name = "ad7799", .driver_data = ID_AD7799 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7793_id);
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 068171d54596..62aaedbce748 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -328,7 +328,7 @@ static int ad7887_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7887_id[] = {
- { "ad7887", ID_AD7887 },
+ { .name = "ad7887", .driver_data = ID_AD7887 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7887_id);
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index acc87d486aa4..44e014da9722 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -360,13 +360,13 @@ static int ad7923_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7923_id[] = {
- { "ad7904", AD7904 },
- { "ad7914", AD7914 },
- { "ad7923", AD7924 },
- { "ad7924", AD7924 },
- { "ad7908", AD7908 },
- { "ad7918", AD7918 },
- { "ad7928", AD7928 },
+ { .name = "ad7904", .driver_data = AD7904 },
+ { .name = "ad7914", .driver_data = AD7914 },
+ { .name = "ad7923", .driver_data = AD7924 },
+ { .name = "ad7924", .driver_data = AD7924 },
+ { .name = "ad7908", .driver_data = AD7908 },
+ { .name = "ad7918", .driver_data = AD7918 },
+ { .name = "ad7928", .driver_data = AD7928 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7923_id);
diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c
index 7722cf9e8214..12656d3cdcec 100644
--- a/drivers/iio/adc/ad7944.c
+++ b/drivers/iio/adc/ad7944.c
@@ -865,9 +865,9 @@ static const struct of_device_id ad7944_of_match[] = {
MODULE_DEVICE_TABLE(of, ad7944_of_match);
static const struct spi_device_id ad7944_spi_id[] = {
- { "ad7944", (kernel_ulong_t)&ad7944_chip_info },
- { "ad7985", (kernel_ulong_t)&ad7985_chip_info },
- { "ad7986", (kernel_ulong_t)&ad7986_chip_info },
+ { .name = "ad7944", .driver_data = (kernel_ulong_t)&ad7944_chip_info },
+ { .name = "ad7985", .driver_data = (kernel_ulong_t)&ad7985_chip_info },
+ { .name = "ad7986", .driver_data = (kernel_ulong_t)&ad7986_chip_info },
{ }
};
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c
index ebc629bcfd4d..93a57a3d59b8 100644
--- a/drivers/iio/adc/ad7949.c
+++ b/drivers/iio/adc/ad7949.c
@@ -410,9 +410,9 @@ static const struct of_device_id ad7949_spi_of_id[] = {
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 },
+ { .name = "ad7949", .driver_data = ID_AD7949 },
+ { .name = "ad7682", .driver_data = ID_AD7682 },
+ { .name = "ad7689", .driver_data = ID_AD7689 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7949_spi_id);
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 0c377f9a7f25..34b7b1256f0d 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -1406,13 +1406,13 @@ static const struct of_device_id ad9467_of_match[] = {
MODULE_DEVICE_TABLE(of, ad9467_of_match);
static const struct spi_device_id ad9467_ids[] = {
- { "ad9211", (kernel_ulong_t)&ad9211_chip_tbl },
- { "ad9265", (kernel_ulong_t)&ad9265_chip_tbl },
- { "ad9434", (kernel_ulong_t)&ad9434_chip_tbl },
- { "ad9467", (kernel_ulong_t)&ad9467_chip_tbl },
- { "ad9643", (kernel_ulong_t)&ad9643_chip_tbl },
- { "ad9649", (kernel_ulong_t)&ad9649_chip_tbl, },
- { "ad9652", (kernel_ulong_t)&ad9652_chip_tbl, },
+ { .name = "ad9211", .driver_data = (kernel_ulong_t)&ad9211_chip_tbl },
+ { .name = "ad9265", .driver_data = (kernel_ulong_t)&ad9265_chip_tbl },
+ { .name = "ad9434", .driver_data = (kernel_ulong_t)&ad9434_chip_tbl },
+ { .name = "ad9467", .driver_data = (kernel_ulong_t)&ad9467_chip_tbl },
+ { .name = "ad9643", .driver_data = (kernel_ulong_t)&ad9643_chip_tbl },
+ { .name = "ad9649", .driver_data = (kernel_ulong_t)&ad9649_chip_tbl },
+ { .name = "ad9652", .driver_data = (kernel_ulong_t)&ad9652_chip_tbl },
{ }
};
MODULE_DEVICE_TABLE(spi, ad9467_ids);
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index c0a9babfa519..c6c3ea953fea 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* ADE9000 driver
*
* Copyright 2025 Analog Devices Inc.
@@ -1768,7 +1768,7 @@ static int ade9000_probe(struct spi_device *spi)
};
static const struct spi_device_id ade9000_id[] = {
- { "ade9000", 0 },
+ { .name = "ade9000" },
{ }
};
MODULE_DEVICE_TABLE(spi, ade9000_id);
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 26b9c75bd4d8..589f03618ecf 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -53,6 +53,10 @@
#define AXI_AD485X_PACKET_FORMAT_24BIT 0x1
#define AXI_AD485X_PACKET_FORMAT_32BIT 0x2
#define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0)
+#define AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(3, 2)
+#define AXI_AD408X_PACKET_FORMAT_20BIT 0x0
+#define AXI_AD408X_PACKET_FORMAT_16BIT 0x1
+#define AXI_AD408X_PACKET_FORMAT_14BIT 0x2
#define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068
#define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0)
@@ -436,6 +440,31 @@ static int axi_adc_ad408x_filter_type_set(struct iio_backend *back,
AXI_AD408X_CNTRL_3_FILTER_EN_MSK);
}
+static int axi_adc_ad408x_data_size_set(struct iio_backend *back,
+ unsigned int size)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+ unsigned int val;
+
+ switch (size) {
+ case 20:
+ val = AXI_AD408X_PACKET_FORMAT_20BIT;
+ break;
+ case 16:
+ val = AXI_AD408X_PACKET_FORMAT_16BIT;
+ break;
+ case 14:
+ val = AXI_AD408X_PACKET_FORMAT_14BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK,
+ FIELD_PREP(AXI_AD408X_CNTRL_3_PACKET_FORMAT_MSK, val));
+}
+
static int axi_adc_ad408x_interface_data_align(struct iio_backend *back,
u32 timeout_us)
{
@@ -659,6 +688,7 @@ static const struct iio_backend_ops adi_ad408x_ops = {
.free_buffer = axi_adc_free_buffer,
.data_sample_trigger = axi_adc_data_sample_trigger,
.filter_type_set = axi_adc_ad408x_filter_type_set,
+ .data_size_set = axi_adc_ad408x_data_size_set,
.interface_data_align = axi_adc_ad408x_interface_data_align,
.num_lanes_set = axi_adc_num_lanes_set,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index f610ad729bf3..0ac74ad92fca 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -988,7 +988,7 @@ static int at91_adc_probe(struct platform_device *pdev)
struct iio_dev *idev;
struct at91_adc_state *st;
u32 reg, prop;
- char *s;
+ const char *s;
idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state));
if (!idev)
@@ -1023,7 +1023,7 @@ static int at91_adc_probe(struct platform_device *pdev)
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)
+ !of_property_read_string(node, "atmel,adc-use-res", &s)
&& !strcmp(s, "lowres"))
st->res = st->caps->low_res_bits;
diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
index 30672e584c10..aace7e103770 100644
--- a/drivers/iio/adc/envelope-detector.c
+++ b/drivers/iio/adc/envelope-detector.c
@@ -405,3 +405,4 @@ 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");
+MODULE_IMPORT_NS("IIO_CONSUMER");
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index b01ba86c2945..9fa63de4b9ca 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -526,7 +526,7 @@ static const struct of_device_id hi8435_dt_ids[] = {
MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
static const struct spi_device_id hi8435_id[] = {
- { "hi8435", 0 },
+ { .name = "hi8435" },
{ }
};
MODULE_DEVICE_TABLE(spi, hi8435_id);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index 17e9badf1d1c..00adafa7cbd7 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -4,6 +4,8 @@
*
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
*/
+#include <linux/array_size.h>
+#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -20,6 +22,8 @@
#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
+#define HX711_DATA_BITS 24
+
/* gain to pulse and scale conversion */
#define HX711_GAIN_MAX 3
#define HX711_RESET_GAIN 128
@@ -73,6 +77,20 @@ static int hx711_get_scale_to_gain(const int *gain_scale, int scale)
return -EINVAL;
}
+/**
+ * struct hx711_chip_info - per-variant static configuration
+ * @name: IIO device name
+ * @channels: channel specification array
+ * @num_channels: number of entries in @channels
+ * @iio_info: IIO info ops for this variant
+ */
+struct hx711_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels __counted_by_ptr(num_channels);
+ unsigned int num_channels;
+ const struct iio_info *iio_info;
+};
+
struct hx711_data {
struct device *dev;
struct gpio_desc *gpiod_pd_sck;
@@ -80,6 +98,7 @@ struct hx711_data {
int gain_set; /* gain set on device */
int gain_chan_a; /* gain for channel A */
int gain_scale[HX711_GAIN_MAX];
+ const struct hx711_chip_info *chip_info;
struct mutex lock;
/*
* triggered buffer
@@ -137,17 +156,18 @@ static int hx711_cycle(struct hx711_data *hx711_data)
return gpiod_get_value(hx711_data->gpiod_dout);
}
-static int hx711_read(struct hx711_data *hx711_data)
+static int hx711_read(struct hx711_data *hx711_data, int trailing_pulses)
{
- int i, ret;
int value = 0;
- int val = gpiod_get_value(hx711_data->gpiod_dout);
+ int val;
+ int ret;
/* we double check if it's really down */
+ val = gpiod_get_value(hx711_data->gpiod_dout);
if (val)
return -EIO;
- for (i = 0; i < 24; i++) {
+ for (int i = 0; i < HX711_DATA_BITS; i++) {
value <<= 1;
ret = hx711_cycle(hx711_data);
if (ret)
@@ -156,7 +176,7 @@ static int hx711_read(struct hx711_data *hx711_data)
value ^= 0x800000;
- for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++)
+ for (int i = 0; i < trailing_pulses; i++)
hx711_cycle(hx711_data);
return value;
@@ -186,8 +206,9 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data)
static int hx711_reset(struct hx711_data *hx711_data)
{
- int val = hx711_wait_for_ready(hx711_data);
+ int val;
+ val = hx711_wait_for_ready(hx711_data);
if (val) {
/*
* an examination with the oszilloscope indicated
@@ -219,7 +240,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
if (hx711_data->gain_set == 32) {
hx711_data->gain_set = hx711_data->gain_chan_a;
- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0)
return ret;
@@ -231,7 +253,8 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
if (hx711_data->gain_set != 32) {
hx711_data->gain_set = 32;
- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0)
return ret;
@@ -244,10 +267,26 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
return 0;
}
-static int hx711_reset_read(struct hx711_data *hx711_data, int chan)
+static int hx711_set_hx711_channel(struct hx711_data *hx711_data,
+ const struct iio_chan_spec *chan,
+ int *trailing_pulses)
{
int ret;
- int val;
+
+ ret = hx711_set_gain_for_channel(hx711_data, chan->channel);
+ if (ret < 0)
+ return ret;
+
+ *trailing_pulses = hx711_get_gain_to_pulse(hx711_data->gain_set);
+
+ return 0;
+}
+
+static int hx711_reset_read(struct hx711_data *hx711_data,
+ const struct iio_chan_spec *chan)
+{
+ int trailing_pulses;
+ int ret;
/*
* hx711_reset() must be called from here
@@ -258,13 +297,11 @@ static int hx711_reset_read(struct hx711_data *hx711_data, int chan)
return -EIO;
}
- ret = hx711_set_gain_for_channel(hx711_data, chan);
+ ret = hx711_set_hx711_channel(hx711_data, chan, &trailing_pulses);
if (ret < 0)
return ret;
- val = hx711_read(hx711_data);
-
- return val;
+ return hx711_read(hx711_data, trailing_pulses);
}
static int hx711_read_raw(struct iio_dev *indio_dev,
@@ -277,7 +314,7 @@ static int hx711_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
mutex_lock(&hx711_data->lock);
- *val = hx711_reset_read(hx711_data, chan->channel);
+ *val = hx711_reset_read(hx711_data, chan);
mutex_unlock(&hx711_data->lock);
@@ -331,7 +368,8 @@ static int hx711_write_raw(struct iio_dev *indio_dev,
if (gain != 32)
hx711_data->gain_chan_a = gain;
- ret = hx711_read(hx711_data);
+ ret = hx711_read(hx711_data,
+ hx711_get_gain_to_pulse(hx711_data->gain_set));
if (ret < 0) {
mutex_unlock(&hx711_data->lock);
return ret;
@@ -366,8 +404,8 @@ static irqreturn_t hx711_trigger(int irq, void *p)
memset(&hx711_data->buffer, 0, sizeof(hx711_data->buffer));
iio_for_each_active_channel(indio_dev, i) {
- hx711_data->buffer.channel[j] = hx711_reset_read(hx711_data,
- indio_dev->channels[i].channel);
+ hx711_data->buffer.channel[j] =
+ hx711_reset_read(hx711_data, &indio_dev->channels[i]);
j++;
}
@@ -455,8 +493,16 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
IIO_CHAN_SOFT_TIMESTAMP(2),
};
+static const struct hx711_chip_info hx711_chip = {
+ .name = "hx711",
+ .channels = hx711_chan_spec,
+ .iio_info = &hx711_iio_info,
+ .num_channels = ARRAY_SIZE(hx711_chan_spec),
+};
+
static int hx711_probe(struct platform_device *pdev)
{
+ const struct hx711_chip_info *chip_info;
struct device *dev = &pdev->dev;
struct hx711_data *hx711_data;
struct iio_dev *indio_dev;
@@ -472,6 +518,12 @@ static int hx711_probe(struct platform_device *pdev)
mutex_init(&hx711_data->lock);
+ chip_info = device_get_match_data(dev);
+ if (!chip_info)
+ return dev_err_probe(dev, -ENODEV, "missing driver data\n");
+
+ hx711_data->chip_info = chip_info;
+
/*
* PD_SCK stands for power down and serial clock input of HX711
* in the driver it is an output
@@ -533,11 +585,11 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->data_ready_delay_ns =
1000000000 / hx711_data->clock_frequency;
- indio_dev->name = "hx711";
- indio_dev->info = &hx711_iio_info;
+ indio_dev->name = chip_info->name;
+ indio_dev->info = chip_info->iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = hx711_chan_spec;
- indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec);
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
@@ -554,7 +606,7 @@ static int hx711_probe(struct platform_device *pdev)
}
static const struct of_device_id of_hx711_match[] = {
- { .compatible = "avia,hx711", },
+ { .compatible = "avia,hx711", .data = &hx711_chip },
{ }
};
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
new file mode 100644
index 000000000000..b0ff2be1aee0
--- /dev/null
+++ b/drivers/iio/adc/ltc2378.c
@@ -0,0 +1,857 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices LTC2378 ADC series driver
+ *
+ * Copyright (C) 2026 Analog Devices Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/device-id/spi.h>
+#include <linux/device-id/of.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pwm.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>
+#include <linux/spi/offload/types.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/types.h>
+
+#define LTC2378_TDSDOBUSYL_NS 5
+#define LTC2378_TBUSYLH_NS 13
+#define LTC2378_TCNV_HIGH_NS 20
+#define LTC2378_MAX_DATA_WAIT_US 4 /* max(TBUSYLH + TCONV + TDSDOBUSYL) */
+
+#define LTC2378_CHANNEL(_sign, _real_bits, _storage_bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _sign, \
+ .channel = 0, \
+ .channel2 = _sign ? 1 : 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \
+ IIO_SCAN_FORMAT_UNSIGNED_INT, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = _storage_bits - _real_bits, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+#define LTC2378_DIFF_CHANNEL(_real_bits) \
+ LTC2378_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+
+#define LTC2378_PSEUDO_DIFF_CHANNEL(_real_bits) \
+ LTC2378_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16))
+
+#define LTC2378_OFFLOAD_CHANNEL(_sign, _real_bits, _storage_bits) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _sign, \
+ .channel = 0, \
+ .channel2 = _sign ? 1 : 0, \
+ .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), \
+ .scan_index = 0, \
+ .scan_type = { \
+ .format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT : \
+ IIO_SCAN_FORMAT_UNSIGNED_INT, \
+ .realbits = _real_bits, \
+ .storagebits = _storage_bits, \
+ .shift = 0, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+/*
+ * Currently, the available offload hardware + DMA configuration only supports
+ * pushing 32-bit data elements to DMA IIO buffers in CPU endianness. For 16-bit
+ * precision parts, those 32-bit elements (in CPU endianness) contain 2 bytes
+ * with data and 2 bytes always zeroed out. Nevertheless, for the offload use
+ * case, the IIO buffer is configured for 32 storage bits in CPU endianness so
+ * data is correctly aligned in user space despite 2 out of the 4 bytes being
+ * zeros.
+ */
+#define LTC2378_OFFLOAD_DIFF_CHANNEL(_real_bits) \
+ LTC2378_OFFLOAD_CHANNEL(1, (_real_bits), 32)
+
+#define LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(_real_bits) \
+ LTC2378_OFFLOAD_CHANNEL(0, (_real_bits), 32)
+
+struct ltc2378_chip_info {
+ const char *name;
+ unsigned int internal_ref_uV;
+ struct u32_fract internal_div;
+ struct iio_chan_spec chan[2]; /* 1 physical chan + 1 timestamp chan */
+ struct iio_chan_spec offload_chan;
+ unsigned int max_sample_rate_Hz;
+ unsigned int tconv_ns;
+};
+
+struct ltc2378_state {
+ const struct ltc2378_chip_info *info;
+ struct gpio_desc *cnv_gpio;
+ struct spi_device *spi;
+ struct mutex lock; /* Protect data acquisition cycle */
+ int ref_uV;
+ struct spi_transfer xfer;
+ struct spi_transfer offload_xfer;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ struct pwm_waveform cnv_wf;
+ struct spi_message offload_msg;
+ struct spi_offload_trigger_config offload_trigger_config;
+ struct pwm_device *cnv_trigger;
+ unsigned int cnv_Hz;
+ unsigned int sample_freq_range[3];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ struct {
+ union {
+ __be16 sample_buf16_be;
+ __be32 sample_buf32_be;
+ u16 sample_buf16;
+ u32 sample_buf32;
+ } data;
+ aligned_s64 timestamp;
+ } scan __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct ltc2378_chip_info ltc2338_18_chip_info = {
+ .name = "ltc2338-18",
+ .internal_ref_uV = 2048000,
+ .internal_div = { .numerator = 5, .denominator = 2 },
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2364_16_chip_info = {
+ .name = "ltc2364-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2364_18_chip_info = {
+ .name = "ltc2364-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2367_16_chip_info = {
+ .name = "ltc2367-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2367_18_chip_info = {
+ .name = "ltc2367-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2368_16_chip_info = {
+ .name = "ltc2368-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2368_18_chip_info = {
+ .name = "ltc2368-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2369_18_chip_info = {
+ .name = "ltc2369-18",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
+};
+
+static const struct ltc2378_chip_info ltc2370_16_chip_info = {
+ .name = "ltc2370-16",
+ .chan = { LTC2378_PSEUDO_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_PSEUDO_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
+};
+
+static const struct ltc2378_chip_info ltc2376_16_chip_info = {
+ .name = "ltc2376-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2376_18_chip_info = {
+ .name = "ltc2376-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2376_20_chip_info = {
+ .name = "ltc2376-20",
+ .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
+};
+
+static const struct ltc2378_chip_info ltc2377_16_chip_info = {
+ .name = "ltc2377-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2377_18_chip_info = {
+ .name = "ltc2377-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2377_20_chip_info = {
+ .name = "ltc2377-20",
+ .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
+};
+
+static const struct ltc2378_chip_info ltc2378_16_chip_info = {
+ .name = "ltc2378-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2378_18_chip_info = {
+ .name = "ltc2378-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 527,
+};
+
+static const struct ltc2378_chip_info ltc2378_20_chip_info = {
+ .name = "ltc2378-20",
+ .chan = { LTC2378_DIFF_CHANNEL(20), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(20),
+ .max_sample_rate_Hz = 1 * HZ_PER_MHZ,
+ .tconv_ns = 675,
+};
+
+static const struct ltc2378_chip_info ltc2379_18_chip_info = {
+ .name = "ltc2379-18",
+ .chan = { LTC2378_DIFF_CHANNEL(18), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(18),
+ .max_sample_rate_Hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
+};
+
+static const struct ltc2378_chip_info ltc2380_16_chip_info = {
+ .name = "ltc2380-16",
+ .chan = { LTC2378_DIFF_CHANNEL(16), IIO_CHAN_SOFT_TIMESTAMP(1) },
+ .offload_chan = LTC2378_OFFLOAD_DIFF_CHANNEL(16),
+ .max_sample_rate_Hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
+};
+
+static int ltc2378_convert_and_acquire(struct ltc2378_state *st)
+{
+ int ret;
+
+ /* Cause a rising edge of CNV to initiate a new ADC conversion */
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ fsleep(LTC2378_MAX_DATA_WAIT_US);
+ ret = spi_sync_transfer(st->spi, &st->xfer, 1);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+
+ return ret;
+}
+
+static irqreturn_t ltc2378_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret < 0)
+ goto err_out;
+
+ iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
+ pf->timestamp);
+
+err_out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int ltc2378_channel_single_read(const struct iio_chan_spec *chan,
+ struct ltc2378_state *st, int *val)
+{
+ const struct iio_scan_type *scan_type = &chan->scan_type;
+ u32 sample;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret)
+ return ret;
+
+ if (chan->scan_type.endianness == IIO_BE) {
+ if (chan->scan_type.realbits > 16)
+ sample = be32_to_cpu(st->scan.data.sample_buf32_be);
+ else
+ sample = be16_to_cpu(st->scan.data.sample_buf16_be);
+ } else { /* IIO_CPU */
+ if (chan->scan_type.realbits > 16)
+ sample = st->scan.data.sample_buf32;
+ else
+ sample = st->scan.data.sample_buf16;
+ }
+
+ sample >>= chan->scan_type.shift;
+
+ if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val = sign_extend32(sample, scan_type->realbits - 1);
+ else
+ *val = sample;
+
+ return 0;
+}
+
+static int ltc2378_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ltc2378_channel_single_read(chan, st, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE: {
+ struct u32_fract fract = st->info->internal_div;
+ *val = st->ref_uV / MILLI;
+ if (fract.numerator && fract.denominator)
+ *val = mult_frac(*val, fract.numerator, fract.denominator);
+ /*
+ * For all LTC2378-like devices, the amount of bits that express
+ * voltage magnitude depend on the polarity / output code format:
+ * - straight binary: All precision/resolution bits are used.
+ * - 2's complement: One of the precision bits is used for sign.
+ */
+ if (chan->scan_type.format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val2 = chan->scan_type.realbits - 1;
+ else
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->cnv_Hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc2378_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = st->sample_freq_range;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * SPI offload wiring schema
+ *
+ * +-------------+ +-------------+
+ * | CNV |<-----+--| GPIO |
+ * | | +--| PWM0 |
+ * | | | |
+ * | | +--| PWM1 |
+ * | | | +-------------+
+ * | | +->| TRIGGER |
+ * | | | |
+ * | ADC | | SPI |
+ * | | | controller |
+ * | | | |
+ * | SDI |<--------| SDO |
+ * | SDO |-------->| SDI |
+ * | SCLK |<--------| SCLK |
+ * +-------------+ +-------------+
+ *
+ */
+static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz)
+{
+ struct spi_offload_trigger_config config = st->offload_trigger_config;
+ unsigned int min_read_offset, offload_period_ns;
+ struct pwm_waveform cnv_wf = { };
+ u64 target = LTC2378_TCNV_HIGH_NS;
+ unsigned int count;
+ u64 offload_offset_ns;
+ int ret;
+
+ if (freq_Hz == 0)
+ return -EINVAL;
+
+ if (!in_range(freq_Hz, 1, st->info->max_sample_rate_Hz))
+ return -ERANGE;
+
+ /* Configure CNV PWM waveform */
+ cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz);
+
+ /*
+ * Ensure CNV high time meets minimum requirement (20ns). The PWM
+ * hardware may round the duty cycle, so iterate until we get at least
+ * the minimum required high time (or reach a try count limit).
+ */
+ count = 100;
+ do {
+ cnv_wf.duty_length_ns = target;
+ ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf);
+ if (ret)
+ return ret;
+ target += 10; /* Increment by PWM duty cycle period */
+ } while (count-- && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS);
+
+ /* Check the minimum CNV high time is met */
+ if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+ return -EDOM;
+
+ /*
+ * Configure SPI offload PWM trigger.
+ * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL.
+ * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns)
+ *
+ * Use the same period as CNV PWM to avoid timing issues.
+ * Convert back from period to frequency for the SPI offload API.
+ */
+ offload_period_ns = cnv_wf.period_length_ns;
+ config.periodic.frequency_hz = div_u64(HZ_PER_GHZ, offload_period_ns);
+ min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS;
+ offload_offset_ns = min_read_offset;
+ count = 100;
+ do {
+ config.periodic.offset_ns = offload_offset_ns;
+ ret = spi_offload_trigger_validate(st->offload_trigger, &config);
+ if (ret)
+ return ret;
+ offload_offset_ns += 10;
+ } while (count-- && config.periodic.offset_ns < min_read_offset);
+
+ /* Check the minimum CNV to SCLK delay is met */
+ if (config.periodic.offset_ns < min_read_offset)
+ return -EDOM;
+
+ /* Check the PWM periods remain the same */
+ offload_period_ns = div64_u64(HZ_PER_GHZ, config.periodic.frequency_hz);
+ if (cnv_wf.period_length_ns != offload_period_ns)
+ return -EDOM;
+
+ st->offload_trigger_config = config;
+ st->cnv_wf = cnv_wf;
+ st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns);
+
+ return 0;
+}
+
+static int ltc2378_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ltc2378_update_conversion_rate(st, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ltc2378_iio_info = {
+ .read_raw = &ltc2378_read_raw,
+};
+
+static const struct iio_info ltc2378_offload_iio_info = {
+ .read_raw = &ltc2378_read_raw,
+ .read_avail = &ltc2378_read_avail,
+ .write_raw = &ltc2378_write_raw,
+};
+
+static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, true);
+ if (ret)
+ return ret;
+
+ ret = spi_offload_trigger_enable(st->offload, st->offload_trigger,
+ &st->offload_trigger_config);
+ if (ret)
+ goto out_pwm_disable;
+
+ return 0;
+
+out_pwm_disable:
+ pwm_disable(st->cnv_trigger);
+ return ret;
+}
+
+static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = {
+ .postenable = &ltc2378_offload_buffer_postenable,
+ .predisable = &ltc2378_offload_buffer_predisable,
+};
+
+static int ltc2378_prepare_offload_message(struct device *dev,
+ struct ltc2378_state *st)
+{
+ unsigned int resolution = st->info->offload_chan.scan_type.realbits;
+
+ st->offload_xfer.bits_per_word = resolution;
+ st->offload_xfer.len = spi_bpw_to_bytes(resolution);
+ st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+
+ /* Initialize message with offload */
+ spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1);
+ st->offload_msg.offload = st->offload;
+
+ return devm_spi_optimize_message(dev, st->spi, &st->offload_msg);
+}
+
+static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev,
+ struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct dma_chan *rx_dma;
+
+ indio_dev->setup_ops = &ltc2378_offload_buffer_ops;
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "failed to get offload trigger\n");
+
+ st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC;
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n");
+
+ return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
+ IIO_BUFFER_DIRECTION_IN);
+}
+
+static int ltc2378_pwm_get(struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+
+ st->cnv_trigger = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnv_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_trigger),
+ "failed to get cnv pwm\n");
+
+ /*
+ * Disable the PWM connected to CNV in case it was left running by
+ * something else.
+ */
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct spi_offload_config ltc2378_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
+};
+
+static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st)
+{
+ int ret;
+
+ /*
+ * The internal reference buffer amplifies both the internal reference
+ * and REFIN by a factor of 2.
+ */
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret == -ENODEV) { /* refin is optional */
+ st->ref_uV = st->info->internal_ref_uV * 2;
+ return 0;
+ }
+
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+
+ st->ref_uV = ret * 2;
+
+ return 0;
+}
+
+static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st)
+{
+ int ret;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read ref regulator\n");
+
+ st->ref_uV = ret;
+
+ return 0;
+}
+
+static int ltc2378_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ltc2378_state *st;
+ 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 = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -EINVAL;
+
+ if (st->info->internal_ref_uV)
+ ret = ltc2378_refin_setup(dev, st);
+ else
+ ret = ltc2378_ref_setup(dev, st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "failed to get CNV GPIO");
+
+ st->offload = devm_spi_offload_get(dev, spi, &ltc2378_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ /* Fall back to low speed usage when no SPI offload is available. */
+ if (ret == -ENODEV) {
+ indio_dev->info = &ltc2378_iio_info;
+ indio_dev->channels = st->info->chan;
+ indio_dev->num_channels = ARRAY_SIZE(st->info->chan);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ltc2378_trigger_handler,
+ NULL);
+ if (ret)
+ return ret;
+ } else if (ret) {
+ return dev_err_probe(dev, ret, "failed to get offload\n");
+ } else {
+ indio_dev->info = &ltc2378_offload_iio_info;
+ indio_dev->channels = &st->info->offload_chan;
+ indio_dev->num_channels = 1;
+ ret = ltc2378_spi_offload_setup(indio_dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to setup SPI offload\n");
+
+ ret = ltc2378_pwm_get(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get PWM\n");
+
+ st->sample_freq_range[0] = 1; /* min */
+ st->sample_freq_range[1] = 1; /* step */
+ st->sample_freq_range[2] = st->info->max_sample_rate_Hz; /* max */
+
+ /*
+ * Start with a slower sampling rate so there is some room for
+ * adjusting the sample averaging and the sampling frequency
+ * without hitting the maximum conversion rate.
+ */
+ ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_Hz >> 4);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set offload samp freq\n");
+
+ ret = ltc2378_prepare_offload_message(&spi->dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
+
+ /*
+ * Set single-read transfer bits_per_word so the SPI subsystem
+ * rearranges data to CPU endianness, enabling us to reuse
+ * offload_chan specifications for single-shot reads.
+ */
+ st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits;
+ }
+
+ st->xfer.rx_buf = &st->scan.data;
+ st->xfer.len = spi_bpw_to_bytes(indio_dev->channels[0].scan_type.realbits);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ltc2378_of_match[] = {
+ { .compatible = "adi,ltc2338-18", .data = &ltc2338_18_chip_info },
+ { .compatible = "adi,ltc2364-16", .data = &ltc2364_16_chip_info },
+ { .compatible = "adi,ltc2364-18", .data = &ltc2364_18_chip_info },
+ { .compatible = "adi,ltc2367-16", .data = &ltc2367_16_chip_info },
+ { .compatible = "adi,ltc2367-18", .data = &ltc2367_18_chip_info },
+ { .compatible = "adi,ltc2368-16", .data = &ltc2368_16_chip_info },
+ { .compatible = "adi,ltc2368-18", .data = &ltc2368_18_chip_info },
+ { .compatible = "adi,ltc2369-18", .data = &ltc2369_18_chip_info },
+ { .compatible = "adi,ltc2370-16", .data = &ltc2370_16_chip_info },
+ { .compatible = "adi,ltc2376-16", .data = &ltc2376_16_chip_info },
+ { .compatible = "adi,ltc2376-18", .data = &ltc2376_18_chip_info },
+ { .compatible = "adi,ltc2376-20", .data = &ltc2376_20_chip_info },
+ { .compatible = "adi,ltc2377-16", .data = &ltc2377_16_chip_info },
+ { .compatible = "adi,ltc2377-18", .data = &ltc2377_18_chip_info },
+ { .compatible = "adi,ltc2377-20", .data = &ltc2377_20_chip_info },
+ { .compatible = "adi,ltc2378-16", .data = &ltc2378_16_chip_info },
+ { .compatible = "adi,ltc2378-18", .data = &ltc2378_18_chip_info },
+ { .compatible = "adi,ltc2378-20", .data = &ltc2378_20_chip_info },
+ { .compatible = "adi,ltc2379-18", .data = &ltc2379_18_chip_info },
+ { .compatible = "adi,ltc2380-16", .data = &ltc2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc2378_of_match);
+
+static const struct spi_device_id ltc2378_spi_id[] = {
+ { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)&ltc2338_18_chip_info },
+ { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)&ltc2364_16_chip_info },
+ { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)&ltc2364_18_chip_info },
+ { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)&ltc2367_16_chip_info },
+ { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)&ltc2367_18_chip_info },
+ { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)&ltc2368_16_chip_info },
+ { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)&ltc2368_18_chip_info },
+ { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)&ltc2369_18_chip_info },
+ { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)&ltc2370_16_chip_info },
+ { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)&ltc2376_16_chip_info },
+ { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)&ltc2376_18_chip_info },
+ { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)&ltc2376_20_chip_info },
+ { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)&ltc2377_16_chip_info },
+ { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)&ltc2377_18_chip_info },
+ { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)&ltc2377_20_chip_info },
+ { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)&ltc2378_16_chip_info },
+ { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)&ltc2378_18_chip_info },
+ { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)&ltc2378_20_chip_info },
+ { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)&ltc2379_18_chip_info },
+ { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)&ltc2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ltc2378_spi_id);
+
+static struct spi_driver ltc2378_driver = {
+ .driver = {
+ .name = "ltc2378",
+ .of_match_table = ltc2378_of_match
+ },
+ .probe = ltc2378_probe,
+ .id_table = ltc2378_spi_id,
+};
+module_spi_driver(ltc2378_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
+MODULE_IMPORT_NS("SPI_OFFLOAD");
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 8b2c7b2a9c8f..fb6f12316724 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -72,12 +72,12 @@ enum max1027_id {
};
static const struct spi_device_id max1027_id[] = {
- { "max1027", max1027 },
- { "max1029", max1029 },
- { "max1031", max1031 },
- { "max1227", max1227 },
- { "max1229", max1229 },
- { "max1231", max1231 },
+ { .name = "max1027", .driver_data = max1027 },
+ { .name = "max1029", .driver_data = max1029 },
+ { .name = "max1031", .driver_data = max1031 },
+ { .name = "max1227", .driver_data = max1227 },
+ { .name = "max1229", .driver_data = max1229 },
+ { .name = "max1231", .driver_data = max1231 },
{ }
};
MODULE_DEVICE_TABLE(spi, max1027_id);
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
index d394e03cc4e8..75e51ce3077e 100644
--- a/drivers/iio/adc/max1118.c
+++ b/drivers/iio/adc/max1118.c
@@ -256,9 +256,9 @@ static int max1118_probe(struct spi_device *spi)
}
static const struct spi_device_id max1118_id[] = {
- { "max1117", max1117 },
- { "max1118", max1118 },
- { "max1119", max1119 },
+ { .name = "max1117", .driver_data = max1117 },
+ { .name = "max1118", .driver_data = max1118 },
+ { .name = "max1119", .driver_data = max1119 },
{ }
};
MODULE_DEVICE_TABLE(spi, max1118_id);
diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c
index 6c803df220b6..63b6561dcf47 100644
--- a/drivers/iio/adc/max11205.c
+++ b/drivers/iio/adc/max11205.c
@@ -145,8 +145,8 @@ static int max11205_probe(struct spi_device *spi)
}
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] },
+ { .name = "max11205a", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] },
+ { .name = "max11205b", .driver_data = (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] },
{ }
};
MODULE_DEVICE_TABLE(spi, max11205_spi_ids);
diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c
index dc1b96356592..23ae684cf08b 100644
--- a/drivers/iio/adc/max11410.c
+++ b/drivers/iio/adc/max11410.c
@@ -1025,7 +1025,7 @@ static const struct of_device_id max11410_spi_of_id[] = {
MODULE_DEVICE_TABLE(of, max11410_spi_of_id);
static const struct spi_device_id max11410_id[] = {
- { "max11410" },
+ { .name = "max11410" },
{ }
};
MODULE_DEVICE_TABLE(spi, max11410_id);
diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c
index d62c1a011659..b3cecbf9a89e 100644
--- a/drivers/iio/adc/max1241.c
+++ b/drivers/iio/adc/max1241.c
@@ -15,10 +15,6 @@
#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;
@@ -166,7 +162,7 @@ static int max1241_probe(struct spi_device *spi)
else
dev_dbg(dev, "no shutdown pin passed, low-power mode disabled");
- indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->name = "max1241";
indio_dev->info = &max1241_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = max1241_channels;
@@ -176,9 +172,10 @@ static int max1241_probe(struct spi_device *spi)
}
static const struct spi_device_id max1241_id[] = {
- { "max1241", max1241 },
+ { .name = "max1241" },
{ }
};
+MODULE_DEVICE_TABLE(spi, max1241_id);
static const struct of_device_id max1241_dt_ids[] = {
{ .compatible = "maxim,max1241" },
diff --git a/drivers/iio/adc/max14001.c b/drivers/iio/adc/max14001.c
index 09017163b191..58adeac62dde 100644
--- a/drivers/iio/adc/max14001.c
+++ b/drivers/iio/adc/max14001.c
@@ -362,8 +362,8 @@ static struct max14001_chip_info max14002_chip_info = {
};
static const struct spi_device_id max14001_id_table[] = {
- { "max14001", (kernel_ulong_t)&max14001_chip_info },
- { "max14002", (kernel_ulong_t)&max14002_chip_info },
+ { .name = "max14001", .driver_data = (kernel_ulong_t)&max14001_chip_info },
+ { .name = "max14002", .driver_data = (kernel_ulong_t)&max14002_chip_info },
{ }
};
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 686c519aa3d2..18cb957e7095 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -476,19 +476,19 @@ static const struct of_device_id mcp320x_dt_ids[] = {
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 },
+ { .name = "mcp3001", .driver_data = mcp3001 },
+ { .name = "mcp3002", .driver_data = mcp3002 },
+ { .name = "mcp3004", .driver_data = mcp3004 },
+ { .name = "mcp3008", .driver_data = mcp3008 },
+ { .name = "mcp3201", .driver_data = mcp3201 },
+ { .name = "mcp3202", .driver_data = mcp3202 },
+ { .name = "mcp3204", .driver_data = mcp3204 },
+ { .name = "mcp3208", .driver_data = mcp3208 },
+ { .name = "mcp3301", .driver_data = mcp3301 },
+ { .name = "mcp3550-50", .driver_data = mcp3550_50 },
+ { .name = "mcp3550-60", .driver_data = mcp3550_60 },
+ { .name = "mcp3551", .driver_data = mcp3551 },
+ { .name = "mcp3553", .driver_data = mcp3553 },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp320x_id);
diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c
index 36675563829e..259ac4ebdedf 100644
--- a/drivers/iio/adc/mcp3564.c
+++ b/drivers/iio/adc/mcp3564.c
@@ -1451,18 +1451,18 @@ static const struct of_device_id mcp3564_dt_ids[] = {
MODULE_DEVICE_TABLE(of, mcp3564_dt_ids);
static const struct spi_device_id mcp3564_id[] = {
- { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] },
- { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] },
- { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] },
- { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] },
- { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] },
- { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] },
- { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] },
- { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] },
- { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] },
- { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] },
- { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] },
- { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] },
+ { .name = "mcp3461", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] },
+ { .name = "mcp3462", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] },
+ { .name = "mcp3464", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] },
+ { .name = "mcp3561", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] },
+ { .name = "mcp3562", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] },
+ { .name = "mcp3564", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] },
+ { .name = "mcp3461r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] },
+ { .name = "mcp3462r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] },
+ { .name = "mcp3464r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] },
+ { .name = "mcp3561r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] },
+ { .name = "mcp3562r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] },
+ { .name = "mcp3564r", .driver_data = (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp3564_id);
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index 5bf74b49bdb5..797424f59f74 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -927,13 +927,13 @@ static const struct of_device_id mcp3911_dt_ids[] = {
MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
static const struct spi_device_id mcp3911_id[] = {
- { "mcp3910", (kernel_ulong_t)&mcp3911_chip_info[MCP3910] },
- { "mcp3911", (kernel_ulong_t)&mcp3911_chip_info[MCP3911] },
- { "mcp3912", (kernel_ulong_t)&mcp3911_chip_info[MCP3912] },
- { "mcp3913", (kernel_ulong_t)&mcp3911_chip_info[MCP3913] },
- { "mcp3914", (kernel_ulong_t)&mcp3911_chip_info[MCP3914] },
- { "mcp3918", (kernel_ulong_t)&mcp3911_chip_info[MCP3918] },
- { "mcp3919", (kernel_ulong_t)&mcp3911_chip_info[MCP3919] },
+ { .name = "mcp3910", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3910] },
+ { .name = "mcp3911", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3911] },
+ { .name = "mcp3912", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3912] },
+ { .name = "mcp3913", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3913] },
+ { .name = "mcp3914", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3914] },
+ { .name = "mcp3918", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3918] },
+ { .name = "mcp3919", .driver_data = (kernel_ulong_t)&mcp3911_chip_info[MCP3919] },
{ }
};
MODULE_DEVICE_TABLE(spi, mcp3911_id);
diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c
new file mode 100644
index 000000000000..4f73ac42abb7
--- /dev/null
+++ b/drivers/iio/adc/mt6323-auxadc.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Roman Vivchar <rva333@protonmail.com>
+ *
+ * Based on drivers/iio/adc/mt6359-auxadc.c
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/stringify.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <linux/mfd/mt6323/registers.h>
+
+#include <dt-bindings/iio/adc/mediatek,mt6323-auxadc.h>
+
+#define AUXADC_STRUP_CON10_RSTB_SEL BIT(7)
+#define AUXADC_STRUP_CON10_RSTB_SW BIT(5)
+
+#define AUXADC_TOP_CKPDN2_CTL_CK BIT(5)
+
+#define AUXADC_TRIM_CH2_MASK GENMASK(11, 10)
+#define AUXADC_TRIM_CH4_MASK GENMASK(9, 8)
+#define AUXADC_TRIM_CH5_MASK GENMASK(5, 4)
+#define AUXADC_TRIM_CH6_MASK GENMASK(3, 2)
+
+#define AUXADC_CON27_VREF18_ENB_MD BIT(15)
+#define AUXADC_CON27_MD_STATUS BIT(0)
+
+#define AUXADC_CON19_GPS_STATUS BIT(1)
+
+#define AUXADC_CON26_VREF18_SELB BIT(1)
+#define AUXADC_CON26_DECI_GDLY_SEL BIT(0)
+
+#define AUXADC_CON11_VBUF_EN BIT(4)
+
+#define AUXADC_CON19_DECI_GDLY_MASK GENMASK(15, 14)
+#define AUXADC_ADC19_BUSY_MASK GENMASK(15, 1)
+#define AUXADC_READY_MASK BIT(15)
+#define AUXADC_DATA_MASK GENMASK(14, 0)
+
+#define AUXADC_CON9_OSR_MASK GENMASK(12, 10)
+#define AUXADC_DEFAULT_OSR 3
+
+#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _chan, \
+ .address = _addr, \
+ .datasheet_name = __stringify(_name), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+/*
+ * AUXADC reports everything in mV, including temperature and
+ * current channels. Channel macros are mapped such that their
+ * ID matches their respective hardware bit position in CON22.
+ */
+static const struct iio_chan_spec mt6323_auxadc_channels[] = {
+ MTK_PMIC_IIO_CHAN(baton2, MT6323_AUXADC_BATON2, MT6323_AUXADC_ADC6),
+ MTK_PMIC_IIO_CHAN(ch6, MT6323_AUXADC_CH6, MT6323_AUXADC_ADC11),
+ MTK_PMIC_IIO_CHAN(bat_temp, MT6323_AUXADC_BAT_TEMP, MT6323_AUXADC_ADC5),
+ MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4),
+ MTK_PMIC_IIO_CHAN(vcdt, MT6323_AUXADC_VCDT, MT6323_AUXADC_ADC2),
+ MTK_PMIC_IIO_CHAN(baton1, MT6323_AUXADC_BATON1, MT6323_AUXADC_ADC3),
+ MTK_PMIC_IIO_CHAN(isense, MT6323_AUXADC_ISENSE, MT6323_AUXADC_ADC1),
+ MTK_PMIC_IIO_CHAN(batsns, MT6323_AUXADC_BATSNS, MT6323_AUXADC_ADC0),
+ MTK_PMIC_IIO_CHAN(accdet, MT6323_AUXADC_ACCDET, MT6323_AUXADC_ADC7),
+};
+
+/*
+ * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy:
+ * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM)
+ *
+ * Therefore, PWRAP regmap should be obtained using dev->parent->parent.
+ */
+struct mt6323_auxadc {
+ struct regmap *regmap;
+ /* AUXADC doesn't support reading multiple channels simultaneously. */
+ struct mutex lock;
+};
+
+static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc)
+{
+ struct regmap *map = auxadc->regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_read(map, MT6323_AUXADC_CON19, &val);
+ if (ret)
+ return ret;
+
+ /* The ADC is idle. */
+ if (!(val & AUXADC_CON19_DECI_GDLY_MASK))
+ return 0;
+
+ ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19,
+ val, !(val & AUXADC_ADC19_BUSY_MASK),
+ 10, 500);
+ if (ret)
+ return ret;
+
+ return regmap_clear_bits(map, MT6323_AUXADC_CON19,
+ AUXADC_CON19_DECI_GDLY_MASK);
+}
+
+static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc,
+ unsigned long channel)
+{
+ struct regmap *map = auxadc->regmap;
+ int ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel));
+}
+
+static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc,
+ unsigned long channel)
+{
+ struct regmap *map = auxadc->regmap;
+ int ret;
+
+ ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel));
+ if (ret)
+ return ret;
+
+ return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
+}
+
+static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc,
+ const struct iio_chan_spec *chan, int *out)
+{
+ struct regmap *map = auxadc->regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(map, chan->address,
+ val, (val & AUXADC_READY_MASK),
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ *out = FIELD_GET(AUXADC_DATA_MASK, val);
+
+ return 0;
+}
+
+static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct mt6323_auxadc *auxadc = iio_priv(indio_dev);
+ int ret, mult;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->channel == MT6323_AUXADC_ISENSE ||
+ chan->channel == MT6323_AUXADC_BATSNS)
+ mult = 4;
+ else
+ mult = 1;
+
+ /* 1800mV full range with 15-bit resolution. */
+ *val = mult * 1800;
+ *val2 = 15;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_RAW: {
+ guard(mutex)(&auxadc->lock);
+
+ ret = mt6323_auxadc_prepare_channel(auxadc);
+ if (ret)
+ return ret;
+
+ ret = mt6323_auxadc_request(auxadc, chan->channel);
+ if (ret)
+ return ret;
+
+ /* Hardware limitation: the AUXADC needs a delay to become ready. */
+ fsleep(300);
+
+ ret = mt6323_auxadc_read(auxadc, chan, val);
+
+ if (mt6323_auxadc_release(auxadc, chan->channel))
+ dev_err(&indio_dev->dev,
+ "failed to release channel %d\n", chan->channel);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc)
+{
+ struct regmap *map = auxadc->regmap;
+ int ret;
+
+ ret = regmap_set_bits(map, MT6323_STRUP_CON10,
+ AUXADC_STRUP_CON10_RSTB_SW |
+ AUXADC_STRUP_CON10_RSTB_SEL);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(map, MT6323_AUXADC_CON10,
+ AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK |
+ AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK,
+ FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) |
+ FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) |
+ FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) |
+ FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON27,
+ AUXADC_CON27_VREF18_ENB_MD |
+ AUXADC_CON27_MD_STATUS);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, MT6323_AUXADC_CON26,
+ AUXADC_CON26_VREF18_SELB |
+ AUXADC_CON26_DECI_GDLY_SEL);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK,
+ FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR));
+}
+
+static const struct iio_info mt6323_auxadc_iio_info = {
+ .read_raw = mt6323_auxadc_read_raw,
+};
+
+static int mt6323_auxadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mt6323_auxadc *auxadc;
+ struct regmap *regmap;
+ struct iio_dev *iio;
+ int ret;
+
+ regmap = dev_get_regmap(dev->parent->parent, NULL);
+ if (!regmap)
+ return dev_err_probe(dev, -ENODEV, "failed to get regmap\n");
+
+ iio = devm_iio_device_alloc(dev, sizeof(*auxadc));
+ if (!iio)
+ return -ENOMEM;
+
+ auxadc = iio_priv(iio);
+ auxadc->regmap = regmap;
+
+ ret = devm_mutex_init(dev, &auxadc->lock);
+ if (ret)
+ return ret;
+
+ ret = mt6323_auxadc_init(auxadc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize auxadc\n");
+
+ iio->name = "mt6323-auxadc";
+ iio->info = &mt6323_auxadc_iio_info;
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->channels = mt6323_auxadc_channels;
+ iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels);
+
+ return devm_iio_device_register(dev, iio);
+}
+
+static const struct of_device_id mt6323_auxadc_of_match[] = {
+ { .compatible = "mediatek,mt6323-auxadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match);
+
+static struct platform_driver mt6323_auxadc_driver = {
+ .driver = {
+ .name = "mt6323-auxadc",
+ .of_match_table = mt6323_auxadc_of_match,
+ },
+ .probe = mt6323_auxadc_probe,
+};
+module_platform_driver(mt6323_auxadc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver");
diff --git a/drivers/iio/adc/pac1921.c b/drivers/iio/adc/pac1921.c
index 68bdd2f30bad..f0bab94462f4 100644
--- a/drivers/iio/adc/pac1921.c
+++ b/drivers/iio/adc/pac1921.c
@@ -1037,7 +1037,7 @@ static irqreturn_t pac1921_trigger_handler(int irq, void *p)
iio_for_each_active_channel(idev, bit) {
u16 val;
- ret = pac1921_read_res(priv, idev->channels[ch].address, &val);
+ ret = pac1921_read_res(priv, idev->channels[bit].address, &val);
if (ret)
goto done;
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index 3a17b3898bf6..d38cf3caeae0 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -159,16 +159,6 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = {
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);
-
- 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)
@@ -200,7 +190,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
- ret = rcar_gyroadc_set_power(priv, true);
+ ret = pm_runtime_resume_and_get(priv->dev);
if (ret < 0) {
iio_device_release_direct(indio_dev);
return ret;
@@ -209,7 +199,7 @@ static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev,
*val = readl(priv->regs + datareg);
*val &= BIT(priv->sample_width) - 1;
- ret = rcar_gyroadc_set_power(priv, false);
+ ret = pm_runtime_put_autosuspend(priv->dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/rohm-bd79112.c b/drivers/iio/adc/rohm-bd79112.c
index c4b0bec80794..2506fec7be84 100644
--- a/drivers/iio/adc/rohm-bd79112.c
+++ b/drivers/iio/adc/rohm-bd79112.c
@@ -529,7 +529,7 @@ static const struct of_device_id bd79112_of_match[] = {
MODULE_DEVICE_TABLE(of, bd79112_of_match);
static const struct spi_device_id bd79112_id[] = {
- { "bd79112" },
+ { .name = "bd79112" },
{ }
};
MODULE_DEVICE_TABLE(spi, bd79112_id);
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
index ba525a3c5cc2..2083c4351e43 100644
--- a/drivers/iio/adc/rtq6056.c
+++ b/drivers/iio/adc/rtq6056.c
@@ -871,8 +871,8 @@ static const struct richtek_dev_data rtq6059_devdata = {
};
static const struct i2c_device_id rtq6056_id[] = {
- { "rtq6056", (kernel_ulong_t)&rtq6056_devdata },
- { "rtq6059", (kernel_ulong_t)&rtq6059_devdata },
+ { .name = "rtq6056", .driver_data = (kernel_ulong_t)&rtq6056_devdata },
+ { .name = "rtq6059", .driver_data = (kernel_ulong_t)&rtq6059_devdata },
{ }
};
MODULE_DEVICE_TABLE(i2c, rtq6056_id);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 9664b9bd75d4..00f05e167afc 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -660,11 +660,8 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
}
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;
+ df_ch->alt_si = of_property_present(indio_dev->dev.of_node,
+ "st,adc-alt-channel");
return 0;
}
@@ -1815,9 +1812,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
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->dfsdm->fl_list[adc->fl_id].sync_mode =
+ of_property_read_bool(dev->of_node, "st,filter0-sync");
adc->dev_data = dev_data;
ret = dev_data->init(dev, iio);
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
index e33a2f4bf66c..2df8f1c21c2a 100644
--- a/drivers/iio/adc/ti-adc081c.c
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -189,10 +189,8 @@ static int adc081c_probe(struct i2c_client *client)
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;
- }
+ if (err < 0)
+ return dev_err_probe(&client->dev, err, "iio triggered buffer setup failed\n");
return devm_iio_device_register(&client->dev, iio);
}
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
index 63d712d5d111..a522d8676034 100644
--- a/drivers/iio/adc/ti-adc0832.c
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -313,10 +313,10 @@ static const struct of_device_id adc0832_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adc0832_dt_ids);
static const struct spi_device_id adc0832_id[] = {
- { "adc0831", adc0831 },
- { "adc0832", adc0832 },
- { "adc0834", adc0834 },
- { "adc0838", adc0838 },
+ { .name = "adc0831", .driver_data = adc0831 },
+ { .name = "adc0832", .driver_data = adc0832 },
+ { .name = "adc0834", .driver_data = adc0834 },
+ { .name = "adc0838", .driver_data = adc0838 },
{ }
};
MODULE_DEVICE_TABLE(spi, adc0832_id);
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index 51596b500a90..cf2f6ee59411 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -228,10 +228,8 @@ static int adc084s021_probe(struct spi_device *spi)
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;
- }
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to setup triggered buffer\n");
return devm_iio_device_register(&spi->dev, indio_dev);
}
@@ -243,7 +241,7 @@ static const struct of_device_id adc084s021_of_match[] = {
MODULE_DEVICE_TABLE(of, adc084s021_of_match);
static const struct spi_device_id adc084s021_id[] = {
- { ADC084S021_DRIVER_NAME, 0 },
+ { .name = ADC084S021_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, adc084s021_id);
diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c
index d1f61b440959..10959e835cb8 100644
--- a/drivers/iio/adc/ti-adc108s102.c
+++ b/drivers/iio/adc/ti-adc108s102.c
@@ -276,7 +276,7 @@ static const struct acpi_device_id adc108s102_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids);
static const struct spi_device_id adc108s102_id[] = {
- { "adc108s102", 0 },
+ { .name = "adc108s102" },
{ }
};
MODULE_DEVICE_TABLE(spi, adc108s102_id);
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
index e5ec4b073daa..279e4999eb49 100644
--- a/drivers/iio/adc/ti-adc12138.c
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -516,9 +516,9 @@ static const struct of_device_id adc12138_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adc12138_dt_ids);
static const struct spi_device_id adc12138_id[] = {
- { "adc12130", adc12130 },
- { "adc12132", adc12132 },
- { "adc12138", adc12138 },
+ { .name = "adc12130", .driver_data = adc12130 },
+ { .name = "adc12132", .driver_data = adc12132 },
+ { .name = "adc12138", .driver_data = adc12138 },
{ }
};
MODULE_DEVICE_TABLE(spi, adc12138_id);
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 2cb68582ee6a..1899813c1aee 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -247,18 +247,18 @@ static const struct of_device_id adc128_of_match[] = {
MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
- { "adc128s052", (kernel_ulong_t)&adc128s_config },
- { "adc122s021", (kernel_ulong_t)&adc122s_config },
- { "adc122s051", (kernel_ulong_t)&adc122s_config },
- { "adc122s101", (kernel_ulong_t)&adc122s_config },
- { "adc124s021", (kernel_ulong_t)&adc124s_config },
- { "adc124s051", (kernel_ulong_t)&adc124s_config },
- { "adc124s101", (kernel_ulong_t)&adc124s_config },
- { "bd79100", (kernel_ulong_t)&bd79100_config },
- { "bd79101", (kernel_ulong_t)&bd79101_config },
- { "bd79102", (kernel_ulong_t)&bd79102_config },
- { "bd79103", (kernel_ulong_t)&bd79104_config },
- { "bd79104", (kernel_ulong_t)&bd79104_config },
+ { .name = "adc128s052", .driver_data = (kernel_ulong_t)&adc128s_config },
+ { .name = "adc122s021", .driver_data = (kernel_ulong_t)&adc122s_config },
+ { .name = "adc122s051", .driver_data = (kernel_ulong_t)&adc122s_config },
+ { .name = "adc122s101", .driver_data = (kernel_ulong_t)&adc122s_config },
+ { .name = "adc124s021", .driver_data = (kernel_ulong_t)&adc124s_config },
+ { .name = "adc124s051", .driver_data = (kernel_ulong_t)&adc124s_config },
+ { .name = "adc124s101", .driver_data = (kernel_ulong_t)&adc124s_config },
+ { .name = "bd79100", .driver_data = (kernel_ulong_t)&bd79100_config },
+ { .name = "bd79101", .driver_data = (kernel_ulong_t)&bd79101_config },
+ { .name = "bd79102", .driver_data = (kernel_ulong_t)&bd79102_config },
+ { .name = "bd79103", .driver_data = (kernel_ulong_t)&bd79104_config },
+ { .name = "bd79104", .driver_data = (kernel_ulong_t)&bd79104_config },
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index 08aa32bd5e4b..422df0685580 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -229,8 +229,8 @@ static const struct of_device_id ti_adc_dt_ids[] = {
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
static const struct spi_device_id ti_adc_id[] = {
- { "adc141s626", TI_ADC141S626 },
- { "adc161s626", TI_ADC161S626 },
+ { .name = "adc141s626", .driver_data = TI_ADC141S626 },
+ { .name = "adc161s626", .driver_data = TI_ADC161S626 },
{ }
};
MODULE_DEVICE_TABLE(spi, ti_adc_id);
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 8a272af69f7d..0fbfa4e499aa 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -930,6 +930,17 @@ static int ads1015_set_conv_mode(struct ads1015_data *data, int mode)
mode << ADS1015_CFG_MOD_SHIFT);
}
+static void ads1015_power_down(void *p)
+{
+ struct ads1015_data *data = p;
+ int ret;
+
+ ret = ads1015_set_conv_mode(data, ADS1015_SINGLESHOT);
+ if (ret)
+ dev_warn(regmap_get_device(data->regmap),
+ "Failed to power down (%pe)\n", ERR_PTR(ret));
+}
+
static int ads1015_probe(struct i2c_client *client)
{
const struct ads1015_chip_data *chip;
@@ -1030,6 +1041,10 @@ static int ads1015_probe(struct i2c_client *client)
if (ret)
return ret;
+ ret = devm_add_action_or_reset(&client->dev, ads1015_power_down, data);
+ if (ret)
+ return ret;
+
data->conv_invalid = true;
ret = pm_runtime_set_active(&client->dev);
@@ -1037,33 +1052,11 @@ static int ads1015_probe(struct i2c_client *client)
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");
+ ret = devm_pm_runtime_enable(&client->dev);
+ if (ret)
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));
+ return devm_iio_device_register(&client->dev, indio_dev);
}
#ifdef CONFIG_PM
@@ -1150,7 +1143,6 @@ static struct i2c_driver ads1015_driver = {
.pm = &ads1015_pm_ops,
},
.probe = ads1015_probe,
- .remove = ads1015_remove,
.id_table = ads1015_id,
};
diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c
index d6624c71a374..d9ec4a8d7ce5 100644
--- a/drivers/iio/adc/ti-ads1018.c
+++ b/drivers/iio/adc/ti-ads1018.c
@@ -717,8 +717,8 @@ static const struct of_device_id ads1018_of_match[] = {
MODULE_DEVICE_TABLE(of, ads1018_of_match);
static const struct spi_device_id ads1018_spi_match[] = {
- { "ads1018", (kernel_ulong_t)&ads1018_chip_info },
- { "ads1118", (kernel_ulong_t)&ads1118_chip_info },
+ { .name = "ads1018", .driver_data = (kernel_ulong_t)&ads1018_chip_info },
+ { .name = "ads1118", .driver_data = (kernel_ulong_t)&ads1118_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ads1018_spi_match);
diff --git a/drivers/iio/adc/ti-ads112c14.c b/drivers/iio/adc/ti-ads112c14.c
new file mode 100644
index 000000000000..60eab4852ba2
--- /dev/null
+++ b/drivers/iio/adc/ti-ads112c14.c
@@ -0,0 +1,1454 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IIO driver for Texas Instruments ADS112C14 and similar ADCs.
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Copyright (C) 2026 Baylibre Inc.
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/ads122c14.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/i2c.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+/* Arbitrary limit since channels are dynamic. */
+#define ADS112C14_MAX_MEASUREMENT_CHANNELS 16
+
+/* Datasheet t_d(RST) - time to wait after reset before next I2C use. */
+#define ADS112C14_DELAY_RESET_US 500
+
+#define ADS112C14_CMD_RDATA 0x00
+#define ADS112C14_CMD_RREG 0x40
+#define ADS112C14_CMD_WREG 0x80
+
+#define ADS112C14_REG_DEVICE_ID 0x00
+#define ADS112C14_DEVICE_ID_BITS GENMASK(3, 0)
+
+#define ADS112C14_REG_REVISION_ID 0x01
+
+#define ADS112C14_REG_STATUS_MSB 0x02
+#define ADS112C14_STATUS_MSB_RESETN BIT(7)
+#define ADS112C14_STATUS_MSB_AVDD_UVN BIT(6)
+#define ADS112C14_STATUS_MSB_REF_UVN BIT(5)
+#define ADS112C14_STATUS_MSB_REG_MAP_CRC_FAULTN BIT(3)
+#define ADS112C14_STATUS_MSB_MEM_FAULTN BIT(2)
+#define ADS112C14_STATUS_MSB_REG_WRITE_FAULTN BIT(1)
+#define ADS112C14_STATUS_MSB_DRDY BIT(0)
+
+#define ADS112C14_REG_STATUS_LSB 0x03
+#define ADS112C14_STATUS_LSB_CONV_COUNT GENMASK(7, 4)
+#define ADS112C14_STATUS_LSB_GPIO3_DAT_IN BIT(3)
+#define ADS112C14_STATUS_LSB_GPIO2_DAT_IN BIT(2)
+#define ADS112C14_STATUS_LSB_GPIO1_DAT_IN BIT(1)
+#define ADS112C14_STATUS_LSB_GPIO0_DAT_IN BIT(0)
+
+#define ADS112C14_REG_CONVERSION_CTRL 0x04
+#define ADS112C14_CONVERSION_CTRL_RESET GENMASK(7, 2)
+#define ADS112C14_CONVERSION_CTRL_START BIT(1)
+#define ADS112C14_CONVERSION_CTRL_STOP BIT(0)
+
+#define ADS112C14_REG_DEVICE_CFG 0x05
+#define ADS112C14_DEVICE_CFG_PWDN BIT(7)
+#define ADS112C14_DEVICE_CFG_STBY_MODE BIT(6)
+#define ADS112C14_DEVICE_CFG_BOCS GENMASK(5, 4)
+#define ADS112C14_DEVICE_CFG_CLK_SEL BIT(3)
+#define ADS112C14_DEVICE_CFG_CONV_MODE BIT(2)
+#define ADS112C14_DEVICE_CFG_CONV_MODE_CONTINUOUS 0
+#define ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT 1
+#define ADS112C14_DEVICE_CFG_SPEED_MODE GENMASK(1, 0)
+
+#define ADS112C14_REG_DATA_RATE_CFG 0x06
+#define ADS112C14_DATA_RATE_CFG_DELAY GENMASK(7, 4)
+#define ADS112C14_DATA_RATE_CFG_GC_EN BIT(3)
+#define ADS112C14_DATA_RATE_CFG_FLTR_OSR GENMASK(2, 0)
+
+#define ADS112C14_REG_MUX_CFG 0x07
+#define ADS112C14_MUX_CFG_AINP GENMASK(7, 4)
+#define ADS112C14_MUX_CFG_AINN GENMASK(3, 0)
+#define ADS112C14_MUX_CFG_AIN_GND 8
+
+#define ADS112C14_REG_GAIN_CFG 0x08
+#define ADS112C14_GAIN_CFG_SPARE BIT(7)
+#define ADS112C14_GAIN_CFG_SYS_MON GENMASK(6, 4)
+#define ADS112C14_GAIN_CFG_GAIN GENMASK(3, 0)
+
+#define ADS112C14_REG_REFERENCE_CFG 0x09
+#define ADS112C14_REFERENCE_CFG_REF_UV_EN BIT(7)
+#define ADS112C14_REFERENCE_CFG_REFP_BUF_EN BIT(5)
+#define ADS112C14_REFERENCE_CFG_REFN_BUF_EN BIT(4)
+#define ADS112C14_REFERENCE_CFG_REF_VAL BIT(2)
+#define ADS112C14_REFERENCE_CFG_REF_VAL_1_25V 0
+#define ADS112C14_REFERENCE_CFG_REF_VAL_2_5V 1
+#define ADS112C14_REFERENCE_CFG_REF_SEL GENMASK(1, 0)
+#define ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL 0
+#define ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL 1
+#define ADS112C14_REFERENCE_CFG_REF_SEL_AVDD 2
+
+#define ADS112C14_REG_DIGITAL_CFG 0x0A
+#define ADS112C14_DIGITAL_CFG_REG_MAP_CRC_EN BIT(6)
+#define ADS112C14_DIGITAL_CFG_I2C_CRC_EN BIT(5)
+#define ADS112C14_DIGITAL_CFG_STATUS_EN BIT(4)
+#define ADS112C14_DIGITAL_CFG_FAULT_PIN_BEHAVIOR BIT(3)
+#define ADS112C14_DIGITAL_CFG_CODING BIT(1)
+
+#define ADS112C14_REG_GPIO_CFG 0x0B
+#define ADS112C14_GPIO_CFG_GPIO3_CFG GENMASK(7, 6)
+#define ADS112C14_GPIO_CFG_GPIO2_CFG GENMASK(5, 4)
+#define ADS112C14_GPIO_CFG_GPIO1_CFG GENMASK(3, 2)
+#define ADS112C14_GPIO_CFG_GPIO0_CFG GENMASK(1, 0)
+
+#define ADS112C14_REG_GPIO_DATA_OUTPUT 0x0C
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_SRC BIT(7)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_SRC BIT(6)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO3_DAT_OUT BIT(3)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO2_DAT_OUT BIT(2)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO1_DAT_OUT BIT(1)
+#define ADS112C14_GPIO_DATA_OUTPUT_GPIO0_DAT_OUT BIT(0)
+
+#define ADS112C14_REG_IDAC_MAG_CFG 0x0D
+#define ADS112C14_IDAC_MAG_CFG_I2MAG GENMASK(7, 4)
+#define ADS112C14_IDAC_MAG_CFG_I1MAG GENMASK(3, 0)
+
+#define ADS112C14_REG_IDAC_MUX_CFG 0x0E
+#define ADS112C14_IDAC_MUX_CFG_IUNIT BIT(7)
+#define ADS112C14_IDAC_MUX_CFG_I2MUX GENMASK(6, 4)
+#define ADS112C14_IDAC_MUX_CFG_I1MUX GENMASK(2, 0)
+
+#define ADS112C14_REG_REG_MAP_CRC 0x0F
+
+#define ADS112C14_INT_REF0_mV 1250
+#define ADS112C14_INT_REF1_mV 2500
+
+enum {
+ ADS112C14_VREF_SOURCE_INTERNAL_2_5V,
+ ADS112C14_VREF_SOURCE_INTERNAL_1_25V,
+ ADS112C14_VREF_SOURCE_EXTERNAL,
+ ADS112C14_VREF_SOURCE_AVDD,
+};
+
+static const char * const ads112c14_vref_source_names[] = {
+ [ADS112C14_VREF_SOURCE_INTERNAL_2_5V] = "internal-2.5v",
+ [ADS112C14_VREF_SOURCE_INTERNAL_1_25V] = "internal-1.25v",
+ [ADS112C14_VREF_SOURCE_EXTERNAL] = "external",
+ [ADS112C14_VREF_SOURCE_AVDD] = "avdd",
+};
+
+/*
+ * Available gains as tenths (e.g. value 5 == 0.5 gain). Indexes correspond to
+ * ADS112C14_GAIN_CFG_GAIN values.
+ */
+static const u32 ads112c14_pga_gains_x10[] = {
+ 5, 10, 20, 40, 50, 80, 100, 160, /* 0 - 7 */
+ 200, 320, 500, 640, 1000, 1280, 2000, 2560, /* 8 - 15 */
+};
+
+#define ADS112C14_I2C_CRC8_POLYNOMIAL 0x07
+DECLARE_CRC8_TABLE(ads112c14_crc8_table);
+
+struct ads112c14_chip_info {
+ const char *name;
+ u8 device_id;
+ u32 resolution_bits;
+};
+
+/* Fixed channels for system monitor measurements. */
+
+#define ADS112C14_SYS_MON_CHANNEL_BASE 100
+
+enum {
+ ADS112C14_SYS_MON_CHANNEL_TEMP = ADS112C14_SYS_MON_CHANNEL_BASE,
+ ADS112C14_SYS_MON_CHANNEL_EXT_REF,
+ ADS112C14_SYS_MON_CHANNEL_AVDD,
+ ADS112C14_SYS_MON_CHANNEL_DVDD,
+ ADS112C14_SYS_MON_CHANNEL_SHORT,
+};
+
+static const struct iio_chan_spec ads112c14_sys_mon_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_TEMP,
+ .address = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE)
+ | BIT(IIO_CHAN_INFO_OFFSET),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_EXT_REF,
+ .address = 3,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_AVDD,
+ .address = 4,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_DVDD,
+ .address = 5,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = ADS112C14_SYS_MON_CHANNEL_SHORT,
+ .channel2 = ADS112C14_SYS_MON_CHANNEL_SHORT,
+ .differential = 1,
+ .address = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+struct ads112c14_measurement {
+ const char *label;
+ u32 vref_source;
+ u8 iunit;
+ u8 idac1_mag;
+ u8 idac2_mag;
+ u8 idac1_mux;
+ u8 idac2_mux;
+ u8 iadc_count;
+ u8 gain_val;
+ bool global_chop;
+ bool bipolar;
+ int scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
+};
+
+struct ads112c14_data {
+ const struct ads112c14_chip_info *chip_info;
+ struct regmap *regmap;
+ /* Synchronizes access to register value fields. */
+ struct mutex lock;
+ bool i2c_crc_enabled;
+ u32 avdd_uV;
+ u32 ext_ref_uV;
+ bool refp_is_avdd;
+ bool refn_is_gnd;
+ u32 ext_ref_ohms;
+ struct ads112c14_measurement *measurements;
+ u32 num_measurements;
+ u8 sys_mon_chan_short_gain_val;
+ int sys_mon_chan_short_scale_available[ARRAY_SIZE(ads112c14_pga_gains_x10)][2];
+ IIO_DECLARE_BUFFER_WITH_TS(__be32, scan, ADS112C14_MAX_MEASUREMENT_CHANNELS +
+ ARRAY_SIZE(ads112c14_sys_mon_channels));
+};
+
+static bool ads112c14_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADS112C14_REG_DEVICE_ID:
+ case ADS112C14_REG_REVISION_ID:
+ case ADS112C14_REG_STATUS_LSB:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool ads112c14_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADS112C14_REG_STATUS_MSB:
+ case ADS112C14_REG_STATUS_LSB:
+ case ADS112C14_REG_CONVERSION_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default ads112c14_reg_defaults[] = {
+ { ADS112C14_REG_DEVICE_CFG, 0 },
+ { ADS112C14_REG_DATA_RATE_CFG, 0 },
+ { ADS112C14_REG_MUX_CFG, 0 },
+ { ADS112C14_REG_GAIN_CFG, FIELD_PREP_CONST(ADS112C14_GAIN_CFG_GAIN, 1) },
+ { ADS112C14_REG_REFERENCE_CFG, 0 },
+ { ADS112C14_REG_DIGITAL_CFG, 0 },
+ { ADS112C14_REG_GPIO_CFG, 0 },
+ { ADS112C14_REG_GPIO_DATA_OUTPUT, 0 },
+ { ADS112C14_REG_IDAC_MAG_CFG, 0 },
+ { ADS112C14_REG_IDAC_MUX_CFG, FIELD_PREP_CONST(ADS112C14_IDAC_MUX_CFG_I2MUX, 1) },
+};
+
+/**
+ * ads112c14_i2c_read_bytes() - Read bytes from the device over I2C
+ * @client: I2C client for the device
+ * @cmd: Command to send to the device before reading
+ * @buf: Buffer to store the read bytes
+ * @len: Number of bytes to read
+ * @use_crc: Whether to use CRC8 for data integrity check
+ *
+ * If I2C_CRC is enabled, @use_crc may be set to true to perform a CRC8 check
+ * on the received data.
+ */
+static int ads112c14_i2c_read_bytes(struct i2c_client *client, u8 cmd,
+ u8 *buf, u8 len, bool use_crc)
+{
+ u8 rx_buf[4]; /* Up to 3 data bytes + 1 CRC byte. */
+ u8 rx_len;
+ int ret;
+
+ rx_len = len + (use_crc ? 1 : 0);
+
+ if (rx_len > sizeof(rx_buf))
+ return -EINVAL;
+
+ ret = i2c_smbus_read_i2c_block_data(client, cmd, rx_len, rx_buf);
+ if (ret < 0)
+ return ret;
+
+ if (use_crc) {
+ u8 crc = crc8(ads112c14_crc8_table, rx_buf, len, CRC8_INIT_VALUE);
+
+ if (crc != rx_buf[len])
+ return -EBADMSG;
+ }
+
+ memcpy(buf, rx_buf, len);
+
+ return 0;
+}
+
+/**
+ * ads112c14_regmap_bus_read() - Read a register from the device
+ * @context: Pointer to the device context
+ * @reg_buf: Register address to read
+ * @reg_size: Size of the register address (should be 1)
+ * @val_buf: Buffer to store the read value
+ * @val_size: Size of the value to read
+ *
+ * Custom regmap read function that also does CRC check when enabled.
+ */
+static int ads112c14_regmap_bus_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct ads112c14_data *data = context;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 *cmd = reg_buf;
+
+ if (reg_size != 1)
+ return -EINVAL;
+
+ return ads112c14_i2c_read_bytes(client, cmd[0], val_buf, val_size,
+ data->i2c_crc_enabled);
+}
+
+/**
+ * ads112c14_regmap_bus_write() - Write a register to the device
+ * @context: Pointer to the device context
+ * @data_buf: Buffer containing the register address and value to write
+ * @count: Number of bytes to write
+ *
+ * Custom regmap write function that also does readback with CRC check of
+ * nonvolatile registers when CRC is enabled.
+ */
+static int ads112c14_regmap_bus_write(void *context, const void *data_buf,
+ size_t count)
+{
+ struct ads112c14_data *data = context;
+ struct device *dev = regmap_get_device(data->regmap);
+ struct i2c_client *client = to_i2c_client(dev);
+ const u8 *tx = data_buf;
+ u8 reg, readback;
+ int ret;
+
+ if (count != 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(client, tx[0], tx[1]);
+ if (ret)
+ return ret;
+
+ reg = tx[0] & ~ADS112C14_CMD_WREG;
+
+ if (!data->i2c_crc_enabled || ads112c14_volatile_reg(dev, reg))
+ return 0;
+
+ ret = ads112c14_i2c_read_bytes(client, reg | ADS112C14_CMD_RREG,
+ &readback, sizeof(readback), true);
+ if (ret)
+ return ret;
+
+ if (readback != tx[1])
+ return -EIO;
+
+ return 0;
+}
+
+static const struct regmap_bus ads112c14_regmap_bus = {
+ .read = ads112c14_regmap_bus_read,
+ .write = ads112c14_regmap_bus_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config ads112c14_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = ADS112C14_CMD_RREG,
+ .write_flag_mask = ADS112C14_CMD_WREG,
+ .max_register = ADS112C14_REG_REG_MAP_CRC,
+ .writeable_reg = ads112c14_writeable_reg,
+ .volatile_reg = ads112c14_volatile_reg,
+ .reg_defaults = ads112c14_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ads112c14_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ads112c14_prepare_measurement_channel(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan)
+{
+ struct ads112c14_measurement *measurement = &data->measurements[chan->scan_index];
+ u32 refp_buf_en, refn_buf_en, ref_val, ref_sel;
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_MUX_CFG,
+ ADS112C14_MUX_CFG_AINP | ADS112C14_MUX_CFG_AINN,
+ FIELD_PREP(ADS112C14_MUX_CFG_AINP, chan->channel) |
+ FIELD_PREP(ADS112C14_MUX_CFG_AINN, chan->channel2));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_CODING,
+ !measurement->bipolar);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG,
+ ADS112C14_GAIN_CFG_SYS_MON |
+ ADS112C14_GAIN_CFG_GAIN,
+ FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, 0) |
+ FIELD_PREP(ADS112C14_GAIN_CFG_GAIN,
+ measurement->gain_val));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MAG_CFG,
+ ADS112C14_IDAC_MAG_CFG_I2MAG |
+ ADS112C14_IDAC_MAG_CFG_I1MAG,
+ FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I2MAG,
+ measurement->idac2_mag) |
+ FIELD_PREP(ADS112C14_IDAC_MAG_CFG_I1MAG,
+ measurement->idac1_mag));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_IDAC_MUX_CFG,
+ ADS112C14_IDAC_MUX_CFG_IUNIT |
+ ADS112C14_IDAC_MUX_CFG_I2MUX |
+ ADS112C14_IDAC_MUX_CFG_I1MUX,
+ FIELD_PREP(ADS112C14_IDAC_MUX_CFG_IUNIT,
+ measurement->iunit) |
+ FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I2MUX,
+ measurement->idac2_mux) |
+ FIELD_PREP(ADS112C14_IDAC_MUX_CFG_I1MUX,
+ measurement->idac1_mux));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DATA_RATE_CFG,
+ ADS112C14_DATA_RATE_CFG_GC_EN,
+ FIELD_PREP(ADS112C14_DATA_RATE_CFG_GC_EN,
+ measurement->global_chop));
+ if (ret)
+ return ret;
+
+ refp_buf_en = !data->refp_is_avdd &&
+ measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL;
+ refn_buf_en = !data->refn_is_gnd &&
+ measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL;
+
+ ref_val = measurement->vref_source == ADS112C14_VREF_SOURCE_INTERNAL_2_5V ?
+ ADS112C14_REFERENCE_CFG_REF_VAL_2_5V :
+ ADS112C14_REFERENCE_CFG_REF_VAL_1_25V;
+
+ switch (measurement->vref_source) {
+ case ADS112C14_VREF_SOURCE_AVDD:
+ ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_AVDD;
+ break;
+ case ADS112C14_VREF_SOURCE_EXTERNAL:
+ ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_EXTERNAL;
+ break;
+ default:
+ ref_sel = ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL;
+ break;
+ }
+
+ return regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG,
+ ADS112C14_REFERENCE_CFG_REFP_BUF_EN |
+ ADS112C14_REFERENCE_CFG_REFN_BUF_EN |
+ ADS112C14_REFERENCE_CFG_REF_VAL |
+ ADS112C14_REFERENCE_CFG_REF_SEL,
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REFP_BUF_EN,
+ refp_buf_en) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REFN_BUF_EN,
+ refn_buf_en) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL,
+ ref_val) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL,
+ ref_sel));
+}
+
+static int ads112c14_prepare_sys_mon_channel(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan)
+{
+ u32 gain_val;
+ int ret;
+
+ /*
+ * NB: IDAC registers are left as-is in case they are generating current
+ * needed for the external reference measurement.
+ */
+
+ /*
+ * All SYS_MON channels use GAIN of 1 to keep it simple. Other than
+ * the internal short channel, where it is useful in practice.
+ */
+ gain_val = chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT ?
+ data->sys_mon_chan_short_gain_val : 1;
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_GAIN_CFG,
+ ADS112C14_GAIN_CFG_SYS_MON |
+ ADS112C14_GAIN_CFG_GAIN,
+ FIELD_PREP(ADS112C14_GAIN_CFG_SYS_MON, chan->address) |
+ FIELD_PREP(ADS112C14_GAIN_CFG_GAIN, gain_val));
+ if (ret)
+ return ret;
+
+ /* All SYS_MON channels use signed data to keep it simple. */
+ ret = regmap_clear_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_CODING);
+ if (ret)
+ return ret;
+
+ /*
+ * REVISIT: if we implement regulator support for the REFOUT pin, we
+ * might need to make this voltage match what is required by that. In
+ * that case, we could also adjust GAIN so that we still get the same
+ * range.
+ */
+ /*
+ * NB: SYS_MON channels ignore REF_SEL except for the shorted input
+ * channel, so we set it here to internal reference to be consistent.
+ * If we ever need to make a measurement of shorted input with other
+ * reference source, we could add additional channels for that.
+ */
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_REFERENCE_CFG,
+ ADS112C14_REFERENCE_CFG_REF_VAL |
+ ADS112C14_REFERENCE_CFG_REF_SEL,
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_VAL,
+ ADS112C14_REFERENCE_CFG_REF_VAL_2_5V) |
+ FIELD_PREP(ADS112C14_REFERENCE_CFG_REF_SEL,
+ ADS112C14_REFERENCE_CFG_REF_SEL_INTERNAL));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ads112c14_single_conversion(struct ads112c14_data *data,
+ const struct iio_chan_spec *chan,
+ u8 *buf, bool for_scan)
+{
+ struct i2c_client *client = to_i2c_client(regmap_get_device(data->regmap));
+ u32 reg_val;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ ret = ads112c14_prepare_measurement_channel(data, chan);
+ if (ret)
+ return ret;
+ } else {
+ ret = ads112c14_prepare_sys_mon_channel(data, chan);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ ADS112C14_CONVERSION_CTRL_START);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(data->regmap,
+ ADS112C14_REG_STATUS_MSB, reg_val,
+ FIELD_GET(ADS112C14_STATUS_MSB_DRDY, reg_val),
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+ if (ret)
+ return ret;
+
+ /*
+ * When doing buffered read, we don't check the CRC, but rather pass it
+ * along with the raw data. This way, we don't silently drop samples
+ * with CRC errors, but rather leave it to userspace to decide what to
+ * do.
+ */
+ if (for_scan) {
+ u8 len = BITS_TO_BYTES(data->chip_info->resolution_bits) +
+ (data->i2c_crc_enabled ? 1 : 0);
+
+ ret = i2c_smbus_read_i2c_block_data(client, ADS112C14_CMD_RDATA,
+ len, buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ }
+
+ return ads112c14_i2c_read_bytes(client, ADS112C14_CMD_RDATA, buf,
+ BITS_TO_BYTES(data->chip_info->resolution_bits),
+ data->i2c_crc_enabled);
+}
+
+static int ads112c14_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct ads112c14_measurement *measurement = NULL;
+ const int *scale_avail;
+ u32 vref_uV, fsr_bits;
+
+ /* Selecting V_REF source is not implemented yet. */
+ vref_uV = ADS112C14_INT_REF1_mV * (MICRO / MILLI);
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ measurement = &data->measurements[chan->scan_index];
+ fsr_bits = data->chip_info->resolution_bits - measurement->bipolar;
+ } else {
+ /* All SYS_MON channels are using signed coding. */
+ fsr_bits = data->chip_info->resolution_bits - 1;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ u8 buf[3];
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ads112c14_single_conversion(data, chan, buf, false);
+ if (ret)
+ return ret;
+
+ switch (data->chip_info->resolution_bits) {
+ case 16:
+ *val = get_unaligned_be16(buf);
+ break;
+ case 24:
+ *val = get_unaligned_be24(buf);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!measurement || measurement->bipolar)
+ *val = sign_extend32(*val, fsr_bits);
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ /* TS_TC (typical) = 405 uV/°C */
+ *val = MILLI * vref_uV / 405;
+ *val2 = fsr_bits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ guard(mutex)(&data->lock);
+
+ scale_avail = measurement->scale_available[measurement->gain_val];
+ *val = scale_avail[0];
+ *val2 = scale_avail[1];
+
+ return IIO_VAL_DECIMAL64_PICO;
+ }
+
+ if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) {
+ u8 idx;
+
+ guard(mutex)(&data->lock);
+
+ idx = data->sys_mon_chan_short_gain_val;
+ scale_avail = data->sys_mon_chan_short_scale_available[idx];
+ *val = scale_avail[0];
+ *val2 = scale_avail[1];
+
+ return IIO_VAL_DECIMAL64_PICO;
+ }
+
+ *val = vref_uV / (MICRO / MILLI);
+
+ /*
+ * Some SYS_MON channels (ext ref, AVDD, DVDD) need to be
+ * multiplied by 8 to account for internal attenuation of / 8.
+ */
+ switch (chan->address) {
+ case 3 ... 5:
+ *val2 = fsr_bits - 3;
+ break;
+ default:
+ *val2 = fsr_bits;
+ break;
+ }
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ /* Only the temperature channel has an offset. */
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+ /*
+ * Die temperature [°C] = 25°C + (Measured voltage – TS_Offset) / TS_TC
+ * TS_TC (typical) = 405 uV/°C
+ * TS_Offset (typical) = 119.5 mV
+ */
+ *val = div_s64((s64)(25 * 405 - 119500) * BIT(fsr_bits), vref_uV);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_read_avail(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ struct ads112c14_measurement *measurement;
+
+ guard(mutex)(&data->lock);
+
+ measurement = &data->measurements[chan->scan_index];
+ *vals = (const int *)measurement->scale_available;
+ *length = 2 * ARRAY_SIZE(measurement->scale_available);
+ *type = IIO_VAL_DECIMAL64_PICO;
+ return IIO_AVAIL_LIST;
+ }
+
+ if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) {
+ guard(mutex)(&data->lock);
+
+ *vals = (const int *)data->sys_mon_chan_short_scale_available;
+ *length = 2 * ARRAY_SIZE(data->sys_mon_chan_short_scale_available);
+ *type = IIO_VAL_DECIMAL64_PICO;
+ return IIO_AVAIL_LIST;
+ }
+
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ const int (*scale_avail)[2];
+ u8 *gain_val;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->lock);
+
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ struct ads112c14_measurement *measurement;
+
+ measurement = &data->measurements[chan->scan_index];
+ scale_avail = measurement->scale_available;
+ gain_val = &measurement->gain_val;
+ } else if (chan->channel == ADS112C14_SYS_MON_CHANNEL_SHORT) {
+ scale_avail = data->sys_mon_chan_short_scale_available;
+ gain_val = &data->sys_mon_chan_short_gain_val;
+ } else {
+ return -EINVAL;
+ }
+
+ for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) {
+ if (iio_val_s64_compose(val, val2) ==
+ iio_val_s64_compose(scale_avail[i][0], scale_avail[i][1])) {
+ *gain_val = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ads112c14_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_DECIMAL64_PICO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static int ads112c14_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(data->regmap, reg, readval);
+
+ return regmap_write(data->regmap, reg, writeval);
+}
+
+static int ads112c14_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, char *label)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ const char *label_source;
+
+ /* measurement channels */
+ if (chan->channel < ADS112C14_SYS_MON_CHANNEL_BASE) {
+ struct ads112c14_measurement *measurement;
+
+ measurement = &data->measurements[chan->scan_index];
+ if (!measurement->label)
+ return -EINVAL;
+
+ return sysfs_emit(label, "%s\n", measurement->label);
+ }
+
+ /* System monitor channels. */
+ switch (chan->channel) {
+ case ADS112C14_SYS_MON_CHANNEL_TEMP:
+ label_source = "Internal temperature sensor";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_EXT_REF:
+ label_source = "External reference";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_AVDD:
+ label_source = "AVDD";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_DVDD:
+ label_source = "DVDD";
+ break;
+ case ADS112C14_SYS_MON_CHANNEL_SHORT:
+ label_source = "Internal short (internal reference source)";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sysfs_emit(label, "%s\n", label_source);
+}
+
+static irqreturn_t ads112c14_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ u32 offset = 0;
+ u32 i;
+ int ret;
+
+ iio_for_each_active_channel(indio_dev, i) {
+ const struct iio_chan_spec *chan = &indio_dev->channels[i];
+
+ ret = ads112c14_single_conversion(data, chan,
+ (u8 *)&data->scan[offset++],
+ true);
+ if (ret) {
+ dev_err_once(indio_dev->dev.parent,
+ "failed to read channel %d: %pe; additional errors will be suppressed\n",
+ chan->channel, ERR_PTR(ret));
+ goto out;
+ }
+ }
+
+ iio_push_to_buffers_with_ts(indio_dev, data->scan,
+ sizeof(data->scan), pf->timestamp);
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info ads112c14_info = {
+ .read_raw = ads112c14_read_raw,
+ .read_avail = ads112c14_read_avail,
+ .write_raw = ads112c14_write_raw,
+ .write_raw_get_fmt = ads112c14_write_raw_get_fmt,
+ .debugfs_reg_access = ads112c14_debugfs_reg_access,
+ .read_label = ads112c14_read_label,
+};
+
+static int ads112c14_populate_idac_mag(u32 current_nA, u8 *idac_mag)
+{
+ u32 current_uA = current_nA / (NANO / MICRO);
+
+ /* Convert microamps to IMAG bits */
+ if (current_uA == 1)
+ *idac_mag = 1;
+ else if (in_range(current_uA, 10, 100) && current_uA % 10 == 0)
+ *idac_mag = current_uA / 10 + 1;
+ else
+ return dev_err_probe(NULL, -EINVAL,
+ "invalid excitation-current-nanoamp value\n");
+
+ return 0;
+}
+
+static int ads112c14_parse_channels(struct iio_dev *indio_dev,
+ bool *need_avdd_ref, bool *need_ext_ref)
+{
+ struct ads112c14_data *data = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *channels;
+ u32 num_child_nodes, i, pair[2];
+ int ret;
+
+ *need_avdd_ref = false;
+ *need_ext_ref = false;
+
+ num_child_nodes = device_get_named_child_node_count(dev, "channel");
+
+ data->measurements = devm_kcalloc(dev, num_child_nodes,
+ sizeof(*data->measurements), GFP_KERNEL);
+ if (!data->measurements)
+ return -ENOMEM;
+
+ channels = devm_kcalloc(dev, num_child_nodes +
+ ARRAY_SIZE(ads112c14_sys_mon_channels) + 1,
+ sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_named_child_node_scoped(dev, child, "channel") {
+ struct ads112c14_measurement *measurement = &data->measurements[i];
+ struct iio_chan_spec *spec = &channels[i];
+
+ spec->indexed = 1;
+ spec->scan_index = i;
+ measurement->gain_val = 1;
+
+ if (fwnode_property_present(child, "label")) {
+ ret = fwnode_property_read_string(child, "label", &measurement->label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read label property\n");
+ }
+
+ if (fwnode_property_present(child, "single-channel")) {
+ ret = fwnode_property_read_u32(child, "single-channel",
+ &pair[0]);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read single-channel property\n");
+
+ if (pair[0] >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "single-channel value must be between 0 and 7\n");
+
+ spec->channel = pair[0];
+ /*
+ * NB: channel2 is unused by iio core code in this case.
+ * Let's us avoid special case for negative input mux
+ * for single-ended channels when taking measurements.
+ */
+ spec->channel2 = ADS112C14_MUX_CFG_AIN_GND;
+ } else if (fwnode_property_present(child, "diff-channels")) {
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ pair, ARRAY_SIZE(pair));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read diff-channels property\n");
+
+ if (pair[0] >= 8 || pair[1] >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "diff-channels values must be between 0 and 7\n");
+
+ spec->differential = 1;
+ spec->channel = pair[0];
+ spec->channel2 = pair[1];
+ } else {
+ return dev_err_probe(dev, -EINVAL,
+ "channel node missing channel type property\n");
+ }
+
+ if (fwnode_property_present(child, "excitation-channels")) {
+ ret = fwnode_property_count_u32(child, "excitation-channels");
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to read excitation-channels property\n");
+
+ if (ret < 1 || ret > 2)
+ return dev_err_probe(dev, -EINVAL,
+ "excitation-channels property must have 1 or 2 values\n");
+
+ measurement->iadc_count = ret;
+ pair[1] = 0;
+
+ ret = fwnode_property_read_u32_array(child, "excitation-channels",
+ pair, measurement->iadc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read excitation-channels property\n");
+
+ if (pair[0] >= 8 || pair[1] >= 8)
+ return dev_err_probe(dev, -EINVAL,
+ "excitation-channels values must be between 0 and 7\n");
+
+ measurement->idac1_mux = pair[0];
+ measurement->idac2_mux = measurement->iadc_count > 1 ? pair[1] : 0;
+
+ ret = fwnode_property_read_u32_array(child, "excitation-current-nanoamp",
+ pair, measurement->iadc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read excitation-current-nanoamp property\n");
+
+ if (pair[0] <= 100 * (NANO / MICRO) &&
+ (measurement->iadc_count == 1 || pair[1] <= 100 * (NANO / MICRO))) {
+ /*
+ * If both values are 100µA or less, then we can
+ * use IUNIT = 1µA for better precision.
+ */
+ ret = ads112c14_populate_idac_mag(pair[0],
+ &measurement->idac1_mag);
+ if (ret)
+ return ret;
+
+ if (measurement->iadc_count > 1) {
+ ret = ads112c14_populate_idac_mag(pair[1],
+ &measurement->idac2_mag);
+ if (ret)
+ return ret;
+ }
+ } else {
+ /*
+ * Otherwise, IUINT is 10µA (flag set) and so
+ * IxMAG is 1/10 of the actual current.
+ */
+ measurement->iunit = 1;
+
+ ret = ads112c14_populate_idac_mag(pair[0] / 10,
+ &measurement->idac1_mag);
+ if (ret)
+ return ret;
+
+ if (measurement->iadc_count > 1) {
+ ret = ads112c14_populate_idac_mag(pair[1] / 10,
+ &measurement->idac2_mag);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ measurement->bipolar = fwnode_property_read_bool(child, "bipolar");
+ measurement->global_chop = fwnode_property_read_bool(child,
+ "input-chopping");
+
+ if (fwnode_property_present(child, "reference-sources")) {
+ ret = fwnode_property_match_property_string(child,
+ "reference-sources", ads112c14_vref_source_names,
+ ARRAY_SIZE(ads112c14_vref_source_names));
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "invalid reference-sources value\n");
+
+ measurement->vref_source = ret;
+ }
+
+ if (measurement->vref_source == ADS112C14_VREF_SOURCE_AVDD)
+ *need_avdd_ref = true;
+ if (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL)
+ *need_ext_ref = true;
+
+ spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE);
+ spec->info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE);
+
+ /*
+ * If reference source is resistor rather than voltage supply,
+ * then the measurement is effectively a resistance measurement.
+ */
+ spec->type = (measurement->vref_source == ADS112C14_VREF_SOURCE_EXTERNAL &&
+ data->ext_ref_ohms) ? IIO_RESISTANCE : IIO_VOLTAGE;
+
+ if (spec->type == IIO_RESISTANCE)
+ spec->differential = 0;
+
+ spec->scan_type = (struct iio_scan_type){
+ .format = measurement->bipolar ?
+ IIO_SCAN_FORMAT_SIGNED_INT :
+ IIO_SCAN_FORMAT_UNSIGNED_INT,
+ .realbits = data->chip_info->resolution_bits,
+ .storagebits = 32,
+ .shift = 32 - data->chip_info->resolution_bits,
+ .endianness = IIO_BE,
+ };
+
+ i++;
+ }
+
+ data->num_measurements = i;
+ if (data->num_measurements > ADS112C14_MAX_MEASUREMENT_CHANNELS)
+ return dev_err_probe(dev, -EINVAL,
+ "too many measurement channels defined\n");
+
+ memcpy(channels + i, ads112c14_sys_mon_channels, sizeof(ads112c14_sys_mon_channels));
+
+ for (u32 j = 0; j < ARRAY_SIZE(ads112c14_sys_mon_channels); j++) {
+ struct iio_chan_spec *spec = &channels[i];
+
+ /* Update the template that was already copied with dynamic values. */
+ spec->scan_index = i;
+ spec->scan_type = (struct iio_scan_type){
+ .format = IIO_SCAN_FORMAT_SIGNED_INT,
+ .realbits = data->chip_info->resolution_bits,
+ .storagebits = 32,
+ .shift = 32 - data->chip_info->resolution_bits,
+ .endianness = IIO_BE,
+ };
+
+ i++;
+ }
+
+ channels[i] = IIO_CHAN_SOFT_TIMESTAMP(i);
+ i++;
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = i;
+
+ return 0;
+}
+
+static void ads112c14_populate_scale_available(int (*scale_avail)[2],
+ u32 full_scale, u32 fsr_bits)
+{
+ for (u32 i = 0; i < ARRAY_SIZE(ads112c14_pga_gains_x10); i++) {
+ u64 gain_x10 = ads112c14_pga_gains_x10[i];
+ s64 scale;
+
+ scale = div64_u64((u64)PICO * 10U * full_scale,
+ gain_x10 * BIT(fsr_bits));
+
+ iio_val_s64_decompose(scale, &scale_avail[i][0],
+ &scale_avail[i][1]);
+ }
+}
+
+static void ads112c14_populate_tables(struct ads112c14_data *data)
+{
+ u32 full_scale, fsr_bits;
+
+ for (u32 i = 0; i < data->num_measurements; i++) {
+ struct ads112c14_measurement *measurement = &data->measurements[i];
+
+ switch (measurement->vref_source) {
+ case ADS112C14_VREF_SOURCE_EXTERNAL:
+ if (data->ext_ref_ohms)
+ full_scale = data->ext_ref_ohms;
+ else
+ full_scale = data->ext_ref_uV / (MICRO / MILLI);
+ break;
+ case ADS112C14_VREF_SOURCE_AVDD:
+ full_scale = data->avdd_uV / (MICRO / MILLI);
+ break;
+ case ADS112C14_VREF_SOURCE_INTERNAL_1_25V:
+ full_scale = ADS112C14_INT_REF0_mV;
+ break;
+ default:
+ full_scale = ADS112C14_INT_REF1_mV;
+ break;
+ }
+
+ fsr_bits = data->chip_info->resolution_bits - measurement->bipolar;
+
+ ads112c14_populate_scale_available(measurement->scale_available,
+ full_scale, fsr_bits);
+ }
+
+ /* For now, assuming all sys_mon channels are using 2.5V reference. */
+ full_scale = ADS112C14_INT_REF1_mV;
+ fsr_bits = data->chip_info->resolution_bits - 1;
+
+ ads112c14_populate_scale_available(data->sys_mon_chan_short_scale_available,
+ full_scale, fsr_bits);
+}
+
+static int ads112c14_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const struct ads112c14_chip_info *info;
+ struct iio_dev *indio_dev;
+ struct ads112c14_data *data;
+ bool need_avdd_ref, need_ext_ref;
+ u32 refp_uV = 0;
+ u32 refn_uV = 0;
+ u32 reg_val;
+ int ret;
+
+ info = i2c_get_match_data(client);
+ if (!info)
+ return dev_err_probe(dev, -ENODEV, "missing match data\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->chip_info = info;
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ if (device_property_present(dev, "ti,refp-refn-resistor-ohms")) {
+ ret = device_property_read_u32(dev, "ti,refp-refn-resistor-ohms",
+ &data->ext_ref_ohms);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read ti,refp-refn-resistor-ohms property\n");
+ }
+
+ ret = ads112c14_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable(dev, "dvdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get dvdd regulator\n");
+
+ if (need_avdd_ref) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "avdd");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get avdd voltage\n");
+
+ data->avdd_uV = ret;
+ } else {
+ ret = devm_regulator_get_enable(dev, "avdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get avdd regulator\n");
+ }
+
+ if (device_property_present(dev, "refp-supply")) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "refp");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get refp voltage\n");
+
+ refp_uV = ret;
+
+ struct fwnode_handle *refp_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(dev->fwnode, "refp-supply", 0);
+ if (IS_ERR(refp_fwnode))
+ return dev_err_probe(dev, PTR_ERR(refp_fwnode),
+ "failed to get refp fwnode\n");
+
+ struct fwnode_handle *avdd_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(dev->fwnode, "avdd-supply", 0);
+ if (IS_ERR(avdd_fwnode))
+ return dev_err_probe(dev, PTR_ERR(avdd_fwnode),
+ "failed to get avdd fwnode\n");
+
+ /* REFP buffer should not be enabled when connected to AVDD */
+ data->refp_is_avdd = refp_fwnode == avdd_fwnode;
+ }
+
+ if (device_property_present(dev, "refn-supply")) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "refn");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get refn voltage\n");
+
+ refn_uV = ret;
+ } else {
+ data->refn_is_gnd = true;
+ }
+
+ data->ext_ref_uV = refp_uV - refn_uV;
+
+ if (data->ext_ref_uV && data->ext_ref_ohms)
+ return dev_err_probe(dev, -EINVAL,
+ "ti,refp-refn-resistor-ohms property should not be present when refp-supply or refn-supply is present\n");
+
+ if (need_ext_ref && !data->ext_ref_uV && !data->ext_ref_ohms)
+ return dev_err_probe(dev, -EINVAL,
+ "external reference measurements require either refp-supply or ti,refp-refn-resistor-ohms property\n");
+
+ /* It takes some time for the internal reference to stabilize. */
+ fsleep(10 * USEC_PER_MSEC);
+
+ data->regmap = devm_regmap_init(dev, &ads112c14_regmap_bus, data,
+ &ads112c14_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "failed to init regmap\n");
+
+ /*
+ * Write magic reset value (0x16) to ensure known state. The reset may
+ * cause an error because of failing to get the I2C ACK at the end of
+ * the message. The device still gets reset so it is safe to ignore the
+ * return value here. If something else is wrong, later read/write will
+ * likely have the same error.
+ */
+ regmap_write(data->regmap, ADS112C14_REG_CONVERSION_CTRL,
+ FIELD_PREP(ADS112C14_CONVERSION_CTRL_RESET, 0x16));
+
+ fsleep(ADS112C14_DELAY_RESET_US);
+
+ ret = regmap_read(data->regmap, ADS112C14_REG_STATUS_MSB, &reg_val);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(ADS112C14_STATUS_MSB_RESETN, reg_val))
+ return dev_err_probe(dev, -EIO, "reset failed\n");
+
+ /* Default gain after reset is 1. */
+ data->sys_mon_chan_short_gain_val = 1;
+
+ /*
+ * Clear reset bit to prepare for next probe. And clear AVDD fault since
+ * that happens on every reset.
+ */
+ ret = regmap_write(data->regmap, ADS112C14_REG_STATUS_MSB,
+ ADS112C14_STATUS_MSB_RESETN |
+ ADS112C14_STATUS_MSB_AVDD_UVN);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(data->regmap, ADS112C14_REG_DIGITAL_CFG,
+ ADS112C14_DIGITAL_CFG_I2C_CRC_EN);
+ if (ret)
+ return ret;
+
+ data->i2c_crc_enabled = true;
+
+ ret = regmap_read(data->regmap, ADS112C14_REG_DEVICE_ID, &reg_val);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val) != info->device_id)
+ dev_info(dev, "device ID mismatch, expected 0x%X, got 0x%lX\n",
+ info->device_id,
+ FIELD_GET(ADS112C14_DEVICE_ID_BITS, reg_val));
+
+ ret = regmap_update_bits(data->regmap, ADS112C14_REG_DEVICE_CFG,
+ ADS112C14_DEVICE_CFG_CONV_MODE,
+ FIELD_PREP(ADS112C14_DEVICE_CFG_CONV_MODE,
+ ADS112C14_DEVICE_CFG_CONV_MODE_SINGLE_SHOT));
+ if (ret)
+ return ret;
+
+ ads112c14_populate_tables(data);
+
+ indio_dev->name = info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads112c14_info;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads112c14_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct ads112c14_chip_info ads112c14_chip_info = {
+ .name = "ads112c14",
+ .device_id = 0xE,
+ .resolution_bits = 16,
+};
+
+static const struct ads112c14_chip_info ads122c14_chip_info = {
+ .name = "ads122c14",
+ .device_id = 0xF,
+ .resolution_bits = 24,
+};
+
+static const struct of_device_id ads112c14_of_match[] = {
+ { .compatible = "ti,ads112c14", .data = &ads112c14_chip_info },
+ { .compatible = "ti,ads122c14", .data = &ads122c14_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads112c14_of_match);
+
+static const struct i2c_device_id ads112c14_id[] = {
+ { .name = "ads112c14", .driver_data = (kernel_ulong_t)&ads112c14_chip_info },
+ { .name = "ads122c14", .driver_data = (kernel_ulong_t)&ads122c14_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ads112c14_id);
+
+static int ads112c14_i2c_add_driver(struct i2c_driver *driver)
+{
+ crc8_populate_msb(ads112c14_crc8_table, ADS112C14_I2C_CRC8_POLYNOMIAL);
+
+ return i2c_add_driver(driver);
+}
+
+static struct i2c_driver ads112c14_driver = {
+ .driver = {
+ .name = "ads112c14",
+ .of_match_table = ads112c14_of_match,
+ },
+ .probe = ads112c14_probe,
+ .id_table = ads112c14_id,
+};
+module_driver(ads112c14_driver, ads112c14_i2c_add_driver, i2c_del_driver);
+
+MODULE_AUTHOR("David Lechner (TI) <dlechner@baylibre.com>");
+MODULE_DESCRIPTION("TI ADS112C14 I2C ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
index 45de47778809..49fbc63508ac 100644
--- a/drivers/iio/adc/ti-ads124s08.c
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -348,8 +348,8 @@ static int ads124s_probe(struct spi_device *spi)
}
static const struct spi_device_id ads124s_id[] = {
- { "ads124s06", ADS124S06_ID },
- { "ads124s08", ADS124S08_ID },
+ { .name = "ads124s06", .driver_data = ADS124S06_ID },
+ { .name = "ads124s08", .driver_data = ADS124S08_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, ads124s_id);
diff --git a/drivers/iio/adc/ti-ads1298.c b/drivers/iio/adc/ti-ads1298.c
index 9e47ace16acd..06c6353aa1b2 100644
--- a/drivers/iio/adc/ti-ads1298.c
+++ b/drivers/iio/adc/ti-ads1298.c
@@ -743,7 +743,7 @@ static int ads1298_probe(struct spi_device *spi)
}
static const struct spi_device_id ads1298_id[] = {
- { "ads1298" },
+ { .name = "ads1298" },
{ }
};
MODULE_DEVICE_TABLE(spi, ads1298_id);
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index a585621b0bc3..1d25f887cadb 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -917,9 +917,9 @@ static const struct of_device_id ads131e08_of_match[] = {
MODULE_DEVICE_TABLE(of, ads131e08_of_match);
static const struct spi_device_id ads131e08_ids[] = {
- { "ads131e04", (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
- { "ads131e06", (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
- { "ads131e08", (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
+ { .name = "ads131e04", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e04] },
+ { .name = "ads131e06", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e06] },
+ { .name = "ads131e08", .driver_data = (kernel_ulong_t)&ads131e08_info_tbl[ads131e08] },
{ }
};
MODULE_DEVICE_TABLE(spi, ads131e08_ids);
diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c
index 36203ce37c89..2f8f75c8216b 100644
--- a/drivers/iio/adc/ti-ads131m02.c
+++ b/drivers/iio/adc/ti-ads131m02.c
@@ -943,11 +943,11 @@ static const struct of_device_id ads131m_of_match[] = {
MODULE_DEVICE_TABLE(of, ads131m_of_match);
static const struct spi_device_id ads131m_id[] = {
- { "ads131m02", (kernel_ulong_t)&ads131m02_config },
- { "ads131m03", (kernel_ulong_t)&ads131m03_config },
- { "ads131m04", (kernel_ulong_t)&ads131m04_config },
- { "ads131m06", (kernel_ulong_t)&ads131m06_config },
- { "ads131m08", (kernel_ulong_t)&ads131m08_config },
+ { .name = "ads131m02", .driver_data = (kernel_ulong_t)&ads131m02_config },
+ { .name = "ads131m03", .driver_data = (kernel_ulong_t)&ads131m03_config },
+ { .name = "ads131m04", .driver_data = (kernel_ulong_t)&ads131m04_config },
+ { .name = "ads131m06", .driver_data = (kernel_ulong_t)&ads131m06_config },
+ { .name = "ads131m08", .driver_data = (kernel_ulong_t)&ads131m08_config },
{ }
};
MODULE_DEVICE_TABLE(spi, ads131m_id);
diff --git a/drivers/iio/adc/ti-ads7138.c b/drivers/iio/adc/ti-ads7138.c
index af87f5f19a0f..da82a6947b2b 100644
--- a/drivers/iio/adc/ti-ads7138.c
+++ b/drivers/iio/adc/ti-ads7138.c
@@ -227,6 +227,26 @@ static int ads7138_osr_to_bits(int osr)
return -EINVAL;
}
+static int ads7138_read_statistics(const struct i2c_client *client, u8 reg,
+ u8 *out_values, u8 length)
+{
+ int ret;
+
+ /* Disable statistics update so the value is not updated mid read */
+ ret = ads7138_i2c_clear_bit(client, ADS7138_REG_GENERAL_CFG,
+ ADS7138_GENERAL_CFG_STATS_EN);
+ if (ret)
+ return ret;
+
+ ret = ads7138_i2c_read_block(client, reg, out_values, length);
+ if (ret)
+ return ret;
+
+ /* Enable statistics update after read */
+ return ads7138_i2c_set_bit(client, ADS7138_REG_GENERAL_CFG,
+ ADS7138_GENERAL_CFG_STATS_EN);
+}
+
static int ads7138_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -236,28 +256,32 @@ static int ads7138_read_raw(struct iio_dev *indio_dev,
u8 values[2];
switch (mask) {
+ /*
+ * Reading the statistics registers reinitializes them. This is
+ * unfortunate but necessary to prevent data races.
+ */
case IIO_CHAN_INFO_RAW:
- ret = ads7138_i2c_read_block(data->client,
- ADS7138_REG_RECENT_LSB_CH(chan->channel),
- values, ARRAY_SIZE(values));
+ ret = ads7138_read_statistics(data->client,
+ ADS7138_REG_RECENT_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_PEAK:
- ret = ads7138_i2c_read_block(data->client,
- ADS7138_REG_MAX_LSB_CH(chan->channel),
- values, ARRAY_SIZE(values));
+ ret = ads7138_read_statistics(data->client,
+ ADS7138_REG_MAX_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_TROUGH:
- ret = ads7138_i2c_read_block(data->client,
- ADS7138_REG_MIN_LSB_CH(chan->channel),
- values, ARRAY_SIZE(values));
+ ret = ads7138_read_statistics(data->client,
+ ADS7138_REG_MIN_LSB_CH(chan->channel),
+ values, ARRAY_SIZE(values));
if (ret)
return ret;
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index 39a074bce6d5..d27c1f2885fd 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -619,18 +619,18 @@ static int ti_ads7950_probe(struct spi_device *spi)
}
static const struct spi_device_id ti_ads7950_id[] = {
- { "ads7950", (kernel_ulong_t)&ti_ads7950_chip_info },
- { "ads7951", (kernel_ulong_t)&ti_ads7951_chip_info },
- { "ads7952", (kernel_ulong_t)&ti_ads7952_chip_info },
- { "ads7953", (kernel_ulong_t)&ti_ads7953_chip_info },
- { "ads7954", (kernel_ulong_t)&ti_ads7954_chip_info },
- { "ads7955", (kernel_ulong_t)&ti_ads7955_chip_info },
- { "ads7956", (kernel_ulong_t)&ti_ads7956_chip_info },
- { "ads7957", (kernel_ulong_t)&ti_ads7957_chip_info },
- { "ads7958", (kernel_ulong_t)&ti_ads7958_chip_info },
- { "ads7959", (kernel_ulong_t)&ti_ads7959_chip_info },
- { "ads7960", (kernel_ulong_t)&ti_ads7960_chip_info },
- { "ads7961", (kernel_ulong_t)&ti_ads7961_chip_info },
+ { .name = "ads7950", .driver_data = (kernel_ulong_t)&ti_ads7950_chip_info },
+ { .name = "ads7951", .driver_data = (kernel_ulong_t)&ti_ads7951_chip_info },
+ { .name = "ads7952", .driver_data = (kernel_ulong_t)&ti_ads7952_chip_info },
+ { .name = "ads7953", .driver_data = (kernel_ulong_t)&ti_ads7953_chip_info },
+ { .name = "ads7954", .driver_data = (kernel_ulong_t)&ti_ads7954_chip_info },
+ { .name = "ads7955", .driver_data = (kernel_ulong_t)&ti_ads7955_chip_info },
+ { .name = "ads7956", .driver_data = (kernel_ulong_t)&ti_ads7956_chip_info },
+ { .name = "ads7957", .driver_data = (kernel_ulong_t)&ti_ads7957_chip_info },
+ { .name = "ads7958", .driver_data = (kernel_ulong_t)&ti_ads7958_chip_info },
+ { .name = "ads7959", .driver_data = (kernel_ulong_t)&ti_ads7959_chip_info },
+ { .name = "ads7960", .driver_data = (kernel_ulong_t)&ti_ads7960_chip_info },
+ { .name = "ads7961", .driver_data = (kernel_ulong_t)&ti_ads7961_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
index ba5e240aa41a..8ad070b5ec4c 100644
--- a/drivers/iio/adc/ti-ads8688.c
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -453,8 +453,8 @@ static int ads8688_probe(struct spi_device *spi)
}
static const struct spi_device_id ads8688_id[] = {
- { "ads8684", ID_ADS8684 },
- { "ads8688", ID_ADS8688 },
+ { .name = "ads8684", .driver_data = ID_ADS8684 },
+ { .name = "ads8688", .driver_data = ID_ADS8688 },
{ }
};
MODULE_DEVICE_TABLE(spi, ads8688_id);
diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c
index 7e57006a8a12..d248bbea0d31 100644
--- a/drivers/iio/adc/ti-lmp92064.c
+++ b/drivers/iio/adc/ti-lmp92064.c
@@ -357,7 +357,7 @@ static int lmp92064_adc_probe(struct spi_device *spi)
}
static const struct spi_device_id lmp92064_id_table[] = {
- { "lmp92064" },
+ { .name = "lmp92064" },
{ }
};
MODULE_DEVICE_TABLE(spi, lmp92064_id_table);
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
index 94bbf5afe30e..ea776f80de72 100644
--- a/drivers/iio/adc/ti-tlc4541.c
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -240,8 +240,8 @@ static const struct of_device_id tlc4541_dt_ids[] = {
MODULE_DEVICE_TABLE(of, tlc4541_dt_ids);
static const struct spi_device_id tlc4541_id[] = {
- { "tlc3541", TLC3541 },
- { "tlc4541", TLC4541 },
+ { .name = "tlc3541", .driver_data = TLC3541 },
+ { .name = "tlc4541", .driver_data = TLC4541 },
{ }
};
MODULE_DEVICE_TABLE(spi, tlc4541_id);
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
index aba4b10a17ac..125f6d7e2d84 100644
--- a/drivers/iio/adc/ti-tsc2046.c
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -121,11 +121,6 @@ struct tsc2046_adc_group_layout {
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;
@@ -141,7 +136,6 @@ enum tsc2046_state {
struct tsc2046_adc_priv {
struct spi_device *spi;
- const struct tsc2046_adc_dcfg *dcfg;
bool internal_vref;
struct iio_trigger *trig;
@@ -214,11 +208,6 @@ const struct iio_chan_spec name ## _channels[] = { \
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.
@@ -739,7 +728,6 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv)
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;
@@ -752,10 +740,6 @@ static int tsc2046_adc_probe(struct spi_device *spi)
return -EINVAL;
}
- dcfg = spi_get_device_match_data(spi);
- if (!dcfg)
- return -EINVAL;
-
spi->mode &= ~SPI_MODE_X_MASK;
spi->mode |= SPI_MODE_0;
ret = spi_setup(spi);
@@ -767,14 +751,13 @@ static int tsc2046_adc_probe(struct spi_device *spi)
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->channels = tsc2046_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(tsc2046_adc_channels);
indio_dev->info = &tsc2046_adc_info;
ret = devm_regulator_get_enable_read_voltage(dev, "vref");
@@ -829,13 +812,13 @@ static int tsc2046_adc_probe(struct spi_device *spi)
}
static const struct of_device_id ads7950_of_table[] = {
- { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e },
+ { .compatible = "ti,tsc2046e-adc" },
{ }
};
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 },
+ { .name = "tsc2046e-adc" },
{ }
};
MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids);
diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c
new file mode 100644
index 000000000000..1b55d343982e
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-core.c
@@ -0,0 +1,1052 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon core driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/limits.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/units.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+
+#include "versal-sysmon.h"
+
+/*
+ * Oversampling ratio values exposed to userspace via IIO.
+ * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x.
+ */
+static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 };
+
+/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */
+#define SYSMON_TEMP_HYST_MASK BIT(1)
+
+/* Compute alarm register offset from a channel address */
+#define SYSMON_ALARM_OFFSET(addr) \
+ (SYSMON_ALARM_REG + ((addr) / SYSMON_ALARM_BITS_PER_REG) * SYSMON_REG_STRIDE)
+
+#define SYSMON_CHAN_TEMP(_chan, _address, _name) \
+{ \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .address = _address, \
+ .channel = _chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = _name, \
+}
+
+enum sysmon_alarm_bit {
+ SYSMON_BIT_ALARM0 = 0,
+ SYSMON_BIT_ALARM1 = 1,
+ SYSMON_BIT_ALARM2 = 2,
+ SYSMON_BIT_ALARM3 = 3,
+ SYSMON_BIT_ALARM4 = 4,
+ SYSMON_BIT_TEMP = 9,
+};
+
+/* Temperature event specification: rising threshold + hysteresis only */
+static const struct iio_event_spec sysmon_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),
+ },
+};
+
+/* Supply event specifications */
+static const struct iio_event_spec sysmon_supply_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 temperature channels (always present).
+ *
+ * These are hardware-computed aggregate registers across all active
+ * temperature satellites:
+ * temp: current max temperature across all active satellites
+ * min: current min temperature across all active satellites
+ * max_max: highest peak recorded since last hardware reset
+ * min_min: lowest trough recorded since last hardware reset
+ */
+static const struct iio_chan_spec temp_channels[] = {
+ SYSMON_CHAN_TEMP(0, SYSMON_TEMP_MAX, "temp"),
+ SYSMON_CHAN_TEMP(1, SYSMON_TEMP_MIN, "min"),
+ SYSMON_CHAN_TEMP(2, SYSMON_TEMP_MAX_MAX, "max_max"),
+ SYSMON_CHAN_TEMP(3, SYSMON_TEMP_MIN_MIN, "min_min"),
+};
+
+static void sysmon_q8p7_to_millicelsius(s16 raw_data, int *val)
+{
+ *val = (raw_data * MILLIDEGREE_PER_DEGREE) >> SYSMON_FRACTIONAL_SHIFT;
+}
+
+static void sysmon_millicelsius_to_q8p7(u32 *raw_data, int val)
+{
+ *raw_data = (val << SYSMON_FRACTIONAL_SHIFT) / MILLIDEGREE_PER_DEGREE;
+}
+
+static void sysmon_supply_rawtoprocessed(int raw_data, int *val)
+{
+ int mantissa, format, exponent;
+
+ mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data);
+ exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data);
+ format = FIELD_GET(SYSMON_FMT_MASK, raw_data);
+ /*
+ * When format bit is set the mantissa is two's complement
+ * (per hardware spec); sign-extend to int for correct arithmetic.
+ */
+ if (format)
+ mantissa = sign_extend32(mantissa, 15);
+
+ *val = (mantissa * (int)MILLI) >> exponent;
+}
+
+static void sysmon_supply_processedtoraw(int val, u32 reg_val, u32 *raw_data)
+{
+ int exponent = FIELD_GET(SYSMON_MODE_MASK, reg_val);
+ int format = FIELD_GET(SYSMON_FMT_MASK, reg_val);
+ int scale, tmp;
+
+ scale = BIT(SYSMON_SUPPLY_MANTISSA_BITS - exponent);
+ tmp = (val * scale) / (int)MILLI;
+
+ if (format)
+ tmp = clamp(tmp, S16_MIN, S16_MAX);
+ else
+ tmp = clamp(tmp, 0, U16_MAX);
+
+ *raw_data = (u16)tmp;
+}
+
+static int sysmon_supply_thresh_offset(unsigned long address, enum iio_event_direction dir)
+{
+ if (dir == IIO_EV_DIR_RISING)
+ return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_UP;
+ if (dir == IIO_EV_DIR_FALLING)
+ return (address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_TH_LOW;
+
+ return -EINVAL;
+}
+
+static int sysmon_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int regval;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
+ *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling :
+ sysmon->supply_oversampling;
+ return IIO_VAL_INT;
+ }
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ if (mask == IIO_CHAN_INFO_SCALE) {
+ /* Q8.7 to millicelsius: raw * 1000 / 128 */
+ *val = MILLIDEGREE_PER_DEGREE;
+ *val2 = BIT(SYSMON_FRACTIONAL_SHIFT);
+ return IIO_VAL_FRACTIONAL;
+ }
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ ret = regmap_read(sysmon->regmap, chan->address, &regval);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(regval, 15);
+ return IIO_VAL_INT;
+
+ case IIO_VOLTAGE:
+ if (mask != IIO_CHAN_INFO_PROCESSED)
+ return -EINVAL;
+
+ ret = regmap_read(sysmon->regmap,
+ chan->address * SYSMON_REG_STRIDE +
+ SYSMON_SUPPLY_BASE, &regval);
+ if (ret)
+ return ret;
+
+ sysmon_supply_rawtoprocessed(regval, val);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static u32 sysmon_get_event_mask(const struct iio_chan_spec *chan)
+{
+ if (chan->type == IIO_TEMP)
+ return BIT(SYSMON_BIT_TEMP);
+
+ return BIT(chan->address / SYSMON_ALARM_BITS_PER_REG);
+}
+
+static int sysmon_read_alarm_config(struct sysmon *sysmon,
+ unsigned long address)
+{
+ u32 shift = address % SYSMON_ALARM_BITS_PER_REG;
+ u32 offset = SYSMON_ALARM_OFFSET(address);
+
+ return regmap_test_bits(sysmon->regmap, offset, BIT(shift));
+}
+
+static int sysmon_write_alarm_config(struct sysmon *sysmon,
+ unsigned long address, bool enable)
+{
+ u32 shift = address % SYSMON_ALARM_BITS_PER_REG;
+ u32 offset = SYSMON_ALARM_OFFSET(address);
+
+ return regmap_assign_bits(sysmon->regmap, offset, BIT(shift), enable);
+}
+
+static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev);
+ u32 mask = sysmon_get_event_mask(chan);
+ unsigned int imr;
+ int config_value;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr);
+ if (ret)
+ return ret;
+
+ /* IMR bits are 1=masked, invert to get 1=enabled */
+ imr = ~imr;
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ config_value = sysmon_read_alarm_config(sysmon, chan->address);
+ if (config_value < 0)
+ return config_value;
+ return config_value && (imr & mask);
+
+ case IIO_TEMP:
+ /*
+ * Return the administrative state, not the hardware IMR.
+ * The IRQ handler temporarily masks the interrupt during
+ * the polling window; reading IMR would show it as disabled.
+ * temp_mask bit is set when administratively disabled.
+ */
+ return !(sysmon->temp_mask & mask);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ u32 offset = SYSMON_ALARM_OFFSET(chan->address);
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ u32 mask = sysmon_get_event_mask(chan);
+ unsigned int alarm_config;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = sysmon_write_alarm_config(sysmon, chan->address, state);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysmon->regmap, offset, &alarm_config);
+ if (ret)
+ return ret;
+
+ if (alarm_config)
+ return regmap_write(sysmon->regmap, SYSMON_IER, mask);
+
+ return regmap_write(sysmon->regmap, SYSMON_IDR, mask);
+
+ case IIO_TEMP:
+ if (state) {
+ ret = regmap_write(sysmon->regmap, SYSMON_IER, mask);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->temp_mask &= ~mask;
+ } else {
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, mask);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->temp_mask |= mask;
+ }
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Recompute the lower threshold register from upper threshold and
+ * cached hysteresis. Called when either upper threshold or hysteresis
+ * is written.
+ */
+static int sysmon_update_temp_lower(struct sysmon *sysmon)
+{
+ unsigned int upper_reg;
+ int upper_mc, lower_mc;
+ u32 raw_val;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc);
+ lower_mc = clamp(upper_mc - sysmon->temp_hysteresis, -256000, 255992);
+ sysmon_millicelsius_to_q8p7(&raw_val, lower_mc);
+
+ return regmap_write(sysmon->regmap, SYSMON_TEMP_TH_LOW, raw_val);
+}
+
+static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int reg_val;
+ int offset;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &reg_val);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(reg_val, val);
+
+ return IIO_VAL_INT;
+
+ case IIO_EV_INFO_HYSTERESIS:
+ *val = sysmon->temp_hysteresis;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_VOLTAGE:
+ offset = sysmon_supply_thresh_offset(chan->address, dir);
+ if (offset < 0)
+ return offset;
+
+ ret = regmap_read(sysmon->regmap, offset, &reg_val);
+ if (ret)
+ return ret;
+
+ sysmon_supply_rawtoprocessed(reg_val, val);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_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 sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int reg_val;
+ u32 raw_val;
+ int offset;
+ int ret;
+
+ guard(mutex)(&sysmon->lock);
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ /* Q8.7 signed range: -256000 to +255992 mC */
+ if (val < -256000 || val > 255992)
+ return -EINVAL;
+
+ sysmon_millicelsius_to_q8p7(&raw_val, val);
+
+ ret = regmap_write(sysmon->regmap, SYSMON_TEMP_TH_UP, raw_val);
+ if (ret)
+ return ret;
+
+ /* Recompute lower = upper - hysteresis */
+ return sysmon_update_temp_lower(sysmon);
+
+ case IIO_EV_INFO_HYSTERESIS:
+ if (val < 0)
+ return -EINVAL;
+
+ sysmon->temp_hysteresis = val;
+
+ return sysmon_update_temp_lower(sysmon);
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_VOLTAGE:
+ offset = sysmon_supply_thresh_offset(chan->address, dir);
+ if (offset < 0)
+ return offset;
+
+ ret = regmap_read(sysmon->regmap, offset, &reg_val);
+ if (ret)
+ return ret;
+
+ /* Clamp to prevent overflow in processedtoraw conversion */
+ if (val < -32768 || val > 32767)
+ return -EINVAL;
+
+ sysmon_supply_processedtoraw(val, reg_val, &raw_val);
+
+ /*
+ * The hardware threshold register returns FMT and MODE
+ * bits in the upper 16 bits on read, but only the lower
+ * 16-bit mantissa is used on write.
+ */
+ return regmap_write(sysmon->regmap, offset, raw_val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sysmon_set_avg_enable(struct sysmon *sysmon,
+ u32 base, u32 count, u32 val)
+{
+ struct regmap *map = sysmon->regmap;
+ int ret;
+
+ for (unsigned int i = 0; i < count; i++) {
+ ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val)
+{
+ /*
+ * HW register encoding is sample_count / 2:
+ * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based).
+ */
+ unsigned int hw_val = val >> 1;
+ unsigned int readback;
+ int ret;
+
+ ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
+ SYSMON_CONFIG_TEMP_SAT_OSR,
+ FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val));
+ if (ret)
+ return ret;
+
+ /*
+ * Readback fence: the SysMon CONFIG register resides in the
+ * PMC domain behind the NoC. A posted write may not reach the
+ * hardware before the next MMIO access. Reading the register
+ * back forces the interconnect to complete the write, preventing
+ * a bus hang on the subsequent access.
+ */
+ regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
+
+ return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE,
+ SYSMON_TEMP_EN_AVG_COUNT,
+ hw_val ? ~0 : 0);
+}
+
+static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val)
+{
+ /* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */
+ unsigned int hw_val = val >> 1;
+ unsigned int readback;
+ int ret;
+
+ ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
+ SYSMON_CONFIG_SUPPLY_OSR,
+ FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val));
+ if (ret)
+ return ret;
+
+ /* Readback fence -- see sysmon_osr_write_temp for details */
+ regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
+
+ return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE,
+ SYSMON_SUPPLY_EN_AVG_COUNT,
+ hw_val ? ~0 : 0);
+}
+
+static int sysmon_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int i;
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) {
+ if (val == sysmon_oversampling_avail[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(sysmon_oversampling_avail))
+ return -EINVAL;
+
+ guard(mutex)(&sysmon->lock);
+
+ if (chan->type == IIO_TEMP) {
+ ret = sysmon_osr_write_temp(sysmon, val);
+ if (ret)
+ return ret;
+ sysmon->temp_oversampling = val;
+ } else {
+ ret = sysmon_osr_write_supply(sysmon, val);
+ if (ret)
+ return ret;
+ sysmon->supply_oversampling = val;
+ }
+
+ return 0;
+}
+
+static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return IIO_VAL_INT;
+
+ return -EINVAL;
+}
+
+static int sysmon_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ *vals = sysmon_oversampling_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(sysmon_oversampling_avail);
+
+ return IIO_AVAIL_LIST;
+}
+
+static int sysmon_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ char *label)
+{
+ if (chan->datasheet_name)
+ return sysfs_emit(label, "%s\n", chan->datasheet_name);
+
+ return -EINVAL;
+}
+
+static const struct iio_info sysmon_iio_info = {
+ .read_raw = sysmon_read_raw,
+ .write_raw = sysmon_write_raw,
+ .write_raw_get_fmt = sysmon_write_raw_get_fmt,
+ .read_avail = sysmon_read_avail,
+ .read_label = sysmon_read_label,
+ .read_event_config = sysmon_read_event_config,
+ .write_event_config = sysmon_write_event_config,
+ .read_event_value = sysmon_read_event_value,
+ .write_event_value = sysmon_write_event_value,
+};
+
+static void sysmon_push_event(struct iio_dev *indio_dev, u32 address)
+{
+ const struct iio_chan_spec *chan;
+ enum iio_event_direction dir;
+
+ for (unsigned int i = 0; i < indio_dev->num_channels; i++) {
+ if (indio_dev->channels[i].address != address)
+ continue;
+
+ chan = &indio_dev->channels[i];
+ /* Temp uses hysteresis mode (rising only), voltage uses window */
+ dir = (chan->type == IIO_TEMP) ? IIO_EV_DIR_RISING :
+ IIO_EV_DIR_EITHER;
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(chan->type,
+ chan->channel,
+ IIO_EV_TYPE_THRESH,
+ dir),
+ iio_get_time_ns(indio_dev));
+ }
+}
+
+static int sysmon_handle_event(struct iio_dev *indio_dev, u32 event)
+{
+ u32 alarm_flag_offset = SYSMON_ALARM_FLAG + event * SYSMON_REG_STRIDE;
+ u32 alarm_reg_offset = SYSMON_ALARM_REG + event * SYSMON_REG_STRIDE;
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned long alarm_flag_reg;
+ unsigned int reg_val;
+ u32 address, bit;
+ int ret;
+
+ switch (event) {
+ case SYSMON_BIT_TEMP:
+ sysmon_push_event(indio_dev, SYSMON_TEMP_MAX);
+
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, BIT(SYSMON_BIT_TEMP));
+ if (ret)
+ return ret;
+
+ sysmon->masked_temp |= BIT(SYSMON_BIT_TEMP);
+ return 0;
+
+ case SYSMON_BIT_ALARM0:
+ case SYSMON_BIT_ALARM1:
+ case SYSMON_BIT_ALARM2:
+ case SYSMON_BIT_ALARM3:
+ case SYSMON_BIT_ALARM4:
+ ret = regmap_read(sysmon->regmap, alarm_flag_offset, &reg_val);
+ if (ret)
+ return ret;
+
+ alarm_flag_reg = reg_val;
+
+ for_each_set_bit(bit, &alarm_flag_reg, SYSMON_ALARM_BITS_PER_REG) {
+ address = bit + SYSMON_ALARM_BITS_PER_REG * event;
+ sysmon_push_event(indio_dev, address);
+ ret = regmap_clear_bits(sysmon->regmap, alarm_reg_offset, BIT(bit));
+ if (ret)
+ return ret;
+ }
+
+ return regmap_write(sysmon->regmap, alarm_flag_offset, alarm_flag_reg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void sysmon_handle_events(struct iio_dev *indio_dev,
+ unsigned long events)
+{
+ unsigned int bit;
+
+ for_each_set_bit(bit, &events, SYSMON_NO_OF_EVENTS)
+ sysmon_handle_event(indio_dev, bit);
+}
+
+static void sysmon_unmask_temp(struct sysmon *sysmon, unsigned int isr)
+{
+ unsigned int status;
+ u32 ier;
+
+ status = isr & SYSMON_TEMP_INTR_MASK;
+
+ ier = ~status & sysmon->masked_temp;
+ sysmon->masked_temp &= status;
+
+ /* Only unmask if not administratively disabled by userspace */
+ ier &= ~sysmon->temp_mask;
+
+ regmap_write(sysmon->regmap, SYSMON_IER, ier);
+}
+
+/*
+ * Versal threshold interrupts are level-sensitive. Active threshold
+ * interrupts are masked in the handler and polled via delayed work
+ * until the condition clears, then unmasked.
+ */
+static void sysmon_unmask_worker(struct work_struct *work)
+{
+ struct sysmon *sysmon =
+ container_of(work, struct sysmon, sysmon_unmask_work.work);
+ unsigned int isr;
+
+ /*
+ * If the ISR read fails, skip processing to avoid acting
+ * on undefined data.
+ */
+ scoped_guard(spinlock_irq, &sysmon->irq_lock) {
+ if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr))
+ break;
+ regmap_write(sysmon->regmap, SYSMON_ISR, isr);
+ sysmon_unmask_temp(sysmon, isr);
+ }
+
+ if (sysmon->masked_temp)
+ schedule_delayed_work(&sysmon->sysmon_unmask_work,
+ msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS));
+ else
+ regmap_write(sysmon->regmap, SYSMON_STATUS_RESET, 1);
+}
+
+static irqreturn_t sysmon_iio_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct sysmon *sysmon = iio_priv(indio_dev);
+ unsigned int isr, imr;
+
+ guard(spinlock)(&sysmon->irq_lock);
+
+ if (regmap_read(sysmon->regmap, SYSMON_ISR, &isr) ||
+ regmap_read(sysmon->regmap, SYSMON_IMR, &imr))
+ return IRQ_NONE;
+
+ isr &= ~imr;
+ if (!isr)
+ return IRQ_NONE;
+
+ regmap_write(sysmon->regmap, SYSMON_ISR, isr);
+
+ sysmon_handle_events(indio_dev, isr);
+ schedule_delayed_work(&sysmon->sysmon_unmask_work,
+ msecs_to_jiffies(SYSMON_UNMASK_WORK_DELAY_MS));
+
+ return IRQ_HANDLED;
+}
+
+static void sysmon_disable_interrupts(void *data)
+{
+ struct sysmon *sysmon = data;
+
+ regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
+
+ scoped_guard(spinlock_irq, &sysmon->irq_lock)
+ sysmon->masked_temp = 0;
+
+ cancel_delayed_work_sync(&sysmon->sysmon_unmask_work);
+}
+
+static int sysmon_init_interrupt(struct sysmon *sysmon,
+ struct device *dev,
+ struct iio_dev *indio_dev,
+ int irq)
+{
+ unsigned int imr;
+ int ret;
+
+ /* Events not supported without IRQ (e.g. I2C path) */
+ if (!irq)
+ return 0;
+
+ INIT_DELAYED_WORK(&sysmon->sysmon_unmask_work, sysmon_unmask_worker);
+
+ ret = regmap_read(sysmon->regmap, SYSMON_IMR, &imr);
+ if (ret)
+ return ret;
+ sysmon->temp_mask = imr & SYSMON_TEMP_INTR_MASK;
+
+ ret = devm_request_irq(dev, irq, sysmon_iio_irq, 0, "sysmon-irq", indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, sysmon_disable_interrupts, sysmon);
+}
+
+/*
+ * Initialize the cached hysteresis for a temperature channel from the
+ * current hardware threshold registers: hysteresis = upper - lower.
+ */
+static int sysmon_init_hysteresis(struct sysmon *sysmon, int *hysteresis)
+{
+ unsigned int upper_reg, lower_reg;
+ int upper_mc, lower_mc;
+ int ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_UP, &upper_reg);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(sysmon->regmap, SYSMON_TEMP_TH_LOW, &lower_reg);
+ if (ret)
+ return ret;
+
+ sysmon_q8p7_to_millicelsius(upper_reg, &upper_mc);
+ sysmon_q8p7_to_millicelsius(lower_reg, &lower_mc);
+ *hysteresis = upper_mc - lower_mc;
+
+ return 0;
+}
+
+/**
+ * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels.
+ * @indio_dev: IIO device instance
+ * @dev: Parent device
+ * @irq: IRQ number (positive enables event channels, 0 disables)
+ *
+ * Reads voltage-channels and temperature-channels container nodes from
+ * firmware and builds the IIO channel array. Static temperature channels
+ * and event channels are prepended, followed by supply and satellite
+ * channels from DT.
+ *
+ * Event channels and per-channel event specs are only added when the
+ * device has an IRQ. I2C devices have no interrupt line, and the I2C
+ * regmap cannot be called from atomic context, so events are not
+ * supported on that path.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq)
+{
+ unsigned int num_chan, num_static, num_supply, num_temp;
+ unsigned int idx, temp_chan_idx, volt_chan_idx;
+ struct iio_chan_spec *sysmon_channels;
+ const char *label;
+ u32 reg;
+ int ret;
+
+ struct fwnode_handle *supply_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "voltage-channels");
+ num_supply = fwnode_get_child_node_count(supply_node);
+
+ struct fwnode_handle *temp_node __free(fwnode_handle) =
+ device_get_named_child_node(dev, "temperature-channels");
+ num_temp = fwnode_get_child_node_count(temp_node);
+
+ num_static = ARRAY_SIZE(temp_channels);
+ num_chan = size_add(num_temp, size_add(num_static, num_supply));
+ sysmon_channels = devm_kcalloc(dev, num_chan, sizeof(*sysmon_channels), GFP_KERNEL);
+ if (!sysmon_channels)
+ return -ENOMEM;
+
+ memcpy(sysmon_channels, temp_channels, sizeof(temp_channels));
+
+ /* Attach event spec to channel 0 when IRQ is available */
+ if (irq > 0) {
+ sysmon_channels[0].event_spec = sysmon_temp_events;
+ sysmon_channels[0].num_event_specs = ARRAY_SIZE(sysmon_temp_events);
+ }
+
+ idx = num_static;
+
+ /* Supply channels from DT */
+ fwnode_for_each_child_node_scoped(supply_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg for supply channel\n");
+
+ if (reg > SYSMON_SUPPLY_IDX_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "supply reg %u exceeds max %u\n",
+ reg, SYSMON_SUPPLY_IDX_MAX);
+
+ ret = fwnode_property_read_string(child, "label", &label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing label for supply channel\n");
+
+ sysmon_channels[idx++] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .address = reg,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .event_spec = irq > 0 ?
+ sysmon_supply_events : NULL,
+ .num_event_specs = irq > 0 ?
+ ARRAY_SIZE(sysmon_supply_events) : 0,
+ .datasheet_name = label,
+ };
+ }
+
+ /* Temperature satellite channels from DT */
+ fwnode_for_each_child_node_scoped(temp_node, child) {
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing reg for temp channel\n");
+
+ if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX)
+ return dev_err_probe(dev, -EINVAL,
+ "temp reg %u out of range [1..%u]\n",
+ reg, SYSMON_TEMP_SAT_MAX);
+
+ ret = fwnode_property_read_string(child, "label", &label);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing label for temp channel\n");
+
+ sysmon_channels[idx++] = (struct iio_chan_spec) {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .address = SYSMON_TEMP_SAT_BASE +
+ (reg - 1) * SYSMON_REG_STRIDE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .datasheet_name = label,
+ };
+ }
+
+ indio_dev->num_channels = idx;
+ indio_dev->info = &sysmon_iio_info;
+
+ /*
+ * Assign per-type sequential channel numbers.
+ * IIO sysfs uses type prefix (in_tempN, in_voltageN)
+ * so numbers only need to be unique within each type.
+ */
+ temp_chan_idx = 0;
+ volt_chan_idx = 0;
+ for (unsigned int idx = 0; idx < indio_dev->num_channels; idx++) {
+ if (sysmon_channels[idx].type == IIO_TEMP)
+ sysmon_channels[idx].channel = temp_chan_idx++;
+ else
+ sysmon_channels[idx].channel = volt_chan_idx++;
+ }
+
+ indio_dev->channels = sysmon_channels;
+
+ return 0;
+}
+
+/**
+ * devm_versal_sysmon_core_probe() - Initialize Versal SysMon core
+ * @dev: Parent device
+ * @regmap: Register map for hardware access
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+ struct sysmon *sysmon;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sysmon));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ sysmon = iio_priv(indio_dev);
+ sysmon->regmap = regmap;
+ sysmon->temp_oversampling = 1;
+ sysmon->supply_oversampling = 1;
+
+ ret = devm_mutex_init(dev, &sysmon->lock);
+ if (ret)
+ return ret;
+ spin_lock_init(&sysmon->irq_lock);
+
+ /* Disable all interrupts and clear pending status */
+ ret = regmap_write(sysmon->regmap, SYSMON_IDR, SYSMON_INTR_ALL_MASK);
+ if (ret)
+ return ret;
+ ret = regmap_write(sysmon->regmap, SYSMON_ISR, SYSMON_INTR_ALL_MASK);
+ if (ret)
+ return ret;
+
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+ if (irq == -EPROBE_DEFER)
+ return dev_err_probe(dev, irq, "failed to get IRQ\n");
+
+ indio_dev->name = "versal-sysmon";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = sysmon_parse_fw(indio_dev, dev, irq);
+ if (ret)
+ return ret;
+
+ if (irq > 0) {
+ /* Set hysteresis mode for temperature threshold */
+ ret = regmap_set_bits(sysmon->regmap, SYSMON_TEMP_EV_CFG,
+ SYSMON_TEMP_HYST_MASK);
+ if (ret)
+ return ret;
+
+ /* Initialize cached hysteresis from hardware registers */
+ ret = sysmon_init_hysteresis(sysmon, &sysmon->temp_hysteresis);
+ if (ret)
+ return ret;
+
+ ret = sysmon_init_interrupt(sysmon, dev, indio_dev, irq);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(devm_versal_sysmon_core_probe, "VERSAL_SYSMON");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon Core Driver");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon-i2c.c b/drivers/iio/adc/versal-sysmon-i2c.c
new file mode 100644
index 000000000000..e9a7629159ab
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon-i2c.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon I2C driver
+ *
+ * Copyright (C) 2023 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "versal-sysmon.h"
+
+#define SYSMON_I2C_INSTR_READ BIT(2)
+#define SYSMON_I2C_INSTR_WRITE BIT(3)
+
+/*
+ * I2C command frame layout (8 bytes):
+ * [0..3] data payload (little-endian u32)
+ * [4..5] register offset >> 2 (little-endian u16)
+ * [6] instruction (read/write)
+ * [7] reserved
+ */
+#define SYSMON_I2C_DATA_OFS 0
+#define SYSMON_I2C_REG_OFS 4
+#define SYSMON_I2C_INSTR_OFS 6
+
+static int sysmon_i2c_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct i2c_client *client = context;
+ u8 write_buf[8] = { };
+ u8 read_buf[4];
+ int ret;
+
+ put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
+ write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_READ;
+
+ ret = i2c_master_send(client, write_buf, sizeof(write_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(write_buf))
+ return -EIO;
+
+ ret = i2c_master_recv(client, read_buf, sizeof(read_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(read_buf))
+ return -EIO;
+
+ *val = get_unaligned_le32(read_buf);
+
+ return 0;
+}
+
+static int sysmon_i2c_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = context;
+ u8 write_buf[8] = { };
+ int ret;
+
+ put_unaligned_le32(val, &write_buf[SYSMON_I2C_DATA_OFS]);
+ put_unaligned_le16(reg >> 2, &write_buf[SYSMON_I2C_REG_OFS]);
+ write_buf[SYSMON_I2C_INSTR_OFS] = SYSMON_I2C_INSTR_WRITE;
+
+ ret = i2c_master_send(client, write_buf, sizeof(write_buf));
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(write_buf))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Almost all registers are volatile (live ADC readings, interrupt
+ * status). The rest are not accessed often enough to benefit from
+ * caching.
+ */
+static const struct regmap_config sysmon_i2c_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = SYSMON_REG_STRIDE,
+ .max_register = SYSMON_MAX_REG,
+ .reg_read = sysmon_i2c_reg_read,
+ .reg_write = sysmon_i2c_reg_write,
+};
+
+static int sysmon_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init(dev, NULL, client, &sysmon_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* I2C has no IRQ connection; events are not supported */
+ return devm_versal_sysmon_core_probe(dev, regmap);
+}
+
+static const struct of_device_id sysmon_i2c_of_match_table[] = {
+ { .compatible = "xlnx,versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sysmon_i2c_of_match_table);
+
+static const struct i2c_device_id sysmon_i2c_id_table[] = {
+ { .name = "versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sysmon_i2c_id_table);
+
+static struct i2c_driver sysmon_i2c_driver = {
+ .probe = sysmon_i2c_probe,
+ .driver = {
+ .name = "versal-sysmon-i2c",
+ .of_match_table = sysmon_i2c_of_match_table,
+ },
+ .id_table = sysmon_i2c_id_table,
+};
+module_i2c_driver(sysmon_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon I2C Driver");
+MODULE_IMPORT_NS("VERSAL_SYSMON");
+MODULE_AUTHOR("Conall O'Griofa <conall.ogriofa@amd.com>");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c
new file mode 100644
index 000000000000..529d0486c9f9
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Versal SysMon MMIO platform driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "versal-sysmon.h"
+
+struct sysmon_mmio {
+ void __iomem *base;
+};
+
+static int sysmon_mmio_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct sysmon_mmio *mmio = context;
+
+ *val = readl(mmio->base + reg);
+ return 0;
+}
+
+static int sysmon_mmio_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct sysmon_mmio *mmio = context;
+
+ /* NPI must be unlocked before any register write except to NPI_LOCK */
+ if (reg != SYSMON_NPI_LOCK)
+ writel(SYSMON_NPI_UNLOCK_CODE, mmio->base + SYSMON_NPI_LOCK);
+ writel(val, mmio->base + reg);
+
+ return 0;
+}
+
+static const struct regmap_config sysmon_mmio_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = SYSMON_REG_STRIDE,
+ .max_register = SYSMON_MAX_REG,
+ .reg_read = sysmon_mmio_reg_read,
+ .reg_write = sysmon_mmio_reg_write,
+ .fast_io = true,
+};
+
+static int sysmon_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sysmon_mmio *mmio;
+ struct regmap *regmap;
+
+ mmio = devm_kzalloc(dev, sizeof(*mmio), GFP_KERNEL);
+ if (!mmio)
+ return -ENOMEM;
+
+ mmio->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(mmio->base))
+ return PTR_ERR(mmio->base);
+
+ regmap = devm_regmap_init(dev, NULL, mmio, &sysmon_mmio_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return devm_versal_sysmon_core_probe(dev, regmap);
+}
+
+static const struct of_device_id sysmon_of_match_table[] = {
+ { .compatible = "xlnx,versal-sysmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sysmon_of_match_table);
+
+static struct platform_driver sysmon_platform_driver = {
+ .probe = sysmon_platform_probe,
+ .driver = {
+ .name = "versal-sysmon",
+ .of_match_table = sysmon_of_match_table,
+ },
+};
+module_platform_driver(sysmon_platform_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD Versal SysMon Platform Driver");
+MODULE_IMPORT_NS("VERSAL_SYSMON");
+MODULE_AUTHOR("Salih Erim <salih.erim@amd.com>");
diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h
new file mode 100644
index 000000000000..bb9a75bf71c0
--- /dev/null
+++ b/drivers/iio/adc/versal-sysmon.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Versal SysMon driver
+ *
+ * Copyright (C) 2019 - 2022, Xilinx, Inc.
+ * Copyright (C) 2022 - 2026, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _VERSAL_SYSMON_H_
+#define _VERSAL_SYSMON_H_
+
+#include <linux/bits.h>
+#include <linux/mutex.h>
+#include <linux/spinlock_types.h>
+#include <linux/workqueue.h>
+
+struct device;
+struct regmap;
+
+/* Register offsets (sorted by address) */
+#define SYSMON_NPI_LOCK 0x000C
+#define SYSMON_ISR 0x0044
+#define SYSMON_IMR 0x0048
+#define SYSMON_IER 0x004C
+#define SYSMON_IDR 0x0050
+#define SYSMON_CONFIG 0x0100
+#define SYSMON_TEMP_MAX 0x1030
+#define SYSMON_TEMP_MIN 0x1034
+#define SYSMON_SUPPLY_BASE 0x1040
+#define SYSMON_ALARM_FLAG 0x1018
+#define SYSMON_ALARM_REG 0x1940
+#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958
+#define SYSMON_TEMP_TH_LOW 0x1970
+#define SYSMON_TEMP_TH_UP 0x1974
+#define SYSMON_SUPPLY_TH_LOW 0x1980
+#define SYSMON_SUPPLY_TH_UP 0x1C80
+#define SYSMON_TEMP_EV_CFG 0x1F84
+#define SYSMON_TEMP_MIN_MIN 0x1F8C
+#define SYSMON_TEMP_MAX_MAX 0x1F90
+#define SYSMON_STATUS_RESET 0x1F94
+#define SYSMON_TEMP_SAT_BASE 0x1FAC
+#define SYSMON_TEMP_EN_AVG_BASE 0x24B4
+#define SYSMON_MAX_REG 0x24C0
+
+/* NPI unlock value written to SYSMON_NPI_LOCK */
+#define SYSMON_NPI_UNLOCK_CODE 0xF9E8D7C6
+
+/* Register stride: 4 bytes per 32-bit register */
+#define SYSMON_REG_STRIDE 4
+
+#define SYSMON_SUPPLY_IDX_MAX 159
+#define SYSMON_TEMP_SAT_MAX 64
+#define SYSMON_NO_OF_EVENTS 32
+#define SYSMON_INTR_ALL_MASK GENMASK(31, 0)
+
+/* ISR/IMR temperature alarm mask (bit 9) */
+#define SYSMON_TEMP_INTR_MASK BIT(9)
+
+/* SYSMON_CONFIG: supply oversampling ratio */
+#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14)
+
+/* SYSMON_CONFIG: temperature satellite oversampling ratio */
+#define SYSMON_CONFIG_TEMP_SAT_OSR GENMASK(27, 24)
+
+/* Per-channel averaging enable register counts */
+#define SYSMON_SUPPLY_EN_AVG_COUNT 5
+#define SYSMON_TEMP_EN_AVG_COUNT 2
+
+/* Supply voltage conversion register fields */
+#define SYSMON_MANTISSA_MASK GENMASK(15, 0)
+#define SYSMON_FMT_MASK BIT(16)
+#define SYSMON_MODE_MASK GENMASK(18, 17)
+
+/* Q8.7 fractional shift */
+#define SYSMON_FRACTIONAL_SHIFT 7U
+#define SYSMON_SUPPLY_MANTISSA_BITS 16
+
+/* Bits per alarm register */
+#define SYSMON_ALARM_BITS_PER_REG 32
+
+#define SYSMON_UNMASK_WORK_DELAY_MS 500
+
+/**
+ * struct sysmon - Driver data for Versal SysMon
+ * @regmap: register map for hardware access
+ * @lock: protects read-modify-write sequences on threshold registers
+ * and cached state that spans multiple regmap calls
+ * @irq_lock: protects interrupt mask register updates (MMIO path only)
+ * @masked_temp: currently masked temperature alarm bits
+ * @temp_mask: temperature interrupt configuration mask
+ * @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius
+ * @sysmon_unmask_work: re-enables events after alarm condition clears
+ * @temp_oversampling: current temp oversampling ratio
+ * @supply_oversampling: current supply oversampling ratio
+ */
+struct sysmon {
+ struct regmap *regmap;
+ /*
+ * Protects read-modify-write sequences on threshold registers
+ * and cached state (oversampling ratios, hysteresis values)
+ * that spans multiple regmap calls.
+ */
+ struct mutex lock;
+ /*
+ * Protects interrupt mask register updates. Only used on the
+ * MMIO path (fast_io regmap); I2C has no IRQ and never reaches
+ * the event code that takes this lock.
+ */
+ spinlock_t irq_lock;
+ unsigned int masked_temp;
+ unsigned int temp_mask;
+ int temp_hysteresis;
+ struct delayed_work sysmon_unmask_work;
+ unsigned int temp_oversampling;
+ unsigned int supply_oversampling;
+};
+
+int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap);
+
+#endif /* _VERSAL_SYSMON_H_ */