summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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