summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-02-02 17:08:30 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-02-02 17:08:30 +0100
commit9991bbc6d5e8fdfa0b1bcfeebf4637e8fd764bd8 (patch)
tree3f5085832b88f5c26f956103e1dd22dd599920b0
parent96657eb5ab7e231f6333a3fbc674c0451f13f7f1 (diff)
parent0713b26190addfa3a774b386c8658952ef9f7faf (diff)
Merge tag 'iio-for-7.0a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next
Jonathan writes: IIO: New device support, features and cleanup for the 6.20/7.0 cycle. Slightly messier than normal unfortunately due to some conflicts and build config bugs related to I3C drivers. One last minute Kconfig fix right at the top after a linux-next report. I've simplified the Kconfig and made it match other instances in the kernel so that should be safe enough despite short soak time in front of build bots. Merge of an immutable branch from I3C to get some stubs that were missing and caused build issues with dual I2C / I3C drivers. This also brought in a drop of some deprecated interfaces so there is also one patch to update a new driver to not use those. We are having another go at using cleanup.h magic with the IIO mode claim functions after backing out last try at this. This time we have wrappers around the new ACQUIRE() and ACQUIRE_ERR() macros. Having been burnt once, we will be taking it a bit more slowly this time wrt to wide adoption of these! Thanks in particular to Kurt for taking on this core IIO work. New Device Support ================== adi,ad18113 - New driver to support the AD18113 amplifier - an interesting device due to the external bypass paths where we need to describe what gain those paths have in DT. Longer term it will be interesting to see if this simplistic description is enough for real deployments. adi,ad4062 - New driver for the AD4060 and AD4052 SAR ADCs including trigger, event and GPIO controller support. Follow up patch replaced use of some deprecated I3C interfaces prior to the I3C immutable branch merge as that includes dropping them. adi,ad4134 - New driver for the AD4134 24bit 4 channel simultaneous sampling ADC. adi,ad7768-1, - Add support for the ADAQ767-1, ADAQ7768-1 and ADAQ7769-1 ADCs after some rework to enable the driver to support multiple device types. adi,ad9467 - Add support for the similar ad9211 ADC to this existing driver. - Make the selection of 2s comp mode explicit for normal operation and switch to offset binary when entering calibration mode. honeywell,abp2 - New driver to support this huge family (100+) of board mount pressure and temperature sensors. maxim,max22007 - New drier for this 4 channel DAC. memsic,mmc5633 - New driver for this I2C/I3C magnetometer. Follow on patches fixed up issues related to single driver supporting both bus types. microchip,mcp747feb02 - New driver for the Microchip MCP47F(E/V)B(0/1/2)1, MCP47F(E/V)B(0/1/2)2, MCP47F(E/V)B(0/1/2)4 and MCP47F(E/V)B(0/1/2)8 buffered voltage output DACs. nxp,sar-adc - New driver support ADCs found on s32g2 and s32g3 platforms. ti,ads1018 - New drier for the ADS1018 and ADS1118 SPI ADCs. ti,ads131m02 - New driver supporting ADS131M(02/03/04/06/08)24-bit simultaneous sampling ADCs. Features ======== iio-core - New IIO_DEV_ACQUIRE_DIRECT_MODE() / IIO_DEV_ACQUIRE_FAILED() + equivalents for the much rarer case where the mode needs pinning whether or not it is in direct mode. These use the ACQUIRE() / ACQUIRE_ERR() infrastructure underneath to provide both simple checks on whether we got the requested mode and to provide scope based release. Applied in a few initial drivers. adi,ad9467 - Support calibbias control adi,adf4377 - Add support to act as a clock provider. adi,adxl380 - Support low power 1KHz sampling frequency mode. Required rework of how events and filters were configured, plus applying of constraints when in this mode. rf-digital,rfd77402 - Add interrupt support as alternative to polling for completion. st,lsm6dsx - Tap event detection (after considerable driver rework) Cleanup and Minor Fixes ======================= More minor cleanup such as typos, white space etc not called out except where they were applied to a lot of drivers. Various drivers. - Use of dev_err_probe() to cleanup error handling. - Introduce local struct device and struct device_node variables to reduce duplication of getting them from containing structs. - Ensure uses of iio_trigger_generic_data_rdy_poll() set IRQF_NO_THREAD as that function calls non threaded child interrupt handlers. - Replace IRQF_ONESHOT in not thread interrupt handlers with IRQF_NO_THREAD to ensure they run as intended. Drop one unnecessary case. iio-sw-device/trigger. - Constify configs_group_operations structures. iio-buffer-dma / buffer-dma-engine - Use lockdep_assert_held() to replace WARN_ON() to check lock is correctly held. - Make use of cleanup.h magic to simplify various code paths. - Make iio_dma_buffer_init() return void rather than always success. adi,ad7766 - Replace custom interrupt handler with iio_trigger_generic_data_rdy_poll() adi,ad9832 - Drop legacy platform_data support. adi,ade9000 - Add a maintainer entry. adi,adt7316 - Move to EXPORT_GPL_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() so the compiler can cleanly drop unused pm structures and callbacks. adi,adxl345 - Relax build constraint vs the driver that is in input so both may be built as modules and selection made at runtime. adi,adxl380 - Make sure we don't read tail entries in the hardware fifo if a partial new scan has been written. - Move to a single larger regmap_noinc_read() to read the hardware fifo. aspeed,ast2600 - Add missing interrupts property to DT binding. bosch,bmi270_i2c - Add missing MODULE_DEVICE_TABLE() macros so auto probing of modules can work. bosch,smi330 - Drop duplicate assignment of IIO_TYPE in smi330_read_avail() - Use new common field_get() and field_prep() helpers to replace local version. honeywell,mprls0025pa Fixes delayed to merge window as late in cycle and we didn't want to delay the rest of the series. - Allow Kconfig selection of specific bus sub-drivers rather than tying that to the buses themselves being supported. - Zero spi_transfer structure to avoid chance of unintentionally set fields effecting transfer. - Fix a potential timing violation wrt to the chip select to first clock edge timing. - As recent driver, take risk inherent in dropping interrupt direction from driver as that should be set by firmware. - Fix wrong reported number of data bits for channel. - Fix a pressure channel calculation bug. - Rework to allow embedding the tx buffer in the iio_priv() structure rather than requiring separate allocation. - Move the buffer clearing to the shared core bringing it into affect for SPI as well as I2C. - Stricter checks for status byte. - Greatly simplify the measurement sequence. - Add a copyright entry to reflect Petre's continued work on this driver. intersil,isl29018 - Switch from spritnf to sysfs_emit_at() to make it clear overflow can't occur. invensense,icm42600 - Allow sysfs access to temperature when buffered capture in use as it does not impact other sensor data paths. invensense,itg3200 - Check unused return value in read_raw() callback. men,z188 - Drop now duplicated module alias. rf-digital,rfd77402 - Add DT binding doc and explicit of_device_id table. - Poll for timeout with times as on datasheet, then replace opencoded version with read_poll_timeout(). sensiron,scd4x - Add missing timestamp channel. The code to push it to the buffer was there but there was no way to turn it on. vti,sca3000 - Fix resource leak if iio_device_register() fails. * tag 'iio-for-7.0a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (144 commits) iio: magn: mmc5633: Fix Kconfig for combination of I3C as module and driver builtin iio: sca3000: Fix a resource leak in sca3000_probe() iio: proximity: rfd77402: Add interrupt handling support iio: proximity: rfd77402: Document device private data structure iio: proximity: rfd77402: Use devm-managed mutex initialization iio: proximity: rfd77402: Use kernel helper for result polling iio: proximity: rfd77402: Align polling timeout with datasheet iio: cros_ec: Allow enabling/disabling calibration mode iio: frequency: ad9523: correct kernel-doc bad line warning iio: buffer: buffer_impl.h: fix kernel-doc warnings iio: gyro: itg3200: Fix unchecked return value in read_raw MAINTAINERS: add entry for ADE9000 driver iio: accel: sca3000: remove unused last_timestamp field iio: accel: adxl372: remove unused int2_bitmask field iio: adc: ad7766: Use iio_trigger_generic_data_rdy_poll() iio: magnetometer: Remove IRQF_ONESHOT iio: Replace IRQF_ONESHOT with IRQF_NO_THREAD iio: Use IRQF_NO_THREAD iio: dac: Add MAX22007 DAC driver support dt-bindings: iio: dac: Add max22007 ...
-rw-r--r--Documentation/ABI/testing/sysfs-bus-i3c11
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-cros-ec9
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml120
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml191
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml64
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml2
-rw-r--r--Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml3
-rw-r--r--Documentation/devicetree/bindings/iio/adc/nxp,s32g2-sar-adc.yaml63
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml82
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti,ads131m02.yaml208
-rw-r--r--Documentation/devicetree/bindings/iio/amplifiers/adi,adl8113.yaml87
-rw-r--r--Documentation/devicetree/bindings/iio/dac/adi,max22007.yaml120
-rw-r--r--Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml302
-rw-r--r--Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml8
-rw-r--r--Documentation/devicetree/bindings/iio/pressure/honeywell,abp2030pa.yaml132
-rw-r--r--Documentation/devicetree/bindings/iio/proximity/rfdigital,rfd77402.yaml53
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml4
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml2
-rw-r--r--Documentation/iio/ad4062.rst148
-rw-r--r--Documentation/iio/index.rst1
-rw-r--r--MAINTAINERS53
-rw-r--r--drivers/i3c/master.c46
-rw-r--r--drivers/i3c/master/dw-i3c-master.c59
-rw-r--r--drivers/i3c/master/svc-i3c-master.c4
-rw-r--r--drivers/iio/accel/Kconfig10
-rw-r--r--drivers/iio/accel/adxl355_core.c5
-rw-r--r--drivers/iio/accel/adxl372.c10
-rw-r--r--drivers/iio/accel/adxl380.c138
-rw-r--r--drivers/iio/accel/adxl380.h10
-rw-r--r--drivers/iio/accel/bma180.c5
-rw-r--r--drivers/iio/accel/mxc4005.c11
-rw-r--r--drivers/iio/accel/sca3000.c8
-rw-r--r--drivers/iio/accel/stk8ba50.c11
-rw-r--r--drivers/iio/adc/Kconfig60
-rw-r--r--drivers/iio/adc/Makefile5
-rw-r--r--drivers/iio/adc/ad4062.c1609
-rw-r--r--drivers/iio/adc/ad4134.c500
-rw-r--r--drivers/iio/adc/ad4170-4.c2
-rw-r--r--drivers/iio/adc/ad7476.c1
-rw-r--r--drivers/iio/adc/ad7606_spi.c2
-rw-r--r--drivers/iio/adc/ad7766.c10
-rw-r--r--drivers/iio/adc/ad7768-1.c428
-rw-r--r--drivers/iio/adc/ad7779.c2
-rw-r--r--drivers/iio/adc/ad9467.c152
-rw-r--r--drivers/iio/adc/ade9000.c2
-rw-r--r--drivers/iio/adc/adi-axi-adc.c64
-rw-r--r--drivers/iio/adc/aspeed_adc.c49
-rw-r--r--drivers/iio/adc/exynos_adc.c57
-rw-r--r--drivers/iio/adc/mcp3911.c2
-rw-r--r--drivers/iio/adc/men_z188_adc.c1
-rw-r--r--drivers/iio/adc/nxp-sar-adc.c1016
-rw-r--r--drivers/iio/adc/qcom-spmi-rradc.c20
-rw-r--r--drivers/iio/adc/rockchip_saradc.c59
-rw-r--r--drivers/iio/adc/sc27xx_adc.c49
-rw-r--r--drivers/iio/adc/ti-ads1018.c739
-rw-r--r--drivers/iio/adc/ti-ads131e08.c2
-rw-r--r--drivers/iio/adc/ti-ads131m02.c968
-rw-r--r--drivers/iio/amplifiers/Kconfig12
-rw-r--r--drivers/iio/amplifiers/Makefile1
-rw-r--r--drivers/iio/amplifiers/adl8113.c269
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c188
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c23
-rw-r--r--drivers/iio/chemical/ens160_core.c9
-rw-r--r--drivers/iio/chemical/scd4x.c3
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c11
-rw-r--r--drivers/iio/dac/Kconfig33
-rw-r--r--drivers/iio/dac/Makefile2
-rw-r--r--drivers/iio/dac/adi-axi-dac.c66
-rw-r--r--drivers/iio/dac/ds4424.c1
-rw-r--r--drivers/iio/dac/max22007.c491
-rw-r--r--drivers/iio/dac/mcp47feb02.c1250
-rw-r--r--drivers/iio/frequency/adf4377.c122
-rw-r--r--drivers/iio/gyro/adxrs290.c2
-rw-r--r--drivers/iio/gyro/itg3200_buffer.c8
-rw-r--r--drivers/iio/gyro/itg3200_core.c2
-rw-r--r--drivers/iio/gyro/mpu3050-core.c6
-rw-r--r--drivers/iio/health/afe4403.c9
-rw-r--r--drivers/iio/health/afe4404.c9
-rw-r--r--drivers/iio/health/max30100.c8
-rw-r--r--drivers/iio/health/max30102.c35
-rw-r--r--drivers/iio/imu/bmi270/bmi270_i2c.c3
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c3
-rw-r--r--drivers/iio/imu/smi330/smi330_core.c9
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h55
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c555
-rw-r--r--drivers/iio/industrialio-core.c86
-rw-r--r--drivers/iio/industrialio-sw-device.c2
-rw-r--r--drivers/iio/industrialio-sw-trigger.c2
-rw-r--r--drivers/iio/light/isl29018.c12
-rw-r--r--drivers/iio/light/opt4060.c52
-rw-r--r--drivers/iio/light/si1145.c2
-rw-r--r--drivers/iio/light/vcnl4000.c49
-rw-r--r--drivers/iio/magnetometer/Kconfig13
-rw-r--r--drivers/iio/magnetometer/Makefile1
-rw-r--r--drivers/iio/magnetometer/ak8975.c2
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c9
-rw-r--r--drivers/iio/magnetometer/mmc5633.c586
-rw-r--r--drivers/iio/pressure/Kconfig63
-rw-r--r--drivers/iio/pressure/Makefile3
-rw-r--r--drivers/iio/pressure/abp2030pa.c544
-rw-r--r--drivers/iio/pressure/abp2030pa.h73
-rw-r--r--drivers/iio/pressure/abp2030pa_i2c.c90
-rw-r--r--drivers/iio/pressure/abp2030pa_spi.c67
-rw-r--r--drivers/iio/pressure/dlhl60d.c7
-rw-r--r--drivers/iio/pressure/mprls0025pa.c117
-rw-r--r--drivers/iio/pressure/mprls0025pa.h15
-rw-r--r--drivers/iio/pressure/mprls0025pa_i2c.c13
-rw-r--r--drivers/iio/pressure/mprls0025pa_spi.c41
-rw-r--r--drivers/iio/proximity/rfd77402.c185
-rw-r--r--drivers/iio/temperature/tmp006.c10
-rw-r--r--drivers/iio/test/Kconfig1
-rw-r--r--drivers/staging/iio/addac/adt7316-i2c.c2
-rw-r--r--drivers/staging/iio/addac/adt7316-spi.c2
-rw-r--r--drivers/staging/iio/addac/adt7316.c6
-rw-r--r--drivers/staging/iio/addac/adt7316.h6
-rw-r--r--drivers/staging/iio/frequency/ad9832.c37
-rw-r--r--drivers/staging/iio/frequency/ad9832.h33
-rw-r--r--include/linux/i3c/device.h22
-rw-r--r--include/linux/i3c/master.h6
-rw-r--r--include/linux/iio/buffer-dma.h20
-rw-r--r--include/linux/iio/buffer_impl.h8
-rw-r--r--include/linux/iio/frequency/ad9523.h2
-rw-r--r--include/linux/iio/iio.h139
-rw-r--r--include/linux/platform_data/cros_ec_commands.h12
-rw-r--r--include/linux/units.h19
125 files changed, 12096 insertions, 1227 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-i3c b/Documentation/ABI/testing/sysfs-bus-i3c
index c812ab180ff4..c1e048957a01 100644
--- a/Documentation/ABI/testing/sysfs-bus-i3c
+++ b/Documentation/ABI/testing/sysfs-bus-i3c
@@ -161,3 +161,14 @@ Contact: linux-i3c@vger.kernel.org
Description:
These directories are just symbolic links to
/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>.
+
+What: /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/dev_nack_retry_count
+KernelVersion: 6.18
+Contact: linux-i3c@vger.kernel.org
+Description:
+ Expose the dev_nak_retry_count which controls the number of
+ automatic retries that will be performed by the controller when
+ the target device returns a NACK response. A value of 0 disables
+ the automatic retries. Exist only when I3C constroller supports
+ this retry on nack feature.
+
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec
index 9e3926243797..3de1dfc98389 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec
+++ b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec
@@ -3,9 +3,12 @@ Date: July 2015
KernelVersion: 4.7
Contact: linux-iio@vger.kernel.org
Description:
- Writing '1' will perform a FOC (Fast Online Calibration). The
- corresponding calibration offsets can be read from `*_calibbias`
- entries.
+ Writing '1' either perform a FOC (Fast Online Calibration) or
+ enter calibration mode.
+ Writing '0` exits calibration mode. It is a NOP for FOC enabled
+ sensors.
+ The corresponding calibration offsets can be read from `*_calibbias`
+ entries.
What: /sys/bus/iio/devices/iio:deviceX/id
Date: September 2017
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml
new file mode 100644
index 000000000000..eeb148081663
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright 2025 Analog Devices Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad4062.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD4062 ADC family device driver
+
+maintainers:
+ - Jorge Marques <jorge.marques@analog.com>
+
+description: |
+ Analog Devices AD4062 Single Channel Precision SAR ADC family
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4060.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4062.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad4060
+ - adi,ad4062
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ description:
+ Two pins are available that can be configured as either a general purpose
+ digital output, device enable signal (used to synchronise other parts of
+ the signal chain with ADC sampling), device ready (GP1 only) or various
+ interrupt signals. If intended for use as a GPIO or device enable, will not
+ present here.
+ minItems: 1
+ items:
+ - description:
+ GP0 pin, cannot be configured as DEV_RDY.
+ - description:
+ GP1 pin, can be configured to any setting.
+
+ interrupt-names:
+ minItems: 1
+ items:
+ - const: gp0
+ - const: gp1
+
+ gpio-controller:
+ description:
+ Marks the device node as a GPIO controller. GPs not listed as interrupts
+ are exposed as a GPO.
+
+ '#gpio-cells':
+ const: 2
+ description:
+ The first cell is the GPIO number and the second cell specifies
+ GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
+
+ vdd-supply:
+ description: Analog power supply.
+
+ vio-supply:
+ description: Digital interface logic power supply.
+
+ ref-supply:
+ description:
+ Reference voltage to set the ADC full-scale range. If not present,
+ vdd-supply is used as the reference voltage.
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - vio-supply
+
+allOf:
+ - $ref: /schemas/i3c/i3c.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i3c {
+ #address-cells = <3>;
+ #size-cells = <0>;
+
+ adc@0,2ee007c0000 {
+ reg = <0x0 0x2ee 0x7c0000>;
+ vdd-supply = <&vdd>;
+ vio-supply = <&vio>;
+ ref-supply = <&ref>;
+
+ interrupt-parent = <&gpio>;
+ interrupts = <0 0 IRQ_TYPE_EDGE_RISING>,
+ <0 1 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-names = "gp0", "gp1";
+ };
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i3c {
+ #address-cells = <3>;
+ #size-cells = <0>;
+
+ adc@0,2ee007c0000 {
+ reg = <0x0 0x2ee 0x7c0000>;
+ vdd-supply = <&vdd>;
+ vio-supply = <&vio>;
+ ref-supply = <&ref>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml
new file mode 100644
index 000000000000..ea6d7e026419
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml
@@ -0,0 +1,191 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad4134.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD4134 ADC
+
+maintainers:
+ - Marcelo Schmitt <marcelo.schmitt@analog.com>
+
+description: |
+ The AD4134 is a quad channel, low noise, simultaneous sampling, precision
+ analog-to-digital converter (ADC).
+ Specifications can be found at:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4134.pdf
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ enum:
+ - adi,ad4134
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 50000000
+
+ avdd5-supply:
+ description: A 5V supply that powers the chip's analog circuitry.
+
+ dvdd5-supply:
+ description: A 5V supply that powers the chip's digital circuitry.
+
+ iovdd-supply:
+ description:
+ A 1.8V supply that sets the logic levels for the digital interface pins.
+
+ refin-supply:
+ description:
+ A 4.096V or 5V supply that serves as reference for ADC conversions.
+
+ avdd1v8-supply:
+ description: A 1.8V supply used by the analog circuitry.
+
+ dvdd1v8-supply:
+ description: A 1.8V supply used by the digital circuitry.
+
+ clkvdd-supply:
+ description: A 1.8V supply for the chip's clock management circuit.
+
+ ldoin-supply:
+ description:
+ A 2.6V to 5.5V supply that generates 1.8V for AVDD1V8, DVDD1V8, and CLKVDD
+ pins.
+
+ clocks:
+ maxItems: 1
+ description:
+ Required external clock source. Can specify either a crystal or CMOS clock
+ source. If an external crystal is set, connect the CLKSEL pin to IOVDD.
+ Otherwise, connect the CLKSEL pin to IOGND and the external CMOS clock
+ signal to the XTAL2/CLKIN pin.
+
+ clock-names:
+ enum:
+ - xtal
+ - clkin
+ default: clkin
+
+ '#clock-cells':
+ const: 0
+
+ clock-output-names:
+ maxItems: 1
+
+ regulators:
+ type: object
+ description:
+ list of regulators provided by this controller.
+
+ properties:
+ vcm-output:
+ $ref: /schemas/regulator/regulator.yaml#
+ type: object
+ unevaluatedProperties: false
+
+ additionalProperties: false
+
+ reset-gpios:
+ maxItems: 1
+
+ powerdown-gpios:
+ description:
+ Active low GPIO connected to the /PDN pin. Forces the device into full
+ power-down mode when brought low. Pull this input to IOVDD for normal
+ operation.
+ maxItems: 1
+
+ odr-gpios:
+ description:
+ GPIO connected to ODR pin. Used to sample ADC data in minimum I/O mode.
+ maxItems: 1
+
+ adi,asrc-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ Asynchronous Sample Rate Converter (ASRC) operation mode control input.
+ Describes whether the MODE pin is set to a high level (for master mode
+ operation) or to a low level (for slave mode operation).
+ enum: [ high, low ]
+ default: low
+
+ adi,dclkio:
+ description:
+ DCLK pin I/O direction control for when the device operates in Pin Control
+ Slave Mode or in SPI Control Mode. Describes if DEC0/DCLKIO pin is at a
+ high level (which configures DCLK as an output) or to set to a low level
+ (configuring DCLK for input).
+ enum: [ out, in ]
+ default: in
+
+ adi,dclkmode:
+ description:
+ DCLK mode control for when the device operates in Pin Control Slave Mode
+ or in SPI Control Mode. Describes whether the DEC1/DCLKMODE pin is set to
+ a high level (configuring the DCLK to operate in free running mode) or
+ to a low level (to configure DCLK to operate in gated mode).
+ enum: [ free-running, gated ]
+ default: gated
+
+required:
+ - compatible
+ - reg
+ - avdd5-supply
+ - dvdd5-supply
+ - iovdd-supply
+ - refin-supply
+ - clocks
+ - clock-names
+
+oneOf:
+ - required:
+ - ldoin-supply
+ - required:
+ - avdd1v8-supply
+ - dvdd1v8-supply
+ - clkvdd-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "adi,ad4134";
+ reg = <0>;
+
+ spi-max-frequency = <1000000>;
+
+ reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>;
+ odr-gpios = <&gpio0 87 GPIO_ACTIVE_HIGH>;
+ powerdown-gpios = <&gpio0 88 GPIO_ACTIVE_LOW>;
+
+ clocks = <&sys_clk>;
+ clock-names = "clkin";
+
+ avdd5-supply = <&avdd5>;
+ dvdd5-supply = <&dvdd5>;
+ iovdd-supply = <&iovdd>;
+ refin-supply = <&refin>;
+ avdd1v8-supply = <&avdd1v8>;
+ dvdd1v8-supply = <&dvdd1v8>;
+ clkvdd-supply = <&clkvdd>;
+
+ regulators {
+ vcm_reg: vcm-output {
+ regulator-name = "ad4134-vcm";
+ };
+ };
+
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
index c06d0fc791d3..dfa2d7fa9fb3 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
@@ -4,18 +4,26 @@
$id: http://devicetree.org/schemas/iio/adc/adi,ad7768-1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Analog Devices AD7768-1 ADC device driver
+title: Analog Devices AD7768-1 ADC family
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
description: |
- Datasheet at:
- https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf
+ Analog Devices AD7768-1 24-Bit Single Channel Low Power sigma-delta ADC family
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7767-1.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7768-1.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7769-1.pdf
properties:
compatible:
- const: adi,ad7768-1
+ enum:
+ - adi,ad7768-1
+ - adi,adaq7767-1
+ - adi,adaq7768-1
+ - adi,adaq7769-1
reg:
maxItems: 1
@@ -58,6 +66,25 @@ properties:
description:
ADC reference voltage supply
+ adi,aaf-gain-bp:
+ description: |
+ Specifies the gain applied by the Analog Anti-Aliasing Filter (AAF)
+ to the ADC input in basis points (one hundredth of a percent).
+ The hardware gain is determined by which input pin(s) the signal goes
+ through into the AAF. The possible connections are:
+ * For the ADAQ7767-1: Input connected to IN1±, IN2± or IN3±.
+ * For the ADAQ7769-1: OUT_PGA pin connected to IN1_AAF+, IN2_AAF+,
+ or IN3_AAF+.
+ enum: [1430, 3640, 10000]
+ default: 10000
+
+ pga-gpios:
+ description:
+ GAIN 0, GAIN1 and GAIN2 pins for gain selection. For devices that have
+ PGA configuration input pins, pga-gpios must be defined.
+ minItems: 3
+ maxItems: 3
+
adi,sync-in-gpios:
maxItems: 1
description:
@@ -147,6 +174,35 @@ patternProperties:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
+ # AAF Gain property only applies to ADAQ7767-1 and ADAQ7769-1 devices
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,adaq7767-1
+ - adi,adaq7769-1
+ then:
+ required:
+ - adi,aaf-gain-bp
+ else:
+ properties:
+ adi,aaf-gain-bp: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,adaq7768-1
+ - adi,adaq7769-1
+ then:
+ required:
+ - pga-gpios
+ else:
+ properties:
+ pga-gpios: false
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
index 2606c0c5dfc6..5acfb0eef4d5 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml
@@ -18,6 +18,7 @@ description: |
All the parts support the register map described by Application Note AN-877
https://www.analog.com/media/en/technical-documentation/application-notes/AN-877.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD9211.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9265.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9434.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9467.pdf
@@ -25,6 +26,7 @@ description: |
properties:
compatible:
enum:
+ - adi,ad9211
- adi,ad9265
- adi,ad9434
- adi,ad9467
diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml
index 509bfb1007c4..249101b55cf4 100644
--- a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml
@@ -44,6 +44,9 @@ properties:
Input clock used to derive the sample clock. Expected to be the
SoC's APB clock.
+ interrupts:
+ maxItems: 1
+
resets:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,s32g2-sar-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,s32g2-sar-adc.yaml
new file mode 100644
index 000000000000..ec258f224df8
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/nxp,s32g2-sar-adc.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/nxp,s32g2-sar-adc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP Successive Approximation ADC
+
+description:
+ The NXP SAR ADC provides fast and accurate analog-to-digital
+ conversion using the Successive Approximation Register (SAR) method.
+ It has 12-bit resolution with 8 input channels. Conversions can be
+ launched in software or using hardware triggers. It supports
+ continuous and one-shot modes with separate registers.
+
+maintainers:
+ - Daniel Lezcano <daniel.lezcano@kernel.org>
+
+properties:
+ compatible:
+ oneOf:
+ - const: nxp,s32g2-sar-adc
+ - items:
+ - const: nxp,s32g3-sar-adc
+ - const: nxp,s32g2-sar-adc
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ dmas:
+ maxItems: 1
+
+ dma-names:
+ const: rx
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - dmas
+ - dma-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ adc@401f8000 {
+ compatible = "nxp,s32g2-sar-adc";
+ reg = <0x401f8000 0x1000>;
+ interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks 0x41>;
+ dmas = <&edma0 0 32>;
+ dma-names = "rx";
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml
new file mode 100644
index 000000000000..81ee024be2e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/ti,ads1018.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI ADS1018/ADS1118 SPI analog to digital converter
+
+maintainers:
+ - Kurt Borja <kuurtb@gmail.com>
+
+description: |
+ The ADS1018/ADS1118 is a precision, low-power, 12-bit/16-bit, analog to
+ digital converter (ADC). It integrates a programmable gain amplifier (PGA),
+ internal voltage reference, oscillator and high-accuracy temperature sensor.
+
+ Datasheets:
+ - ADS1018: https://www.ti.com/lit/ds/symlink/ads1018.pdf
+ - ADS1118: https://www.ti.com/lit/ds/symlink/ads1118.pdf
+
+properties:
+ compatible:
+ enum:
+ - ti,ads1018
+ - ti,ads1118
+
+ reg:
+ maxItems: 1
+
+ vdd-supply: true
+
+ spi-max-frequency:
+ maximum: 4000000
+
+ spi-cpha: true
+
+ interrupts:
+ description: DOUT/DRDY (Data Out/Data Ready) line.
+ maxItems: 1
+
+ drdy-gpios:
+ description:
+ Extra GPIO line connected to DOUT/DRDY (Data Out/Data Ready). This allows
+ distinguishing between interrupts triggered by the data-ready signal and
+ interrupts triggered by an SPI transfer.
+ maxItems: 1
+
+ '#io-channel-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "ti,ads1118";
+ reg = <0>;
+
+ spi-max-frequency = <4000000>;
+ spi-cpha;
+
+ vdd-supply = <&vdd_3v3_reg>;
+
+ interrupts-extended = <&gpio 14 IRQ_TYPE_EDGE_FALLING>;
+ drdy-gpios = <&gpio 14 GPIO_ACTIVE_LOW>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads131m02.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads131m02.yaml
new file mode 100644
index 000000000000..5d52bb7dd5d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/ti,ads131m02.yaml
@@ -0,0 +1,208 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/ti,ads131m02.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments ADS131M0x 2-, 3-, 4-, 6- and 8-Channel ADCs
+
+maintainers:
+ - Oleksij Rempel <o.rempel@pengutronix.de>
+
+description: |
+ The ADS131M0x are a family of multichannel, simultaneous sampling,
+ 24-bit, delta-sigma, analog-to-digital converters (ADCs) with a
+ built-in programmable gain amplifier (PGA) and internal reference.
+ Communication with the ADC chip is via SPI.
+
+ Datasheets:
+ - ADS131M02: https://www.ti.com/lit/ds/symlink/ads131m02.pdf
+ - ADS131M03: https://www.ti.com/lit/ds/symlink/ads131m03.pdf
+ - ADS131M04: https://www.ti.com/lit/ds/symlink/ads131m04.pdf
+ - ADS131M06: https://www.ti.com/lit/ds/symlink/ads131m06.pdf
+ - ADS131M08: https://www.ti.com/lit/ds/symlink/ads131m08.pdf
+
+properties:
+ compatible:
+ enum:
+ - ti,ads131m02
+ - ti,ads131m03
+ - ti,ads131m04
+ - ti,ads131m06
+ - ti,ads131m08
+
+ reg:
+ description: SPI chip select number.
+
+ clocks:
+ description:
+ Phandle to the external clock source required by the ADC's CLKIN pin.
+ The datasheet recommends specific frequencies based on the desired power
+ mode (e.g., 8.192 MHz for High-Resolution mode).
+ maxItems: 1
+
+ avdd-supply:
+ description: Analog power supply (AVDD).
+
+ dvdd-supply:
+ description: Digital power supply (DVDD).
+
+ interrupts:
+ description: DRDY (Data Ready) output signal.
+ maxItems: 1
+
+ reset-gpios:
+ description: Optional RESET signal.
+ maxItems: 1
+
+ clock-names:
+ description:
+ Indicates if a crystal oscillator (XTAL) or CMOS signal is connected
+ (CLKIN). Note that XTAL mode is only supported on ADS131M06 and ADS131M08.
+ enum: [xtal, clkin]
+
+ refin-supply:
+ description: Optional external reference supply (REFIN).
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - avdd-supply
+ - dvdd-supply
+
+patternProperties:
+ "^channel@[0-7]$":
+ type: object
+ $ref: /schemas/iio/adc/adc.yaml#
+ description: Properties for a single ADC channel.
+
+ properties:
+ reg:
+ description: The channel index (0-7).
+ minimum: 0
+ maximum: 7 # Max channels on ADS131M08
+
+ label: true
+
+ required:
+ - reg
+
+ unevaluatedProperties: false
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+ - if:
+ # 20-pin devices: M02, M03, M04
+ # These do not support XTAL or REFIN.
+ properties:
+ compatible:
+ enum:
+ - ti,ads131m02
+ - ti,ads131m03
+ - ti,ads131m04
+ then:
+ properties:
+ clock-names:
+ const: clkin
+ refin-supply: false
+
+ - if:
+ # ADS131M02: 2 channels max (0-1)
+ properties:
+ compatible:
+ contains:
+ const: ti,ads131m02
+ then:
+ patternProperties:
+ "^channel@[0-1]$":
+ properties:
+ reg:
+ maximum: 1
+ "^channel@[2-7]$": false
+
+ - if:
+ # ADS131M03: 3 channels max (0-2)
+ properties:
+ compatible:
+ contains:
+ const: ti,ads131m03
+ then:
+ patternProperties:
+ "^channel@[0-2]$":
+ properties:
+ reg:
+ maximum: 2
+ "^channel@[3-7]$": false
+
+ - if:
+ # ADS131M04: 4 channels max (0-3)
+ properties:
+ compatible:
+ contains:
+ const: ti,ads131m04
+ then:
+ patternProperties:
+ "^channel@[0-3]$":
+ properties:
+ reg:
+ maximum: 3
+ "^channel@[4-7]$": false
+
+ - if:
+ # ADS131M06: 6 channels max (0-5)
+ properties:
+ compatible:
+ contains:
+ const: ti,ads131m06
+ then:
+ patternProperties:
+ "^channel@[0-5]$":
+ properties:
+ reg:
+ maximum: 5
+ "^channel@[6-7]$": false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/stm32mp1-clks.h>
+
+ spi1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "ti,ads131m02";
+ reg = <0>;
+ spi-max-frequency = <8000000>;
+
+ clocks = <&rcc CK_MCO2>;
+ clock-names = "clkin";
+
+ avdd-supply = <&vdd_ana>;
+ dvdd-supply = <&vdd_dig>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ label = "input_voltage";
+ };
+
+ channel@1 {
+ reg = <1>;
+ label = "input_current";
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/amplifiers/adi,adl8113.yaml b/Documentation/devicetree/bindings/iio/amplifiers/adi,adl8113.yaml
new file mode 100644
index 000000000000..6b8491d18139
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/amplifiers/adi,adl8113.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/amplifiers/adi,adl8113.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADL8113 Low Noise Amplifier with integrated bypass switches
+
+maintainers:
+ - Antoniu Miclaus <antoniu.miclaus@analog.com>
+
+description: |
+ The ADL8113 is a 10MHz to 12GHz Low Noise Amplifier with integrated bypass
+ switches controlled by two GPIO pins (VA and VB). The device supports four
+ operation modes:
+ - Internal Amplifier: VA=0, VB=0 - Signal passes through the internal LNA
+ - Internal Bypass: VA=1, VB=1 - Signal bypasses through internal path
+ - External Bypass A: VA=0, VB=1 - Signal routes from RFIN to OUT_A and from IN_A to RFOUT
+ - External Bypass B: VA=1, VB=0 - Signal routes from RFIN to OUT_B and from IN_B to RFOUT
+
+ https://www.analog.com/en/products/adl8113.html
+
+properties:
+ compatible:
+ const: adi,adl8113
+
+ vdd1-supply: true
+
+ vdd2-supply: true
+
+ vss2-supply: true
+
+ ctrl-gpios:
+ items:
+ - description: VA control pin
+ - description: VB control pin
+
+ adi,external-bypass-a-gain-db:
+ description:
+ Gain in dB of external amplifier connected to bypass path A (OUT_A/IN_A).
+ When specified, this gain value becomes selectable via the hardwaregain
+ attribute and automatically routes through the external A path.
+
+ adi,external-bypass-b-gain-db:
+ description:
+ Gain in dB of external amplifier connected to bypass path B (OUT_B/IN_B).
+ When specified, this gain value becomes selectable via the hardwaregain
+ attribute and automatically routes through the external B path.
+
+required:
+ - compatible
+ - ctrl-gpios
+ - vdd1-supply
+ - vdd2-supply
+ - vss2-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ /* Basic configuration with only internal paths */
+ amplifier {
+ compatible = "adi,adl8113";
+ ctrl-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>,
+ <&gpio 23 GPIO_ACTIVE_HIGH>;
+ vdd1-supply = <&vdd1_5v>;
+ vdd2-supply = <&vdd2_3v3>;
+ vss2-supply = <&vss2_neg>;
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ /* Configuration with external bypass amplifiers */
+ amplifier {
+ compatible = "adi,adl8113";
+ ctrl-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>,
+ <&gpio 25 GPIO_ACTIVE_HIGH>;
+ vdd1-supply = <&vdd1_5v>;
+ vdd2-supply = <&vdd2_3v3>;
+ vss2-supply = <&vss2_neg>;
+ adi,external-bypass-a-gain-db = <20>; /* 20dB external amp on path A */
+ adi,external-bypass-b-gain-db = <6>; /* 6dB external amp on path B */
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,max22007.yaml b/Documentation/devicetree/bindings/iio/dac/adi,max22007.yaml
new file mode 100644
index 000000000000..93d95f6b4c08
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,max22007.yaml
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,max22007.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX22007 DAC
+
+maintainers:
+ - Janani Sunil <janani.sunil@analog.com>
+
+description:
+ The MAX22007 is a quad-channel, 12-bit digital-to-analog converter (DAC)
+ with integrated precision output amplifiers and current output capability.
+ Each channel can be independently configured for voltage or current output.
+ Datasheet available at https://www.analog.com/en/products/max22007.html
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ const: adi,max22007
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 500000
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ vdd-supply:
+ description: Low-Voltage Power Supply from +2.7V to +5.5V.
+
+ hvdd-supply:
+ description:
+ Positive High-Voltage Power Supply from +8V to (HVSS +24V) for
+ the Output Channels.
+
+ hvss-supply:
+ description:
+ Optional Negative High-Voltage Power Supply from -2V to 0V for the Output
+ Channels. For most applications HVSS can be connected to GND (0V), but for
+ applications requiring output down to true 0V or 0mA, connect to a -2V supply.
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ Active low GPIO.
+
+patternProperties:
+ "^channel@[0-3]$":
+ $ref: /schemas/iio/dac/dac.yaml#
+ type: object
+ description:
+ Represents the external channels which are connected to the DAC.
+
+ properties:
+ reg:
+ description: Channel number
+ items:
+ minimum: 0
+ maximum: 3
+
+ adi,ch-func:
+ description:
+ Channel output type. Use CH_FUNC_VOLTAGE_OUTPUT for voltage
+ output or CH_FUNC_CURRENT_OUTPUT for current output.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1, 2]
+
+ required:
+ - reg
+ - adi,ch-func
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - hvdd-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/iio/addac/adi,ad74413r.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,max22007";
+ reg = <0>;
+ spi-max-frequency = <500000>;
+ reset-gpios = <&gpio 19 GPIO_ACTIVE_LOW>;
+ vdd-supply = <&vdd_reg>;
+ hvdd-supply = <&hvdd_reg>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ adi,ch-func = <CH_FUNC_VOLTAGE_OUTPUT>;
+ };
+
+ channel@1 {
+ reg = <1>;
+ adi,ch-func = <CH_FUNC_CURRENT_OUTPUT>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
new file mode 100644
index 000000000000..d2466aa6bda2
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
@@ -0,0 +1,302 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/microchip,mcp47feb02.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip MCP47F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C Interface Families
+
+maintainers:
+ - Ariana Lazar <ariana.lazar@microchip.com>
+
+description: |
+ Datasheet for MCP47FEB01, MCP47FEB11, MCP47FEB21, MCP47FEB02, MCP47FEB12,
+ MCP47FEB22 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
+ Datasheet for MCP47FVB01, MCP47FVB11, MCP47FVB21, MCP47FVB02, MCP47FVB12,
+ MCP47FVB22 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
+ Datasheet for MCP47FEB04, MCP47FEB14, MCP47FEB24, MCP47FEB08, MCP47FEB18,
+ MCP47FEB28, MCP47FVB04, MCP47FVB14, MCP47FVB24, MCP47FVB08, MCP47FVB18,
+ MCP47FVB28 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
+
+ +------------+--------------+-------------+-------------+------------+
+ | Device | Resolution | Channels | Vref number | Memory |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FEB01 | 8-bit | 1 | 1 | EEPROM |
+ | MCP47FEB11 | 10-bit | 1 | 1 | EEPROM |
+ | MCP47FEB21 | 12-bit | 1 | 1 | EEPROM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FEB02 | 8-bit | 2 | 1 | EEPROM |
+ | MCP47FEB12 | 10-bit | 2 | 1 | EEPROM |
+ | MCP47FEB22 | 12-bit | 2 | 1 | EEPROM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FVB01 | 8-bit | 1 | 1 | RAM |
+ | MCP47FVB11 | 10-bit | 1 | 1 | RAM |
+ | MCP47FVB21 | 12-bit | 1 | 1 | RAM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FVB02 | 8-bit | 2 | 1 | RAM |
+ | MCP47FVB12 | 10-bit | 2 | 1 | RAM |
+ | MCP47FVB22 | 12-bit | 2 | 1 | RAM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FVB04 | 8-bit | 4 | 2 | RAM |
+ | MCP47FVB14 | 10-bit | 4 | 2 | RAM |
+ | MCP47FVB24 | 12-bit | 4 | 2 | RAM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FVB08 | 8-bit | 8 | 2 | RAM |
+ | MCP47FVB18 | 10-bit | 8 | 2 | RAM |
+ | MCP47FVB28 | 12-bit | 8 | 2 | RAM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FEB04 | 8-bit | 4 | 2 | EEPROM |
+ | MCP47FEB14 | 10-bit | 4 | 2 | EEPROM |
+ | MCP47FEB24 | 12-bit | 4 | 2 | EEPROM |
+ |------------|--------------|-------------|-------------|------------|
+ | MCP47FEB08 | 8-bit | 8 | 2 | EEPROM |
+ | MCP47FEB18 | 10-bit | 8 | 2 | EEPROM |
+ | MCP47FEB28 | 12-bit | 8 | 2 | EEPROM |
+ +------------+--------------+-------------+-------------+------------+
+
+properties:
+ compatible:
+ enum:
+ - microchip,mcp47feb01
+ - microchip,mcp47feb11
+ - microchip,mcp47feb21
+ - microchip,mcp47feb02
+ - microchip,mcp47feb12
+ - microchip,mcp47feb22
+ - microchip,mcp47fvb01
+ - microchip,mcp47fvb11
+ - microchip,mcp47fvb21
+ - microchip,mcp47fvb02
+ - microchip,mcp47fvb12
+ - microchip,mcp47fvb22
+ - microchip,mcp47fvb04
+ - microchip,mcp47fvb14
+ - microchip,mcp47fvb24
+ - microchip,mcp47fvb08
+ - microchip,mcp47fvb18
+ - microchip,mcp47fvb28
+ - microchip,mcp47feb04
+ - microchip,mcp47feb14
+ - microchip,mcp47feb24
+ - microchip,mcp47feb08
+ - microchip,mcp47feb18
+ - microchip,mcp47feb28
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ vdd-supply:
+ description:
+ Provides power to the chip and it could be used as reference voltage. The
+ voltage is used to calculate scale. For parts without EEPROM at powerup
+ this will be the selected as voltage reference.
+
+ vref-supply:
+ description: |
+ Vref pin (it could be found as Vref0 into the datasheet) may be used as a
+ voltage reference when this supply is specified. The internal reference
+ will be taken into account for voltage reference besides VDD if this supply
+ does not exist.
+
+ This supply will be voltage reference for the following outputs:
+ - for single-channel device: Vout0;
+ - for dual-channel device: Vout0, Vout1;
+ - for quad-channel device: Vout0, Vout2;
+ - for octal-channel device: Vout0, Vout2, Vout6, Vout8;
+
+ vref1-supply:
+ description: |
+ Vref1 pin may be used as a voltage reference when this supply is specified.
+ The internal reference will be taken into account for voltage reference
+ beside VDD if this supply does not exist.
+
+ This supply will be voltage reference for the following outputs:
+ - for quad-channel device: Vout1, Vout3;
+ - for octal-channel device: Vout1, Vout3, Vout5, Vout7;
+
+ lat-gpios:
+ description:
+ LAT pin to be used as a hardware trigger to synchronously update the DAC
+ channels. The pin is active Low. It could be also found as LAT0 in
+ datasheet.
+ maxItems: 1
+
+ lat1-gpios:
+ description:
+ LAT1 pin to be used as a hardware trigger to synchronously update the odd
+ DAC channels on devices with 4 and 8 channels. The pin is active Low.
+ maxItems: 1
+
+ microchip,vref-buffered:
+ type: boolean
+ description:
+ Enable buffering of the external Vref/Vref0 pin in cases where the
+ external reference voltage does not have sufficient current capability in
+ order not to drop it’s voltage when connected to the internal resistor
+ ladder circuit.
+
+ microchip,vref1-buffered:
+ type: boolean
+ description:
+ Enable buffering of the external Vref1 pin in cases where the external
+ reference voltage does not have sufficient current capability in order not
+ to drop it’s voltage when connected to the internal resistor ladder
+ circuit.
+
+patternProperties:
+ "^channel@[0-7]$":
+ $ref: dac.yaml
+ type: object
+ description: Voltage output channel.
+
+ properties:
+ reg:
+ description: The channel number.
+ minItems: 1
+ maxItems: 8
+
+ label:
+ description: Unique name to identify which channel this is.
+
+ required:
+ - reg
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp47feb01
+ - microchip,mcp47feb11
+ - microchip,mcp47feb21
+ - microchip,mcp47fvb01
+ - microchip,mcp47fvb11
+ - microchip,mcp47fvb21
+ then:
+ properties:
+ lat1-gpios: false
+ vref1-supply: false
+ microchip,vref1-buffered: false
+ channel@0:
+ properties:
+ reg:
+ const: 0
+ patternProperties:
+ "^channel@[1-7]$": false
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp47feb02
+ - microchip,mcp47feb12
+ - microchip,mcp47feb22
+ - microchip,mcp47fvb02
+ - microchip,mcp47fvb12
+ - microchip,mcp47fvb22
+ then:
+ properties:
+ lat1-gpios: false
+ vref1-supply: false
+ microchip,vref1-buffered: false
+ patternProperties:
+ "^channel@[0-1]$":
+ properties:
+ reg:
+ enum: [0, 1]
+ "^channel@[2-7]$": false
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp47fvb04
+ - microchip,mcp47fvb14
+ - microchip,mcp47fvb24
+ - microchip,mcp47feb04
+ - microchip,mcp47feb14
+ - microchip,mcp47feb24
+ then:
+ patternProperties:
+ "^channel@[0-3]$":
+ properties:
+ reg:
+ enum: [0, 1, 2, 3]
+ "^channel@[4-7]$": false
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - microchip,mcp47fvb08
+ - microchip,mcp47fvb18
+ - microchip,mcp47fvb28
+ - microchip,mcp47feb08
+ - microchip,mcp47feb18
+ - microchip,mcp47feb28
+ then:
+ patternProperties:
+ "^channel@[0-7]$":
+ properties:
+ reg:
+ enum: [0, 1, 2, 3, 4, 5, 6, 7]
+ - if:
+ not:
+ required:
+ - vref-supply
+ then:
+ properties:
+ microchip,vref-buffered: false
+ - if:
+ not:
+ required:
+ - vref1-supply
+ then:
+ properties:
+ microchip,vref1-buffered: false
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ dac@0 {
+ compatible = "microchip,mcp47feb02";
+ reg = <0>;
+ vdd-supply = <&vdac_vdd>;
+ vref-supply = <&vref_reg>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ label = "Adjustable_voltage_ch0";
+ };
+
+ channel@1 {
+ reg = <0x1>;
+ label = "Adjustable_voltage_ch1";
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml
index 5f950ee9aec7..be69b9c68e74 100644
--- a/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml
+++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf4377.yaml
@@ -40,6 +40,12 @@ properties:
items:
- const: ref_in
+ '#clock-cells':
+ const: 0
+
+ clock-output-names:
+ maxItems: 1
+
chip-enable-gpios:
description:
GPIO that controls the Chip Enable Pin.
@@ -97,6 +103,8 @@ examples:
spi-max-frequency = <10000000>;
clocks = <&adf4377_ref_in>;
clock-names = "ref_in";
+ #clock-cells = <0>;
+ clock-output-names = "adf4377";
};
};
...
diff --git a/Documentation/devicetree/bindings/iio/pressure/honeywell,abp2030pa.yaml b/Documentation/devicetree/bindings/iio/pressure/honeywell,abp2030pa.yaml
new file mode 100644
index 000000000000..e82897ffac3b
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/pressure/honeywell,abp2030pa.yaml
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/pressure/honeywell,abp2030pa.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Honeywell abp2030pa pressure sensor
+
+maintainers:
+ - Petre Rodan <petre.rodan@subdimension.ro>
+
+description: |
+ Honeywell pressure sensor of model abp2030pa.
+
+ This sensor has an I2C and SPI interface.
+
+ There are many models with different pressure ranges available. The vendor
+ calls them "ABP2 series". All of them have an identical programming model and
+ differ in the pressure range and measurement unit.
+
+ To support different models one needs to specify its pressure triplet.
+
+ For custom silicon chips not covered by the Honeywell ABP2 series datasheet,
+ the pressure values can be specified manually via honeywell,pmin-pascal and
+ honeywell,pmax-pascal.
+
+ Specifications about the devices can be found at:
+ https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/basic-abp2-series/documents/sps-siot-abp2-series-datasheet-32350268-en.pdf
+
+properties:
+ compatible:
+ const: honeywell,abp2030pa
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ description:
+ Optional interrupt for indicating end of conversion.
+ SPI variants of ABP2 chips do not provide this feature.
+ maxItems: 1
+
+ honeywell,pressure-triplet:
+ description: |
+ Case-sensitive five character string that defines pressure range, unit
+ and type as part of the device nomenclature. In the unlikely case of a
+ custom chip, unset and provide pmin-pascal and pmax-pascal instead.
+ enum: [001BA, 1.6BA, 2.5BA, 004BA, 006BA, 008BA, 010BA, 012BA, 001BD,
+ 1.6BD, 2.5BD, 004BD, 001BG, 1.6BG, 2.5BG, 004BG, 006BG, 008BG,
+ 010BG, 012BG, 001GG, 1.2GG, 100KA, 160KA, 250KA, 001KD, 1.6KD,
+ 2.5KD, 004KD, 006KD, 010KD, 016KD, 025KD, 040KD, 060KD, 100KD,
+ 160KD, 250KD, 400KD, 001KG, 1.6KG, 2.5KG, 004KG, 006KG, 010KG,
+ 016KG, 025KG, 040KG, 060KG, 100KG, 160KG, 250KG, 400KG, 600KG,
+ 800KG, 250LD, 600LD, 600LG, 2.5MD, 006MD, 010MD, 016MD, 025MD,
+ 040MD, 060MD, 100MD, 160MD, 250MD, 400MD, 600MD, 006MG, 010MG,
+ 016MG, 025MG, 040MG, 060MG, 100MG, 160MG, 250MG, 400MG, 600MG,
+ 001ND, 002ND, 004ND, 005ND, 010ND, 020ND, 030ND, 002NG, 004NG,
+ 005NG, 010NG, 020NG, 030NG, 015PA, 030PA, 060PA, 100PA, 150PA,
+ 175PA, 001PD, 005PD, 015PD, 030PD, 060PD, 001PG, 005PG, 015PG,
+ 030PG, 060PG, 100PG, 150PG, 175PG]
+ $ref: /schemas/types.yaml#/definitions/string
+
+ honeywell,pmin-pascal:
+ description:
+ Minimum pressure value the sensor can measure in pascal.
+
+ honeywell,pmax-pascal:
+ description:
+ Maximum pressure value the sensor can measure in pascal.
+
+ spi-max-frequency:
+ maximum: 800000
+
+ vdd-supply: true
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+oneOf:
+ - required:
+ - honeywell,pressure-triplet
+ - required:
+ - honeywell,pmin-pascal
+ - honeywell,pmax-pascal
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml
+ - if:
+ required:
+ - honeywell,pressure-triplet
+ then:
+ properties:
+ honeywell,pmin-pascal: false
+ honeywell,pmax-pascal: false
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pressure@18 {
+ compatible = "honeywell,abp2030pa";
+ reg = <0x18>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <21 IRQ_TYPE_EDGE_RISING>;
+
+ honeywell,pressure-triplet = "001BA";
+ vdd-supply = <&vcc_3v3>;
+ };
+ };
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pressure@0 {
+ compatible = "honeywell,abp2030pa";
+ reg = <0>;
+ spi-max-frequency = <800000>;
+
+ honeywell,pressure-triplet = "001PD";
+ vdd-supply = <&vcc_3v3>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/proximity/rfdigital,rfd77402.yaml b/Documentation/devicetree/bindings/iio/proximity/rfdigital,rfd77402.yaml
new file mode 100644
index 000000000000..1ef6326b209e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/proximity/rfdigital,rfd77402.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/proximity/rfdigital,rfd77402.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: RF Digital RFD77402 ToF sensor
+
+maintainers:
+ - Shrikant Raskar <raskar.shree97@gmail.com>
+
+description:
+ The RF Digital RFD77402 is a Time-of-Flight (ToF) proximity and distance
+ sensor providing up to 200 mm range measurement over an I2C interface.
+
+properties:
+ compatible:
+ const: rfdigital,rfd77402
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+ description:
+ Interrupt asserted when a new distance measurement is available.
+
+ vdd-supply:
+ description: Regulator that provides power to the sensor.
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ proximity@4c {
+ compatible = "rfdigital,rfd77402";
+ reg = <0x4c>;
+ vdd-supply = <&vdd_3v3>;
+ interrupt-parent = <&gpio>;
+ interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index d0f7dbf15d6f..055c9e2b7d47 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -229,6 +229,10 @@ properties:
- meas,tsys01
# MEMSIC magnetometer
- memsic,mmc35240
+ # MEMSIC 3-axis magnetometer
+ - memsic,mmc5603
+ # MEMSIC 3-axis magnetometer (Support I3C HDR)
+ - memsic,mmc5633
# MEMSIC 3-axis accelerometer
- memsic,mxc4005
# MEMSIC 2-axis 8-bit digital accelerometer
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index c7591b2aec2a..59ac4f0756d9 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1361,6 +1361,8 @@ patternProperties:
description: Revolution Robotics, Inc. (Revotics)
"^rex,.*":
description: iMX6 Rex Project
+ "^rfdigital,.*":
+ description: RF Digital Corporation
"^richtek,.*":
description: Richtek Technology Corporation
"^ricoh,.*":
diff --git a/Documentation/iio/ad4062.rst b/Documentation/iio/ad4062.rst
new file mode 100644
index 000000000000..d77287836430
--- /dev/null
+++ b/Documentation/iio/ad4062.rst
@@ -0,0 +1,148 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=============
+AD4062 driver
+=============
+
+ADC driver for Analog Devices Inc. AD4060/AD4062 devices. The module name is
+``ad4062``.
+
+Supported devices
+=================
+
+The following chips are supported by this driver:
+
+* `AD4060 <https://www.analog.com/AD4060>`_
+* `AD4062 <https://www.analog.com/AD4062>`_
+
+Wiring modes
+============
+
+The ADC is interfaced through an I3C bus, and contains two programmable GPIOs.
+
+The ADC convert-start happens on the SDA rising edge of the I3C stop (P) bit
+at the end of the read command.
+
+The two programmable GPIOS are optional and have a role assigned if present in
+the devicetree ``interrupt-names`` property:
+
+- GP0: Is assigned the role of Threshold Either signal.
+- GP1: Is assigned the role of Data Ready signal.
+
+If the property ``gpio-controller`` is present in the devicetree, then the GPO
+not present in the ``interrupt-names`` is exposed as a GPO.
+
+Device attributes
+=================
+
+The ADC contains only one channel with following attributes:
+
+.. list-table:: Channel attributes
+ :header-rows: 1
+
+ * - Attribute
+ - Description
+ * - ``in_voltage_calibscale``
+ - Sets the gain scaling factor that the hardware applies to the sample,
+ to compensate for system gain error.
+ * - ``in_voltage_oversampling_ratio``
+ - Sets device's burst averaging mode to over sample using the
+ internal sample rate. Value 1 disable the burst averaging mode.
+ * - ``in_voltage_oversampling_ratio_available``
+ - List of available oversampling values.
+ * - ``in_voltage_raw``
+ - Returns the raw ADC voltage value.
+ * - ``in_voltage_scale``
+ - Returns the channel scale in reference to the reference voltage
+ ``ref-supply`` or ``vdd-supply`` if the former not present.
+
+Also contain the following device attributes:
+
+.. list-table:: Device attributes
+ :header-rows: 1
+
+ * - Attribute
+ - Description
+ * - ``sampling_frequency``
+ - Sets the duration of a single scan, used in the burst averaging mode.
+ The duration is described by ``(n_avg - 1) / fosc + tconv``, where
+ ``n_avg`` is the oversampling ratio, ``fosc`` is the internal sample
+ rate and ``tconv`` is the ADC conversion time.
+ * - ``sampling_frequency_available``
+ - Lists the available sampling frequencies, computed on the current
+ oversampling ratio. If the ratio is 1, the frequency is ``1/tconv``.
+
+Interrupts
+==========
+
+The interrupts are mapped through the ``interrupt-names`` and ``interrupts``
+properties.
+
+The ``interrupt-names`` ``gp0`` entry sets the role of Threshold signal, and
+entry ``gp1`` the role of Data Ready signal.
+
+If each is not present, the driver fallback to enabling the same role as an
+I3C IBI.
+
+Low-power mode
+==============
+
+The device enters low-power mode on idle to save power. Enabling an event puts
+the device out of the low-power since the ADC autonomously samples to assert
+the event condition.
+
+IIO trigger support
+===================
+
+An IIO trigger ``ad4062-devX`` is registered by the driver to be used by the
+same device, to capture samples to a software buffer. It is required to attach
+the trigger to the device by setting the ``current_trigger`` before enabling
+and reading the buffer.
+
+The acquisition is sequential and bounded by the protocol timings, software
+latency and internal timings, the sample rate is not configurable. The burst
+averaging mode does impact the effective sample rate, since it increases the
+internal timing to output a single sample.
+
+Threshold events
+================
+
+The ADC supports a monitoring mode to raise threshold events. The driver
+supports a single interrupt for both rising and falling readings.
+
+The feature is enabled/disabled by setting ``thresh_either_en``. During monitor
+mode, the device continuously operates in autonomous mode. Any register access
+puts the device back in configuration mode, due to this, any access disables
+monitor mode.
+
+The following event attributes are available:
+
+.. list-table:: Event attributes
+ :header-rows: 1
+
+ * - Attribute
+ - Description
+ * - ``sampling_frequency``
+ - Frequency used in the monitoring mode, sets the device internal sample
+ rate when the mode is activated.
+ * - ``sampling_frequency_available``
+ - List of available sample rates.
+ * - ``thresh_either_en``
+ - Enable monitoring mode.
+ * - ``thresh_falling_hysteresis``
+ - Set the hysteresis value for the minimum threshold.
+ * - ``thresh_falling_value``
+ - Set the minimum threshold value.
+ * - ``thresh_rising_hysteresis``
+ - Set the hysteresis value for the maximum threshold.
+ * - ``thresh_rising_value``
+ - Set the maximum threshold value.
+
+GPO controller support
+======================
+
+The device supports using GP0 and GP1 as GPOs. If the devicetree contains the
+node ``gpio-controller```, the device is marked as a GPIO controller and the
+GPs not listed in ``interrupt-names`` are exposed as a GPO. The GPIO index
+matches the pin name, so if GP0 is not exposed but GP1 is, index 0 is masked
+out and only index 1 can be set.
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 315ae37d6fd4..ba3e609c6a13 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -22,6 +22,7 @@ Industrial I/O Kernel Drivers
ad3552r
ad4000
ad4030
+ ad4062
ad4695
ad7191
ad7380
diff --git a/MAINTAINERS b/MAINTAINERS
index 0c4a3297033d..d8b2f326554b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1435,6 +1435,14 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
F: Documentation/iio/ad4030.rst
F: drivers/iio/adc/ad4030.c
+ANALOG DEVICES INC AD4062 DRIVER
+M: Jorge Marques <jorge.marques@analog.com>
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml
+F: Documentation/iio/ad4062.rst
+F: drivers/iio/adc/ad4062.c
+
ANALOG DEVICES INC AD4080 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -1452,6 +1460,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
F: drivers/iio/adc/ad4130.c
+ANALOG DEVICES INC AD4134 DRIVER
+M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ad4134.yaml
+F: drivers/iio/adc/ad4134.c
+
ANALOG DEVICES INC AD4170-4 DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
L: linux-iio@vger.kernel.org
@@ -1596,6 +1612,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
F: drivers/iio/dac/ad9739a.c
+ANALOG DEVICES INC MAX22007 DRIVER
+M: Janani Sunil <janani.sunil@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,max22007.yaml
+F: drivers/iio/dac/max22007.c
+
ANALOG DEVICES INC ADA4250 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -1604,6 +1628,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml
F: drivers/iio/amplifiers/ada4250.c
+ANALOG DEVICES INC ADE9000 DRIVER
+M: Antoniu Miclaus <antoniu.miclaus@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
+F: drivers/iio/adc/ade9000.c
+
ANALOG DEVICES INC ADF4377 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -11515,6 +11547,13 @@ F: lib/test_hmm*
F: mm/hmm*
F: tools/testing/selftests/mm/*hmm*
+HONEYWELL ABP2030PA PRESSURE SENSOR SERIES IIO DRIVER
+M: Petre Rodan <petre.rodan@subdimension.ro>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/pressure/honeywell,abp2030pa.yaml
+F: drivers/iio/pressure/abp2030pa*
+
HONEYWELL HSC030PA PRESSURE SENSOR SERIES IIO DRIVER
M: Petre Rodan <petre.rodan@subdimension.ro>
L: linux-iio@vger.kernel.org
@@ -15663,6 +15702,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
F: drivers/iio/potentiometer/mcp4018.c
F: drivers/iio/potentiometer/mcp4531.c
+MCP47FEB02 MICROCHIP DAC DRIVER
+M: Ariana Lazar <ariana.lazar@microchip.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
+F: drivers/iio/dac/mcp47feb02.c
+
MCP4821 DAC DRIVER
M: Anshul Dalal <anshulusr@gmail.com>
L: linux-iio@vger.kernel.org
@@ -26031,6 +26077,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml
F: drivers/iio/adc/ti-ads1119.c
+TI ADS1018 ADC DRIVER
+M: Kurt Borja <kuurtb@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml
+F: drivers/iio/adc/ti-ads1018.c
+
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
L: linux-iio@vger.kernel.org
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 7f606c871648..e551b1de5d7b 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -683,6 +683,39 @@ static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, cha
static DEVICE_ATTR_RW(hotjoin);
+static ssize_t dev_nack_retry_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count);
+}
+
+static ssize_t dev_nack_retry_count_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
+ struct i3c_master_controller *master = dev_to_i3cmaster(dev);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ i3c_bus_maintenance_lock(i3cbus);
+ ret = master->ops->set_dev_nack_retry(master, val);
+ i3c_bus_maintenance_unlock(i3cbus);
+
+ if (ret)
+ return ret;
+
+ master->dev_nack_retry_count = val;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(dev_nack_retry_count);
+
static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_current_master.attr,
@@ -2370,19 +2403,16 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
{
struct device *dev = &master->dev;
struct device_node *i3cbus_np = dev->of_node;
- struct device_node *node;
int ret;
u32 val;
if (!i3cbus_np)
return 0;
- for_each_available_child_of_node(i3cbus_np, node) {
+ for_each_available_child_of_node_scoped(i3cbus_np, node) {
ret = of_i3c_master_add_dev(master, node);
- if (ret) {
- of_node_put(node);
+ if (ret)
return ret;
- }
}
/*
@@ -2959,6 +2989,9 @@ int i3c_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
+ if (master->ops->set_dev_nack_retry)
+ device_create_file(&master->dev, &dev_attr_dev_nack_retry_count);
+
return 0;
err_del_dev:
@@ -2984,6 +3017,9 @@ void i3c_master_unregister(struct i3c_master_controller *master)
{
i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
+ if (master->ops->set_dev_nack_retry)
+ device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count);
+
i3c_master_i2c_adapter_cleanup(master);
i3c_master_unregister_i3c_devs(master);
i3c_master_bus_cleanup(master);
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 889e2ed5bc83..48af00659e19 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -5,6 +5,7 @@
* Author: Vitor Soares <vitor.soares@synopsys.com>
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
@@ -204,11 +205,17 @@
#define EXTENDED_CAPABILITY 0xe8
#define SLAVE_CONFIG 0xec
+#define DW_I3C_DEV_NACK_RETRY_CNT_MAX 0x3
+#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK GENMASK(30, 29)
+#define DEV_ADDR_TABLE_DYNAMIC_MASK GENMASK(23, 16)
+#define DEV_ADDR_TABLE_STATIC_MASK GENMASK(6, 0)
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
+#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \
+ FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x))
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
-#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
-#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
+#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
+#define DEV_ADDR_TABLE_STATIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_STATIC_MASK, x)
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
#define I3C_BUS_SDR1_SCL_RATE 8000000
@@ -1489,6 +1496,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m,
+ unsigned long dev_nack_retry_cnt)
+{
+ struct dw_i3c_master *master = to_dw_i3c_master(m);
+ u32 reg;
+ int i;
+
+ if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) {
+ dev_err(&master->base.dev,
+ "Value %ld exceeds maximum %d\n",
+ dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX);
+ return -ERANGE;
+ }
+
+ /*
+ * Update DAT entries for all currently attached devices.
+ * We directly iterate through the master's device array.
+ */
+ for (i = 0; i < master->maxdevs; i++) {
+ /* Skip free/empty slots */
+ if (master->free_pos & BIT(i))
+ continue;
+
+ reg = readl(master->regs +
+ DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
+ reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK;
+ reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt);
+ writel(reg, master->regs +
+ DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
+ }
+
+ return 0;
+}
+
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.bus_init = dw_i3c_master_bus_init,
.bus_cleanup = dw_i3c_master_bus_cleanup,
@@ -1509,6 +1550,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
+ .set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry,
};
/* default platform ops implementations */
@@ -1676,11 +1718,16 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
if (master->free_pos & BIT(pos))
continue;
- if (master->devs[pos].is_i2c_addr)
- reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
+ reg_val = readl(master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
+
+ if (master->devs[pos].is_i2c_addr) {
+ reg_val &= ~DEV_ADDR_TABLE_STATIC_MASK;
+ reg_val |= DEV_ADDR_TABLE_LEGACY_I2C_DEV |
DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
- else
- reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
+ } else {
+ reg_val &= ~DEV_ADDR_TABLE_DYNAMIC_MASK;
+ reg_val |= DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
+ }
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
}
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index a62f22ff8b57..857504d36e18 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -533,8 +533,8 @@ static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 msta
static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
{
struct svc_i3c_i2c_dev_data *data;
+ struct i3c_dev_desc *dev = NULL;
unsigned int ibitype, ibiaddr;
- struct i3c_dev_desc *dev;
u32 status, val;
int ret;
@@ -627,7 +627,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
* for the slave to interrupt again.
*/
if (svc_i3c_master_error(master)) {
- if (master->ibi.tbq_slot) {
+ if (master->ibi.tbq_slot && dev) {
data = i3c_dev_get_master_data(dev);
i3c_generic_ibi_recycle_slot(data->ibi_pool,
master->ibi.tbq_slot);
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 76911278fb21..3d3f8d8673dd 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -64,7 +64,7 @@ config ADXL345
config ADXL345_I2C
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer I2C Driver"
- depends on INPUT_ADXL34X=n
+ depends on !INPUT_ADXL34X
depends on I2C
select ADXL345
select REGMAP_I2C
@@ -74,11 +74,12 @@ config ADXL345_I2C
To compile this driver as a module, choose M here: the module
will be called adxl345_i2c and you will also get adxl345_core
- for the core module.
+ for the core module. INPUT_ADXL34X share compatibles with this
+ driver, do not add both modules to the kernel.
config ADXL345_SPI
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer SPI Driver"
- depends on INPUT_ADXL34X=n
+ depends on !INPUT_ADXL34X
depends on SPI
select ADXL345
select REGMAP_SPI
@@ -88,7 +89,8 @@ config ADXL345_SPI
To compile this driver as a module, choose M here: the module
will be called adxl345_spi and you will also get adxl345_core
- for the core module.
+ for the core module. INPUT_ADXL34X share compatibles with this
+ driver, do not add both modules to the kernel.
config ADXL355
tristate
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 5fc7f814b907..1c1d64d5cbcb 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -768,9 +768,8 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq)
data->dready_trig->ops = &adxl355_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
- ret = devm_request_irq(data->dev, irq,
- &iio_trigger_generic_data_rdy_poll,
- IRQF_ONESHOT, "adxl355_irq", data->dready_trig);
+ ret = devm_request_irq(data->dev, irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, "adxl355_irq", data->dready_trig);
if (ret)
return dev_err_probe(data->dev, ret, "request irq %d failed\n",
irq);
diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
index 46d518a2a029..28a8793a53b6 100644
--- a/drivers/iio/accel/adxl372.c
+++ b/drivers/iio/accel/adxl372.c
@@ -295,7 +295,6 @@ struct adxl372_state {
u32 inact_time_ms;
u8 fifo_set_size;
unsigned long int1_bitmask;
- unsigned long int2_bitmask;
u16 watermark;
__be16 fifo_buf[ADXL372_FIFO_SIZE];
bool peak_fifo_mode_en;
@@ -1247,11 +1246,10 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
indio_dev->trig = iio_trigger_get(st->dready_trig);
- ret = devm_request_threaded_irq(dev, st->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- indio_dev->name, st->dready_trig);
+ ret = devm_request_irq(dev, st->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ indio_dev->name, st->dready_trig);
if (ret < 0)
return ret;
}
diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c
index aef5109c1ddd..8fab2fdbe147 100644
--- a/drivers/iio/accel/adxl380.c
+++ b/drivers/iio/accel/adxl380.c
@@ -232,25 +232,46 @@ bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_NS_GPL(adxl380_readable_noinc_reg, "IIO_ADXL380");
+static int adxl380_act_inact_enabled(struct adxl380_state *st, bool *enabled)
+{
+ unsigned int act_inact_ctl;
+ int ret;
+
+ if (!st->chip_info->has_low_power) {
+ *enabled = false;
+ return 0;
+ }
+
+ ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
+ if (ret)
+ return ret;
+
+ *enabled = FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
+ FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl);
+
+ return 0;
+}
+
static int adxl380_set_measure_en(struct adxl380_state *st, bool en)
{
int ret;
- unsigned int act_inact_ctl;
u8 op_mode = ADXL380_OP_MODE_STANDBY;
if (en) {
- ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
+ bool act_inact_enabled;
+
+ ret = adxl380_act_inact_enabled(st, &act_inact_enabled);
if (ret)
return ret;
/*
* Activity/Inactivity detection available only in VLP/ULP
- * mode and for devices that support low power modes. Otherwise
- * go straight to measure mode (same bits as ADXL380_OP_MODE_HP).
+ * mode and for devices that support low power modes.
*/
- if (st->chip_info->has_low_power &&
- (FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
- FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl)))
+ if (act_inact_enabled)
+ st->odr = ADXL380_ODR_VLP;
+
+ if (st->odr == ADXL380_ODR_VLP)
op_mode = ADXL380_OP_MODE_VLP;
else
op_mode = ADXL380_OP_MODE_HP;
@@ -417,17 +438,7 @@ static int adxl380_read_chn(struct adxl380_state *st, u8 addr)
static int adxl380_get_odr(struct adxl380_state *st, int *odr)
{
- int ret;
- unsigned int trig_cfg, odr_idx;
-
- ret = regmap_read(st->regmap, ADXL380_TRIG_CFG_REG, &trig_cfg);
- if (ret)
- return ret;
-
- odr_idx = (FIELD_GET(ADXL380_TRIG_CFG_SINC_RATE_MSK, trig_cfg) << 1) |
- (FIELD_GET(ADXL380_TRIG_CFG_DEC_2X_MSK, trig_cfg) & 1);
-
- *odr = st->chip_info->samp_freq_tbl[odr_idx];
+ *odr = st->chip_info->samp_freq_tbl[st->odr];
return 0;
}
@@ -488,18 +499,24 @@ static int adxl380_set_odr(struct adxl380_state *st, u8 odr)
if (ret)
return ret;
- ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
- ADXL380_TRIG_CFG_DEC_2X_MSK,
- FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, odr & 1));
- if (ret)
- return ret;
+ if (odr >= ADXL380_ODR_DSM) {
+ u8 mul = odr - ADXL380_ODR_DSM;
+ u8 field;
- ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
- ADXL380_TRIG_CFG_SINC_RATE_MSK,
- FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, odr >> 1));
- if (ret)
- return ret;
+ field = FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, mul & 1);
+ ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
+ ADXL380_TRIG_CFG_DEC_2X_MSK, field);
+ if (ret)
+ return ret;
+ field = FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, mul >> 1);
+ ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
+ ADXL380_TRIG_CFG_SINC_RATE_MSK, field);
+ if (ret)
+ return ret;
+ }
+
+ st->odr = odr;
ret = adxl380_set_measure_en(st, true);
if (ret)
return ret;
@@ -949,14 +966,13 @@ static irqreturn_t adxl380_irq_handler(int irq, void *p)
if (ret)
return IRQ_HANDLED;
- for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
- ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA,
- &st->fifo_buf[i],
- 2 * st->fifo_set_size);
- if (ret)
- return IRQ_HANDLED;
+ fifo_entries = rounddown(fifo_entries, st->fifo_set_size);
+ ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, &st->fifo_buf,
+ sizeof(*st->fifo_buf) * fifo_entries);
+ if (ret)
+ return IRQ_HANDLED;
+ for (i = 0; i < fifo_entries; i += st->fifo_set_size)
iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
- }
return IRQ_HANDLED;
}
@@ -1138,6 +1154,32 @@ static const struct iio_buffer_setup_ops adxl380_buffer_ops = {
.predisable = adxl380_buffer_predisable,
};
+static int adxl380_samp_freq_avail(struct adxl380_state *st, const int **vals,
+ int *length)
+{
+ bool act_inact_enabled;
+ int ret;
+
+ if (!st->chip_info->has_low_power) {
+ *vals = st->chip_info->samp_freq_tbl + ADXL380_ODR_DSM;
+ *length = ADXL380_ODR_MAX - ADXL380_ODR_DSM;
+ return 0;
+ }
+
+ ret = adxl380_act_inact_enabled(st, &act_inact_enabled);
+ if (ret)
+ return 0;
+
+ /*
+ * Motion detection is only functional in low-power mode, and this
+ * affects the available sampling frequencies.
+ */
+ *vals = st->chip_info->samp_freq_tbl;
+ *length = act_inact_enabled ? ADXL380_ODR_DSM : ADXL380_ODR_MAX;
+
+ return 0;
+}
+
static int adxl380_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
@@ -1218,6 +1260,7 @@ static int adxl380_read_avail(struct iio_dev *indio_dev,
long mask)
{
struct adxl380_state *st = iio_priv(indio_dev);
+ int ret;
if (chan->type != IIO_ACCEL)
return -EINVAL;
@@ -1229,9 +1272,11 @@ static int adxl380_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(st->chip_info->scale_tbl) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
- *vals = (const int *)st->chip_info->samp_freq_tbl;
+ ret = adxl380_samp_freq_avail(st, vals, length);
+ if (ret)
+ return ret;
+
*type = IIO_VAL_INT;
- *length = ARRAY_SIZE(st->chip_info->samp_freq_tbl);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = (const int *)st->lpf_tbl;
@@ -1254,12 +1299,16 @@ static int adxl380_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct adxl380_state *st = iio_priv(indio_dev);
- int odr_index, lpf_index, hpf_index, range_index;
+ const int *freq_vals;
+ int odr_index, lpf_index, hpf_index, range_index, freq_count, ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
- odr_index = adxl380_find_match_1d_tbl(st->chip_info->samp_freq_tbl,
- ARRAY_SIZE(st->chip_info->samp_freq_tbl),
+ ret = adxl380_samp_freq_avail(st, &freq_vals, &freq_count);
+ if (ret)
+ return ret;
+
+ odr_index = adxl380_find_match_1d_tbl(freq_vals, freq_count,
val);
return adxl380_set_odr(st, odr_index);
case IIO_CHAN_INFO_CALIBBIAS:
@@ -1621,7 +1670,7 @@ const struct adxl380_chip_info adxl318_chip_info = {
[ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
[ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
},
- .samp_freq_tbl = { 8000, 16000, 32000 },
+ .samp_freq_tbl = { 0, 8000, 16000, 32000 },
/*
* The datasheet defines an intercept of 550 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1639,7 +1688,7 @@ const struct adxl380_chip_info adxl319_chip_info = {
[ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
[ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
},
- .samp_freq_tbl = { 16000, 32000, 64000 },
+ .samp_freq_tbl = { 0, 16000, 32000, 64000 },
/*
* The datasheet defines an intercept of 550 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1657,7 +1706,7 @@ const struct adxl380_chip_info adxl380_chip_info = {
[ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
[ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
},
- .samp_freq_tbl = { 8000, 16000, 32000 },
+ .samp_freq_tbl = { 1000, 8000, 16000, 32000 },
/*
* The datasheet defines an intercept of 470 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1677,7 +1726,7 @@ const struct adxl380_chip_info adxl382_chip_info = {
[ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
[ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
},
- .samp_freq_tbl = { 16000, 32000, 64000 },
+ .samp_freq_tbl = { 1000, 16000, 32000, 64000 },
/*
* The datasheet defines an intercept of 570 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1916,6 +1965,7 @@ int adxl380_probe(struct device *dev, struct regmap *regmap,
st->dev = dev;
st->regmap = regmap;
st->chip_info = chip_info;
+ st->odr = ADXL380_ODR_DSM;
mutex_init(&st->lock);
diff --git a/drivers/iio/accel/adxl380.h b/drivers/iio/accel/adxl380.h
index e67c5aab8efc..d2c260c8b2fa 100644
--- a/drivers/iio/accel/adxl380.h
+++ b/drivers/iio/accel/adxl380.h
@@ -8,10 +8,18 @@
#ifndef _ADXL380_H_
#define _ADXL380_H_
+enum adxl380_odr {
+ ADXL380_ODR_VLP,
+ ADXL380_ODR_DSM,
+ ADXL380_ODR_DSM_2X,
+ ADXL380_ODR_DSM_4X,
+ ADXL380_ODR_MAX
+};
+
struct adxl380_chip_info {
const char *name;
const int scale_tbl[3][2];
- const int samp_freq_tbl[3];
+ const int samp_freq_tbl[ADXL380_ODR_MAX];
const struct iio_info *info;
const int temp_offset;
const u16 chip_id;
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index 8925f5279e62..7bc6761f5135 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -986,8 +986,9 @@ static int bma180_probe(struct i2c_client *client)
}
ret = devm_request_irq(dev, client->irq,
- iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
- "bma180_event", data->trig);
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "bma180_event", data->trig);
if (ret) {
dev_err(dev, "unable to request IRQ\n");
goto err_trigger_free;
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
index ac973d871c8b..a2c3cf13d098 100644
--- a/drivers/iio/accel/mxc4005.c
+++ b/drivers/iio/accel/mxc4005.c
@@ -486,13 +486,10 @@ static int mxc4005_probe(struct i2c_client *client)
if (!data->dready_trig)
return -ENOMEM;
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- "mxc4005_event",
- data->dready_trig);
+ ret = devm_request_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
+ "mxc4005_event", data->dready_trig);
if (ret) {
dev_err(&client->dev,
"failed to init threaded irq\n");
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index bfa8a3f5a92f..4a827be439a2 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -153,7 +153,6 @@
* struct sca3000_state - device instance state information
* @us: the associated spi device
* @info: chip variant information
- * @last_timestamp: the timestamp of the last event
* @mo_det_use_count: reference counter for the motion detection unit
* @lock: lock used to protect elements of sca3000_state
* and the underlying device state.
@@ -163,7 +162,6 @@
struct sca3000_state {
struct spi_device *us;
const struct sca3000_chip_info *info;
- s64 last_timestamp;
int mo_det_use_count;
struct mutex lock;
/* Can these share a cacheline ? */
@@ -1489,7 +1487,11 @@ static int sca3000_probe(struct spi_device *spi)
if (ret)
goto error_free_irq;
- return iio_device_register(indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_irq;
+
+ return 0;
error_free_irq:
if (spi->irq)
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
index 384f1fbcbcb3..a9ff2a273fe1 100644
--- a/drivers/iio/accel/stk8ba50.c
+++ b/drivers/iio/accel/stk8ba50.c
@@ -428,13 +428,10 @@ static int stk8ba50_probe(struct i2c_client *client)
}
if (client->irq > 0) {
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- stk8ba50_data_rdy_trig_poll,
- NULL,
- IRQF_TRIGGER_RISING |
- IRQF_ONESHOT,
- "stk8ba50_event",
- indio_dev);
+ ret = devm_request_irq(&client->dev, client->irq,
+ stk8ba50_data_rdy_trig_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "stk8ba50_event", indio_dev);
if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",
client->irq);
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 58da8255525e..60038ae8dfc4 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -70,6 +70,19 @@ config AD4030
To compile this driver as a module, choose M here: the module will be
called ad4030.
+config AD4062
+ tristate "Analog Devices AD4062 Driver"
+ depends on I3C
+ select REGMAP_I3C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD4062 I3C analog
+ to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4062.
+
config AD4080
tristate "Analog Devices AD4080 high speed ADC"
depends on SPI
@@ -99,6 +112,17 @@ config AD4130
To compile this driver as a module, choose M here: the module will be
called ad4130.
+config AD4134
+ tristate "Analog Device AD4134 ADC Driver"
+ depends on SPI
+ select REGMAP_SPI
+ select CRC8
+ help
+ Say yes here to build support for Analog Devices AD4134 SPI analog to
+ digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad4134_spi.
config AD4170_4
tristate "Analog Device AD4170-4 ADC Driver"
@@ -387,6 +411,7 @@ config AD7768_1
depends on SPI
select REGULATOR
select REGMAP_SPI
+ select RATIONAL
select IIO_BUFFER
select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
@@ -1222,6 +1247,18 @@ config NPCM_ADC
This driver can also be built as a module. If so, the module
will be called npcm_adc.
+config NXP_SAR_ADC
+ tristate "NXP S32G SAR-ADC driver"
+ depends on ARCH_S32 || COMPILE_TEST
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for S32G platforms
+ analog-to-digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called nxp_sar_adc.
+
config PAC1921
tristate "Microchip Technology PAC1921 driver"
depends on I2C
@@ -1664,6 +1701,18 @@ config TI_ADS1015
This driver can also be built as a module. If so, the module will be
called ti-ads1015.
+config TI_ADS1018
+ tristate "Texas Instruments ADS1018 ADC"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADS1018 and
+ ADS1118 ADC chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads1018.
+
config TI_ADS1100
tristate "Texas Instruments ADS1100 and ADS1000 ADC"
depends on I2C
@@ -1722,6 +1771,17 @@ config TI_ADS131E08
This driver can also be built as a module. If so, the module will be
called ti-ads131e08.
+config TI_ADS131M02
+ tristate "Texas Instruments ADS131M02"
+ depends on SPI && REGULATOR
+ select CRC_ITU_T
+ help
+ Say yes here to get support for Texas Instruments ADS131M02, ADS131M03,
+ ADS131M04, ADS131M06 and ADS131M08 chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads131m02.
+
config TI_ADS7138
tristate "Texas Instruments ADS7128 and ADS7138 ADC driver"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7cc8f9a12f76..c76550415ff1 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -11,8 +11,10 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4030) += ad4030.o
+obj-$(CONFIG_AD4062) += ad4062.o
obj-$(CONFIG_AD4080) += ad4080.o
obj-$(CONFIG_AD4130) += ad4130.o
+obj-$(CONFIG_AD4134) += ad4134.o
obj-$(CONFIG_AD4170_4) += ad4170-4.o
obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD4851) += ad4851.o
@@ -108,6 +110,7 @@ obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NCT7201) += nct7201.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
+obj-$(CONFIG_NXP_SAR_ADC) += nxp-sar-adc.o
obj-$(CONFIG_PAC1921) += pac1921.o
obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
@@ -145,11 +148,13 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
+obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
+obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
new file mode 100644
index 000000000000..dd4ad32aa6f5
--- /dev/null
+++ b/drivers/iio/adc/ad4062.c
@@ -0,0 +1,1609 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD4062 I3C ADC driver
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/devm-helpers.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/units.h>
+#include <linux/unaligned.h>
+#include <linux/util_macros.h>
+
+#define AD4062_REG_INTERFACE_CONFIG_A 0x00
+#define AD4062_REG_DEVICE_CONFIG 0x02
+#define AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK GENMASK(1, 0)
+#define AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE 3
+#define AD4062_REG_PROD_ID_1 0x05
+#define AD4062_REG_DEVICE_GRADE 0x06
+#define AD4062_REG_SCRATCH_PAD 0x0A
+#define AD4062_REG_VENDOR_H 0x0D
+#define AD4062_REG_STREAM_MODE 0x0E
+#define AD4062_REG_INTERFACE_STATUS 0x11
+#define AD4062_REG_MODE_SET 0x20
+#define AD4062_REG_MODE_SET_ENTER_ADC BIT(0)
+#define AD4062_REG_ADC_MODES 0x21
+#define AD4062_REG_ADC_MODES_MODE_MSK GENMASK(1, 0)
+#define AD4062_REG_ADC_CONFIG 0x22
+#define AD4062_REG_ADC_CONFIG_REF_EN_MSK BIT(5)
+#define AD4062_REG_ADC_CONFIG_SCALE_EN_MSK BIT(4)
+#define AD4062_REG_AVG_CONFIG 0x23
+#define AD4062_REG_GP_CONF 0x24
+#define AD4062_REG_GP_CONF_MODE_MSK_0 GENMASK(2, 0)
+#define AD4062_REG_GP_CONF_MODE_MSK_1 GENMASK(6, 4)
+#define AD4062_REG_INTR_CONF 0x25
+#define AD4062_REG_INTR_CONF_EN_MSK_0 GENMASK(1, 0)
+#define AD4062_REG_INTR_CONF_EN_MSK_1 GENMASK(5, 4)
+#define AD4062_REG_TIMER_CONFIG 0x27
+#define AD4062_REG_TIMER_CONFIG_FS_MASK GENMASK(7, 4)
+#define AD4062_REG_MAX_LIMIT 0x29
+#define AD4062_REG_MIN_LIMIT 0x2B
+#define AD4062_REG_MAX_HYST 0x2C
+#define AD4062_REG_MIN_HYST 0x2D
+#define AD4062_REG_MON_VAL 0x2F
+#define AD4062_REG_ADC_IBI_EN 0x31
+#define AD4062_REG_ADC_IBI_EN_CONV_TRIGGER BIT(2)
+#define AD4062_REG_ADC_IBI_EN_MAX BIT(1)
+#define AD4062_REG_ADC_IBI_EN_MIN BIT(0)
+#define AD4062_REG_FUSE_CRC 0x40
+#define AD4062_REG_DEVICE_STATUS 0x41
+#define AD4062_REG_DEVICE_STATUS_DEVICE_RESET BIT(6)
+#define AD4062_REG_IBI_STATUS 0x48
+#define AD4062_REG_CONV_READ_LSB 0x50
+#define AD4062_REG_CONV_READ_16BITS 0x51
+#define AD4062_REG_CONV_READ_32BITS 0x53
+#define AD4062_REG_CONV_TRIGGER_16BITS 0x57
+#define AD4062_REG_CONV_TRIGGER_32BITS 0x59
+#define AD4062_REG_CONV_AUTO 0x61
+#define AD4062_MAX_REG AD4062_REG_CONV_AUTO
+
+#define AD4062_MON_VAL_MIDDLE_POINT 0x8000
+
+#define AD4062_I3C_VENDOR 0x0177
+#define AD4062_SOFT_RESET 0x81
+#define AD4060_PROD_ID 0x7A
+#define AD4062_PROD_ID 0x7C
+
+#define AD4062_GP_DISABLED 0x0
+#define AD4062_GP_INTR 0x1
+#define AD4062_GP_DRDY 0x2
+#define AD4062_GP_STATIC_LOW 0x5
+#define AD4062_GP_STATIC_HIGH 0x6
+
+#define AD4062_LIMIT_BITS 12
+
+#define AD4062_INTR_EN_NEITHER 0x0
+#define AD4062_INTR_EN_EITHER 0x3
+
+#define AD4062_TCONV_NS 270
+
+enum ad4062_operation_mode {
+ AD4062_SAMPLE_MODE = 0x0,
+ AD4062_BURST_AVERAGING_MODE = 0x1,
+ AD4062_MONITOR_MODE = 0x3,
+};
+
+struct ad4062_chip_info {
+ const struct iio_chan_spec channels[1];
+ const char *name;
+ u16 prod_id;
+ u16 avg_max;
+};
+
+enum {
+ AD4062_SCAN_TYPE_SAMPLE,
+ AD4062_SCAN_TYPE_BURST_AVG,
+};
+
+static const struct iio_scan_type ad4062_scan_type_12_s[] = {
+ [AD4062_SCAN_TYPE_SAMPLE] = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ [AD4062_SCAN_TYPE_BURST_AVG] = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+};
+
+static const struct iio_scan_type ad4062_scan_type_16_s[] = {
+ [AD4062_SCAN_TYPE_SAMPLE] = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ [AD4062_SCAN_TYPE_BURST_AVG] = {
+ .sign = 's',
+ .realbits = 20,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+};
+
+static const unsigned int ad4062_conversion_freqs[] = {
+ 2000000, 1000000, 300000, 100000, /* 0 - 3 */
+ 33300, 10000, 3000, 500, /* 4 - 7 */
+ 333, 250, 200, 166, /* 8 - 11 */
+ 140, 124, 111, /* 12 - 15 */
+};
+
+struct ad4062_state {
+ const struct ad4062_chip_info *chip;
+ const struct ad4062_bus_ops *ops;
+ enum ad4062_operation_mode mode;
+ struct work_struct trig_conv;
+ struct completion completion;
+ struct iio_trigger *trigger;
+ struct iio_dev *indio_dev;
+ struct i3c_device *i3cdev;
+ struct regmap *regmap;
+ bool wait_event;
+ int vref_uV;
+ unsigned int samp_freqs[ARRAY_SIZE(ad4062_conversion_freqs)];
+ bool gpo_irq[2];
+ u16 sampling_frequency;
+ u16 events_frequency;
+ u8 oversamp_ratio;
+ u8 conv_sizeof;
+ u8 conv_addr;
+ union {
+ __be32 be32;
+ __be16 be16;
+ } buf __aligned(IIO_DMA_MINALIGN);
+};
+
+static const struct regmap_range ad4062_regmap_rd_ranges[] = {
+ regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_GRADE),
+ regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_INTERFACE_STATUS),
+ regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN),
+ regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_IBI_STATUS),
+ regmap_reg_range(AD4062_REG_CONV_READ_LSB, AD4062_REG_CONV_AUTO),
+};
+
+static const struct regmap_access_table ad4062_regmap_rd_table = {
+ .yes_ranges = ad4062_regmap_rd_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4062_regmap_rd_ranges),
+};
+
+static const struct regmap_range ad4062_regmap_wr_ranges[] = {
+ regmap_reg_range(AD4062_REG_INTERFACE_CONFIG_A, AD4062_REG_DEVICE_CONFIG),
+ regmap_reg_range(AD4062_REG_SCRATCH_PAD, AD4062_REG_SCRATCH_PAD),
+ regmap_reg_range(AD4062_REG_STREAM_MODE, AD4062_REG_INTERFACE_STATUS),
+ regmap_reg_range(AD4062_REG_MODE_SET, AD4062_REG_ADC_IBI_EN),
+ regmap_reg_range(AD4062_REG_FUSE_CRC, AD4062_REG_DEVICE_STATUS),
+};
+
+static const struct regmap_access_table ad4062_regmap_wr_table = {
+ .yes_ranges = ad4062_regmap_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(ad4062_regmap_wr_ranges),
+};
+
+static const struct iio_event_spec ad4062_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_HYSTERESIS),
+ },
+};
+
+#define AD4062_CHAN(bits) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = 1, \
+ .channel = 0, \
+ .event_spec = ad4062_events, \
+ .num_event_specs = ARRAY_SIZE(ad4062_events), \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad4062_scan_type_##bits##_s, \
+ .num_ext_scan_type = ARRAY_SIZE(ad4062_scan_type_##bits##_s), \
+}
+
+static const struct ad4062_chip_info ad4060_chip_info = {
+ .name = "ad4060",
+ .channels = { AD4062_CHAN(12) },
+ .prod_id = AD4060_PROD_ID,
+ .avg_max = 256,
+};
+
+static const struct ad4062_chip_info ad4062_chip_info = {
+ .name = "ad4062",
+ .channels = { AD4062_CHAN(16) },
+ .prod_id = AD4062_PROD_ID,
+ .avg_max = 4096,
+};
+
+static ssize_t sampling_frequency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad4062_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%d\n", ad4062_conversion_freqs[st->events_frequency]);
+}
+
+static int sampling_frequency_store_dispatch(struct iio_dev *indio_dev,
+ const char *buf)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int val, ret;
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ st->events_frequency = find_closest_descending(val, ad4062_conversion_freqs,
+ ARRAY_SIZE(ad4062_conversion_freqs));
+ return 0;
+}
+
+static ssize_t sampling_frequency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = sampling_frequency_store_dispatch(indio_dev, buf);
+ iio_device_release_direct(indio_dev);
+ return ret ?: len;
+}
+
+static IIO_DEVICE_ATTR_RW(sampling_frequency, 0);
+
+static ssize_t sampling_frequency_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret = 0;
+
+ for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++)
+ ret += sysfs_emit_at(buf, ret, "%d%s", ad4062_conversion_freqs[i],
+ i != (ARRAY_SIZE(ad4062_conversion_freqs) - 1) ? " " : "\n");
+ return ret;
+}
+
+static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
+
+static struct attribute *ad4062_event_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad4062_event_attribute_group = {
+ .attrs = ad4062_event_attributes,
+};
+
+static int ad4062_set_oversampling_ratio(struct ad4062_state *st, int val, int val2)
+{
+ const u32 _max = st->chip->avg_max;
+ const u32 _min = 1;
+ int ret;
+
+ if (!in_range(val, _min, _max) || val2 != 0)
+ return -EINVAL;
+
+ /* 1 disables oversampling */
+ val = ilog2(val);
+ if (val == 0) {
+ st->mode = AD4062_SAMPLE_MODE;
+ } else {
+ st->mode = AD4062_BURST_AVERAGING_MODE;
+ ret = regmap_write(st->regmap, AD4062_REG_AVG_CONFIG, val - 1);
+ if (ret)
+ return ret;
+ }
+ st->oversamp_ratio = val;
+
+ return 0;
+}
+
+static int ad4062_get_oversampling_ratio(struct ad4062_state *st, int *val)
+{
+ int ret, buf;
+
+ if (st->mode == AD4062_SAMPLE_MODE) {
+ *val = 1;
+ return 0;
+ }
+
+ ret = regmap_read(st->regmap, AD4062_REG_AVG_CONFIG, &buf);
+ if (ret)
+ return ret;
+
+ *val = BIT(buf + 1);
+ return 0;
+}
+
+static int ad4062_calc_sampling_frequency(unsigned int fosc, unsigned int oversamp_ratio)
+{
+ /* From datasheet p.31: (n_avg - 1)/fosc + tconv */
+ u32 n_avg = BIT(oversamp_ratio) - 1;
+ u32 period_ns = NSEC_PER_SEC / fosc;
+
+ /* Result is less than 1 Hz */
+ if (n_avg >= fosc)
+ return 1;
+
+ return NSEC_PER_SEC / (n_avg * period_ns + AD4062_TCONV_NS);
+}
+
+static int ad4062_populate_sampling_frequency(struct ad4062_state *st)
+{
+ for (u8 i = 0; i < ARRAY_SIZE(ad4062_conversion_freqs); i++)
+ st->samp_freqs[i] =
+ ad4062_calc_sampling_frequency(ad4062_conversion_freqs[i],
+ st->oversamp_ratio);
+ return 0;
+}
+
+static int ad4062_get_sampling_frequency(struct ad4062_state *st, int *val)
+{
+ int freq = ad4062_conversion_freqs[st->sampling_frequency];
+
+ *val = ad4062_calc_sampling_frequency(freq, st->oversamp_ratio);
+ return IIO_VAL_INT;
+}
+
+static int ad4062_set_sampling_frequency(struct ad4062_state *st, int val, int val2)
+{
+ int ret;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ ret = ad4062_populate_sampling_frequency(st);
+ if (ret)
+ return ret;
+
+ st->sampling_frequency =
+ find_closest_descending(val, st->samp_freqs,
+ ARRAY_SIZE(ad4062_conversion_freqs));
+ return 0;
+}
+
+static int ad4062_check_ids(struct ad4062_state *st)
+{
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+ u16 val;
+
+ ret = regmap_bulk_read(st->regmap, AD4062_REG_PROD_ID_1,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ val = be16_to_cpu(st->buf.be16);
+ if (val != st->chip->prod_id)
+ dev_warn(dev, "Production ID x%x does not match known values", val);
+
+ ret = regmap_bulk_read(st->regmap, AD4062_REG_VENDOR_H,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ val = be16_to_cpu(st->buf.be16);
+ if (val != AD4062_I3C_VENDOR) {
+ dev_err(dev, "Vendor ID x%x does not match expected value\n", val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ad4062_conversion_frequency_set(struct ad4062_state *st, u8 val)
+{
+ return regmap_write(st->regmap, AD4062_REG_TIMER_CONFIG,
+ FIELD_PREP(AD4062_REG_TIMER_CONFIG_FS_MASK, val));
+}
+
+static int ad4062_set_operation_mode(struct ad4062_state *st,
+ enum ad4062_operation_mode mode)
+{
+ const unsigned int samp_freq = mode == AD4062_MONITOR_MODE ?
+ st->events_frequency : st->sampling_frequency;
+ int ret;
+
+ ret = ad4062_conversion_frequency_set(st, samp_freq);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_MODES,
+ AD4062_REG_ADC_MODES_MODE_MSK, mode);
+ if (ret)
+ return ret;
+
+ if (mode == AD4062_MONITOR_MODE) {
+ /* Change address pointer to enter monitor mode */
+ struct i3c_xfer xfer_trigger = {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ };
+ st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS;
+ return i3c_device_do_xfers(st->i3cdev, &xfer_trigger, 1, I3C_SDR);
+ }
+
+ return regmap_write(st->regmap, AD4062_REG_MODE_SET,
+ AD4062_REG_MODE_SET_ENTER_ADC);
+}
+
+static int ad4062_soft_reset(struct ad4062_state *st)
+{
+ u8 val = AD4062_SOFT_RESET;
+ int ret;
+
+ ret = regmap_write(st->regmap, AD4062_REG_INTERFACE_CONFIG_A, val);
+ if (ret)
+ return ret;
+
+ /* Wait AD4062 treset time, datasheet p8 */
+ ndelay(60);
+
+ return 0;
+}
+
+static int ad4062_setup(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ const bool *ref_sel)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
+ int ret;
+
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_0,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0,
+ AD4062_GP_INTR));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_1,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1,
+ AD4062_GP_DRDY));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG,
+ AD4062_REG_ADC_CONFIG_REF_EN_MSK,
+ FIELD_PREP(AD4062_REG_ADC_CONFIG_REF_EN_MSK,
+ *ref_sel));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD4062_REG_DEVICE_STATUS,
+ AD4062_REG_DEVICE_STATUS_DEVICE_RESET);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF,
+ AD4062_REG_INTR_CONF_EN_MSK_0,
+ FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_0,
+ AD4062_INTR_EN_EITHER));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_INTR_CONF,
+ AD4062_REG_INTR_CONF_EN_MSK_1,
+ FIELD_PREP(AD4062_REG_INTR_CONF_EN_MSK_1,
+ AD4062_INTR_EN_NEITHER));
+ if (ret)
+ return ret;
+
+ st->buf.be16 = cpu_to_be16(AD4062_MON_VAL_MIDDLE_POINT);
+ return regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL,
+ &st->buf.be16, sizeof(st->buf.be16));
+}
+
+static irqreturn_t ad4062_irq_handler_thresh(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ad4062_irq_handler_drdy(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev))
+ iio_trigger_poll(st->trigger);
+ else
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static void ad4062_ibi_handler(struct i3c_device *i3cdev,
+ const struct i3c_ibi_payload *payload)
+{
+ struct ad4062_state *st = i3cdev_get_drvdata(i3cdev);
+
+ if (st->wait_event) {
+ iio_push_event(st->indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(st->indio_dev));
+ return;
+ }
+ if (iio_buffer_enabled(st->indio_dev))
+ iio_trigger_poll_nested(st->trigger);
+ else
+ complete(&st->completion);
+}
+
+static void ad4062_trigger_work(struct work_struct *work)
+{
+ struct ad4062_state *st =
+ container_of(work, struct ad4062_state, trig_conv);
+ int ret;
+
+ /*
+ * Read current conversion, if at reg CONV_READ, stop bit triggers
+ * next sample and does not need writing the address.
+ */
+ struct i3c_xfer xfer_sample = {
+ .data.in = &st->buf.be32,
+ .len = st->conv_sizeof,
+ .rnw = true,
+ };
+ struct i3c_xfer xfer_trigger = {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ };
+
+ ret = i3c_device_do_xfers(st->i3cdev, &xfer_sample, 1, I3C_SDR);
+ if (ret)
+ return;
+
+ iio_push_to_buffers_with_ts(st->indio_dev, &st->buf.be32, st->conv_sizeof,
+ iio_get_time_ns(st->indio_dev));
+ if (st->gpo_irq[1])
+ return;
+
+ i3c_device_do_xfers(st->i3cdev, &xfer_trigger, 1, I3C_SDR);
+}
+
+static irqreturn_t ad4062_poll_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ iio_trigger_notify_done(indio_dev->trig);
+ schedule_work(&st->trig_conv);
+
+ return IRQ_HANDLED;
+}
+
+static void ad4062_disable_ibi(void *data)
+{
+ struct i3c_device *i3cdev = data;
+
+ i3c_device_disable_ibi(i3cdev);
+}
+
+static void ad4062_free_ibi(void *data)
+{
+ struct i3c_device *i3cdev = data;
+
+ i3c_device_free_ibi(i3cdev);
+}
+
+static int ad4062_request_ibi(struct i3c_device *i3cdev)
+{
+ const struct i3c_ibi_setup ibireq = {
+ .max_payload_len = 1,
+ .num_slots = 1,
+ .handler = ad4062_ibi_handler,
+ };
+ int ret;
+
+ ret = i3c_device_request_ibi(i3cdev, &ibireq);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i3cdev->dev, ad4062_free_ibi, i3cdev);
+ if (ret)
+ return ret;
+
+ ret = i3c_device_enable_ibi(i3cdev);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(&i3cdev->dev, ad4062_disable_ibi, i3cdev);
+}
+
+static int ad4062_request_irq(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+
+ ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp0");
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ if (ret < 0) {
+ st->gpo_irq[0] = false;
+ ret = regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN,
+ AD4062_REG_ADC_IBI_EN_MAX | AD4062_REG_ADC_IBI_EN_MIN,
+ AD4062_REG_ADC_IBI_EN_MAX | AD4062_REG_ADC_IBI_EN_MIN);
+ if (ret)
+ return ret;
+ } else {
+ st->gpo_irq[0] = true;
+ ret = devm_request_threaded_irq(dev, ret, NULL,
+ ad4062_irq_handler_thresh,
+ IRQF_ONESHOT, indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = fwnode_irq_get_byname(dev_fwnode(&st->i3cdev->dev), "gp1");
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ if (ret < 0) {
+ st->gpo_irq[1] = false;
+ return regmap_update_bits(st->regmap, AD4062_REG_ADC_IBI_EN,
+ AD4062_REG_ADC_IBI_EN_CONV_TRIGGER,
+ AD4062_REG_ADC_IBI_EN_CONV_TRIGGER);
+ }
+ st->gpo_irq[1] = true;
+
+ return devm_request_threaded_irq(dev, ret,
+ ad4062_irq_handler_drdy,
+ NULL, IRQF_ONESHOT, indio_dev->name,
+ indio_dev);
+}
+
+static const struct iio_trigger_ops ad4062_trigger_ops = {
+ .validate_device = &iio_trigger_validate_own_device,
+};
+
+static int ad4062_request_trigger(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+
+ st->trigger = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!st->trigger)
+ return -ENOMEM;
+
+ st->trigger->ops = &ad4062_trigger_ops;
+ iio_trigger_set_drvdata(st->trigger, indio_dev);
+
+ ret = devm_iio_trigger_register(dev, st->trigger);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trigger);
+
+ return 0;
+}
+
+static const int ad4062_oversampling_avail[] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, /* 0 - 7 */
+ 256, 512, 1024, 2048, 4096, /* 8 - 12 */
+};
+
+static int ad4062_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *len, long mask)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = ad4062_oversampling_avail;
+ *len = ARRAY_SIZE(ad4062_oversampling_avail);
+ *len -= st->chip->avg_max == 256 ? 4 : 0;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = ad4062_populate_sampling_frequency(st);
+ if (ret)
+ return ret;
+ *vals = st->samp_freqs;
+ *len = st->oversamp_ratio ? ARRAY_SIZE(ad4062_conversion_freqs) : 1;
+ *type = IIO_VAL_INT;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_get_chan_scale(struct iio_dev *indio_dev, int *val, int *val2)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
+
+ /*
+ * In burst averaging mode the averaging filter accumulates resulting
+ * in a sample with increased precision.
+ */
+ scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
+
+ *val = (st->vref_uV * 2) / (MICRO / MILLI); /* signed */
+ *val2 = scan_type->realbits - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad4062_get_chan_calibscale(struct ad4062_state *st, int *val, int *val2)
+{
+ int ret;
+
+ ret = regmap_bulk_read(st->regmap, AD4062_REG_MON_VAL,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ /* From datasheet: code out = code in × mon_val/0x8000 */
+ *val = be16_to_cpu(st->buf.be16) * 2;
+ *val2 = 16;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad4062_set_chan_calibscale(struct ad4062_state *st, int gain_int,
+ int gain_frac)
+{
+ /* Divide numerator and denumerator by known great common divider */
+ const u32 mon_val = AD4062_MON_VAL_MIDDLE_POINT / 64;
+ const u32 micro = MICRO / 64;
+ const u32 gain_fp = gain_int * MICRO + gain_frac;
+ const u32 reg_val = DIV_ROUND_CLOSEST(gain_fp * mon_val, micro);
+ int ret;
+
+ /* Checks if the gain is in range and the value fits the field */
+ if (gain_int < 0 || gain_int > 1 || reg_val > BIT(16) - 1)
+ return -EINVAL;
+
+ st->buf.be16 = cpu_to_be16(reg_val);
+ ret = regmap_bulk_write(st->regmap, AD4062_REG_MON_VAL,
+ &st->buf.be16, sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ /* Enable scale if gain is not equal to one */
+ return regmap_update_bits(st->regmap, AD4062_REG_ADC_CONFIG,
+ AD4062_REG_ADC_CONFIG_SCALE_EN_MSK,
+ FIELD_PREP(AD4062_REG_ADC_CONFIG_SCALE_EN_MSK,
+ !(gain_int == 1 && gain_frac == 0)));
+}
+
+static int ad4062_read_chan_raw(struct ad4062_state *st, int *val)
+{
+ struct i3c_device *i3cdev = st->i3cdev;
+ struct i3c_xfer xfer_trigger = {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ };
+ struct i3c_xfer xfer_sample = {
+ .data.in = &st->buf.be32,
+ .len = sizeof(st->buf.be32),
+ .rnw = true,
+ };
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = ad4062_set_operation_mode(st, st->mode);
+ if (ret)
+ return ret;
+
+ reinit_completion(&st->completion);
+ /* Change address pointer to trigger conversion */
+ st->conv_addr = AD4062_REG_CONV_TRIGGER_32BITS;
+ ret = i3c_device_do_xfers(i3cdev, &xfer_trigger, 1, I3C_SDR);
+ if (ret)
+ return ret;
+ /*
+ * Single sample read should be used only for oversampling and
+ * sampling frequency pairs that take less than 1 sec.
+ */
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ ret = i3c_device_do_xfers(i3cdev, &xfer_sample, 1, I3C_SDR);
+ if (ret)
+ return ret;
+ *val = be32_to_cpu(st->buf.be32);
+ return 0;
+}
+
+static int ad4062_read_raw_dispatch(struct ad4062_state *st,
+ int *val, int *val2, long info)
+{
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return ad4062_read_chan_raw(st, val);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4062_get_chan_calibscale(st, val, val2);
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4062_get_oversampling_ratio(st, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ return ad4062_get_chan_scale(indio_dev, val, val2);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad4062_get_sampling_frequency(st, val);
+ }
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_read_raw_dispatch(st, val, val2, info);
+ iio_device_release_direct(indio_dev);
+ return ret ?: IIO_VAL_INT;
+}
+
+static int ad4062_write_raw_dispatch(struct ad4062_state *st, int val, int val2,
+ long info)
+{
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return ad4062_set_oversampling_ratio(st, val, val2);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return ad4062_set_chan_calibscale(st, val, val2);
+
+ default:
+ return -EINVAL;
+ }
+};
+
+static int ad4062_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long info)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad4062_set_sampling_frequency(st, val, val2);
+ }
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_write_raw_dispatch(st, val, val2, info);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int pm_ad4062_monitor_mode_enable(struct ad4062_state *st)
+{
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ return ad4062_set_operation_mode(st, AD4062_MONITOR_MODE);
+}
+
+static int ad4062_monitor_mode_enable(struct ad4062_state *st)
+{
+ int ret;
+
+ ret = pm_ad4062_monitor_mode_enable(st);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(&st->i3cdev->dev);
+ return 0;
+}
+
+static int ad4062_monitor_mode_disable(struct ad4062_state *st)
+{
+ pm_runtime_put_autosuspend(&st->i3cdev->dev);
+ return 0;
+}
+
+static int ad4062_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 ad4062_state *st = iio_priv(indio_dev);
+
+ return st->wait_event;
+}
+
+static int ad4062_write_event_config_dispatch(struct iio_dev *indio_dev,
+ bool state)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (st->wait_event == state)
+ ret = 0;
+ else if (state)
+ ret = ad4062_monitor_mode_enable(st);
+ else
+ ret = ad4062_monitor_mode_disable(st);
+ if (ret)
+ return ret;
+
+ st->wait_event = state;
+ return 0;
+}
+
+static int ad4062_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_write_event_config_dispatch(indio_dev, state);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int __ad4062_read_event_info_value(struct ad4062_state *st,
+ enum iio_event_direction dir, int *val)
+{
+ int ret;
+ u8 reg;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_LIMIT;
+ else
+ reg = AD4062_REG_MIN_LIMIT;
+
+ ret = regmap_bulk_read(st->regmap, reg, &st->buf.be16,
+ sizeof(st->buf.be16));
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(be16_to_cpu(st->buf.be16), AD4062_LIMIT_BITS - 1);
+
+ return 0;
+}
+
+static int __ad4062_read_event_info_hysteresis(struct ad4062_state *st,
+ enum iio_event_direction dir, int *val)
+{
+ u8 reg;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_HYST;
+ else
+ reg = AD4062_REG_MIN_HYST;
+ return regmap_read(st->regmap, reg, val);
+}
+
+static int ad4062_read_event_config_dispatch(struct iio_dev *indio_dev,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return __ad4062_read_event_info_value(st, dir, val);
+ case IIO_EV_INFO_HYSTERESIS:
+ return __ad4062_read_event_info_hysteresis(st, dir, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val,
+ int *val2)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_read_event_config_dispatch(indio_dev, dir, info, val);
+ iio_device_release_direct(indio_dev);
+ return ret ?: IIO_VAL_INT;
+}
+
+static int __ad4062_write_event_info_value(struct ad4062_state *st,
+ enum iio_event_direction dir, int val)
+{
+ u8 reg;
+
+ if (val != sign_extend32(val, AD4062_LIMIT_BITS - 1))
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_LIMIT;
+ else
+ reg = AD4062_REG_MIN_LIMIT;
+ st->buf.be16 = cpu_to_be16(val);
+
+ return regmap_bulk_write(st->regmap, reg, &st->buf.be16,
+ sizeof(st->buf.be16));
+}
+
+static int __ad4062_write_event_info_hysteresis(struct ad4062_state *st,
+ enum iio_event_direction dir, int val)
+{
+ u8 reg;
+
+ if (val > BIT(7) - 1)
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING)
+ reg = AD4062_REG_MAX_HYST;
+ else
+ reg = AD4062_REG_MIN_HYST;
+
+ return regmap_write(st->regmap, reg, val);
+}
+
+static int ad4062_write_event_value_dispatch(struct iio_dev *indio_dev,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return __ad4062_write_event_info_value(st, dir, val);
+ case IIO_EV_INFO_HYSTERESIS:
+ return __ad4062_write_event_info_hysteresis(st, dir, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4062_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val,
+ int val2)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad4062_write_event_value_dispatch(indio_dev, type, dir, info, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+/*
+ * The AD4062 in burst averaging mode increases realbits from 16-bits to
+ * 20-bits, increasing the storagebits from 16-bits to 32-bits.
+ */
+static inline size_t ad4062_sizeof_storagebits(struct ad4062_state *st)
+{
+ const struct iio_scan_type *scan_type =
+ iio_get_current_scan_type(st->indio_dev, st->chip->channels);
+
+ return BITS_TO_BYTES(scan_type->storagebits);
+}
+
+/* Read registers only with realbits (no sign extension bytes) */
+static inline size_t ad4062_get_conv_addr(struct ad4062_state *st, size_t _sizeof)
+{
+ if (st->gpo_irq[1])
+ return _sizeof == sizeof(u32) ? AD4062_REG_CONV_READ_32BITS :
+ AD4062_REG_CONV_READ_16BITS;
+ return _sizeof == sizeof(u32) ? AD4062_REG_CONV_TRIGGER_32BITS :
+ AD4062_REG_CONV_TRIGGER_16BITS;
+}
+
+static int pm_ad4062_triggered_buffer_postenable(struct ad4062_state *st)
+{
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(&st->i3cdev->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ if (st->wait_event)
+ return -EBUSY;
+
+ ret = ad4062_set_operation_mode(st, st->mode);
+ if (ret)
+ return ret;
+
+ st->conv_sizeof = ad4062_sizeof_storagebits(st);
+ st->conv_addr = ad4062_get_conv_addr(st, st->conv_sizeof);
+ /* CONV_READ requires read to trigger first sample. */
+ struct i3c_xfer xfer_sample[2] = {
+ {
+ .data.out = &st->conv_addr,
+ .len = sizeof(st->conv_addr),
+ .rnw = false,
+ },
+ {
+ .data.in = &st->buf.be32,
+ .len = sizeof(st->buf.be32),
+ .rnw = true,
+ }
+ };
+
+ return i3c_device_do_xfers(st->i3cdev, xfer_sample,
+ st->gpo_irq[1] ? 2 : 1, I3C_SDR);
+}
+
+static int ad4062_triggered_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_ad4062_triggered_buffer_postenable(st);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(&st->i3cdev->dev);
+ return 0;
+}
+
+static int ad4062_triggered_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ pm_runtime_put_autosuspend(&st->i3cdev->dev);
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad4062_triggered_buffer_setup_ops = {
+ .postenable = &ad4062_triggered_buffer_postenable,
+ .predisable = &ad4062_triggered_buffer_predisable,
+};
+
+static int ad4062_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int ad4062_get_current_scan_type(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad4062_state *st = iio_priv(indio_dev);
+
+ return st->mode == AD4062_BURST_AVERAGING_MODE ?
+ AD4062_SCAN_TYPE_BURST_AVG :
+ AD4062_SCAN_TYPE_SAMPLE;
+}
+
+static const struct iio_info ad4062_info = {
+ .read_raw = ad4062_read_raw,
+ .write_raw = ad4062_write_raw,
+ .read_avail = ad4062_read_avail,
+ .read_event_config = ad4062_read_event_config,
+ .write_event_config = ad4062_write_event_config,
+ .read_event_value = ad4062_read_event_value,
+ .write_event_value = ad4062_write_event_value,
+ .event_attrs = &ad4062_event_attribute_group,
+ .get_current_scan_type = ad4062_get_current_scan_type,
+ .debugfs_reg_access = ad4062_debugfs_reg_access,
+};
+
+static const struct regmap_config ad4062_regmap_config = {
+ .name = "ad4062",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AD4062_MAX_REG,
+ .rd_table = &ad4062_regmap_rd_table,
+ .wr_table = &ad4062_regmap_wr_table,
+ .can_sleep = true,
+};
+
+static int ad4062_regulators_get(struct ad4062_state *st, bool *ref_sel)
+{
+ struct device *dev = &st->i3cdev->dev;
+ int ret;
+
+ ret = devm_regulator_get_enable(dev, "vio");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable vio voltage\n");
+
+ st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref");
+ *ref_sel = st->vref_uV == -ENODEV;
+ if (st->vref_uV < 0 && !*ref_sel)
+ return dev_err_probe(dev, st->vref_uV,
+ "Failed to enable and read ref voltage\n");
+
+ if (*ref_sel) {
+ st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "vdd");
+ if (st->vref_uV < 0)
+ return dev_err_probe(dev, st->vref_uV,
+ "Failed to enable and read vdd voltage\n");
+ } else {
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable vdd regulator\n");
+ }
+
+ return 0;
+}
+
+static int ad4062_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int ad4062_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct ad4062_state *st = gpiochip_get_data(gc);
+ unsigned int reg_val = value ? AD4062_GP_STATIC_HIGH : AD4062_GP_STATIC_LOW;
+
+ if (offset)
+ return regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_1,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, reg_val));
+ else
+ return regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_0,
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, reg_val));
+}
+
+static int ad4062_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct ad4062_state *st = gpiochip_get_data(gc);
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD4062_REG_GP_CONF, &reg_val);
+ if (ret)
+ return ret;
+
+ if (offset)
+ reg_val = FIELD_GET(AD4062_REG_GP_CONF_MODE_MSK_1, reg_val);
+ else
+ reg_val = FIELD_GET(AD4062_REG_GP_CONF_MODE_MSK_0, reg_val);
+
+ return reg_val == AD4062_GP_STATIC_HIGH;
+}
+
+static void ad4062_gpio_disable(void *data)
+{
+ struct ad4062_state *st = data;
+ u8 val = FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, AD4062_GP_DISABLED) |
+ FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, AD4062_GP_DISABLED);
+
+ regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ AD4062_REG_GP_CONF_MODE_MSK_1 | AD4062_REG_GP_CONF_MODE_MSK_0,
+ val);
+}
+
+static int ad4062_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct ad4062_state *st = gpiochip_get_data(gc);
+
+ bitmap_zero(valid_mask, ngpios);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(st->gpo_irq); i++)
+ __assign_bit(i, valid_mask, !st->gpo_irq[i]);
+
+ return 0;
+}
+
+static int ad4062_gpio_init(struct ad4062_state *st)
+{
+ struct device *dev = &st->i3cdev->dev;
+ struct gpio_chip *gc;
+ u8 val, mask;
+ int ret;
+
+ if (!device_property_read_bool(dev, "gpio-controller"))
+ return 0;
+
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
+ if (!gc)
+ return -ENOMEM;
+
+ val = 0;
+ mask = 0;
+ if (!st->gpo_irq[0]) {
+ mask |= AD4062_REG_GP_CONF_MODE_MSK_0;
+ val |= FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_0, AD4062_GP_STATIC_LOW);
+ }
+ if (!st->gpo_irq[1]) {
+ mask |= AD4062_REG_GP_CONF_MODE_MSK_1;
+ val |= FIELD_PREP(AD4062_REG_GP_CONF_MODE_MSK_1, AD4062_GP_STATIC_LOW);
+ }
+
+ ret = regmap_update_bits(st->regmap, AD4062_REG_GP_CONF,
+ mask, val);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, ad4062_gpio_disable, st);
+ if (ret)
+ return ret;
+
+ gc->parent = dev;
+ gc->label = st->chip->name;
+ gc->owner = THIS_MODULE;
+ gc->base = -1;
+ gc->ngpio = 2;
+ gc->init_valid_mask = ad4062_gpio_init_valid_mask;
+ gc->get_direction = ad4062_gpio_get_direction;
+ gc->set = ad4062_gpio_set;
+ gc->get = ad4062_gpio_get;
+ gc->can_sleep = true;
+
+ ret = devm_gpiochip_add_data(dev, gc, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to register GPIO chip\n");
+
+ return 0;
+}
+
+static const struct i3c_device_id ad4062_id_table[] = {
+ I3C_DEVICE(AD4062_I3C_VENDOR, AD4060_PROD_ID, &ad4060_chip_info),
+ I3C_DEVICE(AD4062_I3C_VENDOR, AD4062_PROD_ID, &ad4062_chip_info),
+ { }
+};
+MODULE_DEVICE_TABLE(i3c, ad4062_id_table);
+
+static int ad4062_probe(struct i3c_device *i3cdev)
+{
+ const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table);
+ const struct ad4062_chip_info *chip = id->data;
+ struct device *dev = &i3cdev->dev;
+ struct iio_dev *indio_dev;
+ struct ad4062_state *st;
+ bool ref_sel;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->i3cdev = i3cdev;
+ i3cdev_set_drvdata(i3cdev, st);
+ init_completion(&st->completion);
+
+ ret = ad4062_regulators_get(st, &ref_sel);
+ if (ret)
+ return ret;
+
+ st->regmap = devm_regmap_init_i3c(i3cdev, &ad4062_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap\n");
+
+ st->mode = AD4062_SAMPLE_MODE;
+ st->wait_event = false;
+ st->chip = chip;
+ st->sampling_frequency = 0;
+ st->events_frequency = 0;
+ st->oversamp_ratio = 0;
+ st->indio_dev = indio_dev;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &ad4062_info;
+ indio_dev->name = chip->name;
+ indio_dev->channels = chip->channels;
+
+ ret = ad4062_soft_reset(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "AD4062 failed to soft reset\n");
+
+ ret = ad4062_check_ids(st);
+ if (ret)
+ return ret;
+
+ ret = ad4062_setup(indio_dev, indio_dev->channels, &ref_sel);
+ if (ret)
+ return ret;
+
+ ret = ad4062_request_irq(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad4062_request_trigger(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&i3cdev->dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad4062_poll_handler,
+ &ad4062_triggered_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n");
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = ad4062_request_ibi(i3cdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request i3c ibi\n");
+
+ ret = ad4062_gpio_init(st);
+ if (ret)
+ return ret;
+
+ ret = devm_work_autocancel(dev, &st->trig_conv, ad4062_trigger_work);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int ad4062_runtime_suspend(struct device *dev)
+{
+ struct ad4062_state *st = dev_get_drvdata(dev);
+
+ return regmap_write(st->regmap, AD4062_REG_DEVICE_CONFIG,
+ FIELD_PREP(AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK,
+ AD4062_REG_DEVICE_CONFIG_LOW_POWER_MODE));
+}
+
+static int ad4062_runtime_resume(struct device *dev)
+{
+ struct ad4062_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD4062_REG_DEVICE_CONFIG,
+ AD4062_REG_DEVICE_CONFIG_POWER_MODE_MSK);
+ if (ret)
+ return ret;
+
+ /* Wait device functional blocks to power up */
+ fsleep(3 * USEC_PER_MSEC);
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ad4062_pm_ops,
+ ad4062_runtime_suspend, ad4062_runtime_resume, NULL);
+
+static struct i3c_driver ad4062_driver = {
+ .driver = {
+ .name = "ad4062",
+ .pm = pm_ptr(&ad4062_pm_ops),
+ },
+ .probe = ad4062_probe,
+ .id_table = ad4062_id_table,
+};
+module_i3c_driver(ad4062_driver);
+
+MODULE_AUTHOR("Jorge Marques <jorge.marques@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4062");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad4134.c b/drivers/iio/adc/ad4134.c
new file mode 100644
index 000000000000..e42ee328fcbf
--- /dev/null
+++ b/drivers/iio/adc/ad4134.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Analog Devices, Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#define AD4134_RESET_TIME_US (10 * USEC_PER_SEC)
+
+#define AD4134_REG_READ_MASK BIT(7)
+#define AD4134_SPI_MAX_XFER_LEN 3
+
+#define AD4134_EXT_CLOCK_MHZ (48 * HZ_PER_MHZ)
+
+#define AD4134_NUM_CHANNELS 4
+#define AD4134_CHAN_PRECISION_BITS 24
+
+#define AD4134_IFACE_CONFIG_A_REG 0x00
+#define AD4134_IFACE_CONFIG_B_REG 0x01
+#define AD4134_IFACE_CONFIG_B_SINGLE_INSTR BIT(7)
+
+#define AD4134_DEVICE_CONFIG_REG 0x02
+#define AD4134_DEVICE_CONFIG_POWER_MODE_MASK BIT(0)
+#define AD4134_POWER_MODE_HIGH_PERF 0x1
+
+#define AD4134_SILICON_REV_REG 0x07
+#define AD4134_SCRATCH_PAD_REG 0x0A
+#define AD4134_STREAM_MODE_REG 0x0E
+#define AD4134_SDO_PIN_SRC_SEL_REG 0x10
+#define AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK BIT(2)
+
+#define AD4134_DATA_PACKET_CONFIG_REG 0x11
+#define AD4134_DATA_PACKET_CONFIG_FRAME_MASK GENMASK(5, 4)
+#define AD4134_DATA_PACKET_24BIT_FRAME 0x2
+
+#define AD4134_DIG_IF_CFG_REG 0x12
+#define AD4134_DIF_IF_CFG_FORMAT_MASK GENMASK(1, 0)
+#define AD4134_DATA_FORMAT_SINGLE_CH_MODE 0x0
+
+#define AD4134_PW_DOWN_CTRL_REG 0x13
+#define AD4134_DEVICE_STATUS_REG 0x15
+#define AD4134_ODR_VAL_INT_LSB_REG 0x16
+#define AD4134_CH3_OFFSET_MSB_REG 0x3E
+#define AD4134_AIN_OR_ERROR_REG 0x48
+
+/*
+ * AD4134 register map ends at address 0x48 and there is no register for
+ * retrieving ADC sample data. Though, to make use of Linux regmap API both
+ * for register access and sample read, we define one virtual register for each
+ * ADC channel. AD4134_CH_VREG(x) maps a channel number to it's virtual register
+ * address while AD4134_VREG_CH(x) tells which channel given the address.
+ */
+#define AD4134_CH_VREG(x) ((x) + 0x50)
+#define AD4134_VREG_CH(x) ((x) - 0x50)
+
+#define AD4134_SPI_CRC_POLYNOM 0x07
+#define AD4134_SPI_CRC_INIT_VALUE 0xA5
+static unsigned char ad4134_spi_crc_table[CRC8_TABLE_SIZE];
+
+#define AD4134_CHANNEL(_index) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec ad4134_chan_set[] = {
+ AD4134_CHANNEL(0),
+ AD4134_CHANNEL(1),
+ AD4134_CHANNEL(2),
+ AD4134_CHANNEL(3),
+};
+
+struct ad4134_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ unsigned long sys_clk_hz;
+ struct gpio_desc *odr_gpio;
+ int refin_mv;
+ /*
+ * DMA (thus cache coherency maintenance) requires the transfer buffers
+ * to live in their own cache lines.
+ */
+ u8 rx_buf[AD4134_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[AD4134_SPI_MAX_XFER_LEN];
+};
+
+static const struct regmap_range ad4134_regmap_rd_range[] = {
+ regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_SILICON_REV_REG),
+ regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_PW_DOWN_CTRL_REG),
+ regmap_reg_range(AD4134_DEVICE_STATUS_REG, AD4134_AIN_OR_ERROR_REG),
+ regmap_reg_range(AD4134_CH_VREG(0), AD4134_CH_VREG(AD4134_NUM_CHANNELS)),
+};
+
+static const struct regmap_range ad4134_regmap_wr_range[] = {
+ regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_DEVICE_CONFIG_REG),
+ regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_SCRATCH_PAD_REG),
+ regmap_reg_range(AD4134_STREAM_MODE_REG, AD4134_PW_DOWN_CTRL_REG),
+ regmap_reg_range(AD4134_ODR_VAL_INT_LSB_REG, AD4134_CH3_OFFSET_MSB_REG),
+};
+
+static const struct regmap_access_table ad4134_regmap_rd_table = {
+ .yes_ranges = ad4134_regmap_rd_range,
+ .n_yes_ranges = ARRAY_SIZE(ad4134_regmap_rd_range),
+};
+
+static const struct regmap_access_table ad4134_regmap_wr_table = {
+ .yes_ranges = ad4134_regmap_wr_range,
+ .n_yes_ranges = ARRAY_SIZE(ad4134_regmap_wr_range),
+};
+
+static int ad4134_calc_spi_crc(u8 inst, u8 data)
+{
+ u8 buf[] = { inst, data };
+
+ return crc8(ad4134_spi_crc_table, buf, ARRAY_SIZE(buf),
+ AD4134_SPI_CRC_INIT_VALUE);
+}
+
+static void ad4134_prepare_spi_tx_buf(u8 inst, u8 data, u8 *buf)
+{
+ buf[0] = inst;
+ buf[1] = data;
+ buf[2] = ad4134_calc_spi_crc(inst, data);
+}
+
+static int ad4134_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct ad4134_state *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ .len = AD4134_SPI_MAX_XFER_LEN,
+ };
+ int ret;
+
+ ad4134_prepare_spi_tx_buf(reg, val, st->tx_buf);
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ if (st->rx_buf[2] != st->tx_buf[2])
+ dev_dbg(&st->spi->dev, "reg write CRC check failed\n");
+
+ return 0;
+}
+
+static int ad4134_data_read(struct ad4134_state *st, unsigned int reg,
+ unsigned int *val)
+{
+ unsigned int i;
+ int ret;
+
+ /*
+ * To be able to read data from all 4 channels through a single line, we
+ * set DOUTx output format to 0 in the digital interface config register
+ * (0x12). With that, data from all four channels is serialized and
+ * output on DOUT0. During the probe, we also set SDO_PIN_SRC_SEL in
+ * DEVICE_CONFIG_1 register to duplicate DOUT0 on the SDO pin. Combined,
+ * those configurations enable ADC data read through a conventional SPI
+ * interface. Now we read data from all channels but keep only the bits
+ * from the requested one.
+ */
+ for (i = 0; i < ARRAY_SIZE(ad4134_chan_set); i++) {
+ ret = spi_write_then_read(st->spi, NULL, 0, st->rx_buf,
+ BITS_TO_BYTES(AD4134_CHAN_PRECISION_BITS));
+ if (ret)
+ return ret;
+
+ /*
+ * AD4134 has a built-in feature that flags when data transfers
+ * don't run enough clock cycles to read the entire data frame.
+ * Clock out data from all channels to avoid that.
+ */
+ if (i == AD4134_VREG_CH(reg))
+ *val = get_unaligned_be24(st->rx_buf);
+ }
+
+ return 0;
+}
+
+static int ad4134_register_read(struct ad4134_state *st, unsigned int reg,
+ unsigned int *val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ .len = AD4134_SPI_MAX_XFER_LEN,
+ };
+ unsigned int inst;
+ int ret;
+
+ inst = AD4134_REG_READ_MASK | reg;
+ ad4134_prepare_spi_tx_buf(inst, 0, st->tx_buf);
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ *val = st->rx_buf[1];
+
+ /* Check CRC */
+ if (st->rx_buf[2] != st->tx_buf[2])
+ dev_dbg(&st->spi->dev, "reg read CRC check failed\n");
+
+ return 0;
+}
+
+static int ad4134_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct ad4134_state *st = context;
+
+ if (reg >= AD4134_CH_VREG(0))
+ return ad4134_data_read(st, reg, val);
+
+ return ad4134_register_read(st, reg, val);
+}
+
+static const struct regmap_config ad4134_regmap_config = {
+ .reg_read = ad4134_reg_read,
+ .reg_write = ad4134_reg_write,
+ .rd_table = &ad4134_regmap_rd_table,
+ .wr_table = &ad4134_regmap_wr_table,
+ .max_register = AD4134_CH_VREG(ARRAY_SIZE(ad4134_chan_set)),
+};
+
+static int ad4134_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad4134_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ gpiod_set_value_cansleep(st->odr_gpio, 1);
+ /*
+ * For slave mode gated DCLK (data sheet page 11), the minimum
+ * ODR high time is 3 * tDIGCLK. The internal digital clock
+ * period is tDIGCLK = 1/fDIGCLK = 2/fSYSCLK.
+ * The System clock frequency (fSYSCLK) is typically 48 MHz.
+ * Thus, ODR high time = 3 * (2 / (48 * HZ_PER_MHZ))
+ * ODR high time = 0.000000125 s = 125 ns
+ * 1 micro second should be more than enough. Not worth it
+ * tweaking for shorter dealy since this is not a fast data path.
+ */
+ fsleep(1);
+ gpiod_set_value_cansleep(st->odr_gpio, 0);
+ ret = regmap_read(st->regmap, AD4134_CH_VREG(chan->channel), val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->refin_mv;
+ *val2 = AD4134_CHAN_PRECISION_BITS - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad4134_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad4134_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int ad4134_min_io_mode_setup(struct ad4134_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ st->odr_gpio = devm_gpiod_get(dev, "odr", GPIOD_OUT_LOW);
+ if (IS_ERR(st->odr_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->odr_gpio),
+ "failed to get ODR GPIO\n");
+
+ ret = regmap_update_bits(st->regmap, AD4134_DIG_IF_CFG_REG,
+ AD4134_DIF_IF_CFG_FORMAT_MASK,
+ FIELD_PREP(AD4134_DIF_IF_CFG_FORMAT_MASK,
+ AD4134_DATA_FORMAT_SINGLE_CH_MODE));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set single channel mode\n");
+
+ ret = regmap_set_bits(st->regmap, AD4134_SDO_PIN_SRC_SEL_REG,
+ AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set SDO source selection\n");
+
+ return regmap_set_bits(st->regmap, AD4134_IFACE_CONFIG_B_REG,
+ AD4134_IFACE_CONFIG_B_SINGLE_INSTR);
+}
+
+static const struct iio_info ad4134_info = {
+ .read_raw = ad4134_read_raw,
+ .debugfs_reg_access = ad4134_debugfs_reg_access,
+};
+
+static const char * const ad4143_required_regulators[] = {
+ "avdd5", "dvdd5", "iovdd",
+};
+
+static const char * const ad4143_optional_regulators[] = {
+ "avdd1v8", "dvdd1v8", "clkvdd",
+};
+
+static int ad4134_regulator_setup(struct ad4134_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_required_regulators),
+ ad4143_required_regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable power supplies\n");
+
+ /* Required regulator that we need to read the voltage */
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get REFIN voltage.\n");
+
+ st->refin_mv = ret / (MICRO / MILLI);
+
+ ret = devm_regulator_get_enable_optional(dev, "ldoin");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to enable ldoin supply\n");
+
+ /* If ldoin was provided, then use the use the internal LDO regulators */
+ if (ret == 0)
+ return 0;
+
+ /*
+ * If ldoin is not provided, then avdd1v8, dvdd1v8, and clkvdd are
+ * required.
+ */
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_optional_regulators),
+ ad4143_optional_regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable 1V8 power supplies\n");
+
+ return 0;
+}
+
+static int ad4134_clock_select(struct ad4134_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct clk *xtal_clk, *clkin_clk;
+
+ /*
+ * AD4134 requires one external clock source and only one external clock
+ * source can be provided at a time. Try to get a crystal provided clock.
+ * If that fails, try to get a CMOS clock.
+ */
+ xtal_clk = devm_clk_get_optional_enabled(dev, "xtal");
+ if (!xtal_clk)
+ xtal_clk = devm_clk_get_optional_enabled(dev, "xtal");
+ if (IS_ERR(xtal_clk))
+ return dev_err_probe(dev, PTR_ERR(xtal_clk),
+ "failed to get xtal\n");
+
+ clkin_clk = devm_clk_get_optional_enabled(dev, "clkin");
+ if (!clkin_clk)
+ clkin_clk = devm_clk_get_optional_enabled(dev, "clkin");
+ if (IS_ERR(clkin_clk))
+ return dev_err_probe(dev, PTR_ERR(clkin_clk),
+ "failed to get clkin\n");
+
+ st->sys_clk_hz = clk_get_rate(xtal_clk) | clk_get_rate(clkin_clk);
+ if (st->sys_clk_hz != AD4134_EXT_CLOCK_MHZ)
+ dev_warn(dev, "invalid external clock frequency %lu\n",
+ st->sys_clk_hz);
+
+ return 0;
+}
+
+static int ad4134_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct reset_control *rst;
+ struct iio_dev *indio_dev;
+ struct ad4134_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ indio_dev->name = "ad4134";
+ indio_dev->channels = ad4134_chan_set;
+ indio_dev->num_channels = ARRAY_SIZE(ad4134_chan_set);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad4134_info;
+
+ ret = ad4134_regulator_setup(st);
+ if (ret)
+ return ret;
+
+ ret = ad4134_clock_select(st);
+ if (ret)
+ return ret;
+
+ rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst),
+ "failed to get and deassert reset\n");
+
+ crc8_populate_msb(ad4134_spi_crc_table, AD4134_SPI_CRC_POLYNOM);
+
+ st->regmap = devm_regmap_init(dev, NULL, st, &ad4134_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "failed to initialize regmap");
+
+ ret = ad4134_min_io_mode_setup(st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to setup minimum I/O mode\n");
+
+ /* Bump precision to 24-bit */
+ ret = regmap_update_bits(st->regmap, AD4134_DATA_PACKET_CONFIG_REG,
+ AD4134_DATA_PACKET_CONFIG_FRAME_MASK,
+ FIELD_PREP(AD4134_DATA_PACKET_CONFIG_FRAME_MASK,
+ AD4134_DATA_PACKET_24BIT_FRAME));
+ if (ret)
+ return ret;
+
+ /* Set high performance power mode */
+ ret = regmap_update_bits(st->regmap, AD4134_DEVICE_CONFIG_REG,
+ AD4134_DEVICE_CONFIG_POWER_MODE_MASK,
+ FIELD_PREP(AD4134_DEVICE_CONFIG_POWER_MODE_MASK,
+ AD4134_POWER_MODE_HIGH_PERF));
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id ad4134_id[] = {
+ { "ad4134" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad4134_id);
+
+static const struct of_device_id ad4134_of_match[] = {
+ { .compatible = "adi,ad4134" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad4134_of_match);
+
+static struct spi_driver ad4134_driver = {
+ .driver = {
+ .name = "ad4134",
+ .of_match_table = ad4134_of_match,
+ },
+ .probe = ad4134_probe,
+ .id_table = ad4134_id,
+};
+module_spi_driver(ad4134_driver);
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_AD4134");
diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c
index efaed92191f1..82205bfae531 100644
--- a/drivers/iio/adc/ad4170-4.c
+++ b/drivers/iio/adc/ad4170-4.c
@@ -2973,7 +2973,7 @@ static int ad4170_probe(struct spi_device *spi)
if (spi->irq) {
ret = devm_request_irq(dev, spi->irq, &ad4170_irq_handler,
- IRQF_ONESHOT, indio_dev->name, indio_dev);
+ IRQF_NO_THREAD, indio_dev->name, indio_dev);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index 1bec6657394c..21d3f6aae972 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -16,7 +16,6 @@
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
-#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index f28e4ca37707..7e17ccbcedd0 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -345,7 +345,7 @@ static int ad7606_spi_update_scan_mode(struct iio_dev *indio_dev,
* has no way of demuxing the data to filter out unwanted
* channels.
*/
- if (bitmap_weight(scan_mask, num_adc_ch) != num_adc_ch)
+ if (!bitmap_full(scan_mask, num_adc_ch))
return -EINVAL;
}
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 4d570383ef02..9e4a66477d2d 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -184,12 +184,6 @@ static const struct iio_info ad7766_info = {
.read_raw = &ad7766_read_raw,
};
-static irqreturn_t ad7766_irq(int irq, void *private)
-{
- iio_trigger_poll(private);
- return IRQ_HANDLED;
-}
-
static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
@@ -260,8 +254,8 @@ static int ad7766_probe(struct spi_device *spi)
* Some platforms might not allow the option to power it down so
* don't enable the interrupt to avoid extra load on the system
*/
- ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
- IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
+ ret = devm_request_irq(&spi->dev, spi->irq, iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN | IRQF_NO_THREAD,
dev_name(&spi->dev),
ad7766->trig);
if (ret < 0)
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index d96802b7847a..fcd8aea7152e 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -6,6 +6,7 @@
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -14,8 +15,12 @@
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
+#include <linux/limits.h>
+#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
@@ -107,10 +112,15 @@
#define AD7768_VCM_OFF 0x07
+#define ADAQ776X_GAIN_MAX_NANO (128 * NANO)
+#define ADAQ776X_MAX_GAIN_MODES 8
+
#define AD7768_TRIGGER_SOURCE_SYNC_IDX 0
#define AD7768_MAX_CHANNELS 1
+#define ADAQ7768_PGA_PINS 3
+
enum ad7768_conv_mode {
AD7768_CONTINUOUS,
AD7768_ONE_SHOT,
@@ -153,6 +163,51 @@ enum ad7768_scan_type {
AD7768_SCAN_TYPE_HIGH_SPEED,
};
+enum {
+ AD7768_PGA_GAIN_0,
+ AD7768_PGA_GAIN_1,
+ AD7768_PGA_GAIN_2,
+ AD7768_PGA_GAIN_3,
+ AD7768_PGA_GAIN_4,
+ AD7768_PGA_GAIN_5,
+ AD7768_PGA_GAIN_6,
+ AD7768_PGA_GAIN_7,
+};
+
+enum {
+ AD7768_AAF_IN1,
+ AD7768_AAF_IN2,
+ AD7768_AAF_IN3,
+};
+
+/* PGA and AAF gains in V/V */
+static const int adaq7768_gains[] = {
+ [AD7768_PGA_GAIN_0] = 325, /* 0.325 */
+ [AD7768_PGA_GAIN_1] = 650, /* 0.650 */
+ [AD7768_PGA_GAIN_2] = 1300, /* 1.300 */
+ [AD7768_PGA_GAIN_3] = 2600, /* 2.600 */
+ [AD7768_PGA_GAIN_4] = 5200, /* 5.200 */
+ [AD7768_PGA_GAIN_5] = 10400, /* 10.400 */
+ [AD7768_PGA_GAIN_6] = 20800, /* 20.800 */
+};
+
+static const int adaq7769_gains[] = {
+ [AD7768_PGA_GAIN_0] = 1000, /* 1.000 */
+ [AD7768_PGA_GAIN_1] = 2000, /* 2.000 */
+ [AD7768_PGA_GAIN_2] = 4000, /* 4.000 */
+ [AD7768_PGA_GAIN_3] = 8000, /* 8.000 */
+ [AD7768_PGA_GAIN_4] = 16000, /* 16.000 */
+ [AD7768_PGA_GAIN_5] = 32000, /* 32.000 */
+ [AD7768_PGA_GAIN_6] = 64000, /* 64.000 */
+ [AD7768_PGA_GAIN_7] = 128000, /* 128.000 */
+};
+
+static const int ad7768_aaf_gains_bp[] = {
+ [AD7768_AAF_IN1] = 10000, /* 1.000 */
+ [AD7768_AAF_IN2] = 3640, /* 0.364 */
+ [AD7768_AAF_IN3] = 1430, /* 0.143 */
+};
+
/* -3dB cutoff frequency multipliers (relative to ODR) for each filter type. */
static const int ad7768_filter_3db_odr_multiplier[] = {
[AD7768_FILTER_SINC5] = 204, /* 0.204 */
@@ -213,6 +268,19 @@ static const struct iio_scan_type ad7768_scan_type[] = {
},
};
+struct ad7768_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channel_spec;
+ int num_channels;
+ const int *pga_gains;
+ int num_pga_modes;
+ int default_pga_mode;
+ int pgia_mode2pin_offset;
+ bool has_pga;
+ bool has_variable_aaf;
+ bool has_vcm_regulator;
+};
+
struct ad7768_state {
struct spi_device *spi;
struct regmap *regmap;
@@ -228,13 +296,19 @@ struct ad7768_state {
unsigned int samp_freq;
unsigned int samp_freq_avail[ARRAY_SIZE(ad7768_mclk_div_rates)];
unsigned int samp_freq_avail_len;
+ unsigned int pga_gain_mode;
+ unsigned int aaf_gain;
+ int scale_tbl[ADAQ776X_MAX_GAIN_MODES][2];
struct completion completion;
struct iio_trigger *trig;
+ struct gpio_descs *pga_gpios;
struct gpio_desc *gpio_sync_in;
struct gpio_desc *gpio_reset;
const char *labels[AD7768_MAX_CHANNELS];
struct gpio_chip gpiochip;
+ const struct ad7768_chip_info *chip;
bool en_spi_sync;
+ struct mutex pga_lock; /* protect device internal state (PGA) */
/*
* DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines.
@@ -457,6 +531,42 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
return ret;
}
+static void ad7768_fill_scale_tbl(struct iio_dev *dev)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ const struct iio_scan_type *scan_type;
+ int val, val2, tmp0, tmp1, i;
+ struct u32_fract fract;
+ unsigned long n, d;
+ u64 tmp2;
+
+ scan_type = iio_get_current_scan_type(dev, &dev->channels[0]);
+ if (scan_type->sign == 's')
+ val2 = scan_type->realbits - 1;
+ else
+ val2 = scan_type->realbits;
+
+ for (i = 0; i < st->chip->num_pga_modes; i++) {
+ /* Convert gain to a fraction format */
+ fract.numerator = st->chip->pga_gains[i];
+ fract.denominator = MILLI;
+ if (st->chip->has_variable_aaf) {
+ fract.numerator *= ad7768_aaf_gains_bp[st->aaf_gain];
+ fract.denominator *= PERMYRIAD;
+ }
+
+ rational_best_approximation(fract.numerator, fract.denominator,
+ INT_MAX, INT_MAX, &n, &d);
+
+ val = mult_frac(st->vref_uv, d, n);
+ /* Would multiply by NANO here, but value is already in milli */
+ tmp2 = ((u64)val * MICRO) >> val2;
+ tmp0 = div_u64_rem(tmp2, NANO, &tmp1);
+ st->scale_tbl[i][0] = tmp0; /* Integer part */
+ st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */
+ }
+}
+
static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
unsigned int dec_rate)
{
@@ -558,12 +668,66 @@ static int ad7768_configure_dig_fil(struct iio_dev *dev,
st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_idx];
}
+ /* Update scale table: scale values vary according to the precision */
+ ad7768_fill_scale_tbl(dev);
+
ad7768_fill_samp_freq_tbl(st);
/* A sync-in pulse is required after every configuration change */
return ad7768_send_sync_pulse(st);
}
+static int ad7768_setup_pga(struct device *dev, struct ad7768_state *st)
+{
+ st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW);
+ if (IS_ERR(st->pga_gpios))
+ return dev_err_probe(dev, PTR_ERR(st->pga_gpios),
+ "Failed to get PGA gpios.\n");
+
+ if (st->pga_gpios->ndescs != ADAQ7768_PGA_PINS)
+ return dev_err_probe(dev, -EINVAL,
+ "Expected %d GPIOs for PGA control.\n",
+ ADAQ7768_PGA_PINS);
+ return 0;
+}
+
+static int ad7768_calc_pga_gain(struct ad7768_state *st, int gain_int,
+ int gain_fract, int precision)
+{
+ u64 gain_nano;
+ u32 tmp;
+
+ gain_nano = gain_int * NANO + gain_fract;
+ gain_nano = clamp(gain_nano, 0, ADAQ776X_GAIN_MAX_NANO);
+ tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << precision, NANO);
+ gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp);
+ if (st->chip->has_variable_aaf)
+ gain_nano = DIV_ROUND_CLOSEST_ULL(gain_nano * PERMYRIAD,
+ ad7768_aaf_gains_bp[st->aaf_gain]);
+
+ return find_closest(gain_nano, st->chip->pga_gains,
+ (int)st->chip->num_pga_modes);
+}
+
+static int ad7768_set_pga_gain(struct ad7768_state *st,
+ int gain_mode)
+{
+ int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset);
+ DECLARE_BITMAP(bitmap, ADAQ7768_PGA_PINS) = { };
+ int ret;
+
+ guard(mutex)(&st->pga_lock);
+
+ bitmap_write(bitmap, pgia_pins_value, 0, ADAQ7768_PGA_PINS);
+ ret = gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap);
+ if (ret)
+ return ret;
+
+ st->pga_gain_mode = gain_mode;
+
+ return 0;
+}
+
static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
struct iio_dev *indio_dev = gpiochip_get_data(chip);
@@ -735,6 +899,19 @@ static int ad7768_get_filter_type_attr(struct iio_dev *dev,
return ad7768_filter_regval_to_type[FIELD_GET(mask, mode)];
}
+static int ad7768_update_dec_rate(struct iio_dev *dev, unsigned int dec_rate)
+{
+ struct ad7768_state *st = iio_priv(dev);
+ int ret;
+
+ ret = ad7768_configure_dig_fil(dev, st->filter_type, dec_rate);
+ if (ret)
+ return ret;
+
+ /* Update sampling frequency */
+ return ad7768_set_freq(st, st->samp_freq);
+}
+
static const struct iio_enum ad7768_filter_type_iio_enum = {
.items = ad7768_filter_enum,
.num_items = ARRAY_SIZE(ad7768_filter_enum),
@@ -748,24 +925,32 @@ static const struct iio_chan_spec_ext_info ad7768_ext_info[] = {
{ }
};
+#define AD7768_CHAN(_idx, _msk_avail) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate_available = _msk_avail, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .ext_info = ad7768_ext_info, \
+ .indexed = 1, \
+ .channel = _idx, \
+ .scan_index = _idx, \
+ .has_ext_scan_type = 1, \
+ .ext_scan_type = ad7768_scan_type, \
+ .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), \
+}
+
static const struct iio_chan_spec ad7768_channels[] = {
- {
- .type = IIO_VOLTAGE,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
- .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
- .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
- .ext_info = ad7768_ext_info,
- .indexed = 1,
- .channel = 0,
- .scan_index = 0,
- .has_ext_scan_type = 1,
- .ext_scan_type = ad7768_scan_type,
- .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type),
- },
+ AD7768_CHAN(0, 0),
+};
+
+static const struct iio_chan_spec adaq776x_channels[] = {
+ AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)),
};
static int ad7768_read_raw(struct iio_dev *indio_dev,
@@ -795,7 +980,19 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = (st->vref_uv * 2) / 1000;
+ if (st->chip->has_pga) {
+ guard(mutex)(&st->pga_lock);
+
+ *val = st->scale_tbl[st->pga_gain_mode][0];
+ *val2 = st->scale_tbl[st->pga_gain_mode][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ temp = (st->vref_uv * 2) / 1000;
+ if (st->chip->has_variable_aaf)
+ temp = (temp * PERMYRIAD) / ad7768_aaf_gains_bp[st->aaf_gain];
+
+ *val = temp;
*val2 = scan_type->realbits;
return IIO_VAL_FRACTIONAL_LOG2;
@@ -851,31 +1048,24 @@ static int ad7768_read_avail(struct iio_dev *indio_dev,
*length = st->samp_freq_avail_len;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (int *)st->scale_tbl;
+ *length = st->chip->num_pga_modes * 2;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
-static int __ad7768_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long info)
+static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
{
- struct ad7768_state *st = iio_priv(indio_dev);
- int ret;
-
- switch (info) {
- case IIO_CHAN_INFO_SAMP_FREQ:
- return ad7768_set_freq(st, val);
-
- case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val);
- if (ret)
- return ret;
-
- /* Update sampling frequency */
- return ad7768_set_freq(st, st->samp_freq);
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
default:
- return -EINVAL;
+ return IIO_VAL_INT_PLUS_MICRO;
}
}
@@ -883,15 +1073,47 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ const struct iio_scan_type *scan_type;
int ret;
- if (!iio_device_claim_direct(indio_dev))
- return -EBUSY;
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
+ if (IS_ERR(scan_type))
+ return PTR_ERR(scan_type);
- ret = __ad7768_write_raw(indio_dev, chan, val, val2, info);
- iio_device_release_direct(indio_dev);
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
- return ret;
+ ret = ad7768_set_freq(st, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = ad7768_update_dec_rate(indio_dev, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SCALE: {
+ int gain_mode;
+
+ if (!st->chip->has_pga)
+ return -EOPNOTSUPP;
+
+ if (scan_type->sign == 's')
+ gain_mode = ad7768_calc_pga_gain(st, val, val2,
+ scan_type->realbits - 1);
+ else
+ gain_mode = ad7768_calc_pga_gain(st, val, val2,
+ scan_type->realbits);
+
+ return ad7768_set_pga_gain(st, gain_mode);
+ }
+ default:
+ return -EINVAL;
+ }
}
static int ad7768_read_label(struct iio_dev *indio_dev,
@@ -915,6 +1137,7 @@ static const struct iio_info ad7768_info = {
.read_raw = &ad7768_read_raw,
.read_avail = &ad7768_read_avail,
.write_raw = &ad7768_write_raw,
+ .write_raw_get_fmt = &ad7768_write_raw_get_fmt,
.read_label = ad7768_read_label,
.get_current_scan_type = &ad7768_get_current_scan_type,
.debugfs_reg_access = &ad7768_reg_access,
@@ -1298,8 +1521,9 @@ static const struct regulator_desc vcm_desc = {
.owner = THIS_MODULE,
};
-static int ad7768_register_regulators(struct device *dev, struct ad7768_state *st,
- struct iio_dev *indio_dev)
+static int ad7768_register_vcm_regulator(struct device *dev,
+ struct ad7768_state *st,
+ struct iio_dev *indio_dev)
{
struct regulator_config config = {
.dev = dev,
@@ -1321,6 +1545,79 @@ static int ad7768_register_regulators(struct device *dev, struct ad7768_state *s
return 0;
}
+static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st)
+{
+ u32 val;
+ int ret;
+
+ ret = device_property_read_u32(dev, "adi,aaf-gain-bp", &val);
+ if (ret == -EINVAL) {
+ /* If controllable, use default */
+ if (st->chip->has_variable_aaf)
+ st->aaf_gain = AD7768_AAF_IN1;
+ return 0;
+ }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get AAF gain value\n");
+
+ if (!st->chip->has_variable_aaf)
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "AAF gain provided, but not supported for %s\n", st->chip->name);
+
+ switch (val) {
+ case 10000:
+ st->aaf_gain = AD7768_AAF_IN1;
+ break;
+ case 3640:
+ st->aaf_gain = AD7768_AAF_IN2;
+ break;
+ case 1430:
+ st->aaf_gain = AD7768_AAF_IN3;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid firmware provided AAF gain\n");
+ }
+
+ return 0;
+}
+
+static const struct ad7768_chip_info ad7768_chip_info = {
+ .name = "ad7768-1",
+ .channel_spec = ad7768_channels,
+ .num_channels = ARRAY_SIZE(ad7768_channels),
+ .has_vcm_regulator = true,
+};
+
+static const struct ad7768_chip_info adaq7767_chip_info = {
+ .name = "adaq7767-1",
+ .channel_spec = ad7768_channels,
+ .num_channels = ARRAY_SIZE(ad7768_channels),
+ .has_variable_aaf = true,
+};
+
+static const struct ad7768_chip_info adaq7768_chip_info = {
+ .name = "adaq7768-1",
+ .channel_spec = adaq776x_channels,
+ .num_channels = ARRAY_SIZE(adaq776x_channels),
+ .pga_gains = adaq7768_gains,
+ .default_pga_mode = AD7768_PGA_GAIN_2,
+ .num_pga_modes = ARRAY_SIZE(adaq7768_gains),
+ .pgia_mode2pin_offset = 6,
+ .has_pga = true,
+};
+
+static const struct ad7768_chip_info adaq7769_chip_info = {
+ .name = "adaq7769-1",
+ .channel_spec = adaq776x_channels,
+ .num_channels = ARRAY_SIZE(adaq776x_channels),
+ .pga_gains = adaq7769_gains,
+ .default_pga_mode = AD7768_PGA_GAIN_0,
+ .num_pga_modes = ARRAY_SIZE(adaq7769_gains),
+ .pgia_mode2pin_offset = 0,
+ .has_pga = true,
+ .has_variable_aaf = true,
+};
+
static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
@@ -1347,6 +1644,7 @@ static int ad7768_probe(struct spi_device *spi)
return ret;
}
+ st->chip = spi_get_device_match_data(spi);
st->spi = spi;
st->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config);
@@ -1371,14 +1669,20 @@ static int ad7768_probe(struct spi_device *spi)
st->mclk_freq = clk_get_rate(st->mclk);
- indio_dev->channels = ad7768_channels;
- indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
- indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = st->chip->channel_spec;
+ indio_dev->num_channels = st->chip->num_channels;
+ indio_dev->name = st->chip->name;
indio_dev->info = &ad7768_info;
indio_dev->modes = INDIO_DIRECT_MODE;
/* Register VCM output regulator */
- ret = ad7768_register_regulators(&spi->dev, st, indio_dev);
+ if (st->chip->has_vcm_regulator) {
+ ret = ad7768_register_vcm_regulator(&spi->dev, st, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = ad7768_parse_aaf_gain(&spi->dev, st);
if (ret)
return ret;
@@ -1389,14 +1693,26 @@ static int ad7768_probe(struct spi_device *spi)
}
init_completion(&st->completion);
+ ret = devm_mutex_init(&spi->dev, &st->pga_lock);
+ if (ret)
+ return ret;
+
+ if (st->chip->has_pga) {
+ ret = ad7768_setup_pga(&spi->dev, st);
+ if (ret)
+ return ret;
+
+ ret = ad7768_set_pga_gain(st, st->chip->default_pga_mode);
+ if (ret)
+ return ret;
+ }
- ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels));
+ ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels);
if (ret)
return ret;
- ret = devm_request_irq(&spi->dev, spi->irq,
- &ad7768_interrupt,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ret = devm_request_irq(&spi->dev, spi->irq, &ad7768_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
indio_dev->name, indio_dev);
if (ret)
return ret;
@@ -1409,13 +1725,19 @@ static int ad7768_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7768_id_table[] = {
- { "ad7768-1", 0 },
+ { "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 },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
static const struct of_device_id ad7768_of_match[] = {
- { .compatible = "adi,ad7768-1" },
+ { .compatible = "adi,ad7768-1", .data = &ad7768_chip_info },
+ { .compatible = "adi,adaq7767-1", .data = &adaq7767_chip_info },
+ { .compatible = "adi,adaq7768-1", .data = &adaq7768_chip_info },
+ { .compatible = "adi,adaq7769-1", .data = &adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad7768_of_match);
diff --git a/drivers/iio/adc/ad7779.c b/drivers/iio/adc/ad7779.c
index aac5049c9a07..695cc79e78da 100644
--- a/drivers/iio/adc/ad7779.c
+++ b/drivers/iio/adc/ad7779.c
@@ -840,7 +840,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
iio_trigger_set_drvdata(st->trig, st);
ret = devm_request_irq(dev, st->spi->irq, iio_trigger_generic_data_rdy_poll,
- IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name,
+ IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
return dev_err_probe(dev, ret, "request IRQ %d failed\n",
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 2d8f8da3671d..022888545580 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -5,29 +5,29 @@
* Copyright 2012-2020 Analog Devices Inc.
*/
+#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
+#include <linux/clk.h>
#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
+#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/seq_file.h>
#include <linux/err.h>
-#include <linux/delay.h>
#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
-
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include <linux/clk.h>
-
/*
* ADI High-Speed ADC common spi interface registers
* See Application-Note AN-877:
@@ -73,6 +73,7 @@
#define AN877_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0
#define AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1
#define AN877_ADC_OUTPUT_MODE_GRAY_CODE 0x2
+#define AN877_ADC_OUTPUT_MODE_MASK GENMASK(1, 0)
/* AN877_ADC_REG_OUTPUT_PHASE */
#define AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20
@@ -82,11 +83,19 @@
#define AN877_ADC_DCO_DELAY_ENABLE 0x80
/*
+ * Analog Devices AD9211 10-Bit, 200/250/300 MSPS ADC
+ */
+
+#define CHIPID_AD9211 0x06
+#define AD9211_DEF_OUTPUT_MODE 0x01
+#define AD9211_REG_VREF_MASK GENMASK(4, 0)
+
+/*
* Analog Devices AD9265 16-Bit, 125/105/80 MSPS ADC
*/
#define CHIPID_AD9265 0x64
-#define AD9265_DEF_OUTPUT_MODE 0x40
+#define AD9265_DEF_OUTPUT_MODE 0x41
#define AD9265_REG_VREF_MASK 0xC0
/*
@@ -94,7 +103,7 @@
*/
#define CHIPID_AD9434 0x6A
-#define AD9434_DEF_OUTPUT_MODE 0x00
+#define AD9434_DEF_OUTPUT_MODE 0x01
#define AD9434_REG_VREF_MASK GENMASK(4, 0)
/*
@@ -102,7 +111,7 @@
*/
#define CHIPID_AD9467 0x50
-#define AD9467_DEF_OUTPUT_MODE 0x08
+#define AD9467_DEF_OUTPUT_MODE 0x09
#define AD9467_REG_VREF_MASK 0x0F
/*
@@ -110,6 +119,7 @@
*/
#define CHIPID_AD9643 0x82
+#define AD9643_DEF_OUTPUT_MODE 0x01
#define AD9643_REG_VREF_MASK 0x1F
/*
@@ -117,6 +127,7 @@
*/
#define CHIPID_AD9652 0xC1
+#define AD9652_DEF_OUTPUT_MODE 0x01
#define AD9652_REG_VREF_MASK 0xC0
/*
@@ -124,6 +135,7 @@
*/
#define CHIPID_AD9649 0x6F
+#define AD9649_DEF_OUTPUT_MODE 0x01
#define AD9649_TEST_POINTS 8
#define AD9647_MAX_TEST_POINTS 32
@@ -145,6 +157,7 @@ struct ad9467_chip_info {
unsigned int num_lanes;
unsigned int dco_en;
unsigned int test_points;
+ const int *offset_range;
/* data clock output */
bool has_dco;
bool has_dco_invert;
@@ -234,6 +247,21 @@ static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg,
return 0;
}
+static const int ad9434_offset_range[] = {
+ -128, 1, 127,
+};
+
+static const unsigned int ad9211_scale_table[][2] = {
+ {980, 0x10}, {1000, 0x11}, {1020, 0x12}, {1040, 0x13},
+ {1060, 0x14}, {1080, 0x15}, {1100, 0x16}, {1120, 0x17},
+ {1140, 0x18}, {1160, 0x19}, {1180, 0x1A}, {1190, 0x1B},
+ {1200, 0x1C}, {1210, 0x1D}, {1220, 0x1E}, {1230, 0x1F},
+ {1250, 0x0}, {1270, 0x1}, {1290, 0x2}, {1310, 0x3},
+ {1330, 0x4}, {1350, 0x5}, {1370, 0x6}, {1390, 0x7},
+ {1410, 0x8}, {1430, 0x9}, {1450, 0xA}, {1460, 0xB},
+ {1470, 0xC}, {1480, 0xD}, {1490, 0xE}, {1500, 0xF},
+};
+
static const unsigned int ad9265_scale_table[][2] = {
{1250, 0x00}, {1500, 0x40}, {1750, 0x80}, {2000, 0xC0},
};
@@ -297,8 +325,29 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
}, \
}
+static const struct iio_chan_spec ad9211_channels[] = {
+ AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 10, 's'),
+};
+
static const struct iio_chan_spec ad9434_channels[] = {
- AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 12, 's'),
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ },
+ },
};
static const struct iio_chan_spec ad9467_channels[] = {
@@ -367,6 +416,24 @@ static const struct ad9467_chip_info ad9434_chip_tbl = {
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
.num_lanes = 6,
+ .offset_range = ad9434_offset_range,
+};
+
+static const struct ad9467_chip_info ad9211_chip_tbl = {
+ .name = "ad9211",
+ .id = CHIPID_AD9211,
+ .max_rate = 300 * HZ_PER_MHZ,
+ .scale_table = ad9211_scale_table,
+ .num_scales = ARRAY_SIZE(ad9211_scale_table),
+ .channels = ad9211_channels,
+ .num_channels = ARRAY_SIZE(ad9211_channels),
+ .test_points = AD9647_MAX_TEST_POINTS,
+ .test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
+ AN877_ADC_TESTMODE_OFF),
+ .test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
+ .default_output_mode = AD9211_DEF_OUTPUT_MODE,
+ .vref_mask = AD9211_REG_VREF_MASK,
+ .has_dco = true,
};
static const struct ad9467_chip_info ad9265_chip_tbl = {
@@ -399,6 +466,7 @@ static const struct ad9467_chip_info ad9643_chip_tbl = {
.test_mask = BIT(AN877_ADC_TESTMODE_RAMP) |
GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_RAMP + 1,
+ .default_output_mode = AD9643_DEF_OUTPUT_MODE,
.vref_mask = AD9643_REG_VREF_MASK,
.has_dco = true,
.has_dco_invert = true,
@@ -417,6 +485,7 @@ static const struct ad9467_chip_info ad9649_chip_tbl = {
.test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1,
+ .default_output_mode = AD9649_DEF_OUTPUT_MODE,
.has_dco = true,
.has_dco_invert = true,
.dco_en = AN877_ADC_DCO_DELAY_ENABLE,
@@ -434,6 +503,7 @@ static const struct ad9467_chip_info ad9652_chip_tbl = {
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
+ .default_output_mode = AD9652_DEF_OUTPUT_MODE,
.vref_mask = AD9652_REG_VREF_MASK,
.has_dco = true,
};
@@ -499,6 +569,33 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
return -EINVAL;
}
+static int ad9467_get_offset(struct ad9467_state *st, int *val)
+{
+ int ret;
+
+ ret = ad9467_spi_read(st, AN877_ADC_REG_OFFSET);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+
+ return IIO_VAL_INT;
+}
+
+static int ad9467_set_offset(struct ad9467_state *st, int val)
+{
+ int ret;
+
+ if (val < st->info->offset_range[0] || val > st->info->offset_range[2])
+ return -EINVAL;
+
+ ret = ad9467_spi_write(st, AN877_ADC_REG_OFFSET, val);
+ if (ret < 0)
+ return ret;
+
+ return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
+ AN877_ADC_TRANSFER_SYNC);
+}
+
static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode)
{
int ret;
@@ -582,10 +679,14 @@ static int ad9467_backend_testmode_off(struct ad9467_state *st,
static int ad9647_calibrate_prepare(struct ad9467_state *st)
{
+ unsigned int cmode;
unsigned int c;
int ret;
- ret = ad9467_outputmode_set(st, st->info->default_output_mode);
+ cmode = st->info->default_output_mode;
+ FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode,
+ AN877_ADC_OUTPUT_MODE_OFFSET_BINARY);
+ ret = ad9467_outputmode_set(st, cmode);
if (ret)
return ret;
@@ -689,7 +790,7 @@ static int ad9647_calibrate_stop(struct ad9467_state *st)
return ret;
}
- mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ mode = st->info->default_output_mode;
return ad9467_outputmode_set(st, mode);
}
@@ -802,6 +903,8 @@ static int ad9467_read_raw(struct iio_dev *indio_dev,
struct ad9467_state *st = iio_priv(indio_dev);
switch (m) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad9467_get_offset(st, val);
case IIO_CHAN_INFO_SCALE:
return ad9467_get_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -836,6 +939,8 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
int ret;
switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ return ad9467_set_offset(st, val);
case IIO_CHAN_INFO_SCALE:
return ad9467_set_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -874,6 +979,10 @@ static int ad9467_read_avail(struct iio_dev *indio_dev,
const struct ad9467_chip_info *info = st->info;
switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *type = IIO_VAL_INT;
+ *vals = info->offset_range;
+ return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)st->scales;
*type = IIO_VAL_INT_PLUS_MICRO;
@@ -1077,12 +1186,17 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file,
if (ret)
return ret;
- out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
+ out_mode = st->info->default_output_mode;
ret = ad9467_outputmode_set(st, out_mode);
if (ret)
return ret;
} else {
- ret = ad9467_outputmode_set(st, st->info->default_output_mode);
+ unsigned int cmode;
+
+ cmode = st->info->default_output_mode;
+ FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode,
+ AN877_ADC_OUTPUT_MODE_OFFSET_BINARY);
+ ret = ad9467_outputmode_set(st, cmode);
if (ret)
return ret;
@@ -1264,6 +1378,7 @@ static int ad9467_probe(struct spi_device *spi)
}
static const struct of_device_id ad9467_of_match[] = {
+ { .compatible = "adi,ad9211", .data = &ad9211_chip_tbl, },
{ .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, },
{ .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, },
@@ -1275,6 +1390,7 @@ 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 },
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index 2de8a718d62a..db085dc5e526 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -964,7 +964,7 @@ static irqreturn_t ade9000_dready_thread(int irq, void *data)
struct iio_dev *indio_dev = data;
/* Handle data ready interrupt from C4/EVENT/DREADY pin */
- if (!iio_device_claim_buffer_mode(indio_dev)) {
+ if (iio_device_try_claim_buffer_mode(indio_dev)) {
ade9000_iio_push_buffer(indio_dev);
iio_device_release_buffer_mode(indio_dev);
}
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 14fa4238c2b9..5f445e0de9ea 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -591,17 +591,12 @@ static int axi_adc_create_platform_device(struct adi_axi_adc_state *st,
.size_data = st->info->pdata_sz,
};
struct platform_device *pdev;
- int ret;
pdev = platform_device_register_full(&pi);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
- ret = devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
- if (ret)
- return ret;
-
- return 0;
+ return devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
}
static const struct iio_backend_ops adi_axi_adc_ops = {
@@ -674,13 +669,14 @@ static const struct iio_backend_info axi_ad408x = {
static int adi_axi_adc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
struct clk *clk;
int ret;
- st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
@@ -688,20 +684,19 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- st->dev = &pdev->dev;
- st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &axi_adc_regmap_config);
+ st->dev = dev;
+ st->regmap = devm_regmap_init_mmio(dev, base, &axi_adc_regmap_config);
if (IS_ERR(st->regmap))
- return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to init register map\n");
- st->info = device_get_match_data(&pdev->dev);
+ st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
- clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ return dev_err_probe(dev, PTR_ERR(clk),
"failed to get clock\n");
/*
@@ -716,47 +711,42 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
- ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
- dev_err(&pdev->dev,
- "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
- ADI_AXI_PCORE_VER_MAJOR(st->info->version),
- ADI_AXI_PCORE_VER_MINOR(st->info->version),
- ADI_AXI_PCORE_VER_PATCH(st->info->version),
- ADI_AXI_PCORE_VER_MAJOR(ver),
- ADI_AXI_PCORE_VER_MINOR(ver),
- ADI_AXI_PCORE_VER_PATCH(ver));
- return -ENODEV;
- }
-
- ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version))
+ return dev_err_probe(dev, -ENODEV,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version),
+ ADI_AXI_PCORE_VER_MINOR(st->info->version),
+ ADI_AXI_PCORE_VER_PATCH(st->info->version),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ ret = devm_iio_backend_register(dev, st->info->backend_info, st);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "failed to register iio backend\n");
+ return dev_err_probe(dev, ret, "failed to register iio backend\n");
- device_for_each_child_node_scoped(&pdev->dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
int val;
if (!st->info->has_child_nodes)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "invalid reg property.");
+ return dev_err_probe(dev, ret, "invalid reg property.");
if (val != 0)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"invalid node address.");
ret = axi_adc_create_platform_device(st, child);
if (ret)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"cannot create device.");
}
- dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
+ dev_info(dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index bf2bfd6bdc41..4be44c524b4d 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -472,16 +472,18 @@ static int aspeed_adc_probe(struct platform_device *pdev)
struct aspeed_adc_data *data;
int ret;
u32 adc_engine_control_reg_val;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev_of_node(dev);
unsigned long scaler_flags = 0;
char clk_name[32], clk_parent_name[32];
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
- data->dev = &pdev->dev;
- data->model_data = of_device_get_match_data(&pdev->dev);
+ data->dev = dev;
+ data->model_data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, indio_dev);
data->base = devm_platform_ioremap_resource(pdev, 0);
@@ -491,16 +493,15 @@ static int aspeed_adc_probe(struct platform_device *pdev)
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
- of_clk_get_parent_name(pdev->dev.of_node, 0));
+ of_clk_get_parent_name(np, 0));
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
data->model_data->model_name);
- data->fixed_div_clk = clk_hw_register_fixed_factor(
- &pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
+ data->fixed_div_clk = clk_hw_register_fixed_factor(dev, clk_name,
+ clk_parent_name, 0, 1, 2);
if (IS_ERR(data->fixed_div_clk))
return PTR_ERR(data->fixed_div_clk);
- ret = devm_add_action_or_reset(data->dev,
- aspeed_adc_unregister_fixed_divider,
+ ret = devm_add_action_or_reset(dev, aspeed_adc_unregister_fixed_divider,
data->fixed_div_clk);
if (ret)
return ret;
@@ -510,7 +511,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
data->model_data->model_name);
data->clk_prescaler = devm_clk_hw_register_divider(
- &pdev->dev, clk_name, clk_parent_name, 0,
+ dev, clk_name, clk_parent_name, 0,
data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
&data->clk_lock);
if (IS_ERR(data->clk_prescaler))
@@ -526,7 +527,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
data->model_data->model_name);
data->clk_scaler = devm_clk_hw_register_divider(
- &pdev->dev, clk_name, clk_parent_name, scaler_flags,
+ dev, clk_name, clk_parent_name, scaler_flags,
data->base + ASPEED_REG_CLOCK_CONTROL, 0,
data->model_data->scaler_bit_width,
data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0,
@@ -534,16 +535,14 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (IS_ERR(data->clk_scaler))
return PTR_ERR(data->clk_scaler);
- data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
- if (IS_ERR(data->rst)) {
- dev_err(&pdev->dev,
- "invalid or missing reset controller device tree entry");
- return PTR_ERR(data->rst);
- }
+ data->rst = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(data->rst))
+ return dev_err_probe(dev, PTR_ERR(data->rst),
+ "invalid or missing reset controller device tree entry");
+
reset_control_deassert(data->rst);
- ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
- data->rst);
+ ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst);
if (ret)
return ret;
@@ -555,7 +554,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (of_property_present(data->dev->of_node, "aspeed,battery-sensing")) {
+ if (of_property_present(np, "aspeed,battery-sensing")) {
if (data->model_data->bat_sense_sup) {
data->battery_sensing = 1;
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
@@ -567,15 +566,13 @@ static int aspeed_adc_probe(struct platform_device *pdev)
data->battery_mode_gain.div = 2;
}
} else
- dev_warn(&pdev->dev,
- "Failed to enable battery-sensing mode\n");
+ dev_warn(dev, "Failed to enable battery-sensing mode\n");
}
ret = clk_prepare_enable(data->clk_scaler->clk);
if (ret)
return ret;
- ret = devm_add_action_or_reset(data->dev,
- aspeed_adc_clk_disable_unprepare,
+ ret = devm_add_action_or_reset(dev, aspeed_adc_clk_disable_unprepare,
data->clk_scaler->clk);
if (ret)
return ret;
@@ -593,8 +590,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
- ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
- data);
+ ret = devm_add_action_or_reset(dev, aspeed_adc_power_down, data);
if (ret)
return ret;
@@ -626,8 +622,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
aspeed_adc_iio_channels;
indio_dev->num_channels = data->model_data->num_channels;
- ret = devm_iio_device_register(data->dev, indio_dev);
- return ret;
+ return devm_iio_device_register(dev, indio_dev);
}
static const struct aspeed_adc_trim_locate ast2500_adc_trim = {
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index f2400897818c..c1a0061b16ca 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -543,22 +543,21 @@ static const struct iio_chan_spec exynos_adc_iio_channels[] = {
static int exynos_adc_probe(struct platform_device *pdev)
{
struct exynos_adc *info = NULL;
+ struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
int ret;
int irq;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct exynos_adc));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->data = exynos_adc_get_data(pdev);
- if (!info->data) {
- dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
- return -EINVAL;
- }
+ if (!info->data)
+ return dev_err_probe(dev, -EINVAL, "failed getting exynos_adc_data\n");
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
@@ -566,44 +565,34 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (info->data->needs_adc_phy) {
- info->pmu_map = syscon_regmap_lookup_by_phandle(
- pdev->dev.of_node,
- "samsung,syscon-phandle");
- if (IS_ERR(info->pmu_map)) {
- dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
- return PTR_ERR(info->pmu_map);
- }
+ info->pmu_map = syscon_regmap_lookup_by_phandle(np, "samsung,syscon-phandle");
+ if (IS_ERR(info->pmu_map))
+ return dev_err_probe(dev, PTR_ERR(info->pmu_map),
+ "syscon regmap lookup failed.\n");
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
info->irq = irq;
- info->dev = &pdev->dev;
+ info->dev = dev;
init_completion(&info->completion);
- info->clk = devm_clk_get(&pdev->dev, "adc");
- if (IS_ERR(info->clk)) {
- dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
- PTR_ERR(info->clk));
- return PTR_ERR(info->clk);
- }
+ info->clk = devm_clk_get(dev, "adc");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n");
if (info->data->needs_sclk) {
- info->sclk = devm_clk_get(&pdev->dev, "sclk");
- if (IS_ERR(info->sclk)) {
- dev_err(&pdev->dev,
- "failed getting sclk clock, err = %ld\n",
- PTR_ERR(info->sclk));
- return PTR_ERR(info->sclk);
- }
+ info->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(info->sclk))
+ return dev_err_probe(dev, PTR_ERR(info->sclk),
+ "failed getting sclk clock\n");
}
- info->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ info->vdd = devm_regulator_get(dev, "vdd");
if (IS_ERR(info->vdd))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->vdd),
- "failed getting regulator");
+ return dev_err_probe(dev, PTR_ERR(info->vdd), "failed getting regulator");
ret = regulator_enable(info->vdd);
if (ret)
@@ -619,7 +608,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
- indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->name = dev_name(dev);
indio_dev->info = &exynos_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = exynos_adc_iio_channels;
@@ -627,11 +616,9 @@ static int exynos_adc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
- ret = request_irq(info->irq, exynos_adc_isr,
- 0, dev_name(&pdev->dev), info);
+ ret = request_irq(info->irq, exynos_adc_isr, 0, dev_name(dev), info);
if (ret < 0) {
- dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
- info->irq);
+ dev_err(dev, "failed requesting irq, irq = %d\n", info->irq);
goto err_disable_clk;
}
@@ -644,7 +631,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
if (ret < 0) {
- dev_err(&pdev->dev, "failed adding child nodes\n");
+ dev_err(dev, "failed adding child nodes\n");
goto err_of_populate;
}
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index a6f21791c685..ddc3721f3f68 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -815,7 +815,7 @@ static int mcp3911_probe(struct spi_device *spi)
* don't enable the interrupt to avoid extra load on the system.
*/
ret = devm_request_irq(dev, spi->irq, &iio_trigger_generic_data_rdy_poll,
- IRQF_NO_AUTOEN | IRQF_ONESHOT,
+ IRQF_NO_AUTOEN | IRQF_NO_THREAD,
indio_dev->name, adc->trig);
if (ret)
return ret;
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index cf8a8c0412ec..90919d282e7b 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -171,5 +171,4 @@ module_mcb_driver(men_z188_driver);
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
-MODULE_ALIAS("mcb:16z188");
MODULE_IMPORT_NS("MCB");
diff --git a/drivers/iio/adc/nxp-sar-adc.c b/drivers/iio/adc/nxp-sar-adc.c
new file mode 100644
index 000000000000..9efa883c277d
--- /dev/null
+++ b/drivers/iio/adc/nxp-sar-adc.c
@@ -0,0 +1,1016 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP SAR-ADC driver (adapted from Freescale Vybrid vf610 ADC driver
+ * by Fugang Duan <B38611@freescale.com>)
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2017, 2020-2025 NXP
+ * Copyright 2025, Linaro Ltd
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/circ_buf.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+/* SAR ADC registers. */
+#define NXP_SAR_ADC_CDR(__base, __channel) (((__base) + 0x100) + ((__channel) * 0x4))
+
+#define NXP_SAR_ADC_CDR_CDATA_MASK GENMASK(11, 0)
+#define NXP_SAR_ADC_CDR_VALID BIT(19)
+
+/* Main Configuration Register */
+#define NXP_SAR_ADC_MCR(__base) ((__base) + 0x00)
+
+#define NXP_SAR_ADC_MCR_PWDN BIT(0)
+#define NXP_SAR_ADC_MCR_ACKO BIT(5)
+#define NXP_SAR_ADC_MCR_ADCLKSEL BIT(8)
+#define NXP_SAR_ADC_MCR_TSAMP_MASK GENMASK(10, 9)
+#define NXP_SAR_ADC_MCR_NRSMPL_MASK GENMASK(12, 11)
+#define NXP_SAR_ADC_MCR_AVGEN BIT(13)
+#define NXP_SAR_ADC_MCR_CALSTART BIT(14)
+#define NXP_SAR_ADC_MCR_NSTART BIT(24)
+#define NXP_SAR_ADC_MCR_MODE BIT(29)
+#define NXP_SAR_ADC_MCR_OWREN BIT(31)
+
+/* Main Status Register */
+#define NXP_SAR_ADC_MSR(__base) ((__base) + 0x04)
+
+#define NXP_SAR_ADC_MSR_CALBUSY BIT(29)
+#define NXP_SAR_ADC_MSR_CALFAIL BIT(30)
+
+/* Interrupt Status Register */
+#define NXP_SAR_ADC_ISR(__base) ((__base) + 0x10)
+
+#define NXP_SAR_ADC_ISR_ECH BIT(0)
+
+/* Channel Pending Register */
+#define NXP_SAR_ADC_CEOCFR0(__base) ((__base) + 0x14)
+#define NXP_SAR_ADC_CEOCFR1(__base) ((__base) + 0x18)
+
+#define NXP_SAR_ADC_EOC_CH(c) BIT(c)
+
+/* Interrupt Mask Register */
+#define NXP_SAR_ADC_IMR(__base) ((__base) + 0x20)
+
+/* Channel Interrupt Mask Register */
+#define NXP_SAR_ADC_CIMR0(__base) ((__base) + 0x24)
+#define NXP_SAR_ADC_CIMR1(__base) ((__base) + 0x28)
+
+/* DMA Setting Register */
+#define NXP_SAR_ADC_DMAE(__base) ((__base) + 0x40)
+
+#define NXP_SAR_ADC_DMAE_DMAEN BIT(0)
+#define NXP_SAR_ADC_DMAE_DCLR BIT(1)
+
+/* DMA Control register */
+#define NXP_SAR_ADC_DMAR0(__base) ((__base) + 0x44)
+#define NXP_SAR_ADC_DMAR1(__base) ((__base) + 0x48)
+
+/* Conversion Timing Register */
+#define NXP_SAR_ADC_CTR0(__base) ((__base) + 0x94)
+#define NXP_SAR_ADC_CTR1(__base) ((__base) + 0x98)
+
+#define NXP_SAR_ADC_CTR_INPSAMP_MIN 0x08
+#define NXP_SAR_ADC_CTR_INPSAMP_MAX 0xff
+
+/* Normal Conversion Mask Register */
+#define NXP_SAR_ADC_NCMR0(__base) ((__base) + 0xa4)
+#define NXP_SAR_ADC_NCMR1(__base) ((__base) + 0xa8)
+
+/* Normal Conversion Mask Register field define */
+#define NXP_SAR_ADC_CH_MASK GENMASK(7, 0)
+
+/* Other field define */
+#define NXP_SAR_ADC_CONV_TIMEOUT (msecs_to_jiffies(100))
+#define NXP_SAR_ADC_CAL_TIMEOUT_US (100 * USEC_PER_MSEC)
+#define NXP_SAR_ADC_WAIT_US (2 * USEC_PER_MSEC)
+#define NXP_SAR_ADC_RESOLUTION 12
+
+/* Duration of conversion phases */
+#define NXP_SAR_ADC_TPT 2
+#define NXP_SAR_ADC_DP 2
+#define NXP_SAR_ADC_CT ((NXP_SAR_ADC_RESOLUTION + 2) * 4)
+#define NXP_SAR_ADC_CONV_TIME (NXP_SAR_ADC_TPT + NXP_SAR_ADC_CT + NXP_SAR_ADC_DP)
+
+#define NXP_SAR_ADC_NR_CHANNELS 8
+
+#define NXP_PAGE_SIZE SZ_4K
+#define NXP_SAR_ADC_DMA_SAMPLE_SZ DMA_SLAVE_BUSWIDTH_4_BYTES
+#define NXP_SAR_ADC_DMA_BUFF_SZ (NXP_PAGE_SIZE * NXP_SAR_ADC_DMA_SAMPLE_SZ)
+#define NXP_SAR_ADC_DMA_SAMPLE_CNT (NXP_SAR_ADC_DMA_BUFF_SZ / NXP_SAR_ADC_DMA_SAMPLE_SZ)
+
+struct nxp_sar_adc {
+ void __iomem *regs;
+ phys_addr_t regs_phys;
+ u8 current_channel;
+ u8 channels_used;
+ u16 value;
+ u32 vref_mV;
+
+ /* Save and restore context. */
+ u32 inpsamp;
+ u32 pwdn;
+
+ struct clk *clk;
+ struct dma_chan *dma_chan;
+ struct completion completion;
+ struct circ_buf dma_buf;
+
+ dma_addr_t rx_dma_buf;
+ dma_cookie_t cookie;
+
+ /* Protect circular buffers access. */
+ spinlock_t lock;
+
+ /* Array of enabled channels. */
+ u16 buffered_chan[NXP_SAR_ADC_NR_CHANNELS];
+
+ /* Buffer to be filled by the DMA. */
+ IIO_DECLARE_BUFFER_WITH_TS(u16, buffer, NXP_SAR_ADC_NR_CHANNELS);
+};
+
+struct nxp_sar_adc_data {
+ u32 vref_mV;
+ const char *model;
+};
+
+#define ADC_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+}
+
+static const struct iio_chan_spec nxp_sar_adc_iio_channels[] = {
+ ADC_CHAN(0, IIO_VOLTAGE),
+ ADC_CHAN(1, IIO_VOLTAGE),
+ ADC_CHAN(2, IIO_VOLTAGE),
+ ADC_CHAN(3, IIO_VOLTAGE),
+ ADC_CHAN(4, IIO_VOLTAGE),
+ ADC_CHAN(5, IIO_VOLTAGE),
+ ADC_CHAN(6, IIO_VOLTAGE),
+ ADC_CHAN(7, IIO_VOLTAGE),
+ /*
+ * The NXP SAR ADC documentation marks the channels 8 to 31 as
+ * "Reserved". Reflect the same in the driver in case new ADC
+ * variants comes with more channels.
+ */
+ IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static void nxp_sar_adc_irq_cfg(struct nxp_sar_adc *info, bool enable)
+{
+ if (enable)
+ writel(NXP_SAR_ADC_ISR_ECH, NXP_SAR_ADC_IMR(info->regs));
+ else
+ writel(0, NXP_SAR_ADC_IMR(info->regs));
+}
+
+static bool nxp_sar_adc_set_enabled(struct nxp_sar_adc *info, bool enable)
+{
+ u32 mcr;
+ bool pwdn;
+
+ mcr = readl(NXP_SAR_ADC_MCR(info->regs));
+
+ /*
+ * Get the current state and return it later. This is used for
+ * suspend/resume to get the power state
+ */
+ pwdn = FIELD_GET(NXP_SAR_ADC_MCR_PWDN, mcr);
+
+ /* When the enabled flag is not set, we set the power down bit */
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_PWDN, &mcr, !enable);
+
+ writel(mcr, NXP_SAR_ADC_MCR(info->regs));
+
+ /*
+ * Ensure there are at least three cycles between the
+ * configuration of NCMR and the setting of NSTART.
+ */
+ if (enable)
+ ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk) * 3));
+
+ return pwdn;
+}
+
+static inline bool nxp_sar_adc_enable(struct nxp_sar_adc *info)
+{
+ return nxp_sar_adc_set_enabled(info, true);
+}
+
+static inline bool nxp_sar_adc_disable(struct nxp_sar_adc *info)
+{
+ return nxp_sar_adc_set_enabled(info, false);
+}
+
+static inline void nxp_sar_adc_calibration_start(void __iomem *base)
+{
+ u32 mcr = readl(NXP_SAR_ADC_MCR(base));
+
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_CALSTART, &mcr, 0x1);
+
+ writel(mcr, NXP_SAR_ADC_MCR(base));
+}
+
+static inline int nxp_sar_adc_calibration_wait(void __iomem *base)
+{
+ u32 msr, ret;
+
+ ret = readl_poll_timeout(NXP_SAR_ADC_MSR(base), msr,
+ !FIELD_GET(NXP_SAR_ADC_MSR_CALBUSY, msr),
+ NXP_SAR_ADC_WAIT_US,
+ NXP_SAR_ADC_CAL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(NXP_SAR_ADC_MSR_CALFAIL, msr)) {
+ /*
+ * If the calibration fails, the status register bit must be
+ * cleared.
+ */
+ FIELD_MODIFY(NXP_SAR_ADC_MSR_CALFAIL, &msr, 0x0);
+ writel(msr, NXP_SAR_ADC_MSR(base));
+
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int nxp_sar_adc_calibration(struct nxp_sar_adc *info)
+{
+ int ret;
+
+ /* Calibration works only if the ADC is powered up. */
+ nxp_sar_adc_enable(info);
+
+ /* The calibration operation starts. */
+ nxp_sar_adc_calibration_start(info->regs);
+
+ ret = nxp_sar_adc_calibration_wait(info->regs);
+
+ /*
+ * Calibration works only if the ADC is powered up. However
+ * the calibration is called from the probe function where the
+ * iio is not enabled, so we disable after the calibration.
+ */
+ nxp_sar_adc_disable(info);
+
+ return ret;
+}
+
+static void nxp_sar_adc_conversion_timing_set(struct nxp_sar_adc *info, u32 inpsamp)
+{
+ inpsamp = clamp(inpsamp, NXP_SAR_ADC_CTR_INPSAMP_MIN, NXP_SAR_ADC_CTR_INPSAMP_MAX);
+
+ writel(inpsamp, NXP_SAR_ADC_CTR0(info->regs));
+}
+
+static u32 nxp_sar_adc_conversion_timing_get(struct nxp_sar_adc *info)
+{
+ return readl(NXP_SAR_ADC_CTR0(info->regs));
+}
+
+static void nxp_sar_adc_read_notify(struct nxp_sar_adc *info)
+{
+ writel(NXP_SAR_ADC_CH_MASK, NXP_SAR_ADC_CEOCFR0(info->regs));
+ writel(NXP_SAR_ADC_CH_MASK, NXP_SAR_ADC_CEOCFR1(info->regs));
+}
+
+static int nxp_sar_adc_read_data(struct nxp_sar_adc *info, unsigned int chan)
+{
+ u32 ceocfr, cdr;
+
+ ceocfr = readl(NXP_SAR_ADC_CEOCFR0(info->regs));
+
+ /*
+ * FIELD_GET() can not be used here because EOC_CH is not constant.
+ * TODO: Switch to field_get() when it will be available.
+ */
+ if (!(NXP_SAR_ADC_EOC_CH(chan) & ceocfr))
+ return -EIO;
+
+ cdr = readl(NXP_SAR_ADC_CDR(info->regs, chan));
+ if (!(FIELD_GET(NXP_SAR_ADC_CDR_VALID, cdr)))
+ return -EIO;
+
+ return FIELD_GET(NXP_SAR_ADC_CDR_CDATA_MASK, cdr);
+}
+
+static void nxp_sar_adc_isr_buffer(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < info->channels_used; i++) {
+ ret = nxp_sar_adc_read_data(info, info->buffered_chan[i]);
+ if (ret < 0) {
+ nxp_sar_adc_read_notify(info);
+ return;
+ }
+
+ info->buffer[i] = ret;
+ }
+
+ nxp_sar_adc_read_notify(info);
+
+ iio_push_to_buffers_with_ts(indio_dev, info->buffer, sizeof(info->buffer),
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+}
+
+static void nxp_sar_adc_isr_read_raw(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = nxp_sar_adc_read_data(info, info->current_channel);
+ nxp_sar_adc_read_notify(info);
+ if (ret < 0)
+ return;
+
+ info->value = ret;
+ complete(&info->completion);
+}
+
+static irqreturn_t nxp_sar_adc_isr(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int isr;
+
+ isr = readl(NXP_SAR_ADC_ISR(info->regs));
+ if (!(FIELD_GET(NXP_SAR_ADC_ISR_ECH, isr)))
+ return IRQ_NONE;
+
+ if (iio_buffer_enabled(indio_dev))
+ nxp_sar_adc_isr_buffer(indio_dev);
+ else
+ nxp_sar_adc_isr_read_raw(indio_dev);
+
+ writel(NXP_SAR_ADC_ISR_ECH, NXP_SAR_ADC_ISR(info->regs));
+
+ return IRQ_HANDLED;
+}
+
+static void nxp_sar_adc_channels_disable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 ncmr, cimr;
+
+ ncmr = readl(NXP_SAR_ADC_NCMR0(info->regs));
+ cimr = readl(NXP_SAR_ADC_CIMR0(info->regs));
+
+ /* FIELD_MODIFY() can not be used because the mask is not constant */
+ ncmr &= ~mask;
+ cimr &= ~mask;
+
+ writel(ncmr, NXP_SAR_ADC_NCMR0(info->regs));
+ writel(cimr, NXP_SAR_ADC_CIMR0(info->regs));
+}
+
+static void nxp_sar_adc_channels_enable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 ncmr, cimr;
+
+ ncmr = readl(NXP_SAR_ADC_NCMR0(info->regs));
+ cimr = readl(NXP_SAR_ADC_CIMR0(info->regs));
+
+ ncmr |= mask;
+ cimr |= mask;
+
+ writel(ncmr, NXP_SAR_ADC_NCMR0(info->regs));
+ writel(cimr, NXP_SAR_ADC_CIMR0(info->regs));
+}
+
+static void nxp_sar_adc_dma_channels_enable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 dmar;
+
+ dmar = readl(NXP_SAR_ADC_DMAR0(info->regs));
+
+ dmar |= mask;
+
+ writel(dmar, NXP_SAR_ADC_DMAR0(info->regs));
+}
+
+static void nxp_sar_adc_dma_channels_disable(struct nxp_sar_adc *info, u32 mask)
+{
+ u32 dmar;
+
+ dmar = readl(NXP_SAR_ADC_DMAR0(info->regs));
+
+ dmar &= ~mask;
+
+ writel(dmar, NXP_SAR_ADC_DMAR0(info->regs));
+}
+
+static void nxp_sar_adc_dma_cfg(struct nxp_sar_adc *info, bool enable)
+{
+ u32 dmae;
+
+ dmae = readl(NXP_SAR_ADC_DMAE(info->regs));
+
+ FIELD_MODIFY(NXP_SAR_ADC_DMAE_DMAEN, &dmae, enable);
+
+ writel(dmae, NXP_SAR_ADC_DMAE(info->regs));
+}
+
+static void nxp_sar_adc_stop_conversion(struct nxp_sar_adc *info)
+{
+ u32 mcr;
+
+ mcr = readl(NXP_SAR_ADC_MCR(info->regs));
+
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_NSTART, &mcr, 0x0);
+
+ writel(mcr, NXP_SAR_ADC_MCR(info->regs));
+
+ /*
+ * On disable, we have to wait for the transaction to finish.
+ * ADC does not abort the transaction if a chain conversion is
+ * in progress. Wait for the worst case scenario - 80 ADC clk
+ * cycles. The clock rate is 80MHz, this routine is called
+ * only when the capture finishes. The delay will be very
+ * short, usec-ish, which is acceptable in the atomic context.
+ */
+ ndelay(div64_u64(NSEC_PER_SEC, clk_get_rate(info->clk)) * 80);
+}
+
+static int nxp_sar_adc_start_conversion(struct nxp_sar_adc *info, bool raw)
+{
+ u32 mcr;
+
+ mcr = readl(NXP_SAR_ADC_MCR(info->regs));
+
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_NSTART, &mcr, 0x1);
+ FIELD_MODIFY(NXP_SAR_ADC_MCR_MODE, &mcr, raw ? 0 : 1);
+
+ writel(mcr, NXP_SAR_ADC_MCR(info->regs));
+
+ return 0;
+}
+
+static int nxp_sar_adc_read_channel(struct nxp_sar_adc *info, int channel)
+{
+ int ret;
+
+ info->current_channel = channel;
+ nxp_sar_adc_channels_enable(info, BIT(channel));
+ nxp_sar_adc_irq_cfg(info, true);
+ nxp_sar_adc_enable(info);
+
+ reinit_completion(&info->completion);
+ ret = nxp_sar_adc_start_conversion(info, true);
+ if (ret < 0)
+ goto out_disable;
+
+ if (!wait_for_completion_interruptible_timeout(&info->completion,
+ NXP_SAR_ADC_CONV_TIMEOUT))
+ ret = -ETIMEDOUT;
+
+ nxp_sar_adc_stop_conversion(info);
+
+out_disable:
+ nxp_sar_adc_channels_disable(info, BIT(channel));
+ nxp_sar_adc_irq_cfg(info, false);
+ nxp_sar_adc_disable(info);
+
+ return ret;
+}
+
+static int nxp_sar_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ u32 inpsamp;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = nxp_sar_adc_read_channel(info, chan->channel);
+
+ iio_device_release_direct(indio_dev);
+
+ if (ret)
+ return ret;
+
+ *val = info->value;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->vref_mV;
+ *val2 = NXP_SAR_ADC_RESOLUTION;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ inpsamp = nxp_sar_adc_conversion_timing_get(info);
+ *val = clk_get_rate(info->clk) / (inpsamp + NXP_SAR_ADC_CONV_TIME);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int nxp_sar_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ u32 inpsamp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * Configures the sample period duration in terms of the SAR
+ * controller clock. The minimum acceptable value is 8.
+ * Configuring it to a value lower than 8 sets the sample period
+ * to 8 cycles. We read the clock value and divide by the
+ * sampling timing which gives us the number of cycles expected.
+ * The value is 8-bit wide, consequently the max value is 0xFF.
+ */
+ inpsamp = clk_get_rate(info->clk) / val - NXP_SAR_ADC_CONV_TIME;
+ nxp_sar_adc_conversion_timing_set(info, inpsamp);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void nxp_sar_adc_dma_cb(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ struct dma_tx_state state;
+ struct circ_buf *dma_buf;
+ struct device *dev_dma;
+ u32 *dma_samples;
+ s64 timestamp;
+ int idx, ret;
+
+ guard(spinlock_irqsave)(&info->lock);
+
+ dma_buf = &info->dma_buf;
+ dma_samples = (u32 *)dma_buf->buf;
+ dev_dma = info->dma_chan->device->dev;
+
+ /*
+ * DMA in some corner cases might have already be charged for
+ * the next transfer. Potentially there can be a race where
+ * the residue changes while the dma engine updates the
+ * buffer. That could be handled by using the
+ * callback_result() instead of callback() because the residue
+ * will be passed as a parameter to the function. However this
+ * new callback is pretty new and the backend does not update
+ * the residue. So let's stick to the version other drivers do
+ * which has proven running well in production since several
+ * years.
+ */
+ dmaengine_tx_status(info->dma_chan, info->cookie, &state);
+
+ dma_sync_single_for_cpu(dev_dma, info->rx_dma_buf,
+ NXP_SAR_ADC_DMA_BUFF_SZ, DMA_FROM_DEVICE);
+
+ /* Current head position. */
+ dma_buf->head = (NXP_SAR_ADC_DMA_BUFF_SZ - state.residue) /
+ NXP_SAR_ADC_DMA_SAMPLE_SZ;
+
+ /* If everything was transferred, avoid an off by one error. */
+ if (!state.residue)
+ dma_buf->head--;
+
+ /* Something went wrong and nothing transferred. */
+ if (state.residue != NXP_SAR_ADC_DMA_BUFF_SZ) {
+ /* Make sure that head is multiple of info->channels_used. */
+ dma_buf->head -= dma_buf->head % info->channels_used;
+
+ /*
+ * dma_buf->tail != dma_buf->head condition will become false
+ * because dma_buf->tail will be incremented with 1.
+ */
+ while (dma_buf->tail != dma_buf->head) {
+ idx = dma_buf->tail % info->channels_used;
+ info->buffer[idx] = dma_samples[dma_buf->tail];
+ dma_buf->tail = (dma_buf->tail + 1) % NXP_SAR_ADC_DMA_SAMPLE_CNT;
+ if (idx != info->channels_used - 1)
+ continue;
+
+ /*
+ * iio_push_to_buffers_with_ts() should not be
+ * called with dma_samples as parameter. The samples
+ * will be smashed if timestamp is enabled.
+ */
+ timestamp = iio_get_time_ns(indio_dev);
+ ret = iio_push_to_buffers_with_ts(indio_dev, info->buffer,
+ sizeof(info->buffer),
+ timestamp);
+ if (ret < 0 && ret != -EBUSY)
+ dev_err_ratelimited(&indio_dev->dev,
+ "failed to push iio buffer: %d",
+ ret);
+ }
+
+ dma_buf->tail = dma_buf->head;
+ }
+
+ dma_sync_single_for_device(dev_dma, info->rx_dma_buf,
+ NXP_SAR_ADC_DMA_BUFF_SZ, DMA_FROM_DEVICE);
+}
+
+static int nxp_sar_adc_start_cyclic_dma(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ struct dma_slave_config config;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ info->dma_buf.head = 0;
+ info->dma_buf.tail = 0;
+
+ config.direction = DMA_DEV_TO_MEM;
+ config.src_addr_width = NXP_SAR_ADC_DMA_SAMPLE_SZ;
+ config.src_addr = NXP_SAR_ADC_CDR(info->regs_phys, info->buffered_chan[0]);
+ config.src_port_window_size = info->channels_used;
+ config.src_maxburst = info->channels_used;
+ ret = dmaengine_slave_config(info->dma_chan, &config);
+ if (ret < 0)
+ return ret;
+
+ desc = dmaengine_prep_dma_cyclic(info->dma_chan,
+ info->rx_dma_buf,
+ NXP_SAR_ADC_DMA_BUFF_SZ,
+ NXP_SAR_ADC_DMA_BUFF_SZ / 2,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EINVAL;
+
+ desc->callback = nxp_sar_adc_dma_cb;
+ desc->callback_param = indio_dev;
+ info->cookie = dmaengine_submit(desc);
+ ret = dma_submit_error(info->cookie);
+ if (ret) {
+ dmaengine_terminate_async(info->dma_chan);
+ return ret;
+ }
+
+ dma_async_issue_pending(info->dma_chan);
+
+ return 0;
+}
+
+static void nxp_sar_adc_buffer_software_do_predisable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+
+ /*
+ * The ADC DMAEN bit should be cleared before DMA transaction
+ * is canceled.
+ */
+ nxp_sar_adc_stop_conversion(info);
+ dmaengine_terminate_sync(info->dma_chan);
+ nxp_sar_adc_dma_cfg(info, false);
+ nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask);
+
+ dma_release_channel(info->dma_chan);
+}
+
+static int nxp_sar_adc_buffer_software_do_postenable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ nxp_sar_adc_dma_channels_enable(info, *indio_dev->active_scan_mask);
+
+ nxp_sar_adc_dma_cfg(info, true);
+
+ ret = nxp_sar_adc_start_cyclic_dma(indio_dev);
+ if (ret)
+ goto out_dma_channels_disable;
+
+ ret = nxp_sar_adc_start_conversion(info, false);
+ if (ret)
+ goto out_stop_cyclic_dma;
+
+ return 0;
+
+out_stop_cyclic_dma:
+ dmaengine_terminate_sync(info->dma_chan);
+
+out_dma_channels_disable:
+ nxp_sar_adc_dma_cfg(info, false);
+ nxp_sar_adc_dma_channels_disable(info, *indio_dev->active_scan_mask);
+
+ return ret;
+}
+
+static void nxp_sar_adc_buffer_trigger_do_predisable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+
+ nxp_sar_adc_irq_cfg(info, false);
+}
+
+static int nxp_sar_adc_buffer_trigger_do_postenable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+
+ nxp_sar_adc_irq_cfg(info, true);
+
+ return 0;
+}
+
+static int nxp_sar_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int current_mode = iio_device_get_current_mode(indio_dev);
+ unsigned long channel;
+ int ret;
+
+ info->dma_chan = dma_request_chan(indio_dev->dev.parent, "rx");
+ if (IS_ERR(info->dma_chan))
+ return PTR_ERR(info->dma_chan);
+
+ info->channels_used = 0;
+
+ /*
+ * The SAR-ADC has two groups of channels.
+ *
+ * - Group #0:
+ * * bit 0-7 : channel 0 -> channel 7
+ * * bit 8-31 : reserved
+ *
+ * - Group #32:
+ * * bit 0-7 : Internal
+ * * bit 8-31 : reserved
+ *
+ * The 8 channels from group #0 are used in this driver for
+ * ADC as described when declaring the IIO device and the
+ * mapping is the same. That means the active_scan_mask can be
+ * used directly to write the channel interrupt mask.
+ */
+ nxp_sar_adc_channels_enable(info, *indio_dev->active_scan_mask);
+
+ for_each_set_bit(channel, indio_dev->active_scan_mask, NXP_SAR_ADC_NR_CHANNELS)
+ info->buffered_chan[info->channels_used++] = channel;
+
+ nxp_sar_adc_enable(info);
+
+ if (current_mode == INDIO_BUFFER_SOFTWARE)
+ ret = nxp_sar_adc_buffer_software_do_postenable(indio_dev);
+ else
+ ret = nxp_sar_adc_buffer_trigger_do_postenable(indio_dev);
+ if (ret)
+ goto out_postenable;
+
+ return 0;
+
+out_postenable:
+ nxp_sar_adc_disable(info);
+ nxp_sar_adc_channels_disable(info, *indio_dev->active_scan_mask);
+
+ return ret;
+}
+
+static int nxp_sar_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int currentmode = iio_device_get_current_mode(indio_dev);
+
+ if (currentmode == INDIO_BUFFER_SOFTWARE)
+ nxp_sar_adc_buffer_software_do_predisable(indio_dev);
+ else
+ nxp_sar_adc_buffer_trigger_do_predisable(indio_dev);
+
+ nxp_sar_adc_disable(info);
+
+ nxp_sar_adc_channels_disable(info, *indio_dev->active_scan_mask);
+
+ return 0;
+}
+
+static irqreturn_t nxp_sar_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct nxp_sar_adc *info = iio_priv(indio_dev);
+ int ret;
+
+ ret = nxp_sar_adc_start_conversion(info, true);
+ if (ret < 0)
+ dev_dbg(&indio_dev->dev, "Failed to start conversion\n");
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = nxp_sar_adc_buffer_postenable,
+ .predisable = nxp_sar_adc_buffer_predisable,
+};
+
+static const struct iio_info nxp_sar_adc_iio_info = {
+ .read_raw = nxp_sar_adc_read_raw,
+ .write_raw = nxp_sar_adc_write_raw,
+};
+
+static int nxp_sar_adc_dma_probe(struct device *dev, struct nxp_sar_adc *info)
+{
+ u8 *rx_buf;
+
+ rx_buf = dmam_alloc_coherent(dev, NXP_SAR_ADC_DMA_BUFF_SZ,
+ &info->rx_dma_buf, GFP_KERNEL);
+ if (!rx_buf)
+ return -ENOMEM;
+
+ info->dma_buf.buf = rx_buf;
+
+ return 0;
+}
+
+/*
+ * The documentation describes the reset values for the registers.
+ * However some registers do not have these values after a reset. It
+ * is not a desirable situation. In some other SoC family
+ * documentation NXP recommends not assuming the default values are
+ * set and to initialize the registers conforming to the documentation
+ * reset information to prevent this situation. Assume the same rule
+ * applies here as there is a discrepancy between what is read from
+ * the registers at reset time and the documentation.
+ */
+static void nxp_sar_adc_set_default_values(struct nxp_sar_adc *info)
+{
+ writel(0x00003901, NXP_SAR_ADC_MCR(info->regs));
+ writel(0x00000001, NXP_SAR_ADC_MSR(info->regs));
+ writel(0x00000014, NXP_SAR_ADC_CTR0(info->regs));
+ writel(0x00000014, NXP_SAR_ADC_CTR1(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_CIMR0(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_CIMR1(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_NCMR0(info->regs));
+ writel(0x00000000, NXP_SAR_ADC_NCMR1(info->regs));
+}
+
+static int nxp_sar_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct nxp_sar_adc_data *data = device_get_match_data(dev);
+ struct nxp_sar_adc *info;
+ struct iio_dev *indio_dev;
+ struct resource *mem;
+ int irq, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ info->vref_mV = data->vref_mV;
+ spin_lock_init(&info->lock);
+ info->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
+ if (IS_ERR(info->regs))
+ return dev_err_probe(dev, PTR_ERR(info->regs),
+ "Failed to get and remap resource");
+
+ info->regs_phys = mem->start;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, nxp_sar_adc_isr, 0, dev_name(dev),
+ indio_dev);
+ if (ret < 0)
+ return ret;
+
+ info->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk),
+ "Failed to get the clock\n");
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = data->model;
+ indio_dev->info = &nxp_sar_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->channels = nxp_sar_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(nxp_sar_adc_iio_channels);
+
+ nxp_sar_adc_set_default_values(info);
+
+ ret = nxp_sar_adc_calibration(info);
+ if (ret)
+ dev_err_probe(dev, ret, "Calibration failed\n");
+
+ ret = nxp_sar_adc_dma_probe(dev, info);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize the DMA\n");
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &nxp_sar_adc_trigger_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Couldn't initialise the buffer\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Couldn't register the device\n");
+
+ return 0;
+}
+
+static int nxp_sar_adc_suspend(struct device *dev)
+{
+ struct nxp_sar_adc *info = iio_priv(dev_get_drvdata(dev));
+
+ info->pwdn = nxp_sar_adc_disable(info);
+ info->inpsamp = nxp_sar_adc_conversion_timing_get(info);
+
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+static int nxp_sar_adc_resume(struct device *dev)
+{
+ struct nxp_sar_adc *info = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+
+ nxp_sar_adc_conversion_timing_set(info, info->inpsamp);
+
+ if (!info->pwdn)
+ nxp_sar_adc_enable(info);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(nxp_sar_adc_pm_ops, nxp_sar_adc_suspend,
+ nxp_sar_adc_resume);
+
+static const struct nxp_sar_adc_data s32g2_sar_adc_data = {
+ .vref_mV = 1800,
+ .model = "s32g2-sar-adc",
+};
+
+static const struct of_device_id nxp_sar_adc_match[] = {
+ { .compatible = "nxp,s32g2-sar-adc", .data = &s32g2_sar_adc_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nxp_sar_adc_match);
+
+static struct platform_driver nxp_sar_adc_driver = {
+ .probe = nxp_sar_adc_probe,
+ .driver = {
+ .name = "nxp-sar-adc",
+ .of_match_table = nxp_sar_adc_match,
+ .pm = pm_sleep_ptr(&nxp_sar_adc_pm_ops),
+ },
+};
+module_platform_driver(nxp_sar_adc_driver);
+
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("NXP SAR-ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/qcom-spmi-rradc.c b/drivers/iio/adc/qcom-spmi-rradc.c
index b245416bae12..8e75665204d1 100644
--- a/drivers/iio/adc/qcom-spmi-rradc.c
+++ b/drivers/iio/adc/qcom-spmi-rradc.c
@@ -934,20 +934,15 @@ static int rradc_probe(struct platform_device *pdev)
chip = iio_priv(indio_dev);
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!chip->regmap) {
- dev_err(dev, "Couldn't get parent's regmap\n");
- return -EINVAL;
- }
+ if (!chip->regmap)
+ return dev_err_probe(dev, -EINVAL, "Couldn't get parent's regmap\n");
chip->dev = dev;
mutex_init(&chip->conversion_lock);
ret = device_property_read_u32(dev, "reg", &chip->base);
- if (ret < 0) {
- dev_err(chip->dev, "Couldn't find reg address, ret = %d\n",
- ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Couldn't find reg address\n");
batt_id_delay = -1;
ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms",
@@ -975,10 +970,9 @@ static int rradc_probe(struct platform_device *pdev)
/* Get the PMIC revision, we need it to handle some varying coefficients */
chip->pmic = qcom_pmic_get(chip->dev);
- if (IS_ERR(chip->pmic)) {
- dev_err(chip->dev, "Unable to get reference to PMIC device\n");
- return PTR_ERR(chip->pmic);
- }
+ if (IS_ERR(chip->pmic))
+ return dev_err_probe(dev, PTR_ERR(chip->pmic),
+ "Unable to get reference to PMIC device\n");
switch (chip->pmic->subtype) {
case PMI8998_SUBTYPE:
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 6721da0ed7bb..0f0bf2906af0 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -456,6 +456,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
{
const struct rockchip_saradc_data *match_data;
struct rockchip_saradc *info = NULL;
+ struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
int ret;
@@ -464,23 +465,21 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (!np)
return -ENODEV;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
- match_data = of_device_get_match_data(&pdev->dev);
+ match_data = of_device_get_match_data(dev);
if (!match_data)
- return dev_err_probe(&pdev->dev, -ENODEV,
- "failed to match device\n");
+ return dev_err_probe(dev, -ENODEV, "failed to match device\n");
info->data = match_data;
/* Sanity check for possible later IP variants with more channels */
if (info->data->num_channels > SARADC_MAX_CHANNELS)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "max channels exceeded");
+ return dev_err_probe(dev, -EINVAL, "max channels exceeded");
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
@@ -490,12 +489,10 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
* The reset should be an optional property, as it should work
* with old devicetrees as well
*/
- info->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
- "saradc-apb");
- if (IS_ERR(info->reset)) {
- ret = PTR_ERR(info->reset);
- return dev_err_probe(&pdev->dev, ret, "failed to get saradc-apb\n");
- }
+ info->reset = devm_reset_control_get_optional_exclusive(dev, "saradc-apb");
+ if (IS_ERR(info->reset))
+ return dev_err_probe(dev, PTR_ERR(info->reset),
+ "failed to get saradc-apb\n");
init_completion(&info->completion);
@@ -503,16 +500,14 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
+ ret = devm_request_irq(dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq);
- info->vref = devm_regulator_get(&pdev->dev, "vref");
+ info->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(info->vref))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->vref),
+ return dev_err_probe(dev, PTR_ERR(info->vref),
"failed to get regulator\n");
if (info->reset)
@@ -520,11 +515,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
ret = regulator_enable(info->vref);
if (ret < 0)
- return dev_err_probe(&pdev->dev, ret,
- "failed to enable vref regulator\n");
+ return dev_err_probe(dev, ret, "failed to enable vref regulator\n");
- ret = devm_add_action_or_reset(&pdev->dev,
- rockchip_saradc_regulator_disable, info);
+ ret = devm_add_action_or_reset(dev, rockchip_saradc_regulator_disable, info);
if (ret)
return ret;
@@ -534,14 +527,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
info->uv_vref = ret;
- info->pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk");
+ info->pclk = devm_clk_get_enabled(dev, "apb_pclk");
if (IS_ERR(info->pclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk),
- "failed to get pclk\n");
+ return dev_err_probe(dev, PTR_ERR(info->pclk), "failed to get pclk\n");
- info->clk = devm_clk_get_enabled(&pdev->dev, "saradc");
+ info->clk = devm_clk_get_enabled(dev, "saradc");
if (IS_ERR(info->clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(info->clk),
+ return dev_err_probe(dev, PTR_ERR(info->clk),
"failed to get adc clock\n");
/*
* Use a default value for the converter clock.
@@ -549,18 +541,17 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
*/
ret = clk_set_rate(info->clk, info->data->clk_rate);
if (ret < 0)
- return dev_err_probe(&pdev->dev, ret,
- "failed to set adc clk rate\n");
+ return dev_err_probe(dev, ret, "failed to set adc clk rate\n");
platform_set_drvdata(pdev, indio_dev);
- indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->name = dev_name(dev);
indio_dev->info = &rockchip_saradc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = info->data->channels;
indio_dev->num_channels = info->data->num_channels;
- ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL,
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
rockchip_saradc_trigger_handler,
NULL);
if (ret)
@@ -571,7 +562,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = devm_add_action_or_reset(&pdev->dev,
+ ret = devm_add_action_or_reset(dev,
rockchip_saradc_regulator_unreg_notifier,
info);
if (ret)
@@ -579,7 +570,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
- return devm_iio_device_register(&pdev->dev, indio_dev);
+ return devm_iio_device_register(dev, indio_dev);
}
static int rockchip_saradc_suspend(struct device *dev)
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
index 2535c2c3e60b..6209499c5c37 100644
--- a/drivers/iio/adc/sc27xx_adc.c
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -867,10 +867,8 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
int ret;
pdata = of_device_get_match_data(dev);
- if (!pdata) {
- dev_err(dev, "No matching driver data found\n");
- return -EINVAL;
- }
+ if (!pdata)
+ return dev_err_probe(dev, -EINVAL, "No matching driver data found\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
if (!indio_dev)
@@ -879,56 +877,43 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
sc27xx_data = iio_priv(indio_dev);
sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
- if (!sc27xx_data->regmap) {
- dev_err(dev, "failed to get ADC regmap\n");
- return -ENODEV;
- }
+ if (!sc27xx_data->regmap)
+ return dev_err_probe(dev, -ENODEV, "failed to get ADC regmap\n");
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
- if (ret) {
- dev_err(dev, "failed to get ADC base address\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get ADC base address\n");
sc27xx_data->irq = platform_get_irq(pdev, 0);
if (sc27xx_data->irq < 0)
return sc27xx_data->irq;
ret = of_hwspin_lock_get_id(np, 0);
- if (ret < 0) {
- dev_err(dev, "failed to get hwspinlock id\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get hwspinlock id\n");
sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
- if (!sc27xx_data->hwlock) {
- dev_err(dev, "failed to request hwspinlock\n");
- return -ENXIO;
- }
+ if (!sc27xx_data->hwlock)
+ return dev_err_probe(dev, -ENXIO, "failed to request hwspinlock\n");
sc27xx_data->dev = dev;
if (pdata->set_volref) {
sc27xx_data->volref = devm_regulator_get(dev, "vref");
- if (IS_ERR(sc27xx_data->volref)) {
- ret = PTR_ERR(sc27xx_data->volref);
- return dev_err_probe(dev, ret, "failed to get ADC volref\n");
- }
+ if (IS_ERR(sc27xx_data->volref))
+ return dev_err_probe(dev, PTR_ERR(sc27xx_data->volref),
+ "failed to get ADC volref\n");
}
sc27xx_data->var_data = pdata;
sc27xx_data->var_data->init_scale(sc27xx_data);
ret = sc27xx_adc_enable(sc27xx_data);
- if (ret) {
- dev_err(dev, "failed to enable ADC module\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable ADC module\n");
ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
- if (ret) {
- dev_err(dev, "failed to add ADC disable action\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add ADC disable action\n");
indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/adc/ti-ads1018.c b/drivers/iio/adc/ti-ads1018.c
new file mode 100644
index 000000000000..6246b3cab71f
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1018.c
@@ -0,0 +1,739 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Texas Instruments ADS1018 ADC driver
+ *
+ * Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/dev_printk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define ADS1018_CFG_OS_TRIG BIT(15)
+#define ADS1018_CFG_TS_MODE_EN BIT(4)
+#define ADS1018_CFG_PULL_UP BIT(3)
+#define ADS1018_CFG_NOP BIT(1)
+#define ADS1018_CFG_VALID (ADS1018_CFG_PULL_UP | ADS1018_CFG_NOP)
+
+#define ADS1018_CFG_MUX_MASK GENMASK(14, 12)
+
+#define ADS1018_CFG_PGA_MASK GENMASK(11, 9)
+#define ADS1018_PGA_DEFAULT 2
+
+#define ADS1018_CFG_MODE_MASK BIT(8)
+#define ADS1018_MODE_CONTINUOUS 0
+#define ADS1018_MODE_ONESHOT 1
+
+#define ADS1018_CFG_DRATE_MASK GENMASK(7, 5)
+#define ADS1018_DRATE_DEFAULT 4
+
+#define ADS1018_NUM_PGA_MODES 6
+#define ADS1018_CHANNELS_MAX 10
+
+struct ads1018_chan_data {
+ u8 pga_mode;
+ u8 data_rate_mode;
+};
+
+struct ads1018_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ unsigned long num_channels;
+
+ /* IIO_VAL_INT */
+ const u32 *data_rate_mode_to_hz;
+ unsigned long num_data_rate_mode_to_hz;
+
+ /*
+ * Let `res` be the chip's resolution and `fsr` (millivolts) be the
+ * full-scale range corresponding to the PGA mode given by the array
+ * index. Then, the gain is calculated using the following formula:
+ *
+ * gain = |fsr| / 2^(res - 1)
+ *
+ * This value then has to be represented in IIO_VAL_INT_PLUS_NANO
+ * format. For example if:
+ *
+ * gain = 6144 / 2^(16 - 1) = 0.1875
+ *
+ * ...then the formatted value is:
+ *
+ * { 0, 187500000 }
+ */
+ const u32 pga_mode_to_gain[ADS1018_NUM_PGA_MODES][2];
+
+ /* IIO_VAL_INT_PLUS_MICRO */
+ const u32 temp_scale[2];
+};
+
+struct ads1018 {
+ struct spi_device *spi;
+ struct iio_trigger *indio_trig;
+
+ struct gpio_desc *drdy_gpiod;
+ int drdy_irq;
+
+ struct ads1018_chan_data chan_data[ADS1018_CHANNELS_MAX];
+ const struct ads1018_chip_info *chip_info;
+
+ struct spi_message msg_read;
+ struct spi_transfer xfer;
+ __be16 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
+ __be16 rx_buf[2];
+};
+
+#define ADS1018_VOLT_DIFF_CHAN(_index, _chan, _chan2, _realbits) { \
+ .type = IIO_VOLTAGE, \
+ .channel = _chan, \
+ .channel2 = _chan2, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = _realbits, \
+ .storagebits = 16, \
+ .shift = 16 - _realbits, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = true, \
+ .differential = true, \
+}
+
+#define ADS1018_VOLT_CHAN(_index, _chan, _realbits) { \
+ .type = IIO_VOLTAGE, \
+ .channel = _chan, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = _realbits, \
+ .storagebits = 16, \
+ .shift = 16 - _realbits, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = true, \
+}
+
+#define ADS1018_TEMP_CHAN(_index, _realbits) { \
+ .type = IIO_TEMP, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = _realbits, \
+ .storagebits = 16, \
+ .shift = 16 - _realbits, \
+ .endianness = IIO_BE, \
+ }, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec ads1118_iio_channels[] = {
+ ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 16),
+ ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 16),
+ ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 16),
+ ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 16),
+ ADS1018_VOLT_CHAN(4, 0, 16),
+ ADS1018_VOLT_CHAN(5, 1, 16),
+ ADS1018_VOLT_CHAN(6, 2, 16),
+ ADS1018_VOLT_CHAN(7, 3, 16),
+ ADS1018_TEMP_CHAN(8, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
+static const struct iio_chan_spec ads1018_iio_channels[] = {
+ ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 12),
+ ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 12),
+ ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 12),
+ ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 12),
+ ADS1018_VOLT_CHAN(4, 0, 12),
+ ADS1018_VOLT_CHAN(5, 1, 12),
+ ADS1018_VOLT_CHAN(6, 2, 12),
+ ADS1018_VOLT_CHAN(7, 3, 12),
+ ADS1018_TEMP_CHAN(8, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
+/**
+ * ads1018_calc_delay - Calculates a suitable delay for a single-shot reading
+ * @hz: Sampling frequency
+ *
+ * Calculates an appropriate delay for a single shot reading given a sampling
+ * frequency.
+ *
+ * Return: Delay in microseconds (Always greater than 0).
+ */
+static u32 ads1018_calc_delay(unsigned int hz)
+{
+ /*
+ * Calculate the worst-case sampling rate by subtracting 10% error
+ * specified in the datasheet...
+ */
+ hz -= DIV_ROUND_UP(hz, 10);
+
+ /* ...Then calculate time per sample in microseconds. */
+ return DIV_ROUND_UP(HZ_PER_MHZ, hz);
+}
+
+/**
+ * ads1018_spi_read_exclusive - Reads a conversion value from the device
+ * @ads1018: Device data
+ * @cnv: ADC Conversion value (optional)
+ * @hold_cs: Keep CS line asserted after the SPI transfer
+ *
+ * Reads the most recent ADC conversion value, without updating the
+ * device's configuration.
+ *
+ * Context: Expects SPI bus *exclusive* use.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int ads1018_spi_read_exclusive(struct ads1018 *ads1018, __be16 *cnv,
+ bool hold_cs)
+{
+ int ret;
+
+ ads1018->xfer.cs_change = hold_cs;
+
+ ret = spi_sync_locked(ads1018->spi, &ads1018->msg_read);
+ if (ret)
+ return ret;
+
+ if (cnv)
+ *cnv = ads1018->rx_buf[0];
+
+ return 0;
+}
+
+/**
+ * ads1018_single_shot - Performs a one-shot reading sequence
+ * @ads1018: Device data
+ * @chan: Channel specification
+ * @cnv: Conversion value
+ *
+ * Writes a new configuration, waits an appropriate delay, then reads the most
+ * recent conversion.
+ *
+ * Context: Expects iio_device_claim_direct() is held.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int ads1018_single_shot(struct ads1018 *ads1018,
+ struct iio_chan_spec const *chan, u16 *cnv)
+{
+ u8 max_drate_mode = ads1018->chip_info->num_data_rate_mode_to_hz - 1;
+ u8 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode];
+ u8 pga_mode = ads1018->chan_data[chan->scan_index].pga_mode;
+ struct spi_transfer xfer[2] = {
+ {
+ .tx_buf = ads1018->tx_buf,
+ .len = sizeof(ads1018->tx_buf[0]),
+ .delay = {
+ .value = ads1018_calc_delay(drate),
+ .unit = SPI_DELAY_UNIT_USECS,
+ },
+ .cs_change = 1, /* 16-bit mode requires CS de-assert */
+ },
+ {
+ .rx_buf = ads1018->rx_buf,
+ .len = sizeof(ads1018->rx_buf[0]),
+ },
+ };
+ u16 cfg;
+ int ret;
+
+ cfg = ADS1018_CFG_VALID | ADS1018_CFG_OS_TRIG;
+ cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, chan->scan_index);
+ cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga_mode);
+ cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT);
+ cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, max_drate_mode);
+
+ if (chan->type == IIO_TEMP)
+ cfg |= ADS1018_CFG_TS_MODE_EN;
+
+ ads1018->tx_buf[0] = cpu_to_be16(cfg);
+ ret = spi_sync_transfer(ads1018->spi, xfer, ARRAY_SIZE(xfer));
+ if (ret)
+ return ret;
+
+ *cnv = be16_to_cpu(ads1018->rx_buf[0]);
+
+ return 0;
+}
+
+static int
+ads1018_read_raw_direct_mode(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ const struct ads1018_chip_info *chip_info = ads1018->chip_info;
+ u8 addr = chan->scan_index;
+ u8 pga_mode, drate_mode;
+ u16 cnv;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads1018_single_shot(ads1018, chan, &cnv);
+ if (ret)
+ return ret;
+
+ cnv >>= chan->scan_type.shift;
+ *val = sign_extend32(cnv, chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ pga_mode = ads1018->chan_data[addr].pga_mode;
+ *val = chip_info->pga_mode_to_gain[pga_mode][0];
+ *val2 = chip_info->pga_mode_to_gain[pga_mode][1];
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_TEMP:
+ *val = chip_info->temp_scale[0];
+ *val2 = chip_info->temp_scale[1];
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ drate_mode = ads1018->chan_data[addr].data_rate_mode;
+ *val = chip_info->data_rate_mode_to_hz[drate_mode];
+ return IIO_VAL_INT;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1018_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ads1018_read_raw_direct_mode(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int
+ads1018_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length, long mask)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *vals = (const int *)ads1018->chip_info->pga_mode_to_gain;
+ *length = ADS1018_NUM_PGA_MODES * 2;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT;
+ *vals = ads1018->chip_info->data_rate_mode_to_hz;
+ *length = ads1018->chip_info->num_data_rate_mode_to_hz;
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1018_write_raw_direct_mode(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long mask)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ const struct ads1018_chip_info *info = ads1018->chip_info;
+ unsigned int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ for (i = 0; i < ADS1018_NUM_PGA_MODES; i++) {
+ if (val == info->pga_mode_to_gain[i][0] &&
+ val2 == info->pga_mode_to_gain[i][1])
+ break;
+ }
+ if (i == ADS1018_NUM_PGA_MODES)
+ return -EINVAL;
+
+ ads1018->chan_data[chan->scan_index].pga_mode = i;
+ return 0;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < info->num_data_rate_mode_to_hz; i++) {
+ if (val == info->data_rate_mode_to_hz[i])
+ break;
+ }
+ if (i == info->num_data_rate_mode_to_hz)
+ return -EINVAL;
+
+ ads1018->chan_data[chan->scan_index].data_rate_mode = i;
+ return 0;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+ads1018_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ ret = ads1018_write_raw_direct_mode(indio_dev, chan, val, val2, mask);
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+static int
+ads1018_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static const struct iio_info ads1018_iio_info = {
+ .read_raw = ads1018_read_raw,
+ .read_avail = ads1018_read_avail,
+ .write_raw = ads1018_write_raw,
+ .write_raw_get_fmt = ads1018_write_raw_get_fmt,
+};
+
+static void ads1018_set_trigger_enable(struct ads1018 *ads1018)
+{
+ spi_bus_lock(ads1018->spi->controller);
+ ads1018_spi_read_exclusive(ads1018, NULL, true);
+ enable_irq(ads1018->drdy_irq);
+}
+
+static void ads1018_set_trigger_disable(struct ads1018 *ads1018)
+{
+ disable_irq(ads1018->drdy_irq);
+ ads1018_spi_read_exclusive(ads1018, NULL, false);
+ spi_bus_unlock(ads1018->spi->controller);
+}
+
+static int ads1018_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct ads1018 *ads1018 = iio_trigger_get_drvdata(trig);
+
+ /*
+ * We need to lock the SPI bus and tie CS low (hold_cs) to catch
+ * data-ready interrupts, otherwise the MISO line enters a Hi-Z state.
+ */
+
+ if (state)
+ ads1018_set_trigger_enable(ads1018);
+ else
+ ads1018_set_trigger_disable(ads1018);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops ads1018_trigger_ops = {
+ .set_trigger_state = ads1018_set_trigger_state,
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int ads1018_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ const struct ads1018_chip_info *chip_info = ads1018->chip_info;
+ unsigned int pga, drate, addr;
+ u16 cfg;
+
+ addr = find_first_bit(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
+ pga = ads1018->chan_data[addr].pga_mode;
+ drate = ads1018->chan_data[addr].data_rate_mode;
+
+ cfg = ADS1018_CFG_VALID;
+ cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, addr);
+ cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga);
+ cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_CONTINUOUS);
+ cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, drate);
+
+ if (chip_info->channels[addr].type == IIO_TEMP)
+ cfg |= ADS1018_CFG_TS_MODE_EN;
+
+ ads1018->tx_buf[0] = cpu_to_be16(cfg);
+ ads1018->tx_buf[1] = 0;
+
+ return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf));
+}
+
+static int ads1018_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ u16 cfg;
+
+ cfg = ADS1018_CFG_VALID;
+ cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT);
+
+ ads1018->tx_buf[0] = cpu_to_be16(cfg);
+ ads1018->tx_buf[1] = 0;
+
+ return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf));
+}
+
+static const struct iio_buffer_setup_ops ads1018_buffer_ops = {
+ .preenable = ads1018_buffer_preenable,
+ .postdisable = ads1018_buffer_postdisable,
+ .validate_scan_mask = iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ads1018_irq_handler(int irq, void *dev_id)
+{
+ struct ads1018 *ads1018 = dev_id;
+
+ /*
+ * We need to check if the "drdy" pin is actually active or if it's a
+ * pending interrupt triggered by the SPI transfer.
+ */
+ if (!gpiod_get_value(ads1018->drdy_gpiod))
+ return IRQ_HANDLED;
+
+ iio_trigger_poll(ads1018->indio_trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ads1018_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ struct {
+ __be16 conv;
+ aligned_s64 ts;
+ } scan = {};
+ int ret;
+
+ if (iio_trigger_using_own(indio_dev)) {
+ disable_irq(ads1018->drdy_irq);
+ ret = ads1018_spi_read_exclusive(ads1018, &scan.conv, true);
+ enable_irq(ads1018->drdy_irq);
+ } else {
+ ret = spi_read(ads1018->spi, ads1018->rx_buf, sizeof(ads1018->rx_buf));
+ scan.conv = ads1018->rx_buf[0];
+ }
+
+ if (!ret)
+ iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan),
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads1018_trigger_setup(struct iio_dev *indio_dev)
+{
+ struct ads1018 *ads1018 = iio_priv(indio_dev);
+ struct spi_device *spi = ads1018->spi;
+ struct device *dev = &spi->dev;
+ const char *con_id = "drdy";
+ int ret;
+
+ ads1018->drdy_gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_IN);
+ if (IS_ERR(ads1018->drdy_gpiod))
+ return dev_err_probe(dev, PTR_ERR(ads1018->drdy_gpiod),
+ "Failed to get %s GPIO.\n", con_id);
+
+ /* First try to get IRQ from SPI core, then from GPIO */
+ if (spi->irq > 0)
+ ads1018->drdy_irq = spi->irq;
+ else if (ads1018->drdy_gpiod)
+ ads1018->drdy_irq = gpiod_to_irq(ads1018->drdy_gpiod);
+ if (ads1018->drdy_irq < 0)
+ return dev_err_probe(dev, ads1018->drdy_irq,
+ "Failed to get IRQ from %s GPIO.\n", con_id);
+
+ /* An IRQ line is only an optional requirement for the IIO trigger */
+ if (ads1018->drdy_irq == 0)
+ return 0;
+
+ ads1018->indio_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-%s",
+ indio_dev->name,
+ iio_device_id(indio_dev),
+ con_id);
+ if (!ads1018->indio_trig)
+ return -ENOMEM;
+
+ iio_trigger_set_drvdata(ads1018->indio_trig, ads1018);
+ ads1018->indio_trig->ops = &ads1018_trigger_ops;
+
+ ret = devm_iio_trigger_register(dev, ads1018->indio_trig);
+ if (ret)
+ return ret;
+
+ /*
+ * The "data-ready" IRQ line is shared with the MOSI pin, thus we need
+ * to keep it disabled until we actually request data.
+ */
+ return devm_request_irq(dev, ads1018->drdy_irq, ads1018_irq_handler,
+ IRQF_NO_AUTOEN, ads1018->chip_info->name, ads1018);
+}
+
+static int ads1018_spi_probe(struct spi_device *spi)
+{
+ const struct ads1018_chip_info *info = spi_get_device_match_data(spi);
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ads1018 *ads1018;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ads1018));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ads1018 = iio_priv(indio_dev);
+ ads1018->spi = spi;
+ ads1018->chip_info = info;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = info->name;
+ indio_dev->info = &ads1018_iio_info;
+ indio_dev->channels = info->channels;
+ indio_dev->num_channels = info->num_channels;
+
+ for (unsigned int i = 0; i < ADS1018_CHANNELS_MAX; i++) {
+ ads1018->chan_data[i].data_rate_mode = ADS1018_DRATE_DEFAULT;
+ ads1018->chan_data[i].pga_mode = ADS1018_PGA_DEFAULT;
+ }
+
+ ads1018->xfer.rx_buf = ads1018->rx_buf;
+ ads1018->xfer.len = sizeof(ads1018->rx_buf);
+ spi_message_init_with_transfers(&ads1018->msg_read, &ads1018->xfer, 1);
+
+ ret = ads1018_trigger_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ads1018_trigger_handler,
+ &ads1018_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const unsigned int ads1018_data_rate_table[] = {
+ 128, 250, 490, 920, 1600, 2400, 3300,
+};
+
+static const unsigned int ads1118_data_rate_table[] = {
+ 8, 16, 32, 64, 128, 250, 475, 860,
+};
+
+static const struct ads1018_chip_info ads1018_chip_info = {
+ .name = "ads1018",
+ .channels = ads1018_iio_channels,
+ .num_channels = ARRAY_SIZE(ads1018_iio_channels),
+ .data_rate_mode_to_hz = ads1018_data_rate_table,
+ .num_data_rate_mode_to_hz = ARRAY_SIZE(ads1018_data_rate_table),
+ .pga_mode_to_gain = {
+ { 3, 0 }, /* fsr = 6144 mV */
+ { 2, 0 }, /* fsr = 4096 mV */
+ { 1, 0 }, /* fsr = 2048 mV */
+ { 0, 500000000 }, /* fsr = 1024 mV */
+ { 0, 250000000 }, /* fsr = 512 mV */
+ { 0, 125000000 }, /* fsr = 256 mV */
+ },
+ .temp_scale = { 125, 0 },
+};
+
+static const struct ads1018_chip_info ads1118_chip_info = {
+ .name = "ads1118",
+ .channels = ads1118_iio_channels,
+ .num_channels = ARRAY_SIZE(ads1118_iio_channels),
+ .data_rate_mode_to_hz = ads1118_data_rate_table,
+ .num_data_rate_mode_to_hz = ARRAY_SIZE(ads1118_data_rate_table),
+ .pga_mode_to_gain = {
+ { 0, 187500000 }, /* fsr = 6144 mV */
+ { 0, 125000000 }, /* fsr = 4096 mV */
+ { 0, 62500000 }, /* fsr = 2048 mV */
+ { 0, 31250000 }, /* fsr = 1024 mV */
+ { 0, 15625000 }, /* fsr = 512 mV */
+ { 0, 7812500 }, /* fsr = 256 mV */
+ },
+ .temp_scale = { 31, 250000 },
+};
+
+static const struct of_device_id ads1018_of_match[] = {
+ { .compatible = "ti,ads1018", .data = &ads1018_chip_info },
+ { .compatible = "ti,ads1118", .data = &ads1118_chip_info },
+ { }
+};
+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 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads1018_spi_match);
+
+static struct spi_driver ads1018_spi_driver = {
+ .driver = {
+ .name = "ads1018",
+ .of_match_table = ads1018_of_match,
+ },
+ .probe = ads1018_spi_probe,
+ .id_table = ads1018_spi_match,
+};
+module_spi_driver(ads1018_spi_driver);
+
+MODULE_DESCRIPTION("Texas Instruments ADS1018 ADC Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index c9a20024d6b1..a585621b0bc3 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -827,7 +827,7 @@ static int ads131e08_probe(struct spi_device *spi)
if (spi->irq) {
ret = devm_request_irq(&spi->dev, spi->irq,
ads131e08_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
spi->dev.driver->name, indio_dev);
if (ret)
return dev_err_probe(&spi->dev, ret,
diff --git a/drivers/iio/adc/ti-ads131m02.c b/drivers/iio/adc/ti-ads131m02.c
new file mode 100644
index 000000000000..07d63bf62c5f
--- /dev/null
+++ b/drivers/iio/adc/ti-ads131m02.c
@@ -0,0 +1,968 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Texas Instruments ADS131M02 family ADC chips.
+ *
+ * Copyright (C) 2024 Protonic Holland
+ * Copyright (C) 2025 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
+ *
+ * Primary Datasheet Reference (used for citations):
+ * ADS131M08 8-Channel, Simultaneously-Sampling, 24-Bit, Delta-Sigma ADC
+ * Document SBAS950B, Revised February 2021
+ * https://www.ti.com/lit/ds/symlink/ads131m08.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/crc-itu-t.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/lockdep.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+/* Max channels supported by the largest variant in the family (ADS131M08) */
+#define ADS131M_MAX_CHANNELS 8
+
+/* Section 6.7, t_REGACQ (min time after reset) is 5us */
+#define ADS131M_RESET_DELAY_US 5
+
+#define ADS131M_WORD_SIZE_BYTES 3
+#define ADS131M_RESPONSE_WORDS 1
+#define ADS131M_CRC_WORDS 1
+
+/*
+ * SPI Frame word count calculation.
+ * Frame = N channel words + 1 response word + 1 CRC word.
+ * Word size depends on WLENGTH bits in MODE register (Default 24-bit).
+ */
+#define ADS131M_FRAME_WORDS(nch) \
+ ((nch) + ADS131M_RESPONSE_WORDS + ADS131M_CRC_WORDS)
+
+/*
+ * SPI Frame byte size calculation.
+ * Assumes default word size of 24 bits (3 bytes).
+ */
+#define ADS131M_FRAME_BYTES(nch) \
+ (ADS131M_FRAME_WORDS(nch) * ADS131M_WORD_SIZE_BYTES)
+
+/*
+ * Index calculation for the start byte of channel 'x' data within the RX buffer.
+ * Assumes 24-bit words (3 bytes per word).
+ * The received frame starts with the response word (e.g., STATUS register
+ * content when NULL command was sent), followed by data for channels 0 to N-1,
+ * and finally the output CRC word.
+ * Response = index 0..2, Chan0 = index 3..5, Chan1 = index 6..8, ...
+ * Index for ChanX = 3 (response) + x * 3 (channel data size).
+ */
+#define ADS131M_CHANNEL_INDEX(x) \
+ ((x) * ADS131M_WORD_SIZE_BYTES + ADS131M_WORD_SIZE_BYTES)
+
+#define ADS131M_CMD_NULL 0x0000
+#define ADS131M_CMD_RESET 0x0011
+
+#define ADS131M_CMD_ADDR_MASK GENMASK(11, 7)
+#define ADS131M_CMD_NUM_MASK GENMASK(6, 0)
+
+#define ADS131M_CMD_RREG_OP 0xa000
+#define ADS131M_CMD_WREG_OP 0x6000
+
+#define ADS131M_CMD_RREG(a, n) \
+ (ADS131M_CMD_RREG_OP | \
+ FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \
+ FIELD_PREP(ADS131M_CMD_NUM_MASK, n))
+#define ADS131M_CMD_WREG(a, n) \
+ (ADS131M_CMD_WREG_OP | \
+ FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \
+ FIELD_PREP(ADS131M_CMD_NUM_MASK, n))
+
+/* STATUS Register (0x01h) bit definitions */
+#define ADS131M_STATUS_CRC_ERR BIT(12) /* Input CRC error */
+
+#define ADS131M_REG_MODE 0x02
+#define ADS131M_MODE_RX_CRC_EN BIT(12) /* Enable Input CRC */
+#define ADS131M_MODE_CRC_TYPE_ANSI BIT(11) /* 0 = CCITT, 1 = ANSI */
+#define ADS131M_MODE_RESET_FLAG BIT(10)
+
+#define ADS131M_REG_CLOCK 0x03
+#define ADS131M_CLOCK_XTAL_DIS BIT(7)
+#define ADS131M_CLOCK_EXTREF_EN BIT(6)
+
+/* 1.2V internal reference, in millivolts, for IIO_VAL_FRACTIONAL_LOG2 */
+#define ADS131M_VREF_INTERNAL_mV 1200
+/* 24-bit resolution */
+#define ADS131M_RESOLUTION_BITS 24
+/* Signed data uses (RESOLUTION_BITS - 1) magnitude bits */
+#define ADS131M_CODE_BITS (ADS131M_RESOLUTION_BITS - 1)
+
+/* External ref FSR = Vref * 0.96 */
+#define ADS131M_EXTREF_SCALE_NUM 96
+#define ADS131M_EXTREF_SCALE_DEN 100
+
+struct ads131m_configuration {
+ const struct iio_chan_spec *channels;
+ const char *name;
+ u16 reset_ack;
+ u8 num_channels;
+ u8 supports_extref:1;
+ u8 supports_xtal:1;
+};
+
+struct ads131m_priv {
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ const struct ads131m_configuration *config;
+
+ bool use_external_ref;
+ int scale_val;
+ int scale_val2;
+
+ struct spi_transfer xfer;
+ struct spi_message msg;
+
+ /*
+ * Protects the shared tx_buffer and rx_buffer. More importantly,
+ * this serializes all SPI communication to ensure the atomicity
+ * of multi-cycle command sequences (like WREG, RREG, or RESET).
+ */
+ struct mutex lock;
+
+ /* DMA-safe buffers should be placed at the end of the struct. */
+ u8 tx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)]
+ __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)];
+};
+
+/**
+ * ads131m_tx_frame_unlocked - Sends a command frame with Input CRC
+ * @priv: Device private data structure.
+ * @command: The 16-bit command to send (e.g., NULL, RREG, RESET).
+ *
+ * This function sends a command in Word 0, and its calculated 16-bit
+ * CRC in Word 1, as required when Input CRC is enabled.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_tx_frame_unlocked(struct ads131m_priv *priv, u32 command)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ u16 crc;
+
+ lockdep_assert_held(&priv->lock);
+
+ memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels));
+
+ /* Word 0: 16-bit command, MSB-aligned in 24-bit word */
+ put_unaligned_be16(command, &priv->tx_buffer[0]);
+
+ /* Word 1: Input CRC. Calculated over the 3 bytes of Word 0. */
+ crc = crc_itu_t(0xffff, priv->tx_buffer, 3);
+ put_unaligned_be16(crc, &priv->tx_buffer[3]);
+
+ return spi_sync(priv->spi, &priv->msg);
+}
+
+/**
+ * ads131m_rx_frame_unlocked - Receives a full SPI data frame.
+ * @priv: Device private data structure.
+ *
+ * This function sends a NULL command (with its CRC) to clock out a
+ * full SPI frame from the device (e.g., response + channel data + CRC).
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_rx_frame_unlocked(struct ads131m_priv *priv)
+{
+ return ads131m_tx_frame_unlocked(priv, ADS131M_CMD_NULL);
+}
+
+/**
+ * ads131m_check_status_crc_err - Checks for an Input CRC error.
+ * @priv: Device private data structure.
+ *
+ * Sends a NULL command to fetch the STATUS register and checks the
+ * CRC_ERR bit. This is used to verify the integrity of the previous
+ * command (like RREG or WREG).
+ *
+ * Return: 0 on success, -EIO if CRC_ERR bit is set.
+ */
+static int ads131m_check_status_crc_err(struct ads131m_priv *priv)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 status;
+ int ret;
+
+ lockdep_assert_held(&priv->lock);
+
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0) {
+ dev_err_ratelimited(dev,
+ "SPI error on STATUS read for CRC check\n");
+ return ret;
+ }
+
+ status = get_unaligned_be16(&priv->rx_buffer[0]);
+ if (status & ADS131M_STATUS_CRC_ERR) {
+ dev_err_ratelimited(dev,
+ "Input CRC error reported in STATUS = 0x%04x\n",
+ status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ads131m_write_reg_unlocked - Writes a single register and verifies the ACK.
+ * @priv: Device private data structure.
+ * @reg: The 8-bit register address.
+ * @val: The 16-bit value to write.
+ *
+ * This function performs the full 3-cycle WREG operation with Input CRC:
+ * 1. (Cycle 1) Sends WREG command, data, and its calculated CRC.
+ * 2. (Cycle 2) Sends NULL+CRC to retrieve the response from Cycle 1.
+ * 3. Verifies the response is the correct ACK for the WREG.
+ * 4. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_write_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 val)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ u16 command, expected_ack, response, crc;
+ struct device *dev = &priv->spi->dev;
+ int ret_crc_err = 0;
+ int ret;
+
+ lockdep_assert_held(&priv->lock);
+
+ command = ADS131M_CMD_WREG(reg, 0); /* n = 0 for 1 register */
+ /*
+ * Per Table 8-11, WREG response is: 010a aaaa ammm mmmm
+ * For 1 reg (n = 0 -> m = 0): 010a aaaa a000 0000 = 0x4000 | (reg << 7)
+ */
+ expected_ack = 0x4000 | (reg << 7);
+
+ /* Cycle 1: Send WREG Command + Data + Input CRC */
+
+ memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels));
+
+ /* Word 0: WREG command, 1 reg (n = 0), MSB-aligned */
+ put_unaligned_be16(command, &priv->tx_buffer[0]);
+
+ /* Word 1: Data, MSB-aligned */
+ put_unaligned_be16(val, &priv->tx_buffer[3]);
+
+ /* Word 2: Input CRC. Calculated over Word 0 (Cmd) and Word 1 (Data). */
+ crc = crc_itu_t(0xffff, priv->tx_buffer, 6);
+ put_unaligned_be16(crc, &priv->tx_buffer[6]);
+
+ /* Ignore the RX buffer (it's from the previous command) */
+ ret = spi_sync(priv->spi, &priv->msg);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on WREG (cycle 1)\n");
+ return ret;
+ }
+
+ /* Cycle 2: Send NULL Command to get the WREG response */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on WREG ACK (cycle 2)\n");
+ return ret;
+ }
+
+ /*
+ * Response is in the first 2 bytes of the RX buffer
+ * (MSB-aligned 16-bit response)
+ */
+ response = get_unaligned_be16(&priv->rx_buffer[0]);
+ if (response != expected_ack) {
+ dev_err_ratelimited(dev, "WREG(0x%02x) failed, expected ACK 0x%04x, got 0x%04x\n",
+ reg, expected_ack, response);
+ ret_crc_err = -EIO;
+ /*
+ * Don't return yet, still need to do Cycle 3 to clear
+ * any potential CRC_ERR flag from this failed command.
+ */
+ }
+
+ /*
+ * Cycle 3: Check STATUS for Input CRC error.
+ * This is necessary even if ACK was wrong, to clear the CRC_ERR flag.
+ */
+ ret = ads131m_check_status_crc_err(priv);
+ if (ret < 0)
+ return ret;
+
+ return ret_crc_err;
+}
+
+/**
+ * ads131m_read_reg_unlocked - Reads a single register from the device.
+ * @priv: Device private data structure.
+ * @reg: The 8-bit register address.
+ * @val: Pointer to store the 16-bit register value.
+ *
+ * This function performs the full 3-cycle RREG operation with Input CRC:
+ * 1. (Cycle 1) Sends the RREG command + Input CRC.
+ * 2. (Cycle 2) Sends NULL+CRC to retrieve the register data.
+ * 3. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_read_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 *val)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 command;
+ int ret;
+
+ lockdep_assert_held(&priv->lock);
+
+ command = ADS131M_CMD_RREG(reg, 0); /* n=0 for 1 register */
+
+ /*
+ * Cycle 1: Send RREG Command + Input CRC
+ * Ignore the RX buffer (it's from the previous command)
+ */
+ ret = ads131m_tx_frame_unlocked(priv, command);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on RREG (cycle 1)\n");
+ return ret;
+ }
+
+ /* Cycle 2: Send NULL Command to get the register data */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0) {
+ dev_err_ratelimited(dev, "SPI error on RREG data (cycle 2)\n");
+ return ret;
+ }
+
+ /*
+ * Per datasheet, for a single reg read, the response is the data.
+ * It's in the first 2 bytes of the RX buffer (MSB-aligned 16-bit).
+ */
+ *val = get_unaligned_be16(&priv->rx_buffer[0]);
+
+ /*
+ * Cycle 3: Check STATUS for Input CRC error.
+ * The RREG command does not execute if CRC is bad, but we read
+ * STATUS anyway to clear the flag in case it was set.
+ */
+ return ads131m_check_status_crc_err(priv);
+}
+
+/**
+ * ads131m_rmw_reg - Reads, modifies, and writes a single register.
+ * @priv: Device private data structure.
+ * @reg: The 8-bit register address.
+ * @clear: Bitmask of bits to clear.
+ * @set: Bitmask of bits to set.
+ *
+ * This function performs an atomic read-modify-write operation on a register.
+ * It reads the register, applies the clear and set masks, and writes
+ * the new value back if it has changed.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_rmw_reg(struct ads131m_priv *priv, u8 reg, u16 clear, u16 set)
+{
+ u16 old_val, new_val;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ ret = ads131m_read_reg_unlocked(priv, reg, &old_val);
+ if (ret < 0)
+ return ret;
+
+ new_val = (old_val & ~clear) | set;
+ if (new_val == old_val)
+ return 0;
+
+ return ads131m_write_reg_unlocked(priv, reg, new_val);
+}
+
+/**
+ * ads131m_verify_output_crc - Verifies the CRC of the received SPI frame.
+ * @priv: Device private data structure.
+ *
+ * This function calculates the CRC-16-CCITT (Poly 0x1021, Seed 0xFFFF) over
+ * the received response and channel data, and compares it to the CRC word
+ * received at the end of the SPI frame.
+ *
+ * Return: 0 on success, -EIO on CRC mismatch.
+ */
+static int ads131m_verify_output_crc(struct ads131m_priv *priv)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ struct device *dev = &priv->spi->dev;
+ u16 calculated_crc, received_crc;
+ size_t data_len;
+
+ lockdep_assert_held(&priv->lock);
+
+ /*
+ * Frame: [Response][Chan 0]...[Chan N-1][CRC Word]
+ * Data for CRC: [Response][Chan 0]...[Chan N-1]
+ * Data length = (N_channels + 1) * 3 bytes (at 24-bit word size)
+ */
+ data_len = ADS131M_FRAME_BYTES(indio_dev->num_channels) - 3;
+ calculated_crc = crc_itu_t(0xffff, priv->rx_buffer, data_len);
+
+ /*
+ * The received 16-bit CRC is MSB-aligned in the last 24-bit word.
+ * We extract it from the first 2 bytes (BE) of that word.
+ */
+ received_crc = get_unaligned_be16(&priv->rx_buffer[data_len]);
+ if (calculated_crc != received_crc) {
+ dev_err_ratelimited(dev, "Output CRC error. Got %04x, expected %04x\n",
+ received_crc, calculated_crc);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * ads131m_adc_read - Reads channel data, checks input and output CRCs.
+ * @priv: Device private data structure.
+ * @channel: The channel number to read.
+ * @val: Pointer to store the raw 24-bit value.
+ *
+ * This function sends a NULL command (with Input CRC) to retrieve data.
+ * It checks the received STATUS word for any Input CRC errors from the
+ * previous command, and then verifies the Output CRC of the current
+ * data frame.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_adc_read(struct ads131m_priv *priv, u8 channel, s32 *val)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 status;
+ int ret;
+ u8 *buf;
+
+ guard(mutex)(&priv->lock);
+
+ /* Send NULL command + Input CRC, and receive data frame */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Check STATUS for Input CRC error from the previous command frame.
+ * Note: the STATUS word belongs to the frame before this NULL command.
+ */
+ status = get_unaligned_be16(&priv->rx_buffer[0]);
+ if (status & ADS131M_STATUS_CRC_ERR) {
+ dev_err_ratelimited(dev,
+ "Previous input CRC error reported in STATUS (0x%04x)\n",
+ status);
+ }
+
+ ret = ads131m_verify_output_crc(priv);
+ if (ret < 0)
+ return ret;
+
+ buf = &priv->rx_buffer[ADS131M_CHANNEL_INDEX(channel)];
+ *val = sign_extend32(get_unaligned_be24(buf), ADS131M_CODE_BITS);
+
+ return 0;
+}
+
+static int ads131m_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel,
+ int *val, int *val2, long mask)
+{
+ struct ads131m_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads131m_adc_read(priv, channel->channel, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = priv->scale_val;
+ *val2 = priv->scale_val2;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define ADS131M_VOLTAGE_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .differential = 1, \
+ .indexed = 1, \
+ .channel = (num), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ }
+
+static const struct iio_chan_spec ads131m02_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+};
+
+static const struct iio_chan_spec ads131m03_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+};
+
+static const struct iio_chan_spec ads131m04_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+ ADS131M_VOLTAGE_CHANNEL(3),
+};
+
+static const struct iio_chan_spec ads131m06_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+ ADS131M_VOLTAGE_CHANNEL(3),
+ ADS131M_VOLTAGE_CHANNEL(4),
+ ADS131M_VOLTAGE_CHANNEL(5),
+};
+
+static const struct iio_chan_spec ads131m08_channels[] = {
+ ADS131M_VOLTAGE_CHANNEL(0),
+ ADS131M_VOLTAGE_CHANNEL(1),
+ ADS131M_VOLTAGE_CHANNEL(2),
+ ADS131M_VOLTAGE_CHANNEL(3),
+ ADS131M_VOLTAGE_CHANNEL(4),
+ ADS131M_VOLTAGE_CHANNEL(5),
+ ADS131M_VOLTAGE_CHANNEL(6),
+ ADS131M_VOLTAGE_CHANNEL(7),
+};
+
+static const struct ads131m_configuration ads131m02_config = {
+ .channels = ads131m02_channels,
+ .num_channels = ARRAY_SIZE(ads131m02_channels),
+ .reset_ack = 0xff22,
+ .name = "ads131m02",
+};
+
+static const struct ads131m_configuration ads131m03_config = {
+ .channels = ads131m03_channels,
+ .num_channels = ARRAY_SIZE(ads131m03_channels),
+ .reset_ack = 0xff23,
+ .name = "ads131m03",
+};
+
+static const struct ads131m_configuration ads131m04_config = {
+ .channels = ads131m04_channels,
+ .num_channels = ARRAY_SIZE(ads131m04_channels),
+ .reset_ack = 0xff24,
+ .name = "ads131m04",
+};
+
+static const struct ads131m_configuration ads131m06_config = {
+ .channels = ads131m06_channels,
+ .num_channels = ARRAY_SIZE(ads131m06_channels),
+ .reset_ack = 0xff26,
+ .supports_extref = true,
+ .supports_xtal = true,
+ .name = "ads131m06",
+};
+
+static const struct ads131m_configuration ads131m08_config = {
+ .channels = ads131m08_channels,
+ .num_channels = ARRAY_SIZE(ads131m08_channels),
+ .reset_ack = 0xff28,
+ .supports_extref = true,
+ .supports_xtal = true,
+ .name = "ads131m08",
+};
+
+static const struct iio_info ads131m_info = {
+ .read_raw = ads131m_read_raw,
+};
+
+/*
+ * Prepares the reusable SPI message structure for a full-duplex transfer.
+ * The ADS131M requires sending a command frame while simultaneously
+ * receiving the response/data frame from the previous command cycle.
+ *
+ * This message is optimized for the primary data acquisition workflow:
+ * sending a single-word command (like NULL) and receiving a full data
+ * frame (Response + N*Channels + CRC).
+ *
+ * This message is sized for a full data frame and is reused for all
+ * command/data cycles. The driver does not implement variable-length SPI
+ * messages.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_prepare_message(struct ads131m_priv *priv)
+{
+ struct iio_dev *indio_dev = priv->indio_dev;
+ struct device *dev = &priv->spi->dev;
+ int ret;
+
+ priv->xfer.tx_buf = priv->tx_buffer;
+ priv->xfer.rx_buf = priv->rx_buffer;
+ priv->xfer.len = ADS131M_FRAME_BYTES(indio_dev->num_channels);
+ spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
+
+ ret = devm_spi_optimize_message(dev, priv->spi, &priv->msg);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
+
+ return 0;
+}
+
+/**
+ * ads131m_hw_reset - Pulses the optional hardware reset.
+ * @priv: Device private data structure.
+ * @rstc: Reset control for the /RESET line.
+ *
+ * Pulses the /RESET line to perform a hardware reset and waits the
+ * required t_REGACQ time for the device to be ready.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_hw_reset(struct ads131m_priv *priv,
+ struct reset_control *rstc)
+{
+ struct device *dev = &priv->spi->dev;
+ int ret;
+
+ /*
+ * Manually pulse the reset line using the framework.
+ * The reset-gpio provider does not implement the .reset op,
+ * so we must use .assert and .deassert.
+ */
+ ret = reset_control_assert(rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to assert reset\n");
+
+ /* Datasheet: Hold /RESET low for > 2 f_CLKIN cycles. 1us is ample. */
+ fsleep(1);
+
+ ret = reset_control_deassert(rstc);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to deassert reset\n");
+
+ /* Wait t_REGACQ (5us) for registers to be accessible */
+ fsleep(ADS131M_RESET_DELAY_US);
+
+ return 0;
+}
+
+/**
+ * ads131m_sw_reset - Issues a software RESET and verifies ACK.
+ * @priv: Device private data structure.
+ *
+ * This function sends a RESET command (with Input CRC), waits t_REGACQ,
+ * reads back the RESET ACK, and then sends a final NULL to check for
+ * any input CRC errors.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_sw_reset(struct ads131m_priv *priv)
+{
+ u16 expected_ack = priv->config->reset_ack;
+ struct device *dev = &priv->spi->dev;
+ u16 response;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ ret = ads131m_tx_frame_unlocked(priv, ADS131M_CMD_RESET);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to send RESET command\n");
+
+ /* Wait t_REGACQ (5us) for device to be ready after reset */
+ fsleep(ADS131M_RESET_DELAY_US);
+
+ /* Cycle 2: Send NULL + CRC to retrieve the response to the RESET */
+ ret = ads131m_rx_frame_unlocked(priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read RESET ACK\n");
+
+ response = get_unaligned_be16(&priv->rx_buffer[0]);
+
+ /* Check against the device-specific ACK value */
+ if (response != expected_ack)
+ return dev_err_probe(dev, -EIO,
+ "RESET ACK mismatch, got 0x%04x, expected 0x%04x\n",
+ response, expected_ack);
+
+ /* Cycle 3: Check STATUS for Input CRC error on the RESET command. */
+ return ads131m_check_status_crc_err(priv);
+}
+
+/**
+ * ads131m_reset - Resets the device using hardware or software.
+ * @priv: Device private data structure.
+ * @rstc: Optional reset control, or NULL for software reset.
+ *
+ * This function performs a hardware reset if supported (rstc provided),
+ * otherwise it issues a software RESET command via SPI.
+ *
+ * Note: The software reset path also validates the device's reset
+ * acknowledgment against the expected ID for the compatible string.
+ * The hardware reset path bypasses this ID check.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_reset(struct ads131m_priv *priv, struct reset_control *rstc)
+{
+ if (rstc)
+ return ads131m_hw_reset(priv, rstc);
+
+ return ads131m_sw_reset(priv);
+}
+
+static int ads131m_power_init(struct ads131m_priv *priv)
+{
+ static const char * const supply_ids[] = { "avdd", "dvdd" };
+ struct device *dev = &priv->spi->dev;
+ int vref_uV;
+ int ret;
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supply_ids), supply_ids);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to enable regulators\n");
+
+ /* Default to Internal 1.2V reference: 1200mV / 2^23 */
+ priv->scale_val = ADS131M_VREF_INTERNAL_mV;
+ priv->scale_val2 = BIT(ADS131M_CODE_BITS);
+
+ if (!priv->config->supports_extref)
+ return 0;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "failed to get refin supply\n");
+
+ if (ret == 0)
+ return dev_err_probe(dev, -EINVAL, "refin supply reports 0V\n");
+
+ if (ret == -ENODEV)
+ return 0;
+
+ vref_uV = ret;
+
+ /*
+ * External reference found: Scale(mV) = (vref_uV * 0.96) / 1000
+ * The denominator is 100 * 2^23 because of the 0.96 factor (96/100).
+ */
+ priv->scale_val = div_s64((s64)vref_uV * ADS131M_EXTREF_SCALE_NUM, 1000);
+ priv->scale_val2 = ADS131M_EXTREF_SCALE_DEN * BIT(ADS131M_CODE_BITS);
+ priv->use_external_ref = true;
+
+ return 0;
+}
+
+/**
+ * ads131m_hw_init - Initialize the ADC hardware.
+ * @priv: Device private data structure.
+ * @rstc: Optional reset control, or NULL for software reset.
+ * @is_xtal: True if 'clock-names' is "xtal", false if "clkin".
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_hw_init(struct ads131m_priv *priv,
+ struct reset_control *rstc, bool is_xtal)
+{
+ struct device *dev = &priv->spi->dev;
+ u16 mode_clear, mode_set;
+ int ret;
+
+ ret = ads131m_reset(priv, rstc);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Configure CLOCK register (0x03) based on DT properties.
+ * This register only needs configuration for 32-pin (M06/M08)
+ * variants, as the configurable bits (XTAL_DIS, EXTREF_EN)
+ * are reserved on 20-pin (M02/M03/M04) variants.
+ */
+ if (priv->config->supports_xtal || priv->config->supports_extref) {
+ u16 clk_set = 0;
+
+ if (priv->config->supports_xtal && !is_xtal)
+ clk_set |= ADS131M_CLOCK_XTAL_DIS;
+
+ if (priv->config->supports_extref && priv->use_external_ref)
+ clk_set |= ADS131M_CLOCK_EXTREF_EN;
+
+ ret = ads131m_rmw_reg(priv, ADS131M_REG_CLOCK,
+ ADS131M_CLOCK_EXTREF_EN | ADS131M_CLOCK_XTAL_DIS,
+ clk_set);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to configure CLOCK register\n");
+ }
+
+ /*
+ * The RESET command sets all registers to default, which means:
+ * 1. The RESET bit (Bit 10) in MODE is set to '1'.
+ * 2. The CRC_TYPE bit (Bit 11) in MODE is '0' (CCITT).
+ * 3. The RX_CRC_EN bit (Bit 12) in MODE is '0' (Disabled).
+ *
+ * We must:
+ * 1. Clear the RESET bit.
+ * 2. Enable Input CRC (RX_CRC_EN).
+ * 3. Explicitly clear the ANSI CRC bit (for certainty).
+ */
+ mode_clear = ADS131M_MODE_CRC_TYPE_ANSI | ADS131M_MODE_RESET_FLAG;
+ mode_set = ADS131M_MODE_RX_CRC_EN;
+
+ ret = ads131m_rmw_reg(priv, ADS131M_REG_MODE, mode_clear, mode_set);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to configure MODE register\n");
+
+ return 0;
+}
+
+/**
+ * ads131m_parse_clock - enable clock and detect "xtal" selection
+ * @priv: Device private data structure.
+ * @is_xtal: result flag (true if "xtal", false if default "clkin")
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+static int ads131m_parse_clock(struct ads131m_priv *priv, bool *is_xtal)
+{
+ struct device *dev = &priv->spi->dev;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR_OR_NULL(clk)) {
+ if (IS_ERR(clk))
+ ret = PTR_ERR(clk);
+ else
+ ret = -ENODEV;
+
+ return dev_err_probe(dev, ret, "clk get enabled failed\n");
+ }
+
+ ret = device_property_match_string(dev, "clock-names", "xtal");
+ if (ret > 0)
+ return dev_err_probe(dev, -EINVAL,
+ "'xtal' must be the only or first clock name");
+
+ if (ret < 0 && ret != -ENODATA)
+ return dev_err_probe(dev, ret,
+ "failed to read 'clock-names' property");
+
+ if (ret == 0 && !priv->config->supports_xtal)
+ return dev_err_probe(dev, -EINVAL,
+ "'xtal' clock not supported on this device");
+
+ *is_xtal = !ret;
+
+ return 0;
+}
+
+static int ads131m_probe(struct spi_device *spi)
+{
+ const struct ads131m_configuration *config;
+ struct device *dev = &spi->dev;
+ struct reset_control *rstc;
+ struct iio_dev *indio_dev;
+ struct ads131m_priv *priv;
+ bool is_xtal;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->indio_dev = indio_dev;
+ priv->spi = spi;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads131m_info;
+
+ config = spi_get_device_match_data(spi);
+
+ priv->config = config;
+ indio_dev->name = config->name;
+ indio_dev->channels = config->channels;
+ indio_dev->num_channels = config->num_channels;
+
+ rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "Failed to get reset controller\n");
+
+ ret = devm_mutex_init(dev, &priv->lock);
+ if (ret < 0)
+ return ret;
+
+ ret = ads131m_prepare_message(priv);
+ if (ret < 0)
+ return ret;
+
+ ret = ads131m_power_init(priv);
+ if (ret < 0)
+ return ret;
+
+ /* Power must be applied and stable before the clock is enabled. */
+ ret = ads131m_parse_clock(priv, &is_xtal);
+ if (ret < 0)
+ return ret;
+
+ ret = ads131m_hw_init(priv, rstc, is_xtal);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ads131m_of_match[] = {
+ { .compatible = "ti,ads131m02", .data = &ads131m02_config },
+ { .compatible = "ti,ads131m03", .data = &ads131m03_config },
+ { .compatible = "ti,ads131m04", .data = &ads131m04_config },
+ { .compatible = "ti,ads131m06", .data = &ads131m06_config },
+ { .compatible = "ti,ads131m08", .data = &ads131m08_config },
+ { }
+};
+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 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads131m_id);
+
+static struct spi_driver ads131m_driver = {
+ .driver = {
+ .name = "ads131m02",
+ .of_match_table = ads131m_of_match,
+ },
+ .probe = ads131m_probe,
+ .id_table = ads131m_id,
+};
+module_spi_driver(ads131m_driver);
+
+MODULE_AUTHOR("David Jander <david@protonic.nl>");
+MODULE_DESCRIPTION("Texas Instruments ADS131M02 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig
index 55eb16b32f6c..a8a604863eed 100644
--- a/drivers/iio/amplifiers/Kconfig
+++ b/drivers/iio/amplifiers/Kconfig
@@ -36,6 +36,18 @@ config ADA4250
To compile this driver as a module, choose M here: the
module will be called ada4250.
+config ADL8113
+ tristate "Analog Devices ADL8113 Low Noise Amplifier"
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices ADL8113 Low Noise
+ Amplifier with integrated bypass switches. The device supports four
+ operation modes controlled by GPIO pins: internal amplifier,
+ internal bypass, and two external bypass modes.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adl8113.
+
config HMC425
tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers"
depends on GPIOLIB
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile
index 2126331129cf..0a76443be1aa 100644
--- a/drivers/iio/amplifiers/Makefile
+++ b/drivers/iio/amplifiers/Makefile
@@ -6,4 +6,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD8366) += ad8366.o
obj-$(CONFIG_ADA4250) += ada4250.o
+obj-$(CONFIG_ADL8113) += adl8113.o
obj-$(CONFIG_HMC425) += hmc425a.o
diff --git a/drivers/iio/amplifiers/adl8113.c b/drivers/iio/amplifiers/adl8113.c
new file mode 100644
index 000000000000..b8a431b6616b
--- /dev/null
+++ b/drivers/iio/amplifiers/adl8113.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADL8113 Low Noise Amplifier with integrated bypass switches
+ *
+ * Copyright 2025 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitmap.h>
+#include <linux/device/driver.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+enum adl8113_signal_path {
+ ADL8113_INTERNAL_AMP,
+ ADL8113_INTERNAL_BYPASS,
+ ADL8113_EXTERNAL_A,
+ ADL8113_EXTERNAL_B,
+};
+
+struct adl8113_gain_config {
+ enum adl8113_signal_path path;
+ int gain_db;
+};
+
+struct adl8113_state {
+ struct gpio_descs *gpios;
+ struct adl8113_gain_config *gain_configs;
+ unsigned int num_gain_configs;
+ enum adl8113_signal_path current_path;
+};
+
+static const char * const adl8113_supply_names[] = {
+ "vdd1",
+ "vss2",
+ "vdd2",
+};
+
+static int adl8113_set_path(struct adl8113_state *st,
+ enum adl8113_signal_path path)
+{
+ DECLARE_BITMAP(values, 2);
+ int ret;
+
+ /*
+ * Determine GPIO values based on signal path.
+ * Va: bit 0, Vb: bit 1.
+ */
+ switch (path) {
+ case ADL8113_INTERNAL_AMP:
+ bitmap_write(values, 0x00, 0, 2);
+ break;
+ case ADL8113_INTERNAL_BYPASS:
+ bitmap_write(values, 0x03, 0, 2);
+ break;
+ case ADL8113_EXTERNAL_A:
+ bitmap_write(values, 0x02, 0, 2);
+ break;
+ case ADL8113_EXTERNAL_B:
+ bitmap_write(values, 0x01, 0, 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
+ st->gpios->info, values);
+ if (ret)
+ return ret;
+
+ st->current_path = path;
+ return 0;
+}
+
+static int adl8113_find_gain_config(struct adl8113_state *st, int gain_db)
+{
+ unsigned int i;
+
+ for (i = 0; i < st->num_gain_configs; i++) {
+ if (st->gain_configs[i].gain_db == gain_db)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adl8113_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
+ },
+};
+
+static int adl8113_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct adl8113_state *st = iio_priv(indio_dev);
+ unsigned int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ /* Find current gain configuration */
+ for (i = 0; i < st->num_gain_configs; i++) {
+ if (st->gain_configs[i].path == st->current_path) {
+ *val = st->gain_configs[i].gain_db;
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO_DB;
+ }
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adl8113_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct adl8113_state *st = iio_priv(indio_dev);
+ int config_idx;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ if (val2 != 0)
+ return -EINVAL;
+
+ config_idx = adl8113_find_gain_config(st, val);
+ if (config_idx < 0)
+ return config_idx;
+
+ return adl8113_set_path(st, st->gain_configs[config_idx].path);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info adl8113_info = {
+ .read_raw = adl8113_read_raw,
+ .write_raw = adl8113_write_raw,
+};
+
+static int adl8113_init_gain_configs(struct device *dev, struct adl8113_state *st)
+{
+ int external_a_gain, external_b_gain;
+ unsigned int i;
+
+ /*
+ * Allocate for all 4 possible paths:
+ * - Internal amp and bypass (always present)
+ * - External bypass A and B (optional if configured)
+ */
+ st->gain_configs = devm_kcalloc(dev, 4, sizeof(*st->gain_configs),
+ GFP_KERNEL);
+ if (!st->gain_configs)
+ return -ENOMEM;
+
+ /* Start filling the gain configurations with data */
+ i = 0;
+
+ /* Always include internal amplifier (14dB) */
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_INTERNAL_AMP,
+ .gain_db = 14,
+ };
+
+ /* Always include internal bypass (-2dB insertion loss) */
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_INTERNAL_BYPASS,
+ .gain_db = -2,
+ };
+
+ /* Add external bypass A if configured */
+ if (!device_property_read_u32(dev, "adi,external-bypass-a-gain-db",
+ &external_a_gain)) {
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_EXTERNAL_A,
+ .gain_db = external_a_gain,
+ };
+ }
+
+ /* Add external bypass B if configured */
+ if (!device_property_read_u32(dev, "adi,external-bypass-b-gain-db",
+ &external_b_gain)) {
+ st->gain_configs[i++] = (struct adl8113_gain_config) {
+ .path = ADL8113_EXTERNAL_B,
+ .gain_db = external_b_gain,
+ };
+ }
+
+ st->num_gain_configs = i;
+
+ return 0;
+}
+
+static int adl8113_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct adl8113_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->gpios = devm_gpiod_get_array(dev, "ctrl", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpios))
+ return dev_err_probe(dev, PTR_ERR(st->gpios),
+ "failed to get control GPIOs\n");
+
+ if (st->gpios->ndescs != 2)
+ return dev_err_probe(dev, -EINVAL,
+ "expected 2 control GPIOs, got %u\n",
+ st->gpios->ndescs);
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(adl8113_supply_names),
+ adl8113_supply_names);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable supplies\n");
+
+ /* Initialize gain configurations from devicetree */
+ ret = adl8113_init_gain_configs(dev, st);
+ if (ret)
+ return ret;
+
+ /* Initialize to internal amplifier path (14dB) */
+ ret = adl8113_set_path(st, ADL8113_INTERNAL_AMP);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &adl8113_info;
+ indio_dev->name = "adl8113";
+ indio_dev->channels = adl8113_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adl8113_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id adl8113_of_match[] = {
+ { .compatible = "adi,adl8113" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adl8113_of_match);
+
+static struct platform_driver adl8113_driver = {
+ .driver = {
+ .name = "adl8113",
+ .of_match_table = adl8113_of_match,
+ },
+ .probe = adl8113_probe,
+};
+module_platform_driver(adl8113_driver);
+
+MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADL8113 Low Noise Amplifier");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 7a7a9d37339b..1c94b334f987 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -6,6 +6,7 @@
#include <linux/atomic.h>
#include <linux/cleanup.h>
+#include <linux/lockdep.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -135,9 +136,8 @@ static void iio_dma_buffer_cleanup_worker(struct work_struct *work)
struct iio_dma_buffer_block *block, *_block;
LIST_HEAD(block_list);
- spin_lock_irq(&iio_dma_buffer_dead_blocks_lock);
- list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
- spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock);
+ scoped_guard(spinlock_irq, &iio_dma_buffer_dead_blocks_lock)
+ list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
list_for_each_entry_safe(block, _block, &block_list, head)
iio_buffer_block_release(&block->kref);
@@ -147,13 +147,11 @@ static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker);
static void iio_buffer_block_release_atomic(struct kref *kref)
{
struct iio_dma_buffer_block *block;
- unsigned long flags;
block = container_of(kref, struct iio_dma_buffer_block, kref);
- spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags);
- list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
- spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags);
+ scoped_guard(spinlock_irqsave, &iio_dma_buffer_dead_blocks_lock)
+ list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
schedule_work(&iio_dma_buffer_cleanup_work);
}
@@ -171,22 +169,20 @@ static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf)
return container_of(buf, struct iio_dma_buffer_queue, buffer);
}
-static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
- struct iio_dma_buffer_queue *queue, size_t size, bool fileio)
+static struct iio_dma_buffer_block *
+iio_dma_buffer_alloc_block(struct iio_dma_buffer_queue *queue, size_t size,
+ bool fileio)
{
- struct iio_dma_buffer_block *block;
-
- block = kzalloc(sizeof(*block), GFP_KERNEL);
+ struct iio_dma_buffer_block *block __free(kfree) =
+ kzalloc(sizeof(*block), GFP_KERNEL);
if (!block)
return NULL;
if (fileio) {
block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size),
&block->phys_addr, GFP_KERNEL);
- if (!block->vaddr) {
- kfree(block);
+ if (!block->vaddr)
return NULL;
- }
}
block->fileio = fileio;
@@ -201,7 +197,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
if (!fileio)
atomic_inc(&queue->num_dmabufs);
- return block;
+ return_ptr(block);
}
static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
@@ -232,14 +228,12 @@ static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
{
struct iio_dma_buffer_queue *queue = block->queue;
- unsigned long flags;
bool cookie;
cookie = dma_fence_begin_signalling();
- spin_lock_irqsave(&queue->list_lock, flags);
- _iio_dma_buffer_block_done(block);
- spin_unlock_irqrestore(&queue->list_lock, flags);
+ scoped_guard(spinlock_irqsave, &queue->list_lock)
+ _iio_dma_buffer_block_done(block);
if (!block->fileio)
iio_buffer_signal_dmabuf_done(block->fence, 0);
@@ -261,25 +255,25 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_block_done, "IIO_DMA_BUFFER");
* hand the blocks back to the queue.
*/
void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
- struct list_head *list)
+ struct list_head *list)
{
struct iio_dma_buffer_block *block, *_block;
- unsigned long flags;
bool cookie;
cookie = dma_fence_begin_signalling();
- spin_lock_irqsave(&queue->list_lock, flags);
- list_for_each_entry_safe(block, _block, list, head) {
- list_del(&block->head);
- block->bytes_used = 0;
- _iio_dma_buffer_block_done(block);
+ scoped_guard(spinlock_irqsave, &queue->list_lock) {
+ list_for_each_entry_safe(block, _block, list, head) {
+ list_del(&block->head);
+ block->bytes_used = 0;
+ _iio_dma_buffer_block_done(block);
- if (!block->fileio)
- iio_buffer_signal_dmabuf_done(block->fence, -EINTR);
- iio_buffer_block_put_atomic(block);
+ if (!block->fileio)
+ iio_buffer_signal_dmabuf_done(block->fence,
+ -EINTR);
+ iio_buffer_block_put_atomic(block);
+ }
}
- spin_unlock_irqrestore(&queue->list_lock, flags);
if (queue->fileio.enabled)
queue->fileio.enabled = false;
@@ -328,7 +322,6 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
struct iio_dma_buffer_block *block;
bool try_reuse = false;
size_t size;
- int ret = 0;
int i;
/*
@@ -339,13 +332,13 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
size = DIV_ROUND_UP(queue->buffer.bytes_per_datum *
queue->buffer.length, 2);
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
queue->fileio.enabled = iio_dma_buffer_can_use_fileio(queue);
/* If DMABUFs were created, disable fileio interface */
if (!queue->fileio.enabled)
- goto out_unlock;
+ return 0;
/* Allocations are page aligned */
if (PAGE_ALIGN(queue->fileio.block_size) == PAGE_ALIGN(size))
@@ -354,21 +347,21 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
queue->fileio.block_size = size;
queue->fileio.active_block = NULL;
- spin_lock_irq(&queue->list_lock);
- for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
- block = queue->fileio.blocks[i];
+ scoped_guard(spinlock_irq, &queue->list_lock) {
+ for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
+ block = queue->fileio.blocks[i];
- /* If we can't re-use it free it */
- if (block && (!iio_dma_block_reusable(block) || !try_reuse))
- block->state = IIO_BLOCK_STATE_DEAD;
- }
+ /* If we can't re-use it free it */
+ if (block && (!iio_dma_block_reusable(block) || !try_reuse))
+ block->state = IIO_BLOCK_STATE_DEAD;
+ }
- /*
- * At this point all blocks are either owned by the core or marked as
- * dead. This means we can reset the lists without having to fear
- * corrution.
- */
- spin_unlock_irq(&queue->list_lock);
+ /*
+ * At this point all blocks are either owned by the core or
+ * marked as dead. This means we can reset the lists without
+ * having to fear corruption.
+ */
+ }
INIT_LIST_HEAD(&queue->incoming);
@@ -388,10 +381,9 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
if (!block) {
block = iio_dma_buffer_alloc_block(queue, size, true);
- if (!block) {
- ret = -ENOMEM;
- goto out_unlock;
- }
+ if (!block)
+ return -ENOMEM;
+
queue->fileio.blocks[i] = block;
}
@@ -415,10 +407,7 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
}
}
-out_unlock:
- mutex_unlock(&queue->lock);
-
- return ret;
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_request_update, "IIO_DMA_BUFFER");
@@ -426,13 +415,13 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue)
{
unsigned int i;
- spin_lock_irq(&queue->list_lock);
- for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
- if (!queue->fileio.blocks[i])
- continue;
- queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
+ scoped_guard(spinlock_irq, &queue->list_lock) {
+ for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
+ if (!queue->fileio.blocks[i])
+ continue;
+ queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
+ }
}
- spin_unlock_irq(&queue->list_lock);
INIT_LIST_HEAD(&queue->incoming);
@@ -446,7 +435,7 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue)
}
static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block)
+ struct iio_dma_buffer_block *block)
{
int ret;
@@ -490,19 +479,17 @@ static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
*
* This will allocate the DMA buffers and start the DMA transfers.
*/
-int iio_dma_buffer_enable(struct iio_buffer *buffer,
- struct iio_dev *indio_dev)
+int iio_dma_buffer_enable(struct iio_buffer *buffer, struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block, *_block;
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
queue->active = true;
list_for_each_entry_safe(block, _block, &queue->incoming, head) {
list_del(&block->head);
iio_dma_buffer_submit_block(queue, block);
}
- mutex_unlock(&queue->lock);
return 0;
}
@@ -516,24 +503,22 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enable, "IIO_DMA_BUFFER");
* Needs to be called when the device that the buffer is attached to stops
* sampling. Typically should be the iio_buffer_access_ops disable callback.
*/
-int iio_dma_buffer_disable(struct iio_buffer *buffer,
- struct iio_dev *indio_dev)
+int iio_dma_buffer_disable(struct iio_buffer *buffer, struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
queue->active = false;
if (queue->ops && queue->ops->abort)
queue->ops->abort(queue);
- mutex_unlock(&queue->lock);
return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_disable, "IIO_DMA_BUFFER");
static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block)
+ struct iio_dma_buffer_block *block)
{
if (block->state == IIO_BLOCK_STATE_DEAD) {
iio_buffer_block_put(block);
@@ -545,25 +530,22 @@ static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
}
}
-static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
- struct iio_dma_buffer_queue *queue)
+static struct iio_dma_buffer_block *
+iio_dma_buffer_dequeue(struct iio_dma_buffer_queue *queue)
{
struct iio_dma_buffer_block *block;
unsigned int idx;
- spin_lock_irq(&queue->list_lock);
+ guard(spinlock_irq)(&queue->list_lock);
idx = queue->fileio.next_dequeue;
block = queue->fileio.blocks[idx];
- if (block->state == IIO_BLOCK_STATE_DONE) {
- idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks);
- queue->fileio.next_dequeue = idx;
- } else {
- block = NULL;
- }
+ if (block->state != IIO_BLOCK_STATE_DONE)
+ return NULL;
- spin_unlock_irq(&queue->list_lock);
+ idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks);
+ queue->fileio.next_dequeue = idx;
return block;
}
@@ -579,14 +561,13 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
if (n < buffer->bytes_per_datum)
return -EINVAL;
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
if (!queue->fileio.active_block) {
block = iio_dma_buffer_dequeue(queue);
- if (block == NULL) {
- ret = 0;
- goto out_unlock;
- }
+ if (!block)
+ return 0;
+
queue->fileio.pos = 0;
queue->fileio.active_block = block;
} else {
@@ -602,10 +583,8 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
ret = copy_from_user(addr, user_buffer, n);
else
ret = copy_to_user(user_buffer, addr, n);
- if (ret) {
- ret = -EFAULT;
- goto out_unlock;
- }
+ if (ret)
+ return -EFAULT;
queue->fileio.pos += n;
@@ -614,12 +593,7 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
iio_dma_buffer_enqueue(queue, block);
}
- ret = n;
-
-out_unlock:
- mutex_unlock(&queue->lock);
-
- return ret;
+ return n;
}
/**
@@ -677,23 +651,19 @@ size_t iio_dma_buffer_usage(struct iio_buffer *buf)
* but won't increase since all blocks are in use.
*/
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
if (queue->fileio.active_block)
data_available += queue->fileio.active_block->size;
- spin_lock_irq(&queue->list_lock);
+ guard(spinlock_irq)(&queue->list_lock);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
block = queue->fileio.blocks[i];
- if (block != queue->fileio.active_block
- && block->state == IIO_BLOCK_STATE_DONE)
+ if (block != queue->fileio.active_block && block->state == IIO_BLOCK_STATE_DONE)
data_available += block->size;
}
- spin_unlock_irq(&queue->list_lock);
- mutex_unlock(&queue->lock);
-
return data_available;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_usage, "IIO_DMA_BUFFER");
@@ -764,7 +734,7 @@ int iio_dma_buffer_enqueue_dmabuf(struct iio_buffer *buffer,
bool cookie;
int ret;
- WARN_ON(!mutex_is_locked(&queue->lock));
+ lockdep_assert_held(&queue->lock);
cookie = dma_fence_begin_signalling();
@@ -854,8 +824,8 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_set_length, "IIO_DMA_BUFFER");
* should refer to the device that will perform the DMA to ensure that
* allocations are done from a memory region that can be accessed by the device.
*/
-int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
- struct device *dev, const struct iio_dma_buffer_ops *ops)
+void iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, struct device *dev,
+ const struct iio_dma_buffer_ops *ops)
{
iio_buffer_init(&queue->buffer);
queue->buffer.length = PAGE_SIZE;
@@ -867,8 +837,6 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
mutex_init(&queue->lock);
spin_lock_init(&queue->list_lock);
-
- return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER");
@@ -881,12 +849,10 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER");
*/
void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue)
{
- mutex_lock(&queue->lock);
+ guard(mutex)(&queue->lock);
iio_dma_buffer_fileio_free(queue);
queue->ops = NULL;
-
- mutex_unlock(&queue->lock);
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_exit, "IIO_DMA_BUFFER");
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 27dd56334345..0d3454f4adb0 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -6,6 +6,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/cleanup.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
@@ -39,27 +40,24 @@ struct dmaengine_buffer {
size_t max_size;
};
-static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(
- struct iio_buffer *buffer)
+static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(struct iio_buffer *buffer)
{
return container_of(buffer, struct dmaengine_buffer, queue.buffer);
}
static void iio_dmaengine_buffer_block_done(void *data,
- const struct dmaengine_result *result)
+ const struct dmaengine_result *result)
{
struct iio_dma_buffer_block *block = data;
- unsigned long flags;
- spin_lock_irqsave(&block->queue->list_lock, flags);
- list_del(&block->head);
- spin_unlock_irqrestore(&block->queue->list_lock, flags);
+ scoped_guard(spinlock_irqsave, &block->queue->list_lock)
+ list_del(&block->head);
block->bytes_used -= result->residue;
iio_dma_buffer_block_done(block);
}
static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block)
+ struct iio_dma_buffer_block *block)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
@@ -131,9 +129,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
if (dma_submit_error(cookie))
return dma_submit_error(cookie);
- spin_lock_irq(&dmaengine_buffer->queue.list_lock);
- list_add_tail(&block->head, &dmaengine_buffer->active);
- spin_unlock_irq(&dmaengine_buffer->queue.list_lock);
+ scoped_guard(spinlock_irq, &dmaengine_buffer->queue.list_lock)
+ list_add_tail(&block->head, &dmaengine_buffer->active);
dma_async_issue_pending(dmaengine_buffer->chan);
@@ -189,7 +186,7 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
};
static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
struct dmaengine_buffer *dmaengine_buffer =
@@ -248,7 +245,7 @@ static struct iio_buffer *iio_dmaengine_buffer_alloc(struct dma_chan *chan)
dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev);
iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
- &iio_dmaengine_default_ops);
+ &iio_dmaengine_default_ops);
dmaengine_buffer->queue.buffer.attrs = iio_dmaengine_buffer_attrs;
dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;
diff --git a/drivers/iio/chemical/ens160_core.c b/drivers/iio/chemical/ens160_core.c
index 86bde4a91bf7..bbc96c4c6283 100644
--- a/drivers/iio/chemical/ens160_core.c
+++ b/drivers/iio/chemical/ens160_core.c
@@ -316,12 +316,9 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq)
indio_dev->trig = iio_trigger_get(trig);
- ret = devm_request_threaded_irq(dev, irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_ONESHOT,
- indio_dev->name,
- indio_dev->trig);
+ ret = devm_request_irq(dev, irq, iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, indio_dev->name,
+ indio_dev->trig);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq\n");
diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c
index 0fd839176e26..23a326fb62a7 100644
--- a/drivers/iio/chemical/scd4x.c
+++ b/drivers/iio/chemical/scd4x.c
@@ -59,6 +59,8 @@ enum scd4x_channel_idx {
SCD4X_CO2,
SCD4X_TEMP,
SCD4X_HR,
+ /* kernel timestamp, at the end of buffer */
+ SCD4X_TS,
};
struct scd4x_state {
@@ -615,6 +617,7 @@ static const struct iio_chan_spec scd4x_channels[] = {
.endianness = IIO_CPU,
},
},
+ IIO_CHAN_SOFT_TIMESTAMP(SCD4X_TS),
};
static int scd4x_suspend(struct device *dev)
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
index 9ac80e4b7d75..5133755c2ea6 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -188,11 +188,8 @@ int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
/*
* Ignore samples if the buffer is not set: it is needed if the ODR is
* set but the buffer is not enabled yet.
- *
- * Note: iio_device_claim_buffer_mode() returns -EBUSY if the buffer
- * is not enabled.
*/
- if (iio_device_claim_buffer_mode(indio_dev) < 0)
+ if (!iio_device_try_claim_buffer_mode(indio_dev))
return 0;
out = (s16 *)st->samples;
@@ -444,14 +441,14 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
ret = kstrtobool(buf, &calibrate);
if (ret < 0)
return ret;
- if (!calibrate)
- return -EINVAL;
mutex_lock(&st->cmd_lock);
st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
+ st->param.perform_calib.enable = calibrate;
ret = cros_ec_motion_send_host_cmd(st, 0);
if (ret != 0) {
- dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
+ dev_warn(&indio_dev->dev, "Unable to calibrate sensor: %d\n",
+ ret);
} else {
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 7cd3caec1262..db9f5c711b3d 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -482,6 +482,19 @@ config MAX517
This driver can also be built as a module. If so, the module
will be called max517.
+config MAX22007
+ tristate "Analog Devices MAX22007 DAC Driver"
+ depends on SPI
+ select REGMAP_SPI
+ select CRC8
+ help
+ Say Y here if you want to build a driver for Analog Devices MAX22007.
+
+ MAX22007 is a quad-channel, 12-bit, voltage-output digital to
+ analog converter (DAC) with SPI interface.
+
+ If compiled as a module, it will be called max22007.
+
config MAX5522
tristate "Maxim MAX5522 DAC driver"
depends on SPI_MASTER
@@ -524,6 +537,26 @@ config MCP4728
To compile this driver as a module, choose M here: the module
will be called mcp4728.
+config MCP47FEB02
+ tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver"
+ depends on I2C
+ help
+ Say yes here if you want to build the driver for the Microchip:
+ - 8-bit DAC:
+ MCP47FEB01, MCP47FEB02, MCP47FEB04, MCP47FEB08,
+ MCP47FVB01, MCP47FVB02, MCP47FVB04, MCP47FVB08
+ - 10-bit DAC:
+ MCP47FEB11, MCP47FEB12, MCP47FEB14, MCP47FEB18,
+ MCP47FVB11, MCP47FVB12, MCP47FVB14, MCP47FVB18
+ - 12-bit DAC:
+ MCP47FEB21, MCP47FEB22, MCP47FEB24, MCP47FEB28,
+ MCP47FVB21, MCP47FVB22, MCP47FVB24, MCP47FVB28
+ having 1 to 8 channels, 8/10/12-bit digital-to-analog converter
+ (DAC) with I2C interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp47feb02.
+
config MCP4821
tristate "MCP4801/02/11/12/21/22 DAC driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index e6ac4c67e337..2a80bbf4e80a 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -48,10 +48,12 @@ obj-$(CONFIG_LTC2664) += ltc2664.o
obj-$(CONFIG_LTC2688) += ltc2688.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
+obj-$(CONFIG_MAX22007) += max22007.o
obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
+obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o
obj-$(CONFIG_MCP4821) += mcp4821.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
index 0d525272a8a8..9cc895bbe51a 100644
--- a/drivers/iio/dac/adi-axi-dac.c
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -885,34 +885,35 @@ static const struct regmap_config axi_dac_regmap_config = {
static int axi_dac_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct axi_dac_state *st;
void __iomem *base;
unsigned int ver;
struct clk *clk;
int ret;
- st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
- st->info = device_get_match_data(&pdev->dev);
+ st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
- clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
+ clk = devm_clk_get_enabled(dev, "s_axi_aclk");
if (IS_ERR(clk)) {
/* Backward compat., old fdt versions without clock-names. */
- clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(clk),
- "failed to get clock\n");
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "failed to get clock\n");
}
if (st->info->has_dac_clk) {
struct clk *dac_clk;
- dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
+ dac_clk = devm_clk_get_enabled(dev, "dac_clk");
if (IS_ERR(dac_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
+ return dev_err_probe(dev, PTR_ERR(dac_clk),
"failed to get dac_clk clock\n");
/* We only care about the streaming mode rate */
@@ -923,11 +924,10 @@ static int axi_dac_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- st->dev = &pdev->dev;
- st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &axi_dac_regmap_config);
+ st->dev = dev;
+ st->regmap = devm_regmap_init_mmio(dev, base, &axi_dac_regmap_config);
if (IS_ERR(st->regmap))
- return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to init register map\n");
/*
@@ -942,18 +942,15 @@ static int axi_dac_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
- ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
- dev_err(&pdev->dev,
- "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
- ADI_AXI_PCORE_VER_MAJOR(st->info->version),
- ADI_AXI_PCORE_VER_MINOR(st->info->version),
- ADI_AXI_PCORE_VER_PATCH(st->info->version),
- ADI_AXI_PCORE_VER_MAJOR(ver),
- ADI_AXI_PCORE_VER_MINOR(ver),
- ADI_AXI_PCORE_VER_PATCH(ver));
- return -ENODEV;
- }
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version))
+ return dev_err_probe(dev, -ENODEV,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(st->info->version),
+ ADI_AXI_PCORE_VER_MINOR(st->info->version),
+ ADI_AXI_PCORE_VER_PATCH(st->info->version),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
/* Let's get the core read only configuration */
ret = regmap_read(st->regmap, AXI_DAC_CONFIG_REG, &st->reg_config);
@@ -975,34 +972,33 @@ static int axi_dac_probe(struct platform_device *pdev)
mutex_init(&st->lock);
- ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
+ ret = devm_iio_backend_register(dev, st->info->backend_info, st);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
+ return dev_err_probe(dev, ret,
"failed to register iio backend\n");
- device_for_each_child_node_scoped(&pdev->dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
int val;
if (!st->info->has_child_nodes)
- return dev_err_probe(&pdev->dev, -EINVAL,
+ return dev_err_probe(dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "invalid reg property.");
+ return dev_err_probe(dev, ret, "invalid reg property.");
if (val != 0)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "invalid node address.");
+ return dev_err_probe(dev, -EINVAL,
+ "invalid node address.");
ret = axi_dac_create_platform_device(st, child);
if (ret)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "cannot create device.");
+ return dev_err_probe(dev, -EINVAL,
+ "cannot create device.");
}
- dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+ dev_info(dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c
index a8198ba4f98a..6dda8918975a 100644
--- a/drivers/iio/dac/ds4424.c
+++ b/drivers/iio/dac/ds4424.c
@@ -14,7 +14,6 @@
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
-#include <linux/iio/consumer.h>
#define DS4422_MAX_DAC_CHANNELS 2
#define DS4424_MAX_DAC_CHANNELS 4
diff --git a/drivers/iio/dac/max22007.c b/drivers/iio/dac/max22007.c
new file mode 100644
index 000000000000..182ac7155a89
--- /dev/null
+++ b/drivers/iio/dac/max22007.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * max22007.c - MAX22007 DAC driver
+ *
+ * Driver for Analog Devices MAX22007 Digital to Analog Converter.
+ *
+ * Copyright (c) 2026 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/kstrtox.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#include <dt-bindings/iio/addac/adi,ad74413r.h>
+struct device;
+
+#define MAX22007_NUM_CHANNELS 4
+#define MAX22007_REV_ID_REG 0x00
+#define MAX22007_STAT_INTR_REG 0x01
+#define MAX22007_INTERRUPT_EN_REG 0x02
+#define MAX22007_CONFIG_REG 0x03
+#define MAX22007_CONTROL_REG 0x04
+#define MAX22007_CHANNEL_MODE_REG 0x05
+#define MAX22007_SOFT_RESET_REG 0x06
+#define MAX22007_DAC_CHANNEL_REG(ch) (0x07 + (ch))
+#define MAX22007_GPIO_CTRL_REG 0x0B
+#define MAX22007_GPIO_DATA_REG 0x0C
+#define MAX22007_GPI_EDGE_INT_CTRL_REG 0x0D
+#define MAX22007_GPI_INT_STATUS_REG 0x0E
+
+/* Channel mask definitions */
+#define MAX22007_CH_MODE_CH_MASK(ch) BIT(12 + (ch))
+#define MAX22007_CH_PWRON_CH_MASK(ch) BIT(8 + (ch))
+#define MAX22007_DAC_LATCH_MODE_MASK(ch) BIT(12 + (ch))
+#define MAX22007_LDAC_UPDATE_MASK(ch) BIT(12 + (ch))
+#define MAX22007_SW_RST_MASK BIT(8)
+#define MAX22007_SW_CLR_MASK BIT(12)
+#define MAX22007_SOFT_RESET_BITS_MASK (MAX22007_SW_RST_MASK | \
+ MAX22007_SW_CLR_MASK)
+#define MAX22007_DAC_DATA_MASK GENMASK(15, 4)
+#define MAX22007_DAC_MAX_RAW GENMASK(11, 0)
+#define MAX22007_CRC8_POLYNOMIAL 0x8C
+#define MAX22007_CRC_EN_MASK BIT(0)
+#define MAX22007_RW_MASK BIT(0)
+#define MAX22007_CRC_OVERHEAD 1
+#define MAX22007_NUM_SUPPLIES 3
+#define MAX22007_REF_MV 2500
+
+/* Field value preparation macros with masking */
+#define MAX22007_CH_PWR_VAL(ch, val) (((val) & 0x1) << (8 + (ch)))
+#define MAX22007_CH_MODE_VAL(ch, val) (((val) & 0x1) << (12 + (ch)))
+#define MAX22007_DAC_LATCH_MODE_VAL(ch, val) (((val) & 0x1) << (12 + (ch)))
+
+static u8 max22007_crc8_table[CRC8_TABLE_SIZE];
+
+static const char * const max22007_supply_names[MAX22007_NUM_SUPPLIES] = {
+ "vdd",
+ "hvdd",
+ "hvss",
+};
+
+struct max22007_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct iio_chan_spec *iio_chans;
+ u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[4];
+};
+
+static int max22007_spi_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct max22007_state *st = context;
+ u8 calculated_crc, received_crc;
+ u8 rx_buf[4];
+ u8 reg_byte;
+ int ret;
+
+ if (reg_size != 1)
+ return -EINVAL;
+
+ if (val_size == 0 || val_size > 3)
+ return -EINVAL;
+
+ memcpy(&reg_byte, reg, 1);
+
+ ret = spi_write_then_read(st->spi, &reg_byte, 1, rx_buf,
+ val_size + MAX22007_CRC_OVERHEAD);
+ if (ret) {
+ dev_err(&st->spi->dev, "SPI transfer failed: %d\n", ret);
+ return ret;
+ }
+
+ calculated_crc = crc8(max22007_crc8_table, &reg_byte, 1, 0x00);
+ calculated_crc = crc8(max22007_crc8_table, rx_buf, 2, calculated_crc);
+ received_crc = rx_buf[val_size];
+
+ if (calculated_crc != received_crc) {
+ dev_err(&st->spi->dev, "CRC mismatch on read register %02x\n", reg_byte);
+ return -EIO;
+ }
+
+ memcpy(val, rx_buf, val_size);
+
+ return 0;
+}
+
+static int max22007_spi_write(void *context, const void *data, size_t count)
+{
+ struct max22007_state *st = context;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx_buf,
+ .rx_buf = st->rx_buf,
+ };
+
+ if (count + MAX22007_CRC_OVERHEAD > sizeof(st->tx_buf))
+ return -EINVAL;
+
+ memset(st->tx_buf, 0, sizeof(st->tx_buf));
+
+ xfer.len = count + MAX22007_CRC_OVERHEAD;
+
+ memcpy(st->tx_buf, data, count);
+ st->tx_buf[count] = crc8(max22007_crc8_table, st->tx_buf,
+ sizeof(st->tx_buf) - 1, 0x00);
+
+ return spi_sync_transfer(st->spi, &xfer, 1);
+}
+
+static bool max22007_reg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX22007_REV_ID_REG:
+ case MAX22007_STAT_INTR_REG:
+ case MAX22007_CONFIG_REG:
+ case MAX22007_CONTROL_REG:
+ case MAX22007_CHANNEL_MODE_REG:
+ case MAX22007_SOFT_RESET_REG:
+ case MAX22007_GPIO_CTRL_REG:
+ case MAX22007_GPIO_DATA_REG:
+ case MAX22007_GPI_EDGE_INT_CTRL_REG:
+ case MAX22007_GPI_INT_STATUS_REG:
+ return true;
+ case MAX22007_DAC_CHANNEL_REG(0) ... MAX22007_DAC_CHANNEL_REG(MAX22007_NUM_CHANNELS - 1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max22007_reg_writable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX22007_CONFIG_REG:
+ case MAX22007_CONTROL_REG:
+ case MAX22007_CHANNEL_MODE_REG:
+ case MAX22007_SOFT_RESET_REG:
+ case MAX22007_GPIO_CTRL_REG:
+ case MAX22007_GPIO_DATA_REG:
+ case MAX22007_GPI_EDGE_INT_CTRL_REG:
+ return true;
+ case MAX22007_DAC_CHANNEL_REG(0) ... MAX22007_DAC_CHANNEL_REG(MAX22007_NUM_CHANNELS - 1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_bus max22007_regmap_bus = {
+ .read = max22007_spi_read,
+ .write = max22007_spi_write,
+ .read_flag_mask = MAX22007_RW_MASK,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config max22007_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .reg_shift = -1,
+ .readable_reg = max22007_reg_readable,
+ .writeable_reg = max22007_reg_writable,
+ .max_register = 0x0E,
+};
+
+static int max22007_write_channel_data(struct max22007_state *st,
+ unsigned int channel, int data)
+{
+ unsigned int reg_val;
+
+ if (data < 0 || data > MAX22007_DAC_MAX_RAW)
+ return -EINVAL;
+
+ reg_val = FIELD_PREP(MAX22007_DAC_DATA_MASK, data);
+
+ return regmap_write(st->regmap, MAX22007_DAC_CHANNEL_REG(channel), reg_val);
+}
+
+static int max22007_read_channel_data(struct max22007_state *st,
+ unsigned int channel, int *data)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(st->regmap, MAX22007_DAC_CHANNEL_REG(channel), &reg_val);
+ if (ret)
+ return ret;
+
+ *data = FIELD_GET(MAX22007_DAC_DATA_MASK, reg_val);
+
+ return 0;
+}
+
+static int max22007_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max22007_read_channel_data(st, chan->channel, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLTAGE)
+ *val = 5 * MAX22007_REF_MV; /* 5 * Vref in mV */
+ else
+ *val = 25; /* Vref / (2 * Rsense) = MAX22007_REF_MV / 100 */
+ *val2 = 12; /* 12-bit DAC resolution */
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max22007_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return max22007_write_channel_data(st, chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info max22007_info = {
+ .read_raw = max22007_read_raw,
+ .write_raw = max22007_write_raw,
+};
+
+static ssize_t max22007_read_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+ unsigned int reg_val;
+ bool powerdown;
+ int ret;
+
+ ret = regmap_read(st->regmap, MAX22007_CHANNEL_MODE_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ powerdown = !(reg_val & MAX22007_CH_PWRON_CH_MASK(chan->channel));
+
+ return sysfs_emit(buf, "%d\n", powerdown);
+}
+
+static ssize_t max22007_write_dac_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct max22007_state *st = iio_priv(indio_dev);
+ bool powerdown;
+ int ret;
+
+ ret = kstrtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, MAX22007_CHANNEL_MODE_REG,
+ MAX22007_CH_PWRON_CH_MASK(chan->channel),
+ MAX22007_CH_PWR_VAL(chan->channel, powerdown ? 0 : 1));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static const struct iio_chan_spec_ext_info max22007_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = max22007_read_dac_powerdown,
+ .write = max22007_write_dac_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ { }
+};
+
+static int max22007_parse_channel_cfg(struct max22007_state *st, u8 *num_channels)
+{
+ struct device *dev = &st->spi->dev;
+ int ret, num_chan;
+ int i = 0;
+ u32 reg;
+
+ num_chan = device_get_child_node_count(dev);
+ if (!num_chan)
+ return dev_err_probe(dev, -ENODEV, "no channels configured\n");
+
+ st->iio_chans = devm_kcalloc(dev, num_chan, sizeof(*st->iio_chans), GFP_KERNEL);
+ if (!st->iio_chans)
+ return -ENOMEM;
+
+ device_for_each_child_node_scoped(dev, child) {
+ u32 ch_func;
+ enum iio_chan_type chan_type;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read reg property of %pfwP\n", child);
+
+ if (reg >= MAX22007_NUM_CHANNELS)
+ return dev_err_probe(dev, -EINVAL,
+ "reg out of range in %pfwP\n", child);
+
+ ret = fwnode_property_read_u32(child, "adi,ch-func", &ch_func);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing adi,ch-func property for %pfwP\n", child);
+
+ switch (ch_func) {
+ case CH_FUNC_VOLTAGE_OUTPUT:
+ chan_type = IIO_VOLTAGE;
+ break;
+ case CH_FUNC_CURRENT_OUTPUT:
+ chan_type = IIO_CURRENT;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "invalid adi,ch-func %u for %pfwP\n",
+ ch_func, child);
+ }
+
+ st->iio_chans[i++] = (struct iio_chan_spec) {
+ .output = 1,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = max22007_ext_info,
+ .channel = reg,
+ .type = chan_type,
+ };
+
+ ret = regmap_update_bits(st->regmap, MAX22007_CHANNEL_MODE_REG,
+ MAX22007_CH_MODE_CH_MASK(reg),
+ MAX22007_CH_MODE_VAL(reg, ch_func - 1));
+ if (ret)
+ return ret;
+
+ /* Set DAC to transparent mode (immediate update) */
+ ret = regmap_update_bits(st->regmap, MAX22007_CONFIG_REG,
+ MAX22007_DAC_LATCH_MODE_MASK(reg),
+ MAX22007_DAC_LATCH_MODE_VAL(reg, 1));
+ if (ret)
+ return ret;
+ }
+
+ *num_channels = num_chan;
+
+ return 0;
+}
+
+static int max22007_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct gpio_desc *reset_gpio;
+ struct max22007_state *st;
+ struct iio_dev *indio_dev;
+ u8 num_channels;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ crc8_populate_lsb(max22007_crc8_table, MAX22007_CRC8_POLYNOMIAL);
+
+ st->regmap = devm_regmap_init(dev, &max22007_regmap_bus, st,
+ &max22007_regmap_config);
+ if (IS_ERR(st->regmap))
+ return dev_err_probe(dev, PTR_ERR(st->regmap),
+ "Failed to initialize regmap\n");
+
+ ret = devm_regulator_bulk_get_enable(dev, MAX22007_NUM_SUPPLIES,
+ max22007_supply_names);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable regulators\n");
+
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(reset_gpio),
+ "Failed to get reset GPIO\n");
+
+ if (reset_gpio) {
+ gpiod_set_value_cansleep(reset_gpio, 1);
+ usleep_range(1000, 5000);
+ gpiod_set_value_cansleep(reset_gpio, 0);
+ usleep_range(1000, 5000);
+ } else {
+ ret = regmap_write(st->regmap, MAX22007_SOFT_RESET_REG,
+ MAX22007_SOFT_RESET_BITS_MASK);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_set_bits(st->regmap, MAX22007_CONFIG_REG,
+ MAX22007_CRC_EN_MASK);
+ if (ret)
+ return ret;
+
+ ret = max22007_parse_channel_cfg(st, &num_channels);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &max22007_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->iio_chans;
+ indio_dev->num_channels = num_channels;
+ indio_dev->name = "max22007";
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct spi_device_id max22007_id[] = {
+ { "max22007" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, max22007_id);
+
+static const struct of_device_id max22007_of_match[] = {
+ { .compatible = "adi,max22007" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max22007_of_match);
+
+static struct spi_driver max22007_driver = {
+ .driver = {
+ .name = "max22007",
+ .of_match_table = max22007_of_match,
+ },
+ .probe = max22007_probe,
+ .id_table = max22007_id,
+};
+module_spi_driver(max22007_driver);
+
+MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>");
+MODULE_DESCRIPTION("Analog Devices MAX22007 DAC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02.c
new file mode 100644
index 000000000000..b218f0c3a0bd
--- /dev/null
+++ b/drivers/iio/dac/mcp47feb02.c
@@ -0,0 +1,1250 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface
+ *
+ * Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Ariana Lazar <ariana.lazar@microchip.com>
+ *
+ * Datasheet links:
+ * [MCP47FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
+ * [MCP47FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
+ * [MCP47FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/kstrtox.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time64.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+/* Register addresses must be left shifted with 3 positions in order to append command mask */
+#define MCP47FEB02_DAC0_REG_ADDR 0x00
+#define MCP47FEB02_VREF_REG_ADDR 0x40
+#define MCP47FEB02_POWER_DOWN_REG_ADDR 0x48
+#define MCP47FEB02_DAC_CTRL_MASK GENMASK(1, 0)
+
+#define MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR 0x50
+#define MCP47FEB02_GAIN_BIT_MASK BIT(0)
+#define MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK BIT(6)
+#define MCP47FEB02_GAIN_BITS_MASK GENMASK(15, 8)
+
+#define MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR 0x58
+
+#define MCP47FEB02_NV_DAC0_REG_ADDR 0x80
+#define MCP47FEB02_NV_VREF_REG_ADDR 0xC0
+#define MCP47FEB02_NV_POWER_DOWN_REG_ADDR 0xC8
+#define MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR 0xD0
+#define MCP47FEB02_NV_I2C_SLAVE_ADDR_MASK GENMASK(7, 0)
+
+/* Voltage reference, Power-Down control register and DAC Wiperlock status register fields */
+#define DAC_CTRL_MASK(ch) (GENMASK(1, 0) << (2 * (ch)))
+#define DAC_CTRL_VAL(ch, val) ((val) << (2 * (ch)))
+
+/* Gain Control and I2C Slave Address Reguster fields */
+#define DAC_GAIN_MASK(ch) (BIT(0) << (8 + (ch)))
+#define DAC_GAIN_VAL(ch, val) ((val) << (8 + (ch)))
+
+#define REG_ADDR(reg) ((reg) << 3)
+#define NV_REG_ADDR(reg) ((NV_DAC_ADDR_OFFSET + (reg)) << 3)
+#define READFLAG_MASK GENMASK(2, 1)
+
+#define MCP47FEB02_MAX_CH 8
+#define MCP47FEB02_MAX_SCALES_CH 3
+#define MCP47FEB02_DAC_WIPER_UNLOCKED 0
+#define MCP47FEB02_NORMAL_OPERATION 0
+#define MCP47FEB02_INTERNAL_BAND_GAP_mV 2440
+#define NV_DAC_ADDR_OFFSET 0x10
+
+enum mcp47feb02_vref_mode {
+ MCP47FEB02_VREF_VDD = 0,
+ MCP47FEB02_INTERNAL_BAND_GAP = 1,
+ MCP47FEB02_EXTERNAL_VREF_UNBUFFERED = 2,
+ MCP47FEB02_EXTERNAL_VREF_BUFFERED = 3,
+};
+
+enum mcp47feb02_scale {
+ MCP47FEB02_SCALE_VDD = 0,
+ MCP47FEB02_SCALE_GAIN_X1 = 1,
+ MCP47FEB02_SCALE_GAIN_X2 = 2,
+};
+
+enum mcp47feb02_gain_bit_mode {
+ MCP47FEB02_GAIN_BIT_X1 = 0,
+ MCP47FEB02_GAIN_BIT_X2 = 1,
+};
+
+static const char * const mcp47feb02_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "open_circuit",
+};
+
+/**
+ * struct mcp47feb02_features - chip specific data
+ * @name: device name
+ * @phys_channels: number of hardware channels
+ * @resolution: DAC resolution
+ * @have_ext_vref1: does the hardware have an the second external voltage reference?
+ * @have_eeprom: does the hardware have an internal eeprom?
+ */
+struct mcp47feb02_features {
+ const char *name;
+ unsigned int phys_channels;
+ unsigned int resolution;
+ bool have_ext_vref1;
+ bool have_eeprom;
+};
+
+static const struct mcp47feb02_features mcp47feb01_chip_features = {
+ .name = "mcp47feb01",
+ .phys_channels = 1,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb02_chip_features = {
+ .name = "mcp47feb02",
+ .phys_channels = 2,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb04_chip_features = {
+ .name = "mcp47feb04",
+ .phys_channels = 4,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb08_chip_features = {
+ .name = "mcp47feb08",
+ .phys_channels = 8,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb11_chip_features = {
+ .name = "mcp47feb11",
+ .phys_channels = 1,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb12_chip_features = {
+ .name = "mcp47feb12",
+ .phys_channels = 2,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb14_chip_features = {
+ .name = "mcp47feb14",
+ .phys_channels = 4,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb18_chip_features = {
+ .name = "mcp47feb18",
+ .phys_channels = 8,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb21_chip_features = {
+ .name = "mcp47feb21",
+ .phys_channels = 1,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb22_chip_features = {
+ .name = "mcp47feb22",
+ .phys_channels = 2,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb24_chip_features = {
+ .name = "mcp47feb24",
+ .phys_channels = 4,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47feb28_chip_features = {
+ .name = "mcp47feb28",
+ .phys_channels = 8,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = true,
+};
+
+static const struct mcp47feb02_features mcp47fvb01_chip_features = {
+ .name = "mcp47fvb01",
+ .phys_channels = 1,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb02_chip_features = {
+ .name = "mcp47fvb02",
+ .phys_channels = 2,
+ .resolution = 8,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb04_chip_features = {
+ .name = "mcp47fvb04",
+ .phys_channels = 4,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb08_chip_features = {
+ .name = "mcp47fvb08",
+ .phys_channels = 8,
+ .resolution = 8,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb11_chip_features = {
+ .name = "mcp47fvb11",
+ .phys_channels = 1,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb12_chip_features = {
+ .name = "mcp47fvb12",
+ .phys_channels = 2,
+ .resolution = 10,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb14_chip_features = {
+ .name = "mcp47fvb14",
+ .phys_channels = 4,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb18_chip_features = {
+ .name = "mcp47fvb18",
+ .phys_channels = 8,
+ .resolution = 10,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb21_chip_features = {
+ .name = "mcp47fvb21",
+ .phys_channels = 1,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb22_chip_features = {
+ .name = "mcp47fvb22",
+ .phys_channels = 2,
+ .resolution = 12,
+ .have_ext_vref1 = false,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb24_chip_features = {
+ .name = "mcp47fvb24",
+ .phys_channels = 4,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+static const struct mcp47feb02_features mcp47fvb28_chip_features = {
+ .name = "mcp47fvb28",
+ .phys_channels = 8,
+ .resolution = 12,
+ .have_ext_vref1 = true,
+ .have_eeprom = false,
+};
+
+/**
+ * struct mcp47feb02_channel_data - channel configuration
+ * @ref_mode: chosen voltage for reference
+ * @use_2x_gain: output driver gain control
+ * @powerdown: is false if the channel is in normal operation mode
+ * @powerdown_mode: selected power-down mode
+ * @dac_data: dac value
+ */
+struct mcp47feb02_channel_data {
+ u8 ref_mode;
+ bool use_2x_gain;
+ bool powerdown;
+ u8 powerdown_mode;
+ u16 dac_data;
+};
+
+/**
+ * struct mcp47feb02_data - chip configuration
+ * @chdata: options configured for each channel on the device
+ * @lock: prevents concurrent reads/writes to driver's state members
+ * @chip_features: pointer to features struct
+ * @scale_1: scales set on channels that are based on Vref1
+ * @scale: scales set on channels that are based on Vref/Vref0
+ * @active_channels_mask: enabled channels
+ * @regmap: regmap for directly accessing device register
+ * @labels: table with channels labels
+ * @phys_channels: physical channels on the device
+ * @vref1_buffered: Vref1 buffer is enabled
+ * @vref_buffered: Vref/Vref0 buffer is enabled
+ * @use_vref1: vref1-supply is defined
+ * @use_vref: vref-supply is defined
+ */
+struct mcp47feb02_data {
+ struct mcp47feb02_channel_data chdata[MCP47FEB02_MAX_CH];
+ struct mutex lock; /* prevents concurrent reads/writes to driver's state members */
+ const struct mcp47feb02_features *chip_features;
+ int scale_1[2 * MCP47FEB02_MAX_SCALES_CH];
+ int scale[2 * MCP47FEB02_MAX_SCALES_CH];
+ unsigned long active_channels_mask;
+ struct regmap *regmap;
+ const char *labels[MCP47FEB02_MAX_CH];
+ u16 phys_channels;
+ bool vref1_buffered;
+ bool vref_buffered;
+ bool use_vref1;
+ bool use_vref;
+};
+
+static const struct regmap_range mcp47feb02_readable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+};
+
+static const struct regmap_range mcp47feb02_writable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+};
+
+static const struct regmap_range mcp47feb02_volatile_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_NV_DAC0_REG_ADDR, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR),
+};
+
+static const struct regmap_access_table mcp47feb02_readable_table = {
+ .yes_ranges = mcp47feb02_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47feb02_readable_ranges),
+};
+
+static const struct regmap_access_table mcp47feb02_writable_table = {
+ .yes_ranges = mcp47feb02_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47feb02_writable_ranges),
+};
+
+static const struct regmap_access_table mcp47feb02_volatile_table = {
+ .yes_ranges = mcp47feb02_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47feb02_volatile_ranges),
+};
+
+static const struct regmap_config mcp47feb02_regmap_config = {
+ .name = "mcp47feb02_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .rd_table = &mcp47feb02_readable_table,
+ .wr_table = &mcp47feb02_writable_table,
+ .volatile_table = &mcp47feb02_volatile_table,
+ .max_register = MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR,
+ .read_flag_mask = READFLAG_MASK,
+ .cache_type = REGCACHE_MAPLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+/* For devices that doesn't have nonvolatile memory */
+static const struct regmap_range mcp47fvb02_readable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+};
+
+static const struct regmap_range mcp47fvb02_writable_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+};
+
+static const struct regmap_range mcp47fvb02_volatile_ranges[] = {
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+ regmap_reg_range(MCP47FEB02_DAC0_REG_ADDR, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR),
+};
+
+static const struct regmap_access_table mcp47fvb02_readable_table = {
+ .yes_ranges = mcp47fvb02_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_readable_ranges),
+};
+
+static const struct regmap_access_table mcp47fvb02_writable_table = {
+ .yes_ranges = mcp47fvb02_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_writable_ranges),
+};
+
+static const struct regmap_access_table mcp47fvb02_volatile_table = {
+ .yes_ranges = mcp47fvb02_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(mcp47fvb02_volatile_ranges),
+};
+
+static const struct regmap_config mcp47fvb02_regmap_config = {
+ .name = "mcp47fvb02_regmap",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .rd_table = &mcp47fvb02_readable_table,
+ .wr_table = &mcp47fvb02_writable_table,
+ .volatile_table = &mcp47fvb02_volatile_table,
+ .max_register = MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR,
+ .read_flag_mask = READFLAG_MASK,
+ .cache_type = REGCACHE_MAPLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int mcp47feb02_write_to_eeprom(struct mcp47feb02_data *data, unsigned int reg,
+ unsigned int val)
+{
+ int eewa_val, ret;
+
+ /*
+ * Wait until the currently occurring EEPROM Write Cycle is completed.
+ * Only serial commands to the volatile memory are allowed.
+ */
+ guard(mutex)(&data->lock);
+
+ ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
+ eewa_val,
+ !(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK),
+ USEC_PER_MSEC, USEC_PER_MSEC * 5);
+ if (ret)
+ return ret;
+
+ return regmap_write(data->regmap, reg, val);
+}
+
+static ssize_t store_eeprom_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct mcp47feb02_data *data = iio_priv(dev_to_iio_dev(dev));
+ unsigned int i, val, val1, eewa_val;
+ bool state;
+ int ret;
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ if (!state)
+ return 0;
+
+ /*
+ * Verify DAC Wiper and DAC Configuration are unlocked. If both are disabled,
+ * writing to EEPROM is available.
+ */
+ ret = regmap_read(data->regmap, MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ if (val) {
+ dev_err(dev, "DAC Wiper and DAC Configuration not are unlocked.\n");
+ return -EINVAL;
+ }
+
+ for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) {
+ ret = mcp47feb02_write_to_eeprom(data, NV_REG_ADDR(i),
+ data->chdata[i].dac_data);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, MCP47FEB02_VREF_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ ret = mcp47feb02_write_to_eeprom(data, MCP47FEB02_NV_VREF_REG_ADDR, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ ret = mcp47feb02_write_to_eeprom(data, MCP47FEB02_NV_POWER_DOWN_REG_ADDR, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, eewa_val,
+ !(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK),
+ USEC_PER_MSEC, USEC_PER_MSEC * 5);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR, &val);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, &val1);
+ if (ret)
+ return ret;
+
+ ret = mcp47feb02_write_to_eeprom(data, MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR,
+ (val1 & MCP47FEB02_GAIN_BITS_MASK) |
+ (val & MCP47FEB02_NV_I2C_SLAVE_ADDR_MASK));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_WO(store_eeprom, 0);
+
+static struct attribute *mcp47feb02_attributes[] = {
+ &iio_dev_attr_store_eeprom.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mcp47feb02_attribute_group = {
+ .attrs = mcp47feb02_attributes,
+};
+
+static int mcp47feb02_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ int ret;
+ u8 ch;
+
+ guard(mutex)(&data->lock);
+
+ for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) {
+ u8 pd_mode;
+
+ data->chdata[ch].powerdown = true;
+ pd_mode = data->chdata[ch].powerdown_mode + 1;
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR,
+ DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, REG_ADDR(ch), data->chdata[ch].dac_data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ u8 ch;
+
+ guard(mutex)(&data->lock);
+
+ for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) {
+ u8 pd_mode;
+ int ret;
+
+ data->chdata[ch].powerdown = false;
+ pd_mode = data->chdata[ch].powerdown_mode + 1;
+
+ ret = regmap_write(data->regmap, REG_ADDR(ch), data->chdata[ch].dac_data);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR,
+ DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
+ DAC_GAIN_MASK(ch),
+ DAC_GAIN_VAL(ch, data->chdata[ch].use_2x_gain));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR,
+ DAC_CTRL_MASK(ch),
+ DAC_CTRL_VAL(ch, MCP47FEB02_NORMAL_OPERATION));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ return data->chdata[chan->address].powerdown_mode;
+}
+
+static int mcp47feb02_set_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *ch,
+ unsigned int mode)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ data->chdata[ch->address].powerdown_mode = mode;
+
+ return 0;
+}
+
+static ssize_t mcp47feb02_read_powerdown(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *ch, char *buf)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ /* Print if channel is in a power-down mode or not */
+ return sysfs_emit(buf, "%d\n", data->chdata[ch->address].powerdown);
+}
+
+static ssize_t mcp47feb02_write_powerdown(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *ch, const char *buf,
+ size_t len)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ u32 reg = ch->address;
+ u8 tmp_pd_mode;
+ bool state;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ /*
+ * Set the channel to the specified power-down mode. Exiting power-down mode
+ * requires writing normal operation mode (0) to the channel-specific register bits.
+ */
+ tmp_pd_mode = state ? (data->chdata[reg].powerdown_mode + 1) : MCP47FEB02_NORMAL_OPERATION;
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR,
+ DAC_CTRL_MASK(reg), DAC_CTRL_VAL(reg, tmp_pd_mode));
+ if (ret)
+ return ret;
+
+ data->chdata[reg].powerdown = state;
+
+ return len;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mcp47feb02_pm_ops, mcp47feb02_suspend, mcp47feb02_resume);
+
+static const struct iio_enum mcp47febxx_powerdown_mode_enum = {
+ .items = mcp47feb02_powerdown_modes,
+ .num_items = ARRAY_SIZE(mcp47feb02_powerdown_modes),
+ .get = mcp47feb02_get_powerdown_mode,
+ .set = mcp47feb02_set_powerdown_mode,
+};
+
+static const struct iio_chan_spec_ext_info mcp47feb02_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = mcp47feb02_read_powerdown,
+ .write = mcp47feb02_write_powerdown,
+ .shared = IIO_SEPARATE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp47febxx_powerdown_mode_enum),
+ IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &mcp47febxx_powerdown_mode_enum),
+ { }
+};
+
+static const struct iio_chan_spec mcp47febxx_ch_template = {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ .ext_info = mcp47feb02_ext_info,
+};
+
+static void mcp47feb02_init_scale(struct mcp47feb02_data *data, enum mcp47feb02_scale scale,
+ int vref_mV, int scale_avail[])
+{
+ u32 value_micro, value_int;
+ u64 tmp;
+
+ /* vref_mV should not be negative */
+ tmp = (u64)vref_mV * MICRO >> data->chip_features->resolution;
+ value_int = div_u64_rem(tmp, MICRO, &value_micro);
+ scale_avail[scale * 2] = value_int;
+ scale_avail[scale * 2 + 1] = value_micro;
+}
+
+static int mcp47feb02_init_scales_avail(struct mcp47feb02_data *data, int vdd_mV,
+ int vref_mV, int vref1_mV)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int tmp_vref;
+
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale);
+
+ if (data->use_vref)
+ tmp_vref = vref_mV;
+ else
+ tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV;
+
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1, tmp_vref, data->scale);
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X2, tmp_vref * 2, data->scale);
+
+ if (data->phys_channels >= 4) {
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_VDD, vdd_mV, data->scale_1);
+
+ if (data->use_vref1 && vref1_mV <= 0)
+ return dev_err_probe(dev, vref1_mV, "Invalid voltage for Vref1\n");
+
+ if (data->use_vref1)
+ tmp_vref = vref1_mV;
+ else
+ tmp_vref = MCP47FEB02_INTERNAL_BAND_GAP_mV;
+
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X1,
+ tmp_vref, data->scale_1);
+ mcp47feb02_init_scale(data, MCP47FEB02_SCALE_GAIN_X2,
+ tmp_vref * 2, data->scale_1);
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ const int **vals, int *type, int *length, long info)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (ch->type) {
+ case IIO_VOLTAGE:
+ if (data->phys_channels >= 4 && (ch->address % 2))
+ *vals = data->scale_1;
+ else
+ *vals = data->scale;
+
+ *length = 2 * MCP47FEB02_MAX_SCALES_CH;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static void mcp47feb02_get_scale(int ch, struct mcp47feb02_data *data, int *val, int *val2)
+{
+ enum mcp47feb02_scale current_scale;
+
+ if (data->chdata[ch].ref_mode == MCP47FEB02_VREF_VDD)
+ current_scale = MCP47FEB02_SCALE_VDD;
+ else if (data->chdata[ch].use_2x_gain)
+ current_scale = MCP47FEB02_SCALE_GAIN_X2;
+ else
+ current_scale = MCP47FEB02_SCALE_GAIN_X1;
+
+ if (data->phys_channels >= 4 && (ch % 2)) {
+ *val = data->scale_1[current_scale * 2];
+ *val2 = data->scale_1[current_scale * 2 + 1];
+ } else {
+ *val = data->scale[current_scale * 2];
+ *val2 = data->scale[current_scale * 2 + 1];
+ }
+}
+
+static int mcp47feb02_check_scale(struct mcp47feb02_data *data, int val, int val2, int scale[])
+{
+ unsigned int i;
+
+ for (i = 0; i < MCP47FEB02_MAX_SCALES_CH; i++) {
+ if (scale[i * 2] == val && scale[i * 2 + 1] == val2)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int mcp47feb02_ch_scale(struct mcp47feb02_data *data, int ch, int scale)
+{
+ int tmp_val, ret;
+
+ if (scale == MCP47FEB02_SCALE_VDD) {
+ tmp_val = MCP47FEB02_VREF_VDD;
+ } else if (data->phys_channels >= 4 && (ch % 2)) {
+ if (data->use_vref1) {
+ if (data->vref1_buffered)
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_BUFFERED;
+ else
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_UNBUFFERED;
+ } else {
+ tmp_val = MCP47FEB02_INTERNAL_BAND_GAP;
+ }
+ } else if (data->use_vref) {
+ if (data->vref_buffered)
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_BUFFERED;
+ else
+ tmp_val = MCP47FEB02_EXTERNAL_VREF_UNBUFFERED;
+ } else {
+ tmp_val = MCP47FEB02_INTERNAL_BAND_GAP;
+ }
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR,
+ DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, tmp_val));
+ if (ret)
+ return ret;
+
+ data->chdata[ch].ref_mode = tmp_val;
+
+ return 0;
+}
+
+/*
+ * Setting the scale in order to choose between VDD and (Vref or Band Gap) from the user
+ * space. The VREF pin is either an input or an output, therefore the user cannot
+ * simultaneously connect an external voltage reference to the pin and select the
+ * internal Band Gap.
+ * When the DAC’s voltage reference is configured as the VREF pin, the pin is an input.
+ * When the DAC’s voltage reference is configured as the internal Band Gap,
+ * the VREF pin is an output.
+ * If Vref/Vref1 voltage is not available, then the internal Band Gap will be used
+ * to calculate the values for the scale.
+ */
+static int mcp47feb02_set_scale(struct mcp47feb02_data *data, int ch, int scale)
+{
+ int tmp_val, ret;
+
+ ret = mcp47feb02_ch_scale(data, ch, scale);
+ if (ret)
+ return ret;
+
+ if (scale == MCP47FEB02_SCALE_GAIN_X2)
+ tmp_val = MCP47FEB02_GAIN_BIT_X2;
+ else
+ tmp_val = MCP47FEB02_GAIN_BIT_X1;
+
+ ret = regmap_update_bits(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
+ DAC_GAIN_MASK(ch), DAC_GAIN_VAL(ch, tmp_val));
+ if (ret)
+ return ret;
+
+ data->chdata[ch].use_2x_gain = tmp_val;
+
+ return 0;
+}
+
+static int mcp47feb02_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ int *val, int *val2, long mask)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(data->regmap, REG_ADDR(ch->address), val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ mcp47feb02_get_scale(ch->address, data, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp47feb02_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ int val, int val2, long mask)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ int *tmp_scale, ret;
+
+ guard(mutex)(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_write(data->regmap, REG_ADDR(ch->address), val);
+ if (ret)
+ return ret;
+
+ data->chdata[ch->address].dac_data = val;
+ return 0;
+ case IIO_CHAN_INFO_SCALE:
+ if (data->phys_channels >= 4 && (ch->address % 2))
+ tmp_scale = data->scale_1;
+ else
+ tmp_scale = data->scale;
+
+ ret = mcp47feb02_check_scale(data, val, val2, tmp_scale);
+ if (ret < 0)
+ return ret;
+
+ return mcp47feb02_set_scale(data, ch->address, ret);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp47feb02_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *ch,
+ char *label)
+{
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+
+ return sysfs_emit(label, "%s\n", data->labels[ch->address]);
+}
+
+static const struct iio_info mcp47feb02_info = {
+ .read_raw = mcp47feb02_read_raw,
+ .write_raw = mcp47feb02_write_raw,
+ .read_label = mcp47feb02_read_label,
+ .read_avail = &mcp47feb02_read_avail,
+ .attrs = &mcp47feb02_attribute_group,
+};
+
+static const struct iio_info mcp47fvb02_info = {
+ .read_raw = mcp47feb02_read_raw,
+ .write_raw = mcp47feb02_write_raw,
+ .read_label = mcp47feb02_read_label,
+ .read_avail = &mcp47feb02_read_avail,
+};
+
+static int mcp47feb02_parse_fw(struct iio_dev *indio_dev,
+ const struct mcp47feb02_features *chip_features)
+{
+ struct iio_chan_spec chanspec = mcp47febxx_ch_template;
+ struct mcp47feb02_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+ struct iio_chan_spec *channels;
+ u32 num_channels;
+ u8 chan_idx = 0;
+
+ guard(mutex)(&data->lock);
+
+ num_channels = device_get_child_node_count(dev);
+ if (num_channels > chip_features->phys_channels)
+ return dev_err_probe(dev, -EINVAL, "More channels than the chip supports\n");
+
+ if (!num_channels)
+ return dev_err_probe(dev, -EINVAL, "No channel specified in the devicetree.\n");
+
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ device_for_each_child_node_scoped(dev, child) {
+ u32 reg = 0;
+ int ret;
+
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Invalid channel number\n");
+
+ if (reg >= chip_features->phys_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "The index of the channels does not match the chip\n");
+
+ set_bit(reg, &data->active_channels_mask);
+
+ ret = fwnode_property_read_string(child, "label", &data->labels[reg]);
+ if (ret)
+ return dev_err_probe(dev, ret, "%pfw: invalid label\n",
+ fwnode_get_name(child));
+
+ chanspec.address = reg;
+ chanspec.channel = reg;
+ channels[chan_idx] = chanspec;
+ chan_idx++;
+ }
+
+ indio_dev->num_channels = num_channels;
+ indio_dev->channels = channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ data->phys_channels = chip_features->phys_channels;
+
+ data->vref_buffered = device_property_read_bool(dev, "microchip,vref-buffered");
+
+ if (chip_features->have_ext_vref1)
+ data->vref1_buffered = device_property_read_bool(dev, "microchip,vref1-buffered");
+
+ return 0;
+}
+
+static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data)
+{
+ unsigned int i, vref_ch, gain_ch, pd_ch;
+ int ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_VREF_REG_ADDR, &vref_ch);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR, &gain_ch);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, MCP47FEB02_POWER_DOWN_REG_ADDR, &pd_ch);
+ if (ret)
+ return ret;
+
+ gain_ch = gain_ch & MCP47FEB02_GAIN_BITS_MASK;
+ for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) {
+ struct device *dev = regmap_get_device(data->regmap);
+ unsigned int pd_tmp;
+
+ data->chdata[i].ref_mode = (vref_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK;
+ data->chdata[i].use_2x_gain = (gain_ch >> i) & MCP47FEB02_GAIN_BIT_MASK;
+
+ /*
+ * Inform the user that the current voltage reference read from the volatile
+ * register of the chip is different from the one specified in the device tree.
+ * Considering that the user cannot have an external voltage reference connected
+ * to the pin and select the internal Band Gap at the same time, in order to avoid
+ * miscofiguring the reference voltage, the volatile register will not be written.
+ * In order to overwrite the setting from volatile register with the one from the
+ * device tree, the user needs to write the chosen scale.
+ */
+ switch (data->chdata[i].ref_mode) {
+ case MCP47FEB02_INTERNAL_BAND_GAP:
+ if (data->phys_channels >= 4 && (i % 2) && data->use_vref1) {
+ dev_dbg(dev, "ch[%u]: was configured to use internal band gap", i);
+ dev_dbg(dev, "ch[%u]: reference voltage set to VREF1", i);
+ break;
+ }
+ if ((data->phys_channels < 4 || (data->phys_channels >= 4 && !(i % 2))) &&
+ data->use_vref) {
+ dev_dbg(dev, "ch[%u]: was configured to use internal band gap", i);
+ dev_dbg(dev, "ch[%u]: reference voltage set to VREF", i);
+ break;
+ }
+ break;
+ case MCP47FEB02_EXTERNAL_VREF_UNBUFFERED:
+ case MCP47FEB02_EXTERNAL_VREF_BUFFERED:
+ if (data->phys_channels >= 4 && (i % 2) && !data->use_vref1) {
+ dev_dbg(dev, "ch[%u]: was configured to use VREF1", i);
+ dev_dbg(dev,
+ "ch[%u]: reference voltage set to internal band gap", i);
+ break;
+ }
+ if ((data->phys_channels < 4 || (data->phys_channels >= 4 && !(i % 2))) &&
+ !data->use_vref) {
+ dev_dbg(dev, "ch[%u]: was configured to use VREF", i);
+ dev_dbg(dev,
+ "ch[%u]: reference voltage set to internal band gap", i);
+ break;
+ }
+ break;
+ }
+
+ pd_tmp = (pd_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK;
+ data->chdata[i].powerdown_mode = pd_tmp ? (pd_tmp - 1) : pd_tmp;
+ data->chdata[i].powerdown = !!(data->chdata[i].powerdown_mode);
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_mV,
+ int vref_mV, int vref1_mV)
+{
+ unsigned int i;
+
+ for_each_set_bit(i, &data->active_channels_mask, data->phys_channels) {
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = mcp47feb02_init_scales_avail(data, vdd_mV, vref_mV, vref1_mV);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init scales for ch %u\n", i);
+ }
+
+ return 0;
+}
+
+static int mcp47feb02_probe(struct i2c_client *client)
+{
+ const struct mcp47feb02_features *chip_features;
+ struct device *dev = &client->dev;
+ struct mcp47feb02_data *data;
+ struct iio_dev *indio_dev;
+ int vref1_mV = 0;
+ int vref_mV = 0;
+ int vdd_mV;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ chip_features = i2c_get_match_data(client);
+ if (!chip_features)
+ return -EINVAL;
+
+ data->chip_features = chip_features;
+
+ if (chip_features->have_eeprom) {
+ data->regmap = devm_regmap_init_i2c(client, &mcp47feb02_regmap_config);
+ indio_dev->info = &mcp47feb02_info;
+ } else {
+ data->regmap = devm_regmap_init_i2c(client, &mcp47fvb02_regmap_config);
+ indio_dev->info = &mcp47fvb02_info;
+ }
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap), "Error initializing i2c regmap\n");
+
+ indio_dev->name = chip_features->name;
+
+ ret = mcp47feb02_parse_fw(indio_dev, chip_features);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error parsing firmware data\n");
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vdd");
+ if (ret < 0)
+ return ret;
+
+ vdd_mV = ret / MILLI;
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret > 0) {
+ vref_mV = ret / MILLI;
+ data->use_vref = true;
+ } else {
+ dev_dbg(dev, "using internal band gap as voltage reference.\n");
+ dev_dbg(dev, "Vref is unavailable.\n");
+ }
+
+ if (chip_features->have_ext_vref1) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref1");
+ if (ret > 0) {
+ vref1_mV = ret / MILLI;
+ data->use_vref1 = true;
+ } else {
+ dev_dbg(dev, "using internal band gap as voltage reference 1.\n");
+ dev_dbg(dev, "Vref1 is unavailable.\n");
+ }
+ }
+
+ ret = mcp47feb02_init_ctrl_regs(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Error initialising vref register\n");
+
+ ret = mcp47feb02_init_ch_scales(data, vdd_mV, vref_mV, vref1_mV);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id mcp47feb02_id[] = {
+ { "mcp47feb01", (kernel_ulong_t)&mcp47feb01_chip_features },
+ { "mcp47feb02", (kernel_ulong_t)&mcp47feb02_chip_features },
+ { "mcp47feb04", (kernel_ulong_t)&mcp47feb04_chip_features },
+ { "mcp47feb08", (kernel_ulong_t)&mcp47feb08_chip_features },
+ { "mcp47feb11", (kernel_ulong_t)&mcp47feb11_chip_features },
+ { "mcp47feb12", (kernel_ulong_t)&mcp47feb12_chip_features },
+ { "mcp47feb14", (kernel_ulong_t)&mcp47feb14_chip_features },
+ { "mcp47feb18", (kernel_ulong_t)&mcp47feb18_chip_features },
+ { "mcp47feb21", (kernel_ulong_t)&mcp47feb21_chip_features },
+ { "mcp47feb22", (kernel_ulong_t)&mcp47feb22_chip_features },
+ { "mcp47feb24", (kernel_ulong_t)&mcp47feb24_chip_features },
+ { "mcp47feb28", (kernel_ulong_t)&mcp47feb28_chip_features },
+ { "mcp47fvb01", (kernel_ulong_t)&mcp47fvb01_chip_features },
+ { "mcp47fvb02", (kernel_ulong_t)&mcp47fvb02_chip_features },
+ { "mcp47fvb04", (kernel_ulong_t)&mcp47fvb04_chip_features },
+ { "mcp47fvb08", (kernel_ulong_t)&mcp47fvb08_chip_features },
+ { "mcp47fvb11", (kernel_ulong_t)&mcp47fvb11_chip_features },
+ { "mcp47fvb12", (kernel_ulong_t)&mcp47fvb12_chip_features },
+ { "mcp47fvb14", (kernel_ulong_t)&mcp47fvb14_chip_features },
+ { "mcp47fvb18", (kernel_ulong_t)&mcp47fvb18_chip_features },
+ { "mcp47fvb21", (kernel_ulong_t)&mcp47fvb21_chip_features },
+ { "mcp47fvb22", (kernel_ulong_t)&mcp47fvb22_chip_features },
+ { "mcp47fvb24", (kernel_ulong_t)&mcp47fvb24_chip_features },
+ { "mcp47fvb28", (kernel_ulong_t)&mcp47fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp47feb02_id);
+
+static const struct of_device_id mcp47feb02_of_match[] = {
+ { .compatible = "microchip,mcp47feb01", .data = &mcp47feb01_chip_features },
+ { .compatible = "microchip,mcp47feb02", .data = &mcp47feb02_chip_features },
+ { .compatible = "microchip,mcp47feb04", .data = &mcp47feb04_chip_features },
+ { .compatible = "microchip,mcp47feb08", .data = &mcp47feb08_chip_features },
+ { .compatible = "microchip,mcp47feb11", .data = &mcp47feb11_chip_features },
+ { .compatible = "microchip,mcp47feb12", .data = &mcp47feb12_chip_features },
+ { .compatible = "microchip,mcp47feb14", .data = &mcp47feb14_chip_features },
+ { .compatible = "microchip,mcp47feb18", .data = &mcp47feb18_chip_features },
+ { .compatible = "microchip,mcp47feb21", .data = &mcp47feb21_chip_features },
+ { .compatible = "microchip,mcp47feb22", .data = &mcp47feb22_chip_features },
+ { .compatible = "microchip,mcp47feb24", .data = &mcp47feb24_chip_features },
+ { .compatible = "microchip,mcp47feb28", .data = &mcp47feb28_chip_features },
+ { .compatible = "microchip,mcp47fvb01", .data = &mcp47fvb01_chip_features },
+ { .compatible = "microchip,mcp47fvb02", .data = &mcp47fvb02_chip_features },
+ { .compatible = "microchip,mcp47fvb04", .data = &mcp47fvb04_chip_features },
+ { .compatible = "microchip,mcp47fvb08", .data = &mcp47fvb08_chip_features },
+ { .compatible = "microchip,mcp47fvb11", .data = &mcp47fvb11_chip_features },
+ { .compatible = "microchip,mcp47fvb12", .data = &mcp47fvb12_chip_features },
+ { .compatible = "microchip,mcp47fvb14", .data = &mcp47fvb14_chip_features },
+ { .compatible = "microchip,mcp47fvb18", .data = &mcp47fvb18_chip_features },
+ { .compatible = "microchip,mcp47fvb21", .data = &mcp47fvb21_chip_features },
+ { .compatible = "microchip,mcp47fvb22", .data = &mcp47fvb22_chip_features },
+ { .compatible = "microchip,mcp47fvb24", .data = &mcp47fvb24_chip_features },
+ { .compatible = "microchip,mcp47fvb28", .data = &mcp47fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp47feb02_of_match);
+
+static struct i2c_driver mcp47feb02_driver = {
+ .driver = {
+ .name = "mcp47feb02",
+ .of_match_table = mcp47feb02_of_match,
+ .pm = pm_sleep_ptr(&mcp47feb02_pm_ops),
+ },
+ .probe = mcp47feb02_probe,
+ .id_table = mcp47feb02_id,
+};
+module_i2c_driver(mcp47feb02_driver);
+
+MODULE_AUTHOR("Ariana Lazar <ariana.lazar@microchip.com>");
+MODULE_DESCRIPTION("IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c
index 08833b7035e4..fa686f785fa4 100644
--- a/drivers/iio/frequency/adf4377.c
+++ b/drivers/iio/frequency/adf4377.c
@@ -8,7 +8,9 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
@@ -435,9 +437,14 @@ struct adf4377_state {
struct gpio_desc *gpio_ce;
struct gpio_desc *gpio_enclk1;
struct gpio_desc *gpio_enclk2;
+ struct clk *clk;
+ struct clk *clkout;
+ struct clk_hw hw;
u8 buf[2] __aligned(IIO_DMA_MINALIGN);
};
+#define to_adf4377_state(h) container_of(h, struct adf4377_state, hw)
+
static const char * const adf4377_muxout_modes[] = {
[ADF4377_MUXOUT_HIGH_Z] = "high_z",
[ADF4377_MUXOUT_LKDET] = "lock_detect",
@@ -929,6 +936,110 @@ static int adf4377_freq_change(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
+static unsigned long adf4377_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+ u64 freq;
+ int ret;
+
+ ret = adf4377_get_freq(st, &freq);
+ if (ret)
+ return 0;
+
+ return freq;
+}
+
+static int adf4377_clk_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+
+ return adf4377_set_freq(st, rate);
+}
+
+static int adf4377_clk_prepare(struct clk_hw *hw)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+
+ return regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
+ ADF4377_001A_PD_CLKOUT2_MSK,
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 0) |
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 0));
+}
+
+static void adf4377_clk_unprepare(struct clk_hw *hw)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+
+ regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
+ ADF4377_001A_PD_CLKOUT2_MSK,
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 1) |
+ FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 1));
+}
+
+static int adf4377_clk_is_prepared(struct clk_hw *hw)
+{
+ struct adf4377_state *st = to_adf4377_state(hw);
+ unsigned int readval;
+ int ret;
+
+ ret = regmap_read(st->regmap, 0x1a, &readval);
+ if (ret)
+ return ret;
+
+ return !(readval & (ADF4377_001A_PD_CLKOUT1_MSK | ADF4377_001A_PD_CLKOUT2_MSK));
+}
+
+static const struct clk_ops adf4377_clk_ops = {
+ .recalc_rate = adf4377_clk_recalc_rate,
+ .set_rate = adf4377_clk_set_rate,
+ .prepare = adf4377_clk_prepare,
+ .unprepare = adf4377_clk_unprepare,
+ .is_prepared = adf4377_clk_is_prepared,
+};
+
+static int adf4377_clk_register(struct adf4377_state *st)
+{
+ struct spi_device *spi = st->spi;
+ struct device *dev = &spi->dev;
+ struct clk_init_data init;
+ struct clk_parent_data parent_data;
+ int ret;
+
+ if (!device_property_present(dev, "#clock-cells"))
+ return 0;
+
+ ret = device_property_read_string(dev, "clock-output-names", &init.name);
+ if (ret) {
+ init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw-clk",
+ dev_fwnode(dev));
+ if (!init.name)
+ return -ENOMEM;
+ }
+
+ parent_data.fw_name = "ref_in";
+
+ init.ops = &adf4377_clk_ops;
+ init.parent_data = &parent_data;
+ init.num_parents = 1;
+ init.flags = CLK_SET_RATE_PARENT;
+
+ st->hw.init = &init;
+ ret = devm_clk_hw_register(dev, &st->hw);
+ if (ret)
+ return ret;
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &st->hw);
+ if (ret)
+ return ret;
+
+ st->clkout = st->hw.clk;
+
+ return 0;
+}
+
static const struct adf4377_chip_info adf4377_chip_info = {
.name = "adf4377",
.has_gpio_enclk2 = true,
@@ -958,8 +1069,6 @@ static int adf4377_probe(struct spi_device *spi)
indio_dev->info = &adf4377_info;
indio_dev->name = "adf4377";
- indio_dev->channels = adf4377_channels;
- indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
st->regmap = regmap;
st->spi = spi;
@@ -979,6 +1088,15 @@ static int adf4377_probe(struct spi_device *spi)
if (ret)
return ret;
+ ret = adf4377_clk_register(st);
+ if (ret)
+ return ret;
+
+ if (!st->clkout) {
+ indio_dev->channels = adf4377_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
+ }
+
return devm_iio_device_register(&spi->dev, indio_dev);
}
diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c
index 8fcb41f45baa..3efe385ebedc 100644
--- a/drivers/iio/gyro/adxrs290.c
+++ b/drivers/iio/gyro/adxrs290.c
@@ -597,7 +597,7 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&st->spi->dev, st->spi->irq,
&iio_trigger_generic_data_rdy_poll,
- IRQF_ONESHOT, "adxrs290_irq", st->dready_trig);
+ IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig);
if (ret < 0)
return dev_err_probe(&st->spi->dev, ret,
"request irq %d failed\n", st->spi->irq);
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
index a624400a239c..cf97adfa9727 100644
--- a/drivers/iio/gyro/itg3200_buffer.c
+++ b/drivers/iio/gyro/itg3200_buffer.c
@@ -118,11 +118,9 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev)
if (!st->trig)
return -ENOMEM;
- ret = request_irq(st->i2c->irq,
- &iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_RISING,
- "itg3200_data_rdy",
- st->trig);
+ ret = request_irq(st->i2c->irq, &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "itg3200_data_rdy", st->trig);
if (ret)
goto error_free_trig;
diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c
index cd8a2dae56cd..bfe95ec1abda 100644
--- a/drivers/iio/gyro/itg3200_core.c
+++ b/drivers/iio/gyro/itg3200_core.c
@@ -93,6 +93,8 @@ static int itg3200_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
reg = (u8)chan->address;
ret = itg3200_read_reg_s16(indio_dev, reg, val);
+ if (ret)
+ return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
index 67ae7d1012bc..ee2fcd20545d 100644
--- a/drivers/iio/gyro/mpu3050-core.c
+++ b/drivers/iio/gyro/mpu3050-core.c
@@ -1162,10 +1162,8 @@ int mpu3050_common_probe(struct device *dev,
mpu3050->regs[1].supply = mpu3050_reg_vlogic;
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(mpu3050->regs),
mpu3050->regs);
- if (ret) {
- dev_err(dev, "Cannot get regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot get regulators\n");
ret = mpu3050_power_up(mpu3050);
if (ret)
diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c
index 0e5a512e3bb8..d358f4d5e5da 100644
--- a/drivers/iio/health/afe4403.c
+++ b/drivers/iio/health/afe4403.c
@@ -540,11 +540,10 @@ static int afe4403_probe(struct spi_device *spi)
return ret;
}
- ret = devm_request_threaded_irq(dev, afe->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL, IRQF_ONESHOT,
- AFE4403_DRIVER_NAME,
- afe->trig);
+ ret = devm_request_irq(dev, afe->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, AFE4403_DRIVER_NAME,
+ afe->trig);
if (ret) {
dev_err(dev, "Unable to request IRQ\n");
return ret;
diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c
index 768d794e574b..032da52a96d0 100644
--- a/drivers/iio/health/afe4404.c
+++ b/drivers/iio/health/afe4404.c
@@ -547,11 +547,10 @@ static int afe4404_probe(struct i2c_client *client)
return ret;
}
- ret = devm_request_threaded_irq(dev, afe->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL, IRQF_ONESHOT,
- AFE4404_DRIVER_NAME,
- afe->trig);
+ ret = devm_request_irq(dev, afe->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, AFE4404_DRIVER_NAME,
+ afe->trig);
if (ret) {
dev_err(dev, "Unable to request IRQ\n");
return ret;
diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
index 3d441013893c..7dfdb5eb305e 100644
--- a/drivers/iio/health/max30100.c
+++ b/drivers/iio/health/max30100.c
@@ -417,13 +417,7 @@ static int max30100_read_raw(struct iio_dev *indio_dev,
* Temperature reading can only be acquired while engine
* is running
*/
- if (iio_device_claim_buffer_mode(indio_dev)) {
- /*
- * Replacing -EBUSY or other error code
- * returned by iio_device_claim_buffer_mode()
- * because user space may rely on the current
- * one.
- */
+ if (!iio_device_try_claim_buffer_mode(indio_dev)) {
ret = -EAGAIN;
} else {
ret = max30100_get_temp(data, val);
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index a48c0881a4c7..47da44efd68b 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -467,44 +467,29 @@ static int max30102_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct max30102_data *data = iio_priv(indio_dev);
- int ret = -EINVAL;
+ int ret;
switch (mask) {
- case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_RAW: {
/*
* Temperature reading can only be acquired when not in
* shutdown; leave shutdown briefly when buffer not running
*/
-any_mode_retry:
- if (iio_device_claim_buffer_mode(indio_dev)) {
- /*
- * This one is a *bit* hacky. If we cannot claim buffer
- * mode, then try direct mode so that we make sure
- * things cannot concurrently change. And we just keep
- * trying until we get one of the modes...
- */
- if (!iio_device_claim_direct(indio_dev))
- goto any_mode_retry;
-
- ret = max30102_get_temp(data, val, true);
- iio_device_release_direct(indio_dev);
- } else {
- ret = max30102_get_temp(data, val, false);
- iio_device_release_buffer_mode(indio_dev);
- }
+ IIO_DEV_GUARD_CURRENT_MODE(indio_dev);
+
+ ret = max30102_get_temp(data, val, !iio_buffer_enabled(indio_dev));
if (ret)
return ret;
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
+ }
case IIO_CHAN_INFO_SCALE:
*val = 1000; /* 62.5 */
*val2 = 16;
- ret = IIO_VAL_FRACTIONAL;
- break;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
}
-
- return ret;
}
static const struct iio_info max30102_info = {
diff --git a/drivers/iio/imu/bmi270/bmi270_i2c.c b/drivers/iio/imu/bmi270/bmi270_i2c.c
index b909a421ad01..b92da4e0776f 100644
--- a/drivers/iio/imu/bmi270/bmi270_i2c.c
+++ b/drivers/iio/imu/bmi270/bmi270_i2c.c
@@ -37,6 +37,7 @@ static const struct i2c_device_id bmi270_i2c_id[] = {
{ "bmi270", (kernel_ulong_t)&bmi270_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(i2c, bmi270_i2c_id);
static const struct acpi_device_id bmi270_acpi_match[] = {
/* GPD Win Mini, Aya Neo AIR Pro, OXP Mini Pro, etc. */
@@ -45,12 +46,14 @@ static const struct acpi_device_id bmi270_acpi_match[] = {
{ "BMI0260", (kernel_ulong_t)&bmi260_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(acpi, bmi270_acpi_match);
static const struct of_device_id bmi270_of_match[] = {
{ .compatible = "bosch,bmi260", .data = &bmi260_chip_info },
{ .compatible = "bosch,bmi270", .data = &bmi270_chip_info },
{ }
};
+MODULE_DEVICE_TABLE(of, bmi270_of_match);
static struct i2c_driver bmi270_i2c_driver = {
.driver = {
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
index 30f6a9595eea..727b03d541a5 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c
@@ -59,10 +59,7 @@ int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (!iio_device_claim_direct(indio_dev))
- return -EBUSY;
ret = inv_icm42600_temp_read(st, &temp);
- iio_device_release_direct(indio_dev);
if (ret)
return ret;
*val = temp;
diff --git a/drivers/iio/imu/smi330/smi330_core.c b/drivers/iio/imu/smi330/smi330_core.c
index 7564f12543e0..7ec5ae7f521a 100644
--- a/drivers/iio/imu/smi330/smi330_core.c
+++ b/drivers/iio/imu/smi330/smi330_core.c
@@ -67,10 +67,6 @@
#define SMI330_CHIP_ID 0x42
#define SMI330_SOFT_RESET_DELAY 2000
-/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
-#define smi330_field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
-#define smi330_field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
-
#define SMI330_ACCEL_CHANNEL(_axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -361,7 +357,7 @@ static int smi330_get_sensor_config(struct smi330_data *data,
if (ret)
return ret;
- reg_val = smi330_field_get(attr->mask, reg_val);
+ reg_val = field_get(attr->mask, reg_val);
if (attr->type == IIO_VAL_INT) {
for (i = 0; i < attr->len; i++) {
@@ -410,7 +406,7 @@ static int smi330_set_sensor_config(struct smi330_data *data,
if (ret)
return ret;
- reg_val = smi330_field_prep(attr->mask, reg_val);
+ reg_val = field_prep(attr->mask, reg_val);
ret = regmap_update_bits(data->regmap, reg, attr->mask, reg_val);
if (ret)
return ret;
@@ -475,7 +471,6 @@ static int smi330_read_avail(struct iio_dev *indio_dev,
*vals = smi330_average_attr.vals;
*length = smi330_average_attr.len;
*type = smi330_average_attr.type;
- *type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = smi330_bandwidth_attr.vals;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index 6405a5367d76..07b1773c87bd 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -79,10 +79,11 @@ enum st_lsm6dsx_hw_id {
#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \
* ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
+#define st_lsm6dsx_field_get(mask, reg) ((reg & mask) >> __ffs(mask))
-#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx) \
+#define ST_LSM6DSX_CHANNEL_ACC(addr, mod, scan_idx, events) \
{ \
- .type = chan_type, \
+ .type = IIO_ACCEL, \
.address = addr, \
.modified = 1, \
.channel2 = mod, \
@@ -96,9 +97,9 @@ enum st_lsm6dsx_hw_id {
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
- .event_spec = &st_lsm6dsx_event, \
+ .event_spec = events, \
+ .num_event_specs = ARRAY_SIZE(events), \
.ext_info = st_lsm6dsx_ext_info, \
- .num_event_specs = 1, \
}
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
@@ -260,14 +261,31 @@ struct st_lsm6dsx_shub_settings {
u8 pause;
};
+enum st_lsm6dsx_event_id {
+ ST_LSM6DSX_EVENT_WAKEUP,
+ ST_LSM6DSX_EVENT_TAP,
+ ST_LSM6DSX_EVENT_MAX
+};
+
+struct st_lsm6dsx_event_src {
+ struct st_lsm6dsx_reg value;
+ struct st_lsm6dsx_reg x_value;
+ struct st_lsm6dsx_reg y_value;
+ struct st_lsm6dsx_reg z_value;
+ u8 enable_mask;
+ u8 enable_axis_reg;
+ u8 enable_x_mask;
+ u8 enable_y_mask;
+ u8 enable_z_mask;
+ struct st_lsm6dsx_reg status;
+ u8 status_x_mask;
+ u8 status_y_mask;
+ u8 status_z_mask;
+};
+
struct st_lsm6dsx_event_settings {
struct st_lsm6dsx_reg enable_reg;
- struct st_lsm6dsx_reg wakeup_reg;
- u8 wakeup_src_reg;
- u8 wakeup_src_status_mask;
- u8 wakeup_src_z_mask;
- u8 wakeup_src_y_mask;
- u8 wakeup_src_x_mask;
+ struct st_lsm6dsx_event_src sources[ST_LSM6DSX_EVENT_MAX];
};
enum st_lsm6dsx_sensor_id {
@@ -353,8 +371,8 @@ struct st_lsm6dsx_settings {
struct {
struct st_lsm6dsx_reg irq1;
struct st_lsm6dsx_reg irq2;
- struct st_lsm6dsx_reg irq1_func;
- struct st_lsm6dsx_reg irq2_func;
+ u8 irq1_func;
+ u8 irq2_func;
struct st_lsm6dsx_reg lir;
struct st_lsm6dsx_reg clear_on_read;
struct st_lsm6dsx_reg hla;
@@ -430,7 +448,6 @@ struct st_lsm6dsx_sensor {
* @sip: Total number of samples (acc/gyro/ts) in a given pattern.
* @buff: Device read buffer.
* @irq_routing: pointer to interrupt routing configuration.
- * @event_threshold: wakeup event threshold.
* @enable_event: enabled event bitmask.
* @iio_devs: Pointers to acc/gyro iio_dev instances.
* @settings: Pointer to the specific sensor settings in use.
@@ -453,9 +470,8 @@ struct st_lsm6dsx_hw {
u8 ts_sip;
u8 sip;
- const struct st_lsm6dsx_reg *irq_routing;
- u8 event_threshold;
- u8 enable_event;
+ u8 irq_routing;
+ u8 enable_event[ST_LSM6DSX_EVENT_MAX];
u8 *buff;
@@ -471,13 +487,6 @@ struct st_lsm6dsx_hw {
} scan[ST_LSM6DSX_ID_MAX];
};
-static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_EITHER,
- .mask_separate = BIT(IIO_EV_INFO_VALUE) |
- BIT(IIO_EV_INFO_ENABLE)
-};
-
static __maybe_unused const unsigned long st_lsm6dsx_available_scan_masks[] = {
0x7, 0x0,
};
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index dc78227952a7..450cb5b47346 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -94,10 +94,41 @@
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
+static const struct iio_event_spec st_lsm6dsx_ev_motion[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_event_spec st_lsm6dsx_ev_motion_tap[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_GESTURE,
+ .dir = IIO_EV_DIR_SINGLETAP,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
- ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
- ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
- ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
+ ST_LSM6DSX_CHANNEL_ACC(0x28, IIO_MOD_X, 0, st_lsm6dsx_ev_motion),
+ ST_LSM6DSX_CHANNEL_ACC(0x2a, IIO_MOD_Y, 1, st_lsm6dsx_ev_motion),
+ ST_LSM6DSX_CHANNEL_ACC(0x2c, IIO_MOD_Z, 2, st_lsm6dsx_ev_motion),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec st_lsm6dsx_acc_tap_channels[] = {
+ ST_LSM6DSX_CHANNEL_ACC(0x28, IIO_MOD_X, 0, st_lsm6dsx_ev_motion_tap),
+ ST_LSM6DSX_CHANNEL_ACC(0x2a, IIO_MOD_Y, 1, st_lsm6dsx_ev_motion_tap),
+ ST_LSM6DSX_CHANNEL_ACC(0x2c, IIO_MOD_Z, 2, st_lsm6dsx_ev_motion_tap),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
@@ -326,14 +357,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -386,15 +411,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
.event_settings = {
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -492,14 +524,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -552,15 +578,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
.event_settings = {
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -688,14 +721,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -789,15 +816,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -937,14 +971,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(6),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -1028,15 +1056,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5b,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1152,14 +1187,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(6),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -1211,15 +1240,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5B,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x1b,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
},
- .wakeup_src_reg = 0x1b,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1248,8 +1284,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.channels = {
[ST_LSM6DSX_ID_ACC] = {
- .chan = st_lsm6dsx_acc_channels,
- .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ .chan = st_lsm6dsx_acc_tap_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_tap_channels),
},
[ST_LSM6DSX_ID_GYRO] = {
.chan = st_lsm6dsx_gyro_channels,
@@ -1329,14 +1365,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(0),
},
- .irq1_func = {
- .addr = 0x5e,
- .mask = BIT(5),
- },
- .irq2_func = {
- .addr = 0x5f,
- .mask = BIT(5),
- },
+ .irq1_func = 0x5e,
+ .irq2_func = 0x5f,
.hla = {
.addr = 0x03,
.mask = BIT(4),
@@ -1419,15 +1449,48 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x50,
.mask = BIT(7),
},
- .wakeup_reg = {
- .addr = 0x5b,
- .mask = GENMASK(5, 0),
+ .sources = {
+ [ST_LSM6DSX_EVENT_WAKEUP] = {
+ .value = {
+ .addr = 0x5b,
+ .mask = GENMASK(5, 0),
+ },
+ .enable_mask = BIT(5),
+ .status = {
+ .addr = 0x45,
+ .mask = BIT(3),
+ },
+ .status_z_mask = BIT(0),
+ .status_y_mask = BIT(1),
+ .status_x_mask = BIT(2),
+ },
+ [ST_LSM6DSX_EVENT_TAP] = {
+ .x_value = {
+ .addr = 0x57,
+ .mask = GENMASK(4, 0),
+ },
+ .y_value = {
+ .addr = 0x58,
+ .mask = GENMASK(4, 0),
+ },
+ .z_value = {
+ .addr = 0x59,
+ .mask = GENMASK(4, 0),
+ },
+ .enable_mask = BIT(6),
+ .enable_axis_reg = 0x56,
+ .enable_x_mask = BIT(3),
+ .enable_y_mask = BIT(2),
+ .enable_z_mask = BIT(1),
+ .status = {
+ .addr = 0x46,
+ .mask = BIT(5),
+ },
+ .status_x_mask = BIT(2),
+ .status_y_mask = BIT(1),
+ .status_z_mask = BIT(0),
+ },
},
- .wakeup_src_reg = 0x45,
- .wakeup_src_status_mask = BIT(3),
- .wakeup_src_z_mask = BIT(0),
- .wakeup_src_y_mask = BIT(1),
- .wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1754,20 +1817,25 @@ __st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
}
static int
-st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor, bool enable)
+st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor)
{
struct st_lsm6dsx_hw *hw = sensor->hw;
+ int event;
- if (sensor->id == ST_LSM6DSX_ID_GYRO || enable)
+ if (sensor->id != ST_LSM6DSX_ID_ACC)
return 0;
- return hw->enable_event;
+ for (event = 0; event < ST_LSM6DSX_EVENT_MAX; event++) {
+ if (hw->enable_event[event])
+ return true;
+ }
+ return false;
}
int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
bool enable)
{
- if (st_lsm6dsx_check_events(sensor, enable))
+ if (st_lsm6dsx_check_events(sensor))
return 0;
return __st_lsm6dsx_sensor_set_enable(sensor, enable);
@@ -1795,11 +1863,9 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
if (err < 0)
return err;
- if (!hw->enable_event) {
- err = st_lsm6dsx_sensor_set_enable(sensor, false);
- if (err < 0)
- return err;
- }
+ err = st_lsm6dsx_sensor_set_enable(sensor, false);
+ if (err < 0)
+ return err;
*val = (s16)le16_to_cpu(data);
@@ -1876,28 +1942,106 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
return err;
}
-static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, bool state)
+static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_event_id event, int axis,
+ bool state)
{
- const struct st_lsm6dsx_reg *reg;
+ const struct st_lsm6dsx_event_src *src;
unsigned int data;
int err;
+ u8 old_enable, new_enable;
- if (!hw->settings->irq_config.irq1_func.addr)
+ if (!hw->irq_routing)
return -ENOTSUPP;
- reg = &hw->settings->event_settings.enable_reg;
- if (reg->addr) {
- data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask);
- err = st_lsm6dsx_update_bits_locked(hw, reg->addr,
- reg->mask, data);
- if (err < 0)
- return err;
+ /* Enable/disable event interrupt */
+ src = &hw->settings->event_settings.sources[event];
+ if (src->enable_axis_reg) {
+ u8 enable_mask;
+
+ switch (axis) {
+ case IIO_MOD_X:
+ enable_mask = src->enable_x_mask;
+ break;
+ case IIO_MOD_Y:
+ enable_mask = src->enable_y_mask;
+ break;
+ case IIO_MOD_Z:
+ enable_mask = src->enable_z_mask;
+ break;
+ default:
+ enable_mask = 0;
+ }
+ if (enable_mask) {
+ data = ST_LSM6DSX_SHIFT_VAL(state, enable_mask);
+ err = st_lsm6dsx_update_bits_locked(hw,
+ src->enable_axis_reg,
+ enable_mask, data);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /*
+ * If the set of axes for which the event source is enabled does not
+ * change from empty to non-empty or vice versa, there is nothing else
+ * to do.
+ */
+ old_enable = hw->enable_event[event];
+ new_enable = state ? (old_enable | BIT(axis)) :
+ (old_enable & ~BIT(axis));
+ if (!old_enable == !new_enable)
+ return 0;
+
+ data = ST_LSM6DSX_SHIFT_VAL(state, src->enable_mask);
+ return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing,
+ src->enable_mask, data);
+}
+
+static enum st_lsm6dsx_event_id
+st_lsm6dsx_get_event_id(enum iio_event_type type)
+{
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ return ST_LSM6DSX_EVENT_WAKEUP;
+ case IIO_EV_TYPE_GESTURE:
+ return ST_LSM6DSX_EVENT_TAP;
+ default:
+ return ST_LSM6DSX_EVENT_MAX;
+ }
+}
+
+static const struct st_lsm6dsx_reg *
+st_lsm6dsx_get_event_reg(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_event_id event,
+ const struct iio_chan_spec *chan)
+{
+ const struct st_lsm6dsx_event_src *src;
+ const struct st_lsm6dsx_reg *reg;
+
+ src = &hw->settings->event_settings.sources[event];
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg = &src->x_value;
+ break;
+ case IIO_MOD_Y:
+ reg = &src->y_value;
+ break;
+ case IIO_MOD_Z:
+ reg = &src->z_value;
+ break;
+ default:
+ return NULL;
}
+ if (reg->addr)
+ return reg;
- /* Enable wakeup interrupt */
- data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask);
- return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr,
- hw->irq_routing->mask, data);
+ /*
+ * The sensor does not support configuring this event source on a per
+ * axis basis: return the register to configure the event source for all
+ * axes.
+ */
+ return &src->value;
}
static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
@@ -1907,14 +2051,26 @@ static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
enum iio_event_info info,
int *val, int *val2)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
+ const struct st_lsm6dsx_reg *reg;
+ u8 data;
+ int err;
+
+ if (event == ST_LSM6DSX_EVENT_MAX)
+ return -EINVAL;
- if (type != IIO_EV_TYPE_THRESH)
+ reg = st_lsm6dsx_get_event_reg(hw, event, chan);
+ if (!reg)
return -EINVAL;
+ err = st_lsm6dsx_read_locked(hw, reg->addr, &data, sizeof(data));
+ if (err < 0)
+ return err;
+
*val2 = 0;
- *val = hw->event_threshold;
+ *val = st_lsm6dsx_field_get(reg->mask, data);
return IIO_VAL_INT;
}
@@ -1927,27 +2083,29 @@ st_lsm6dsx_write_event(struct iio_dev *iio_dev,
enum iio_event_info info,
int val, int val2)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
const struct st_lsm6dsx_reg *reg;
unsigned int data;
int err;
- if (type != IIO_EV_TYPE_THRESH)
+ if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
if (val < 0 || val > 31)
return -EINVAL;
- reg = &hw->settings->event_settings.wakeup_reg;
+ reg = st_lsm6dsx_get_event_reg(hw, event, chan);
+ if (!reg)
+ return -EINVAL;
+
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
err = st_lsm6dsx_update_bits_locked(hw, reg->addr,
reg->mask, data);
if (err < 0)
return -EINVAL;
- hw->event_threshold = val;
-
return 0;
}
@@ -1957,13 +2115,56 @@ st_lsm6dsx_read_event_config(struct iio_dev *iio_dev,
enum iio_event_type type,
enum iio_event_direction dir)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
- if (type != IIO_EV_TYPE_THRESH)
+ if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
- return !!(hw->enable_event & BIT(chan->channel2));
+ return !!(hw->enable_event[event] & BIT(chan->channel2));
+}
+
+/**
+ * st_lsm6dsx_check_other_events - Check for enabled sensor events.
+ * @hw: Sensor hardware instance.
+ * @curr: Current event type.
+ *
+ * Return: whether any events other than @curr are enabled.
+ */
+static bool st_lsm6dsx_check_other_events(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_event_id curr)
+{
+ enum st_lsm6dsx_event_id other;
+
+ for (other = 0; other < ST_LSM6DSX_EVENT_MAX; other++) {
+ if (other != curr && hw->enable_event[other])
+ return true;
+ }
+
+ return false;
+}
+
+static int st_lsm6dsx_events_enable(struct st_lsm6dsx_sensor *sensor,
+ bool state)
+{
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ const struct st_lsm6dsx_reg *reg;
+
+ reg = &hw->settings->event_settings.enable_reg;
+ if (reg->addr) {
+ int err;
+
+ err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
+ ST_LSM6DSX_SHIFT_VAL(state, reg->mask));
+ if (err)
+ return err;
+ }
+
+ if (state || !(hw->fifo_mask & BIT(sensor->id)))
+ return __st_lsm6dsx_sensor_set_enable(sensor, state);
+
+ return 0;
}
static int
@@ -1972,45 +2173,38 @@ st_lsm6dsx_write_event_config(struct iio_dev *iio_dev,
enum iio_event_type type,
enum iio_event_direction dir, bool state)
{
+ enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
u8 enable_event;
int err;
- if (type != IIO_EV_TYPE_THRESH)
+ if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
- if (state) {
- enable_event = hw->enable_event | BIT(chan->channel2);
-
- /* do not enable events if they are already enabled */
- if (hw->enable_event)
- goto out;
- } else {
- enable_event = hw->enable_event & ~BIT(chan->channel2);
-
- /* only turn off sensor if no events is enabled */
- if (enable_event)
- goto out;
- }
+ if (state)
+ enable_event = hw->enable_event[event] | BIT(chan->channel2);
+ else
+ enable_event = hw->enable_event[event] & ~BIT(chan->channel2);
/* stop here if no changes have been made */
- if (hw->enable_event == enable_event)
+ if (hw->enable_event[event] == enable_event)
return 0;
- err = st_lsm6dsx_event_setup(hw, state);
+ err = st_lsm6dsx_event_setup(hw, event, chan->channel2, state);
if (err < 0)
return err;
mutex_lock(&hw->conf_lock);
- if (enable_event || !(hw->fifo_mask & BIT(sensor->id)))
- err = __st_lsm6dsx_sensor_set_enable(sensor, state);
+ if (enable_event)
+ err = st_lsm6dsx_events_enable(sensor, true);
+ else if (!st_lsm6dsx_check_other_events(hw, event))
+ err = st_lsm6dsx_events_enable(sensor, false);
mutex_unlock(&hw->conf_lock);
if (err < 0)
return err;
-out:
- hw->enable_event = enable_event;
+ hw->enable_event[event] = enable_event;
return 0;
}
@@ -2151,11 +2345,11 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw,
switch (drdy_pin) {
case 1:
- hw->irq_routing = &hw->settings->irq_config.irq1_func;
+ hw->irq_routing = hw->settings->irq_config.irq1_func;
*drdy_reg = &hw->settings->irq_config.irq1;
break;
case 2:
- hw->irq_routing = &hw->settings->irq_config.irq2_func;
+ hw->irq_routing = hw->settings->irq_config.irq2_func;
*drdy_reg = &hw->settings->irq_config.irq2;
break;
default:
@@ -2414,53 +2608,70 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
}
static bool
-st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw)
+st_lsm6dsx_report_events(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_event_id id,
+ enum iio_event_type type, enum iio_event_direction dir)
{
const struct st_lsm6dsx_event_settings *event_settings;
+ const struct st_lsm6dsx_event_src *src;
int err, data;
s64 timestamp;
- if (!hw->enable_event)
+ if (!hw->enable_event[id])
return false;
event_settings = &hw->settings->event_settings;
- err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg,
+ src = &event_settings->sources[id];
+ err = st_lsm6dsx_read_locked(hw, src->status.addr,
&data, sizeof(data));
if (err < 0)
return false;
timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
- if ((data & hw->settings->event_settings.wakeup_src_z_mask) &&
- (hw->enable_event & BIT(IIO_MOD_Z)))
+ if ((data & src->status_z_mask) &&
+ (hw->enable_event[id] & BIT(IIO_MOD_Z)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Z,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
+ type,
+ dir),
timestamp);
- if ((data & hw->settings->event_settings.wakeup_src_y_mask) &&
- (hw->enable_event & BIT(IIO_MOD_Y)))
+ if ((data & src->status_y_mask) &&
+ (hw->enable_event[id] & BIT(IIO_MOD_Y)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Y,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
+ type,
+ dir),
timestamp);
- if ((data & hw->settings->event_settings.wakeup_src_x_mask) &&
- (hw->enable_event & BIT(IIO_MOD_X)))
+ if ((data & src->status_x_mask) &&
+ (hw->enable_event[id] & BIT(IIO_MOD_X)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_X,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
+ type,
+ dir),
timestamp);
- return data & event_settings->wakeup_src_status_mask;
+ return data & src->status.mask;
+}
+
+static bool st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw)
+{
+ bool events_found;
+
+ events_found = st_lsm6dsx_report_events(hw, ST_LSM6DSX_EVENT_WAKEUP,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ events_found |= st_lsm6dsx_report_events(hw, ST_LSM6DSX_EVENT_TAP,
+ IIO_EV_TYPE_GESTURE,
+ IIO_EV_DIR_SINGLETAP);
+
+ return events_found;
}
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
@@ -2749,7 +2960,7 @@ static int st_lsm6dsx_suspend(struct device *dev)
continue;
if (device_may_wakeup(dev) &&
- sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) {
+ st_lsm6dsx_check_events(sensor)) {
/* Enable wake from IRQ */
enable_irq_wake(hw->irq);
continue;
@@ -2780,7 +2991,7 @@ static int st_lsm6dsx_resume(struct device *dev)
sensor = iio_priv(hw->iio_devs[i]);
if (device_may_wakeup(dev) &&
- sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event)
+ st_lsm6dsx_check_events(sensor))
disable_irq_wake(hw->irq);
if (!(hw->suspend_mask & BIT(sensor->id)))
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 117ffad4f376..819bfb9c289e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -2174,88 +2174,34 @@ int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev,
EXPORT_SYMBOL_GPL(__devm_iio_device_register);
/**
- * __iio_device_claim_direct - Keep device in direct mode
- * @indio_dev: the iio_dev associated with the device
+ * __iio_dev_mode_lock() - Locks the current IIO device mode
+ * @indio_dev: the iio_dev associated with the device
*
- * If the device is in direct mode it is guaranteed to stay
- * that way until __iio_device_release_direct() is called.
+ * If the device is either in direct or buffer mode, it's guaranteed to stay
+ * that way until __iio_dev_mode_unlock() is called.
*
- * Use with __iio_device_release_direct().
+ * This function is not meant to be used directly by drivers to protect internal
+ * state; a driver should have it's own mechanisms for that matter.
*
- * Drivers should only call iio_device_claim_direct().
- *
- * Returns: true on success, false on failure.
- */
-bool __iio_device_claim_direct(struct iio_dev *indio_dev)
-{
- struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
-
- mutex_lock(&iio_dev_opaque->mlock);
-
- if (iio_buffer_enabled(indio_dev)) {
- mutex_unlock(&iio_dev_opaque->mlock);
- return false;
- }
- return true;
-}
-EXPORT_SYMBOL_GPL(__iio_device_claim_direct);
-
-/**
- * __iio_device_release_direct - releases claim on direct mode
- * @indio_dev: the iio_dev associated with the device
- *
- * Release the claim. Device is no longer guaranteed to stay
- * in direct mode.
- *
- * Drivers should only call iio_device_release_direct().
- *
- * Use with __iio_device_claim_direct()
+ * There are very few cases where a driver actually needs to lock the current
+ * mode unconditionally. It's recommended to use iio_device_claim_direct() or
+ * iio_device_try_claim_buffer_mode() pairs or related helpers instead.
*/
-void __iio_device_release_direct(struct iio_dev *indio_dev)
+void __iio_dev_mode_lock(struct iio_dev *indio_dev)
{
- mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock);
+ mutex_lock(&to_iio_dev_opaque(indio_dev)->mlock);
}
-EXPORT_SYMBOL_GPL(__iio_device_release_direct);
+EXPORT_SYMBOL_GPL(__iio_dev_mode_lock);
/**
- * iio_device_claim_buffer_mode - Keep device in buffer mode
- * @indio_dev: the iio_dev associated with the device
- *
- * If the device is in buffer mode it is guaranteed to stay
- * that way until iio_device_release_buffer_mode() is called.
- *
- * Use with iio_device_release_buffer_mode().
- *
- * Returns: 0 on success, -EBUSY on failure.
- */
-int iio_device_claim_buffer_mode(struct iio_dev *indio_dev)
-{
- struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
-
- mutex_lock(&iio_dev_opaque->mlock);
-
- if (iio_buffer_enabled(indio_dev))
- return 0;
-
- mutex_unlock(&iio_dev_opaque->mlock);
- return -EBUSY;
-}
-EXPORT_SYMBOL_GPL(iio_device_claim_buffer_mode);
-
-/**
- * iio_device_release_buffer_mode - releases claim on buffer mode
- * @indio_dev: the iio_dev associated with the device
- *
- * Release the claim. Device is no longer guaranteed to stay
- * in buffer mode.
- *
- * Use with iio_device_claim_buffer_mode().
+ * __iio_dev_mode_unlock() - Unlocks the current IIO device mode
+ * @indio_dev: the iio_dev associated with the device
*/
-void iio_device_release_buffer_mode(struct iio_dev *indio_dev)
+void __iio_dev_mode_unlock(struct iio_dev *indio_dev)
{
mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock);
}
-EXPORT_SYMBOL_GPL(iio_device_release_buffer_mode);
+EXPORT_SYMBOL_GPL(__iio_dev_mode_unlock);
/**
* iio_device_get_current_mode() - helper function providing read-only access to
diff --git a/drivers/iio/industrialio-sw-device.c b/drivers/iio/industrialio-sw-device.c
index cdaf30a3f233..3582524de0b8 100644
--- a/drivers/iio/industrialio-sw-device.c
+++ b/drivers/iio/industrialio-sw-device.c
@@ -148,7 +148,7 @@ static void device_drop_group(struct config_group *group,
config_item_put(item);
}
-static struct configfs_group_operations device_ops = {
+static const struct configfs_group_operations device_ops = {
.make_group = &device_make_group,
.drop_item = &device_drop_group,
};
diff --git a/drivers/iio/industrialio-sw-trigger.c b/drivers/iio/industrialio-sw-trigger.c
index d86a3305d9e8..334b6b10a784 100644
--- a/drivers/iio/industrialio-sw-trigger.c
+++ b/drivers/iio/industrialio-sw-trigger.c
@@ -152,7 +152,7 @@ static void trigger_drop_group(struct config_group *group,
config_item_put(item);
}
-static struct configfs_group_operations trigger_ops = {
+static const struct configfs_group_operations trigger_ops = {
.make_group = &trigger_make_group,
.drop_item = &trigger_drop_group,
};
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index 1b4c18423048..b6ab726d1dae 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -273,9 +273,9 @@ static ssize_t in_illuminance_scale_available_show
mutex_lock(&chip->lock);
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
- len += sprintf(buf + len, "%d.%06d ",
- isl29018_scales[chip->int_time][i].scale,
- isl29018_scales[chip->int_time][i].uscale);
+ len += sysfs_emit_at(buf, len, "%d.%06d ",
+ isl29018_scales[chip->int_time][i].scale,
+ isl29018_scales[chip->int_time][i].uscale);
mutex_unlock(&chip->lock);
buf[len - 1] = '\n';
@@ -293,8 +293,8 @@ static ssize_t in_illuminance_integration_time_available_show
int len = 0;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
- len += sprintf(buf + len, "0.%06d ",
- isl29018_int_utimes[chip->type][i]);
+ len += sysfs_emit_at(buf, len, "0.%06d ",
+ isl29018_int_utimes[chip->type][i]);
buf[len - 1] = '\n';
@@ -330,7 +330,7 @@ static ssize_t proximity_on_chip_ambient_infrared_suppression_show
* Return the "proximity scheme" i.e. if the chip does on chip
* infrared suppression (1 means perform on chip suppression)
*/
- return sprintf(buf, "%d\n", chip->prox_scheme);
+ return sysfs_emit(buf, "%d\n", chip->prox_scheme);
}
static ssize_t proximity_on_chip_ambient_infrared_suppression_store
diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c
index 981c704e7df5..d6e915ab355d 100644
--- a/drivers/iio/light/opt4060.c
+++ b/drivers/iio/light/opt4060.c
@@ -302,41 +302,23 @@ static int opt4060_set_driver_state(struct iio_dev *indio_dev,
bool continuous_irq)
{
struct opt4060_chip *chip = iio_priv(indio_dev);
- int ret = 0;
-any_mode_retry:
- if (iio_device_claim_buffer_mode(indio_dev)) {
- /*
- * This one is a *bit* hacky. If we cannot claim buffer mode,
- * then try direct mode so that we make sure things cannot
- * concurrently change. And we just keep trying until we get one
- * of the modes...
- */
- if (!iio_device_claim_direct(indio_dev))
- goto any_mode_retry;
- /*
- * This path means that we managed to claim direct mode. In
- * this case the buffer isn't enabled and it's okay to leave
- * continuous mode for sampling and/or irq.
- */
- ret = opt4060_set_state_common(chip, continuous_sampling,
- continuous_irq);
- iio_device_release_direct(indio_dev);
- return ret;
- } else {
- /*
- * This path means that we managed to claim buffer mode. In
- * this case the buffer is enabled and irq and sampling must go
- * to or remain continuous, but only if the trigger is from this
- * device.
- */
- if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
- ret = opt4060_set_state_common(chip, true, true);
- else
- ret = opt4060_set_state_common(chip, continuous_sampling,
- continuous_irq);
- iio_device_release_buffer_mode(indio_dev);
- }
- return ret;
+
+ IIO_DEV_GUARD_CURRENT_MODE(indio_dev);
+
+ /*
+ * If we manage to claim buffer mode and we are using our own trigger,
+ * IRQ and sampling must go to or remain continuous.
+ */
+ if (iio_buffer_enabled(indio_dev) &&
+ iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
+ return opt4060_set_state_common(chip, true, true);
+
+ /*
+ * This path means that we managed to claim direct mode. In this case
+ * the buffer isn't enabled and it's okay to leave continuous mode for
+ * sampling and/or irq.
+ */
+ return opt4060_set_state_common(chip, continuous_sampling, continuous_irq);
}
/*
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index f8eb251eca8d..ef0abc4499b7 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -1248,7 +1248,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
"si1145_irq",
trig);
if (ret < 0) {
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 4dbb2294a843..a36c23813679 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1078,20 +1078,17 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- case IIO_CHAN_INFO_SCALE:
- if (!iio_device_claim_direct(indio_dev))
+ case IIO_CHAN_INFO_SCALE: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
/* Protect against event capture. */
- if (vcnl4010_is_in_periodic_mode(data)) {
- ret = -EBUSY;
- } else {
- ret = vcnl4000_read_raw(indio_dev, chan, val, val2,
- mask);
- }
+ if (vcnl4010_is_in_periodic_mode(data))
+ return -EBUSY;
- iio_device_release_direct(indio_dev);
- return ret;
+ return vcnl4000_read_raw(indio_dev, chan, val, val2, mask);
+ }
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_PROXIMITY:
@@ -1148,36 +1145,27 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
- int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
- if (!iio_device_claim_direct(indio_dev))
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
/* Protect against event capture. */
- if (vcnl4010_is_in_periodic_mode(data)) {
- ret = -EBUSY;
- goto end;
- }
+ if (vcnl4010_is_in_periodic_mode(data))
+ return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_PROXIMITY:
- ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
- goto end;
+ return vcnl4010_write_proxy_samp_freq(data, val, val2);
default:
- ret = -EINVAL;
- goto end;
+ return -EINVAL;
}
default:
- ret = -EINVAL;
- goto end;
+ return -EINVAL;
}
-
-end:
- iio_device_release_direct(indio_dev);
- return ret;
}
static int vcnl4010_read_event(struct iio_dev *indio_dev,
@@ -1438,14 +1426,13 @@ static int vcnl4010_config_threshold_disable(struct vcnl4000_data *data)
static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
{
struct vcnl4000_data *data = iio_priv(indio_dev);
- int ret;
if (state) {
- if (!iio_device_claim_direct(indio_dev))
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- ret = vcnl4010_config_threshold_enable(data);
- iio_device_release_direct(indio_dev);
- return ret;
+
+ return vcnl4010_config_threshold_enable(data);
} else {
return vcnl4010_config_threshold_disable(data);
}
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 81b812a29044..9345fb6d5317 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -139,6 +139,19 @@ config MMC35240
To compile this driver as a module, choose M here: the module
will be called mmc35240.
+config MMC5633
+ tristate "MEMSIC MMC5633 3-axis magnetic sensor"
+ select REGMAP_I2C
+ select REGMAP_I3C if I3C
+ depends on I2C
+ depends on I3C || !I3C
+ help
+ Say yes here to build support for the MEMSIC MMC5633 3-axis
+ magnetic sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mmc5633
+
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index dfe970fcacb8..5bd227f8c120 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o
+obj-$(CONFIG_MMC5633) += mmc5633.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index 3fd0171e5d69..d30315ad85de 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -581,7 +581,7 @@ static int ak8975_setup_irq(struct ak8975_data *data)
irq = gpiod_to_irq(data->eoc_gpiod);
rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ IRQF_TRIGGER_RISING,
dev_name(&client->dev), data);
if (rc < 0) {
dev_err(&client->dev, "irq %d request failed: %d\n", irq, rc);
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
index 6a73f6e2f1f0..a022e1805dff 100644
--- a/drivers/iio/magnetometer/bmc150_magn.c
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -906,12 +906,9 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
goto err_poweroff;
}
- ret = request_threaded_irq(irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "bmc150_magn_event",
- data->dready_trig);
+ ret = request_irq(irq, iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ "bmc150_magn_event", data->dready_trig);
if (ret < 0) {
dev_err(dev, "request irq %d failed\n", irq);
goto err_trigger_unregister;
diff --git a/drivers/iio/magnetometer/mmc5633.c b/drivers/iio/magnetometer/mmc5633.c
new file mode 100644
index 000000000000..9d8e27486963
--- /dev/null
+++ b/drivers/iio/magnetometer/mmc5633.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MMC5633 - MEMSIC 3-axis Magnetic Sensor
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ * Copyright (c) 2025, NXP
+ *
+ * IIO driver for MMC5633, base on mmc35240.c
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/i3c/device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#define MMC5633_REG_XOUT0 0x00
+#define MMC5633_REG_XOUT1 0x01
+#define MMC5633_REG_YOUT0 0x02
+#define MMC5633_REG_YOUT1 0x03
+#define MMC5633_REG_ZOUT0 0x04
+#define MMC5633_REG_ZOUT1 0x05
+#define MMC5633_REG_XOUT2 0x06
+#define MMC5633_REG_YOUT2 0x07
+#define MMC5633_REG_ZOUT2 0x08
+#define MMC5633_REG_TOUT 0x09
+
+#define MMC5633_REG_STATUS1 0x18
+#define MMC5633_REG_STATUS0 0x19
+#define MMC5633_REG_CTRL0 0x1b
+#define MMC5633_REG_CTRL1 0x1c
+#define MMC5633_REG_CTRL2 0x1d
+
+#define MMC5633_REG_ID 0x39
+
+#define MMC5633_STATUS1_MEAS_T_DONE_BIT BIT(7)
+#define MMC5633_STATUS1_MEAS_M_DONE_BIT BIT(6)
+
+#define MMC5633_CTRL0_CMM_FREQ_EN BIT(7)
+#define MMC5633_CTRL0_AUTO_ST_EN BIT(6)
+#define MMC5633_CTRL0_AUTO_SR_EN BIT(5)
+#define MMC5633_CTRL0_RESET BIT(4)
+#define MMC5633_CTRL0_SET BIT(3)
+#define MMC5633_CTRL0_MEAS_T BIT(1)
+#define MMC5633_CTRL0_MEAS_M BIT(0)
+
+#define MMC5633_CTRL1_BW_MASK GENMASK(1, 0)
+
+#define MMC5633_WAIT_SET_RESET_US (1 * USEC_PER_MSEC)
+
+#define MMC5633_HDR_CTRL0_MEAS_M 0x01
+#define MMC5633_HDR_CTRL0_MEAS_T 0x03
+#define MMC5633_HDR_CTRL0_SET 0x05
+#define MMC5633_HDR_CTRL0_RESET 0x07
+
+enum mmc5633_axis {
+ MMC5633_AXIS_X,
+ MMC5633_AXIS_Y,
+ MMC5633_AXIS_Z,
+ MMC5633_TEMPERATURE,
+};
+
+struct mmc5633_data {
+ struct regmap *regmap;
+ struct i3c_device *i3cdev;
+ struct mutex mutex; /* protect to finish one whole measurement */
+};
+
+static int mmc5633_samp_freq[][2] = {
+ { 1, 200000 },
+ { 2, 0 },
+ { 3, 500000 },
+ { 6, 600000 },
+};
+
+#define MMC5633_CHANNEL(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _axis, \
+ .address = MMC5633_AXIS_ ## _axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mmc5633_channels[] = {
+ MMC5633_CHANNEL(X),
+ MMC5633_CHANNEL(Y),
+ MMC5633_CHANNEL(Z),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = MMC5633_TEMPERATURE,
+ },
+};
+
+static int mmc5633_get_samp_freq_index(struct mmc5633_data *data,
+ int val, int val2)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mmc5633_samp_freq); i++)
+ if (mmc5633_samp_freq[i][0] == val &&
+ mmc5633_samp_freq[i][1] == val2)
+ return i;
+ return -EINVAL;
+}
+
+static int mmc5633_init(struct mmc5633_data *data)
+{
+ unsigned int reg_id;
+ int ret;
+
+ ret = regmap_read(data->regmap, MMC5633_REG_ID, &reg_id);
+ if (ret)
+ return dev_err_probe(regmap_get_device(data->regmap), ret,
+ "Error reading product id\n");
+
+ /*
+ * Make sure we restore sensor characteristics, by doing
+ * a SET/RESET sequence, the axis polarity being naturally
+ * aligned after RESET.
+ */
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_SET);
+ if (ret)
+ return ret;
+
+ /*
+ * Minimum time interval between SET or RESET to other operations is
+ * 1ms according to Operating Timing Diagram in datasheet.
+ */
+ fsleep(MMC5633_WAIT_SET_RESET_US);
+
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_RESET);
+ if (ret)
+ return ret;
+
+ /* set default sampling frequency */
+ return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
+ MMC5633_CTRL1_BW_MASK,
+ FIELD_PREP(MMC5633_CTRL1_BW_MASK, 0));
+}
+
+static int mmc5633_take_measurement(struct mmc5633_data *data, int address)
+{
+ unsigned int reg_status, val;
+ int ret;
+
+ val = (address == MMC5633_TEMPERATURE) ? MMC5633_CTRL0_MEAS_T : MMC5633_CTRL0_MEAS_M;
+ ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, val);
+ if (ret < 0)
+ return ret;
+
+ val = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
+ ret = regmap_read_poll_timeout(data->regmap, MMC5633_REG_STATUS1, reg_status,
+ reg_status & val,
+ 10 * USEC_PER_MSEC,
+ 100 * 10 * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(regmap_get_device(data->regmap), "data not ready\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool mmc5633_is_support_hdr(struct mmc5633_data *data)
+{
+ if (!data->i3cdev)
+ return false;
+
+ return i3c_device_get_supported_xfer_mode(data->i3cdev) & BIT(I3C_HDR_DDR);
+}
+
+static int mmc5633_read_measurement(struct mmc5633_data *data, int address, void *buf, size_t sz)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ u8 data_cmd[2], status[2];
+ unsigned int val, ready;
+ int ret;
+
+ if (mmc5633_is_support_hdr(data)) {
+ struct i3c_xfer xfers_wr_cmd[] = {
+ {
+ .cmd = 0x3b,
+ .len = 2,
+ .data.out = data_cmd,
+ }
+ };
+ struct i3c_xfer xfers_rd_sta_cmd[] = {
+ {
+ .cmd = 0x23 | BIT(7), /* RDSTA CMD */
+ .len = 2,
+ .data.in = status,
+ },
+ };
+ struct i3c_xfer xfers_rd_data_cmd[] = {
+ {
+ .cmd = 0x22 | BIT(7), /* RDLONG CMD */
+ .len = sz,
+ .data.in = buf,
+ },
+ };
+
+ data_cmd[0] = 0;
+ data_cmd[1] = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_HDR_CTRL0_MEAS_T : MMC5633_HDR_CTRL0_MEAS_M;
+
+ ret = i3c_device_do_xfers(data->i3cdev, xfers_wr_cmd,
+ ARRAY_SIZE(xfers_wr_cmd), I3C_HDR_DDR);
+ if (ret < 0)
+ return ret;
+
+ ready = (address == MMC5633_TEMPERATURE) ?
+ MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
+ ret = read_poll_timeout(i3c_device_do_xfers, val,
+ val || (status[0] & ready),
+ 10 * USEC_PER_MSEC,
+ 100 * 10 * USEC_PER_MSEC, 0,
+ data->i3cdev, xfers_rd_sta_cmd,
+ ARRAY_SIZE(xfers_rd_sta_cmd), I3C_HDR_DDR);
+ if (ret) {
+ dev_err(dev, "data not ready\n");
+ return ret;
+ }
+ if (val) {
+ dev_err(dev, "i3c transfer error\n");
+ return val;
+ }
+ return i3c_device_do_xfers(data->i3cdev, xfers_rd_data_cmd,
+ ARRAY_SIZE(xfers_rd_data_cmd), I3C_HDR_DDR);
+ }
+
+ /* Fallback to use SDR/I2C mode */
+ ret = mmc5633_take_measurement(data, address);
+ if (ret < 0)
+ return ret;
+
+ if (address == MMC5633_TEMPERATURE)
+ /*
+ * Put tempeature to last byte of buff to align HDR case.
+ * I3C will early terminate data read if previous data is not
+ * available.
+ */
+ return regmap_bulk_read(data->regmap, MMC5633_REG_TOUT, buf + sz - 1, 1);
+
+ return regmap_bulk_read(data->regmap, MMC5633_REG_XOUT0, buf, sz);
+}
+
+/* X,Y,Z 3 channels, each channel has 3 byte and TEMP */
+#define MMC5633_ALL_SIZE (3 * 3 + 1)
+
+static int mmc5633_get_raw(struct mmc5633_data *data, int index, unsigned char *buf, int *val)
+{
+ if (index == MMC5633_TEMPERATURE) {
+ *val = buf[MMC5633_ALL_SIZE - 1];
+ return 0;
+ }
+ /*
+ * X[19..12] X[11..4] Y[19..12] Y[11..4] Z[19..12] Z[11..4] X[3..0] Y[3..0] Z[3..0]
+ */
+ *val = get_unaligned_be16(buf + 2 * index) << 4;
+ *val |= buf[index + 6] >> 4;
+
+ return 0;
+}
+
+static int mmc5633_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct mmc5633_data *data = iio_priv(indio_dev);
+ char buf[MMC5633_ALL_SIZE];
+ unsigned int reg, i;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ scoped_guard(mutex, &data->mutex) {
+ ret = mmc5633_read_measurement(data, chan->address, buf, MMC5633_ALL_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = mmc5633_get_raw(data, chan->address, buf, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_MAGN) {
+ *val = 0;
+ *val2 = 62500;
+ } else {
+ *val = 0;
+ *val2 = 800000000; /* 0.8C */
+ }
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ *val = -75;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ scoped_guard(mutex, &data->mutex) {
+ ret = regmap_read(data->regmap, MMC5633_REG_CTRL1, &reg);
+ if (ret < 0)
+ return ret;
+ }
+
+ i = FIELD_GET(MMC5633_CTRL1_BW_MASK, reg);
+ if (i >= ARRAY_SIZE(mmc5633_samp_freq))
+ return -EINVAL;
+
+ *val = mmc5633_samp_freq[i][0];
+ *val2 = mmc5633_samp_freq[i][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc5633_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct mmc5633_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ ret = mmc5633_get_samp_freq_index(data, val, val2);
+ if (ret < 0)
+ return ret;
+
+ guard(mutex)(&data->mutex);
+
+ return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
+ MMC5633_CTRL1_BW_MASK,
+ FIELD_PREP(MMC5633_CTRL1_BW_MASK, ret));
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc5633_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)mmc5633_samp_freq;
+ *length = ARRAY_SIZE(mmc5633_samp_freq) * 2;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mmc5633_info = {
+ .read_raw = mmc5633_read_raw,
+ .write_raw = mmc5633_write_raw,
+ .read_avail = mmc5633_read_avail,
+};
+
+static bool mmc5633_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_CTRL0:
+ case MMC5633_REG_CTRL1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc5633_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_XOUT0:
+ case MMC5633_REG_XOUT1:
+ case MMC5633_REG_YOUT0:
+ case MMC5633_REG_YOUT1:
+ case MMC5633_REG_ZOUT0:
+ case MMC5633_REG_ZOUT1:
+ case MMC5633_REG_XOUT2:
+ case MMC5633_REG_YOUT2:
+ case MMC5633_REG_ZOUT2:
+ case MMC5633_REG_TOUT:
+ case MMC5633_REG_STATUS1:
+ case MMC5633_REG_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc5633_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC5633_REG_CTRL0:
+ case MMC5633_REG_CTRL1:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct reg_default mmc5633_reg_defaults[] = {
+ { MMC5633_REG_CTRL0, 0x00 },
+ { MMC5633_REG_CTRL1, 0x00 },
+};
+
+static const struct regmap_config mmc5633_regmap_config = {
+ .name = "mmc5633_regmap",
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MMC5633_REG_ID,
+ .cache_type = REGCACHE_MAPLE,
+
+ .writeable_reg = mmc5633_is_writeable_reg,
+ .readable_reg = mmc5633_is_readable_reg,
+ .volatile_reg = mmc5633_is_volatile_reg,
+
+ .reg_defaults = mmc5633_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(mmc5633_reg_defaults),
+};
+
+static int mmc5633_common_probe(struct regmap *regmap, char *name,
+ struct i3c_device *i3cdev)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct mmc5633_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ data->regmap = regmap;
+ data->i3cdev = i3cdev;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &mmc5633_info;
+ indio_dev->name = name;
+ indio_dev->channels = mmc5633_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mmc5633_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = mmc5633_init(data);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "mmc5633 chip init failed\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int mmc5633_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ regcache_cache_only(regmap, true);
+
+ return 0;
+}
+
+static int mmc5633_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ regcache_mark_dirty(regmap);
+ ret = regcache_sync_region(regmap, MMC5633_REG_CTRL0, MMC5633_REG_CTRL1);
+ if (ret)
+ dev_err(dev, "Failed to restore control registers\n");
+
+ regcache_cache_only(regmap, false);
+
+ return 0;
+}
+
+static int mmc5633_i2c_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
+
+ return mmc5633_common_probe(regmap, client->name, NULL);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mmc5633_pm_ops, mmc5633_suspend, mmc5633_resume);
+
+static const struct of_device_id mmc5633_of_match[] = {
+ { .compatible = "memsic,mmc5603" },
+ { .compatible = "memsic,mmc5633" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mmc5633_of_match);
+
+static const struct i2c_device_id mmc5633_i2c_id[] = {
+ { "mmc5603" },
+ { "mmc5633" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id);
+
+static struct i2c_driver mmc5633_i2c_driver = {
+ .driver = {
+ .name = "mmc5633_i2c",
+ .of_match_table = mmc5633_of_match,
+ .pm = pm_sleep_ptr(&mmc5633_pm_ops),
+ },
+ .probe = mmc5633_i2c_probe,
+ .id_table = mmc5633_i2c_id,
+};
+
+static const struct i3c_device_id mmc5633_i3c_ids[] = {
+ I3C_DEVICE(0x0251, 0x0000, NULL),
+ { }
+};
+MODULE_DEVICE_TABLE(i3c, mmc5633_i3c_ids);
+
+static int mmc5633_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct device *dev = i3cdev_to_dev(i3cdev);
+ struct regmap *regmap;
+ char *name;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "mmc5633_%s", dev_name(dev));
+ if (!name)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i3c(i3cdev, &mmc5633_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register i3c regmap\n");
+
+ return mmc5633_common_probe(regmap, name, i3cdev);
+}
+
+static struct i3c_driver mmc5633_i3c_driver = {
+ .driver = {
+ .name = "mmc5633_i3c",
+ },
+ .probe = mmc5633_i3c_probe,
+ .id_table = mmc5633_i3c_ids,
+};
+module_i3c_i2c_driver(mmc5633_i3c_driver, &mmc5633_i2c_driver)
+
+MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>");
+MODULE_DESCRIPTION("MEMSIC MMC5633 magnetic sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 2fe9dc90cceb..838a8340c4c0 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -16,6 +16,35 @@ config ABP060MG
To compile this driver as a module, choose M here: the module
will be called abp060mg.
+config ABP2030PA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config ABP2030PA_I2C
+ tristate "Honeywell ABP2 pressure sensor series I2C driver"
+ depends on I2C
+ select ABP2030PA
+ help
+ Say Y here to build I2C bus support for the Honeywell ABP2
+ series pressure and temperature digital sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp2030pa_i2c and you will also get abp2030pa
+ for the core module.
+
+config ABP2030PA_SPI
+ tristate "Honeywell ABP2 pressure sensor series SPI driver"
+ depends on SPI_MASTER
+ select ABP2030PA
+ help
+ Say Y here to build SPI bus support for the Honeywell ABP2
+ series pressure and temperature digital sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp2030pa_spi and you will also get abp2030pa
+ for the core module.
+
config ROHM_BM1390
tristate "ROHM BM1390GLV-Z pressure sensor driver"
depends on I2C
@@ -187,29 +216,33 @@ config MPL3115
will be called mpl3115.
config MPRLS0025PA
- tristate "Honeywell MPRLS0025PA (MicroPressure sensors series)"
- depends on (I2C || SPI_MASTER)
- select MPRLS0025PA_I2C if I2C
- select MPRLS0025PA_SPI if SPI_MASTER
+ tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
- help
- Say Y here to build support for the Honeywell MicroPressure pressure
- sensor series. There are many different types with different pressure
- range. These ranges can be set up in the device tree.
-
- To compile this driver as a module, choose M here: the module will be
- called mprls0025pa.
config MPRLS0025PA_I2C
- tristate
- depends on MPRLS0025PA
+ tristate "Honeywell MPR pressure sensor series I2C driver"
depends on I2C
+ select MPRLS0025PA
+ help
+ Say Y here to build I2C bus support for the Honeywell MicroPressure
+ series sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mprls0025pa_i2c and you will also get mprls0025pa
+ for the core module.
config MPRLS0025PA_SPI
- tristate
- depends on MPRLS0025PA
+ tristate "Honeywell MPR pressure sensor series SPI driver"
depends on SPI_MASTER
+ select MPRLS0025PA
+ help
+ Say Y here to build SPI bus support for the Honeywell MicroPressure
+ series sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mprls0025pa_spi and you will also get mprls0025pa
+ for the core module.
config MS5611
tristate "Measurement Specialties MS5611 pressure sensor driver"
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index a21443e992b9..bc0d11a20acc 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -5,6 +5,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ABP060MG) += abp060mg.o
+obj-$(CONFIG_ABP2030PA) += abp2030pa.o
+obj-$(CONFIG_ABP2030PA_I2C) += abp2030pa_i2c.o
+obj-$(CONFIG_ABP2030PA_SPI) += abp2030pa_spi.o
obj-$(CONFIG_ADP810) += adp810.o
obj-$(CONFIG_ROHM_BM1390) += rohm-bm1390.o
obj-$(CONFIG_BMP280) += bmp280.o
diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c
new file mode 100644
index 000000000000..4ca056a73cef
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/basic-abp2-series/documents/sps-siot-abp2-series-datasheet-32350268-en.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "abp2030pa.h"
+
+/* Status byte flags */
+#define ABP2_ST_POWER BIT(6) /* 1 if device is powered */
+#define ABP2_ST_BUSY BIT(5) /* 1 if device is busy */
+
+#define ABP2_CMD_NOP 0xf0
+#define ABP2_CMD_SYNC 0xaa
+#define ABP2_PKT_SYNC_LEN 3
+#define ABP2_PKT_NOP_LEN ABP2_MEASUREMENT_RD_SIZE
+
+struct abp2_func_spec {
+ u32 output_min;
+ u32 output_max;
+};
+
+/* transfer function A: 10% to 90% of 2^24 */
+static const struct abp2_func_spec abp2_func_spec[] = {
+ [ABP2_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 },
+};
+
+enum abp2_variants {
+ ABP2001BA, ABP21_6BA, ABP22_5BA, ABP2004BA, ABP2006BA, ABP2008BA,
+ ABP2010BA, ABP2012BA, ABP2001BD, ABP21_6BD, ABP22_5BD, ABP2004BD,
+ ABP2001BG, ABP21_6BG, ABP22_5BG, ABP2004BG, ABP2006BG, ABP2008BG,
+ ABP2010BG, ABP2012BG, ABP2001GG, ABP21_2GG, ABP2100KA, ABP2160KA,
+ ABP2250KA, ABP2001KD, ABP21_6KD, ABP22_5KD, ABP2004KD, ABP2006KD,
+ ABP2010KD, ABP2016KD, ABP2025KD, ABP2040KD, ABP2060KD, ABP2100KD,
+ ABP2160KD, ABP2250KD, ABP2400KD, ABP2001KG, ABP21_6KG, ABP22_5KG,
+ ABP2004KG, ABP2006KG, ABP2010KG, ABP2016KG, ABP2025KG, ABP2040KG,
+ ABP2060KG, ABP2100KG, ABP2160KG, ABP2250KG, ABP2400KG, ABP2600KG,
+ ABP2800KG, ABP2250LD, ABP2600LD, ABP2600LG, ABP22_5MD, ABP2006MD,
+ ABP2010MD, ABP2016MD, ABP2025MD, ABP2040MD, ABP2060MD, ABP2100MD,
+ ABP2160MD, ABP2250MD, ABP2400MD, ABP2600MD, ABP2006MG, ABP2010MG,
+ ABP2016MG, ABP2025MG, ABP2040MG, ABP2060MG, ABP2100MG, ABP2160MG,
+ ABP2250MG, ABP2400MG, ABP2600MG, ABP2001ND, ABP2002ND, ABP2004ND,
+ ABP2005ND, ABP2010ND, ABP2020ND, ABP2030ND, ABP2002NG, ABP2004NG,
+ ABP2005NG, ABP2010NG, ABP2020NG, ABP2030NG, ABP2015PA, ABP2030PA,
+ ABP2060PA, ABP2100PA, ABP2150PA, ABP2175PA, ABP2001PD, ABP2005PD,
+ ABP2015PD, ABP2030PD, ABP2060PD, ABP2001PG, ABP2005PG, ABP2015PG,
+ ABP2030PG, ABP2060PG, ABP2100PG, ABP2150PG, ABP2175PG,
+};
+
+static const char * const abp2_triplet_variants[] = {
+ [ABP2001BA] = "001BA", [ABP21_6BA] = "1.6BA", [ABP22_5BA] = "2.5BA",
+ [ABP2004BA] = "004BA", [ABP2006BA] = "006BA", [ABP2008BA] = "008BA",
+ [ABP2010BA] = "010BA", [ABP2012BA] = "012BA", [ABP2001BD] = "001BD",
+ [ABP21_6BD] = "1.6BD", [ABP22_5BD] = "2.5BD", [ABP2004BD] = "004BD",
+ [ABP2001BG] = "001BG", [ABP21_6BG] = "1.6BG", [ABP22_5BG] = "2.5BG",
+ [ABP2004BG] = "004BG", [ABP2006BG] = "006BG", [ABP2008BG] = "008BG",
+ [ABP2010BG] = "010BG", [ABP2012BG] = "012BG", [ABP2001GG] = "001GG",
+ [ABP21_2GG] = "1.2GG", [ABP2100KA] = "100KA", [ABP2160KA] = "160KA",
+ [ABP2250KA] = "250KA", [ABP2001KD] = "001KD", [ABP21_6KD] = "1.6KD",
+ [ABP22_5KD] = "2.5KD", [ABP2004KD] = "004KD", [ABP2006KD] = "006KD",
+ [ABP2010KD] = "010KD", [ABP2016KD] = "016KD", [ABP2025KD] = "025KD",
+ [ABP2040KD] = "040KD", [ABP2060KD] = "060KD", [ABP2100KD] = "100KD",
+ [ABP2160KD] = "160KD", [ABP2250KD] = "250KD", [ABP2400KD] = "400KD",
+ [ABP2001KG] = "001KG", [ABP21_6KG] = "1.6KG", [ABP22_5KG] = "2.5KG",
+ [ABP2004KG] = "004KG", [ABP2006KG] = "006KG", [ABP2010KG] = "010KG",
+ [ABP2016KG] = "016KG", [ABP2025KG] = "025KG", [ABP2040KG] = "040KG",
+ [ABP2060KG] = "060KG", [ABP2100KG] = "100KG", [ABP2160KG] = "160KG",
+ [ABP2250KG] = "250KG", [ABP2400KG] = "400KG", [ABP2600KG] = "600KG",
+ [ABP2800KG] = "800KG", [ABP2250LD] = "250LD", [ABP2600LD] = "600LD",
+ [ABP2600LG] = "600LG", [ABP22_5MD] = "2.5MD", [ABP2006MD] = "006MD",
+ [ABP2010MD] = "010MD", [ABP2016MD] = "016MD", [ABP2025MD] = "025MD",
+ [ABP2040MD] = "040MD", [ABP2060MD] = "060MD", [ABP2100MD] = "100MD",
+ [ABP2160MD] = "160MD", [ABP2250MD] = "250MD", [ABP2400MD] = "400MD",
+ [ABP2600MD] = "600MD", [ABP2006MG] = "006MG", [ABP2010MG] = "010MG",
+ [ABP2016MG] = "016MG", [ABP2025MG] = "025MG", [ABP2040MG] = "040MG",
+ [ABP2060MG] = "060MG", [ABP2100MG] = "100MG", [ABP2160MG] = "160MG",
+ [ABP2250MG] = "250MG", [ABP2400MG] = "400MG", [ABP2600MG] = "600MG",
+ [ABP2001ND] = "001ND", [ABP2002ND] = "002ND", [ABP2004ND] = "004ND",
+ [ABP2005ND] = "005ND", [ABP2010ND] = "010ND", [ABP2020ND] = "020ND",
+ [ABP2030ND] = "030ND", [ABP2002NG] = "002NG", [ABP2004NG] = "004NG",
+ [ABP2005NG] = "005NG", [ABP2010NG] = "010NG", [ABP2020NG] = "020NG",
+ [ABP2030NG] = "030NG", [ABP2015PA] = "015PA", [ABP2030PA] = "030PA",
+ [ABP2060PA] = "060PA", [ABP2100PA] = "100PA", [ABP2150PA] = "150PA",
+ [ABP2175PA] = "175PA", [ABP2001PD] = "001PD", [ABP2005PD] = "005PD",
+ [ABP2015PD] = "015PD", [ABP2030PD] = "030PD", [ABP2060PD] = "060PD",
+ [ABP2001PG] = "001PG", [ABP2005PG] = "005PG", [ABP2015PG] = "015PG",
+ [ABP2030PG] = "030PG", [ABP2060PG] = "060PG", [ABP2100PG] = "100PG",
+ [ABP2150PG] = "150PG", [ABP2175PG] = "175PG",
+};
+
+/**
+ * struct abp2_range_config - list of pressure ranges based on nomenclature
+ * @pmin: lowest pressure that can be measured
+ * @pmax: highest pressure that can be measured
+ */
+struct abp2_range_config {
+ s32 pmin;
+ s32 pmax;
+};
+
+/* All min max limits have been converted to pascals */
+static const struct abp2_range_config abp2_range_config[] = {
+ [ABP2001BA] = { .pmin = 0, .pmax = 100000 },
+ [ABP21_6BA] = { .pmin = 0, .pmax = 160000 },
+ [ABP22_5BA] = { .pmin = 0, .pmax = 250000 },
+ [ABP2004BA] = { .pmin = 0, .pmax = 400000 },
+ [ABP2006BA] = { .pmin = 0, .pmax = 600000 },
+ [ABP2008BA] = { .pmin = 0, .pmax = 800000 },
+ [ABP2010BA] = { .pmin = 0, .pmax = 1000000 },
+ [ABP2012BA] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2001BD] = { .pmin = -100000, .pmax = 100000 },
+ [ABP21_6BD] = { .pmin = -160000, .pmax = 160000 },
+ [ABP22_5BD] = { .pmin = -250000, .pmax = 250000 },
+ [ABP2004BD] = { .pmin = -400000, .pmax = 400000 },
+ [ABP2001BG] = { .pmin = 0, .pmax = 100000 },
+ [ABP21_6BG] = { .pmin = 0, .pmax = 160000 },
+ [ABP22_5BG] = { .pmin = 0, .pmax = 250000 },
+ [ABP2004BG] = { .pmin = 0, .pmax = 400000 },
+ [ABP2006BG] = { .pmin = 0, .pmax = 600000 },
+ [ABP2008BG] = { .pmin = 0, .pmax = 800000 },
+ [ABP2010BG] = { .pmin = 0, .pmax = 1000000 },
+ [ABP2012BG] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2001GG] = { .pmin = 0, .pmax = 1000000 },
+ [ABP21_2GG] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2100KA] = { .pmin = 0, .pmax = 100000 },
+ [ABP2160KA] = { .pmin = 0, .pmax = 160000 },
+ [ABP2250KA] = { .pmin = 0, .pmax = 250000 },
+ [ABP2001KD] = { .pmin = -1000, .pmax = 1000 },
+ [ABP21_6KD] = { .pmin = -1600, .pmax = 1600 },
+ [ABP22_5KD] = { .pmin = -2500, .pmax = 2500 },
+ [ABP2004KD] = { .pmin = -4000, .pmax = 4000 },
+ [ABP2006KD] = { .pmin = -6000, .pmax = 6000 },
+ [ABP2010KD] = { .pmin = -10000, .pmax = 10000 },
+ [ABP2016KD] = { .pmin = -16000, .pmax = 16000 },
+ [ABP2025KD] = { .pmin = -25000, .pmax = 25000 },
+ [ABP2040KD] = { .pmin = -40000, .pmax = 40000 },
+ [ABP2060KD] = { .pmin = -60000, .pmax = 60000 },
+ [ABP2100KD] = { .pmin = -100000, .pmax = 100000 },
+ [ABP2160KD] = { .pmin = -160000, .pmax = 160000 },
+ [ABP2250KD] = { .pmin = -250000, .pmax = 250000 },
+ [ABP2400KD] = { .pmin = -400000, .pmax = 400000 },
+ [ABP2001KG] = { .pmin = 0, .pmax = 1000 },
+ [ABP21_6KG] = { .pmin = 0, .pmax = 1600 },
+ [ABP22_5KG] = { .pmin = 0, .pmax = 2500 },
+ [ABP2004KG] = { .pmin = 0, .pmax = 4000 },
+ [ABP2006KG] = { .pmin = 0, .pmax = 6000 },
+ [ABP2010KG] = { .pmin = 0, .pmax = 10000 },
+ [ABP2016KG] = { .pmin = 0, .pmax = 16000 },
+ [ABP2025KG] = { .pmin = 0, .pmax = 25000 },
+ [ABP2040KG] = { .pmin = 0, .pmax = 40000 },
+ [ABP2060KG] = { .pmin = 0, .pmax = 60000 },
+ [ABP2100KG] = { .pmin = 0, .pmax = 100000 },
+ [ABP2160KG] = { .pmin = 0, .pmax = 160000 },
+ [ABP2250KG] = { .pmin = 0, .pmax = 250000 },
+ [ABP2400KG] = { .pmin = 0, .pmax = 400000 },
+ [ABP2600KG] = { .pmin = 0, .pmax = 600000 },
+ [ABP2800KG] = { .pmin = 0, .pmax = 800000 },
+ [ABP2250LD] = { .pmin = -250, .pmax = 250 },
+ [ABP2600LD] = { .pmin = -600, .pmax = 600 },
+ [ABP2600LG] = { .pmin = 0, .pmax = 600 },
+ [ABP22_5MD] = { .pmin = -250, .pmax = 250 },
+ [ABP2006MD] = { .pmin = -600, .pmax = 600 },
+ [ABP2010MD] = { .pmin = -1000, .pmax = 1000 },
+ [ABP2016MD] = { .pmin = -1600, .pmax = 1600 },
+ [ABP2025MD] = { .pmin = -2500, .pmax = 2500 },
+ [ABP2040MD] = { .pmin = -4000, .pmax = 4000 },
+ [ABP2060MD] = { .pmin = -6000, .pmax = 6000 },
+ [ABP2100MD] = { .pmin = -10000, .pmax = 10000 },
+ [ABP2160MD] = { .pmin = -16000, .pmax = 16000 },
+ [ABP2250MD] = { .pmin = -25000, .pmax = 25000 },
+ [ABP2400MD] = { .pmin = -40000, .pmax = 40000 },
+ [ABP2600MD] = { .pmin = -60000, .pmax = 60000 },
+ [ABP2006MG] = { .pmin = 0, .pmax = 600 },
+ [ABP2010MG] = { .pmin = 0, .pmax = 1000 },
+ [ABP2016MG] = { .pmin = 0, .pmax = 1600 },
+ [ABP2025MG] = { .pmin = 0, .pmax = 2500 },
+ [ABP2040MG] = { .pmin = 0, .pmax = 4000 },
+ [ABP2060MG] = { .pmin = 0, .pmax = 6000 },
+ [ABP2100MG] = { .pmin = 0, .pmax = 10000 },
+ [ABP2160MG] = { .pmin = 0, .pmax = 16000 },
+ [ABP2250MG] = { .pmin = 0, .pmax = 25000 },
+ [ABP2400MG] = { .pmin = 0, .pmax = 40000 },
+ [ABP2600MG] = { .pmin = 0, .pmax = 60000 },
+ [ABP2001ND] = { .pmin = -249, .pmax = 249 },
+ [ABP2002ND] = { .pmin = -498, .pmax = 498 },
+ [ABP2004ND] = { .pmin = -996, .pmax = 996 },
+ [ABP2005ND] = { .pmin = -1245, .pmax = 1245 },
+ [ABP2010ND] = { .pmin = -2491, .pmax = 2491 },
+ [ABP2020ND] = { .pmin = -4982, .pmax = 4982 },
+ [ABP2030ND] = { .pmin = -7473, .pmax = 7473 },
+ [ABP2002NG] = { .pmin = 0, .pmax = 498 },
+ [ABP2004NG] = { .pmin = 0, .pmax = 996 },
+ [ABP2005NG] = { .pmin = 0, .pmax = 1245 },
+ [ABP2010NG] = { .pmin = 0, .pmax = 2491 },
+ [ABP2020NG] = { .pmin = 0, .pmax = 4982 },
+ [ABP2030NG] = { .pmin = 0, .pmax = 7473 },
+ [ABP2015PA] = { .pmin = 0, .pmax = 103421 },
+ [ABP2030PA] = { .pmin = 0, .pmax = 206843 },
+ [ABP2060PA] = { .pmin = 0, .pmax = 413685 },
+ [ABP2100PA] = { .pmin = 0, .pmax = 689476 },
+ [ABP2150PA] = { .pmin = 0, .pmax = 1034214 },
+ [ABP2175PA] = { .pmin = 0, .pmax = 1206583 },
+ [ABP2001PD] = { .pmin = -6895, .pmax = 6895 },
+ [ABP2005PD] = { .pmin = -34474, .pmax = 34474 },
+ [ABP2015PD] = { .pmin = -103421, .pmax = 103421 },
+ [ABP2030PD] = { .pmin = -206843, .pmax = 206843 },
+ [ABP2060PD] = { .pmin = -413685, .pmax = 413685 },
+ [ABP2001PG] = { .pmin = 0, .pmax = 6895 },
+ [ABP2005PG] = { .pmin = 0, .pmax = 34474 },
+ [ABP2015PG] = { .pmin = 0, .pmax = 103421 },
+ [ABP2030PG] = { .pmin = 0, .pmax = 206843 },
+ [ABP2060PG] = { .pmin = 0, .pmax = 413685 },
+ [ABP2100PG] = { .pmin = 0, .pmax = 689476 },
+ [ABP2150PG] = { .pmin = 0, .pmax = 1034214 },
+ [ABP2175PG] = { .pmin = 0, .pmax = 1206583 },
+};
+
+static_assert(ARRAY_SIZE(abp2_triplet_variants) == ARRAY_SIZE(abp2_range_config));
+
+static int abp2_get_measurement(struct abp2_data *data)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ reinit_completion(&data->completion);
+
+ ret = data->ops->write(data, ABP2_CMD_SYNC, ABP2_PKT_SYNC_LEN);
+ if (ret < 0)
+ return ret;
+
+ if (data->irq > 0) {
+ ret = wait_for_completion_timeout(&data->completion, HZ);
+ if (!ret) {
+ dev_err(dev, "timeout waiting for EOC interrupt\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ fsleep(5 * USEC_PER_MSEC);
+ }
+
+ memset(data->rx_buf, 0, sizeof(data->rx_buf));
+ ret = data->ops->read(data, ABP2_CMD_NOP, ABP2_PKT_NOP_LEN);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Status byte flags
+ * bit7 SANITY_CHK - must always be 0
+ * bit6 ABP2_ST_POWER - 1 if device is powered
+ * bit5 ABP2_ST_BUSY - 1 if device has no new conversion ready
+ * bit4 SANITY_CHK - must always be 0
+ * bit3 SANITY_CHK - must always be 0
+ * bit2 MEMORY_ERR - 1 if integrity test has failed
+ * bit1 SANITY_CHK - must always be 0
+ * bit0 MATH_ERR - 1 during internal math saturation error
+ */
+
+ if (data->rx_buf[0] == (ABP2_ST_POWER | ABP2_ST_BUSY))
+ return -EBUSY;
+
+ /*
+ * The ABP2 sensor series seem to have a noticeable latch-up sensitivity.
+ * A partial latch-up condition manifests as either
+ * - output of invalid status bytes
+ * - zeroed out conversions (despite a normal status byte)
+ * - the MOSI line being pulled low randomly in sync with the SCLK
+ * signal (visible during the ABP2_CMD_NOP command).
+ * https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1588325/am3358-spi-tx-data-corruption
+ */
+
+ if (data->rx_buf[0] != ABP2_ST_POWER) {
+ dev_err(data->dev,
+ "unexpected status byte 0x%02x\n", data->rx_buf[0]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static irqreturn_t abp2_eoc_handler(int irq, void *private)
+{
+ struct abp2_data *data = private;
+
+ complete(&data->completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t abp2_trigger_handler(int irq, void *private)
+{
+ int ret;
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct abp2_data *data = iio_priv(indio_dev);
+
+ ret = abp2_get_measurement(data);
+ if (ret < 0)
+ goto out_notify_done;
+
+ data->scan.chan[0] = get_unaligned_be24(&data->rx_buf[1]);
+ data->scan.chan[1] = get_unaligned_be24(&data->rx_buf[4]);
+
+ iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
+ iio_get_time_ns(indio_dev));
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * IIO ABI expects
+ * value = (conv + offset) * scale
+ *
+ * temp[C] = conv * a + b
+ * where a = 200/16777215; b = -50
+ *
+ * temp[C] = (conv + (b/a)) * a * (1000)
+ * =>
+ * scale = a * 1000 = .0000119209296 * 1000 = .01192092966562
+ * offset = b/a = -50 * 16777215 / 200 = -4194303.75
+ *
+ * pressure = (conv - Omin) * Q + Pmin =
+ * ((conv - Omin) + Pmin/Q) * Q
+ * =>
+ * scale = Q = (Pmax - Pmin) / (Omax - Omin)
+ * offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin
+ */
+static int abp2_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct abp2_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = abp2_get_measurement(data);
+ if (ret < 0)
+ return ret;
+
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ *val = get_unaligned_be24(&data->rx_buf[1]);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = get_unaligned_be24(&data->rx_buf[4]);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 11920929;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_PRESSURE:
+ *val = data->p_scale;
+ *val2 = data->p_scale_dec;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = -4194304;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = data->p_offset;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info abp2_info = {
+ .read_raw = &abp2_read_raw,
+};
+
+static const unsigned long abp2_scan_masks[] = {0x3, 0};
+
+static const struct iio_chan_spec abp2_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq)
+{
+ int ret;
+ struct abp2_data *data;
+ struct iio_dev *indio_dev;
+ const char *triplet;
+ s32 tmp;
+ s64 odelta, pdelta;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->ops = ops;
+ data->irq = irq;
+
+ init_completion(&data->completion);
+
+ indio_dev->name = "abp2030pa";
+ indio_dev->info = &abp2_info;
+ indio_dev->channels = abp2_channels;
+ indio_dev->num_channels = ARRAY_SIZE(abp2_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = abp2_scan_masks;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "can't get and enable vdd supply\n");
+
+ ret = device_property_read_string(dev, "honeywell,pressure-triplet",
+ &triplet);
+ if (ret) {
+ ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
+ &data->pmin);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmin-pascal could not be read\n");
+
+ ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
+ &data->pmax);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmax-pascal could not be read\n");
+ } else {
+ ret = device_property_match_property_string(dev,
+ "honeywell,pressure-triplet",
+ abp2_triplet_variants,
+ ARRAY_SIZE(abp2_triplet_variants));
+ if (ret < 0)
+ return dev_err_probe(dev, -EINVAL, "honeywell,pressure-triplet is invalid\n");
+
+ data->pmin = abp2_range_config[ret].pmin;
+ data->pmax = abp2_range_config[ret].pmax;
+ }
+
+ if (data->pmin >= data->pmax)
+ return dev_err_probe(dev, -EINVAL, "pressure limits are invalid\n");
+
+ data->outmin = abp2_func_spec[data->function].output_min;
+ data->outmax = abp2_func_spec[data->function].output_max;
+
+ odelta = data->outmax - data->outmin;
+ pdelta = data->pmax - data->pmin;
+
+ data->p_scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
+ data->p_scale_dec = tmp;
+
+ data->p_offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
+
+ if (data->irq > 0) {
+ ret = devm_request_irq(dev, irq, abp2_eoc_handler, IRQF_ONESHOT,
+ dev_name(dev), data);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ abp2_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to register iio device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(abp2_common_probe, "IIO_HONEYWELL_ABP2030PA");
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/abp2030pa.h b/drivers/iio/pressure/abp2030pa.h
new file mode 100644
index 000000000000..57e5ed784686
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#ifndef _ABP2030PA_H
+#define _ABP2030PA_H
+
+#include <linux/completion.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define ABP2_MEASUREMENT_RD_SIZE 7
+
+struct device;
+
+struct abp2_data;
+struct abp2_ops;
+
+enum abp2_func_id {
+ ABP2_FUNCTION_A,
+};
+
+/**
+ * struct abp2_data
+ * @dev: current device structure
+ * @ops: pointers for bus specific read and write functions
+ * @pmin: minimal pressure in pascal
+ * @pmax: maximal pressure in pascal
+ * @outmin: minimum raw pressure in counts (based on transfer function)
+ * @outmax: maximum raw pressure in counts (based on transfer function)
+ * @function: transfer function
+ * @p_scale: pressure scale
+ * @p_scale_dec: pressure scale, decimal number
+ * @p_offset: pressure offset
+ * @irq: end of conversion - applies only to the i2c sensor
+ * @completion: handshake from irq to read
+ * @scan: channel values for buffered mode
+ * @tx_buf: transmit buffer used during the SPI communication
+ * @rx_buf: raw data provided by sensor
+ */
+struct abp2_data {
+ struct device *dev;
+ const struct abp2_ops *ops;
+ s32 pmin;
+ s32 pmax;
+ u32 outmin;
+ u32 outmax;
+ enum abp2_func_id function;
+ int p_scale;
+ int p_scale_dec;
+ int p_offset;
+ int irq;
+ struct completion completion;
+ struct {
+ u32 chan[2];
+ aligned_s64 timestamp;
+ } scan;
+ u8 rx_buf[ABP2_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[ABP2_MEASUREMENT_RD_SIZE];
+};
+
+struct abp2_ops {
+ int (*read)(struct abp2_data *data, u8 cmd, u8 nbytes);
+ int (*write)(struct abp2_data *data, u8 cmd, u8 nbytes);
+};
+
+int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq);
+
+#endif
diff --git a/drivers/iio/pressure/abp2030pa_i2c.c b/drivers/iio/pressure/abp2030pa_i2c.c
new file mode 100644
index 000000000000..9f1c1c8a9afb
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa_i2c.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "abp2030pa.h"
+
+static int abp2_i2c_read(struct abp2_data *data, u8 unused, u8 nbytes)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int ret;
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ ret = i2c_master_recv(client, data->rx_buf, nbytes);
+ if (ret < 0)
+ return ret;
+ if (ret != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+static int abp2_i2c_write(struct abp2_data *data, u8 cmd, u8 nbytes)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int ret;
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+ ret = i2c_master_send(client, data->tx_buf, nbytes);
+ if (ret < 0)
+ return ret;
+ if (ret != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct abp2_ops abp2_i2c_ops = {
+ .read = abp2_i2c_read,
+ .write = abp2_i2c_write,
+};
+
+static int abp2_i2c_probe(struct i2c_client *client)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ return abp2_common_probe(&client->dev, &abp2_i2c_ops, client->irq);
+}
+
+static const struct of_device_id abp2_i2c_match[] = {
+ { .compatible = "honeywell,abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abp2_i2c_match);
+
+static const struct i2c_device_id abp2_i2c_id[] = {
+ { "abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abp2_i2c_id);
+
+static struct i2c_driver abp2_i2c_driver = {
+ .driver = {
+ .name = "abp2030pa",
+ .of_match_table = abp2_i2c_match,
+ },
+ .probe = abp2_i2c_probe,
+ .id_table = abp2_i2c_id,
+};
+module_i2c_driver(abp2_i2c_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor i2c driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_ABP2030PA");
diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c
new file mode 100644
index 000000000000..eaea9a3ebf11
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa_spi.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#include <linux/errno.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "abp2030pa.h"
+
+static int abp2_spi_xfer(struct abp2_data *data, u8 cmd, u8 nbytes)
+{
+ struct spi_device *spi = to_spi_device(data->dev);
+ struct spi_transfer xfer = { };
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+ xfer.tx_buf = data->tx_buf;
+ xfer.rx_buf = data->rx_buf;
+ xfer.len = nbytes;
+
+ return spi_sync_transfer(spi, &xfer, 1);
+}
+
+static const struct abp2_ops abp2_spi_ops = {
+ .read = abp2_spi_xfer,
+ .write = abp2_spi_xfer,
+};
+
+static int abp2_spi_probe(struct spi_device *spi)
+{
+ return abp2_common_probe(&spi->dev, &abp2_spi_ops, spi->irq);
+}
+
+static const struct of_device_id abp2_spi_match[] = {
+ { .compatible = "honeywell,abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abp2_spi_match);
+
+static const struct spi_device_id abp2_spi_id[] = {
+ { "abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, abp2_spi_id);
+
+static struct spi_driver abp2_spi_driver = {
+ .driver = {
+ .name = "abp2030pa",
+ .of_match_table = abp2_spi_match,
+ },
+ .probe = abp2_spi_probe,
+ .id_table = abp2_spi_id,
+};
+module_spi_driver(abp2_spi_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor spi driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_ABP2030PA");
diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c
index 8bad7162fec6..46feb27fe632 100644
--- a/drivers/iio/pressure/dlhl60d.c
+++ b/drivers/iio/pressure/dlhl60d.c
@@ -306,10 +306,9 @@ static int dlh_probe(struct i2c_client *client)
indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
if (client->irq > 0) {
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- dlh_interrupt, NULL,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- st->info->name, indio_dev);
+ ret = devm_request_irq(&client->dev, client->irq, dlh_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ st->info->name, indio_dev);
if (ret) {
dev_err(&client->dev, "failed to allocate threaded irq");
return ret;
diff --git a/drivers/iio/pressure/mprls0025pa.c b/drivers/iio/pressure/mprls0025pa.c
index 2336f2760eae..e8c495f336ff 100644
--- a/drivers/iio/pressure/mprls0025pa.c
+++ b/drivers/iio/pressure/mprls0025pa.c
@@ -3,6 +3,7 @@
* MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
*
* Copyright (c) Andreas Klinger <ak@it-klinger.de>
+ * Copyright (c) 2023-2025 Petre Rodan <petre.rodan@subdimension.ro>
*
* Data sheet:
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
@@ -12,15 +13,24 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
+#include <linux/string.h>
+#include <linux/time.h>
#include <linux/units.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -33,10 +43,6 @@
/* bits in status byte */
#define MPR_ST_POWER BIT(6) /* device is powered */
#define MPR_ST_BUSY BIT(5) /* device is busy */
-#define MPR_ST_MEMORY BIT(2) /* integrity test passed */
-#define MPR_ST_MATH BIT(0) /* internal math saturation */
-
-#define MPR_ST_ERR_FLAG (MPR_ST_BUSY | MPR_ST_MEMORY | MPR_ST_MATH)
/*
* support _RAW sysfs interface:
@@ -59,7 +65,7 @@
*
* Values given to the userspace in sysfs interface:
* * raw - press_cnt
- * * offset - (-1 * outputmin) - pmin / scale
+ * * offset - (-1 * outputmin) + pmin / scale
* note: With all sensors from the datasheet pmin = 0
* which reduces the offset to (-1 * outputmin)
*/
@@ -160,8 +166,8 @@ static const struct iio_chan_spec mpr_channels[] = {
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
- .sign = 's',
- .realbits = 32,
+ .sign = 'u',
+ .realbits = 24,
.storagebits = 32,
.endianness = IIO_CPU,
},
@@ -190,15 +196,15 @@ static void mpr_reset(struct mpr_data *data)
*
* Context: The function can sleep and data->lock should be held when calling it
* Return:
- * * 0 - OK, the pressure value could be read
- * * -ETIMEDOUT - Timeout while waiting for the EOC interrupt or busy flag is
- * still set after nloops attempts of reading
+ * * 0 - OK, the pressure value could be read
+ * * -EBUSY - Sensor does not have a new conversion ready
+ * * -ETIMEDOUT - Timeout while waiting for the EOC interrupt
+ * * -EIO - Invalid status byte received from sensor
*/
static int mpr_read_pressure(struct mpr_data *data, s32 *press)
{
struct device *dev = data->dev;
- int ret, i;
- int nloops = 10;
+ int ret;
reinit_completion(&data->completion);
@@ -215,44 +221,38 @@ static int mpr_read_pressure(struct mpr_data *data, s32 *press)
return -ETIMEDOUT;
}
} else {
- /* wait until status indicates data is ready */
- for (i = 0; i < nloops; i++) {
- /*
- * datasheet only says to wait at least 5 ms for the
- * data but leave the maximum response time open
- * --> let's try it nloops (10) times which seems to be
- * quite long
- */
- usleep_range(5000, 10000);
- ret = data->ops->read(data, MPR_CMD_NOP, 1);
- if (ret < 0) {
- dev_err(dev,
- "error while reading, status: %d\n",
- ret);
- return ret;
- }
- if (!(data->buffer[0] & MPR_ST_ERR_FLAG))
- break;
- }
- if (i == nloops) {
- dev_err(dev, "timeout while reading\n");
- return -ETIMEDOUT;
- }
+ fsleep(5 * USEC_PER_MSEC);
}
+ memset(data->rx_buf, 0, sizeof(data->rx_buf));
ret = data->ops->read(data, MPR_CMD_NOP, MPR_PKT_NOP_LEN);
if (ret < 0)
return ret;
- if (data->buffer[0] & MPR_ST_ERR_FLAG) {
+ /*
+ * Status byte flags
+ * bit7 SANITY_CHK - must always be 0
+ * bit6 MPR_ST_POWER - 1 if device is powered
+ * bit5 MPR_ST_BUSY - 1 if device has no new conversion ready
+ * bit4 SANITY_CHK - must always be 0
+ * bit3 SANITY_CHK - must always be 0
+ * bit2 MEMORY_ERR - 1 if integrity test has failed
+ * bit1 SANITY_CHK - must always be 0
+ * bit0 MATH_ERR - 1 during internal math saturation error
+ */
+
+ if (data->rx_buf[0] == (MPR_ST_POWER | MPR_ST_BUSY))
+ return -EBUSY;
+
+ if (data->rx_buf[0] != MPR_ST_POWER) {
dev_err(data->dev,
- "unexpected status byte %02x\n", data->buffer[0]);
- return -ETIMEDOUT;
+ "unexpected status byte 0x%02x\n", data->rx_buf[0]);
+ return -EIO;
}
- *press = get_unaligned_be24(&data->buffer[1]);
+ *press = get_unaligned_be24(&data->rx_buf[1]);
- dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->buffer, *press);
+ dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->rx_buf, *press);
return 0;
}
@@ -313,8 +313,7 @@ static int mpr_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
*val = data->offset;
- *val2 = data->offset2;
- return IIO_VAL_INT_PLUS_NANO;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -330,8 +329,9 @@ int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
struct mpr_data *data;
struct iio_dev *indio_dev;
const char *triplet;
- s64 scale, offset;
+ s64 odelta, pdelta;
u32 func;
+ s32 tmp;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -356,10 +356,6 @@ int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
return dev_err_probe(dev, ret,
"can't get and enable vdd supply\n");
- ret = data->ops->init(data->dev);
- if (ret)
- return ret;
-
ret = device_property_read_u32(dev,
"honeywell,transfer-function", &func);
if (ret)
@@ -405,26 +401,19 @@ int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
data->outmin = mpr_func_spec[data->function].output_min;
data->outmax = mpr_func_spec[data->function].output_max;
- /* use 64 bit calculation for preserving a reasonable precision */
- scale = div_s64(((s64)(data->pmax - data->pmin)) * NANO,
- data->outmax - data->outmin);
- data->scale = div_s64_rem(scale, NANO, &data->scale2);
- /*
- * multiply with NANO before dividing by scale and later divide by NANO
- * again.
- */
- offset = ((-1LL) * (s64)data->outmin) * NANO -
- div_s64(div_s64((s64)data->pmin * NANO, scale), NANO);
- data->offset = div_s64_rem(offset, NANO, &data->offset2);
+ odelta = data->outmax - data->outmin;
+ pdelta = data->pmax - data->pmin;
+
+ data->scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
+ data->scale2 = tmp;
+
+ data->offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
if (data->irq > 0) {
- ret = devm_request_irq(dev, data->irq, mpr_eoc_handler,
- IRQF_TRIGGER_RISING,
- dev_name(dev),
- data);
+ ret = devm_request_irq(dev, data->irq, mpr_eoc_handler, 0,
+ dev_name(dev), data);
if (ret)
- return dev_err_probe(dev, ret,
- "request irq %d failed\n", data->irq);
+ return ret;
}
data->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
diff --git a/drivers/iio/pressure/mprls0025pa.h b/drivers/iio/pressure/mprls0025pa.h
index d62a018eaff3..9f43273e635f 100644
--- a/drivers/iio/pressure/mprls0025pa.h
+++ b/drivers/iio/pressure/mprls0025pa.h
@@ -12,10 +12,7 @@
#define _MPRLS0025PA_H
#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/device.h>
#include <linux/mutex.h>
-#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
@@ -28,9 +25,6 @@
struct device;
-struct iio_chan_spec;
-struct iio_dev;
-
struct mpr_data;
struct mpr_ops;
@@ -53,7 +47,6 @@ enum mpr_func_id {
* @scale: pressure scale
* @scale2: pressure scale, decimal number
* @offset: pressure offset
- * @offset2: pressure offset, decimal number
* @gpiod_reset: reset
* @irq: end of conversion irq. used to distinguish between irq mode and
* reading in a loop until data is ready
@@ -61,7 +54,8 @@ enum mpr_func_id {
* @chan: channel values for buffered mode
* @chan.pres: pressure value
* @chan.ts: timestamp
- * @buffer: raw conversion data
+ * @rx_buf: raw conversion data
+ * @tx_buf: output buffer
*/
struct mpr_data {
struct device *dev;
@@ -75,7 +69,6 @@ struct mpr_data {
int scale;
int scale2;
int offset;
- int offset2;
struct gpio_desc *gpiod_reset;
int irq;
struct completion completion;
@@ -83,11 +76,11 @@ struct mpr_data {
s32 pres;
aligned_s64 ts;
} chan;
- u8 buffer[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[MPR_MEASUREMENT_RD_SIZE];
};
struct mpr_ops {
- int (*init)(struct device *dev);
int (*read)(struct mpr_data *data, const u8 cmd, const u8 cnt);
int (*write)(struct mpr_data *data, const u8 cmd, const u8 cnt);
};
diff --git a/drivers/iio/pressure/mprls0025pa_i2c.c b/drivers/iio/pressure/mprls0025pa_i2c.c
index 79811fd4a02b..0fe8cfe0d7e7 100644
--- a/drivers/iio/pressure/mprls0025pa_i2c.c
+++ b/drivers/iio/pressure/mprls0025pa_i2c.c
@@ -17,11 +17,6 @@
#include "mprls0025pa.h"
-static int mpr_i2c_init(struct device *unused)
-{
- return 0;
-}
-
static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt)
{
int ret;
@@ -30,8 +25,7 @@ static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt)
if (cnt > MPR_MEASUREMENT_RD_SIZE)
return -EOVERFLOW;
- memset(data->buffer, 0, MPR_MEASUREMENT_RD_SIZE);
- ret = i2c_master_recv(client, data->buffer, cnt);
+ ret = i2c_master_recv(client, data->rx_buf, cnt);
if (ret < 0)
return ret;
else if (ret != cnt)
@@ -44,9 +38,9 @@ static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused)
{
int ret;
struct i2c_client *client = to_i2c_client(data->dev);
- u8 wdata[MPR_PKT_SYNC_LEN] = { cmd };
- ret = i2c_master_send(client, wdata, MPR_PKT_SYNC_LEN);
+ data->tx_buf[0] = cmd;
+ ret = i2c_master_send(client, data->tx_buf, MPR_PKT_SYNC_LEN);
if (ret < 0)
return ret;
else if (ret != MPR_PKT_SYNC_LEN)
@@ -56,7 +50,6 @@ static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused)
}
static const struct mpr_ops mpr_i2c_ops = {
- .init = mpr_i2c_init,
.read = mpr_i2c_read,
.write = mpr_i2c_write,
};
diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c
index d04102f8a4a0..8c8c726f703f 100644
--- a/drivers/iio/pressure/mprls0025pa_spi.c
+++ b/drivers/iio/pressure/mprls0025pa_spi.c
@@ -8,6 +8,7 @@
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
*/
+#include <linux/array_size.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
@@ -18,43 +19,31 @@
#include "mprls0025pa.h"
-struct mpr_spi_buf {
- u8 tx[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
-};
-
-static int mpr_spi_init(struct device *dev)
-{
- struct spi_device *spi = to_spi_device(dev);
- struct mpr_spi_buf *buf;
-
- buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- spi_set_drvdata(spi, buf);
-
- return 0;
-}
-
static int mpr_spi_xfer(struct mpr_data *data, const u8 cmd, const u8 pkt_len)
{
struct spi_device *spi = to_spi_device(data->dev);
- struct mpr_spi_buf *buf = spi_get_drvdata(spi);
- struct spi_transfer xfer;
+ struct spi_transfer xfers[2] = { };
if (pkt_len > MPR_MEASUREMENT_RD_SIZE)
return -EOVERFLOW;
- buf->tx[0] = cmd;
- xfer.tx_buf = buf->tx;
- xfer.rx_buf = data->buffer;
- xfer.len = pkt_len;
+ data->tx_buf[0] = cmd;
+
+ /*
+ * Dummy transfer with no data, just cause a 2.5us+ delay between the CS assert
+ * and the first clock edge as per the datasheet tHDSS timing requirement.
+ */
+ xfers[0].delay.value = 2500;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].tx_buf = data->tx_buf;
+ xfers[1].rx_buf = data->rx_buf;
+ xfers[1].len = pkt_len;
- return spi_sync_transfer(spi, &xfer, 1);
+ return spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
}
static const struct mpr_ops mpr_spi_ops = {
- .init = mpr_spi_init,
.read = mpr_spi_xfer,
.write = mpr_spi_xfer,
};
diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c
index aff60a3c1a6f..6afdbfca3e5a 100644
--- a/drivers/iio/proximity/rfd77402.c
+++ b/drivers/iio/proximity/rfd77402.c
@@ -6,19 +6,28 @@
*
* 7-bit I2C slave address 0x4c
*
- * TODO: interrupt
* https://media.digikey.com/pdf/Data%20Sheets/RF%20Digital%20PDFs/RFD77402.pdf
*/
-#include <linux/module.h>
-#include <linux/i2c.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/types.h>
#include <linux/iio/iio.h>
#define RFD77402_DRV_NAME "rfd77402"
#define RFD77402_ICSR 0x00 /* Interrupt Control Status Register */
+#define RFD77402_ICSR_CLR_CFG BIT(0)
+#define RFD77402_ICSR_CLR_TYPE BIT(1)
#define RFD77402_ICSR_INT_MODE BIT(2)
#define RFD77402_ICSR_INT_POL BIT(3)
#define RFD77402_ICSR_RESULT BIT(4)
@@ -26,6 +35,12 @@
#define RFD77402_ICSR_H2M_MSG BIT(6)
#define RFD77402_ICSR_RESET BIT(7)
+#define RFD77402_IER 0x02
+#define RFD77402_IER_RESULT BIT(0)
+#define RFD77402_IER_M2H_MSG BIT(1)
+#define RFD77402_IER_H2M_MSG BIT(2)
+#define RFD77402_IER_RESET BIT(3)
+
#define RFD77402_CMD_R 0x04
#define RFD77402_CMD_SINGLE 0x01
#define RFD77402_CMD_STANDBY 0x10
@@ -76,10 +91,18 @@ static const struct {
{RFD77402_HFCFG_3, 0x45d4},
};
+/**
+ * struct rfd77402_data - device-specific data for the RFD77402 sensor
+ * @client: I2C client handle
+ * @lock: mutex to serialize sensor reads
+ * @completion: completion used for interrupt-driven measurements
+ * @irq_en: indicates whether interrupt mode is enabled
+ */
struct rfd77402_data {
struct i2c_client *client;
- /* Serialize reads from the sensor */
struct mutex lock;
+ struct completion completion;
+ bool irq_en;
};
static const struct iio_chan_spec rfd77402_channels[] = {
@@ -90,6 +113,41 @@ static const struct iio_chan_spec rfd77402_channels[] = {
},
};
+static irqreturn_t rfd77402_interrupt_handler(int irq, void *pdata)
+{
+ struct rfd77402_data *data = pdata;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, RFD77402_ICSR);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ /* Check if the interrupt is from our device */
+ if (!(ret & RFD77402_ICSR_RESULT))
+ return IRQ_NONE;
+
+ /* Signal completion of measurement */
+ complete(&data->completion);
+ return IRQ_HANDLED;
+}
+
+static int rfd77402_wait_for_irq(struct rfd77402_data *data)
+{
+ int ret;
+
+ /*
+ * According to RFD77402 Datasheet v1.8,
+ * Section 3.1.1 "Single Measure" (Figure: Single Measure Flow Chart),
+ * the suggested timeout for single measure is 100 ms.
+ */
+ ret = wait_for_completion_timeout(&data->completion,
+ msecs_to_jiffies(100));
+ if (ret == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
static int rfd77402_set_state(struct i2c_client *client, u8 state, u16 check)
{
int ret;
@@ -110,10 +168,36 @@ static int rfd77402_set_state(struct i2c_client *client, u8 state, u16 check)
return 0;
}
-static int rfd77402_measure(struct i2c_client *client)
+static int rfd77402_wait_for_result(struct rfd77402_data *data)
+{
+ struct i2c_client *client = data->client;
+ int val, ret;
+
+ if (data->irq_en) {
+ reinit_completion(&data->completion);
+ return rfd77402_wait_for_irq(data);
+ }
+
+ /*
+ * As per RFD77402 datasheet section '3.1.1 Single Measure', the
+ * suggested timeout value for single measure is 100ms.
+ */
+ ret = read_poll_timeout(i2c_smbus_read_byte_data, val,
+ (val < 0) || (val & RFD77402_ICSR_RESULT),
+ 10 * USEC_PER_MSEC,
+ 10 * 10 * USEC_PER_MSEC,
+ false,
+ client, RFD77402_ICSR);
+ if (val < 0)
+ return val;
+
+ return ret;
+}
+
+static int rfd77402_measure(struct rfd77402_data *data)
{
+ struct i2c_client *client = data->client;
int ret;
- int tries = 10;
ret = rfd77402_set_state(client, RFD77402_CMD_MCPU_ON,
RFD77402_STATUS_MCPU_ON);
@@ -126,19 +210,9 @@ static int rfd77402_measure(struct i2c_client *client)
if (ret < 0)
goto err;
- while (tries-- > 0) {
- ret = i2c_smbus_read_byte_data(client, RFD77402_ICSR);
- if (ret < 0)
- goto err;
- if (ret & RFD77402_ICSR_RESULT)
- break;
- msleep(20);
- }
-
- if (tries < 0) {
- ret = -ETIMEDOUT;
+ ret = rfd77402_wait_for_result(data);
+ if (ret < 0)
goto err;
- }
ret = i2c_smbus_read_word_data(client, RFD77402_RESULT_R);
if (ret < 0)
@@ -168,7 +242,7 @@ static int rfd77402_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->lock);
- ret = rfd77402_measure(data->client);
+ ret = rfd77402_measure(data);
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
@@ -188,8 +262,20 @@ static const struct iio_info rfd77402_info = {
.read_raw = rfd77402_read_raw,
};
-static int rfd77402_init(struct i2c_client *client)
+static int rfd77402_config_irq(struct i2c_client *client, u8 csr, u8 ier)
{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, RFD77402_ICSR, csr);
+ if (ret)
+ return ret;
+
+ return i2c_smbus_write_byte_data(client, RFD77402_IER, ier);
+}
+
+static int rfd77402_init(struct rfd77402_data *data)
+{
+ struct i2c_client *client = data->client;
int ret, i;
ret = rfd77402_set_state(client, RFD77402_CMD_STANDBY,
@@ -197,10 +283,26 @@ static int rfd77402_init(struct i2c_client *client)
if (ret < 0)
return ret;
- /* configure INT pad as push-pull, active low */
- ret = i2c_smbus_write_byte_data(client, RFD77402_ICSR,
- RFD77402_ICSR_INT_MODE);
- if (ret < 0)
+ if (data->irq_en) {
+ /*
+ * Enable interrupt mode:
+ * - Configure ICSR for auto-clear on read and
+ * push-pull output
+ * - Enable "result ready" interrupt in IER
+ */
+ ret = rfd77402_config_irq(client,
+ RFD77402_ICSR_CLR_CFG |
+ RFD77402_ICSR_INT_MODE,
+ RFD77402_IER_RESULT);
+ } else {
+ /*
+ * Disable all interrupts:
+ * - Clear ICSR configuration
+ * - Disable all interrupts in IER
+ */
+ ret = rfd77402_config_irq(client, 0, 0);
+ }
+ if (ret)
return ret;
/* I2C configuration */
@@ -275,7 +377,26 @@ static int rfd77402_probe(struct i2c_client *client)
data = iio_priv(indio_dev);
data->client = client;
- mutex_init(&data->lock);
+
+ ret = devm_mutex_init(&client->dev, &data->lock);
+ if (ret)
+ return ret;
+
+ init_completion(&data->completion);
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, rfd77402_interrupt_handler,
+ IRQF_ONESHOT,
+ "rfd77402", data);
+ if (ret)
+ return ret;
+
+ data->irq_en = true;
+ dev_dbg(&client->dev, "Using interrupt mode\n");
+ } else {
+ dev_dbg(&client->dev, "Using polling mode\n");
+ }
indio_dev->info = &rfd77402_info;
indio_dev->channels = rfd77402_channels;
@@ -283,7 +404,7 @@ static int rfd77402_probe(struct i2c_client *client)
indio_dev->name = RFD77402_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- ret = rfd77402_init(client);
+ ret = rfd77402_init(data);
if (ret < 0)
return ret;
@@ -301,7 +422,10 @@ static int rfd77402_suspend(struct device *dev)
static int rfd77402_resume(struct device *dev)
{
- return rfd77402_init(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct rfd77402_data *data = iio_priv(indio_dev);
+
+ return rfd77402_init(data);
}
static DEFINE_SIMPLE_DEV_PM_OPS(rfd77402_pm_ops, rfd77402_suspend,
@@ -313,10 +437,17 @@ static const struct i2c_device_id rfd77402_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rfd77402_id);
+static const struct of_device_id rfd77402_of_match[] = {
+ { .compatible = "rfdigital,rfd77402" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rfd77402_of_match);
+
static struct i2c_driver rfd77402_driver = {
.driver = {
.name = RFD77402_DRV_NAME,
.pm = pm_sleep_ptr(&rfd77402_pm_ops),
+ .of_match_table = rfd77402_of_match,
},
.probe = rfd77402_probe,
.id_table = rfd77402_id,
diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c
index 10bd3f221929..d8d8c8936d17 100644
--- a/drivers/iio/temperature/tmp006.c
+++ b/drivers/iio/temperature/tmp006.c
@@ -356,12 +356,10 @@ static int tmp006_probe(struct i2c_client *client)
indio_dev->trig = iio_trigger_get(data->drdy_trig);
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- iio_trigger_generic_data_rdy_poll,
- NULL,
- IRQF_ONESHOT,
- "tmp006_irq",
- data->drdy_trig);
+ ret = devm_request_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_NO_THREAD, "tmp006_irq",
+ data->drdy_trig);
if (ret < 0)
return ret;
}
diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig
index 6e65e929791c..4fc17dd0dcd7 100644
--- a/drivers/iio/test/Kconfig
+++ b/drivers/iio/test/Kconfig
@@ -8,7 +8,6 @@ config IIO_GTS_KUNIT_TEST
tristate "Test IIO gain-time-scale helpers" if !KUNIT_ALL_TESTS
depends on KUNIT
select IIO_GTS_HELPER
- select TEST_KUNIT_DEVICE_HELPERS
default KUNIT_ALL_TESTS
help
build unit tests for the IIO light sensor gain-time-scale helpers.
diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c
index f45968ef94ea..3bdaee925dee 100644
--- a/drivers/staging/iio/addac/adt7316-i2c.c
+++ b/drivers/staging/iio/addac/adt7316-i2c.c
@@ -136,7 +136,7 @@ static struct i2c_driver adt7316_driver = {
.driver = {
.name = "adt7316",
.of_match_table = adt7316_of_match,
- .pm = ADT7316_PM_OPS,
+ .pm = pm_sleep_ptr(&adt7316_pm_ops),
},
.probe = adt7316_i2c_probe,
.id_table = adt7316_i2c_id,
diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c
index af513e003da7..f91325d11394 100644
--- a/drivers/staging/iio/addac/adt7316-spi.c
+++ b/drivers/staging/iio/addac/adt7316-spi.c
@@ -142,7 +142,7 @@ static struct spi_driver adt7316_driver = {
.driver = {
.name = "adt7316",
.of_match_table = adt7316_of_spi_match,
- .pm = ADT7316_PM_OPS,
+ .pm = pm_sleep_ptr(&adt7316_pm_ops),
},
.probe = adt7316_spi_probe,
.id_table = adt7316_spi_id,
diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c
index 8a9a8262c2be..59fb3bd26bc1 100644
--- a/drivers/staging/iio/addac/adt7316.c
+++ b/drivers/staging/iio/addac/adt7316.c
@@ -2082,7 +2082,6 @@ static const struct attribute_group adt7516_event_attribute_group = {
.name = "events",
};
-#ifdef CONFIG_PM_SLEEP
static int adt7316_disable(struct device *dev)
{
struct iio_dev *dev_info = dev_get_drvdata(dev);
@@ -2098,9 +2097,8 @@ static int adt7316_enable(struct device *dev)
return _adt7316_store_enabled(chip, 1);
}
-EXPORT_SYMBOL_GPL(adt7316_pm_ops);
-SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
-#endif
+
+EXPORT_GPL_SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
static const struct iio_info adt7316_info = {
.attrs = &adt7316_attribute_group,
diff --git a/drivers/staging/iio/addac/adt7316.h b/drivers/staging/iio/addac/adt7316.h
index 8c2a92ae7157..f208f0d3583a 100644
--- a/drivers/staging/iio/addac/adt7316.h
+++ b/drivers/staging/iio/addac/adt7316.h
@@ -22,12 +22,8 @@ struct adt7316_bus {
int (*multi_write)(void *client, u8 first_reg, u8 count, u8 *data);
};
-#ifdef CONFIG_PM_SLEEP
extern const struct dev_pm_ops adt7316_pm_ops;
-#define ADT7316_PM_OPS (&adt7316_pm_ops)
-#else
-#define ADT7316_PM_OPS NULL
-#endif
+
int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
const char *name);
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index 49388da5a684..b87ea1781b27 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -23,12 +23,9 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include "ad9832.h"
-
#include "dds.h"
/* Registers */
-
#define AD9832_FREQ0LL 0x0
#define AD9832_FREQ0HL 0x1
#define AD9832_FREQ0LM 0x2
@@ -45,14 +42,12 @@
#define AD9832_PHASE2H 0xD
#define AD9832_PHASE3L 0xE
#define AD9832_PHASE3H 0xF
-
#define AD9832_PHASE_SYM 0x10
#define AD9832_FREQ_SYM 0x11
#define AD9832_PINCTRL_EN 0x12
#define AD9832_OUTPUT_EN 0x13
/* Command Control Bits */
-
#define AD9832_CMD_PHA8BITSW 0x1
#define AD9832_CMD_PHA16BITSW 0x0
#define AD9832_CMD_FRE8BITSW 0x3
@@ -92,7 +87,6 @@
* @phase_data: tuning word spi transmit buffer
* @freq_data: tuning word spi transmit buffer
*/
-
struct ad9832_state {
struct spi_device *spi;
struct clk *mclk;
@@ -299,16 +293,10 @@ static const struct iio_info ad9832_info = {
static int ad9832_probe(struct spi_device *spi)
{
- struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev);
struct iio_dev *indio_dev;
struct ad9832_state *st;
int ret;
- if (!pdata) {
- dev_dbg(&spi->dev, "no platform data?\n");
- return -ENODEV;
- }
-
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@@ -335,7 +323,6 @@ static int ad9832_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
/* Setup default messages */
-
st->xfer.tx_buf = &st->data;
st->xfer.len = 2;
@@ -379,30 +366,6 @@ static int ad9832_probe(struct spi_device *spi)
return ret;
}
- ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0);
- if (ret)
- return ret;
-
- ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2);
- if (ret)
- return ret;
-
- ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3);
- if (ret)
- return ret;
-
return devm_iio_device_register(&spi->dev, indio_dev);
}
diff --git a/drivers/staging/iio/frequency/ad9832.h b/drivers/staging/iio/frequency/ad9832.h
deleted file mode 100644
index d0d840edb8d2..000000000000
--- a/drivers/staging/iio/frequency/ad9832.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * AD9832 SPI DDS driver
- *
- * Copyright 2011 Analog Devices Inc.
- */
-#ifndef IIO_DDS_AD9832_H_
-#define IIO_DDS_AD9832_H_
-
-/*
- * TODO: struct ad9832_platform_data needs to go into include/linux/iio
- */
-
-/**
- * struct ad9832_platform_data - platform specific information
- * @freq0: power up freq0 tuning word in Hz
- * @freq1: power up freq1 tuning word in Hz
- * @phase0: power up phase0 value [0..4095] correlates with 0..2PI
- * @phase1: power up phase1 value [0..4095] correlates with 0..2PI
- * @phase2: power up phase2 value [0..4095] correlates with 0..2PI
- * @phase3: power up phase3 value [0..4095] correlates with 0..2PI
- */
-
-struct ad9832_platform_data {
- unsigned long freq0;
- unsigned long freq1;
- unsigned short phase0;
- unsigned short phase1;
- unsigned short phase2;
- unsigned short phase3;
-};
-
-#endif /* IIO_DDS_AD9832_H_ */
diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h
index 9fcb6410a584..971d53349b6f 100644
--- a/include/linux/i3c/device.h
+++ b/include/linux/i3c/device.h
@@ -25,7 +25,7 @@
* @I3C_ERROR_M2: M2 error
*
* These are the standard error codes as defined by the I3C specification.
- * When -EIO is returned by the i3c_device_do_priv_xfers() or
+ * When -EIO is returned by the i3c_device_do_i3c_xfers() or
* i3c_device_send_hdr_cmds() one can check the error code in
* &struct_i3c_xfer.err or &struct i3c_hdr_cmd.err to get a better idea of
* what went wrong.
@@ -79,9 +79,6 @@ struct i3c_xfer {
enum i3c_error_code err;
};
-/* keep back compatible */
-#define i3c_priv_xfer i3c_xfer
-
/**
* enum i3c_dcr - I3C DCR values
* @I3C_DCR_GENERIC_DEVICE: generic I3C device
@@ -308,15 +305,23 @@ static __always_inline void i3c_i2c_driver_unregister(struct i3c_driver *i3cdrv,
i3c_i2c_driver_unregister, \
__i2cdrv)
+#if IS_ENABLED(CONFIG_I3C)
int i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
int nxfers, enum i3c_xfer_mode mode);
+u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
+#else
+static inline int
+i3c_device_do_xfers(struct i3c_device *dev, struct i3c_xfer *xfers,
+ int nxfers, enum i3c_xfer_mode mode)
+{
+ return -EOPNOTSUPP;
+}
-static inline int i3c_device_do_priv_xfers(struct i3c_device *dev,
- struct i3c_xfer *xfers,
- int nxfers)
+static inline u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev)
{
- return i3c_device_do_xfers(dev, xfers, nxfers, I3C_SDR);
+ return 0;
}
+#endif
int i3c_device_do_setdasa(struct i3c_device *dev);
@@ -358,6 +363,5 @@ int i3c_device_request_ibi(struct i3c_device *dev,
void i3c_device_free_ibi(struct i3c_device *dev);
int i3c_device_enable_ibi(struct i3c_device *dev);
int i3c_device_disable_ibi(struct i3c_device *dev);
-u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev);
#endif /* I3C_DEV_H */
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 58d01ed4cce7..d231c4fbc58b 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -462,6 +462,8 @@ struct i3c_bus {
* @enable_hotjoin: enable hot join event detect.
* @disable_hotjoin: disable hot join event detect.
* @set_speed: adjust I3C open drain mode timing.
+ * @set_dev_nack_retry: configure device NACK retry count for the master
+ * controller.
*/
struct i3c_master_controller_ops {
int (*bus_init)(struct i3c_master_controller *master);
@@ -491,6 +493,8 @@ struct i3c_master_controller_ops {
int (*enable_hotjoin)(struct i3c_master_controller *master);
int (*disable_hotjoin)(struct i3c_master_controller *master);
int (*set_speed)(struct i3c_master_controller *master, enum i3c_open_drain_speed speed);
+ int (*set_dev_nack_retry)(struct i3c_master_controller *master,
+ unsigned long dev_nack_retry_cnt);
};
/**
@@ -514,6 +518,7 @@ struct i3c_master_controller_ops {
* in a thread context. Typical examples are Hot Join processing which
* requires taking the bus lock in maintenance, which in turn, can only
* be done from a sleep-able context
+ * @dev_nack_retry_count: retry count when slave device nack
*
* A &struct i3c_master_controller has to be registered to the I3C subsystem
* through i3c_master_register(). None of &struct i3c_master_controller fields
@@ -534,6 +539,7 @@ struct i3c_master_controller {
} boardinfo;
struct i3c_bus bus;
struct workqueue_struct *wq;
+ unsigned int dev_nack_retry_count;
};
/**
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 4f33e6a39797..ef8687a88b73 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -119,7 +119,12 @@ struct iio_dma_buffer_queue {
struct device *dev;
const struct iio_dma_buffer_ops *ops;
+ /*
+ * A mutex to protect accessing, configuring (eg: enqueuing DMA blocks)
+ * and do file IO on struct iio_dma_buffer_queue objects.
+ */
struct mutex lock;
+ /* A spin lock to protect adding/removing blocks to the queue list */
spinlock_t list_lock;
struct list_head incoming;
@@ -136,20 +141,19 @@ struct iio_dma_buffer_queue {
*/
struct iio_dma_buffer_ops {
int (*submit)(struct iio_dma_buffer_queue *queue,
- struct iio_dma_buffer_block *block);
+ struct iio_dma_buffer_block *block);
void (*abort)(struct iio_dma_buffer_queue *queue);
};
void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block);
void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
- struct list_head *list);
+ struct list_head *list);
-int iio_dma_buffer_enable(struct iio_buffer *buffer,
- struct iio_dev *indio_dev);
+int iio_dma_buffer_enable(struct iio_buffer *buffer, struct iio_dev *indio_dev);
int iio_dma_buffer_disable(struct iio_buffer *buffer,
- struct iio_dev *indio_dev);
+ struct iio_dev *indio_dev);
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
- char __user *user_buffer);
+ char __user *user_buffer);
int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
const char __user *user_buffer);
size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
@@ -157,8 +161,8 @@ int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);
int iio_dma_buffer_request_update(struct iio_buffer *buffer);
-int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
- struct device *dma_dev, const struct iio_dma_buffer_ops *ops);
+void iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, struct device *dev,
+ const struct iio_dma_buffer_ops *ops);
void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue);
void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue);
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index c0b0e0992a85..9442c165561a 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -113,10 +113,10 @@ struct iio_buffer {
/** @flags: File ops flags including busy flag. */
unsigned long flags;
- /** @bytes_per_datum: Size of individual datum including timestamp. */
+ /** @bytes_per_datum: Size of individual datum including timestamp. */
size_t bytes_per_datum;
- /* @direction: Direction of the data stream (in/out). */
+ /** @direction: Direction of the data stream (in/out). */
enum iio_buffer_direction direction;
/**
@@ -178,7 +178,9 @@ struct iio_buffer {
* @insert_buffer: buffer to insert
* @remove_buffer: buffer_to_remove
*
- * Note this will tear down the all buffering and build it up again
+ * Note this will tear down all the buffering and build it up again
+ *
+ * Returns: 0 on success or -errno on error
*/
int iio_update_buffers(struct iio_dev *indio_dev,
struct iio_buffer *insert_buffer,
diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h
index ff22a0ac15f5..236437a226b2 100644
--- a/include/linux/iio/frequency/ad9523.h
+++ b/include/linux/iio/frequency/ad9523.h
@@ -45,7 +45,7 @@ enum ref_sel_mode {
* @output_dis: Disables, powers down the entire channel.
* @driver_mode: Output driver mode (logic level family).
* @divider_phase: Divider initial phase after a SYNC. Range 0..63
- LSB = 1/2 of a period of the divider input clock.
+ * LSB = 1/2 of a period of the divider input clock.
* @channel_divider: 10-bit channel divider.
* @extended_name: Optional descriptive channel name.
*/
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 872ebdf0dd77..a9ecff191bd9 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -10,6 +10,7 @@
#include <linux/align.h>
#include <linux/device.h>
#include <linux/cdev.h>
+#include <linux/cleanup.h>
#include <linux/compiler_types.h>
#include <linux/minmax.h>
#include <linux/slab.h>
@@ -661,34 +662,148 @@ void iio_device_unregister(struct iio_dev *indio_dev);
int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev,
struct module *this_mod);
int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp);
-bool __iio_device_claim_direct(struct iio_dev *indio_dev);
-void __iio_device_release_direct(struct iio_dev *indio_dev);
+
+void __iio_dev_mode_lock(struct iio_dev *indio_dev) __acquires(indio_dev);
+void __iio_dev_mode_unlock(struct iio_dev *indio_dev) __releases(indio_dev);
/*
* Helper functions that allow claim and release of direct mode
* in a fashion that doesn't generate many false positives from sparse.
* Note this must remain static inline in the header so that sparse
- * can see the __acquire() marking. Revisit when sparse supports
- * __cond_acquires()
+ * can see the __acquires() and __releases() annotations.
+ */
+
+/**
+ * iio_device_claim_direct() - Keep device in direct mode
+ * @indio_dev: the iio_dev associated with the device
+ *
+ * If the device is in direct mode it is guaranteed to stay
+ * that way until iio_device_release_direct() is called.
+ *
+ * Use with iio_device_release_direct().
+ *
+ * Returns: true on success, false on failure.
*/
static inline bool iio_device_claim_direct(struct iio_dev *indio_dev)
{
- if (!__iio_device_claim_direct(indio_dev))
- return false;
+ __iio_dev_mode_lock(indio_dev);
- __acquire(iio_dev);
+ if (iio_buffer_enabled(indio_dev)) {
+ __iio_dev_mode_unlock(indio_dev);
+ return false;
+ }
return true;
}
-static inline void iio_device_release_direct(struct iio_dev *indio_dev)
+/**
+ * iio_device_release_direct() - Releases claim on direct mode
+ * @indio_dev: the iio_dev associated with the device
+ *
+ * Release the claim. Device is no longer guaranteed to stay
+ * in direct mode.
+ *
+ * Use with iio_device_claim_direct().
+ */
+#define iio_device_release_direct(indio_dev) __iio_dev_mode_unlock(indio_dev)
+
+/**
+ * iio_device_try_claim_buffer_mode() - Keep device in buffer mode
+ * @indio_dev: the iio_dev associated with the device
+ *
+ * If the device is in buffer mode it is guaranteed to stay
+ * that way until iio_device_release_buffer_mode() is called.
+ *
+ * Use with iio_device_release_buffer_mode().
+ *
+ * Returns: true on success, false on failure.
+ */
+static inline bool iio_device_try_claim_buffer_mode(struct iio_dev *indio_dev)
{
- __iio_device_release_direct(indio_dev);
- __release(indio_dev);
+ __iio_dev_mode_lock(indio_dev);
+
+ if (!iio_buffer_enabled(indio_dev)) {
+ __iio_dev_mode_unlock(indio_dev);
+ return false;
+ }
+
+ return true;
}
-int iio_device_claim_buffer_mode(struct iio_dev *indio_dev);
-void iio_device_release_buffer_mode(struct iio_dev *indio_dev);
+/**
+ * iio_device_release_buffer_mode() - releases claim on buffer mode
+ * @indio_dev: the iio_dev associated with the device
+ *
+ * Release the claim. Device is no longer guaranteed to stay
+ * in buffer mode.
+ *
+ * Use with iio_device_try_claim_buffer_mode().
+ */
+#define iio_device_release_buffer_mode(indio_dev) __iio_dev_mode_unlock(indio_dev)
+
+/*
+ * These classes are not meant to be used directly by drivers (hence the
+ * __priv__ prefix). Instead, documented wrapper macros are provided below to
+ * enforce the use of ACQUIRE() or guard() semantics and avoid the problematic
+ * scoped guard variants.
+ */
+DEFINE_GUARD(__priv__iio_dev_mode_lock, struct iio_dev *,
+ __iio_dev_mode_lock(_T), __iio_dev_mode_unlock(_T));
+DEFINE_GUARD_COND(__priv__iio_dev_mode_lock, _try_direct,
+ iio_device_claim_direct(_T));
+
+/**
+ * IIO_DEV_ACQUIRE_DIRECT_MODE() - Tries to acquire the direct mode lock with
+ * automatic release
+ * @dev: IIO device instance
+ * @claim: Variable identifier to store acquire result
+ *
+ * Tries to acquire the direct mode lock with cleanup ACQUIRE() semantics and
+ * automatically releases it at the end of the scope. It most be always paired
+ * with IIO_DEV_ACQUIRE_ERR(), for example (notice the scope braces)::
+ *
+ * switch() {
+ * case IIO_CHAN_INFO_RAW: {
+ * IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ * if (IIO_DEV_ACQUIRE_FAILED(claim))
+ * return -EBUSY;
+ *
+ * ...
+ * }
+ * case IIO_CHAN_INFO_SCALE:
+ * ...
+ * ...
+ * }
+ *
+ * Context: Can sleep
+ */
+#define IIO_DEV_ACQUIRE_DIRECT_MODE(dev, claim) \
+ ACQUIRE(__priv__iio_dev_mode_lock_try_direct, claim)(dev)
+
+/**
+ * IIO_DEV_ACQUIRE_FAILED() - ACQUIRE_ERR() wrapper
+ * @claim: The claim variable passed to IIO_DEV_ACQUIRE_*_MODE()
+ *
+ * Return: true if failed to acquire the mode, otherwise false.
+ */
+#define IIO_DEV_ACQUIRE_FAILED(claim) \
+ ACQUIRE_ERR(__priv__iio_dev_mode_lock_try_direct, &(claim))
+
+/**
+ * IIO_DEV_GUARD_CURRENT_MODE() - Acquires the mode lock with automatic release
+ * @dev: IIO device instance
+ *
+ * Acquires the mode lock with cleanup guard() semantics. It is usually paired
+ * with iio_buffer_enabled().
+ *
+ * This should *not* be used to protect internal driver state and it's use in
+ * general is *strongly* discouraged. Use any of the IIO_DEV_ACQUIRE_*_MODE()
+ * variants.
+ *
+ * Context: Can sleep
+ */
+#define IIO_DEV_GUARD_CURRENT_MODE(dev) \
+ guard(__priv__iio_dev_mode_lock)(dev)
extern const struct bus_type iio_bus_type;
diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h
index 69294f79cc88..d363d60bb8a3 100644
--- a/include/linux/platform_data/cros_ec_commands.h
+++ b/include/linux/platform_data/cros_ec_commands.h
@@ -2598,14 +2598,20 @@ struct ec_params_motion_sense {
/*
* Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA
- * and MOTIONSENSE_CMD_PERFORM_CALIB.
*/
struct __ec_todo_unpacked {
uint8_t sensor_num;
- } info, info_3, data, fifo_flush, perform_calib,
- list_activities;
+ } info, info_3, data, fifo_flush, list_activities;
/*
+ * Used for MOTIONSENSE_CMD_PERFORM_CALIB:
+ * Allow entering/exiting the calibration mode.
+ */
+ struct __ec_todo_unpacked {
+ uint8_t sensor_num;
+ uint8_t enable;
+ } perform_calib;
+ /*
* Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR
* and MOTIONSENSE_CMD_SENSOR_RANGE.
*/
diff --git a/include/linux/units.h b/include/linux/units.h
index 00e15de33eca..3471c5a38dcf 100644
--- a/include/linux/units.h
+++ b/include/linux/units.h
@@ -21,6 +21,25 @@
#define PICO 1000000000000ULL
#define FEMTO 1000000000000000ULL
+/*
+ * Percentage and related scaling units
+ *
+ * These macros define scaling factors used to convert between ratio and
+ * percentage-based representations with different decimal resolutions.
+ * They are used for precise fractional calculations in engineering, finance,
+ * and measurement applications.
+ *
+ * Examples:
+ * 1% = 0.01 = 1 / PERCENT
+ * 0.1% = 0.001 = 1 / PERMILLE
+ * 0.01% = 0.0001 = 1 / PERMYRIAD (1 basis point)
+ * 0.001% = 0.00001 = 1 / PERCENTMILLE
+ */
+#define PERCENT 100
+#define PERMILLE 1000
+#define PERMYRIAD 10000
+#define PERCENTMILLE 100000
+
#define NANOHZ_PER_HZ 1000000000UL
#define MICROHZ_PER_HZ 1000000UL
#define MILLIHZ_PER_HZ 1000UL