diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-07 14:29:07 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-07 14:29:07 +0100 |
| commit | 15b72c6d38fbce11bdcb674f76a7b9d62ba9a7d1 (patch) | |
| tree | e782ed3a8ec5e20535a7d9317c2462e681a906ae | |
| parent | 0e98cf90e278cf6959faf07af3b41aff24fd7845 (diff) | |
| parent | 309bd01041636b587bcb424b243640b448bd62f7 (diff) | |
| download | linux-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
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, ®val); + + /* 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 = <r301_info, + .info_no_irq = <r301_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 = <r301_info, + .info_no_irq = <r301_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 = <r301_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 = <r301_info, - .info_no_irq = <r301_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 = <r301_info, - .info_no_irq = <r301_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 |
