summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 14:29:07 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 14:29:07 +0100
commit15b72c6d38fbce11bdcb674f76a7b9d62ba9a7d1 (patch)
treee782ed3a8ec5e20535a7d9317c2462e681a906ae
parent0e98cf90e278cf6959faf07af3b41aff24fd7845 (diff)
parent309bd01041636b587bcb424b243640b448bd62f7 (diff)
downloadlinux-next-15b72c6d38fbce11bdcb674f76a7b9d62ba9a7d1.tar.gz
linux-next-15b72c6d38fbce11bdcb674f76a7b9d62ba9a7d1.zip
Merge branch 'togreg' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
# Conflicts: # drivers/iio/adc/ade9000.c
-rw-r--r--Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml13
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml3
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml30
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml65
-rw-r--r--Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml63
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml10
-rw-r--r--Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml149
-rw-r--r--Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml3
-rw-r--r--Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml3
-rw-r--r--Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml24
-rw-r--r--Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml32
-rw-r--r--Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml71
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml4
-rw-r--r--Documentation/iio/ade9000.rst28
-rw-r--r--Documentation/iio/adxl380.rst8
-rw-r--r--MAINTAINERS30
-rw-r--r--drivers/iio/accel/adxl367.c59
-rw-r--r--drivers/iio/accel/adxl380.c7
-rw-r--r--drivers/iio/accel/bma400_core.c2
-rw-r--r--drivers/iio/accel/kionix-kx022a.c20
-rw-r--r--drivers/iio/accel/mma8452.c145
-rw-r--r--drivers/iio/adc/Kconfig57
-rw-r--r--drivers/iio/adc/Makefile3
-rw-r--r--drivers/iio/adc/ad4080.c13
-rw-r--r--drivers/iio/adc/ade9000.c180
-rw-r--r--drivers/iio/adc/axiado_saradc.c277
-rw-r--r--drivers/iio/adc/bcm_iproc_adc.c46
-rw-r--r--drivers/iio/adc/max40080.c574
-rw-r--r--drivers/iio/adc/meson_saradc.c2
-rw-r--r--drivers/iio/adc/pac1934.c9
-rw-r--r--drivers/iio/adc/rockchip_saradc.c16
-rw-r--r--drivers/iio/adc/sophgo-cv1800b-adc.c2
-rw-r--r--drivers/iio/adc/ti-ads1100.c188
-rw-r--r--drivers/iio/adc/ti-ads112c04.c524
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c3
-rw-r--r--drivers/iio/chemical/sps30.c11
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c2
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c5
-rw-r--r--drivers/iio/frequency/adf4350.c2
-rw-r--r--drivers/iio/gyro/adxrs290.c4
-rw-r--r--drivers/iio/gyro/bmg160_core.c4
-rw-r--r--drivers/iio/gyro/itg3200_buffer.c3
-rw-r--r--drivers/iio/humidity/Kconfig8
-rw-r--r--drivers/iio/humidity/am2315.c34
-rw-r--r--drivers/iio/humidity/ens210.c4
-rw-r--r--drivers/iio/humidity/hts221_core.c168
-rw-r--r--drivers/iio/humidity/hts221_i2c.c11
-rw-r--r--drivers/iio/humidity/hts221_spi.c11
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600.h4
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c16
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c112
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_core.c17
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c16
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c16
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c16
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c6
-rw-r--r--drivers/iio/industrialio-core.c6
-rw-r--r--drivers/iio/industrialio-gts-helper.c2
-rw-r--r--drivers/iio/light/apds9999.c12
-rw-r--r--drivers/iio/light/cros_ec_light_prox.c2
-rw-r--r--drivers/iio/light/iqs621-als.c24
-rw-r--r--drivers/iio/light/isl29018.c7
-rw-r--r--drivers/iio/light/lm3533-als.c11
-rw-r--r--drivers/iio/light/ltr390.c6
-rw-r--r--drivers/iio/light/ltr501.c109
-rw-r--r--drivers/iio/light/tsl2583.c15
-rw-r--r--drivers/iio/light/tsl2772.c16
-rw-r--r--drivers/iio/light/vcnl4000.c3
-rw-r--r--drivers/iio/light/veml3328.c53
-rw-r--r--drivers/iio/position/iqs624-pos.c24
-rw-r--r--drivers/iio/pressure/cros_ec_baro.c2
-rw-r--r--drivers/iio/pressure/rohm-bm1390.c4
-rw-r--r--drivers/iio/proximity/aw96103.c2
-rw-r--r--drivers/iio/temperature/tmp117.c9
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a2
-rw-r--r--drivers/staging/iio/frequency/ad9832.c6
-rw-r--r--drivers/staging/iio/frequency/ad9834.c6
-rw-r--r--include/linux/iio/iio-gts-helper.h1
-rw-r--r--include/linux/iio/iio.h2
-rw-r--r--include/linux/notifier.h7
-rw-r--r--kernel/notifier.c105
81 files changed, 2928 insertions, 641 deletions
diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml
index f10d98d34cb8..115170eac058 100644
--- a/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl367.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADXL367 3-Axis Digital Accelerometer
maintainers:
- - Cosmin Tanislav <cosmin.tanislav@analog.com>
+ - Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The ADXL367 is an ultralow power, 3-axis MEMS accelerometer.
@@ -33,7 +33,14 @@ properties:
maxItems: 1
interrupts:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
+
+ interrupt-names:
+ minItems: 1
+ items:
+ - enum: [INT1, INT2]
+ - const: INT2
vdd-supply: true
vddio-supply: true
@@ -61,6 +68,7 @@ examples:
reg = <0x53>;
interrupt-parent = <&gpio>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "INT1";
};
};
- |
@@ -76,5 +84,6 @@ examples:
spi-max-frequency = <1000000>;
interrupt-parent = <&gpio>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "INT1";
};
};
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
index cc38617bb829..a4ab6adef1c6 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
@@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4130 family ADCs
maintainers:
- - Cosmin Tanislav <cosmin.tanislav@analog.com>
+ - Marcelo Schmitt <marcelo.schmitt@analog.com>
+ - Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
Bindings for the Analog Devices AD4130 family ADCs.
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
index f22eba0250ee..1e940e8c7297 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
@@ -5,21 +5,23 @@
$id: http://devicetree.org/schemas/iio/adc/adi,ade9000.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Analog Devices ADE9000 High Performance, Polyphase Energy Metering
+title: Analog Devices ADE9000/ADE9078 High Performance, Polyphase Energy Metering
maintainers:
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
- The ADE9000 is a highly accurate, fully integrated, multiphase energy and power
- quality monitoring device. Superior analog performance and a digital signal
- processing (DSP) core enable accurate energy monitoring over a wide dynamic
- range. An integrated high end reference ensures low drift over temperature
- with a combined drift of less than ±25 ppm/°C maximum for the entire channel
- including a programmable gain amplifier (PGA) and an analog-to-digital
- converter (ADC).
+ The ADE9000 and ADE9078 are highly accurate, fully integrated, multiphase
+ energy and power quality monitoring devices. Superior analog performance and a
+ digital signal processing (DSP) core enable accurate energy monitoring over a
+ wide dynamic range. An integrated high end reference ensures low drift over
+ temperature with a combined drift of less than ±25 ppm/°C maximum for the
+ entire channel including a programmable gain amplifier (PGA) and an
+ analog-to-digital converter (ADC). The ADE9078 does not provide the on-chip
+ dip/swell detection available on the ADE9000.
https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9078.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
@@ -27,6 +29,7 @@ properties:
compatible:
enum:
- adi,ade9000
+ - adi,ade9078
reg:
maxItems: 1
@@ -67,6 +70,17 @@ required:
- reg
- vdd-supply
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,ade9078
+ then:
+ properties:
+ spi-max-frequency:
+ maximum: 10000000
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml
new file mode 100644
index 000000000000..b0ec9159a293
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,max40080.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,max40080.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX40080 bidirectional current-sense amplifier
+
+maintainers:
+ - Ciprian Hegbeli <ciprian.hegbeli@analog.com>
+ - Stefan Popa <stefan.popa@analog.com>
+
+description: |
+ The MAX40080 is a high-precision, bidirectional current-sense amplifier with
+ an integrated 12-bit ADC and an I2C/SMBus interface. It measures the voltage
+ across an external shunt resistor and the input bus voltage, and stores the
+ results in an internal FIFO.
+
+ Datasheet:
+ https://www.analog.com/max40080
+
+properties:
+ compatible:
+ const: adi,max40080
+
+ reg:
+ maxItems: 1
+
+ "#io-channel-cells":
+ const: 1
+
+ vdd-supply:
+ description: Power supply for the device.
+
+ interrupts:
+ maxItems: 1
+ description: ALERT_ output for overcurrent/undervoltage threshold events.
+
+ shunt-resistor-micro-ohms:
+ description:
+ Value of the current-sense shunt resistor connected between the RS+ and
+ RS- inputs. Used to scale the reported current.
+
+required:
+ - compatible
+ - reg
+ - shunt-resistor-micro-ohms
+ - vdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@20 {
+ compatible = "adi,max40080";
+ reg = <0x20>;
+ #io-channel-cells = <1>;
+ vdd-supply = <&vdd>;
+ shunt-resistor-micro-ohms = <100000>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
new file mode 100644
index 000000000000..b910852aa56f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/axiado,ax3000-saradc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Axiado AX3000/AX3005 Successive Approximation Register ADC
+
+description:
+ The Axiado AX3000/AX3005 SAR ADC is a 10-bit ADC with sixteen input
+ channels on AX3000 and eight input channels on AX3005.
+
+maintainers:
+ - Petar Stepanovic <pstepanovic@axiado.com>
+ - Akhila Kavi <akavi@axiado.com>
+ - Prasad Bolisetty <pbolisetty@axiado.com>
+
+properties:
+ compatible:
+ enum:
+ - axiado,ax3000-saradc
+ - axiado,ax3005-saradc
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: saradc
+
+ '#io-channel-cells':
+ const: 1
+
+ vref-supply:
+ description: Reference voltage regulator supplying the ADC
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - '#io-channel-cells'
+ - vref-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ adc@806a0000 {
+ compatible = "axiado,ax3000-saradc";
+ reg = <0x0 0x806a0000 0x0 0x400>;
+ clocks = <&pclk>;
+ clock-names = "saradc";
+ vref-supply = <&vref_reg>;
+ #io-channel-cells = <1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml
index 970ccab15e1e..28c5e2dd0ad6 100644
--- a/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1100.yaml
@@ -4,19 +4,23 @@
$id: http://devicetree.org/schemas/iio/adc/ti,ads1100.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: TI ADS1100/ADS1000 single channel I2C analog to digital converter
+title: TI ADS1100 and similar single channel I2C Analog to Digital Converters
maintainers:
- Mike Looijmans <mike.looijmans@topic.nl>
description: |
- Datasheet at: https://www.ti.com/lit/gpn/ads1100
+ Datasheets:
+ - https://www.ti.com/lit/gpn/ads1000
+ - https://www.ti.com/lit/gpn/ads1100
+ - https://www.ti.com/lit/gpn/ads1110
properties:
compatible:
enum:
- - ti,ads1100
- ti,ads1000
+ - ti,ads1100
+ - ti,ads1110
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml
new file mode 100644
index 000000000000..95a123cd7b67
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml
@@ -0,0 +1,149 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/ti,ads112c04.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments ADS112C04 ADC
+
+maintainers:
+ - Kyle Hsieh <kylehsieh1995@gmail.com>
+
+description:
+ The ADS112C04 (16-bit) are precision analog-to-digital converters (ADCs)
+ with an I2C interface. They feature a flexible input multiplexer, a
+ low-noise programmable gain amplifier (PGA), two programmable excitation
+ current sources, a voltage reference, and a precision temperature sensor.
+
+properties:
+ compatible:
+ enum:
+ - ti,ads112c04
+
+ reg:
+ maxItems: 1
+ description: I2C address of the device.
+
+ interrupts:
+ maxItems: 1
+ description:
+ Data ready (DRDY) interrupt output. DRDY pulses low when a conversion
+ result is ready and returns high once the result is latched, so an
+ edge trigger should be used.
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO connected to the RESET pin. Active low.
+
+ avdd-supply: true
+ dvdd-supply: true
+
+ refp-supply: true
+ refn-supply: true
+
+ ti,refp-refn-resistor-ohms:
+ description: Resistance of the external resistor between REFP and REFN.
+
+patternProperties:
+ "^channel@[0-9a-b]$":
+ $ref: adc.yaml
+ unevaluatedProperties: false
+ properties:
+ reg:
+ items:
+ - maximum: 11
+
+ single-channel:
+ maximum: 3
+
+ diff-channels:
+ items:
+ maximum: 3
+
+ excitation-channels:
+ maxItems: 2
+ items:
+ maximum: 5
+ description:
+ Additionally to AIN0-AIN3, IDAC1/IDAC2 can also be routed to
+ REFP0 (4) and REFN0 (5).
+
+ excitation-current-nanoamp:
+ enum: [10000, 50000, 100000, 250000, 500000, 1000000, 1500000]
+
+ burn-out-current-nanoamp:
+ const: 10000
+
+ reference-sources:
+ items:
+ - enum: [internal, external, avdd]
+ default: [ internal ]
+
+ dependencies:
+ excitation-channels: [ excitation-current-nanoamp ]
+ excitation-current-nanoamp: [ excitation-channels ]
+
+ oneOf:
+ - required: [ single-channel ]
+ - required: [ diff-channels ]
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - dvdd-supply
+
+dependencies:
+ refn-supply: [ refp-supply ]
+
+oneOf:
+ - required:
+ - refp-supply
+ - required:
+ - ti,refp-refn-resistor-ohms
+ - properties:
+ refp-supply: false
+ refn-supply: false
+ ti,refp-refn-resistor-ohms: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@40 {
+ compatible = "ti,ads112c04";
+ reg = <0x40>;
+ interrupt-parent = <&gpio>;
+ interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
+
+ reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>;
+ avdd-supply = <&vdd_3v3_reg>;
+ dvdd-supply = <&vdd_3v3_reg>;
+ refp-supply = <&vref_reg>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ diff-channels = <0>, <1>;
+ };
+
+ channel@1 {
+ reg = <1>;
+ single-channel = <2>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml
index 2a04906531fb..6aa86cb56032 100644
--- a/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml
+++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74115.yaml
@@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD74115H device
maintainers:
- - Cosmin Tanislav <cosmin.tanislav@analog.com>
+ - Marcelo Schmitt <marcelo.schmitt@analog.com>
+ - Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The AD74115H is a single-channel software configurable input/output
diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml
index 590ea7936ad7..5318a00e7669 100644
--- a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml
+++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml
@@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD74412R/AD74413R device
maintainers:
- - Cosmin Tanislav <cosmin.tanislav@analog.com>
+ - Marcelo Schmitt <marcelo.schmitt@analog.com>
+ - Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The AD74412R and AD74413R are quad-channel software configurable input/output
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 81b6e85decd5..9e22b603d47f 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -26,16 +26,20 @@ description: |
properties:
compatible:
- enum:
- - invensense,icm42600
- - invensense,icm42602
- - invensense,icm42605
- - invensense,icm42607
- - invensense,icm42607p
- - invensense,icm42622
- - invensense,icm42631
- - invensense,icm42686
- - invensense,icm42688
+ oneOf:
+ - enum:
+ - invensense,icm42600
+ - invensense,icm42602
+ - invensense,icm42605
+ - invensense,icm42607
+ - invensense,icm42607p
+ - invensense,icm42622
+ - invensense,icm42631
+ - invensense,icm42686
+ - invensense,icm42688
+ - items:
+ - const: invensense,icm42630
+ - const: invensense,icm42631
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml
index c8074f180a79..20abd7da080d 100644
--- a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml
+++ b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml
@@ -9,15 +9,14 @@ title: LiteON LTR501 I2C Proximity and Light sensor
maintainers:
- Nikita Travkin <nikita@trvn.ru>
-allOf:
- - $ref: ../common.yaml#
-
properties:
compatible:
enum:
+ - liteon,ltr301
+ - liteon,ltr303
+ - liteon,ltr329
- liteon,ltr501
- liteon,ltr559
- - liteon,ltr301
reg:
maxItems: 1
@@ -36,6 +35,31 @@ required:
- compatible
- reg
+allOf:
+ - $ref: ../common.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - liteon,ltr329
+ then:
+ properties:
+ interrupts: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - liteon,ltr301
+ - liteon,ltr303
+ - liteon,ltr329
+ then:
+ properties:
+ proximity-near-level: false
+
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
diff --git a/Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml b/Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml
new file mode 100644
index 000000000000..dd739319b9eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/proximity/pulsedlight,lidar-lite-v2.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/proximity/pulsedlight,lidar-lite-v2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Pulsedlight/Garmin LIDAR-Lite range-finding sensors
+
+maintainers:
+ - Matt Ranostay <matt@ranostay.sg>
+ - Rodrigo Gobbi <rodrigo.gobbi.7@gmail.com>
+
+description: |
+ This binding covers the I2C interface of the Pulsedlight/Garmin
+ LIDAR-Lite v2 and v3 laser rangefinders.
+
+ Besides I2C, the mode control pin can also trigger acquisitions and
+ report the measured distance through a PWM signal, without using I2C
+ at all. That usage is bidirectional and non-cyclic, and is not
+ described by this binding. The same pin can also be used, while I2C
+ remains the active interface, as a plain status output signalling
+ when an acquisition has completed.
+
+ Datasheets:
+ https://static.garmin.com/pumac/LIDAR_Lite_v3_Operation_Manual_and_Technical_Specifications.pdf
+ https://github.com/PulsedLight3D/LIDAR-Lite-Documentation/blob/master/Docs/LIDAR-Lite-v2-Docs.pdf
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - grmn,lidar-lite-v3
+ - const: pulsedlight,lidar-lite-v2
+ - const: pulsedlight,lidar-lite-v2
+
+ reg:
+ maxItems: 1
+
+ powerdown-gpios:
+ description: GPIO that can be driven low to shut off power to the device.
+ maxItems: 1
+
+ vdd-supply: true
+
+ interrupts:
+ description:
+ Mode control pin used as a status output, driven while the device is
+ busy performing an acquisition. Can be used to signal completion
+ instead of polling over I2C.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ lidar@62 {
+ compatible = "grmn,lidar-lite-v3", "pulsedlight,lidar-lite-v2";
+ reg = <0x62>;
+ vdd-supply = <&vdd_5v0>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index b1bf52e85720..066f88fc92c6 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -62,6 +62,8 @@ properties:
# Aosong temperature & humidity sensors with I2C interface
- aosong,aht10
- aosong,aht20
+ - aosong,am2315
+ - aosong,am2320
- aosong,dht20
# Arduino microcontroller interface over SPI on UnoQ board
- arduino,unoq-mcu
@@ -400,8 +402,6 @@ properties:
- onnn,adt7462
# 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
- plx,pex8648
- # Pulsedlight LIDAR range-finding sensor
- - pulsedlight,lidar-lite-v2
# Renesas HS3001 Temperature and Relative Humidity Sensors
- renesas,hs3001
# Renesas ISL29501 time-of-flight sensor
diff --git a/Documentation/iio/ade9000.rst b/Documentation/iio/ade9000.rst
index c9ff702a4251..72737b5ec59c 100644
--- a/Documentation/iio/ade9000.rst
+++ b/Documentation/iio/ade9000.rst
@@ -1,24 +1,30 @@
.. SPDX-License-Identifier: GPL-2.0
-===============
-ADE9000 driver
-===============
+======================
+ADE9000/ADE9078 driver
+======================
-This driver supports Analog Device's ADE9000 energy measurement IC on SPI bus.
+This driver supports Analog Device's ADE9000 and ADE9078 energy measurement
+ICs on SPI bus.
1. Supported devices
====================
* `ADE9000 <https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf>`_
-
-The ADE9000 is a highly accurate, fully integrated, multiphase energy and power
-quality monitoring device. Superior analog performance and a digital signal
-processing (DSP) core enable accurate energy monitoring over a wide dynamic
-range. An integrated high end reference ensures low drift over temperature
-with a combined drift of less than ±25 ppm/°C maximum for the entire channel
-including a programmable gain amplifier (PGA) and an analog-to-digital
+* `ADE9078 <https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9078.pdf>`_
+
+The ADE9000 and ADE9078 are highly accurate, fully integrated, multiphase energy
+and power quality monitoring devices. Superior analog performance and a digital
+signal processing (DSP) core enable accurate energy monitoring over a wide
+dynamic range. An integrated high end reference ensures low drift over
+temperature with a combined drift of less than ±25 ppm/°C maximum for the entire
+channel including a programmable gain amplifier (PGA) and an analog-to-digital
converter (ADC).
+The ADE9078 does not provide the on-chip dip/swell (sag) detection available on
+the ADE9000, so the RMS voltage swell/dip events described below are only
+available on the ADE9000.
+
2. Device attributes
====================
diff --git a/Documentation/iio/adxl380.rst b/Documentation/iio/adxl380.rst
index 654d4c0e84a1..a1c249dc2e51 100644
--- a/Documentation/iio/adxl380.rst
+++ b/Documentation/iio/adxl380.rst
@@ -39,13 +39,13 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_scale | Scale for the accelerometer channels. |
+---------------------------------------------------+----------------------------------------------------------+
-| in_accel_filter_high_pass_3db_frequency | Low pass filter bandwidth. |
+| in_accel_filter_high_pass_3db_frequency | High pass filter bandwidth. |
+---------------------------------------------------+----------------------------------------------------------+
-| in_accel_filter_high_pass_3db_frequency_available | Available low pass filter bandwidth configurations. |
+| in_accel_filter_high_pass_3db_frequency_available | Available high pass filter bandwidth configurations. |
+---------------------------------------------------+----------------------------------------------------------+
-| in_accel_filter_low_pass_3db_frequency | High pass filter bandwidth. |
+| in_accel_filter_low_pass_3db_frequency | Low pass filter bandwidth. |
+---------------------------------------------------+----------------------------------------------------------+
-| in_accel_filter_low_pass_3db_frequency_available | Available high pass filter bandwidth configurations. |
+| in_accel_filter_low_pass_3db_frequency_available | Available low pass filter bandwidth configurations. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+---------------------------------------------------+----------------------------------------------------------+
diff --git a/MAINTAINERS b/MAINTAINERS
index 31b5975bbc89..be1d822afb10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -640,6 +640,7 @@ F: drivers/iio/accel/adxl355_spi.c
ADXL367 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
@@ -1517,6 +1518,7 @@ F: drivers/iio/adc/ad4080.c
ANALOG DEVICES INC AD4130 DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
@@ -1622,6 +1624,7 @@ F: drivers/iio/dac/ad7293.c
ANALOG DEVICES INC AD74115 DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
@@ -1630,6 +1633,7 @@ F: drivers/iio/addac/ad74115.c
ANALOG DEVICES INC AD74413R DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
@@ -4440,6 +4444,15 @@ S: Orphan
F: Documentation/devicetree/bindings/sound/axentia,*
F: sound/soc/atmel/tse850-pcm5142.c
+AXIADO SARADC DRIVER
+M: Petar Stepanovic <pstepanovic@axiado.com>
+M: Akhila Kavi <akavi@axiado.com>
+M: Prasad Bolisetty <pbolisetty@axiado.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
+F: drivers/iio/adc/axiado_saradc.c
+
AXIS ARTPEC ARM64 SoC SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com>
M: Lars Persson <lars.persson@axis.com>
@@ -12718,6 +12731,7 @@ R: Nuno Sá <nuno.sa@analog.com>
R: Andy Shevchenko <andy@kernel.org>
L: linux-iio@vger.kernel.org
S: Maintained
+Q: https://patchwork.kernel.org/project/linux-iio/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
F: Documentation/ABI/testing/configfs-iio*
F: Documentation/ABI/testing/sysfs-bus-iio*
@@ -16159,6 +16173,15 @@ L: linux-iio@vger.kernel.org
S: Supported
F: drivers/iio/temperature/max30208.c
+MAXIM MAX40080 CURRENT SENSE AMPLIFIER DRIVER
+M: Ciprian Hegbeli <ciprian.hegbeli@analog.com>
+M: Stefan Popa <stefan.popa@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,max40080.yaml
+F: drivers/iio/adc/max40080.c
+
MAXIM MAX7360 KEYPAD LED MFD DRIVER
M: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
S: Maintained
@@ -27297,6 +27320,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml
F: drivers/iio/adc/ti-ads1119.c
+TI ADS112C04 ADC DRIVER
+M: Kyle Hsieh <kylehsieh1995@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/ti,ads112c04.yaml
+F: drivers/iio/adc/ti-ads112c04.c
+
TI ADS112C14 ADC DRIVER
M: David Lechner <dlechner@baylibre.com>
L: linux-iio@vger.kernel.org
diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c
index 4ff1c7a0988b..9b47e66c49ea 100644
--- a/drivers/iio/accel/adxl367.c
+++ b/drivers/iio/accel/adxl367.c
@@ -13,6 +13,7 @@
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
@@ -81,6 +82,7 @@
#define ADXL367_SAMPLES_L_MASK GENMASK(7, 0)
#define ADXL367_REG_INT1_MAP 0x2A
+#define ADXL367_REG_INT2_MAP 0x2B
#define ADXL367_INT_INACT_MASK BIT(5)
#define ADXL367_INT_ACT_MASK BIT(4)
#define ADXL367_INT_FIFO_WATERMARK_MASK BIT(2)
@@ -168,6 +170,8 @@ struct adxl367_state {
enum adxl367_odr odr;
enum adxl367_range range;
+ u8 int_map_reg;
+
unsigned int act_threshold;
unsigned int act_time_ms;
unsigned int inact_threshold;
@@ -366,8 +370,7 @@ static int adxl367_set_act_interrupt_en(struct adxl367_state *st,
{
unsigned int mask = adxl367_act_int_mask_tbl[act];
- return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP,
- mask, en ? mask : 0);
+ return regmap_assign_bits(st->regmap, st->int_map_reg, mask, en);
}
static int adxl367_get_act_interrupt_en(struct adxl367_state *st,
@@ -378,7 +381,7 @@ static int adxl367_get_act_interrupt_en(struct adxl367_state *st,
unsigned int val;
int ret;
- ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val);
+ ret = regmap_read(st->regmap, st->int_map_reg, &val);
if (ret)
return ret;
@@ -401,9 +404,8 @@ static int adxl367_set_act_en(struct adxl367_state *st,
static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st,
bool en)
{
- return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP,
- ADXL367_INT_FIFO_WATERMARK_MASK,
- en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0);
+ return regmap_assign_bits(st->regmap, st->int_map_reg,
+ ADXL367_INT_FIFO_WATERMARK_MASK, en);
}
static int adxl367_get_fifo_mode(struct adxl367_state *st,
@@ -631,8 +633,7 @@ static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg,
bool en)
{
- return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK,
- en ? ADXL367_ADC_EN_MASK : 0);
+ return regmap_assign_bits(st->regmap, reg, ADXL367_ADC_EN_MASK, en);
}
static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st,
@@ -787,6 +788,13 @@ static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status,
if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status))
return false;
+ if (fifo_entries > ADXL367_FIFO_SIZE) {
+ dev_err_ratelimited(st->dev,
+ "FIFO entry count %u exceeds FIFO size %u\n",
+ fifo_entries, ADXL367_FIFO_SIZE);
+ return true;
+ }
+
fifo_entries -= fifo_entries % st->fifo_set_size;
ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries);
@@ -1426,6 +1434,36 @@ static int adxl367_setup(struct adxl367_state *st)
return adxl367_set_measure_en(st, true);
}
+static int adxl367_set_int_map_reg(struct adxl367_state *st, int irq)
+{
+ int ret;
+
+ /*
+ * Route the mappings to whichever of INT1/INT2 is named in the
+ * interrupt-names property. -EINVAL (property absent) and -ENODATA
+ * (name not listed) are not errors; anything else is.
+ */
+ ret = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT1");
+ if (ret > 0) {
+ st->int_map_reg = ADXL367_REG_INT1_MAP;
+ return ret;
+ }
+ if (ret != -ENODATA && ret != -EINVAL)
+ return ret;
+
+ ret = fwnode_irq_get_byname(dev_fwnode(st->dev), "INT2");
+ if (ret > 0) {
+ st->int_map_reg = ADXL367_REG_INT2_MAP;
+ return ret;
+ }
+ if (ret != -ENODATA && ret != -EINVAL)
+ return ret;
+
+ /* No INT1/INT2 named: default to INT1 and the bus interrupt. */
+ st->int_map_reg = ADXL367_REG_INT1_MAP;
+ return irq;
+}
+
int adxl367_probe(struct device *dev, const struct adxl367_ops *ops,
void *context, struct regmap *regmap, int irq)
{
@@ -1482,6 +1520,11 @@ int adxl367_probe(struct device *dev, const struct adxl367_ops *ops,
if (ret)
return ret;
+ ret = adxl367_set_int_map_reg(st, irq);
+ if (ret < 0)
+ return dev_err_probe(st->dev, ret, "Failed to get interrupt\n");
+ irq = ret;
+
ret = devm_request_threaded_irq(st->dev, irq, NULL,
adxl367_irq_handler, IRQF_ONESHOT,
indio_dev->name, indio_dev);
diff --git a/drivers/iio/accel/adxl380.c b/drivers/iio/accel/adxl380.c
index 7dca5523091f..8518ee23e114 100644
--- a/drivers/iio/accel/adxl380.c
+++ b/drivers/iio/accel/adxl380.c
@@ -966,6 +966,13 @@ static irqreturn_t adxl380_irq_handler(int irq, void *p)
if (ret)
return IRQ_HANDLED;
+ if (fifo_entries > ADXL380_FIFO_SAMPLES) {
+ dev_err_ratelimited(st->dev,
+ "FIFO entry count %u exceeds FIFO size %lu\n",
+ fifo_entries, ADXL380_FIFO_SAMPLES);
+ 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);
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 7e3cff4cca91..6bc64ca87f4b 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -6,8 +6,6 @@
*
* TODO:
* - Support for power management
- * - Support events and interrupts
- * - Create channel for step count
* - Create channel for sensor time
*/
diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c
index 9ff70a373141..41aafe85e82d 100644
--- a/drivers/iio/accel/kionix-kx022a.c
+++ b/drivers/iio/accel/kionix-kx022a.c
@@ -301,11 +301,10 @@ struct kx022a_data {
__le16 *fifo_buffer;
/* 3 x 16bit accel data + timestamp */
- __le16 buffer[8] __aligned(IIO_DMA_MINALIGN);
struct {
__le16 channels[3];
aligned_s64 ts;
- } scan;
+ } scan __aligned(IIO_DMA_MINALIGN);
};
static const struct iio_mount_matrix *
@@ -611,14 +610,15 @@ static int kx022a_get_axis(struct kx022a_data *data,
struct iio_chan_spec const *chan,
int *val)
{
+ __le16 *buf = &data->scan.channels[0];
int ret;
- ret = regmap_bulk_read(data->regmap, chan->address, &data->buffer[0],
- sizeof(__le16));
+ ret = regmap_bulk_read(data->regmap, chan->address,
+ buf, sizeof(*buf));
if (ret)
return ret;
- *val = (s16)le16_to_cpu(data->buffer[0]);
+ *val = (s16)le16_to_cpup(buf);
return IIO_VAL_INT;
}
@@ -864,7 +864,8 @@ static int __kx022a_fifo_flush(struct iio_dev *idev, unsigned int samples,
for_each_set_bit(bit, idev->active_scan_mask, AXIS_MAX)
chs[bit] = sam[bit];
- iio_push_to_buffers_with_timestamp(idev, &data->scan, tstamp);
+ iio_push_to_buffers_with_ts(idev, &data->scan,
+ sizeof(data->scan), tstamp);
tstamp += sample_period;
}
@@ -1047,12 +1048,13 @@ static irqreturn_t kx022a_trigger_handler(int irq, void *p)
struct kx022a_data *data = iio_priv(idev);
int ret;
- ret = regmap_bulk_read(data->regmap, data->chip_info->xout_l, data->buffer,
- KX022A_FIFO_SAMPLES_SIZE_BYTES);
+ ret = regmap_bulk_read(data->regmap, data->chip_info->xout_l,
+ data->scan.channels, KX022A_FIFO_SAMPLES_SIZE_BYTES);
if (ret < 0)
goto err_read;
- iio_push_to_buffers_with_timestamp(idev, data->buffer, data->timestamp);
+ iio_push_to_buffers_with_ts(idev, &data->scan, sizeof(data->scan),
+ data->timestamp);
err_read:
iio_trigger_notify_done(idev->trig);
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 7d683686dd9d..fe62a903f0e2 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -18,6 +18,8 @@
* TODO: orientation events
*/
+#include <linux/array_size.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -110,8 +112,7 @@ struct mma8452_data {
u8 data_cfg;
const struct mma_chip_info *chip_info;
int sleep_val;
- struct regulator *vdd_reg;
- struct regulator *vddio_reg;
+ struct regulator_bulk_data regs[2];
/* Ensure correct alignment of time stamp when present */
struct {
@@ -222,15 +223,15 @@ static int mma8452_drdy(struct mma8452_data *data)
static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
{
#ifdef CONFIG_PM
+ struct device *dev = &client->dev;
int ret;
if (on)
- ret = pm_runtime_resume_and_get(&client->dev);
+ ret = pm_runtime_resume_and_get(dev);
else
- ret = pm_runtime_put_autosuspend(&client->dev);
+ ret = pm_runtime_put_autosuspend(dev);
if (ret < 0) {
- dev_err(&client->dev,
- "failed to change power state to %d\n", on);
+ dev_err(dev, "failed to change power state to %d\n", on);
return ret;
}
@@ -495,14 +496,14 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
int i, ret;
switch (mask) {
- case IIO_CHAN_INFO_RAW:
- if (!iio_device_claim_direct(indio_dev))
+ case IIO_CHAN_INFO_RAW: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- mutex_lock(&data->lock);
+ guard(mutex)(&data->lock);
+
ret = mma8452_read(data, buffer);
- mutex_unlock(&data->lock);
- iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
@@ -511,6 +512,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
+ }
case IIO_CHAN_INFO_SCALE:
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
*val = data->chip_info->mma_scales[i][0];
@@ -599,36 +601,30 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
int ret;
int is_active;
- mutex_lock(&data->lock);
+ guard(mutex)(&data->lock);
is_active = mma8452_is_active(data);
- if (is_active < 0) {
- ret = is_active;
- goto fail;
- }
+ if (is_active < 0)
+ return is_active;
/* config can only be changed when in standby */
if (is_active > 0) {
ret = mma8452_standby(data);
if (ret < 0)
- goto fail;
+ return ret;
}
ret = i2c_smbus_write_byte_data(data->client, reg, val);
if (ret < 0)
- goto fail;
+ return ret;
if (is_active > 0) {
ret = mma8452_active(data);
if (ret < 0)
- goto fail;
+ return ret;
}
- ret = 0;
-fail:
- mutex_unlock(&data->lock);
-
- return ret;
+ return 0;
}
static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode)
@@ -777,14 +773,11 @@ static int mma8452_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))
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- ret = __mma8452_write_raw(indio_dev, chan, val, val2, mask);
- iio_device_release_direct(indio_dev);
- return ret;
+ return __mma8452_write_raw(indio_dev, chan, val, val2, mask);
}
static int mma8452_get_event_regs(struct mma8452_data *data,
@@ -1552,7 +1545,7 @@ static int mma8452_probe(struct i2c_client *client)
struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
@@ -1562,32 +1555,21 @@ static int mma8452_probe(struct i2c_client *client)
data->chip_info = i2c_get_match_data(client);
if (!data->chip_info)
- return dev_err_probe(&client->dev, -ENODEV,
- "unknown device model\n");
+ return dev_err_probe(dev, -ENODEV, "unknown device model\n");
- ret = iio_read_mount_matrix(&client->dev, &data->orientation);
+ ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
return ret;
- data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
- if (IS_ERR(data->vdd_reg))
- return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg),
- "failed to get VDD regulator!\n");
-
- data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
- if (IS_ERR(data->vddio_reg))
- return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg),
- "failed to get VDDIO regulator!\n");
-
- ret = regulator_enable(data->vdd_reg);
+ data->regs[0].supply = "vdd";
+ data->regs[1].supply = "vddio";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->regs), data->regs);
if (ret)
- return dev_err_probe(dev, ret, "failed to enable VDD regulator!\n");
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
- ret = regulator_enable(data->vddio_reg);
- if (ret) {
- dev_err_probe(dev, ret, "failed to enable VDDIO regulator!\n");
- goto disable_regulator_vdd;
- }
+ ret = regulator_bulk_enable(ARRAY_SIZE(data->regs), data->regs);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable regulators\n");
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
if (ret < 0)
@@ -1608,7 +1590,7 @@ static int mma8452_probe(struct i2c_client *client)
goto disable_regulators;
}
- dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
+ dev_info(dev, "registering %s accelerometer; ID 0x%x\n",
data->chip_info->name, data->chip_info->chip_id);
i2c_set_clientdata(client, indio_dev);
@@ -1641,10 +1623,10 @@ static int mma8452_probe(struct i2c_client *client)
if (client->irq) {
int irq2;
- irq2 = fwnode_irq_get_byname(dev_fwnode(&client->dev), "INT2");
+ irq2 = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (irq2 == client->irq) {
- dev_dbg(&client->dev, "using interrupt line INT2\n");
+ dev_dbg(dev, "using interrupt line INT2\n");
} else {
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG5,
@@ -1652,7 +1634,7 @@ static int mma8452_probe(struct i2c_client *client)
if (ret < 0)
goto disable_regulators;
- dev_dbg(&client->dev, "using interrupt line INT1\n");
+ dev_dbg(dev, "using interrupt line INT1\n");
}
ret = i2c_smbus_write_byte_data(client,
@@ -1689,14 +1671,13 @@ static int mma8452_probe(struct i2c_client *client)
goto buffer_cleanup;
}
- ret = pm_runtime_set_active(&client->dev);
+ ret = pm_runtime_set_active(dev);
if (ret < 0)
goto free_irq;
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev,
- MMA8452_AUTO_SUSPEND_DELAY_MS);
- pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, MMA8452_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
@@ -1722,10 +1703,7 @@ trigger_cleanup:
mma8452_trigger_cleanup(indio_dev);
disable_regulators:
- regulator_disable(data->vddio_reg);
-
-disable_regulator_vdd:
- regulator_disable(data->vdd_reg);
+ regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs);
return ret;
}
@@ -1734,11 +1712,12 @@ static void mma8452_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma8452_data *data = iio_priv(indio_dev);
+ struct device *dev = &client->dev;
iio_device_unregister(indio_dev);
- pm_runtime_disable(&client->dev);
- pm_runtime_set_suspended(&client->dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
if (client->irq)
free_irq(client->irq, indio_dev);
@@ -1747,8 +1726,7 @@ static void mma8452_remove(struct i2c_client *client)
mma8452_trigger_cleanup(indio_dev);
mma8452_standby(iio_priv(indio_dev));
- regulator_disable(data->vddio_reg);
- regulator_disable(data->vdd_reg);
+ regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs);
}
#ifdef CONFIG_PM
@@ -1758,23 +1736,16 @@ static int mma8452_runtime_suspend(struct device *dev)
struct mma8452_data *data = iio_priv(indio_dev);
int ret;
- mutex_lock(&data->lock);
- ret = mma8452_standby(data);
- mutex_unlock(&data->lock);
+ scoped_guard(mutex, &data->lock)
+ ret = mma8452_standby(data);
if (ret < 0) {
- dev_err(&data->client->dev, "powering off device failed\n");
+ dev_err(dev, "powering off device failed\n");
return -EAGAIN;
}
- ret = regulator_disable(data->vddio_reg);
- if (ret) {
- dev_err(dev, "failed to disable VDDIO regulator\n");
- return ret;
- }
-
- ret = regulator_disable(data->vdd_reg);
+ ret = regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs);
if (ret) {
- dev_err(dev, "failed to disable VDD regulator\n");
+ dev_err(dev, "failed to disable regulators\n");
return ret;
}
@@ -1787,16 +1758,9 @@ static int mma8452_runtime_resume(struct device *dev)
struct mma8452_data *data = iio_priv(indio_dev);
int ret, sleep_val;
- ret = regulator_enable(data->vdd_reg);
- if (ret) {
- dev_err(dev, "failed to enable VDD regulator\n");
- return ret;
- }
-
- ret = regulator_enable(data->vddio_reg);
+ ret = regulator_bulk_enable(ARRAY_SIZE(data->regs), data->regs);
if (ret) {
- dev_err(dev, "failed to enable VDDIO regulator\n");
- regulator_disable(data->vdd_reg);
+ dev_err(dev, "failed to enable regulators\n");
return ret;
}
@@ -1814,8 +1778,7 @@ static int mma8452_runtime_resume(struct device *dev)
return 0;
runtime_resume_failed:
- regulator_disable(data->vddio_reg);
- regulator_disable(data->vdd_reg);
+ regulator_bulk_disable(ARRAY_SIZE(data->regs), data->regs);
return ret;
}
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 415e519ad4eb..7a60ffd0ddd8 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -576,20 +576,22 @@ config AD9467
called ad9467.
config ADE9000
- tristate "Analog Devices ADE9000 Multiphase Energy, and Power Quality Monitoring IC Driver"
+ tristate "Analog Devices Multiphase Energy and Power Quality Monitoring IC Driver"
depends on SPI
select REGMAP_SPI
select IIO_BUFFER
select IIO_KFIFO_BUF
help
- Say yes here to build support for the Analog Devices ADE9000,
- a highly accurate, multiphase energy and power quality monitoring
- integrated circuit.
+ Say yes here to build support for the following Analog Devices
+ highly accurate, multiphase energy and power quality monitoring
+ integrated circuits:
+ - ADE9000
+ - ADE9078
- The device features high-precision analog-to-digital converters
+ The devices feature high-precision analog-to-digital converters
and digital signal processing to compute RMS values, power factor,
- frequency, and harmonic analysis. It supports SPI communication
- and provides buffered data output through the IIO framework.
+ frequency, and harmonic analysis. They support SPI communication
+ and provide buffered data output through the IIO framework.
To compile this driver as a module, choose M here: the module will
be called ade9000.
@@ -653,6 +655,17 @@ config AT91_SAMA5D2_ADC
To compile this driver as a module, choose M here: the module will be
called at91-sama5d2_adc.
+config AXIADO_SARADC
+ tristate "Axiado SARADC driver"
+ depends on ARCH_AXIADO || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ Say yes here to build support for the SARADC found in Axiado
+ SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called axiado_saradc.
+
config AXP20X_ADC
tristate "X-Powers AXP20X and AXP22X ADC driver"
depends on MFD_AXP20X
@@ -1116,6 +1129,17 @@ config MAX34408
To compile this driver as a module, choose M here: the module will be
called max34408.
+config MAX40080
+ tristate "Analog Devices MAX40080 Current Sense Amplifier"
+ depends on I2C
+ help
+ Say yes here to build support for the Analog Devices MAX40080
+ bidirectional current-sense amplifier with a 12-bit ADC and an I2C
+ interface.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max40080.
+
config MAX77541_ADC
tristate "Analog Devices MAX77541 ADC driver"
depends on MFD_MAX77541
@@ -1801,11 +1825,14 @@ config TI_ADS1018
called ti-ads1018.
config TI_ADS1100
- tristate "Texas Instruments ADS1100 and ADS1000 ADC"
+ tristate "Texas Instruments ADS1100 and similar single channel I2C ADC"
depends on I2C
help
- If you say yes here you get support for Texas Instruments ADS1100 and
- ADS1000 ADC chips.
+ If you say yes here you get support for TI single channel I2C Analog
+ Devices.
+ * ADS1000 12-Bit, 128 SPS Analog-to-Digital Converter
+ * ADS1100 16-Bit, 128 SPS Analog-to-Digital Converter
+ * ADS1110 16-Bit, 240 SPS Analog-to-Digital Converter
This driver can also be built as a module. If so, the module will be
called ti-ads1100.
@@ -1822,6 +1849,16 @@ config TI_ADS1119
This driver can also be built as a module. If so, the module will be
called ti-ads1119.
+config TI_ADS112C04
+ tristate "Texas Instruments ADS112C04 ADC"
+ depends on I2C
+ help
+ If you say yes here you get support for Texas Instruments
+ ADS112C04 (16-bit) I2C analog to digital converters.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads112c04.
+
config TI_ADS112C14
tristate "Texas Instruments ADS112C14/ADS122C14"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index dcec0abb03b7..12db7df4114b 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
+obj-$(CONFIG_AXIADO_SARADC) += axiado_saradc.o
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
@@ -95,6 +96,7 @@ obj-$(CONFIG_MAX1241) += max1241.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MAX14001) += max14001.o
obj-$(CONFIG_MAX34408) += max34408.o
+obj-$(CONFIG_MAX40080) += max40080.o
obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
@@ -155,6 +157,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
+obj-$(CONFIG_TI_ADS112C04) += ti-ads112c04.o
obj-$(CONFIG_TI_ADS112C14) += ti-ads112c14.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
index 04cd6628ebff..994625a7cf8b 100644
--- a/drivers/iio/adc/ad4080.c
+++ b/drivers/iio/adc/ad4080.c
@@ -145,6 +145,10 @@
#define AD4080_MAX_SAMP_FREQ 40000000
#define AD4080_MIN_SAMP_FREQ 1250000
+/* debugfs direct_reg_access channel windowing: 0x0RR = ch0, 0x1RR = ch1 */
+#define AD4080_DEBUGFS_REG_CH_MSK GENMASK(15, 8)
+#define AD4080_DEBUGFS_REG_OFFSET_MSK GENMASK(7, 0)
+
enum ad4080_filter_type {
FILTER_NONE,
SINC_1,
@@ -210,11 +214,16 @@ static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct ad4080_state *st = iio_priv(indio_dev);
+ unsigned int ch = FIELD_GET(AD4080_DEBUGFS_REG_CH_MSK, reg);
+ unsigned int offset = FIELD_GET(AD4080_DEBUGFS_REG_OFFSET_MSK, reg);
+
+ if (ch >= st->info->num_channels)
+ return -EINVAL;
if (readval)
- return regmap_read(st->regmap[0], reg, readval);
+ return regmap_read(st->regmap[ch], offset, readval);
- return regmap_write(st->regmap[0], reg, writeval);
+ return regmap_write(st->regmap[ch], offset, writeval);
}
static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2)
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index da6caabfe2a4..1ed088f10bb2 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -242,14 +242,6 @@
#define ADE9000_LAST_PAGE_BIT BIT(15)
#define ADE9000_MIDDLE_PAGE_BIT BIT(7)
-/*
- * Full scale Codes referred from Datasheet. Respective digital codes are
- * produced when ADC inputs are at full scale.
- */
-#define ADE9000_RMS_FULL_SCALE_CODES 52866837
-#define ADE9000_WATT_FULL_SCALE_CODES 20694066
-#define ADE9000_PCF_FULL_SCALE_CODES 74770000
-
/* Phase and channel definitions */
#define ADE9000_PHASE_A_NR 0
#define ADE9000_PHASE_B_NR 1
@@ -290,7 +282,29 @@ enum ade9000_wfb_cfg {
#define ADE9000_ADDR_ADJUST(addr, chan) \
(((chan) == 0 ? 0 : (chan) == 1 ? 2 : 4) << 4 | (addr))
+/**
+ * struct ade9000_chip_info - part-specific configuration
+ * @name: IIO device name reported to userspace
+ * @channels: channel specification for this part
+ * @num_channels: number of entries in @channels
+ * @rms_full_scale_codes: digital code produced at full-scale RMS input
+ * @watt_full_scale_codes: digital code produced at full-scale power input
+ * @pcf_full_scale_codes: digital code produced at full-scale xI_PCF/xV_PCF input
+ *
+ * The full-scale codes are taken from the respective datasheets and are used to
+ * derive the IIO scale of the raw measurement channels.
+ */
+struct ade9000_chip_info {
+ const char *name;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int rms_full_scale_codes;
+ unsigned int watt_full_scale_codes;
+ unsigned int pcf_full_scale_codes;
+};
+
struct ade9000_state {
+ const struct ade9000_chip_info *info;
struct completion reset_completion;
struct mutex lock; /* Protects SPI transactions */
u8 wf_src;
@@ -488,6 +502,7 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
.scan_index = -1 \
}
+/* With swell/dip (sag) events - ADE9000 only */
#define ADE9000_ALTVOLTAGE_RMS_CHANNEL(num) { \
.type = IIO_ALTVOLTAGE, \
.channel = num, \
@@ -503,6 +518,20 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
.scan_index = -1 \
}
+/* Without swell/dip events - parts lacking DIP_LVL/SWELL_LVL hardware */
+#define ADE9000_ALTVOLTAGE_RMS_CHANNEL_NO_EVENTS(num) { \
+ .type = IIO_ALTVOLTAGE, \
+ .channel = num, \
+ .address = ADE9000_ADDR_ADJUST(ADE9000_REG_AVRMS, num), \
+ .channel2 = IIO_MOD_RMS, \
+ .modified = 1, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .scan_index = -1 \
+}
+
#define ADE9000_POWER_ACTIVE_CHANNEL(num) { \
.type = IIO_POWER, \
.channel = num, \
@@ -584,43 +613,82 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
.scan_index = -1 \
}
-static const struct iio_chan_spec ade9000_channels[] = {
- /* Phase A channels */
- ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR),
- ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AWATTHR_LO),
- ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AVAHR_LO),
- ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, ADE9000_REG_AFVARHR_LO),
- ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_A_NR),
- /* Phase B channels */
- ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_B_NR),
- ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BWATTHR_LO),
- ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BVAHR_LO),
- ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR, ADE9000_REG_BFVARHR_LO),
- ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_B_NR),
- /* Phase C channels */
- ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_ALTVOLTAGE_RMS_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_C_NR),
- ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CWATTHR_LO),
- ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CVAHR_LO),
- ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR, ADE9000_REG_CFVARHR_LO),
- ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_C_NR),
+/*
+ * Declare a full channel array. @altvoltage_rms picks the RMS voltage channel
+ * variant so parts without dip/swell hardware (e.g. ADE9078) omit those events.
+ */
+#define ADE9000_DECLARE_CHANNELS(_name, altvoltage_rms) \
+ static const struct iio_chan_spec _name[] = { \
+ /* Phase A channels */ \
+ ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_A_NR), \
+ ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_A_NR), \
+ ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_A_NR), \
+ altvoltage_rms(ADE9000_PHASE_A_NR), \
+ ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR), \
+ ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR), \
+ ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_A_NR), \
+ ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_A_NR, \
+ ADE9000_REG_AWATTHR_LO), \
+ ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_A_NR, \
+ ADE9000_REG_AVAHR_LO), \
+ ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_A_NR, \
+ ADE9000_REG_AFVARHR_LO), \
+ ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_A_NR), \
+ /* Phase B channels */ \
+ ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_B_NR), \
+ ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_B_NR), \
+ ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_B_NR), \
+ altvoltage_rms(ADE9000_PHASE_B_NR), \
+ ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR), \
+ ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR), \
+ ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_B_NR), \
+ ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_B_NR, \
+ ADE9000_REG_BWATTHR_LO), \
+ ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_B_NR, \
+ ADE9000_REG_BVAHR_LO), \
+ ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_B_NR, \
+ ADE9000_REG_BFVARHR_LO), \
+ ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_B_NR), \
+ /* Phase C channels */ \
+ ADE9000_CURRENT_CHANNEL(ADE9000_PHASE_C_NR), \
+ ADE9000_VOLTAGE_CHANNEL(ADE9000_PHASE_C_NR), \
+ ADE9000_ALTCURRENT_RMS_CHANNEL(ADE9000_PHASE_C_NR), \
+ altvoltage_rms(ADE9000_PHASE_C_NR), \
+ ADE9000_POWER_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR), \
+ ADE9000_POWER_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR), \
+ ADE9000_POWER_APPARENT_CHANNEL(ADE9000_PHASE_C_NR), \
+ ADE9000_ENERGY_ACTIVE_CHANNEL(ADE9000_PHASE_C_NR, \
+ ADE9000_REG_CWATTHR_LO), \
+ ADE9000_ENERGY_APPARENT_CHANNEL(ADE9000_PHASE_C_NR, \
+ ADE9000_REG_CVAHR_LO), \
+ ADE9000_ENERGY_REACTIVE_CHANNEL(ADE9000_PHASE_C_NR, \
+ ADE9000_REG_CFVARHR_LO), \
+ ADE9000_POWER_FACTOR_CHANNEL(ADE9000_PHASE_C_NR), \
+ }
+
+ADE9000_DECLARE_CHANNELS(ade9000_channels, ADE9000_ALTVOLTAGE_RMS_CHANNEL);
+ADE9000_DECLARE_CHANNELS(ade9078_channels, ADE9000_ALTVOLTAGE_RMS_CHANNEL_NO_EVENTS);
+
+/*
+ * Full-scale codes referred from the respective datasheets. These are the
+ * digital codes produced when the ADC inputs are at full scale.
+ */
+static const struct ade9000_chip_info ade9000_chip_info = {
+ .name = "ade9000",
+ .channels = ade9000_channels,
+ .num_channels = ARRAY_SIZE(ade9000_channels),
+ .rms_full_scale_codes = 52866837,
+ .watt_full_scale_codes = 20694066,
+ .pcf_full_scale_codes = 74770000,
+};
+
+static const struct ade9000_chip_info ade9078_chip_info = {
+ .name = "ade9078",
+ .channels = ade9078_channels,
+ .num_channels = ARRAY_SIZE(ade9078_channels),
+ .rms_full_scale_codes = 52866837,
+ .watt_full_scale_codes = 20823646,
+ .pcf_full_scale_codes = 74680000,
};
static const struct reg_sequence ade9000_initialization_sequence[] = {
@@ -1064,7 +1132,7 @@ static int ade9000_read_raw(struct iio_dev *indio_dev,
case ADE9000_REG_CI_PCF:
case ADE9000_REG_CV_PCF:
*val = 1;
- *val2 = ADE9000_PCF_FULL_SCALE_CODES;
+ *val2 = st->info->pcf_full_scale_codes;
return IIO_VAL_FRACTIONAL;
case ADE9000_REG_AIRMS:
case ADE9000_REG_AVRMS:
@@ -1073,14 +1141,14 @@ static int ade9000_read_raw(struct iio_dev *indio_dev,
case ADE9000_REG_CIRMS:
case ADE9000_REG_CVRMS:
*val = 1;
- *val2 = ADE9000_RMS_FULL_SCALE_CODES;
+ *val2 = st->info->rms_full_scale_codes;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_POWER:
*val = 1;
- *val2 = ADE9000_WATT_FULL_SCALE_CODES;
+ *val2 = st->info->watt_full_scale_codes;
return IIO_VAL_FRACTIONAL;
default:
break;
@@ -1692,6 +1760,10 @@ static int ade9000_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -ENODEV;
+
regmap = devm_regmap_init(dev, NULL, st, &ade9000_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Unable to allocate ADE9000 regmap");
@@ -1714,7 +1786,7 @@ static int ade9000_probe(struct spi_device *spi)
if (ret)
return ret;
- indio_dev->name = "ade9000";
+ indio_dev->name = st->info->name;
indio_dev->info = &ade9000_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->setup_ops = &ade9000_buffer_ops;
@@ -1737,8 +1809,8 @@ static int ade9000_probe(struct spi_device *spi)
if (ret)
return ret;
- indio_dev->channels = ade9000_channels;
- indio_dev->num_channels = ARRAY_SIZE(ade9000_channels);
+ indio_dev->channels = st->info->channels;
+ indio_dev->num_channels = st->info->num_channels;
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
&ade9000_buffer_ops);
@@ -1769,13 +1841,15 @@ static int ade9000_probe(struct spi_device *spi)
};
static const struct spi_device_id ade9000_id[] = {
- { .name = "ade9000" },
+ { .name = "ade9000", .driver_data = (kernel_ulong_t)&ade9000_chip_info },
+ { .name = "ade9078", .driver_data = (kernel_ulong_t)&ade9078_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ade9000_id);
static const struct of_device_id ade9000_of_match[] = {
- { .compatible = "adi,ade9000" },
+ { .compatible = "adi,ade9000", .data = &ade9000_chip_info },
+ { .compatible = "adi,ade9078", .data = &ade9078_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ade9000_of_match);
diff --git a/drivers/iio/adc/axiado_saradc.c b/drivers/iio/adc/axiado_saradc.c
new file mode 100644
index 000000000000..699ee31616fc
--- /dev/null
+++ b/drivers/iio/adc/axiado_saradc.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2026 Axiado Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+/* Register offsets */
+#define AX_SARADC_GLOBAL_CTRL_REG 0x0004
+#define AX_SARADC_MANUAL_CTRL_REG 0x0008
+#define AX_SARADC_DOUT_REG 0x001C
+
+/* GLOBAL_CTRL register fields */
+#define AX_SARADC_GLOBAL_CTRL_CH_EN_MASK GENMASK(31, 16)
+#define AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK GENMASK(6, 5)
+#define AX_SARADC_GLOBAL_CTRL_MODE_MASK GENMASK(4, 3)
+#define AX_SARADC_GLOBAL_CTRL_PD BIT(2)
+#define AX_SARADC_GLOBAL_CTRL_ENABLE BIT(0)
+
+/* GLOBAL_CTRL SAMPLE_MASK field value: 0 selects 16 samples. */
+#define AX_SARADC_GLOBAL_CTRL_SAMPLE_16 0
+
+/* GLOBAL_CTRL MODE_MASK field value: 1 selects manual mode. */
+#define AX_SARADC_GLOBAL_CTRL_MODE_MANUAL 1
+
+/* MANUAL_CTRL register fields */
+#define AX_SARADC_MANUAL_CTRL_ENABLE BIT(0)
+#define AX_SARADC_MANUAL_CTRL_CH_SEL_MASK GENMASK(4, 1)
+
+#define AX_RESOLUTION_BITS 10
+#define AX_SARADC_CONV_CYCLES 13
+#define AX_SARADC_CONV_DELAY_MARGIN_US 10
+
+struct axiado_saradc {
+ struct regmap *regmap;
+ struct mutex lock; /* Serializes ADC conversions. */
+ unsigned long clk_rate;
+ int vref_uV;
+};
+
+/*
+ * Registers contain transient control, status, and conversion data,
+ * so accesses must always go directly to hardware.
+ */
+static const struct regmap_config axiado_saradc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = AX_SARADC_DOUT_REG,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int axiado_saradc_conversion(struct axiado_saradc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ unsigned long usecs;
+ unsigned int regval;
+ int ret;
+
+ guard(mutex)(&info->lock);
+
+ /* Select the channel to be used and trigger conversion. */
+ ret = regmap_write(info->regmap, AX_SARADC_MANUAL_CTRL_REG,
+ AX_SARADC_MANUAL_CTRL_ENABLE |
+ FIELD_PREP(AX_SARADC_MANUAL_CTRL_CH_SEL_MASK, chan->channel));
+ if (ret)
+ return ret;
+
+ /* Hardware requires 13 conversion cycles at clk_rate. */
+ usecs = DIV_ROUND_UP(AX_SARADC_CONV_CYCLES * USEC_PER_SEC, info->clk_rate);
+ fsleep(usecs + AX_SARADC_CONV_DELAY_MARGIN_US);
+
+ ret = regmap_read(info->regmap, AX_SARADC_DOUT_REG, &regval);
+
+ /* Best effort to stop manual conversion. */
+ regmap_write(info->regmap, AX_SARADC_MANUAL_CTRL_REG, 0);
+
+ if (ret)
+ return ret;
+
+ *val = regval & GENMASK(AX_RESOLUTION_BITS - 1, 0);
+
+ return 0;
+}
+
+static int axiado_saradc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct axiado_saradc *info = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = axiado_saradc_conversion(info, chan, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->vref_uV / (MICRO / MILLI);
+ *val2 = AX_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info axiado_saradc_iio_info = {
+ .read_raw = axiado_saradc_read_raw,
+};
+
+struct axiado_saradc_soc_data {
+ const char *name;
+ unsigned int num_channels;
+};
+
+static const struct axiado_saradc_soc_data ax3000_saradc_data = {
+ .name = "ax3000_saradc",
+ .num_channels = 16,
+};
+
+static const struct axiado_saradc_soc_data ax3005_saradc_data = {
+ .name = "ax3005_saradc",
+ .num_channels = 8,
+};
+
+#define AX_SARADC_CH(_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), \
+ .datasheet_name = "adc" #_index, \
+ }
+
+static const struct iio_chan_spec axiado_saradc_iio_channels[] = {
+ AX_SARADC_CH(0),
+ AX_SARADC_CH(1),
+ AX_SARADC_CH(2),
+ AX_SARADC_CH(3),
+ AX_SARADC_CH(4),
+ AX_SARADC_CH(5),
+ AX_SARADC_CH(6),
+ AX_SARADC_CH(7),
+ AX_SARADC_CH(8),
+ AX_SARADC_CH(9),
+ AX_SARADC_CH(10),
+ AX_SARADC_CH(11),
+ AX_SARADC_CH(12),
+ AX_SARADC_CH(13),
+ AX_SARADC_CH(14),
+ AX_SARADC_CH(15),
+};
+
+static void axiado_saradc_disable(void *map)
+{
+ regmap_write(map, AX_SARADC_GLOBAL_CTRL_REG, AX_SARADC_GLOBAL_CTRL_PD);
+}
+
+static int axiado_saradc_probe(struct platform_device *pdev)
+{
+ const struct axiado_saradc_soc_data *soc_data;
+ struct device *dev = &pdev->dev;
+ struct axiado_saradc *info;
+ struct iio_dev *indio_dev;
+ void __iomem *regs;
+ struct regmap *map;
+ struct clk *clk;
+ u32 regval;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &axiado_saradc_regmap_config);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ info->regmap = map;
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ info->clk_rate = clk_get_rate(clk);
+ if (!info->clk_rate)
+ return dev_err_probe(dev, -EINVAL, "invalid clock rate\n");
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get vref voltage\n");
+
+ info->vref_uV = ret;
+
+ soc_data = device_get_match_data(dev);
+ if (!soc_data)
+ return dev_err_probe(dev, -ENODATA, "failed to get match data\n");
+
+ ret = devm_mutex_init(dev, &info->lock);
+ if (ret)
+ return ret;
+
+ regval = FIELD_PREP(AX_SARADC_GLOBAL_CTRL_CH_EN_MASK,
+ GENMASK(soc_data->num_channels - 1, 0)) |
+ FIELD_PREP(AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK,
+ AX_SARADC_GLOBAL_CTRL_SAMPLE_16) |
+ FIELD_PREP(AX_SARADC_GLOBAL_CTRL_MODE_MASK,
+ AX_SARADC_GLOBAL_CTRL_MODE_MANUAL) |
+ AX_SARADC_GLOBAL_CTRL_ENABLE;
+
+ ret = regmap_write(map, AX_SARADC_GLOBAL_CTRL_REG, regval);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, axiado_saradc_disable, map);
+ if (ret)
+ return ret;
+
+ indio_dev->name = soc_data->name;
+ indio_dev->info = &axiado_saradc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = axiado_saradc_iio_channels;
+ indio_dev->num_channels = soc_data->num_channels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id axiado_saradc_match[] = {
+ {
+ .compatible = "axiado,ax3000-saradc",
+ .data = &ax3000_saradc_data,
+ },
+ {
+ .compatible = "axiado,ax3005-saradc",
+ .data = &ax3005_saradc_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axiado_saradc_match);
+
+static struct platform_driver axiado_saradc_driver = {
+ .driver = {
+ .name = "axiado-saradc",
+ .of_match_table = axiado_saradc_match,
+ },
+ .probe = axiado_saradc_probe,
+};
+module_platform_driver(axiado_saradc_driver);
+
+MODULE_AUTHOR("Axiado Corporation");
+MODULE_DESCRIPTION("Axiado SARADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c
index cf4738b16e62..ab66b97d2f04 100644
--- a/drivers/iio/adc/bcm_iproc_adc.c
+++ b/drivers/iio/adc/bcm_iproc_adc.c
@@ -506,10 +506,10 @@ static int iproc_adc_probe(struct platform_device *pdev)
{
struct iproc_adc_priv *adc_priv;
struct iio_dev *indio_dev = NULL;
+ struct device *dev = &pdev->dev;
int ret;
- indio_dev = devm_iio_device_alloc(&pdev->dev,
- sizeof(*adc_priv));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc_priv));
if (!indio_dev)
return -ENOMEM;
@@ -522,19 +522,14 @@ static int iproc_adc_probe(struct platform_device *pdev)
adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"adc-syscon");
- if (IS_ERR(adc_priv->regmap)) {
- dev_err(&pdev->dev, "failed to get handle for tsc syscon\n");
- ret = PTR_ERR(adc_priv->regmap);
- return ret;
- }
+ if (IS_ERR(adc_priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(adc_priv->regmap),
+ "failed to get handle for tsc syscon\n");
- adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
- if (IS_ERR(adc_priv->adc_clk)) {
- dev_err(&pdev->dev,
- "failed getting clock tsc_clk\n");
- ret = PTR_ERR(adc_priv->adc_clk);
- return ret;
- }
+ adc_priv->adc_clk = devm_clk_get(dev, "tsc_clk");
+ if (IS_ERR(adc_priv->adc_clk))
+ return dev_err_probe(dev, PTR_ERR(adc_priv->adc_clk),
+ "failed getting clock tsc_clk\n");
adc_priv->irqno = platform_get_irq(pdev, 0);
if (adc_priv->irqno < 0)
@@ -542,32 +537,23 @@ static int iproc_adc_probe(struct platform_device *pdev)
ret = regmap_clear_bits(adc_priv->regmap, IPROC_REGCTL2,
IPROC_ADC_AUXIN_SCAN_ENA);
- if (ret) {
- dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to write IPROC_REGCTL2\n");
- ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno,
+ ret = devm_request_threaded_irq(dev, adc_priv->irqno,
iproc_adc_interrupt_handler,
iproc_adc_interrupt_thread,
IRQF_SHARED, "iproc-adc", indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "request_irq error %d\n", ret);
+ if (ret)
return ret;
- }
ret = clk_prepare_enable(adc_priv->adc_clk);
- if (ret) {
- dev_err(&pdev->dev,
- "clk_prepare_enable failed %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable clock\n");
ret = iproc_adc_enable(indio_dev);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable adc %d\n", ret);
+ if (ret)
goto err_adc_enable;
- }
indio_dev->name = "iproc-static-adc";
indio_dev->info = &iproc_adc_iio_info;
diff --git a/drivers/iio/adc/max40080.c b/drivers/iio/adc/max40080.c
new file mode 100644
index 000000000000..c6946eb6ff5b
--- /dev/null
+++ b/drivers/iio/adc/max40080.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MAX40080 Digital Current-Sense Amplifier driver
+ *
+ * Copyright 2026 Analog Devices, Inc.
+ *
+ * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX40080.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/i2c.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <asm/byteorder.h>
+
+#include <linux/iio/iio.h>
+
+#define MAX40080_REG_CFG 0x00
+#define MAX40080_CFG_MODE_MSK GENMASK(2, 0)
+#define MAX40080_CFG_PEC_EN_MSK BIT(5)
+#define MAX40080_CFG_RANGE_MSK BIT(6)
+#define MAX40080_CFG_FILTER_MSK GENMASK(14, 12)
+
+#define MAX40080_REG_FIFO_CFG 0x0A
+#define MAX40080_FIFO_CFG_STORE_IV_MSK GENMASK(1, 0)
+
+#define MAX40080_REG_IV 0x10
+/* Current is a 13-bit two's-complement value (magnitude + sign bit). */
+#define MAX40080_IV_I_MSK GENMASK(12, 0)
+#define MAX40080_IV_I_SIGN_BIT 12
+#define MAX40080_IV_V_MAG_MSK GENMASK(27, 16)
+#define MAX40080_IV_VALID_MSK BIT(31)
+
+/* CFG.mode field values. */
+#define MAX40080_CFG_MODE_STDBY 0x00
+#define MAX40080_CFG_MODE_SINGLE 0x02
+
+/* CFG.range field values. */
+#define MAX40080_CFG_RANGE_50mV 0
+#define MAX40080_CFG_RANGE_10mV 1
+
+/* FIFO_CFG.store_iv field values. */
+#define MAX40080_FIFO_CFG_STORE_IV 0x02
+
+#define MAX40080_ADC_RES_BITS 12
+#define MAX40080_INTER_VREF_mV 1250
+#define MAX40080_V_BUFF_GAIN 30
+#define MAX40080_CSA_50mV_GAIN 25
+#define MAX40080_CSA_10mV_GAIN 125
+
+/*
+ * The RANGE field (CFG bit 6) selects one of two current-sense full-scale
+ * ranges (the MAX40080 supports exactly two: +/-50 mV and +/-10 mV). Indexed
+ * by the CFG.range field value.
+ */
+static const int max40080_csa_gain[] = {
+ [MAX40080_CFG_RANGE_50mV] = MAX40080_CSA_50mV_GAIN,
+ [MAX40080_CFG_RANGE_10mV] = MAX40080_CSA_10mV_GAIN,
+};
+
+struct max40080_state {
+ struct i2c_client *client;
+ /* Serializes read-modify-write access to the CFG register. */
+ struct mutex lock;
+ u32 shunt_resistor_uOhm;
+ /* Cached configuration: the selected RANGE index and oversampling ratio. */
+ unsigned int range;
+ int oversampling_ratio;
+ /*
+ * Precomputed current scale (mA per code) for each RANGE setting, as
+ * {integer, nano} pairs for IIO_VAL_INT_PLUS_NANO. The range is
+ * selected by writing the corresponding scale.
+ */
+ int current_scale[2][2];
+};
+
+static const int max40080_oversampling_avail[] = { 1, 8, 16, 32, 64, 128 };
+
+static int max40080_update_bits(struct max40080_state *st, u8 reg,
+ u16 mask, u16 val)
+{
+ int tmp;
+
+ tmp = i2c_smbus_read_word_data(st->client, reg);
+ if (tmp < 0)
+ return tmp;
+
+ tmp = (tmp & ~mask) | (val & mask);
+
+ return i2c_smbus_write_word_data(st->client, reg, tmp);
+}
+
+/*
+ * In single-measurement mode the device sits idle until it receives an SMBus
+ * Quick Command, then performs exactly one current and one voltage conversion
+ * and returns to idle. Triggering on demand this way (rather than running the
+ * FIFO continuously in active mode) means each read returns a fresh, coherent
+ * current/voltage pair instead of the oldest queued FIFO entry.
+ */
+static int max40080_trigger_measurement(struct max40080_state *st)
+{
+ return i2c_smbus_xfer(st->client->adapter, st->client->addr,
+ st->client->flags, I2C_SMBUS_WRITE, 0,
+ I2C_SMBUS_QUICK, NULL);
+}
+
+/*
+ * A single measurement holds the matched current/voltage pair in one 32-bit
+ * word (MAX40080_REG_IV). Reading all four bytes in one transaction returns
+ * both from the same conversion; reading the separate current (0x0C) and
+ * voltage (0x0E) registers would decorrelate the two channels.
+ *
+ * Unlike the word accesses used elsewhere, this is a plain I2C block read: the
+ * SMBus layer does not append or verify a PEC byte for it even when PEC is
+ * otherwise enabled for the device, so this transfer is not PEC protected.
+ */
+static int max40080_read_iv_once(struct max40080_state *st, u32 *iv)
+{
+ __le32 buf = 0;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(st->client, MAX40080_REG_IV,
+ sizeof(buf), (u8 *)&buf);
+ if (ret < 0)
+ return ret;
+
+ *iv = le32_to_cpu(buf);
+
+ return 0;
+}
+
+static int max40080_read_iv(struct max40080_state *st, u32 *iv)
+{
+ u32 tmp = 0;
+ int ret, io_ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = max40080_trigger_measurement(st);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Wait for the conversion to complete by polling the FIFO valid bit
+ * (or bail out on an I2C error). Polling the device's own status makes
+ * this independent of the actual conversion time, which varies with the
+ * oversampling ratio and the bus speed. The timeout is only a safety
+ * ceiling: the worst case is the maximum 128x averaging on both the
+ * current and voltage channels at the slowest 15 ksps base rate plus
+ * the inter-channel switching time, i.e. roughly 20 ms; 50 ms leaves
+ * ample margin.
+ */
+ ret = read_poll_timeout(max40080_read_iv_once, io_ret,
+ io_ret || (tmp & MAX40080_IV_VALID_MSK),
+ 500, 50 * USEC_PER_MSEC, false, st, &tmp);
+ if (io_ret)
+ return io_ret;
+
+ /*
+ * Propagate the last-read value even on timeout so the caller can
+ * inspect it for debugging.
+ */
+ *iv = tmp;
+
+ return ret;
+}
+
+static int max40080_get_current(struct max40080_state *st, int *val)
+{
+ u32 iv;
+ int ret;
+
+ ret = max40080_read_iv(st, &iv);
+ if (ret)
+ return ret;
+
+ *val = sign_extend32(FIELD_GET(MAX40080_IV_I_MSK, iv),
+ MAX40080_IV_I_SIGN_BIT);
+
+ return 0;
+}
+
+static int max40080_get_voltage(struct max40080_state *st, int *val)
+{
+ u32 iv;
+ int ret;
+
+ ret = max40080_read_iv(st, &iv);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(MAX40080_IV_V_MAG_MSK, iv);
+
+ return 0;
+}
+
+static int max40080_set_range(struct max40080_state *st, unsigned int range)
+{
+ int ret;
+
+ ret = max40080_update_bits(st, MAX40080_REG_CFG, MAX40080_CFG_RANGE_MSK,
+ FIELD_PREP(MAX40080_CFG_RANGE_MSK, range));
+ if (ret)
+ return ret;
+
+ WRITE_ONCE(st->range, range);
+
+ return 0;
+}
+
+/*
+ * Precompute the current scale (mA per code) for each RANGE setting as
+ * {integer, nano} pairs. The shunt drop for a full-scale code is
+ * Vref[mV] / (BIT(ADC_RES_BITS) * gain)
+ * and current = Vshunt / Rshunt, so with Rshunt in micro-ohms the scale in
+ * mA/code is
+ * Vref[mV] * NANO * MICRO / (BIT(ADC_RES_BITS) * gain * Rshunt[uOhm])
+ * expressed as an integer part plus a nano fractional part.
+ */
+static void max40080_calc_current_scale(struct max40080_state *st)
+{
+ u64 numerator, denominator;
+ u32 rem;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(max40080_csa_gain); i++) {
+ numerator = (u64)MAX40080_INTER_VREF_mV * NANO * MICRO;
+ denominator = BIT_ULL(MAX40080_ADC_RES_BITS) * max40080_csa_gain[i] *
+ st->shunt_resistor_uOhm;
+ numerator = div64_u64(numerator, denominator);
+ st->current_scale[i][0] = div_u64_rem(numerator, NANO, &rem);
+ st->current_scale[i][1] = rem;
+ }
+}
+
+/*
+ * max40080_oversampling_avail[] is ordered so that its index is the FILTER
+ * field value (index 0 = no averaging, index 1 = 8x, ...). Return that index
+ * for an exact match, or -EINVAL for a value that is not on the list.
+ */
+static int max40080_oversampling_to_filter(int val)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(max40080_oversampling_avail); i++) {
+ if (max40080_oversampling_avail[i] == val)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int max40080_set_oversampling_ratio(struct max40080_state *st, int val)
+{
+ int filter;
+ int ret;
+
+ filter = max40080_oversampling_to_filter(val);
+ if (filter < 0)
+ return filter;
+
+ ret = max40080_update_bits(st, MAX40080_REG_CFG, MAX40080_CFG_FILTER_MSK,
+ FIELD_PREP(MAX40080_CFG_FILTER_MSK, filter));
+ if (ret)
+ return ret;
+
+ WRITE_ONCE(st->oversampling_ratio, val);
+
+ return 0;
+}
+
+static int max40080_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max40080_state *st = iio_priv(indio_dev);
+ unsigned int range;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_CURRENT) {
+ ret = max40080_get_current(st, val);
+ if (ret)
+ return ret;
+ } else if (chan->type == IIO_VOLTAGE) {
+ ret = max40080_get_voltage(st, val);
+ if (ret)
+ return ret;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_CURRENT) {
+ /*
+ * The selectable current-sense range is exposed through
+ * scale: each RANGE setting has its own precomputed
+ * mA-per-code value. Userspace picks the range by
+ * writing the matching scale.
+ *
+ * Use READ_ONCE to ensure the compiler reads st->range
+ * exactly once, so val and val2 come from the same
+ * setting even if a concurrent write changes st->range.
+ */
+ range = READ_ONCE(st->range);
+ *val = st->current_scale[range][0];
+ *val2 = st->current_scale[range][1];
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+ /* voltage[mV] = raw * Vref[mV] * buffer_gain / BIT(ADC_RES_BITS) */
+ *val = MAX40080_INTER_VREF_mV * MAX40080_V_BUFF_GAIN;
+ *val2 = MAX40080_ADC_RES_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = READ_ONCE(st->oversampling_ratio);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max40080_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct max40080_state *st = iio_priv(indio_dev);
+
+ guard(mutex)(&st->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ /* Only the current channel has a selectable range/scale. */
+ if (chan->type != IIO_CURRENT)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(max40080_csa_gain); i++) {
+ if (val == st->current_scale[i][0] &&
+ val2 == st->current_scale[i][1])
+ return max40080_set_range(st, i);
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return max40080_set_oversampling_ratio(st, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max40080_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;
+ }
+}
+
+static int max40080_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ struct max40080_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_CURRENT)
+ return -EINVAL;
+
+ *vals = (int *)st->current_scale;
+ *length = ARRAY_SIZE(max40080_csa_gain) * 2;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = max40080_oversampling_avail;
+ *length = ARRAY_SIZE(max40080_oversampling_avail);
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max40080_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int write_val, unsigned int *read_val)
+{
+ struct max40080_state *st = iio_priv(indio_dev);
+ int val;
+
+ if (read_val) {
+ val = i2c_smbus_read_word_data(st->client, reg);
+ if (val < 0)
+ return val;
+
+ *read_val = val;
+
+ return 0;
+ }
+
+ return i2c_smbus_write_word_data(st->client, reg, write_val);
+}
+
+static const struct iio_info max40080_info = {
+ .read_raw = max40080_read_raw,
+ .write_raw = max40080_write_raw,
+ .write_raw_get_fmt = max40080_write_raw_get_fmt,
+ .read_avail = max40080_read_avail,
+ .debugfs_reg_access = &max40080_reg_access,
+};
+
+static const struct iio_chan_spec max40080_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ },
+};
+
+/*
+ * Configure the device from the cached state. The device powers up in standby
+ * with PEC enabled (CFG POR = 0x0060), so PEC is kept enabled throughout.
+ */
+static int max40080_init(struct max40080_state *st)
+{
+ u16 fifo_cfg, cfg;
+ int ret, filter;
+
+ filter = max40080_oversampling_to_filter(st->oversampling_ratio);
+ if (filter < 0)
+ return filter;
+
+ /*
+ * Put the device in standby before (re)configuring the FIFO: the FIFO
+ * configuration register can only be written while the device is not
+ * converting.
+ */
+ cfg = FIELD_PREP(MAX40080_CFG_MODE_MSK, MAX40080_CFG_MODE_STDBY) |
+ FIELD_PREP(MAX40080_CFG_PEC_EN_MSK, 1);
+ ret = i2c_smbus_write_word_data(st->client, MAX40080_REG_CFG, cfg);
+ if (ret)
+ return ret;
+
+ /* Store a matched current+voltage pair per conversion. */
+ fifo_cfg = FIELD_PREP(MAX40080_FIFO_CFG_STORE_IV_MSK, MAX40080_FIFO_CFG_STORE_IV);
+ ret = i2c_smbus_write_word_data(st->client, MAX40080_REG_FIFO_CFG,
+ fifo_cfg);
+ if (ret)
+ return ret;
+
+ /*
+ * Use single-measurement mode: the device stays idle and converts once
+ * per SMBus Quick Command (see max40080_trigger_measurement()), so each
+ * read returns a fresh sample rather than a queued FIFO entry.
+ */
+ cfg = FIELD_PREP(MAX40080_CFG_MODE_MSK, MAX40080_CFG_MODE_SINGLE) |
+ FIELD_PREP(MAX40080_CFG_PEC_EN_MSK, 1) |
+ FIELD_PREP(MAX40080_CFG_RANGE_MSK, st->range) |
+ FIELD_PREP(MAX40080_CFG_FILTER_MSK, filter);
+
+ return i2c_smbus_write_word_data(st->client, MAX40080_REG_CFG, cfg);
+}
+
+static int max40080_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const char *propname;
+ struct iio_dev *indio_dev;
+ struct max40080_state *st;
+ int ret;
+
+ /*
+ * The device powers up with PEC enabled (CFG POR = 0x0060) and rejects
+ * unprotected transactions, so PEC support is mandatory, along with
+ * word access, the I2C block read used for the current/voltage pair,
+ * and the Quick Command used to trigger a conversion.
+ */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK |
+ I2C_FUNC_SMBUS_QUICK |
+ I2C_FUNC_SMBUS_PEC))
+ return -EOPNOTSUPP;
+
+ client->flags |= I2C_CLIENT_PEC;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->client = client;
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ propname = "shunt-resistor-micro-ohms";
+ ret = device_property_read_u32(dev, propname, &st->shunt_resistor_uOhm);
+ if (ret)
+ return dev_err_probe(dev, ret, "can't read %s\n", propname);
+ if (!st->shunt_resistor_uOhm)
+ return dev_err_probe(dev, -EINVAL, "%s must be non-zero\n", propname);
+
+ max40080_calc_current_scale(st);
+
+ /* Defaults: 50 mV range, no averaging. */
+ st->range = MAX40080_CFG_RANGE_50mV;
+ st->oversampling_ratio = 1;
+
+ indio_dev->name = "max40080";
+ indio_dev->info = &max40080_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max40080_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max40080_channels);
+
+ ret = max40080_init(st);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id max40080_i2c_ids[] = {
+ { .name = "max40080" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max40080_i2c_ids);
+
+static const struct of_device_id max40080_of_match[] = {
+ { .compatible = "adi,max40080" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max40080_of_match);
+
+static struct i2c_driver max40080_driver = {
+ .driver = {
+ .name = "max40080",
+ .of_match_table = max40080_of_match,
+ },
+ .probe = max40080_probe,
+ .id_table = max40080_i2c_ids,
+};
+module_i2c_driver(max40080_driver);
+
+MODULE_AUTHOR("Ciprian Hegbeli <ciprian.hegbeli@analog.com>");
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices MAX40080 current-sense amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index 000e39ca5c62..2b003fd5afeb 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -739,7 +739,7 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
struct device *dev = indio_dev->dev.parent;
- struct clk_init_data init;
+ struct clk_init_data init = { };
const char *clk_parents[1];
init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#adc_div", dev_name(dev));
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index 23055405a6e0..2d934452eb11 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/kstrtox.h>
#include <linux/unaligned.h>
/*
@@ -494,11 +495,13 @@ static ssize_t pac1934_shunt_value_store(struct device *dev,
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct pac1934_chip_info *info = iio_priv(indio_dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
- int sh_val;
+ unsigned int sh_val;
+ int ret;
- if (kstrtouint(buf, 10, &sh_val)) {
+ ret = kstrtouint(buf, 10, &sh_val);
+ if (ret) {
dev_err(dev, "Shunt value is not valid\n");
- return -EINVAL;
+ return ret;
}
scoped_guard(mutex, &info->lock)
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 0f0bf2906af0..5c45fae11890 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -348,6 +348,19 @@ static const struct rockchip_saradc_data rk3588_saradc_data = {
.read = rockchip_saradc_read_v2,
};
+static const struct iio_chan_spec rockchip_rv1106_saradc_iio_channels[] = {
+ SARADC_CHANNEL(0, "adc0", 10),
+ SARADC_CHANNEL(1, "adc1", 10),
+};
+
+static const struct rockchip_saradc_data rv1106_saradc_data = {
+ .channels = rockchip_rv1106_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rv1106_saradc_iio_channels),
+ .clk_rate = 1000000,
+ .start = rockchip_saradc_start_v2,
+ .read = rockchip_saradc_read_v2,
+};
+
static const struct of_device_id rockchip_saradc_match[] = {
{
.compatible = "rockchip,saradc",
@@ -370,6 +383,9 @@ static const struct of_device_id rockchip_saradc_match[] = {
}, {
.compatible = "rockchip,rk3588-saradc",
.data = &rk3588_saradc_data,
+ }, {
+ .compatible = "rockchip,rv1106-saradc",
+ .data = &rv1106_saradc_data,
},
{ }
};
diff --git a/drivers/iio/adc/sophgo-cv1800b-adc.c b/drivers/iio/adc/sophgo-cv1800b-adc.c
index bdc3e1326a9a..c6eed09acfcd 100644
--- a/drivers/iio/adc/sophgo-cv1800b-adc.c
+++ b/drivers/iio/adc/sophgo-cv1800b-adc.c
@@ -182,6 +182,8 @@ static int cv1800b_adc_probe(struct platform_device *pdev)
return PTR_ERR(saradc->regs);
saradc->irq = platform_get_irq_optional(pdev, 0);
+ if (saradc->irq < 0 && saradc->irq != -ENXIO)
+ return saradc->irq;
if (saradc->irq > 0) {
init_completion(&saradc->completion);
ret = devm_request_irq(dev, saradc->irq,
diff --git a/drivers/iio/adc/ti-ads1100.c b/drivers/iio/adc/ti-ads1100.c
index 9fe8d54cce83..9957c4d813fb 100644
--- a/drivers/iio/adc/ti-ads1100.c
+++ b/drivers/iio/adc/ti-ads1100.c
@@ -5,7 +5,7 @@
* Copyright (c) 2023, Topic Embedded Products
*
* Datasheet: https://www.ti.com/lit/gpn/ads1100
- * IIO driver for ADS1100 and ADS1000 ADC 16-bit I2C
+ * IIO driver for ADS1100 and similar single channel ADC 16-bit I2C
*/
#include <linux/bitfield.h>
@@ -15,10 +15,12 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/iopoll.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/time.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
@@ -39,17 +41,44 @@
#define ADS1100_SINGLESHOT ADS1100_CFG_SC
#define ADS1100_SLEEP_DELAY_MS 2000
+#define ADS1110_INTERNAL_REF_mV 2048
static const int ads1100_data_rate[] = { 128, 32, 16, 8 };
+static const int ads1110_data_rate[] = { 240, 60, 30, 15 };
static const int ads1100_data_rate_bits[] = { 12, 14, 15, 16 };
+/* Timeout based on the minimum sample rate of 8 SPS (7500ms) */
+#define ADS1100_MAX_DRDY_TIMEOUT_US (7500 * USEC_PER_MSEC)
+
+struct ads1100_config {
+ const char *name;
+ const int *available_data_rate_Hz;
+ const int data_rate_count;
+ bool has_internal_vref_only;
+};
+
+static const struct ads1100_config ads1100_config = {
+ .name = "ads1100",
+ .available_data_rate_Hz = ads1100_data_rate,
+ .data_rate_count = ARRAY_SIZE(ads1100_data_rate),
+ .has_internal_vref_only = false,
+};
+
+static const struct ads1100_config ads1110_config = {
+ .name = "ads1110",
+ .available_data_rate_Hz = ads1110_data_rate,
+ .data_rate_count = ARRAY_SIZE(ads1110_data_rate),
+ .has_internal_vref_only = true,
+};
+
struct ads1100_data {
struct i2c_client *client;
struct regulator *reg_vdd;
struct mutex lock;
int scale_avail[2 * 4]; /* 4 gain settings */
+ const struct ads1100_config *chip_info;
u8 config;
- bool supports_data_rate; /* Only the ADS1100 can select the rate */
+ bool supports_data_rate;
};
static const struct iio_chan_spec ads1100_channel = {
@@ -85,6 +114,20 @@ static int ads1100_set_config_bits(struct ads1100_data *data, u8 mask, u8 value)
return 0;
};
+static int ads1100_get_vref_millivolts(struct ads1100_data *data)
+{
+ int voltage_uV;
+
+ if (data->chip_info->has_internal_vref_only)
+ return ADS1110_INTERNAL_REF_mV;
+
+ voltage_uV = regulator_get_voltage(data->reg_vdd);
+ if (voltage_uV < 0)
+ return voltage_uV;
+
+ return voltage_uV / (MICRO / MILLI);
+}
+
static int ads1100_data_bits(struct ads1100_data *data)
{
return ads1100_data_rate_bits[FIELD_GET(ADS1100_DR_MASK, data->config)];
@@ -123,10 +166,86 @@ static int ads1100_get_adc_result(struct ads1100_data *data, int chan, int *val)
return 0;
}
+static int ads1100_conversion_busy(struct ads1100_data *data)
+{
+ u8 buffer[3];
+ int ret;
+
+ ret = i2c_master_recv(data->client, (char *)&buffer, sizeof(buffer));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "I2C read fail: %d\n", ret);
+ return ret;
+ }
+
+ return FIELD_GET(ADS1100_CFG_ST_BSY, buffer[2]);
+}
+
+static int ads1100_wait_single_conversion(struct ads1100_data *data)
+{
+ int data_rate_index = FIELD_GET(ADS1100_DR_MASK, data->config);
+ int data_rate_Hz = data->chip_info->available_data_rate_Hz[data_rate_index];
+ unsigned long poll_us = DIV_ROUND_CLOSEST(USEC_PER_SEC, data_rate_Hz) / 4;
+ int busy;
+ int ret;
+
+ ret = readx_poll_timeout(ads1100_conversion_busy, data,
+ busy, busy <= 0,
+ poll_us, ADS1100_MAX_DRDY_TIMEOUT_US);
+ if (busy < 0)
+ return busy;
+
+ return ret;
+}
+
+static int ads1100_start_single_conversion(struct ads1100_data *data)
+{
+ u8 config = data->config | ADS1100_CFG_SC;
+ int ret;
+
+ ret = i2c_master_send(data->client, &config, sizeof(config));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "I2C write fail: %d\n", ret);
+ return ret;
+ }
+ /* Need to wait because of change from continuous to single mode */
+ ret = ads1100_wait_single_conversion(data);
+ if (ret)
+ return ret;
+
+ config |= ADS1100_CFG_ST_BSY;
+
+ ret = i2c_master_send(data->client, &config, sizeof(config));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "I2C write fail: %d\n", ret);
+ return ret;
+ }
+
+ /* No need to cache it, it's status bit */
+ data->config = config & ~ADS1100_CFG_ST_BSY;
+
+ return 0;
+}
+
+static int ads1100_poll_data_ready(struct ads1100_data *data)
+{
+ int ret;
+
+ ret = ads1100_start_single_conversion(data);
+ if (ret)
+ return ret;
+
+ ret = ads1100_wait_single_conversion(data);
+ if (ret)
+ return ret;
+
+ return ads1100_set_config_bits(data, ADS1100_CFG_SC, ADS1100_CONTINUOUS);
+}
+
static int ads1100_set_scale(struct ads1100_data *data, int val, int val2)
{
int microvolts;
int gain;
+ int ret;
/* With Vdd between 2.7 and 5V, the scale is always below 1 */
if (val)
@@ -135,7 +254,12 @@ static int ads1100_set_scale(struct ads1100_data *data, int val, int val2)
if (!val2)
return -EINVAL;
- microvolts = regulator_get_voltage(data->reg_vdd);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&data->client->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ microvolts = ads1100_get_vref_millivolts(data) * (MICRO / MILLI);
/*
* val2 is in 'micro' units, n = val2 / 1000000
* result must be millivolts, d = microvolts / 1000
@@ -149,32 +273,40 @@ static int ads1100_set_scale(struct ads1100_data *data, int val, int val2)
ads1100_set_config_bits(data, ADS1100_PGA_MASK, ffs(gain) - 1);
- return 0;
+ return ads1100_poll_data_ready(data);
}
static int ads1100_set_data_rate(struct ads1100_data *data, int chan, int rate)
{
unsigned int i;
unsigned int size;
+ int ret;
- size = data->supports_data_rate ? ARRAY_SIZE(ads1100_data_rate) : 1;
+ size = data->supports_data_rate ? data->chip_info->data_rate_count : 1;
for (i = 0; i < size; i++) {
- if (ads1100_data_rate[i] == rate)
- return ads1100_set_config_bits(data, ADS1100_DR_MASK,
- FIELD_PREP(ADS1100_DR_MASK, i));
+ if (data->chip_info->available_data_rate_Hz[i] == rate)
+ break;
}
- return -EINVAL;
-}
+ if (i == size)
+ return -EINVAL;
-static int ads1100_get_vdd_millivolts(struct ads1100_data *data)
-{
- return regulator_get_voltage(data->reg_vdd) / (MICRO / MILLI);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&data->client->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = ads1100_set_config_bits(data, ADS1100_DR_MASK,
+ FIELD_PREP(ADS1100_DR_MASK, i));
+ if (ret)
+ return ret;
+
+ return ads1100_poll_data_ready(data);
}
static void ads1100_calc_scale_avail(struct ads1100_data *data)
{
- int millivolts = ads1100_get_vdd_millivolts(data);
+ int millivolts = ads1100_get_vref_millivolts(data);
unsigned int i;
for (i = 0; i < ARRAY_SIZE(data->scale_avail) / 2; i++) {
@@ -196,9 +328,9 @@ static int ads1100_read_avail(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*type = IIO_VAL_INT;
- *vals = ads1100_data_rate;
+ *vals = data->chip_info->available_data_rate_Hz;
if (data->supports_data_rate)
- *length = ARRAY_SIZE(ads1100_data_rate);
+ *length = data->chip_info->data_rate_count;
else
*length = 1;
return IIO_AVAIL_LIST;
@@ -218,6 +350,7 @@ static int ads1100_read_raw(struct iio_dev *indio_dev,
{
int ret;
struct ads1100_data *data = iio_priv(indio_dev);
+ int data_rate_index;
guard(mutex)(&data->lock);
switch (mask) {
@@ -233,12 +366,12 @@ static int ads1100_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/* full-scale is the supply voltage in millivolts */
- *val = ads1100_get_vdd_millivolts(data);
+ *val = ads1100_get_vref_millivolts(data);
*val2 = 15 + FIELD_GET(ADS1100_PGA_MASK, data->config);
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
- *val = ads1100_data_rate[FIELD_GET(ADS1100_DR_MASK,
- data->config)];
+ data_rate_index = FIELD_GET(ADS1100_DR_MASK, data->config);
+ *val = data->chip_info->available_data_rate_Hz[data_rate_index];
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -318,7 +451,12 @@ static int ads1100_probe(struct i2c_client *client)
data->client = client;
mutex_init(&data->lock);
- indio_dev->name = "ads1100";
+ data->chip_info = i2c_get_match_data(client);
+ if (!data->chip_info)
+ return dev_err_probe(dev, -ENODATA,
+ "Can't get device data from firmware\n");
+
+ indio_dev->name = data->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = &ads1100_channel;
indio_dev->num_channels = 1;
@@ -400,16 +538,18 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ads1100_pm_ops,
NULL);
static const struct i2c_device_id ads1100_id[] = {
- { .name = "ads1100" },
- { .name = "ads1000" },
+ { .name = "ads1000", .driver_data = (kernel_ulong_t)&ads1100_config },
+ { .name = "ads1100", .driver_data = (kernel_ulong_t)&ads1100_config },
+ { .name = "ads1110", .driver_data = (kernel_ulong_t)&ads1110_config },
{ }
};
MODULE_DEVICE_TABLE(i2c, ads1100_id);
static const struct of_device_id ads1100_of_match[] = {
- {.compatible = "ti,ads1100" },
- {.compatible = "ti,ads1000" },
+ { .compatible = "ti,ads1000", .data = &ads1100_config },
+ { .compatible = "ti,ads1100", .data = &ads1100_config },
+ { .compatible = "ti,ads1110", .data = &ads1110_config },
{ }
};
diff --git a/drivers/iio/adc/ti-ads112c04.c b/drivers/iio/adc/ti-ads112c04.c
new file mode 100644
index 000000000000..acf7209db761
--- /dev/null
+++ b/drivers/iio/adc/ti-ads112c04.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Texas Instruments ADS112C04 16-bit I2C ADC driver
+ *
+ * Copyright (c) 2026 Kyle Hsieh <kylehsieh1995@gmail.com>
+ *
+ * Datasheet: https://www.ti.com/lit/ds/symlink/ads112c04.pdf
+ * Based on TI Reference Code and standard Linux IIO framework.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+#define ADS112C04_CMD_RESET 0x06
+#define ADS112C04_CMD_START_SYNC 0x08
+#define ADS112C04_CMD_RDATA 0x10
+#define ADS112C04_CMD_RREG(reg) (0x20 | ((reg) << 2))
+#define ADS112C04_CMD_WREG(reg) (0x40 | ((reg) << 2))
+
+#define ADS112C04_REG_CONFIG0 0x00
+#define ADS112C04_CONF0_MUX GENMASK(7, 4)
+#define ADS112C04_CONF0_MUX_AIN0_AIN1 0
+#define ADS112C04_CONF0_MUX_AIN_SINGLE_BASE 8
+#define ADS112C04_CONF0_GAIN GENMASK(3, 1)
+#define ADS112C04_CONF0_GAIN_X1 0
+#define ADS112C04_CONF0_PGA_BYPASS BIT(0)
+
+#define ADS112C04_REG_CONFIG1 0x01
+#define ADS112C04_CONF1_DR GENMASK(7, 5)
+#define ADS112C04_CONF1_DR_20SPS 0
+#define ADS112C04_CONF1_MODE BIT(4)
+#define ADS112C04_CONF1_MODE_NORMAL 0
+#define ADS112C04_CONF1_CM BIT(3)
+#define ADS112C04_CONF1_CM_SINGLE_SHOT 0
+#define ADS112C04_CONF1_VREF GENMASK(2, 1)
+#define ADS112C04_CONF1_VREF_INTERNAL 0
+#define ADS112C04_CONF1_VREF_EXTERNAL 1
+#define ADS112C04_CONF1_VREF_AVDD 2
+#define ADS112C04_CONF1_TS BIT(0)
+#define ADS112C04_CONF1_TS_DISABLED 0
+
+#define ADS112C04_REG_CONFIG2 0x02
+#define ADS112C04_CONF2_DRDY BIT(7)
+
+#define ADS112C04_INT_REF_mV 2048
+
+#define ADS112C04_MAX_CHANNELS 12
+
+#define ADS112C04_RESOLUTION_BITS 16
+
+enum {
+ ADS112C04_VREF_SOURCE_INTERNAL,
+ ADS112C04_VREF_SOURCE_EXTERNAL,
+ ADS112C04_VREF_SOURCE_AVDD,
+};
+
+static const char * const ads112c04_vref_names[] = {
+ [ADS112C04_VREF_SOURCE_INTERNAL] = "internal",
+ [ADS112C04_VREF_SOURCE_EXTERNAL] = "external",
+ [ADS112C04_VREF_SOURCE_AVDD] = "avdd",
+};
+
+static const u8 ads112c04_vref_reg_val[] = {
+ [ADS112C04_VREF_SOURCE_INTERNAL] = ADS112C04_CONF1_VREF_INTERNAL,
+ [ADS112C04_VREF_SOURCE_EXTERNAL] = ADS112C04_CONF1_VREF_EXTERNAL,
+ [ADS112C04_VREF_SOURCE_AVDD] = ADS112C04_CONF1_VREF_AVDD,
+};
+
+/* Indexed by [AINP][AINN], -1 means the combination is not available. */
+static const s8 ads112c04_diff_mux[4][4] = {
+ { -1, 0, 1, 2 },
+ { 3, -1, 4, 5 },
+ { -1, -1, -1, 6 },
+ { -1, -1, 7, -1 },
+};
+
+struct ads112c04_state {
+ struct i2c_client *client;
+ /* Protects concurrent ADC reads and device configuration */
+ struct mutex lock;
+ struct completion completion;
+ u32 avdd_mV;
+ u32 ext_ref_mV;
+ u8 *vref_source; /* one entry per channel, indexed by scan order */
+ u8 config0;
+ u8 config1;
+};
+
+static int ads112c04_write_cmd(struct i2c_client *client, u8 cmd)
+{
+ return i2c_smbus_write_byte(client, cmd);
+}
+
+static int ads112c04_read_reg(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, ADS112C04_CMD_RREG(reg));
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int ads112c04_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(client, ADS112C04_CMD_WREG(reg), val);
+}
+
+static int ads112c04_wait_for_data(struct ads112c04_state *st)
+{
+ int ret, err;
+ u8 val;
+
+ if (st->client->irq > 0) {
+ /* Timeout is 100ms (slowest data rate is 20 SPS) */
+ if (!wait_for_completion_timeout(&st->completion, msecs_to_jiffies(100)))
+ return -ETIMEDOUT;
+
+ return 0;
+ }
+
+ ret = read_poll_timeout(ads112c04_read_reg, err,
+ err < 0 || (val & ADS112C04_CONF2_DRDY),
+ 1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC, false,
+ st->client, ADS112C04_REG_CONFIG2, &val);
+ if (err < 0)
+ return err;
+
+ return ret;
+}
+
+static int ads112c04_read_data(struct ads112c04_state *st, int *val)
+{
+ int ret;
+
+ ret = i2c_smbus_read_word_swapped(st->client, ADS112C04_CMD_RDATA);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32(ret, ADS112C04_RESOLUTION_BITS - 1);
+
+ return 0;
+}
+
+static int ads112c04_get_adc_result(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct ads112c04_state *st = iio_priv(indio_dev);
+ unsigned int idx = chan - indio_dev->channels;
+ u8 new_config0, new_config1;
+ int ret;
+
+ new_config0 = st->config0;
+ FIELD_MODIFY(ADS112C04_CONF0_MUX, &new_config0, chan->address);
+
+ if (st->config0 != new_config0) {
+ ret = ads112c04_write_reg(st->client, ADS112C04_REG_CONFIG0,
+ new_config0);
+ if (ret < 0)
+ return ret;
+
+ st->config0 = new_config0;
+ }
+
+ new_config1 = st->config1;
+ FIELD_MODIFY(ADS112C04_CONF1_VREF, &new_config1,
+ ads112c04_vref_reg_val[st->vref_source[idx]]);
+
+ if (st->config1 != new_config1) {
+ ret = ads112c04_write_reg(st->client, ADS112C04_REG_CONFIG1,
+ new_config1);
+ if (ret < 0)
+ return ret;
+
+ st->config1 = new_config1;
+ }
+
+ reinit_completion(&st->completion);
+
+ ret = ads112c04_write_cmd(st->client, ADS112C04_CMD_START_SYNC);
+ if (ret < 0)
+ return ret;
+
+ ret = ads112c04_wait_for_data(st);
+ if (ret < 0)
+ return ret;
+
+ return ads112c04_read_data(st, val);
+}
+
+static int ads112c04_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ads112c04_state *st = iio_priv(indio_dev);
+ unsigned int idx = chan - indio_dev->channels;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ ret = ads112c04_get_adc_result(indio_dev, chan, val);
+ mutex_unlock(&st->lock);
+
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (st->vref_source[idx]) {
+ case ADS112C04_VREF_SOURCE_EXTERNAL:
+ *val = st->ext_ref_mV;
+ break;
+ case ADS112C04_VREF_SOURCE_AVDD:
+ *val = st->avdd_mV;
+ break;
+ default:
+ *val = ADS112C04_INT_REF_mV;
+ break;
+ }
+ *val2 = ADS112C04_RESOLUTION_BITS - 1;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t ads112c04_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ads112c04_state *st = iio_priv(indio_dev);
+
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info ads112c04_info = {
+ .read_raw = ads112c04_read_raw,
+};
+
+static int ads112c04_parse_vref_source(struct fwnode_handle *child)
+{
+ if (!fwnode_property_present(child, "reference-sources"))
+ return ADS112C04_VREF_SOURCE_INTERNAL;
+
+ return fwnode_property_match_property_string(child, "reference-sources",
+ ads112c04_vref_names,
+ ARRAY_SIZE(ads112c04_vref_names));
+}
+
+static int ads112c04_parse_channels(struct iio_dev *indio_dev,
+ bool *need_avdd_ref, bool *need_ext_ref)
+{
+ struct device *dev = indio_dev->dev.parent;
+ struct ads112c04_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels;
+ u32 num_channels, pair[2], channel;
+ unsigned int i;
+ int ret;
+
+ num_channels = device_get_named_child_node_count(dev, "channel");
+ if (!num_channels)
+ return dev_err_probe(dev, -EINVAL, "no channel subnodes found\n");
+
+ if (num_channels > ADS112C04_MAX_CHANNELS)
+ return dev_err_probe(dev, -EINVAL,
+ "num of channel nodes exceeds %d\n",
+ ADS112C04_MAX_CHANNELS);
+
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ st->vref_source = devm_kcalloc(dev, num_channels,
+ sizeof(*st->vref_source), GFP_KERNEL);
+ if (!st->vref_source)
+ return -ENOMEM;
+
+ i = 0;
+ device_for_each_named_child_node_scoped(dev, child, "channel") {
+ const char *s_chan = "single-channel", *d_chan = "diff-channels";
+ struct iio_chan_spec *spec = &channels[i];
+
+ if (fwnode_property_present(child, "excitation-channels"))
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "excitation-channels is not supported yet\n");
+
+ ret = ads112c04_parse_vref_source(child);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "invalid reference-sources value\n");
+
+ st->vref_source[i] = ret;
+
+ *need_ext_ref |= st->vref_source[i] == ADS112C04_VREF_SOURCE_EXTERNAL;
+ *need_avdd_ref |= st->vref_source[i] == ADS112C04_VREF_SOURCE_AVDD;
+
+ /*
+ * REVISIT: when ti,refp-refn-resistor-ohms is implemented, a
+ * channel using an external resistor reference is effectively
+ * a resistance measurement and should use IIO_RESISTANCE.
+ */
+ spec->type = IIO_VOLTAGE;
+ spec->indexed = 1;
+ spec->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE);
+
+ if (fwnode_property_present(child, s_chan)) {
+ ret = fwnode_property_read_u32(child, s_chan, &channel);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read %s property\n", s_chan);
+
+ if (channel > 3)
+ return dev_err_probe(dev, -EINVAL, "%s must be 0-3\n", s_chan);
+
+ spec->channel = channel;
+ spec->address = ADS112C04_CONF0_MUX_AIN_SINGLE_BASE + channel;
+ } else if (fwnode_property_present(child, d_chan)) {
+ ret = fwnode_property_read_u32_array(child, d_chan, pair, ARRAY_SIZE(pair));
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to read %s property\n", d_chan);
+
+ if (pair[0] > 3 || pair[1] > 3)
+ return dev_err_probe(dev, -EINVAL, "%s must be 0-3\n", d_chan);
+
+ spec->channel = pair[0];
+ spec->channel2 = pair[1];
+ spec->differential = 1;
+
+ if (ads112c04_diff_mux[pair[0]][pair[1]] < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid %s combination\n", d_chan);
+
+ spec->address = ads112c04_diff_mux[pair[0]][pair[1]];
+ } else {
+ return dev_err_probe(dev, -EINVAL,
+ "channel node must have %s or %s\n", s_chan, d_chan);
+ }
+
+ i++;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = i;
+
+ return 0;
+}
+
+static int ads112c04_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct ads112c04_state *st;
+ struct reset_control *reset;
+ bool need_avdd_ref = false, need_ext_ref = false;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->client = client;
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ init_completion(&st->completion);
+
+ indio_dev->name = "ads112c04";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ads112c04_info;
+
+ /* Forward compatibility checks for unimplemented DT properties */
+ if (device_property_present(dev, "refn-supply") ||
+ device_property_present(dev, "ti,refp-refn-resistor-ohms"))
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "refn-supply and external resistors are not supported yet\n");
+
+ ret = ads112c04_parse_channels(indio_dev, &need_avdd_ref, &need_ext_ref);
+ if (ret)
+ return ret;
+
+ if (need_avdd_ref) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "avdd");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get avdd voltage\n");
+
+ st->avdd_mV = ret / (MICRO / MILLI);
+ } else {
+ ret = devm_regulator_get_enable(dev, "avdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get avdd regulator\n");
+ }
+
+ ret = devm_regulator_get_enable(dev, "dvdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get dvdd regulator\n");
+
+ if (device_property_present(dev, "refp-supply")) {
+ ret = devm_regulator_get_enable_read_voltage(dev, "refp");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get refp voltage\n");
+
+ st->ext_ref_mV = ret / (MICRO / MILLI);
+ }
+
+ if (need_ext_ref && !st->ext_ref_mV)
+ return dev_err_probe(dev, -EINVAL,
+ "external reference measurements require refp-supply\n");
+
+ /* Datasheet: POR releases ~500us after supplies are stable */
+ fsleep(500);
+
+ reset = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(reset))
+ return dev_err_probe(dev, PTR_ERR(reset), "failed to get reset\n");
+
+ if (reset) {
+ /* Datasheet: tw(RSL), the RESET low pulse, is 250ns minimum */
+ fsleep(1);
+
+ ret = reset_control_deassert(reset);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to deassert reset\n");
+ } else {
+ ret = ads112c04_write_cmd(client, ADS112C04_CMD_RESET);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Datasheet: td(RSSTA) is 100ns minimum after the RESET rising edge */
+ fsleep(1);
+
+ /*
+ * Initialize CONFIG0 with all fields explicit: gain of 1 with the PGA
+ * bypassed, which allows full-scale single-ended measurements. The MUX
+ * field is updated per channel before each conversion.
+ */
+ st->config0 = FIELD_PREP(ADS112C04_CONF0_MUX,
+ ADS112C04_CONF0_MUX_AIN0_AIN1) |
+ FIELD_PREP(ADS112C04_CONF0_GAIN,
+ ADS112C04_CONF0_GAIN_X1) |
+ ADS112C04_CONF0_PGA_BYPASS;
+
+ ret = ads112c04_write_reg(client, ADS112C04_REG_CONFIG0, st->config0);
+ if (ret)
+ return ret;
+
+ st->config1 = FIELD_PREP(ADS112C04_CONF1_DR,
+ ADS112C04_CONF1_DR_20SPS) |
+ FIELD_PREP(ADS112C04_CONF1_MODE,
+ ADS112C04_CONF1_MODE_NORMAL) |
+ FIELD_PREP(ADS112C04_CONF1_CM,
+ ADS112C04_CONF1_CM_SINGLE_SHOT) |
+ FIELD_PREP(ADS112C04_CONF1_VREF,
+ ADS112C04_CONF1_VREF_INTERNAL) |
+ FIELD_PREP(ADS112C04_CONF1_TS,
+ ADS112C04_CONF1_TS_DISABLED);
+
+ ret = ads112c04_write_reg(client, ADS112C04_REG_CONFIG1, st->config1);
+ if (ret)
+ return ret;
+
+ if (client->irq > 0) {
+ ret = devm_request_irq(dev, client->irq, ads112c04_irq_handler, 0,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct i2c_device_id ads112c04_id[] = {
+ { .name = "ads112c04" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ads112c04_id);
+
+static const struct of_device_id ads112c04_of_match[] = {
+ { .compatible = "ti,ads112c04" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ads112c04_of_match);
+
+static struct i2c_driver ads112c04_driver = {
+ .driver = {
+ .name = "ads112c04",
+ .of_match_table = ads112c04_of_match,
+ },
+ .probe = ads112c04_probe,
+ .id_table = ads112c04_id,
+};
+module_i2c_driver(ads112c04_driver);
+
+MODULE_AUTHOR("Kyle Hsieh <kylehsieh1995@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments ADS112C04 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 31ed2e3e7f52..559880e0cad7 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -88,9 +88,6 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
unsigned int i;
int nents;
- max_size = min(block->size, dmaengine_buffer->max_size);
- max_size = round_down(max_size, dmaengine_buffer->align);
-
if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
dma_dir = DMA_DEV_TO_MEM;
else
diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c
index 8e15baa31423..b47d08ab4d0f 100644
--- a/drivers/iio/chemical/sps30.c
+++ b/drivers/iio/chemical/sps30.c
@@ -15,6 +15,7 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/module.h>
#include "sps30.h"
@@ -193,7 +194,10 @@ static ssize_t start_cleaning_store(struct device *dev,
struct sps30_state *state = iio_priv(indio_dev);
int val, ret;
- if (kstrtoint(buf, 0, &val) || val != 1)
+ ret = kstrtoint(buf, 0, &val);
+ if (ret)
+ return ret;
+ if (val != 1)
return -EINVAL;
guard(mutex)(&state->lock);
@@ -230,8 +234,9 @@ static ssize_t cleaning_period_store(struct device *dev, struct device_attribute
struct sps30_state *state = iio_priv(indio_dev);
int val, ret;
- if (kstrtoint(buf, 0, &val))
- return -EINVAL;
+ ret = kstrtoint(buf, 0, &val);
+ if (ret)
+ return ret;
if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) ||
(val > SPS30_AUTO_CLEANING_PERIOD_MAX))
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index b971f8b646be..551b86236abc 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -311,7 +311,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
static struct platform_driver cros_ec_sensors_platform_driver = {
.driver = {
.name = "cros-ec-sensors",
- .pm = &cros_ec_sensors_pm_ops,
+ .pm = pm_sleep_ptr(&cros_ec_sensors_pm_ops),
},
.probe = cros_ec_sensors_probe,
.id_table = cros_ec_sensors_ids,
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 5133755c2ea6..d03e40c91582 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
@@ -862,7 +862,7 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
-static int __maybe_unused cros_ec_sensors_resume(struct device *dev)
+static int cros_ec_sensors_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
@@ -879,8 +879,7 @@ static int __maybe_unused cros_ec_sensors_resume(struct device *dev)
return ret;
}
-SIMPLE_DEV_PM_OPS(cros_ec_sensors_pm_ops, NULL, cros_ec_sensors_resume);
-EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
+EXPORT_GPL_SIMPLE_DEV_PM_OPS(cros_ec_sensors_pm_ops, NULL, cros_ec_sensors_resume);
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 315317d6eec4..ec45c0f65fe3 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -461,7 +461,7 @@ static const struct clk_ops adf4350_clk_ops = {
static int adf4350_clk_register(struct adf4350_state *st)
{
struct spi_device *spi = st->spi;
- struct clk_init_data init;
+ struct clk_init_data init = { };
struct clk *clk;
const char *parent_name;
int ret;
diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c
index 563de2724396..b61cc81122a6 100644
--- a/drivers/iio/gyro/adxrs290.c
+++ b/drivers/iio/gyro/adxrs290.c
@@ -502,8 +502,8 @@ static irqreturn_t adxrs290_trigger_handler(int irq, void *p)
if (ret < 0)
break;
- iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer,
- pf->timestamp);
+ iio_push_to_buffers_with_ts(indio_dev, &st->buffer,
+ sizeof(st->buffer), pf->timestamp);
} while (0);
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
index d611341a0e2a..b007f46d3fd6 100644
--- a/drivers/iio/gyro/bmg160_core.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -897,8 +897,8 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
if (ret < 0)
goto err;
- iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
- pf->timestamp);
+ iio_push_to_buffers_with_ts(indio_dev, &data->scan,
+ sizeof(data->scan), pf->timestamp);
err:
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
index 87efa2c74ca4..426d04d496b4 100644
--- a/drivers/iio/gyro/itg3200_buffer.c
+++ b/drivers/iio/gyro/itg3200_buffer.c
@@ -59,7 +59,8 @@ static irqreturn_t itg3200_trigger_handler(int irq, void *p)
if (ret < 0)
goto error_ret;
- iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp);
+ iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan),
+ pf->timestamp);
error_ret:
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index 54f11f000b6f..13e267943f67 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -5,13 +5,15 @@
menu "Humidity sensors"
config AM2315
- tristate "Aosong AM2315 relative humidity and temperature sensor"
+ tristate "Aosong AM2315 and similar relative humidity and temperature sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- If you say yes here you get support for the Aosong AM2315
- relative humidity and ambient temperature sensor.
+ If you say yes here you get support for the Aosong relative
+ humidity and ambient temperature sensors:
+ - AM2315
+ - AM2320
This driver can also be built as a module. If so, the module will
be called am2315.
diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c
index f29baa251f9f..5c7582f3a4f9 100644
--- a/drivers/iio/humidity/am2315.c
+++ b/drivers/iio/humidity/am2315.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Aosong AM2315 relative humidity and temperature
+ * Aosong AM2315 and similar relative humidity and temperature
*
* Copyright (c) 2016, Intel Corporation.
*
@@ -27,7 +27,17 @@
#define AM2315_TEMP_OFFSET 4
#define AM2315_ALL_CHANNEL_MASK GENMASK(1, 0)
-#define AM2315_DRIVER_NAME "am2315"
+struct am2315_chip_info {
+ const char *name;
+};
+
+static const struct am2315_chip_info am2315_chip_info = {
+ .name = "am2315",
+};
+
+static const struct am2315_chip_info am2320_chip_info = {
+ .name = "am2320",
+};
struct am2315_data {
struct i2c_client *client;
@@ -220,9 +230,14 @@ static const struct iio_info am2315_info = {
static int am2315_probe(struct i2c_client *client)
{
int ret;
+ const struct am2315_chip_info *chip_info;
struct iio_dev *indio_dev;
struct am2315_data *data;
+ chip_info = i2c_get_match_data(client);
+ if (!chip_info)
+ return -ENODATA;
+
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
@@ -233,7 +248,7 @@ static int am2315_probe(struct i2c_client *client)
mutex_init(&data->lock);
indio_dev->info = &am2315_info;
- indio_dev->name = AM2315_DRIVER_NAME;
+ indio_dev->name = chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = am2315_channels;
indio_dev->num_channels = ARRAY_SIZE(am2315_channels);
@@ -249,8 +264,16 @@ static int am2315_probe(struct i2c_client *client)
return devm_iio_device_register(&client->dev, indio_dev);
}
+static const struct of_device_id am2315_of_match[] = {
+ { .compatible = "aosong,am2315", .data = &am2315_chip_info },
+ { .compatible = "aosong,am2320", .data = &am2320_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, am2315_of_match);
+
static const struct i2c_device_id am2315_i2c_id[] = {
- { .name = "am2315" },
+ { .name = "am2315", .driver_data = (kernel_ulong_t)&am2315_chip_info },
+ { .name = "am2320", .driver_data = (kernel_ulong_t)&am2320_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, am2315_i2c_id);
@@ -258,6 +281,7 @@ MODULE_DEVICE_TABLE(i2c, am2315_i2c_id);
static struct i2c_driver am2315_driver = {
.driver = {
.name = "am2315",
+ .of_match_table = am2315_of_match,
},
.probe = am2315_probe,
.id_table = am2315_i2c_id,
@@ -266,5 +290,5 @@ static struct i2c_driver am2315_driver = {
module_i2c_driver(am2315_driver);
MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
-MODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature");
+MODULE_DESCRIPTION("Aosong AM2315 and similar relative humidity and temperature");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/humidity/ens210.c b/drivers/iio/humidity/ens210.c
index 81276195152b..4bdd37e7227b 100644
--- a/drivers/iio/humidity/ens210.c
+++ b/drivers/iio/humidity/ens210.c
@@ -202,8 +202,8 @@ static int ens210_probe(struct i2c_client *client)
int ret;
if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
- I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_WORD_DATA |
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
return dev_err_probe(&client->dev, -EOPNOTSUPP,
"adapter does not support some i2c transactions\n");
diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
index bfeb0a60d3af..47a10884d812 100644
--- a/drivers/iio/humidity/hts221_core.c
+++ b/drivers/iio/humidity/hts221_core.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
-#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/regmap.h>
@@ -49,7 +48,7 @@ struct hts221_odr {
struct hts221_avg {
u8 addr;
u8 mask;
- u16 avg_avl[HTS221_AVG_DEPTH];
+ int avg_avl[HTS221_AVG_DEPTH];
};
static const struct hts221_odr hts221_odr_table[] = {
@@ -58,6 +57,8 @@ static const struct hts221_odr hts221_odr_table[] = {
{ 13, 0x03 }, /* 12.5Hz */
};
+static const int hts221_odr_avail[] = { 1, 7, 13 };
+
static const struct hts221_avg hts221_avg_list[] = {
{
.addr = 0x10,
@@ -97,7 +98,11 @@ static const struct iio_chan_spec hts221_channels[] = {
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_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),
.scan_index = 0,
.scan_type = {
.sign = 's',
@@ -113,7 +118,11 @@ static const struct iio_chan_spec hts221_channels[] = {
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_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),
.scan_index = 1,
.scan_type = {
.sign = 's',
@@ -127,19 +136,15 @@ static const struct iio_chan_spec hts221_channels[] = {
static int hts221_check_whoami(struct hts221_hw *hw)
{
+ struct device *dev = hw->dev;
int err, data;
err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data);
- if (err < 0) {
- dev_err(hw->dev, "failed to read whoami register\n");
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err, "failed to read whoami register\n");
- if (data != HTS221_REG_WHOAMI_VAL) {
- dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
- data, HTS221_REG_WHOAMI_VAL);
- return -ENODEV;
- }
+ if (data != HTS221_REG_WHOAMI_VAL)
+ dev_info(dev, "unexpected whoami 0x%02x, continuing\n", data);
return 0;
}
@@ -192,53 +197,35 @@ static int hts221_update_avg(struct hts221_hw *hw,
return 0;
}
-static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int i;
- ssize_t len = 0;
-
- for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
- len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
- hts221_odr_table[i].hz);
- buf[len - 1] = '\n';
-
- return len;
-}
-
-static ssize_t
-hts221_sysfs_rh_oversampling_avail(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
- ssize_t len = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
- len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
- avg->avg_avl[i]);
- buf[len - 1] = '\n';
-
- return len;
-}
-
-static ssize_t
-hts221_sysfs_temp_oversampling_avail(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int hts221_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
{
- const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
- ssize_t len = 0;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
- len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
- avg->avg_avl[i]);
- buf[len - 1] = '\n';
-
- return len;
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ *vals = hts221_avg_list[HTS221_SENSOR_H].avg_avl;
+ *length = ARRAY_SIZE(hts221_avg_list[HTS221_SENSOR_H].avg_avl);
+ break;
+ case IIO_TEMP:
+ *vals = hts221_avg_list[HTS221_SENSOR_T].avg_avl;
+ *length = ARRAY_SIZE(hts221_avg_list[HTS221_SENSOR_T].avg_avl);
+ break;
+ default:
+ return -EINVAL;
+ }
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = hts221_odr_avail;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(hts221_odr_avail);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
}
int hts221_set_enable(struct hts221_hw *hw, bool enable)
@@ -258,6 +245,7 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
{
+ struct device *dev = hw->dev;
int err, *slope, *b_gen, cal0, cal1;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
__le16 val;
@@ -288,10 +276,20 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
return err;
cal_x1 = le16_to_cpu(val);
+ if (cal_x1 == cal_x0)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid temperature calibration points (x0 %d, x1 %d)\n",
+ cal_x0, cal_x1);
+
slope = &hw->sensors[HTS221_SENSOR_T].slope;
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+ if (!*slope)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid temperature calibration slope (y0 %d, y1 %d)\n",
+ cal_y0, cal_y1);
+
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
(cal_x1 - cal_x0);
*b_gen *= 8;
@@ -301,6 +299,7 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
{
+ struct device *dev = hw->dev;
int err, *slope, *b_gen, data;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
__le16 val;
@@ -327,10 +326,20 @@ static int hts221_parse_rh_caldata(struct hts221_hw *hw)
return err;
cal_x1 = le16_to_cpu(val);
+ if (cal_x1 == cal_x0)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid rh calibration points (x0 %d, x1 %d)\n",
+ cal_x0, cal_x1);
+
slope = &hw->sensors[HTS221_SENSOR_H].slope;
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+ if (!*slope)
+ return dev_err_probe(dev, -EINVAL,
+ "invalid rh calibration slope (y0 %d, y1 %d)\n",
+ cal_y0, cal_y1);
+
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
(cal_x1 - cal_x0);
*b_gen *= 8;
@@ -521,27 +530,10 @@ static int hts221_validate_trigger(struct iio_dev *iio_dev,
return hw->trig == trig ? 0 : -EINVAL;
}
-static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
- hts221_sysfs_rh_oversampling_avail, NULL, 0);
-static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
- hts221_sysfs_temp_oversampling_avail, NULL, 0);
-static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
-
-static struct attribute *hts221_attributes[] = {
- &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
- &iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
- &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group hts221_attribute_group = {
- .attrs = hts221_attributes,
-};
-
static const struct iio_info hts221_info = {
- .attrs = &hts221_attribute_group,
.read_raw = hts221_read_raw,
.write_raw = hts221_write_raw,
+ .read_avail = hts221_read_avail,
.validate_trigger = hts221_validate_trigger,
};
@@ -608,33 +600,23 @@ int hts221_probe(struct device *dev, int irq, const char *name,
/* configure humidity sensor */
err = hts221_parse_rh_caldata(hw);
- if (err < 0) {
- dev_err(hw->dev, "failed to get rh calibration data\n");
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err, "failed to get rh calibration data\n");
data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3];
err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
- if (err < 0) {
- dev_err(hw->dev, "failed to set rh oversampling ratio\n");
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err, "failed to set rh oversampling ratio\n");
/* configure temperature sensor */
err = hts221_parse_temp_caldata(hw);
- if (err < 0) {
- dev_err(hw->dev,
- "failed to get temperature calibration data\n");
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err, "failed to get temperature calibration data\n");
data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3];
err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
- if (err < 0) {
- dev_err(hw->dev,
- "failed to set temperature oversampling ratio\n");
- return err;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err, "failed to set temperature oversampling ratio\n");
if (hw->irq > 0) {
err = hts221_allocate_buffers(iio_dev);
diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c
index 40276abc5d2e..e6dc9532cfbd 100644
--- a/drivers/iio/humidity/hts221_i2c.c
+++ b/drivers/iio/humidity/hts221_i2c.c
@@ -26,17 +26,14 @@ static const struct regmap_config hts221_i2c_regmap_config = {
static int hts221_i2c_probe(struct i2c_client *client)
{
+ struct device *dev = &client->dev;
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Failed to register i2c regmap %ld\n",
- PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register i2c regmap\n");
- return hts221_probe(&client->dev, client->irq,
- client->name, regmap);
+ return hts221_probe(dev, client->irq, client->name, regmap);
}
static const struct acpi_device_id hts221_acpi_match[] = {
diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
index f962842cc71d..f2c0b1a81a8f 100644
--- a/drivers/iio/humidity/hts221_spi.c
+++ b/drivers/iio/humidity/hts221_spi.c
@@ -27,17 +27,14 @@ static const struct regmap_config hts221_spi_regmap_config = {
static int hts221_spi_probe(struct spi_device *spi)
{
+ struct device *dev = &spi->dev;
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&spi->dev, "Failed to register spi regmap %ld\n",
- PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register spi regmap\n");
- return hts221_probe(&spi->dev, spi->irq,
- spi->modalias, regmap);
+ return hts221_probe(dev, spi->irq, spi->modalias, regmap);
}
static const struct of_device_id hts221_spi_of_match[] = {
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
index b55d993f0264..f6c7c84c7e45 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
@@ -356,8 +356,8 @@ struct inv_icm42600_sensor_state {
cpu_to_le16((_wm) & GENMASK(11, 0))
/* FIFO is 2048 bytes, let 12 samples for reading latency */
#define INV_ICM42600_FIFO_WATERMARK_MAX (2048 - 12 * 16)
-/* INV_ICM42600_FIFO_WATERMARK_MAX / 8 = 232 */
-#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 232
+/* INV_ICM42600_FIFO_WATERMARK_MAX / 16 = 116 */
+#define INV_ICM42600_FIFO_WATERMARK_MAX_SAMPLES 116
#define INV_ICM42600_REG_INT_CONFIG1 0x0064
#define INV_ICM42600_INT_CONFIG1_TPULSE_DURATION BIT(6)
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
index 4b0e3cd8a506..3197081a808a 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
@@ -254,15 +254,23 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
fifo_en |= INV_ICM42600_SENSOR_ACCEL;
}
+ /*
+ * Sleep maximum stabilization time before enabling data in FIFO.
+ * We need to release the driver lock to not block gyro data processing.
+ * There is no possible race here since we are under IIO mutex locked.
+ */
+ sleep = max(sleep_accel, sleep_temp);
+ if (sleep) {
+ mutex_unlock(&st->lock);
+ msleep(sleep);
+ mutex_lock(&st->lock);
+ }
+
/* update data FIFO write */
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
out_unlock:
mutex_unlock(&st->lock);
- /* sleep maximum required time */
- sleep = max(sleep_accel, sleep_temp);
- if (sleep)
- msleep(sleep);
return ret;
}
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
index 998d312f7bde..ded45dfe46a2 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c
@@ -5,6 +5,7 @@
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gcd.h>
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/mutex.h>
@@ -131,13 +132,16 @@ int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st,
INV_ICM42600_FIFO_CONFIG1_GYRO_EN |
INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;
- val = 0;
- if (fifo_en & INV_ICM42600_SENSOR_GYRO)
- val |= INV_ICM42600_FIFO_CONFIG1_GYRO_EN;
- if (fifo_en & INV_ICM42600_SENSOR_ACCEL)
- val |= INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;
- if (fifo_en & INV_ICM42600_SENSOR_TEMP)
- val |= INV_ICM42600_FIFO_CONFIG1_TEMP_EN;
+ /*
+ * Always enable/disable all bits to ensure we can flawlessly add
+ * accel/gyro data in the FIFO while it is running.
+ */
+ if (fifo_en)
+ val = INV_ICM42600_FIFO_CONFIG1_TEMP_EN |
+ INV_ICM42600_FIFO_CONFIG1_GYRO_EN |
+ INV_ICM42600_FIFO_CONFIG1_ACCEL_EN;
+ else
+ val = 0;
ret = regmap_update_bits(st->map, INV_ICM42600_REG_FIFO_CONFIG1, mask, val);
if (ret)
@@ -149,19 +153,6 @@ int inv_icm42600_buffer_set_fifo_en(struct inv_icm42600_state *st,
return 0;
}
-static size_t inv_icm42600_get_packet_size(unsigned int fifo_en)
-{
- size_t packet_size;
-
- if ((fifo_en & INV_ICM42600_SENSOR_GYRO) &&
- (fifo_en & INV_ICM42600_SENSOR_ACCEL))
- packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;
- else
- packet_size = INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE;
-
- return packet_size;
-}
-
static unsigned int inv_icm42600_wm_truncate(unsigned int watermark,
size_t packet_size)
{
@@ -185,15 +176,14 @@ static unsigned int inv_icm42600_wm_truncate(unsigned int watermark,
*
* FIFO watermark threshold is computed based on the required watermark values
* set for gyro and accel sensors. Since watermark is all about acceptable data
- * latency, use the smallest setting between the 2. It means choosing the
- * smallest latency but this is not as simple as choosing the smallest watermark
- * value. Latency depends on watermark and ODR. It requires several steps:
- * 1) compute gyro and accel latencies and choose the smallest value.
- * 2) adapt the chosen latency so that it is a multiple of both gyro and accel
- * ones. Otherwise it is possible that you don't meet a requirement. (for
- * example with gyro @100Hz wm 4 and accel @100Hz with wm 6, choosing the
- * value of 4 will not meet accel latency requirement because 6 is not a
- * multiple of 4. You need to use the value 2.)
+ * latency, we should need to use the smallest latency value. But it is not as
+ * simple as choosing the smallest watermark value. Latency depends on watermark
+ * and ODR and IIO buffer watermark adds another requirement. The required steps:
+ * 1) compute gyro and accel periods and latencies
+ * 2) Use the smallest period and the GCD of the latencies. GCD is required
+ * because of the IIO buffer watermark that will prevent send of data if not
+ * crossed. Thus accel and gyro watermarks must be a multiple of the watermark
+ * value. Computing the GCD gives us the biggest value that meets this criteria.
* 3) Since all periods are multiple of each others, watermark is computed by
* dividing this computed latency by the smallest period, which corresponds
* to the FIFO frequency. Beware that this is only true because we are not
@@ -203,13 +193,13 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)
{
size_t packet_size, wm_size;
unsigned int wm_gyro, wm_accel, watermark;
- u32 period_gyro, period_accel;
+ u32 period_gyro, period_accel, period;
u32 latency_gyro, latency_accel, latency;
bool restore;
__le16 raw_wm;
int ret;
- packet_size = inv_icm42600_get_packet_size(st->fifo.en);
+ packet_size = INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;
/* compute sensors latency, depending on sensor watermark and odr */
wm_gyro = inv_icm42600_wm_truncate(st->fifo.watermark.gyro, packet_size);
@@ -231,22 +221,17 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st)
watermark = wm_gyro;
st->fifo.watermark.eff_gyro = wm_gyro;
} else {
- /* compute the smallest latency that is a multiple of both */
- if (latency_gyro <= latency_accel)
- latency = latency_gyro - (latency_accel % latency_gyro);
- else
- latency = latency_accel - (latency_gyro % latency_accel);
- /* all this works because periods are multiple of each others */
- watermark = latency / min(period_gyro, period_accel);
- if (watermark < 1)
- watermark = 1;
- /* update effective watermark */
- st->fifo.watermark.eff_gyro = latency / period_gyro;
- if (st->fifo.watermark.eff_gyro < 1)
- st->fifo.watermark.eff_gyro = 1;
- st->fifo.watermark.eff_accel = latency / period_accel;
- if (st->fifo.watermark.eff_accel < 1)
- st->fifo.watermark.eff_accel = 1;
+ /*
+ * In case of both accel and gyro enabled, we need to use the
+ * shortest period and the gcd of the latencies. Gcd is required
+ * because of the IIO buffer watermark that will prevent data
+ * sending if we are not crossing the watermark level.
+ */
+ period = min(period_gyro, period_accel);
+ latency = gcd(latency_gyro, latency_accel);
+ watermark = max(latency / period, 1);
+ st->fifo.watermark.eff_gyro = max(latency / period_gyro, 1);
+ st->fifo.watermark.eff_accel = max(latency / period_accel, 1);
}
/* compute watermark value in bytes */
@@ -492,24 +477,21 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st,
st->fifo.nb.accel = 0;
st->fifo.nb.total = 0;
- /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */
- if (max == 0)
- max = st->fifo.watermark.value;
- max_count = max * inv_icm42600_get_packet_size(st->fifo.en);
-
- /* read FIFO count value */
- raw_fifo_count = (__be16 *)st->buffer;
- ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT,
- raw_fifo_count, sizeof(*raw_fifo_count));
- if (ret)
- return ret;
- st->fifo.count = be16_to_cpup(raw_fifo_count);
-
- /* check and clamp FIFO count value */
- if (st->fifo.count == 0)
- return 0;
- if (st->fifo.count > max_count)
- st->fifo.count = max_count;
+ /* read watermark samples for interrupt case (max = 0) or read FIFO count */
+ if (max == 0) {
+ st->fifo.count = st->fifo.watermark.value *
+ INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;
+ } else {
+ raw_fifo_count = (__be16 *)st->buffer;
+ ret = regmap_bulk_read(st->map, INV_ICM42600_REG_FIFO_COUNT,
+ raw_fifo_count, sizeof(*raw_fifo_count));
+ if (ret)
+ return ret;
+ max_count = max * INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE;
+ st->fifo.count = min(be16_to_cpup(raw_fifo_count), max_count);
+ if (st->fifo.count == 0)
+ return 0;
+ }
/* read all FIFO data in internal buffer */
ret = regmap_noinc_read(st->map, INV_ICM42600_REG_FIFO_DATA,
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
index dc97d8a274e3..be8cbecdd92e 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
@@ -8,6 +8,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h>
+#include <linux/limits.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
@@ -509,10 +510,20 @@ static int inv_icm42600_setup(struct inv_icm42600_state *st,
ret = regmap_read(st->map, INV_ICM42600_REG_WHOAMI, &val);
if (ret)
return ret;
+
+ /*
+ * SPI interface has no ack mechanism.
+ * 0xFF or 0x00 whoami means no response from the device.
+ */
+ if (val == U8_MAX || val == 0)
+ return dev_err_probe(dev, -ENODEV,
+ "invalid whoami %#04x expected %#04x (%s)\n",
+ val, hw->whoami, hw->name);
+
if (val != hw->whoami) {
- dev_err(dev, "invalid whoami %#02x expected %#02x (%s)\n",
- val, hw->whoami, hw->name);
- return -ENODEV;
+ dev_info(dev,
+ "device id %#04x is not the %#04x associated with the FW-specified device (%s), probably using a valid fallback compatible\n",
+ val, hw->whoami, hw->name);
}
st->name = hw->name;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
index 253bf571439d..9de5c383665f 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
@@ -126,15 +126,23 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
fifo_en |= INV_ICM42600_SENSOR_GYRO;
}
+ /*
+ * Sleep maximum stabilization time before enabling data in FIFO.
+ * We need to release the driver lock to not block accel data processing.
+ * There is no possible race here since we are under IIO mutex locked.
+ */
+ sleep = max(sleep_gyro, sleep_temp);
+ if (sleep) {
+ mutex_unlock(&st->lock);
+ msleep(sleep);
+ mutex_lock(&st->lock);
+ }
+
/* update data FIFO write */
ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en);
out_unlock:
mutex_unlock(&st->lock);
- /* sleep maximum required time */
- sleep = max(sleep_gyro, sleep_temp);
- if (sleep)
- msleep(sleep);
return ret;
}
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
index 28552d2db91d..e44ed6dcde02 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
@@ -77,10 +77,10 @@ static const struct i2c_device_id inv_icm42600_id[] = {
{ .name = "icm42600", .driver_data = INV_CHIP_ICM42600 },
{ .name = "icm42602", .driver_data = INV_CHIP_ICM42602 },
{ .name = "icm42605", .driver_data = INV_CHIP_ICM42605 },
- { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 },
{ .name = "icm42622", .driver_data = INV_CHIP_ICM42622 },
- { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 },
{ .name = "icm42631", .driver_data = INV_CHIP_ICM42631 },
+ { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 },
+ { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 },
{ }
};
MODULE_DEVICE_TABLE(i2c, inv_icm42600_id);
@@ -96,17 +96,17 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
.compatible = "invensense,icm42605",
.data = (void *)INV_CHIP_ICM42605,
}, {
- .compatible = "invensense,icm42686",
- .data = (void *)INV_CHIP_ICM42686,
- }, {
.compatible = "invensense,icm42622",
.data = (void *)INV_CHIP_ICM42622,
}, {
- .compatible = "invensense,icm42688",
- .data = (void *)INV_CHIP_ICM42688,
- }, {
.compatible = "invensense,icm42631",
.data = (void *)INV_CHIP_ICM42631,
+ }, {
+ .compatible = "invensense,icm42686",
+ .data = (void *)INV_CHIP_ICM42686,
+ }, {
+ .compatible = "invensense,icm42688",
+ .data = (void *)INV_CHIP_ICM42688,
},
{ }
};
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
index faf743bc6444..3e2f4eb42e97 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
@@ -74,10 +74,10 @@ static const struct spi_device_id inv_icm42600_id[] = {
{ .name = "icm42600", .driver_data = INV_CHIP_ICM42600 },
{ .name = "icm42602", .driver_data = INV_CHIP_ICM42602 },
{ .name = "icm42605", .driver_data = INV_CHIP_ICM42605 },
- { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 },
{ .name = "icm42622", .driver_data = INV_CHIP_ICM42622 },
- { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 },
{ .name = "icm42631", .driver_data = INV_CHIP_ICM42631 },
+ { .name = "icm42686", .driver_data = INV_CHIP_ICM42686 },
+ { .name = "icm42688", .driver_data = INV_CHIP_ICM42688 },
{ }
};
MODULE_DEVICE_TABLE(spi, inv_icm42600_id);
@@ -93,17 +93,17 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
.compatible = "invensense,icm42605",
.data = (void *)INV_CHIP_ICM42605,
}, {
- .compatible = "invensense,icm42686",
- .data = (void *)INV_CHIP_ICM42686,
- }, {
.compatible = "invensense,icm42622",
.data = (void *)INV_CHIP_ICM42622,
}, {
- .compatible = "invensense,icm42688",
- .data = (void *)INV_CHIP_ICM42688,
- }, {
.compatible = "invensense,icm42631",
.data = (void *)INV_CHIP_ICM42631,
+ }, {
+ .compatible = "invensense,icm42686",
+ .data = (void *)INV_CHIP_ICM42686,
+ }, {
+ .compatible = "invensense,icm42688",
+ .data = (void *)INV_CHIP_ICM42688,
},
{ }
};
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index 5796896d54cd..38c3f3b398d4 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -13,6 +13,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/acpi.h>
+#include <linux/kstrtox.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/math64.h>
@@ -1303,8 +1304,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
struct inv_mpu6050_state *st = iio_priv(indio_dev);
struct device *pdev = regmap_get_device(st->map);
- if (kstrtoint(buf, 10, &fifo_rate))
- return -EINVAL;
+ result = kstrtoint(buf, 10, &fifo_rate);
+ if (result)
+ return result;
if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE ||
fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
return -EINVAL;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 767a7794624a..819a864d1136 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -341,7 +341,8 @@ s64 iio_get_time_ns(const struct iio_dev *indio_dev)
case CLOCK_TAI:
return ktime_get_clocktai_ns();
default:
- BUG();
+ WARN_ON_ONCE(1);
+ return 0;
}
}
EXPORT_SYMBOL(iio_get_time_ns);
@@ -1518,7 +1519,8 @@ static ssize_t current_timestamp_clock_show(struct device *dev,
case CLOCK_TAI:
break;
default:
- BUG();
+ WARN_ON_ONCE(1);
+ return -EINVAL;
}
return sysfs_emit(buf, "%s\n", clock_names[clk]);
diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c
index 4f52dc373abf..af6ed37fb34a 100644
--- a/drivers/iio/industrialio-gts-helper.c
+++ b/drivers/iio/industrialio-gts-helper.c
@@ -5,7 +5,7 @@
*/
#include <linux/device.h>
-#include <linux/errno.h>
+#include <linux/err.h>
#include <linux/export.h>
#include <linux/minmax.h>
#include <linux/module.h>
diff --git a/drivers/iio/light/apds9999.c b/drivers/iio/light/apds9999.c
index 43fa9992c9c2..62620c4a2fbc 100644
--- a/drivers/iio/light/apds9999.c
+++ b/drivers/iio/light/apds9999.c
@@ -100,10 +100,6 @@ static int apds9999_init(struct apds9999_data *data)
u8 regval;
int ret;
- ret = devm_add_action_or_reset(dev, apds9999_standby, client);
- if (ret)
- return ret;
-
guard(mutex)(&data->lock);
regval = FIELD_PREP(APDS9999_LS_RES_MASK, APDS9999_RES_18BIT) |
@@ -121,8 +117,12 @@ static int apds9999_init(struct apds9999_data *data)
return ret;
data->als_gain_idx = APDS9999_GAIN_3X;
- return i2c_smbus_write_byte_data(client, APDS9999_REG_MAIN_CTRL,
- APDS9999_MAIN_CTRL_LS_EN);
+ ret = i2c_smbus_write_byte_data(client, APDS9999_REG_MAIN_CTRL,
+ APDS9999_MAIN_CTRL_LS_EN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, apds9999_standby, client);
}
static int apds9999_read_channel(struct apds9999_data *data, u8 reg,
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index 7ab565b1fb9f..b1c92de7922a 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -249,7 +249,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
static struct platform_driver cros_ec_light_prox_platform_driver = {
.driver = {
.name = "cros-ec-light-prox",
- .pm = &cros_ec_sensors_pm_ops,
+ .pm = pm_sleep_ptr(&cros_ec_sensors_pm_ops),
},
.probe = cros_ec_light_prox_probe,
.id_table = cros_ec_light_prox_ids,
diff --git a/drivers/iio/light/iqs621-als.c b/drivers/iio/light/iqs621-als.c
index cd5843e3e2c3..f9d215ef1970 100644
--- a/drivers/iio/light/iqs621-als.c
+++ b/drivers/iio/light/iqs621-als.c
@@ -179,19 +179,6 @@ static int iqs621_als_notifier(struct notifier_block *notifier,
return NOTIFY_OK;
}
-static void iqs621_als_notifier_unregister(void *context)
-{
- struct iqs621_als_private *iqs621_als = context;
- struct iio_dev *indio_dev = iqs621_als->indio_dev;
- int ret;
-
- ret = blocking_notifier_chain_unregister(&iqs621_als->iqs62x->nh,
- &iqs621_als->notifier);
- if (ret)
- dev_err(indio_dev->dev.parent,
- "Failed to unregister notifier: %d\n", ret);
-}
-
static int iqs621_als_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -563,19 +550,14 @@ static int iqs621_als_probe(struct platform_device *pdev)
mutex_init(&iqs621_als->lock);
iqs621_als->notifier.notifier_call = iqs621_als_notifier;
- ret = blocking_notifier_chain_register(&iqs621_als->iqs62x->nh,
- &iqs621_als->notifier);
+ ret = devm_blocking_notifier_chain_register(&pdev->dev,
+ &iqs621_als->iqs62x->nh,
+ &iqs621_als->notifier);
if (ret) {
dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
return ret;
}
- ret = devm_add_action_or_reset(&pdev->dev,
- iqs621_als_notifier_unregister,
- iqs621_als);
- if (ret)
- return ret;
-
return devm_iio_device_register(&pdev->dev, indio_dev);
}
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index 759cb71ed1c5..7f7a563df4d9 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/err.h>
+#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/delay.h>
@@ -339,9 +340,11 @@ static ssize_t proximity_on_chip_ambient_infrared_suppression_store
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
int val;
+ int ret;
- if (kstrtoint(buf, 10, &val))
- return -EINVAL;
+ ret = kstrtoint(buf, 10, &val);
+ if (ret)
+ return ret;
if (!(val == 0 || val == 1))
return -EINVAL;
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index 99f0b903018c..5db9591a85b9 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
+#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
@@ -434,8 +435,9 @@ static ssize_t store_thresh_either_en(struct device *dev,
if (!als->irq)
return -EBUSY;
- if (kstrtoul(buf, 0, &enable))
- return -EINVAL;
+ ret = kstrtoul(buf, 0, &enable);
+ if (ret)
+ return ret;
int_enabled = test_bit(LM3533_ALS_FLAG_INT_ENABLED, &als->flags);
@@ -542,8 +544,9 @@ static ssize_t store_als_attr(struct device *dev,
u8 val;
int ret;
- if (kstrtou8(buf, 0, &val))
- return -EINVAL;
+ ret = kstrtou8(buf, 0, &val);
+ if (ret)
+ return ret;
switch (als_attr->type) {
case LM3533_ATTR_TYPE_TARGET:
diff --git a/drivers/iio/light/ltr390.c b/drivers/iio/light/ltr390.c
index bc031f2c3141..f1bb066248e6 100644
--- a/drivers/iio/light/ltr390.c
+++ b/drivers/iio/light/ltr390.c
@@ -10,12 +10,6 @@
*
* Datasheet:
* https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
- *
- * TODO:
- * - Support for configurable gain and resolution
- * - Sensor suspend/resume support
- * - Add support for reading the ALS
- * - Interrupt support
*/
#include <linux/bitfield.h>
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index fa0d16890c8b..d9b68da2452b 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -9,6 +9,7 @@
* TODO: IR LED characteristics
*/
+#include <linux/array_size.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
@@ -90,10 +91,11 @@ struct ltr501_samp_table {
#define LTR501_RESERVED_GAIN -1
enum {
- ltr501 = 0,
- ltr559,
ltr301,
ltr303,
+ ltr329,
+ ltr501,
+ ltr559,
};
struct ltr501_gain {
@@ -140,6 +142,7 @@ struct ltr501_chip_info {
u8 als_mode_active;
u8 als_gain_mask;
u8 als_gain_shift;
+ bool no_irq_support;
struct iio_chan_spec const *channels;
const int no_channels;
const struct iio_info *info;
@@ -178,6 +181,11 @@ static const struct ltr501_samp_table ltr501_ps_samp_table[] = {
{500000, 2000000}
};
+static bool ltr501_has_irq_support(const struct ltr501_chip_info *info)
+{
+ return !info->no_irq_support;
+}
+
static int ltr501_match_samp_freq(const struct ltr501_samp_table *tab,
int len, int val, int val2)
{
@@ -821,6 +829,9 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
+ if (!ltr501_has_irq_support(info))
+ return ret;
+
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->als_period);
@@ -840,6 +851,9 @@ static int __ltr501_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
+ if (!ltr501_has_irq_support(info))
+ return ret;
+
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->ps_period);
@@ -1205,6 +1219,42 @@ static const struct iio_info ltr301_info = {
};
static const struct ltr501_chip_info ltr501_chip_info_tbl[] = {
+ [ltr301] = {
+ .partid = 0x08,
+ .als_gain = ltr501_als_gain_tbl,
+ .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl),
+ .als_mode_active = BIT(0) | BIT(1),
+ .als_gain_mask = BIT(3),
+ .als_gain_shift = 3,
+ .info = &ltr301_info,
+ .info_no_irq = &ltr301_info_no_irq,
+ .channels = ltr301_channels,
+ .no_channels = ARRAY_SIZE(ltr301_channels),
+ },
+ [ltr303] = {
+ .partid = 0x0A,
+ .als_gain = ltr559_als_gain_tbl,
+ .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl),
+ .als_mode_active = BIT(0),
+ .als_gain_mask = BIT(2) | BIT(3) | BIT(4),
+ .als_gain_shift = 2,
+ .info = &ltr301_info,
+ .info_no_irq = &ltr301_info_no_irq,
+ .channels = ltr301_channels,
+ .no_channels = ARRAY_SIZE(ltr301_channels),
+ },
+ [ltr329] = {
+ .partid = 0x0A,
+ .als_gain = ltr559_als_gain_tbl,
+ .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl),
+ .als_mode_active = BIT(0),
+ .als_gain_mask = BIT(2) | BIT(3) | BIT(4),
+ .als_gain_shift = 2,
+ .no_irq_support = true,
+ .info_no_irq = &ltr301_info_no_irq,
+ .channels = ltr301_channels,
+ .no_channels = ARRAY_SIZE(ltr301_channels),
+ },
[ltr501] = {
.partid = 0x08,
.als_gain = ltr501_als_gain_tbl,
@@ -1233,30 +1283,6 @@ static const struct ltr501_chip_info ltr501_chip_info_tbl[] = {
.channels = ltr501_channels,
.no_channels = ARRAY_SIZE(ltr501_channels),
},
- [ltr301] = {
- .partid = 0x08,
- .als_gain = ltr501_als_gain_tbl,
- .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl),
- .als_mode_active = BIT(0) | BIT(1),
- .als_gain_mask = BIT(3),
- .als_gain_shift = 3,
- .info = &ltr301_info,
- .info_no_irq = &ltr301_info_no_irq,
- .channels = ltr301_channels,
- .no_channels = ARRAY_SIZE(ltr301_channels),
- },
- [ltr303] = {
- .partid = 0x0A,
- .als_gain = ltr559_als_gain_tbl,
- .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl),
- .als_mode_active = BIT(0),
- .als_gain_mask = BIT(2) | BIT(3) | BIT(4),
- .als_gain_shift = 2,
- .info = &ltr301_info,
- .info_no_irq = &ltr301_info_no_irq,
- .channels = ltr301_channels,
- .no_channels = ARRAY_SIZE(ltr301_channels),
- },
};
static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val)
@@ -1369,13 +1395,15 @@ static int ltr501_init(struct ltr501_data *data)
data->ps_contr = status | LTR501_CONTR_ACTIVE;
- ret = ltr501_read_intr_prst(data, IIO_INTENSITY, &data->als_period);
- if (ret < 0)
- return ret;
+ if (ltr501_has_irq_support(data->chip_info)) {
+ ret = ltr501_read_intr_prst(data, IIO_INTENSITY, &data->als_period);
+ if (ret < 0)
+ return ret;
- ret = ltr501_read_intr_prst(data, IIO_PROXIMITY, &data->ps_period);
- if (ret < 0)
- return ret;
+ ret = ltr501_read_intr_prst(data, IIO_PROXIMITY, &data->ps_period);
+ if (ret < 0)
+ return ret;
+ }
return ltr501_write_contr(data, data->als_contr, data->ps_contr);
}
@@ -1530,6 +1558,11 @@ static int ltr501_probe(struct i2c_client *client)
if (ret < 0)
return ret;
+ if (!ltr501_has_irq_support(data->chip_info) && client->irq > 0) {
+ client->irq = 0;
+ dev_warn(&client->dev, "chip doesn't support IRQ");
+ }
+
if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, ltr501_interrupt_handler,
@@ -1538,7 +1571,7 @@ static int ltr501_probe(struct i2c_client *client)
"ltr501_thresh_event",
indio_dev);
if (ret)
- return ret;
+ goto powerdown_on_error;
} else {
indio_dev->info = data->chip_info->info_no_irq;
}
@@ -1597,19 +1630,21 @@ static const struct acpi_device_id ltr_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, ltr_acpi_match);
static const struct i2c_device_id ltr501_id[] = {
- { .name = "ltr501", .driver_data = ltr501 },
- { .name = "ltr559", .driver_data = ltr559 },
{ .name = "ltr301", .driver_data = ltr301 },
{ .name = "ltr303", .driver_data = ltr303 },
+ { .name = "ltr329", .driver_data = ltr329 },
+ { .name = "ltr501", .driver_data = ltr501 },
+ { .name = "ltr559", .driver_data = ltr559 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltr501_id);
static const struct of_device_id ltr501_of_match[] = {
- { .compatible = "liteon,ltr501", },
- { .compatible = "liteon,ltr559", },
{ .compatible = "liteon,ltr301", },
{ .compatible = "liteon,ltr303", },
+ { .compatible = "liteon,ltr329", },
+ { .compatible = "liteon,ltr501", },
+ { .compatible = "liteon,ltr559", },
{ }
};
MODULE_DEVICE_TABLE(of, ltr501_of_match);
diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c
index 53fd423aa7ae..2d041b7530e6 100644
--- a/drivers/iio/light/tsl2583.c
+++ b/drivers/iio/light/tsl2583.c
@@ -7,10 +7,11 @@
* Copyright (c) 2016-2017 Brian Masney <masneyb@onstation.org>
*/
-#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/errno.h>
#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/unistd.h>
@@ -483,9 +484,12 @@ static ssize_t in_illuminance_input_target_store(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
- int value;
+ int value, ret;
- if (kstrtoint(buf, 0, &value) || !value)
+ ret = kstrtoint(buf, 0, &value);
+ if (ret)
+ return ret;
+ if (!value)
return -EINVAL;
mutex_lock(&chip->als_mutex);
@@ -503,7 +507,10 @@ static ssize_t in_illuminance_calibrate_store(struct device *dev,
struct tsl2583_chip *chip = iio_priv(indio_dev);
int value, ret;
- if (kstrtoint(buf, 0, &value) || value != 1)
+ ret = kstrtoint(buf, 0, &value);
+ if (ret)
+ return ret;
+ if (value != 1)
return -EINVAL;
mutex_lock(&chip->als_mutex);
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index 4486a1d9d84d..c8a7afc65c1f 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -13,6 +13,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
@@ -951,8 +952,9 @@ static ssize_t in_illuminance0_target_input_store(struct device *dev,
u16 value;
int ret;
- if (kstrtou16(buf, 0, &value))
- return -EINVAL;
+ ret = kstrtou16(buf, 0, &value);
+ if (ret)
+ return ret;
chip->settings.als_cal_target = value;
ret = tsl2772_invoke_change(indio_dev);
@@ -970,7 +972,10 @@ static ssize_t in_illuminance0_calibrate_store(struct device *dev,
bool value;
int ret;
- if (kstrtobool(buf, &value) || !value)
+ ret = kstrtobool(buf, &value);
+ if (ret)
+ return ret;
+ if (!value)
return -EINVAL;
ret = tsl2772_als_calibrate(indio_dev);
@@ -1061,7 +1066,10 @@ static ssize_t in_proximity0_calibrate_store(struct device *dev,
bool value;
int ret;
- if (kstrtobool(buf, &value) || !value)
+ ret = kstrtobool(buf, &value);
+ if (ret)
+ return ret;
+ if (!value)
return -EINVAL;
ret = tsl2772_prox_cal(indio_dev);
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 336468d59ea7..911f8d1c74d4 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
@@ -1504,7 +1505,7 @@ static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
{
struct vcnl4000_data *data = iio_priv(indio_dev);
- return sprintf(buf, "%u\n", data->near_level);
+ return sysfs_emit(buf, "%u\n", data->near_level);
}
static irqreturn_t vcnl4010_irq_thread(int irq, void *p)
diff --git a/drivers/iio/light/veml3328.c b/drivers/iio/light/veml3328.c
index 7ff1753925c4..df36c48204be 100644
--- a/drivers/iio/light/veml3328.c
+++ b/drivers/iio/light/veml3328.c
@@ -32,6 +32,11 @@
#define VEML3328_REG_DATA_B 0x07
#define VEML3328_REG_DATA_IR 0x08
+#define VEML3328_CONT_IT_50MSECS 0
+#define VEML3328_CONT_IT_100MSECS 1
+#define VEML3328_CONT_IT_200MSECS 2
+#define VEML3328_CONT_IT_400MSECS 3
+
#define VEML3328_CONF_IT_MASK GENMASK(5, 4)
#define VEML3328_CONF_GAIN_MASK GENMASK(11, 10)
@@ -89,21 +94,40 @@ static const struct iio_chan_spec veml3328_channels[] = {
* Precomputed scale values (micro units).
* Formula for calculation: 0.384 * (50000 / IT_us) * (1 / Gain)
* Gain indexes: 0 (x0.5), 1 (x1), 2 (x2), 3 (x4)
- * IT indexes: 0 (50ms), 1 (100ms), 2 (200ms), 3 (400ms)
*/
-static const int veml3328_scale_vals[4][8] = {
- { 0, 768000, 0, 384000, 0, 192000, 0, 96000 },
- { 0, 384000, 0, 192000, 0, 96000, 0, 48000 },
- { 0, 192000, 0, 96000, 0, 48000, 0, 24000 },
- { 0, 96000, 0, 48000, 0, 24000, 0, 12000 },
+static const int veml3328_scale_vals[4][4][2] = {
+ [VEML3328_CONT_IT_50MSECS] = {
+ { 0, 768000 },
+ { 0, 384000 },
+ { 0, 192000 },
+ { 0, 96000 },
+ },
+ [VEML3328_CONT_IT_100MSECS] = {
+ { 0, 384000 },
+ { 0, 192000 },
+ { 0, 96000 },
+ { 0, 48000 },
+ },
+ [VEML3328_CONT_IT_200MSECS] = {
+ { 0, 192000 },
+ { 0, 96000 },
+ { 0, 48000 },
+ { 0, 24000 },
+ },
+ [VEML3328_CONT_IT_400MSECS] = {
+ { 0, 96000 },
+ { 0, 48000 },
+ { 0, 24000 },
+ { 0, 12000 },
+ },
};
/* integration times in microseconds */
static const int veml3328_it_times[][2] = {
- { 0, 50 * USEC_PER_MSEC },
- { 0, 100 * USEC_PER_MSEC },
- { 0, 200 * USEC_PER_MSEC },
- { 0, 400 * USEC_PER_MSEC },
+ [VEML3328_CONT_IT_50MSECS] = { 0, 50 * USEC_PER_MSEC },
+ [VEML3328_CONT_IT_100MSECS] = { 0, 100 * USEC_PER_MSEC },
+ [VEML3328_CONT_IT_200MSECS] = { 0, 200 * USEC_PER_MSEC },
+ [VEML3328_CONT_IT_400MSECS] = { 0, 400 * USEC_PER_MSEC },
};
static int veml3328_power_down(struct veml3328_data *data)
@@ -184,9 +208,8 @@ static int veml3328_read_raw(struct iio_dev *indio_dev,
if (it_inx >= ARRAY_SIZE(veml3328_it_times) || gain_inx >= 4)
return -EINVAL;
- /* Stride by 2 through the flattened array to match (val, val2) */
- *val = veml3328_scale_vals[it_inx][gain_inx * 2];
- *val2 = veml3328_scale_vals[it_inx][gain_inx * 2 + 1];
+ *val = veml3328_scale_vals[it_inx][gain_inx][0];
+ *val2 = veml3328_scale_vals[it_inx][gain_inx][1];
return IIO_VAL_INT_PLUS_MICRO;
@@ -282,8 +305,8 @@ static int veml3328_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
for (i = 0; i < 4; i++) {
- if (val == veml3328_scale_vals[it_inx][i * 2] &&
- val2 == veml3328_scale_vals[it_inx][i * 2 + 1])
+ if (val == veml3328_scale_vals[it_inx][i][0] &&
+ val2 == veml3328_scale_vals[it_inx][i][1])
break;
}
diff --git a/drivers/iio/position/iqs624-pos.c b/drivers/iio/position/iqs624-pos.c
index 8239239c6ee2..d2aa2df71457 100644
--- a/drivers/iio/position/iqs624-pos.c
+++ b/drivers/iio/position/iqs624-pos.c
@@ -96,19 +96,6 @@ static int iqs624_pos_notifier(struct notifier_block *notifier,
return ret;
}
-static void iqs624_pos_notifier_unregister(void *context)
-{
- struct iqs624_pos_private *iqs624_pos = context;
- struct iio_dev *indio_dev = iqs624_pos->indio_dev;
- int ret;
-
- ret = blocking_notifier_chain_unregister(&iqs624_pos->iqs62x->nh,
- &iqs624_pos->notifier);
- if (ret)
- dev_err(indio_dev->dev.parent,
- "Failed to unregister notifier: %d\n", ret);
-}
-
static int iqs624_pos_angle_get(struct iqs62x_core *iqs62x, unsigned int *val)
{
int ret;
@@ -255,19 +242,14 @@ static int iqs624_pos_probe(struct platform_device *pdev)
mutex_init(&iqs624_pos->lock);
iqs624_pos->notifier.notifier_call = iqs624_pos_notifier;
- ret = blocking_notifier_chain_register(&iqs624_pos->iqs62x->nh,
- &iqs624_pos->notifier);
+ ret = devm_blocking_notifier_chain_register(&pdev->dev,
+ &iqs624_pos->iqs62x->nh,
+ &iqs624_pos->notifier);
if (ret) {
dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
return ret;
}
- ret = devm_add_action_or_reset(&pdev->dev,
- iqs624_pos_notifier_unregister,
- iqs624_pos);
- if (ret)
- return ret;
-
return devm_iio_device_register(&pdev->dev, indio_dev);
}
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
index 6a567b6075d9..e4bc4df48d01 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -192,7 +192,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids);
static struct platform_driver cros_ec_baro_platform_driver = {
.driver = {
.name = "cros-ec-baro",
- .pm = &cros_ec_sensors_pm_ops,
+ .pm = pm_sleep_ptr(&cros_ec_sensors_pm_ops),
},
.probe = cros_ec_baro_probe,
.id_table = cros_ec_baro_ids,
diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c
index f9deb48e6a41..d1268c289831 100644
--- a/drivers/iio/pressure/rohm-bm1390.c
+++ b/drivers/iio/pressure/rohm-bm1390.c
@@ -479,6 +479,7 @@ static const struct iio_info bm1390_info = {
static int bm1390_chip_init(struct bm1390_data *data)
{
+ u8 regval;
int ret;
ret = regmap_write_bits(data->regmap, BM1390_REG_POWER,
@@ -512,8 +513,9 @@ static int bm1390_chip_init(struct bm1390_data *data)
* Default to use IIR filter in "middle" mode. Also the AVE_NUM must
* be fixed when IIR is in use.
*/
+ regval = FIELD_PREP(BM1390_MASK_AVE_NUM, BM1390_IIR_AVE_NUM);
ret = regmap_update_bits(data->regmap, BM1390_REG_MODE_CTRL,
- BM1390_MASK_AVE_NUM, BM1390_IIR_AVE_NUM);
+ BM1390_MASK_AVE_NUM, regval);
if (ret)
return ret;
diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c
index 8352d51e5128..e3ab55170b6c 100644
--- a/drivers/iio/proximity/aw96103.c
+++ b/drivers/iio/proximity/aw96103.c
@@ -687,7 +687,7 @@ static irqreturn_t aw96103_irq(int irq, void *data)
iio_get_time_ns(indio_dev));
break;
default:
- return IRQ_HANDLED;
+ continue;
}
aw96103->channels_arr[i].old_irq_status = curr_status;
}
diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c
index 74cb8d62bef3..3d8c97aa0d99 100644
--- a/drivers/iio/temperature/tmp117.c
+++ b/drivers/iio/temperature/tmp117.c
@@ -97,15 +97,20 @@ static int tmp117_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec
{
struct tmp117_data *data = iio_priv(indio_dev);
s16 off;
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
off = clamp_t(int, val, S16_MIN, S16_MAX);
if (off == data->calibbias)
return 0;
+
+ ret = i2c_smbus_write_word_swapped(data->client, TMP117_REG_TEMP_OFFSET, off);
+ if (ret)
+ return ret;
+
data->calibbias = off;
- return i2c_smbus_write_word_swapped(data->client,
- TMP117_REG_TEMP_OFFSET, off);
+ return 0;
default:
return -EINVAL;
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a
index 863d3856718a..d19db00acd1d 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a
@@ -4,7 +4,7 @@ Contact: linux-iio@vger.kernel.org
Description:
Writing 1 enables the cell balance output switch corresponding
to input Y. Writing 0 disables it. If the inY-inZ_balance_timer
- is set to a none zero value, the corresponding switch will
+ is set to a non-zero value, the corresponding switch will
enable for the programmed amount of time, before it
automatically disables.
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index a78249812b26..35f823f24f9b 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -118,6 +118,12 @@ static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout)
{
u64 freqreg = (u64)fout << AD9832_FREQ_BITS;
+ /*
+ * mclk is an unsigned long, which triggers a Coccinelle false positive
+ * warning about using do_div() for 64-by-32 division. However, mclk
+ * for this hardware will always fit within 32 bits, so do_div() is
+ * safe to use here.
+ */
do_div(freqreg, mclk);
return freqreg;
}
diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c
index 0d20f796e0f0..4cebb9d0d924 100644
--- a/drivers/staging/iio/frequency/ad9834.c
+++ b/drivers/staging/iio/frequency/ad9834.c
@@ -103,6 +103,12 @@ static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout)
{
unsigned long long freqreg = (u64)fout * (u64)BIT(AD9834_FREQ_BITS);
+ /*
+ * mclk is an unsigned long, which triggers a Coccinelle false positive
+ * warning about using a do_div() for 64-by-32 division. However, mclk
+ * for this hardware will always fit within 32 bits, so do_div() is
+ * safe to use here.
+ */
do_div(freqreg, mclk);
return freqreg;
}
diff --git a/include/linux/iio/iio-gts-helper.h b/include/linux/iio/iio-gts-helper.h
index 66f830ab9b49..f63a482b82b8 100644
--- a/include/linux/iio/iio-gts-helper.h
+++ b/include/linux/iio/iio-gts-helper.h
@@ -7,6 +7,7 @@
#ifndef __IIO_GTS_HELPER__
#define __IIO_GTS_HELPER__
+#include <linux/errno.h>
#include <linux/types.h>
struct device;
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 711c00f67371..989c1db8fb36 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -789,7 +789,7 @@ DEFINE_GUARD_COND(__priv__iio_dev_mode_lock, _try_direct,
*
* 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)::
+ * with IIO_DEV_ACQUIRE_FAILED(), for example (notice the scope braces)::
*
* switch() {
* case IIO_CHAN_INFO_RAW: {
diff --git a/include/linux/notifier.h b/include/linux/notifier.h
index 01b6c9d9956f..4eeae9741a6e 100644
--- a/include/linux/notifier.h
+++ b/include/linux/notifier.h
@@ -46,6 +46,7 @@
* often but notifier_blocks will seldom be removed.
*/
+struct device;
struct notifier_block;
typedef int (*notifier_fn_t)(struct notifier_block *nb,
@@ -145,8 +146,14 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
struct notifier_block *nb);
+int devm_atomic_notifier_chain_register(struct device *dev,
+ struct atomic_notifier_head *nh,
+ struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *nb);
+int devm_blocking_notifier_chain_register(struct device *dev,
+ struct blocking_notifier_head *nh,
+ struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
diff --git a/kernel/notifier.c b/kernel/notifier.c
index 2f9fe7c30287..55fbd2be7407 100644
--- a/kernel/notifier.c
+++ b/kernel/notifier.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/device/devres.h>
#include <linux/kdebug.h>
#include <linux/kprobes.h>
#include <linux/export.h>
@@ -197,6 +198,58 @@ int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
}
EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);
+struct atomic_notifier_chain_devres {
+ struct atomic_notifier_head *nh;
+ struct notifier_block *nb;
+};
+
+static void devm_atomic_notifier_chain_unregister(struct device *dev, void *res)
+{
+ struct atomic_notifier_chain_devres *dr = res;
+
+ atomic_notifier_chain_unregister(dr->nh, dr->nb);
+}
+
+/**
+ * devm_atomic_notifier_chain_register - Device-managed atomic notifier registration
+ * @dev: Device to tie the notifier lifetime to
+ * @nh: Pointer to head of the atomic notifier chain
+ * @nb: New entry in notifier chain
+ *
+ * Adds a notifier to an atomic notifier chain and registers a cleanup
+ * action to automatically unregister it when @dev is unbound.
+ *
+ * Must be called in process context.
+ *
+ * Return:
+ * 0 on success, negative errno on error.
+ */
+int devm_atomic_notifier_chain_register(struct device *dev,
+ struct atomic_notifier_head *nh,
+ struct notifier_block *nb)
+{
+ struct atomic_notifier_chain_devres *dr;
+ int ret;
+
+ dr = devres_alloc(devm_atomic_notifier_chain_unregister,
+ sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ ret = atomic_notifier_chain_register(nh, nb);
+ if (ret) {
+ devres_free(dr);
+ return ret;
+ }
+
+ dr->nh = nh;
+ dr->nb = nb;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_atomic_notifier_chain_register);
+
/**
* atomic_notifier_call_chain - Call functions in an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain
@@ -349,6 +402,58 @@ int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh,
}
EXPORT_SYMBOL_GPL(blocking_notifier_call_chain_robust);
+struct blocking_notifier_chain_devres {
+ struct blocking_notifier_head *nh;
+ struct notifier_block *nb;
+};
+
+static void devm_blocking_notifier_chain_unregister(struct device *dev,
+ void *res)
+{
+ struct blocking_notifier_chain_devres *dr = res;
+
+ blocking_notifier_chain_unregister(dr->nh, dr->nb);
+}
+
+/**
+ * devm_blocking_notifier_chain_register - Device-managed blocking notifier registration
+ * @dev: Device to tie the notifier lifetime to
+ * @nh: Pointer to head of the blocking notifier chain
+ * @nb: New entry in notifier chain
+ *
+ * Adds a notifier to a blocking notifier chain and registers a cleanup
+ * action to automatically unregister it when @dev is unbound.
+ * Must be called in process context.
+ *
+ * Return:
+ * 0 on success, negative errno on error.
+ */
+int devm_blocking_notifier_chain_register(struct device *dev,
+ struct blocking_notifier_head *nh,
+ struct notifier_block *nb)
+{
+ struct blocking_notifier_chain_devres *dr;
+ int ret;
+
+ dr = devres_alloc(devm_blocking_notifier_chain_unregister,
+ sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ ret = blocking_notifier_chain_register(nh, nb);
+ if (ret) {
+ devres_free(dr);
+ return ret;
+ }
+
+ dr->nh = nh;
+ dr->nb = nb;
+ devres_add(dev, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_blocking_notifier_chain_register);
+
/**
* blocking_notifier_call_chain - Call functions in a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain