aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-04-23 20:19:37 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-04-23 20:19:37 -0700
commit7732ce06ed8a0757b558665e8b37c29495585ce3 (patch)
tree76bb8a4bda693b5a2d880d571ffbb733605bc8b0
parentdf1aa5b0d1a69b93d1371063299e42dcc56cbe7b (diff)
parentb80ad8e3cd2712b78b98804d1f59199680d8ed91 (diff)
downloadiio-7732ce06ed8a0757b558665e8b37c29495585ce3.tar.gz
Merge tag 'iio-for-6.10a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next
Jonathan writes: IIO: 1st set of new device support, features and cleanup for 6.10 The Analog Device team (Paul Cercueil and Nuno Sa) have been working on improving high speed device handling. They have had some support in their own tree for many years, so it is great to see them bring it to upstream. Some of that is seen here, with the first output device using the IIO dmaengine infrastructure and a new DAC backend FPGA IP driver. This makes use of a new set of interfaces to allow backend and front end driver communication in a fashion that in theory at least allows for a single driver for a given ADC / DAC independent of the IP to which is being used to deal with the data bus and DMA aspects of working with these devices. It is early days for this new generic way of handling split devices, but as it's kernel internals only we can merrily change anything about it as a wider diversity of devices show up and we get a better feel for what works. Alongside the usual set of new drivers and features we have the automatic cleanup of fwnode_handle_put() which didn't quite make it in last cycle. The equivalent DT version was merged by Rob Herring via the DT binding tree and one patch using that in IIO can also be found in this pull request. Rob has been making extensive use of that infrastructure in the DT core which is good to see and provides more evidence this basic approach is useful. In some cases, the IIO driver was converted over from DT only to using the generic firmware description handling of property.h including using the new macros. The general preference for IIO is to use this more generic handling where possible - a bunch of other drivers have been converted this cycle as well. New device support ================== adi,ad7173 - New driver supporting AD7172-2, AD7172-4 AD7173-9, AD7175-2, AD7175-8, AD7176-2 and AD7177-2 ADCs. - Follow up fix for an accidental use of logic not instead of bitwise. adi,ad7944 - New driver supporting AD7944, AD7985 and AD7986 pin compatible ADCs. - Later patch added use of new spi_optimize_message() to reduce overheads of setting up a reused message. - Additional changes later in series reduced code duplication. adi,ad9739a RF DAC - New driver for this 14-bit 2.5 GSPS DAC via an LVDS interface. adi,axi-dac - Support for this FPGA IP used to send data to high performance DACs over an interface such as JESD204B/C or parallel interfaces. Used in conjunction with a DAC driver. The initial user is the ad9739a. The dmaengine-buffer needed various changes to make it bidirectional. avago,apds9306 - New driver for this ambient light sensor. - Fix much later in this pull for an off by 1 error. New device IDs ============== For these at most an ID and a instance of chip specific data was needed. Always nice to see manufacturers sticking to an existing software interface for new parts. allwinner,sun20i - Add support for h616. invensense,mpu6050 - Add support for ICM42688 maxim,max30102 - Add compatible for MAX30101 ti,dac5571 - Add compatible for DAC081C081 General ======= fwnode_handle - Support for cleanup.h based __free(fwnode_handle) - Loop macro using this for looping over child nodes without needing to call fwnode_handle_put() in ever early exit from the loop. - Used in: * adi,ad3552r * adi,ad4130 * adi,ad5770r * adi,ad74413r * adi,ad7173 * adi,adfm2000 * linear,ltc2688 * linear,ltc2983 * maxim,max11410 * microchip,pac1934 * qcom,spmi-adc * renesas,rz2gl * st,ab8500 * st,stm32 (Fix for failure to set return value precedes this patch, providing an example of why enabling direct returns makes bugs less likely) - Conversions to fwnode also using the cleanup logic * adi,ad7124 * adi,ad7292 * freescale,fsl-imx25-gcq - Other conversions to fwnode where the new cleanup handling isn't useful * adi,ad7192 * avia,hx711 * freescale,mma8452 * nxp,fxls8962af * st,spear * ti,twl4030 Features ======== adi,adxl345 - Support SPI_3WIRE mode. adi,ad9944 - Support 3-wire mode, note this isn't normal 3-wire SPI (unlike the adxl345 change above), but rather a wiring scheme where the SPI chip select is used to trigger conversions rather than using a separate pin. - Add some device specific documentation, mostly around the various wiring schemes. invensense,mpu6050 - Add Wake on Motion support as an IIO event and as a wake-up source. linear,ltc2983 - Add vdd-supply. ti,hdc3020 - Add power management using trigger on demand mode and adding suspend and resume handling. - Use reset GPIO if available. Cleanup and fixes ================ iio core - Use the various autocleanup and lock guards from cleanup.h to simplify the IIO core. - Don't set the pointer used for iio_priv() if it is zero sized as that points beyond the end of the allocation. No driver actually uses it in that case but good to clean this up. various drivers - Drop unnecessary casts of other pointer types to void * docs - Add missing ABI entry for in_temp_input. adi,adx345 - General cleanup prior to adding spi-3wire mode. adi,axi-adc - Be more flexible and allow minor version changes as these are expected to be backwards compatible. avago,apds9300/9600 - Merge near identical bindings. The drivers are quite different, but the bindings can be shared. The apds9306 binding introduced in this series uses this shared binding doc as well. - Add missing vdd-supply - Update binding to use IRQ_TYPE_LEVEL_LOW instead of 8. bosch,bmp280 - Organize headers freescale,fxl-imx25-gcq - Use devm_ for remaining probe() time setup allowing dropping of specific error handling and remove() functions. infineon,dps310 - Fix handling of negative temperatures - Bring style of other similar calls inline with the form needed for temperatures - Ensure error handling of regmap calls is consistent within the driver. - Simplify scale reading logic. invensense,mpu6050 - Flip logic in binding to exclude devices without i2c-gate instead of opting in. The list is expected to be much shorter as all recent devices support this feature. honeywell,hsc030pa - Use spi_read() instead of opening coding. renesas,rcar - Use device_for_each_child_of_node_scoped() to remove need to manually release. Left over from series the rest of which went in during 6.9. st,ab8500 - Fix naming of function parameters in kernel-doc * tag 'iio-for-6.10a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (108 commits) iio: adc: ti-ads131e08: Use device_for_each_child_node_scoped() to simplify error paths. iio: adc: adi-axi-adc: only error out in major version mismatch iio: dac: support the ad9739a RF DAC iio: dac: add support for AXI DAC IP core iio: backend: add new functionality dt-bindings: iio: dac: add docs for AD9739A dt-bindings: iio: dac: add docs for AXI DAC IP iio: buffer-dmaengine: Enable write support iio: buffer-dmaengine: Support specifying buffer direction iio: buffer-dma: Enable buffer write support iio: buffer-dma: Rename iio_dma_buffer_data_available() iio: buffer-dma: add iio_dmaengine_buffer_setup() iio: pressure: dps310: simplify scale factor reading iio: pressure: dps310: consistently check return value of `regmap_read` iio: pressure: dps310: introduce consistent error handling iio: pressure: dps310: support negative temperature values dt-bindings: iio: adc: Add GPADC for Allwinner H616 iio: dac: ad5755: make use of of_device_id table iio: imu: inv_icm42600: add support of ICM-42688-P dt-bindings: iio: imu: add icm42688 inside inv_icm42600 ...
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio3
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-ad9739a19
-rw-r--r--Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml2
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml279
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml213
-rw-r--r--Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml9
-rw-r--r--Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml95
-rw-r--r--Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml62
-rw-r--r--Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml12
-rw-r--r--Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml5
-rw-r--r--Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml17
-rw-r--r--Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml20
-rw-r--r--Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml44
-rw-r--r--Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml4
-rw-r--r--Documentation/iio/ad7944.rst130
-rw-r--r--Documentation/iio/index.rst1
-rw-r--r--MAINTAINERS28
-rw-r--r--drivers/base/property.c14
-rw-r--r--drivers/iio/accel/adxl345.h36
-rw-r--r--drivers/iio/accel/adxl345_core.c92
-rw-r--r--drivers/iio/accel/adxl345_i2c.c2
-rw-r--r--drivers/iio/accel/adxl345_spi.c10
-rw-r--r--drivers/iio/accel/adxl367.c2
-rw-r--r--drivers/iio/accel/fxls8962af-core.c10
-rw-r--r--drivers/iio/accel/mma8452.c6
-rw-r--r--drivers/iio/adc/Kconfig27
-rw-r--r--drivers/iio/adc/Makefile2
-rw-r--r--drivers/iio/adc/ab8500-gpadc.c8
-rw-r--r--drivers/iio/adc/ad4130.c7
-rw-r--r--drivers/iio/adc/ad7124.c55
-rw-r--r--drivers/iio/adc/ad7173.c1180
-rw-r--r--drivers/iio/adc/ad7192.c38
-rw-r--r--drivers/iio/adc/ad7292.c13
-rw-r--r--drivers/iio/adc/ad7944.c524
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c23
-rw-r--r--drivers/iio/adc/adi-axi-adc.c20
-rw-r--r--drivers/iio/adc/fsl-imx25-gcq.c140
-rw-r--r--drivers/iio/adc/hx711.c5
-rw-r--r--drivers/iio/adc/max11410.c27
-rw-r--r--drivers/iio/adc/pac1934.c77
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c7
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c21
-rw-r--r--drivers/iio/adc/rzg2l_adc.c11
-rw-r--r--drivers/iio/adc/spear_adc.c25
-rw-r--r--drivers/iio/adc/stm32-adc.c61
-rw-r--r--drivers/iio/adc/ti-ads131e08.c12
-rw-r--r--drivers/iio/adc/twl4030-madc.c19
-rw-r--r--drivers/iio/addac/ad74413r.c10
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c100
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c86
-rw-r--r--drivers/iio/dac/Kconfig37
-rw-r--r--drivers/iio/dac/Makefile2
-rw-r--r--drivers/iio/dac/ad3552r.c110
-rw-r--r--drivers/iio/dac/ad5755.c24
-rw-r--r--drivers/iio/dac/ad5770r.c19
-rw-r--r--drivers/iio/dac/ad9739a.c463
-rw-r--r--drivers/iio/dac/adi-axi-dac.c635
-rw-r--r--drivers/iio/dac/ltc2688.c28
-rw-r--r--drivers/iio/dac/ti-dac5571.c3
-rw-r--r--drivers/iio/frequency/admfm2000.c24
-rw-r--r--drivers/iio/health/max30102.c2
-rw-r--r--drivers/iio/humidity/hdc3020.c111
-rw-r--r--drivers/iio/humidity/hts221_core.c2
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600.h2
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_core.c5
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c3
-rw-r--r--drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c3
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c542
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h36
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c17
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c83
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c2
-rw-r--r--drivers/iio/industrialio-backend.c179
-rw-r--r--drivers/iio/industrialio-buffer.c120
-rw-r--r--drivers/iio/industrialio-core.c49
-rw-r--r--drivers/iio/industrialio-trigger.c71
-rw-r--r--drivers/iio/inkern.c263
-rw-r--r--drivers/iio/light/Kconfig12
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/apds9306.c1361
-rw-r--r--drivers/iio/light/st_uvis25_core.c2
-rw-r--r--drivers/iio/pressure/bmp280-core.c16
-rw-r--r--drivers/iio/pressure/dps310.c138
-rw-r--r--drivers/iio/pressure/hsc030pa_spi.c7
-rw-r--r--drivers/iio/temperature/ltc2983.c142
-rw-r--r--include/linux/iio/adc/ad_sigma_delta.h3
-rw-r--r--include/linux/iio/backend.h49
-rw-r--r--include/linux/iio/buffer-dma.h4
-rw-r--r--include/linux/iio/buffer-dmaengine.h24
-rw-r--r--include/linux/property.h22
92 files changed, 6964 insertions, 1267 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
index 2e6d5ebfd3c73..7cee78ad41085 100644
--- a/Documentation/ABI/testing/sysfs-bus-iio
+++ b/Documentation/ABI/testing/sysfs-bus-iio
@@ -243,7 +243,8 @@ Description:
less measurements. Units after application of scale and offset
are milli degrees Celsius.
-What: /sys/bus/iio/devices/iio:deviceX/in_tempX_input
+What: /sys/bus/iio/devices/iio:deviceX/in_tempY_input
+What: /sys/bus/iio/devices/iio:deviceX/in_temp_input
KernelVersion: 2.6.38
Contact: linux-iio@vger.kernel.org
Description:
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
new file mode 100644
index 0000000000000..ed59299e6f8d9
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
@@ -0,0 +1,19 @@
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
+KernelVersion: 6.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ DAC operating mode. One of the following modes can be selected:
+
+ * normal: This is DAC normal mode.
+ * mixed-mode: In this mode the output is effectively chopped at
+ the DAC sample rate. This has the effect of
+ reducing the power of the fundamental signal while
+ increasing the power of the images centered around
+ the DAC sample rate, thus improving the output
+ power of these images.
+
+What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
+KernelVersion: 6.9
+Contact: linux-iio@vger.kernel.org
+Description:
+ Available operating modes.
diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
index 07cacc3f6a975..280ed479ef5ab 100644
--- a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
@@ -32,6 +32,8 @@ properties:
spi-cpol: true
+ spi-3wire: true
+
interrupts:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
new file mode 100644
index 0000000000000..ea6cfcd0aff46
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
@@ -0,0 +1,279 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Analog Devices Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad7173.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD7173 ADC
+
+maintainers:
+ - Ceclan Dumitru <dumitru.ceclan@analog.com>
+
+description: |
+ Analog Devices AD717x ADC's:
+ The AD717x family offer a complete integrated Sigma-Delta ADC solution which
+ can be used in high precision, low noise single channel applications
+ (Life Science measurements) or higher speed multiplexed applications
+ (Factory Automation PLC Input modules). The Sigma-Delta ADC is intended
+ primarily for measurement of signals close to DC but also delivers
+ outstanding performance with input bandwidths out to ~10kHz.
+
+ Datasheets for supported chips:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-2.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-4.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7173-8.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-2.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-8.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7176-2.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/AD7177-2.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad7172-2
+ - adi,ad7172-4
+ - adi,ad7173-8
+ - adi,ad7175-2
+ - adi,ad7175-8
+ - adi,ad7176-2
+ - adi,ad7177-2
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ minItems: 1
+ items:
+ - description: |
+ Ready: multiplexed with SPI data out. While SPI CS is low,
+ can be used to indicate the completion of a conversion.
+
+ - description: |
+ Error: The three error bits in the status register (ADC_ERROR, CRC_ERROR,
+ and REG_ERROR) are OR'ed, inverted, and mapped to the ERROR pin.
+ Therefore, the ERROR pin indicates that an error has occurred.
+
+ interrupt-names:
+ minItems: 1
+ items:
+ - const: rdy
+ - const: err
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ spi-max-frequency:
+ maximum: 20000000
+
+ gpio-controller:
+ description: Marks the device node as a GPIO controller.
+
+ '#gpio-cells':
+ const: 2
+ description:
+ The first cell is the GPIO number and the second cell specifies
+ GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
+
+ vref-supply:
+ description: |
+ Differential external reference supply used for conversion. The reference
+ voltage (Vref) specified here must be the voltage difference between the
+ REF+ and REF- pins: Vref = (REF+) - (REF-).
+
+ vref2-supply:
+ description: |
+ Differential external reference supply used for conversion. The reference
+ voltage (Vref2) specified here must be the voltage difference between the
+ REF2+ and REF2- pins: Vref2 = (REF2+) - (REF2-).
+
+ avdd-supply:
+ description: Avdd supply, can be used as reference for conversion.
+ This supply is referenced to AVSS, voltage specified here
+ represents (AVDD1 - AVSS).
+
+ avdd2-supply:
+ description: Avdd2 supply, used as the input to the internal voltage regulator.
+ This supply is referenced to AVSS, voltage specified here
+ represents (AVDD2 - AVSS).
+
+ iovdd-supply:
+ description: iovdd supply, used for the chip digital interface.
+
+ clocks:
+ maxItems: 1
+ description: |
+ Optional external clock source. Can include one clock source: external
+ clock or external crystal.
+
+ clock-names:
+ enum:
+ - ext-clk
+ - xtal
+
+ '#clock-cells':
+ const: 0
+
+patternProperties:
+ "^channel@[0-9a-f]$":
+ type: object
+ $ref: adc.yaml
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ minimum: 0
+ maximum: 15
+
+ diff-channels:
+ items:
+ minimum: 0
+ maximum: 31
+
+ adi,reference-select:
+ description: |
+ Select the reference source to use when converting on
+ the specific channel. Valid values are:
+ vref : REF+ /REF−
+ vref2 : REF2+ /REF2−
+ refout-avss: REFOUT/AVSS (Internal reference)
+ avdd : AVDD /AVSS
+
+ External reference ref2 only available on ad7173-8 and ad7172-4.
+ Internal reference refout-avss not available on ad7172-4.
+
+ If not specified, internal reference used (if available).
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - vref
+ - vref2
+ - refout-avss
+ - avdd
+ default: refout-avss
+
+ required:
+ - reg
+ - diff-channels
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+ # Only ad7172-4, ad7173-8 and ad7175-8 support vref2
+ # Other models have [0-3] channel registers
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ enum:
+ - adi,ad7172-4
+ - adi,ad7173-8
+ - adi,ad7175-8
+ then:
+ properties:
+ vref2-supply: false
+ patternProperties:
+ "^channel@[0-9a-f]$":
+ properties:
+ adi,reference-select:
+ enum:
+ - vref
+ - refout-avss
+ - avdd
+ reg:
+ maximum: 3
+
+ # Model ad7172-4 does not support internal reference
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,ad7172-4
+ then:
+ patternProperties:
+ "^channel@[0-9a-f]$":
+ properties:
+ reg:
+ maximum: 7
+ adi,reference-select:
+ enum:
+ - vref
+ - vref2
+ - avdd
+ required:
+ - adi,reference-select
+
+ - if:
+ anyOf:
+ - required: [clock-names]
+ - required: [clocks]
+ then:
+ properties:
+ '#clock-cells': false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "adi,ad7173-8";
+ reg = <0>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
+ interrupt-names = "rdy";
+ interrupt-parent = <&gpio>;
+ spi-max-frequency = <5000000>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ #clock-cells = <0>;
+
+ vref-supply = <&dummy_regulator>;
+
+ channel@0 {
+ reg = <0>;
+ bipolar;
+ diff-channels = <0 1>;
+ adi,reference-select = "vref";
+ };
+
+ channel@1 {
+ reg = <1>;
+ diff-channels = <2 3>;
+ };
+
+ channel@2 {
+ reg = <2>;
+ bipolar;
+ diff-channels = <4 5>;
+ };
+
+ channel@3 {
+ reg = <3>;
+ bipolar;
+ diff-channels = <6 7>;
+ };
+
+ channel@4 {
+ reg = <4>;
+ diff-channels = <8 9>;
+ adi,reference-select = "avdd";
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
new file mode 100644
index 0000000000000..d17d184842d32
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
@@ -0,0 +1,213 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad7944.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices PulSAR LFCSP Analog to Digital Converters
+
+maintainers:
+ - Michael Hennerich <Michael.Hennerich@analog.com>
+ - Nuno Sá <nuno.sa@analog.com>
+
+description: |
+ A family of pin-compatible single channel differential analog to digital
+ converters with SPI support in a LFCSP package.
+
+ * https://www.analog.com/en/products/ad7944.html
+ * https://www.analog.com/en/products/ad7985.html
+ * https://www.analog.com/en/products/ad7986.html
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ enum:
+ - adi,ad7944
+ - adi,ad7985
+ - adi,ad7986
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 111111111
+
+ spi-cpol: true
+ spi-cpha: true
+
+ adi,spi-mode:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ single, chain ]
+ description: |
+ This property indicates the SPI wiring configuration.
+
+ When this property is omitted, it is assumed that the device is using what
+ the datasheet calls "4-wire mode". This is the conventional SPI mode used
+ when there are multiple devices on the same bus. In this mode, the CNV
+ line is used to initiate the conversion and the SDI line is connected to
+ CS on the SPI controller.
+
+ When this property is present, it indicates that the device is using one
+ of the following alternative wiring configurations:
+
+ * single: The datasheet calls this "3-wire mode". (NOTE: The datasheet's
+ definition of 3-wire mode is NOT at all related to the standard
+ spi-3wire property!) This mode is often used when the ADC is the only
+ device on the bus. In this mode, SDI is tied to VIO, and the CNV line
+ can be connected to the CS line of the SPI controller or to a GPIO, in
+ which case the CS line of the controller is unused.
+ * chain: The datasheet calls this "chain mode". This mode is used to save
+ on wiring when multiple ADCs are used. In this mode, the SDI line of
+ one chip is tied to the SDO of the next chip in the chain and the SDI of
+ the last chip in the chain is tied to GND. Only the first chip in the
+ chain is connected to the SPI bus. The CNV line of all chips are tied
+ together. The CS line of the SPI controller can be used as the CNV line
+ only if it is active high.
+
+ '#daisy-chained-devices': true
+
+ avdd-supply:
+ description: A 2.5V supply that powers the analog circuitry.
+
+ dvdd-supply:
+ description: A 2.5V supply that powers the digital circuitry.
+
+ vio-supply:
+ description:
+ A 1.8V to 2.7V supply for the digital inputs and outputs.
+
+ bvdd-supply:
+ description:
+ A voltage supply for the buffered power. When using an external reference
+ without an internal buffer (PDREF high, REFIN low), this should be
+ connected to the same supply as ref-supply. Otherwise, when using an
+ internal reference or an external reference with an internal buffer, this
+ is connected to a 5V supply.
+
+ ref-supply:
+ description:
+ Voltage regulator for the external reference voltage (REF). This property
+ is omitted when using an internal reference.
+
+ refin-supply:
+ description:
+ Voltage regulator for the reference buffer input (REFIN). When using an
+ external buffer with internal reference, this should be connected to a
+ 1.2V external reference voltage supply. Otherwise, this property is
+ omitted.
+
+ cnv-gpios:
+ description:
+ The Convert Input (CNV). This input has multiple functions. It initiates
+ the conversions and selects the SPI mode of the device (chain or CS). In
+ 'single' mode, this property is omitted if the CNV pin is connected to the
+ CS line of the SPI controller.
+ maxItems: 1
+
+ turbo-gpios:
+ description:
+ GPIO connected to the TURBO line. If omitted, it is assumed that the TURBO
+ line is hard-wired and the state is determined by the adi,always-turbo
+ property.
+ maxItems: 1
+
+ adi,always-turbo:
+ type: boolean
+ description:
+ When present, this property indicates that the TURBO line is hard-wired
+ and the state is always high. If neither this property nor turbo-gpios is
+ present, the TURBO line is assumed to be hard-wired and the state is
+ always low.
+
+ interrupts:
+ description:
+ The SDO pin can also function as a busy indicator. This node should be
+ connected to an interrupt that is triggered when the SDO line goes low
+ while the SDI line is high and the CNV line is low ('single' mode) or the
+ SDI line is low and the CNV line is high ('multi' mode); or when the SDO
+ line goes high while the SDI and CNV lines are high (chain mode),
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - dvdd-supply
+ - vio-supply
+ - bvdd-supply
+
+allOf:
+ # ref-supply and refin-supply are mutually exclusive (neither is also valid)
+ - if:
+ required:
+ - ref-supply
+ then:
+ properties:
+ refin-supply: false
+ - if:
+ required:
+ - refin-supply
+ then:
+ properties:
+ ref-supply: false
+ # in '4-wire' mode, cnv-gpios is required, for other modes it is optional
+ - if:
+ not:
+ required:
+ - adi,spi-mode
+ then:
+ required:
+ - cnv-gpios
+ # chain mode has lower SCLK max rate and doesn't work when TURBO is enabled
+ - if:
+ required:
+ - adi,spi-mode
+ properties:
+ adi,spi-mode:
+ const: chain
+ then:
+ properties:
+ spi-max-frequency:
+ maximum: 90909090
+ adi,always-turbo: false
+ required:
+ - '#daisy-chained-devices'
+ else:
+ properties:
+ '#daisy-chained-devices': false
+ # turbo-gpios and adi,always-turbo are mutually exclusive
+ - if:
+ required:
+ - turbo-gpios
+ then:
+ properties:
+ adi,always-turbo: false
+ - if:
+ required:
+ - adi,always-turbo
+ then:
+ properties:
+ turbo-gpios: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc@0 {
+ compatible = "adi,ad7944";
+ reg = <0>;
+ spi-cpha;
+ spi-max-frequency = <111111111>;
+ avdd-supply = <&supply_2_5V>;
+ dvdd-supply = <&supply_2_5V>;
+ vio-supply = <&supply_1_8V>;
+ bvdd-supply = <&supply_5V>;
+ cnv-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+ turbo-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
index 7ef46c90ebc8e..da605a051b949 100644
--- a/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/allwinner,sun20i-d1-gpadc.yaml
@@ -11,8 +11,13 @@ maintainers:
properties:
compatible:
- enum:
- - allwinner,sun20i-d1-gpadc
+ oneOf:
+ - enum:
+ - allwinner,sun20i-d1-gpadc
+ - items:
+ - enum:
+ - allwinner,sun50i-h616-gpadc
+ - const: allwinner,sun20i-d1-gpadc
"#io-channel-cells":
const: 1
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
new file mode 100644
index 0000000000000..c0b36476113ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD9739A RF DAC
+
+maintainers:
+ - Dragos Bogdan <dragos.bogdan@analog.com>
+ - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+ The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
+ of synthesizing wideband signals from dc up to 3 GHz.
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
+
+properties:
+ compatible:
+ enum:
+ - adi,ad9739a
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ vdd-3p3-supply:
+ description: 3.3V Digital input supply.
+
+ vdd-supply:
+ description: 1.8V Digital input supply.
+
+ vdda-supply:
+ description: 3.3V Analog input supply.
+
+ vddc-supply:
+ description: 1.8V Clock input supply.
+
+ vref-supply:
+ description: Input/Output reference supply.
+
+ io-backends:
+ maxItems: 1
+
+ adi,full-scale-microamp:
+ description: This property represents the DAC full scale current.
+ minimum: 8580
+ maximum: 31700
+ default: 20000
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - io-backends
+ - vdd-3p3-supply
+ - vdd-supply
+ - vdda-supply
+ - vddc-supply
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,ad9739a";
+ reg = <0>;
+
+ clocks = <&dac_clk>;
+
+ io-backends = <&iio_backend>;
+
+ vdd-3p3-supply = <&vdd_3_3>;
+ vdd-supply = <&vdd>;
+ vdda-supply = <&vdd_3_3>;
+ vddc-supply = <&vdd>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
new file mode 100644
index 0000000000000..a55e9bfc66d74
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AXI DAC IP core
+
+maintainers:
+ - Nuno Sa <nuno.sa@analog.com>
+
+description: |
+ Analog Devices Generic AXI DAC IP core for interfacing a DAC device
+ with a high speed serial (JESD204B/C) or source synchronous parallel
+ interface (LVDS/CMOS).
+ Usually, some other interface type (i.e SPI) is used as a control
+ interface for the actual DAC, while this IP core will interface
+ to the data-lines of the DAC and handle the streaming of data from
+ memory via DMA into the DAC.
+
+ https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+
+properties:
+ compatible:
+ enum:
+ - adi,axi-dac-9.1.b
+
+ reg:
+ maxItems: 1
+
+ dmas:
+ maxItems: 1
+
+ dma-names:
+ items:
+ - const: tx
+
+ clocks:
+ maxItems: 1
+
+ '#io-backend-cells':
+ const: 0
+
+required:
+ - compatible
+ - dmas
+ - reg
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ dac@44a00000 {
+ compatible = "adi,axi-dac-9.1.b";
+ reg = <0x44a00000 0x10000>;
+ dmas = <&tx_dma 0>;
+ dma-names = "tx";
+ #io-backend-cells = <0>;
+ clocks = <&axi_clk>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml
index 79da0323c327e..e59db861e2eb1 100644
--- a/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml
@@ -21,6 +21,7 @@ properties:
- ti,dac5573
- ti,dac6573
- ti,dac7573
+ - ti,dac081c081
- ti,dac121c081
reg:
diff --git a/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml b/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml
index c13c10c8d65da..a0fe0a818d86a 100644
--- a/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml
+++ b/Documentation/devicetree/bindings/iio/health/maxim,max30102.yaml
@@ -4,16 +4,20 @@
$id: http://devicetree.org/schemas/iio/health/maxim,max30102.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Maxim MAX30102 heart rate and pulse oximeter and MAX30105 particle-sensor
+title: Maxim MAX30101/2 heart rate and pulse oximeter and MAX30105 particle-sensor
maintainers:
- Matt Ranostay <matt.ranostay@konsulko.com>
properties:
compatible:
- enum:
- - maxim,max30102
- - maxim,max30105
+ oneOf:
+ - enum:
+ - maxim,max30102
+ - maxim,max30105
+ - items:
+ - const: maxim,max30101
+ - const: maxim,max30105
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml b/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml
index 8b5dedd1a598c..b375d307513fe 100644
--- a/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml
+++ b/Documentation/devicetree/bindings/iio/humidity/ti,hdc3020.yaml
@@ -34,6 +34,9 @@ properties:
reg:
maxItems: 1
+ reset-gpios:
+ maxItems: 1
+
required:
- compatible
- reg
@@ -43,6 +46,7 @@ additionalProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@@ -54,5 +58,6 @@ examples:
vdd-supply = <&vcc_3v3>;
interrupt-parent = <&gpio3>;
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
+ reset-gpios = <&gpio3 27 GPIO_ACTIVE_LOW>;
};
};
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 7cd05bcbee317..5e0bed2c45ded 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -32,6 +32,7 @@ properties:
- invensense,icm42605
- invensense,icm42622
- invensense,icm42631
+ - invensense,icm42688
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml
index 297b8a1a7ffbc..587ff2bced2dd 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,mpu6050.yaml
@@ -62,14 +62,15 @@ properties:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
- not:
- properties:
- compatible:
- contains:
- enum:
- - invensense,mpu9150
- - invensense,mpu9250
- - invensense,mpu9255
+ properties:
+ compatible:
+ contains:
+ enum:
+ - invensense,iam20680
+ - invensense,icm20602
+ - invensense,icm20608
+ - invensense,icm20609
+ - invensense,icm20689
then:
properties:
i2c-gate: false
diff --git a/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml b/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml
index 206af44f2c433..b750096530bc6 100644
--- a/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml
+++ b/Documentation/devicetree/bindings/iio/light/avago,apds9300.yaml
@@ -4,17 +4,22 @@
$id: http://devicetree.org/schemas/iio/light/avago,apds9300.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Avago APDS9300 ambient light sensor
+title: Avago Gesture/RGB/ALS/Proximity sensors
maintainers:
- - Jonathan Cameron <jic23@kernel.org>
+ - Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
description: |
- Datasheet at https://www.avagotech.com/docs/AV02-1077EN
+ Datasheet: https://www.avagotech.com/docs/AV02-1077EN
+ Datasheet: https://www.avagotech.com/docs/AV02-4191EN
+ Datasheet: https://www.avagotech.com/docs/AV02-4755EN
properties:
compatible:
- const: avago,apds9300
+ enum:
+ - avago,apds9300
+ - avago,apds9306
+ - avago,apds9960
reg:
maxItems: 1
@@ -22,6 +27,8 @@ properties:
interrupts:
maxItems: 1
+ vdd-supply: true
+
additionalProperties: false
required:
@@ -30,6 +37,8 @@ required:
examples:
- |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -38,7 +47,8 @@ examples:
compatible = "avago,apds9300";
reg = <0x39>;
interrupt-parent = <&gpio2>;
- interrupts = <29 8>;
+ interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
+ vdd-supply = <&regulator_3v3>;
};
};
...
diff --git a/Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml b/Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml
deleted file mode 100644
index f06e0fda56299..0000000000000
--- a/Documentation/devicetree/bindings/iio/light/avago,apds9960.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/iio/light/avago,apds9960.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Avago APDS9960 gesture/RGB/ALS/proximity sensor
-
-maintainers:
- - Matt Ranostay <matt.ranostay@konsulko.com>
-
-description: |
- Datasheet at https://www.avagotech.com/docs/AV02-4191EN
-
-properties:
- compatible:
- const: avago,apds9960
-
- reg:
- maxItems: 1
-
- interrupts:
- maxItems: 1
-
-additionalProperties: false
-
-required:
- - compatible
- - reg
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- light-sensor@39 {
- compatible = "avago,apds9960";
- reg = <0x39>;
- interrupt-parent = <&gpio1>;
- interrupts = <16 1>;
- };
- };
-...
diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
index dbb85135fd668..312febeeb3bba 100644
--- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
+++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
@@ -57,6 +57,8 @@ properties:
interrupts:
maxItems: 1
+ vdd-supply: true
+
adi,mux-delay-config-us:
description: |
Extra delay prior to each conversion, in addition to the internal 1ms
@@ -460,6 +462,7 @@ required:
- compatible
- reg
- interrupts
+ - vdd-supply
additionalProperties: false
@@ -489,6 +492,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
+ vdd-supply = <&supply>;
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
diff --git a/Documentation/iio/ad7944.rst b/Documentation/iio/ad7944.rst
new file mode 100644
index 0000000000000..f418ab1288ae3
--- /dev/null
+++ b/Documentation/iio/ad7944.rst
@@ -0,0 +1,130 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=============
+AD7944 driver
+=============
+
+ADC driver for Analog Devices Inc. AD7944 and similar devices. The module name
+is ``ad7944``.
+
+
+Supported devices
+=================
+
+The following chips are supported by this driver:
+
+* `AD7944 <https://www.analog.com/AD7944>`_
+* `AD7985 <https://www.analog.com/AD7985>`_
+* `AD7986 <https://www.analog.com/AD7986>`_
+
+
+Supported features
+==================
+
+SPI wiring modes
+----------------
+
+The driver currently supports two of the many possible SPI wiring configurations.
+
+CS mode, 3-wire, without busy indicator
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block::
+
+ +-------------+
+ +--------------------| CS |
+ v | |
+ VIO +--------------------+ | HOST |
+ | | CNV | | |
+ +--->| SDI AD7944 SDO |-------->| SDI |
+ | SCK | | |
+ +--------------------+ | |
+ ^ | |
+ +--------------------| SCLK |
+ +-------------+
+
+To select this mode in the device tree, set the ``adi,spi-mode`` property to
+``"single"`` and omit the ``cnv-gpios`` property.
+
+CS mode, 4-wire, without busy indicator
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block::
+
+ +-------------+
+ +-----------------------------------| CS |
+ | | |
+ | +--------------------| GPIO |
+ | v | |
+ | +--------------------+ | HOST |
+ | | CNV | | |
+ +--->| SDI AD7944 SDO |-------->| SDI |
+ | SCK | | |
+ +--------------------+ | |
+ ^ | |
+ +--------------------| SCLK |
+ +-------------+
+
+To select this mode in the device tree, omit the ``adi,spi-mode`` property and
+provide the ``cnv-gpios`` property.
+
+Reference voltage
+-----------------
+
+All 3 possible reference voltage sources are supported:
+
+- Internal reference
+- External 1.2V reference and internal buffer
+- External reference
+
+The source is determined by the device tree. If ``ref-supply`` is present, then
+the external reference is used. If ``refin-supply`` is present, then the internal
+buffer is used. If neither is present, then the internal reference is used.
+
+Unimplemented features
+----------------------
+
+- ``BUSY`` indication
+- ``TURBO`` mode
+- Daisy chain mode
+
+
+Device attributes
+=================
+
+There are two types of ADCs in this family, pseudo-differential and fully
+differential. The channel name is different depending on the type of ADC.
+
+Pseudo-differential ADCs
+------------------------
+
+AD7944 and AD7985 are pseudo-differential ADCs and have the following attributes:
+
++---------------------------------------+--------------------------------------------------------------+
+| Attribute | Description |
++=======================================+==============================================================+
+| ``in_voltage0_raw`` | Raw ADC voltage value (*IN+* referenced to ground sense). |
++---------------------------------------+--------------------------------------------------------------+
+| ``in_voltage0_scale`` | Scale factor to convert raw value to mV. |
++---------------------------------------+--------------------------------------------------------------+
+
+Fully-differential ADCs
+-----------------------
+
+AD7986 is a fully-differential ADC and has the following attributes:
+
++---------------------------------------+--------------------------------------------------------------+
+| Attribute | Description |
++=======================================+==============================================================+
+| ``in_voltage0-voltage1_raw`` | Raw ADC voltage value (*IN+* - *IN-*). |
++---------------------------------------+--------------------------------------------------------------+
+| ``in_voltage0-voltage1_scale`` | Scale factor to convert raw value to mV. |
++---------------------------------------+--------------------------------------------------------------+
+
+
+Device buffers
+==============
+
+This driver supports IIO triggered buffers.
+
+See :doc:`iio_devbuf` for more information.
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index 30b09eefe75ed..fb6f9d7432116 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -16,6 +16,7 @@ Industrial I/O Kernel Drivers
.. toctree::
:maxdepth: 1
+ ad7944
adis16475
bno055
ep93xx_adc
diff --git a/MAINTAINERS b/MAINTAINERS
index f80bdb056a2ea..758c202ec712c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -441,6 +441,16 @@ W: http://wiki.analog.com/AD7879
W: https://ez.analog.com/linux-software-drivers
F: drivers/input/touchscreen/ad7879.c
+AD7944 ADC DRIVER (AD7944/AD7985/AD7986)
+M: Michael Hennerich <michael.hennerich@analog.com>
+M: Nuno Sá <nuno.sa@analog.com>
+R: David Lechner <dlechner@baylibre.com>
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
+F: Documentation/iio/ad7944.rst
+F: drivers/iio/adc/ad7944.c
+
ADAFRUIT MINI I2C GAMEPAD
M: Anshul Dalal <anshulusr@gmail.com>
L: linux-input@vger.kernel.org
@@ -1224,6 +1234,15 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
F: drivers/iio/adc/ad7780.c
+ANALOG DEVICES INC AD9739a DRIVER
+M: Nuno Sa <nuno.sa@analog.com>
+M: Dragos Bogdan <dragos.bogdan@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
+F: drivers/iio/dac/ad9739a.c
+
ANALOG DEVICES INC ADA4250 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -1389,6 +1408,14 @@ F: sound/soc/codecs/adav*
F: sound/soc/codecs/sigmadsp.*
F: sound/soc/codecs/ssm*
+ANALOG DEVICES INC AXI DAC DRIVER
+M: Nuno Sa <nuno.sa@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
+F: drivers/iio/dac/adi-axi-dac.c
+
ANALOG DEVICES INC DMA DRIVERS
M: Lars-Peter Clausen <lars@metafoo.de>
S: Supported
@@ -10542,6 +10569,7 @@ 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*
F: Documentation/devicetree/bindings/iio/
+F: Documentation/iio/
F: drivers/iio/
F: drivers/staging/iio/
F: include/dt-bindings/iio/
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 7324a704a9a11..6a3a434d0d6f4 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -869,20 +869,6 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
EXPORT_SYMBOL_GPL(fwnode_handle_get);
/**
- * fwnode_handle_put - Drop reference to a device node
- * @fwnode: Pointer to the device node to drop the reference to.
- *
- * This has to be used when terminating device_for_each_child_node() iteration
- * with break or return to prevent stale device node references from being left
- * behind.
- */
-void fwnode_handle_put(struct fwnode_handle *fwnode)
-{
- fwnode_call_void_op(fwnode, put);
-}
-EXPORT_SYMBOL_GPL(fwnode_handle_put);
-
-/**
* fwnode_device_is_available - check if a device is available for use
* @fwnode: Pointer to the fwnode of the device.
*
diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h
index 284bd387ce69b..3d5c8719db3d7 100644
--- a/drivers/iio/accel/adxl345.h
+++ b/drivers/iio/accel/adxl345.h
@@ -8,6 +8,39 @@
#ifndef _ADXL345_H_
#define _ADXL345_H_
+#define ADXL345_REG_DEVID 0x00
+#define ADXL345_REG_OFSX 0x1E
+#define ADXL345_REG_OFSY 0x1F
+#define ADXL345_REG_OFSZ 0x20
+#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
+#define ADXL345_REG_BW_RATE 0x2C
+#define ADXL345_REG_POWER_CTL 0x2D
+#define ADXL345_REG_DATA_FORMAT 0x31
+#define ADXL345_REG_DATAX0 0x32
+#define ADXL345_REG_DATAY0 0x34
+#define ADXL345_REG_DATAZ0 0x36
+#define ADXL345_REG_DATA_AXIS(index) \
+ (ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
+
+#define ADXL345_BW_RATE GENMASK(3, 0)
+#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
+
+#define ADXL345_POWER_CTL_MEASURE BIT(3)
+#define ADXL345_POWER_CTL_STANDBY 0x00
+
+#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */
+#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */
+#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
+#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */
+#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */
+
+#define ADXL345_DATA_FORMAT_2G 0
+#define ADXL345_DATA_FORMAT_4G 1
+#define ADXL345_DATA_FORMAT_8G 2
+#define ADXL345_DATA_FORMAT_16G 3
+
+#define ADXL345_DEVID 0xE5
+
/*
* In full-resolution mode, scale factor is maintained at ~4 mg/LSB
* in all g ranges.
@@ -28,6 +61,7 @@ struct adxl345_chip_info {
int uscale;
};
-int adxl345_core_probe(struct device *dev, struct regmap *regmap);
+int adxl345_core_probe(struct device *dev, struct regmap *regmap,
+ int (*setup)(struct device*, struct regmap*));
#endif /* _ADXL345_H_ */
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 8bd30a23ed3b6..006ce66c0aa38 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -17,38 +17,9 @@
#include "adxl345.h"
-#define ADXL345_REG_DEVID 0x00
-#define ADXL345_REG_OFSX 0x1e
-#define ADXL345_REG_OFSY 0x1f
-#define ADXL345_REG_OFSZ 0x20
-#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
-#define ADXL345_REG_BW_RATE 0x2C
-#define ADXL345_REG_POWER_CTL 0x2D
-#define ADXL345_REG_DATA_FORMAT 0x31
-#define ADXL345_REG_DATAX0 0x32
-#define ADXL345_REG_DATAY0 0x34
-#define ADXL345_REG_DATAZ0 0x36
-#define ADXL345_REG_DATA_AXIS(index) \
- (ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
-
-#define ADXL345_BW_RATE GENMASK(3, 0)
-#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
-
-#define ADXL345_POWER_CTL_MEASURE BIT(3)
-#define ADXL345_POWER_CTL_STANDBY 0x00
-
-#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
-#define ADXL345_DATA_FORMAT_2G 0
-#define ADXL345_DATA_FORMAT_4G 1
-#define ADXL345_DATA_FORMAT_8G 2
-#define ADXL345_DATA_FORMAT_16G 3
-
-#define ADXL345_DEVID 0xE5
-
struct adxl345_data {
const struct adxl345_chip_info *info;
struct regmap *regmap;
- u8 data_range;
};
#define ADXL345_CHANNEL(index, axis) { \
@@ -197,44 +168,75 @@ static void adxl345_powerdown(void *regmap)
regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY);
}
-int adxl345_core_probe(struct device *dev, struct regmap *regmap)
+/**
+ * adxl345_core_probe() - probe and setup for the adxl345 accelerometer,
+ * also covers the adlx375 accelerometer
+ * @dev: Driver model representation of the device
+ * @regmap: Regmap instance for the device
+ * @setup: Setup routine to be executed right before the standard device
+ * setup
+ *
+ * Return: 0 on success, negative errno on error
+ */
+int adxl345_core_probe(struct device *dev, struct regmap *regmap,
+ int (*setup)(struct device*, struct regmap*))
{
struct adxl345_data *data;
struct iio_dev *indio_dev;
u32 regval;
+ unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE |
+ ADXL345_DATA_FORMAT_JUSTIFY |
+ ADXL345_DATA_FORMAT_FULL_RES |
+ ADXL345_DATA_FORMAT_SELF_TEST);
int ret;
- ret = regmap_read(regmap, ADXL345_REG_DEVID, &regval);
- if (ret < 0)
- return dev_err_probe(dev, ret, "Error reading device ID\n");
-
- if (regval != ADXL345_DEVID)
- return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
- regval, ADXL345_DEVID);
-
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
- /* Enable full-resolution mode */
- data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
data->info = device_get_match_data(dev);
if (!data->info)
return -ENODEV;
- ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
- data->data_range);
- if (ret < 0)
- return dev_err_probe(dev, ret, "Failed to set data range\n");
-
indio_dev->name = data->info->name;
indio_dev->info = &adxl345_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl345_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
+ if (setup) {
+ /* Perform optional initial bus specific configuration */
+ ret = setup(dev, data->regmap);
+ if (ret)
+ return ret;
+
+ /* Enable full-resolution mode */
+ ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT,
+ data_format_mask,
+ ADXL345_DATA_FORMAT_FULL_RES);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set data range\n");
+
+ } else {
+ /* Enable full-resolution mode (init all data_format bits) */
+ ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
+ ADXL345_DATA_FORMAT_FULL_RES);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to set data range\n");
+ }
+
+ ret = regmap_read(data->regmap, ADXL345_REG_DEVID, &regval);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Error reading device ID\n");
+
+ if (regval != ADXL345_DEVID)
+ return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
+ regval, ADXL345_DEVID);
+
/* Enable measurement mode */
ret = adxl345_powerup(data->regmap);
if (ret < 0)
diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c
index a3084b0a8f785..4065b8f7c8a8a 100644
--- a/drivers/iio/accel/adxl345_i2c.c
+++ b/drivers/iio/accel/adxl345_i2c.c
@@ -27,7 +27,7 @@ static int adxl345_i2c_probe(struct i2c_client *client)
if (IS_ERR(regmap))
return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n");
- return adxl345_core_probe(&client->dev, regmap);
+ return adxl345_core_probe(&client->dev, regmap, NULL);
}
static const struct adxl345_chip_info adxl345_i2c_info = {
diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c
index 93ca349f17809..57e16b4417021 100644
--- a/drivers/iio/accel/adxl345_spi.c
+++ b/drivers/iio/accel/adxl345_spi.c
@@ -20,6 +20,11 @@ static const struct regmap_config adxl345_spi_regmap_config = {
.read_flag_mask = BIT(7) | BIT(6),
};
+static int adxl345_spi_setup(struct device *dev, struct regmap *regmap)
+{
+ return regmap_write(regmap, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT_SPI_3WIRE);
+}
+
static int adxl345_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
@@ -33,7 +38,10 @@ static int adxl345_spi_probe(struct spi_device *spi)
if (IS_ERR(regmap))
return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n");
- return adxl345_core_probe(&spi->dev, regmap);
+ if (spi->mode & SPI_3WIRE)
+ return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup);
+ else
+ return adxl345_core_probe(&spi->dev, regmap, NULL);
}
static const struct adxl345_chip_info adxl345_spi_info = {
diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c
index 210228affb80d..5cf4828a5eb5e 100644
--- a/drivers/iio/accel/adxl367.c
+++ b/drivers/iio/accel/adxl367.c
@@ -621,7 +621,7 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- struct adxl367_state *st = iio_priv(indio_dev);;
+ struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c
index be8a15cb945fd..4fbc01bda62e4 100644
--- a/drivers/iio/accel/fxls8962af-core.c
+++ b/drivers/iio/accel/fxls8962af-core.c
@@ -15,9 +15,11 @@
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/i2c.h>
+#include <linux/irq.h>
#include <linux/module.h>
-#include <linux/of_irq.h>
+#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
@@ -1062,12 +1064,12 @@ static void fxls8962af_pm_disable(void *dev_ptr)
fxls8962af_standby(iio_priv(indio_dev));
}
-static void fxls8962af_get_irq(struct device_node *of_node,
+static void fxls8962af_get_irq(struct device *dev,
enum fxls8962af_int_pin *pin)
{
int irq;
- irq = of_irq_get_byname(of_node, "INT2");
+ irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (irq > 0) {
*pin = FXLS8962AF_PIN_INT2;
return;
@@ -1086,7 +1088,7 @@ static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq)
u8 int_pin_sel;
int ret;
- fxls8962af_get_irq(dev->of_node, &int_pin);
+ fxls8962af_get_irq(dev, &int_pin);
switch (int_pin) {
case FXLS8962AF_PIN_INT1:
int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1;
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index d3fd0318e47bf..62e6369e22696 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -19,6 +19,8 @@
*/
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -28,8 +30,6 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/events.h>
#include <linux/delay.h>
-#include <linux/of.h>
-#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -1642,7 +1642,7 @@ static int mma8452_probe(struct i2c_client *client)
if (client->irq) {
int irq2;
- irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
+ irq2 = fwnode_irq_get_byname(dev_fwnode(&client->dev), "INT2");
if (irq2 == client->irq) {
dev_dbg(&client->dev, "using interrupt line INT2\n");
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 0d9282fa67f59..8db68b80b3915 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -70,6 +70,23 @@ config AD7124
To compile this driver as a module, choose M here: the module will be
called ad7124.
+config AD7173
+ tristate "Analog Devices AD7173 driver"
+ depends on SPI_MASTER
+ select AD_SIGMA_DELTA
+ select GPIO_REGMAP if GPIOLIB
+ select REGMAP_SPI if GPIOLIB
+ help
+ Say yes here to build support for Analog Devices AD7173 and similar ADC
+ Currently supported models:
+ - AD7172-2
+ - AD7173-8
+ - AD7175-2
+ - AD7176-2
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7173.
+
config AD7192
tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
depends on SPI
@@ -264,6 +281,16 @@ config AD7923
To compile this driver as a module, choose M here: the
module will be called ad7923.
+config AD7944
+ tristate "Analog Devices AD7944 and similar ADCs driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices
+ AD7944, AD7985, AD7986 ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7944
+
config AD7949
tristate "Analog Devices AD7949 and similar ADCs driver"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index b3c434722364a..edb32ce2af026 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_AD7091R) += ad7091r-base.o
obj-$(CONFIG_AD7091R5) += ad7091r5.o
obj-$(CONFIG_AD7091R8) += ad7091r8.o
obj-$(CONFIG_AD7124) += ad7124.o
+obj-$(CONFIG_AD7173) += ad7173.o
obj-$(CONFIG_AD7192) += ad7192.o
obj-$(CONFIG_AD7266) += ad7266.o
obj-$(CONFIG_AD7280) += ad7280a.o
@@ -28,6 +29,7 @@ obj-$(CONFIG_AD7780) += ad7780.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
+obj-$(CONFIG_AD7944) += ad7944.o
obj-$(CONFIG_AD7949) += ad7949.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AD9467) += ad9467.o
diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c
index 80645fee79a4c..59f66e9cb0e86 100644
--- a/drivers/iio/adc/ab8500-gpadc.c
+++ b/drivers/iio/adc/ab8500-gpadc.c
@@ -1021,14 +1021,13 @@ static int ab8500_gpadc_parse_channel(struct device *dev,
/**
* ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT
* @gpadc: the GPADC to configure the channels for
- * @chans: the IIO channels we parsed
- * @nchans: the number of IIO channels we parsed
+ * @chans_parsed: the IIO channels we parsed
+ * @nchans_parsed: the number of IIO channels we parsed
*/
static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
struct iio_chan_spec **chans_parsed,
unsigned int *nchans_parsed)
{
- struct fwnode_handle *child;
struct ab8500_gpadc_chan_info *ch;
struct iio_chan_spec *iio_chans;
unsigned int nchans;
@@ -1052,7 +1051,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
return -ENOMEM;
i = 0;
- device_for_each_child_node(gpadc->dev, child) {
+ device_for_each_child_node_scoped(gpadc->dev, child) {
struct iio_chan_spec *iio_chan;
int ret;
@@ -1062,7 +1061,6 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch,
iio_chan);
if (ret) {
- fwnode_handle_put(child);
return ret;
}
i++;
diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c
index febb64e67955e..aaf1fb0ac4477 100644
--- a/drivers/iio/adc/ad4130.c
+++ b/drivers/iio/adc/ad4130.c
@@ -1600,17 +1600,14 @@ static int ad4130_parse_fw_children(struct iio_dev *indio_dev)
{
struct ad4130_state *st = iio_priv(indio_dev);
struct device *dev = &st->spi->dev;
- struct fwnode_handle *child;
int ret;
indio_dev->channels = st->chans;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
ret = ad4130_parse_fw_channel(indio_dev, child);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
}
return 0;
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index b9b206fcd748f..e7b1d517d3def 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -14,7 +14,8 @@
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -807,22 +808,19 @@ static int ad7124_check_chip_id(struct ad7124_state *st)
return 0;
}
-static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
- struct device_node *np)
+static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
+ struct device *dev)
{
struct ad7124_state *st = iio_priv(indio_dev);
struct ad7124_channel_config *cfg;
struct ad7124_channel *channels;
- struct device_node *child;
struct iio_chan_spec *chan;
unsigned int ain[2], channel = 0, tmp;
int ret;
- st->num_channels = of_get_available_child_count(np);
- if (!st->num_channels) {
- dev_err(indio_dev->dev.parent, "no channel children\n");
- return -ENODEV;
- }
+ st->num_channels = device_get_child_node_count(dev);
+ if (!st->num_channels)
+ return dev_err_probe(dev, -ENODEV, "no channel children\n");
chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
sizeof(*chan), GFP_KERNEL);
@@ -838,39 +836,38 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
indio_dev->num_channels = st->num_channels;
st->channels = channels;
- for_each_available_child_of_node(np, child) {
+ device_for_each_child_node_scoped(dev, child) {
cfg = &st->channels[channel].cfg;
- ret = of_property_read_u32(child, "reg", &channel);
+ ret = fwnode_property_read_u32(child, "reg", &channel);
if (ret)
- goto err;
+ return ret;
- if (channel >= indio_dev->num_channels) {
- dev_err(indio_dev->dev.parent,
+ if (channel >= indio_dev->num_channels)
+ return dev_err_probe(dev, -EINVAL,
"Channel index >= number of channels\n");
- ret = -EINVAL;
- goto err;
- }
- ret = of_property_read_u32_array(child, "diff-channels",
- ain, 2);
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ ain, 2);
if (ret)
- goto err;
+ return ret;
st->channels[channel].nr = channel;
st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
AD7124_CHANNEL_AINM(ain[1]);
- cfg->bipolar = of_property_read_bool(child, "bipolar");
+ cfg->bipolar = fwnode_property_read_bool(child, "bipolar");
- ret = of_property_read_u32(child, "adi,reference-select", &tmp);
+ ret = fwnode_property_read_u32(child, "adi,reference-select", &tmp);
if (ret)
cfg->refsel = AD7124_INT_REF;
else
cfg->refsel = tmp;
- cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive");
- cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative");
+ cfg->buf_positive =
+ fwnode_property_read_bool(child, "adi,buffered-positive");
+ cfg->buf_negative =
+ fwnode_property_read_bool(child, "adi,buffered-negative");
chan[channel] = ad7124_channel_template;
chan[channel].address = channel;
@@ -880,10 +877,6 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
}
return 0;
-err:
- of_node_put(child);
-
- return ret;
}
static int ad7124_setup(struct ad7124_state *st)
@@ -943,9 +936,7 @@ static int ad7124_probe(struct spi_device *spi)
struct iio_dev *indio_dev;
int i, ret;
- info = of_device_get_match_data(&spi->dev);
- if (!info)
- info = (void *)spi_get_device_id(spi)->driver_data;
+ info = spi_get_device_match_data(spi);
if (!info)
return -ENODEV;
@@ -965,7 +956,7 @@ static int ad7124_probe(struct spi_device *spi)
if (ret < 0)
return ret;
- ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
+ ret = ad7124_parse_channel_config(indio_dev, &spi->dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
new file mode 100644
index 0000000000000..a7826bba0852a
--- /dev/null
+++ b/drivers/iio/adc/ad7173.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD717x family SPI ADC driver
+ *
+ * Supported devices:
+ * AD7172-2/AD7172-4/AD7173-8/AD7175-2
+ * AD7175-8/AD7176-2/AD7177-2
+ *
+ * Copyright (C) 2015, 2024 Analog Devices, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/container_of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define AD7173_REG_COMMS 0x00
+#define AD7173_REG_ADC_MODE 0x01
+#define AD7173_REG_INTERFACE_MODE 0x02
+#define AD7173_REG_CRC 0x03
+#define AD7173_REG_DATA 0x04
+#define AD7173_REG_GPIO 0x06
+#define AD7173_REG_ID 0x07
+#define AD7173_REG_CH(x) (0x10 + (x))
+#define AD7173_REG_SETUP(x) (0x20 + (x))
+#define AD7173_REG_FILTER(x) (0x28 + (x))
+#define AD7173_REG_OFFSET(x) (0x30 + (x))
+#define AD7173_REG_GAIN(x) (0x38 + (x))
+
+#define AD7173_RESET_LENGTH BITS_TO_BYTES(64)
+
+#define AD7173_CH_ENABLE BIT(15)
+#define AD7173_CH_SETUP_SEL_MASK GENMASK(14, 12)
+#define AD7173_CH_SETUP_AINPOS_MASK GENMASK(9, 5)
+#define AD7173_CH_SETUP_AINNEG_MASK GENMASK(4, 0)
+
+#define AD7173_CH_ADDRESS(pos, neg) \
+ (FIELD_PREP(AD7173_CH_SETUP_AINPOS_MASK, pos) | \
+ FIELD_PREP(AD7173_CH_SETUP_AINNEG_MASK, neg))
+#define AD7173_AIN_TEMP_POS 17
+#define AD7173_AIN_TEMP_NEG 18
+
+#define AD7172_2_ID 0x00d0
+#define AD7175_ID 0x0cd0
+#define AD7176_ID 0x0c90
+#define AD7175_2_ID 0x0cd0
+#define AD7172_4_ID 0x2050
+#define AD7173_ID 0x30d0
+#define AD7175_8_ID 0x3cd0
+#define AD7177_ID 0x4fd0
+#define AD7173_ID_MASK GENMASK(15, 4)
+
+#define AD7173_ADC_MODE_REF_EN BIT(15)
+#define AD7173_ADC_MODE_SING_CYC BIT(13)
+#define AD7173_ADC_MODE_MODE_MASK GENMASK(6, 4)
+#define AD7173_ADC_MODE_CLOCKSEL_MASK GENMASK(3, 2)
+#define AD7173_ADC_MODE_CLOCKSEL_INT 0x0
+#define AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT 0x1
+#define AD7173_ADC_MODE_CLOCKSEL_EXT 0x2
+#define AD7173_ADC_MODE_CLOCKSEL_XTAL 0x3
+
+#define AD7173_GPIO_PDSW BIT(14)
+#define AD7173_GPIO_OP_EN2_3 BIT(13)
+#define AD7173_GPIO_MUX_IO BIT(12)
+#define AD7173_GPIO_SYNC_EN BIT(11)
+#define AD7173_GPIO_ERR_EN BIT(10)
+#define AD7173_GPIO_ERR_DAT BIT(9)
+#define AD7173_GPIO_GP_DATA3 BIT(7)
+#define AD7173_GPIO_GP_DATA2 BIT(6)
+#define AD7173_GPIO_IP_EN1 BIT(5)
+#define AD7173_GPIO_IP_EN0 BIT(4)
+#define AD7173_GPIO_OP_EN1 BIT(3)
+#define AD7173_GPIO_OP_EN0 BIT(2)
+#define AD7173_GPIO_GP_DATA1 BIT(1)
+#define AD7173_GPIO_GP_DATA0 BIT(0)
+
+#define AD7173_GPO12_DATA(x) BIT((x) + 0)
+#define AD7173_GPO23_DATA(x) BIT((x) + 4)
+#define AD7173_GPO_DATA(x) ((x) < 2 ? AD7173_GPO12_DATA(x) : AD7173_GPO23_DATA(x))
+
+#define AD7173_INTERFACE_DATA_STAT BIT(6)
+#define AD7173_INTERFACE_DATA_STAT_EN(x) \
+ FIELD_PREP(AD7173_INTERFACE_DATA_STAT, x)
+
+#define AD7173_SETUP_BIPOLAR BIT(12)
+#define AD7173_SETUP_AREF_BUF_MASK GENMASK(11, 10)
+#define AD7173_SETUP_AIN_BUF_MASK GENMASK(9, 8)
+
+#define AD7173_SETUP_REF_SEL_MASK GENMASK(5, 4)
+#define AD7173_SETUP_REF_SEL_AVDD1_AVSS 0x3
+#define AD7173_SETUP_REF_SEL_INT_REF 0x2
+#define AD7173_SETUP_REF_SEL_EXT_REF2 0x1
+#define AD7173_SETUP_REF_SEL_EXT_REF 0x0
+#define AD7173_VOLTAGE_INT_REF_uV 2500000
+#define AD7173_TEMP_SENSIIVITY_uV_per_C 477
+#define AD7177_ODR_START_VALUE 0x07
+
+#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
+#define AD7173_MAX_CONFIGS 8
+
+enum ad7173_ids {
+ ID_AD7172_2,
+ ID_AD7172_4,
+ ID_AD7173_8,
+ ID_AD7175_2,
+ ID_AD7175_8,
+ ID_AD7176_2,
+ ID_AD7177_2,
+};
+
+struct ad7173_device_info {
+ const unsigned int *sinc5_data_rates;
+ unsigned int num_sinc5_data_rates;
+ unsigned int odr_start_value;
+ unsigned int num_channels;
+ unsigned int num_configs;
+ unsigned int num_inputs;
+ unsigned int clock;
+ unsigned int id;
+ char *name;
+ bool has_temp;
+ bool has_int_ref;
+ bool has_ref2;
+ u8 num_gpios;
+};
+
+struct ad7173_channel_config {
+ u8 cfg_slot;
+ bool live;
+
+ /* Following fields are used to compare equality. */
+ struct_group(config_props,
+ bool bipolar;
+ bool input_buf;
+ u8 odr;
+ u8 ref_sel;
+ );
+};
+
+struct ad7173_channel {
+ unsigned int chan_reg;
+ unsigned int ain;
+ struct ad7173_channel_config cfg;
+};
+
+struct ad7173_state {
+ struct ad_sigma_delta sd;
+ const struct ad7173_device_info *info;
+ struct ad7173_channel *channels;
+ struct regulator_bulk_data regulators[3];
+ unsigned int adc_mode;
+ unsigned int interface_mode;
+ unsigned int num_channels;
+ struct ida cfg_slots_status;
+ unsigned long long config_usage_counter;
+ unsigned long long *config_cnts;
+ struct clk *ext_clk;
+ struct clk_hw int_clk_hw;
+#if IS_ENABLED(CONFIG_GPIOLIB)
+ struct regmap *reg_gpiocon_regmap;
+ struct gpio_regmap *gpio_regmap;
+#endif
+};
+
+static const unsigned int ad7173_sinc5_data_rates[] = {
+ 6211000, 6211000, 6211000, 6211000, 6211000, 6211000, 5181000, 4444000, /* 0-7 */
+ 3115000, 2597000, 1007000, 503800, 381000, 200300, 100500, 59520, /* 8-15 */
+ 49680, 20010, 16333, 10000, 5000, 2500, 1250, /* 16-22 */
+};
+
+static const unsigned int ad7175_sinc5_data_rates[] = {
+ 50000000, 41667000, 31250000, 27778000, /* 0-3 */
+ 20833000, 17857000, 12500000, 10000000, /* 4-7 */
+ 5000000, 2500000, 1000000, 500000, /* 8-11 */
+ 397500, 200000, 100000, 59920, /* 12-15 */
+ 49960, 20000, 16666, 10000, /* 16-19 */
+ 5000, /* 20 */
+};
+
+static const struct ad7173_device_info ad7173_device_info[] = {
+ [ID_AD7172_2] = {
+ .name = "ad7172-2",
+ .id = AD7172_2_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_int_ref = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+ },
+ [ID_AD7172_4] = {
+ .id = AD7172_4_ID,
+ .num_inputs = 9,
+ .num_channels = 8,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = false,
+ .has_ref2 = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+ },
+ [ID_AD7173_8] = {
+ .name = "ad7173-8",
+ .id = AD7173_ID,
+ .num_inputs = 17,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = true,
+ .has_int_ref = true,
+ .has_ref2 = true,
+ .clock = 2 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7173_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
+ },
+ [ID_AD7175_2] = {
+ .name = "ad7175-2",
+ .id = AD7175_2_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+ [ID_AD7175_8] = {
+ .id = AD7175_8_ID,
+ .num_inputs = 17,
+ .num_channels = 16,
+ .num_configs = 8,
+ .num_gpios = 4,
+ .has_temp = true,
+ .has_int_ref = true,
+ .has_ref2 = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+ [ID_AD7176_2] = {
+ .name = "ad7176-2",
+ .id = AD7176_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = false,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+ [ID_AD7177_2] = {
+ .id = AD7177_ID,
+ .num_inputs = 5,
+ .num_channels = 4,
+ .num_configs = 4,
+ .num_gpios = 2,
+ .has_temp = true,
+ .has_int_ref = true,
+ .clock = 16 * HZ_PER_MHZ,
+ .odr_start_value = AD7177_ODR_START_VALUE,
+ .sinc5_data_rates = ad7175_sinc5_data_rates,
+ .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
+ },
+};
+
+static const char *const ad7173_ref_sel_str[] = {
+ [AD7173_SETUP_REF_SEL_EXT_REF] = "vref",
+ [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2",
+ [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss",
+ [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd",
+};
+
+static const char *const ad7173_clk_sel[] = {
+ "ext-clk", "xtal"
+};
+
+#if IS_ENABLED(CONFIG_GPIOLIB)
+
+static const struct regmap_range ad7173_range_gpio[] = {
+ regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO),
+};
+
+static const struct regmap_access_table ad7173_access_table = {
+ .yes_ranges = ad7173_range_gpio,
+ .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio),
+};
+
+static const struct regmap_config ad7173_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .rd_table = &ad7173_access_table,
+ .wr_table = &ad7173_access_table,
+ .read_flag_mask = BIT(6),
+};
+
+static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
+ unsigned int offset, unsigned int *reg,
+ unsigned int *mask)
+{
+ *mask = AD7173_GPO_DATA(offset);
+ *reg = base;
+ return 0;
+}
+
+static void ad7173_gpio_disable(void *data)
+{
+ struct ad7173_state *st = data;
+ unsigned int mask;
+
+ mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3;
+ regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, ~mask);
+}
+
+static int ad7173_gpio_init(struct ad7173_state *st)
+{
+ struct gpio_regmap_config gpio_regmap = {};
+ struct device *dev = &st->sd.spi->dev;
+ unsigned int mask;
+ int ret;
+
+ st->reg_gpiocon_regmap = devm_regmap_init_spi(st->sd.spi, &ad7173_regmap_config);
+ ret = PTR_ERR_OR_ZERO(st->reg_gpiocon_regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to init regmap\n");
+
+ mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3;
+ regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, mask);
+
+ ret = devm_add_action_or_reset(dev, ad7173_gpio_disable, st);
+ if (ret)
+ return ret;
+
+ gpio_regmap.parent = dev;
+ gpio_regmap.regmap = st->reg_gpiocon_regmap;
+ gpio_regmap.ngpio = st->info->num_gpios;
+ gpio_regmap.reg_set_base = AD7173_REG_GPIO;
+ gpio_regmap.reg_mask_xlate = ad7173_mask_xlate;
+
+ st->gpio_regmap = devm_gpio_regmap_register(dev, &gpio_regmap);
+ ret = PTR_ERR_OR_ZERO(st->gpio_regmap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to init gpio-regmap\n");
+
+ return 0;
+}
+#else
+static int ad7173_gpio_init(struct ad7173_state *st)
+{
+ return 0;
+}
+#endif /* CONFIG_GPIOLIB */
+
+static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7173_state, sd);
+}
+
+static struct ad7173_state *clk_hw_to_ad7173(struct clk_hw *hw)
+{
+ return container_of(hw, struct ad7173_state, int_clk_hw);
+}
+
+static void ad7173_ida_destroy(void *data)
+{
+ struct ad7173_state *st = data;
+
+ ida_destroy(&st->cfg_slots_status);
+}
+
+static void ad7173_reset_usage_cnts(struct ad7173_state *st)
+{
+ memset64(st->config_cnts, 0, st->info->num_configs);
+ st->config_usage_counter = 0;
+}
+
+static struct ad7173_channel_config *
+ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
+{
+ struct ad7173_channel_config *cfg_aux;
+ ptrdiff_t cmp_size;
+ int i;
+
+ cmp_size = sizeof_field(struct ad7173_channel_config, config_props);
+ for (i = 0; i < st->num_channels; i++) {
+ cfg_aux = &st->channels[i].cfg;
+
+ if (cfg_aux->live &&
+ !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size))
+ return cfg_aux;
+ }
+ return NULL;
+}
+
+/* Could be replaced with a generic LRU implementation */
+static int ad7173_free_config_slot_lru(struct ad7173_state *st)
+{
+ int i, lru_position = 0;
+
+ for (i = 1; i < st->info->num_configs; i++)
+ if (st->config_cnts[i] < st->config_cnts[lru_position])
+ lru_position = i;
+
+ for (i = 0; i < st->num_channels; i++)
+ if (st->channels[i].cfg.cfg_slot == lru_position)
+ st->channels[i].cfg.live = false;
+
+ ida_free(&st->cfg_slots_status, lru_position);
+ return ida_alloc(&st->cfg_slots_status, GFP_KERNEL);
+}
+
+/* Could be replaced with a generic LRU implementation */
+static int ad7173_load_config(struct ad7173_state *st,
+ struct ad7173_channel_config *cfg)
+{
+ unsigned int config;
+ int free_cfg_slot, ret;
+
+ free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0,
+ st->info->num_configs - 1, GFP_KERNEL);
+ if (free_cfg_slot < 0)
+ free_cfg_slot = ad7173_free_config_slot_lru(st);
+
+ cfg->cfg_slot = free_cfg_slot;
+ config = FIELD_PREP(AD7173_SETUP_REF_SEL_MASK, cfg->ref_sel);
+
+ if (cfg->bipolar)
+ config |= AD7173_SETUP_BIPOLAR;
+
+ if (cfg->input_buf)
+ config |= AD7173_SETUP_AIN_BUF_MASK;
+
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_SETUP(free_cfg_slot), 2, config);
+ if (ret)
+ return ret;
+
+ return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
+ AD7173_FILTER_ODR0_MASK & cfg->odr);
+}
+
+static int ad7173_config_channel(struct ad7173_state *st, int addr)
+{
+ struct ad7173_channel_config *cfg = &st->channels[addr].cfg;
+ struct ad7173_channel_config *live_cfg;
+ int ret;
+
+ if (!cfg->live) {
+ live_cfg = ad7173_find_live_config(st, cfg);
+ if (live_cfg) {
+ cfg->cfg_slot = live_cfg->cfg_slot;
+ } else {
+ ret = ad7173_load_config(st, cfg);
+ if (ret)
+ return ret;
+ cfg->live = true;
+ }
+ }
+
+ if (st->config_usage_counter == U64_MAX)
+ ad7173_reset_usage_cnts(st);
+
+ st->config_usage_counter++;
+ st->config_cnts[cfg->cfg_slot] = st->config_usage_counter;
+
+ return 0;
+}
+
+static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+ unsigned int val;
+ int ret;
+
+ ret = ad7173_config_channel(st, channel);
+ if (ret)
+ return ret;
+
+ val = AD7173_CH_ENABLE |
+ FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) |
+ st->channels[channel].ain;
+
+ return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val);
+}
+
+static int ad7173_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+
+ st->adc_mode &= ~AD7173_ADC_MODE_MODE_MASK;
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_MODE_MASK, mode);
+
+ return ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 2, st->adc_mode);
+}
+
+static int ad7173_append_status(struct ad_sigma_delta *sd, bool append)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+ unsigned int interface_mode = st->interface_mode;
+ int ret;
+
+ interface_mode |= AD7173_INTERFACE_DATA_STAT_EN(append);
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_INTERFACE_MODE, 2, interface_mode);
+ if (ret)
+ return ret;
+
+ st->interface_mode = interface_mode;
+
+ return 0;
+}
+
+static int ad7173_disable_all(struct ad_sigma_delta *sd)
+{
+ struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
+ int ret;
+ int i;
+
+ for (i = 0; i < st->num_channels; i++) {
+ ret = ad_sd_write_reg(sd, AD7173_REG_CH(i), 2, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct ad_sigma_delta_info ad7173_sigma_delta_info = {
+ .set_channel = ad7173_set_channel,
+ .append_status = ad7173_append_status,
+ .disable_all = ad7173_disable_all,
+ .set_mode = ad7173_set_mode,
+ .has_registers = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .status_ch_mask = GENMASK(3, 0),
+ .data_reg = AD7173_REG_DATA,
+};
+
+static int ad7173_setup(struct iio_dev *indio_dev)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = &st->sd.spi->dev;
+ u8 buf[AD7173_RESET_LENGTH];
+ unsigned int id;
+ int ret;
+
+ /* reset the serial interface */
+ memset(buf, 0xff, AD7173_RESET_LENGTH);
+ ret = spi_write_then_read(st->sd.spi, buf, sizeof(buf), NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* datasheet recommends a delay of at least 500us after reset */
+ fsleep(500);
+
+ ret = ad_sd_read_reg(&st->sd, AD7173_REG_ID, 2, &id);
+ if (ret)
+ return ret;
+
+ id &= AD7173_ID_MASK;
+ if (id != st->info->id)
+ dev_warn(dev, "Unexpected device id: 0x%04X, expected: 0x%04X\n",
+ id, st->info->id);
+
+ st->adc_mode |= AD7173_ADC_MODE_SING_CYC;
+ st->interface_mode = 0x0;
+
+ st->config_usage_counter = 0;
+ st->config_cnts = devm_kcalloc(dev, st->info->num_configs,
+ sizeof(*st->config_cnts), GFP_KERNEL);
+ if (!st->config_cnts)
+ return -ENOMEM;
+
+ /* All channels are enabled by default after a reset */
+ return ad7173_disable_all(&st->sd);
+}
+
+static unsigned int ad7173_get_ref_voltage_milli(struct ad7173_state *st,
+ u8 reference_select)
+{
+ int vref;
+
+ switch (reference_select) {
+ case AD7173_SETUP_REF_SEL_EXT_REF:
+ vref = regulator_get_voltage(st->regulators[0].consumer);
+ break;
+
+ case AD7173_SETUP_REF_SEL_EXT_REF2:
+ vref = regulator_get_voltage(st->regulators[1].consumer);
+ break;
+
+ case AD7173_SETUP_REF_SEL_INT_REF:
+ vref = AD7173_VOLTAGE_INT_REF_uV;
+ break;
+
+ case AD7173_SETUP_REF_SEL_AVDD1_AVSS:
+ vref = regulator_get_voltage(st->regulators[2].consumer);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (vref < 0)
+ return vref;
+
+ return vref / (MICRO / MILLI);
+}
+
+static int ad7173_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel *ch = &st->channels[chan->address];
+ unsigned int reg;
+ u64 temp;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ /* disable channel after single conversion */
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(chan->address), 2, 0);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ temp = AD7173_VOLTAGE_INT_REF_uV * MILLI;
+ temp /= AD7173_TEMP_SENSIIVITY_uV_per_C;
+ *val = temp;
+ *val2 = chan->scan_type.realbits;
+ } else {
+ *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel);
+ *val2 = chan->scan_type.realbits - !!(ch->cfg.bipolar);
+ }
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ /* 0 Kelvin -> raw sample */
+ temp = -ABSOLUTE_ZERO_MILLICELSIUS;
+ temp *= AD7173_TEMP_SENSIIVITY_uV_per_C;
+ temp <<= chan->scan_type.realbits;
+ temp = DIV_U64_ROUND_CLOSEST(temp,
+ AD7173_VOLTAGE_INT_REF_uV *
+ MILLI);
+ *val = -temp;
+ } else {
+ *val = -BIT(chan->scan_type.realbits - 1);
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ reg = st->channels[chan->address].cfg.odr;
+
+ *val = st->info->sinc5_data_rates[reg] / MILLI;
+ *val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7173_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct ad7173_channel_config *cfg;
+ unsigned int freq, i, reg;
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ freq = val * MILLI + val2 / MILLI;
+ for (i = st->info->odr_start_value; i < st->info->num_sinc5_data_rates - 1; i++)
+ if (freq >= st->info->sinc5_data_rates[i])
+ break;
+
+ cfg = &st->channels[chan->address].cfg;
+ cfg->odr = i;
+
+ if (!cfg->live)
+ break;
+
+ ret = ad_sd_read_reg(&st->sd, AD7173_REG_FILTER(cfg->cfg_slot), 2, &reg);
+ if (ret)
+ break;
+ reg &= ~AD7173_FILTER_ODR0_MASK;
+ reg |= FIELD_PREP(AD7173_FILTER_ODR0_MASK, i);
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(cfg->cfg_slot), 2, reg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+}
+
+static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ int i, ret;
+
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ if (test_bit(i, scan_mask))
+ ret = ad7173_set_channel(&st->sd, i);
+ else
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ u8 reg_size;
+
+ if (reg == AD7173_REG_COMMS)
+ reg_size = 1;
+ else if (reg == AD7173_REG_CRC || reg == AD7173_REG_DATA ||
+ reg >= AD7173_REG_OFFSET(0))
+ reg_size = 3;
+ else
+ reg_size = 2;
+
+ if (readval)
+ return ad_sd_read_reg(&st->sd, reg, reg_size, readval);
+
+ return ad_sd_write_reg(&st->sd, reg, reg_size, writeval);
+}
+
+static const struct iio_info ad7173_info = {
+ .read_raw = &ad7173_read_raw,
+ .write_raw = &ad7173_write_raw,
+ .debugfs_reg_access = &ad7173_debug_reg_access,
+ .validate_trigger = ad_sd_validate_trigger,
+ .update_scan_mode = ad7173_update_scan_mode,
+};
+
+static const struct iio_chan_spec ad7173_channel_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+};
+
+static const struct iio_chan_spec ad7173_temp_iio_channel_template = {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = AD7173_AIN_TEMP_POS,
+ .channel2 = AD7173_AIN_TEMP_NEG,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+};
+
+static void ad7173_disable_regulators(void *data)
+{
+ struct ad7173_state *st = data;
+
+ regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
+}
+
+static void ad7173_clk_disable_unprepare(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static unsigned long ad7173_sel_clk(struct ad7173_state *st,
+ unsigned int clk_sel)
+{
+ int ret;
+
+ st->adc_mode &= ~AD7173_ADC_MODE_CLOCKSEL_MASK;
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, clk_sel);
+ ret = ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 0x2, st->adc_mode);
+
+ return ret;
+}
+
+static unsigned long ad7173_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+
+ return st->info->clock / HZ_PER_KHZ;
+}
+
+static int ad7173_clk_output_is_enabled(struct clk_hw *hw)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+ u32 clk_sel;
+
+ clk_sel = FIELD_GET(AD7173_ADC_MODE_CLOCKSEL_MASK, st->adc_mode);
+ return clk_sel == AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT;
+}
+
+static int ad7173_clk_output_prepare(struct clk_hw *hw)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+
+ return ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT);
+}
+
+static void ad7173_clk_output_unprepare(struct clk_hw *hw)
+{
+ struct ad7173_state *st = clk_hw_to_ad7173(hw);
+
+ ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT);
+}
+
+static const struct clk_ops ad7173_int_clk_ops = {
+ .recalc_rate = ad7173_clk_recalc_rate,
+ .is_enabled = ad7173_clk_output_is_enabled,
+ .prepare = ad7173_clk_output_prepare,
+ .unprepare = ad7173_clk_output_unprepare,
+};
+
+static int ad7173_register_clk_provider(struct iio_dev *indio_dev)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct clk_init_data init = {};
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_COMMON_CLK))
+ return 0;
+
+ init.name = fwnode_get_name(fwnode);
+ init.ops = &ad7173_int_clk_ops;
+
+ st->int_clk_hw.init = &init;
+ ret = devm_clk_hw_register(dev, &st->int_clk_hw);
+ if (ret)
+ return ret;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &st->int_clk_hw);
+}
+
+static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
+{
+ struct ad7173_channel *chans_st_arr, *chan_st_priv;
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ struct iio_chan_spec *chan_arr, *chan;
+ unsigned int ain[2], chan_index = 0;
+ int ref_sel, ret;
+
+ chan_arr = devm_kcalloc(dev, sizeof(*indio_dev->channels),
+ st->num_channels, GFP_KERNEL);
+ if (!chan_arr)
+ return -ENOMEM;
+
+ chans_st_arr = devm_kcalloc(dev, st->num_channels, sizeof(*st->channels),
+ GFP_KERNEL);
+ if (!chans_st_arr)
+ return -ENOMEM;
+
+ indio_dev->channels = chan_arr;
+ st->channels = chans_st_arr;
+
+ if (st->info->has_temp) {
+ chan_arr[chan_index] = ad7173_temp_iio_channel_template;
+ chan_st_priv = &chans_st_arr[chan_index];
+ chan_st_priv->ain =
+ AD7173_CH_ADDRESS(chan_arr[chan_index].channel,
+ chan_arr[chan_index].channel2);
+ chan_st_priv->cfg.bipolar = false;
+ chan_st_priv->cfg.input_buf = true;
+ chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
+ st->adc_mode |= AD7173_ADC_MODE_REF_EN;
+
+ chan_index++;
+ }
+
+ device_for_each_child_node_scoped(dev, child) {
+ chan = &chan_arr[chan_index];
+ chan_st_priv = &chans_st_arr[chan_index];
+ ret = fwnode_property_read_u32_array(child, "diff-channels",
+ ain, ARRAY_SIZE(ain));
+ if (ret)
+ return ret;
+
+ if (ain[0] >= st->info->num_inputs ||
+ ain[1] >= st->info->num_inputs)
+ return dev_err_probe(dev, -EINVAL,
+ "Input pin number out of range for pair (%d %d).\n",
+ ain[0], ain[1]);
+
+ ret = fwnode_property_match_property_string(child,
+ "adi,reference-select",
+ ad7173_ref_sel_str,
+ ARRAY_SIZE(ad7173_ref_sel_str));
+ if (ret < 0)
+ ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
+ else
+ ref_sel = ret;
+
+ if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF &&
+ !st->info->has_int_ref)
+ return dev_err_probe(dev, -EINVAL,
+ "Internal reference is not available on current model.\n");
+
+ if (ref_sel == AD7173_SETUP_REF_SEL_EXT_REF2 && !st->info->has_ref2)
+ return dev_err_probe(dev, -EINVAL,
+ "External reference 2 is not available on current model.\n");
+
+ ret = ad7173_get_ref_voltage_milli(st, ref_sel);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Cannot use reference %u\n", ref_sel);
+
+ if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF)
+ st->adc_mode |= AD7173_ADC_MODE_REF_EN;
+ chan_st_priv->cfg.ref_sel = ref_sel;
+
+ *chan = ad7173_channel_template;
+ chan->address = chan_index;
+ chan->scan_index = chan_index;
+ chan->channel = ain[0];
+ chan->channel2 = ain[1];
+ chan->differential = true;
+
+ chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]);
+ chan_st_priv->chan_reg = chan_index;
+ chan_st_priv->cfg.input_buf = true;
+ chan_st_priv->cfg.odr = 0;
+
+ chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
+ if (chan_st_priv->cfg.bipolar)
+ chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET);
+
+ chan_index++;
+ }
+ return 0;
+}
+
+static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
+{
+ struct ad7173_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ unsigned int num_channels;
+ int ret;
+
+ st->regulators[0].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF];
+ st->regulators[1].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF2];
+ st->regulators[2].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_AVDD1_AVSS];
+
+ /*
+ * If a regulator is not available, it will be set to a dummy regulator.
+ * Each channel reference is checked with regulator_get_voltage() before
+ * setting attributes so if any channel uses a dummy supply the driver
+ * probe will fail.
+ */
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators),
+ st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add regulators disable action\n");
+
+ ret = device_property_match_property_string(dev, "clock-names",
+ ad7173_clk_sel,
+ ARRAY_SIZE(ad7173_clk_sel));
+ if (ret < 0) {
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK,
+ AD7173_ADC_MODE_CLOCKSEL_INT);
+ ad7173_register_clk_provider(indio_dev);
+ } else {
+ st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK,
+ AD7173_ADC_MODE_CLOCKSEL_EXT + ret);
+ st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]);
+ if (IS_ERR(st->ext_clk))
+ return dev_err_probe(dev, PTR_ERR(st->ext_clk),
+ "Failed to get external clock\n");
+
+ ret = clk_prepare_enable(st->ext_clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable external clock\n");
+
+ ret = devm_add_action_or_reset(dev, ad7173_clk_disable_unprepare,
+ st->ext_clk);
+ if (ret)
+ return ret;
+ }
+
+ ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n");
+
+ ad7173_sigma_delta_info.irq_line = ret;
+
+ num_channels = device_get_child_node_count(dev);
+
+ if (st->info->has_temp)
+ num_channels++;
+
+ if (num_channels == 0)
+ return dev_err_probe(dev, -ENODATA, "No channels specified\n");
+ indio_dev->num_channels = num_channels;
+ st->num_channels = num_channels;
+
+ return ad7173_fw_parse_channel_config(indio_dev);
+}
+
+static int ad7173_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct ad7173_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -ENODEV;
+
+ ida_init(&st->cfg_slots_status);
+ ret = devm_add_action_or_reset(dev, ad7173_ida_destroy, st);
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7173_info;
+
+ spi->mode = SPI_MODE_3;
+ spi_setup(spi);
+
+ ad7173_sigma_delta_info.num_slots = st->info->num_configs;
+ ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7173_sigma_delta_info);
+ if (ret)
+ return ret;
+
+ ret = ad7173_fw_parse_device_config(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7173_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB))
+ return ad7173_gpio_init(st);
+
+ return 0;
+}
+
+static const struct of_device_id ad7173_of_match[] = {
+ { .compatible = "adi,ad7172-2",
+ .data = &ad7173_device_info[ID_AD7172_2]},
+ { .compatible = "adi,ad7172-4",
+ .data = &ad7173_device_info[ID_AD7172_4]},
+ { .compatible = "adi,ad7173-8",
+ .data = &ad7173_device_info[ID_AD7173_8]},
+ { .compatible = "adi,ad7175-2",
+ .data = &ad7173_device_info[ID_AD7175_2]},
+ { .compatible = "adi,ad7175-8",
+ .data = &ad7173_device_info[ID_AD7175_8]},
+ { .compatible = "adi,ad7176-2",
+ .data = &ad7173_device_info[ID_AD7176_2]},
+ { .compatible = "adi,ad7177-2",
+ .data = &ad7173_device_info[ID_AD7177_2]},
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7173_of_match);
+
+static const struct spi_device_id ad7173_id_table[] = {
+ { "ad7172-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_2]},
+ { "ad7172-4", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_4]},
+ { "ad7173-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7173_8]},
+ { "ad7175-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7175_2]},
+ { "ad7175-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7175_8]},
+ { "ad7176-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7176_2]},
+ { "ad7177-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7177_2]},
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7173_id_table);
+
+static struct spi_driver ad7173_driver = {
+ .driver = {
+ .name = "ad7173",
+ .of_match_table = ad7173_of_match,
+ },
+ .probe = ad7173_probe,
+ .id_table = ad7173_id_table,
+};
+module_spi_driver(ad7173_driver);
+
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafo.de>");
+MODULE_AUTHOR("Dumitru Ceclan <dumitru.ceclan@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7172/AD7173/AD7175/AD7176 ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c
index adc3cbe92d6e6..7bcc7e2aa2a20 100644
--- a/drivers/iio/adc/ad7192.c
+++ b/drivers/iio/adc/ad7192.c
@@ -17,7 +17,9 @@
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -364,19 +366,19 @@ static inline bool ad7192_valid_external_frequency(u32 freq)
freq <= AD7192_EXT_FREQ_MHZ_MAX);
}
-static int ad7192_of_clock_select(struct ad7192_state *st)
+static int ad7192_clock_select(struct ad7192_state *st)
{
- struct device_node *np = st->sd.spi->dev.of_node;
+ struct device *dev = &st->sd.spi->dev;
unsigned int clock_sel;
clock_sel = AD7192_CLK_INT;
/* use internal clock */
if (!st->mclk) {
- if (of_property_read_bool(np, "adi,int-clock-output-enable"))
+ if (device_property_read_bool(dev, "adi,int-clock-output-enable"))
clock_sel = AD7192_CLK_INT_CO;
} else {
- if (of_property_read_bool(np, "adi,clock-xtal"))
+ if (device_property_read_bool(dev, "adi,clock-xtal"))
clock_sel = AD7192_CLK_EXT_MCLK1_2;
else
clock_sel = AD7192_CLK_EXT_MCLK2;
@@ -385,7 +387,7 @@ static int ad7192_of_clock_select(struct ad7192_state *st)
return clock_sel;
}
-static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
+static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
{
struct ad7192_state *st = iio_priv(indio_dev);
bool rej60_en, refin2_en;
@@ -407,7 +409,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
id = FIELD_GET(AD7192_ID_MASK, id);
if (id != st->chip_info->chip_id)
- dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n",
+ dev_warn(dev, "device ID query failed (0x%X != 0x%X)\n",
id, st->chip_info->chip_id);
st->mode = FIELD_PREP(AD7192_MODE_SEL_MASK, AD7192_MODE_IDLE) |
@@ -416,30 +418,30 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
st->conf = FIELD_PREP(AD7192_CONF_GAIN_MASK, 0);
- rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable");
+ rej60_en = device_property_read_bool(dev, "adi,rejection-60-Hz-enable");
if (rej60_en)
st->mode |= AD7192_MODE_REJ60;
- refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable");
+ refin2_en = device_property_read_bool(dev, "adi,refin2-pins-enable");
if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195)
st->conf |= AD7192_CONF_REFSEL;
st->conf &= ~AD7192_CONF_CHOP;
- buf_en = of_property_read_bool(np, "adi,buffer-enable");
+ buf_en = device_property_read_bool(dev, "adi,buffer-enable");
if (buf_en)
st->conf |= AD7192_CONF_BUF;
- bipolar = of_property_read_bool(np, "bipolar");
+ bipolar = device_property_read_bool(dev, "bipolar");
if (!bipolar)
st->conf |= AD7192_CONF_UNIPOLAR;
- burnout_curr_en = of_property_read_bool(np,
- "adi,burnout-currents-enable");
+ burnout_curr_en = device_property_read_bool(dev,
+ "adi,burnout-currents-enable");
if (burnout_curr_en && buf_en) {
st->conf |= AD7192_CONF_BURN;
} else if (burnout_curr_en) {
- dev_warn(&st->sd.spi->dev,
+ dev_warn(dev,
"Can't enable burnout currents: see CHOP or buffer\n");
}
@@ -1117,9 +1119,7 @@ static int ad7192_probe(struct spi_device *spi)
}
st->int_vref_mv = ret / 1000;
- st->chip_info = of_device_get_match_data(&spi->dev);
- if (!st->chip_info)
- st->chip_info = (void *)spi_get_device_id(spi)->driver_data;
+ st->chip_info = spi_get_device_match_data(spi);
indio_dev->name = st->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
@@ -1140,7 +1140,7 @@ static int ad7192_probe(struct spi_device *spi)
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
- st->clock_sel = ad7192_of_clock_select(st);
+ st->clock_sel = ad7192_clock_select(st);
if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
st->clock_sel == AD7192_CLK_EXT_MCLK2) {
@@ -1152,7 +1152,7 @@ static int ad7192_probe(struct spi_device *spi)
}
}
- ret = ad7192_setup(indio_dev, spi->dev.of_node);
+ ret = ad7192_setup(indio_dev, &spi->dev);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c
index cccacec5db6d2..6aadd14f459df 100644
--- a/drivers/iio/adc/ad7292.c
+++ b/drivers/iio/adc/ad7292.c
@@ -8,7 +8,8 @@
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
@@ -260,7 +261,6 @@ static int ad7292_probe(struct spi_device *spi)
{
struct ad7292_state *st;
struct iio_dev *indio_dev;
- struct device_node *child;
bool diff_channels = false;
int ret;
@@ -305,12 +305,11 @@ static int ad7292_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ad7292_info;
- for_each_available_child_of_node(spi->dev.of_node, child) {
- diff_channels = of_property_read_bool(child, "diff-channels");
- if (diff_channels) {
- of_node_put(child);
+ device_for_each_child_node_scoped(&spi->dev, child) {
+ diff_channels = fwnode_property_read_bool(child,
+ "diff-channels");
+ if (diff_channels)
break;
- }
}
if (diff_channels) {
diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c
new file mode 100644
index 0000000000000..4af574ffa8641
--- /dev/null
+++ b/drivers/iio/adc/ad7944.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD7944/85/86 PulSAR ADC family driver.
+ *
+ * Copyright 2024 Analog Devices, Inc.
+ * Copyright 2024 BayLibre, SAS
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/string_helpers.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define AD7944_INTERNAL_REF_MV 4096
+
+struct ad7944_timing_spec {
+ /* Normal mode max conversion time (t_{CONV}). */
+ unsigned int conv_ns;
+ /* TURBO mode max conversion time (t_{CONV}). */
+ unsigned int turbo_conv_ns;
+};
+
+enum ad7944_spi_mode {
+ /* datasheet calls this "4-wire mode" */
+ AD7944_SPI_MODE_DEFAULT,
+ /* datasheet calls this "3-wire mode" (not related to SPI_3WIRE!) */
+ AD7944_SPI_MODE_SINGLE,
+ /* datasheet calls this "chain mode" */
+ AD7944_SPI_MODE_CHAIN,
+};
+
+/* maps adi,spi-mode property value to enum */
+static const char * const ad7944_spi_modes[] = {
+ [AD7944_SPI_MODE_DEFAULT] = "",
+ [AD7944_SPI_MODE_SINGLE] = "single",
+ [AD7944_SPI_MODE_CHAIN] = "chain",
+};
+
+struct ad7944_adc {
+ struct spi_device *spi;
+ enum ad7944_spi_mode spi_mode;
+ struct spi_transfer xfers[3];
+ struct spi_message msg;
+ /* Chip-specific timing specifications. */
+ const struct ad7944_timing_spec *timing_spec;
+ /* GPIO connected to CNV pin. */
+ struct gpio_desc *cnv;
+ /* Optional GPIO to enable turbo mode. */
+ struct gpio_desc *turbo;
+ /* Indicates TURBO is hard-wired to be always enabled. */
+ bool always_turbo;
+ /* Reference voltage (millivolts). */
+ unsigned int ref_mv;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ struct {
+ union {
+ u16 u16;
+ u32 u32;
+ } raw;
+ u64 timestamp __aligned(8);
+ } sample __aligned(IIO_DMA_MINALIGN);
+};
+
+/* quite time before CNV rising edge */
+#define T_QUIET_NS 20
+
+static const struct ad7944_timing_spec ad7944_timing_spec = {
+ .conv_ns = 420,
+ .turbo_conv_ns = 320,
+};
+
+static const struct ad7944_timing_spec ad7986_timing_spec = {
+ .conv_ns = 500,
+ .turbo_conv_ns = 400,
+};
+
+struct ad7944_chip_info {
+ const char *name;
+ const struct ad7944_timing_spec *timing_spec;
+ const struct iio_chan_spec channels[2];
+};
+
+/*
+ * AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip
+ * @_name: The name of the chip
+ * @_ts: The timing specification for the chip
+ * @_bits: The number of bits in the conversion result
+ * @_diff: Whether the chip is true differential or not
+ */
+#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \
+static const struct ad7944_chip_info _name##_chip_info = { \
+ .name = #_name, \
+ .timing_spec = &_ts##_timing_spec, \
+ .channels = { \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .differential = _diff, \
+ .channel = 0, \
+ .channel2 = _diff ? 1 : 0, \
+ .scan_index = 0, \
+ .scan_type.sign = _diff ? 's' : 'u', \
+ .scan_type.realbits = _bits, \
+ .scan_type.storagebits = _bits > 16 ? 32 : 16, \
+ .scan_type.endianness = IIO_CPU, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
+ | BIT(IIO_CHAN_INFO_SCALE), \
+ }, \
+ IIO_CHAN_SOFT_TIMESTAMP(1), \
+ }, \
+}
+
+/* pseudo-differential with ground sense */
+AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0);
+AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0);
+/* fully differential */
+AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
+
+static void ad7944_unoptimize_msg(void *msg)
+{
+ spi_unoptimize_message(msg);
+}
+
+static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
+ : adc->timing_spec->conv_ns;
+ struct spi_transfer *xfers = adc->xfers;
+ int ret;
+
+ /*
+ * NB: can get better performance from some SPI controllers if we use
+ * the same bits_per_word in every transfer.
+ */
+ xfers[0].bits_per_word = chan->scan_type.realbits;
+ /*
+ * CS is tied to CNV and we need a low to high transition to start the
+ * conversion, so place CNV low for t_QUIET to prepare for this.
+ */
+ xfers[0].delay.value = T_QUIET_NS;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ /*
+ * CS has to be high for full conversion time to avoid triggering the
+ * busy indication.
+ */
+ xfers[1].cs_off = 1;
+ xfers[1].delay.value = t_conv_ns;
+ xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
+ xfers[1].bits_per_word = chan->scan_type.realbits;
+
+ /* Then we can read the data during the acquisition phase */
+ xfers[2].rx_buf = &adc->sample.raw;
+ xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[2].bits_per_word = chan->scan_type.realbits;
+
+ spi_message_init_with_transfers(&adc->msg, xfers, 3);
+
+ ret = spi_optimize_message(adc->spi, &adc->msg);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
+}
+
+static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
+ : adc->timing_spec->conv_ns;
+ struct spi_transfer *xfers = adc->xfers;
+ int ret;
+
+ /*
+ * NB: can get better performance from some SPI controllers if we use
+ * the same bits_per_word in every transfer.
+ */
+ xfers[0].bits_per_word = chan->scan_type.realbits;
+ /*
+ * CS has to be high for full conversion time to avoid triggering the
+ * busy indication.
+ */
+ xfers[0].cs_off = 1;
+ xfers[0].delay.value = t_conv_ns;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].rx_buf = &adc->sample.raw;
+ xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
+ xfers[1].bits_per_word = chan->scan_type.realbits;
+
+ spi_message_init_with_transfers(&adc->msg, xfers, 2);
+
+ ret = spi_optimize_message(adc->spi, &adc->msg);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
+}
+
+/**
+ * ad7944_convert_and_acquire - Perform a single conversion and acquisition
+ * @adc: The ADC device structure
+ * @chan: The channel specification
+ * Return: 0 on success, a negative error code on failure
+ *
+ * Perform a conversion and acquisition of a single sample using the
+ * pre-optimized adc->msg.
+ *
+ * Upon successful return adc->sample.raw will contain the conversion result.
+ */
+static int ad7944_convert_and_acquire(struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan)
+{
+ int ret;
+
+ /*
+ * In 4-wire mode, the CNV line is held high for the entire conversion
+ * and acquisition process. In other modes adc->cnv is NULL and is
+ * ignored (CS is wired to CNV in those cases).
+ */
+ gpiod_set_value_cansleep(adc->cnv, 1);
+ ret = spi_sync(adc->spi, &adc->msg);
+ gpiod_set_value_cansleep(adc->cnv, 0);
+
+ return ret;
+}
+
+static int ad7944_single_conversion(struct ad7944_adc *adc,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ int ret;
+
+ ret = ad7944_convert_and_acquire(adc, chan);
+ if (ret)
+ return ret;
+
+ if (chan->scan_type.storagebits > 16)
+ *val = adc->sample.raw.u32;
+ else
+ *val = adc->sample.raw.u16;
+
+ if (chan->scan_type.sign == 's')
+ *val = sign_extend32(*val, chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+}
+
+static int ad7944_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7944_single_conversion(adc, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = adc->ref_mv;
+
+ if (chan->scan_type.sign == 's')
+ *val2 = chan->scan_type.realbits - 1;
+ else
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7944_iio_info = {
+ .read_raw = &ad7944_read_raw,
+};
+
+static irqreturn_t ad7944_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7944_adc *adc = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad7944_convert_and_acquire(adc, &indio_dev->channels[0]);
+ if (ret)
+ goto out;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &adc->sample.raw,
+ pf->timestamp);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const char * const ad7944_power_supplies[] = {
+ "avdd", "dvdd", "bvdd", "vio"
+};
+
+static void ad7944_ref_disable(void *ref)
+{
+ regulator_disable(ref);
+}
+
+static int ad7944_probe(struct spi_device *spi)
+{
+ const struct ad7944_chip_info *chip_info;
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad7944_adc *adc;
+ bool have_refin = false;
+ struct regulator *ref;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ chip_info = spi_get_device_match_data(spi);
+ if (!chip_info)
+ return dev_err_probe(dev, -EINVAL, "no chip info\n");
+
+ adc->timing_spec = chip_info->timing_spec;
+
+ ret = device_property_match_property_string(dev, "adi,spi-mode",
+ ad7944_spi_modes,
+ ARRAY_SIZE(ad7944_spi_modes));
+ /* absence of adi,spi-mode property means default mode */
+ if (ret == -EINVAL)
+ adc->spi_mode = AD7944_SPI_MODE_DEFAULT;
+ else if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "getting adi,spi-mode property failed\n");
+ else
+ adc->spi_mode = ret;
+
+ /*
+ * Some chips use unusual word sizes, so check now instead of waiting
+ * for the first xfer.
+ */
+ if (!spi_is_bpw_supported(spi, chip_info->channels[0].scan_type.realbits))
+ return dev_err_probe(dev, -EINVAL,
+ "SPI host does not support %d bits per word\n",
+ chip_info->channels[0].scan_type.realbits);
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(ad7944_power_supplies),
+ ad7944_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable supplies\n");
+
+ /*
+ * Sort out what is being used for the reference voltage. Options are:
+ * - internal reference: neither REF or REFIN is connected
+ * - internal reference with external buffer: REF not connected, REFIN
+ * is connected
+ * - external reference: REF is connected, REFIN is not connected
+ */
+
+ ref = devm_regulator_get_optional(dev, "ref");
+ if (IS_ERR(ref)) {
+ if (PTR_ERR(ref) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(ref),
+ "failed to get REF supply\n");
+
+ ref = NULL;
+ }
+
+ ret = devm_regulator_get_enable_optional(dev, "refin");
+ if (ret == 0)
+ have_refin = true;
+ else if (ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "failed to get and enable REFIN supply\n");
+
+ if (have_refin && ref)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot have both refin and ref supplies\n");
+
+ if (ref) {
+ ret = regulator_enable(ref);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable REF supply\n");
+
+ ret = devm_add_action_or_reset(dev, ad7944_ref_disable, ref);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(ref);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get REF voltage\n");
+
+ /* external reference */
+ adc->ref_mv = ret / 1000;
+ } else {
+ /* internal reference */
+ adc->ref_mv = AD7944_INTERNAL_REF_MV;
+ }
+
+ adc->cnv = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(adc->cnv))
+ return dev_err_probe(dev, PTR_ERR(adc->cnv),
+ "failed to get CNV GPIO\n");
+
+ if (!adc->cnv && adc->spi_mode == AD7944_SPI_MODE_DEFAULT)
+ return dev_err_probe(&spi->dev, -EINVAL, "CNV GPIO is required\n");
+ if (adc->cnv && adc->spi_mode != AD7944_SPI_MODE_DEFAULT)
+ return dev_err_probe(&spi->dev, -EINVAL,
+ "CNV GPIO in single and chain mode is not currently supported\n");
+
+ adc->turbo = devm_gpiod_get_optional(dev, "turbo", GPIOD_OUT_LOW);
+ if (IS_ERR(adc->turbo))
+ return dev_err_probe(dev, PTR_ERR(adc->turbo),
+ "failed to get TURBO GPIO\n");
+
+ adc->always_turbo = device_property_present(dev, "adi,always-turbo");
+
+ if (adc->turbo && adc->always_turbo)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot have both turbo-gpios and adi,always-turbo\n");
+
+ if (adc->spi_mode == AD7944_SPI_MODE_CHAIN && adc->always_turbo)
+ return dev_err_probe(dev, -EINVAL,
+ "cannot have both chain mode and always turbo\n");
+
+ switch (adc->spi_mode) {
+ case AD7944_SPI_MODE_DEFAULT:
+ ret = ad7944_4wire_mode_init_msg(dev, adc, &chip_info->channels[0]);
+ if (ret)
+ return ret;
+
+ break;
+ case AD7944_SPI_MODE_SINGLE:
+ ret = ad7944_3wire_cs_mode_init_msg(dev, adc, &chip_info->channels[0]);
+ if (ret)
+ return ret;
+
+ break;
+ case AD7944_SPI_MODE_CHAIN:
+ return dev_err_probe(dev, -EINVAL, "chain mode is not implemented\n");
+ }
+
+ indio_dev->name = chip_info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7944_iio_info;
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ ad7944_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ad7944_of_match[] = {
+ { .compatible = "adi,ad7944", .data = &ad7944_chip_info },
+ { .compatible = "adi,ad7985", .data = &ad7985_chip_info },
+ { .compatible = "adi,ad7986", .data = &ad7986_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7944_of_match);
+
+static const struct spi_device_id ad7944_spi_id[] = {
+ { "ad7944", (kernel_ulong_t)&ad7944_chip_info },
+ { "ad7985", (kernel_ulong_t)&ad7985_chip_info },
+ { "ad7986", (kernel_ulong_t)&ad7986_chip_info },
+ { }
+
+};
+MODULE_DEVICE_TABLE(spi, ad7944_spi_id);
+
+static struct spi_driver ad7944_driver = {
+ .driver = {
+ .name = "ad7944",
+ .of_match_table = ad7944_of_match,
+ },
+ .probe = ad7944_probe,
+ .id_table = ad7944_spi_id,
+};
+module_spi_driver(ad7944_driver);
+
+MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
+MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index a602429cdde4e..97a05f325df7c 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -222,11 +222,11 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
goto out;
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
if (timeout == 0) {
sigma_delta->irq_dis = true;
- disable_irq_nosync(sigma_delta->spi->irq);
+ disable_irq_nosync(sigma_delta->irq_line);
ret = -EIO;
} else {
ret = 0;
@@ -295,7 +295,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
ret = wait_for_completion_interruptible_timeout(
&sigma_delta->completion, HZ);
@@ -315,7 +315,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
out:
if (!sigma_delta->irq_dis) {
- disable_irq_nosync(sigma_delta->spi->irq);
+ disable_irq_nosync(sigma_delta->irq_line);
sigma_delta->irq_dis = true;
}
@@ -396,7 +396,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
goto err_unlock;
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
return 0;
@@ -414,7 +414,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
wait_for_completion_timeout(&sigma_delta->completion, HZ);
if (!sigma_delta->irq_dis) {
- disable_irq_nosync(sigma_delta->spi->irq);
+ disable_irq_nosync(sigma_delta->irq_line);
sigma_delta->irq_dis = true;
}
@@ -516,7 +516,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
irq_handled:
iio_trigger_notify_done(indio_dev->trig);
sigma_delta->irq_dis = false;
- enable_irq(sigma_delta->spi->irq);
+ enable_irq(sigma_delta->irq_line);
return IRQ_HANDLED;
}
@@ -587,13 +587,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de
sigma_delta->irq_dis = true;
/* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */
- irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY);
+ irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY);
/* Allow overwriting the flags from firmware */
if (!irq_flags)
irq_flags = sigma_delta->info->irq_flags;
- ret = devm_request_irq(dev, sigma_delta->spi->irq,
+ ret = devm_request_irq(dev, sigma_delta->irq_line,
ad_sd_data_rdy_trig_poll,
irq_flags | IRQF_NO_AUTOEN,
indio_dev->name,
@@ -673,6 +673,11 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
}
}
+ if (info->irq_line)
+ sigma_delta->irq_line = info->irq_line;
+ else
+ sigma_delta->irq_line = spi->irq;
+
iio_device_set_drvdata(indio_dev, sigma_delta);
return 0;
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 4156639b3c8bd..9444b0c5a93c6 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -124,26 +124,12 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
struct iio_dev *indio_dev)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
- struct iio_buffer *buffer;
const char *dma_name;
- int ret;
if (device_property_read_string(st->dev, "dma-names", &dma_name))
dma_name = "rx";
- buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
- if (IS_ERR(buffer)) {
- dev_err(st->dev, "Could not get DMA buffer, %ld\n",
- PTR_ERR(buffer));
- return ERR_CAST(buffer);
- }
-
- indio_dev->modes |= INDIO_BUFFER_HARDWARE;
- ret = iio_device_attach_buffer(indio_dev, buffer);
- if (ret)
- return ERR_PTR(ret);
-
- return buffer;
+ return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name);
}
static void axi_adc_free_buffer(struct iio_backend *back,
@@ -207,9 +193,9 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- if (*expected_ver > ver) {
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
dev_err(&pdev->dev,
- "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
ADI_AXI_PCORE_VER_MINOR(*expected_ver),
ADI_AXI_PCORE_VER_PATCH(*expected_ver),
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index 68c813de0605b..394396b916309 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -12,8 +12,9 @@
#include <linux/interrupt.h>
#include <linux/mfd/imx25-tsadc.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -198,8 +199,6 @@ static int mx25_gcq_ext_regulator_setup(struct device *dev,
static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
struct mx25_gcq_priv *priv)
{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *child;
struct device *dev = &pdev->dev;
int ret, i;
@@ -216,37 +215,30 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
MX25_ADCQ_CFG_IN(i) |
MX25_ADCQ_CFG_REFN_NGND2);
- for_each_child_of_node(np, child) {
+ device_for_each_child_node_scoped(dev, child) {
u32 reg;
u32 refp = MX25_ADCQ_CFG_REFP_INT;
u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
- ret = of_property_read_u32(child, "reg", &reg);
- if (ret) {
- dev_err(dev, "Failed to get reg property\n");
- of_node_put(child);
- return ret;
- }
+ ret = fwnode_property_read_u32(child, "reg", &reg);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get reg property\n");
- if (reg >= MX25_NUM_CFGS) {
- dev_err(dev,
+ if (reg >= MX25_NUM_CFGS)
+ return dev_err_probe(dev, -EINVAL,
"reg value is greater than the number of available configuration registers\n");
- of_node_put(child);
- return -EINVAL;
- }
- of_property_read_u32(child, "fsl,adc-refp", &refp);
- of_property_read_u32(child, "fsl,adc-refn", &refn);
+ fwnode_property_read_u32(child, "fsl,adc-refp", &refp);
+ fwnode_property_read_u32(child, "fsl,adc-refn", &refn);
switch (refp) {
case MX25_ADC_REFP_EXT:
case MX25_ADC_REFP_XP:
case MX25_ADC_REFP_YP:
ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp);
- if (ret) {
- of_node_put(child);
+ if (ret)
return ret;
- }
priv->channel_vref_mv[reg] =
regulator_get_voltage(priv->vref[refp]);
/* Conversion from uV to mV */
@@ -256,9 +248,8 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
priv->channel_vref_mv[reg] = 2500;
break;
default:
- dev_err(dev, "Invalid positive reference %d\n", refp);
- of_node_put(child);
- return -EINVAL;
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid positive reference %d\n", refp);
}
/*
@@ -268,16 +259,13 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
refp = MX25_ADCQ_CFG_REFP(refp);
refn = MX25_ADCQ_CFG_REFN(refn);
- if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
- dev_err(dev, "Invalid fsl,adc-refp property value\n");
- of_node_put(child);
- return -EINVAL;
- }
- if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
- dev_err(dev, "Invalid fsl,adc-refn property value\n");
- of_node_put(child);
- return -EINVAL;
- }
+ if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid fsl,adc-refp property value\n");
+
+ if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid fsl,adc-refn property value\n");
regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
MX25_ADCQ_CFG_REFP_MASK |
@@ -294,6 +282,17 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
return 0;
}
+static void mx25_gcq_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+/* Custom handling needed as this driver doesn't own the clock */
+static void mx25_gcq_clk_disable(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
static int mx25_gcq_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
@@ -315,10 +314,9 @@ static int mx25_gcq_probe(struct platform_device *pdev)
return PTR_ERR(mem);
priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
- if (IS_ERR(priv->regs)) {
- dev_err(dev, "Failed to initialize regmap\n");
- return PTR_ERR(priv->regs);
- }
+ if (IS_ERR(priv->regs))
+ return dev_err_probe(dev, PTR_ERR(priv->regs),
+ "Failed to initialize regmap\n");
mutex_init(&priv->lock);
@@ -334,69 +332,44 @@ static int mx25_gcq_probe(struct platform_device *pdev)
ret = regulator_enable(priv->vref[i]);
if (ret)
- goto err_regulator_disable;
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mx25_gcq_reg_disable,
+ priv->vref[i]);
+ if (ret)
+ return ret;
}
priv->clk = tsadc->clk;
ret = clk_prepare_enable(priv->clk);
- if (ret) {
- dev_err(dev, "Failed to enable clock\n");
- goto err_vref_disable;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clock\n");
+
+ ret = devm_add_action_or_reset(dev, mx25_gcq_clk_disable,
+ priv->clk);
+ if (ret)
+ return ret;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
- goto err_clk_unprepare;
+ return ret;
priv->irq = ret;
- ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
- if (ret) {
- dev_err(dev, "Failed requesting IRQ\n");
- goto err_clk_unprepare;
- }
+ ret = devm_request_irq(dev, priv->irq, mx25_gcq_irq, 0, pdev->name,
+ priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed requesting IRQ\n");
indio_dev->channels = mx25_gcq_channels;
indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
indio_dev->info = &mx25_gcq_iio_info;
indio_dev->name = driver_name;
- ret = iio_device_register(indio_dev);
- if (ret) {
- dev_err(dev, "Failed to register iio device\n");
- goto err_irq_free;
- }
-
- platform_set_drvdata(pdev, indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register iio device\n");
return 0;
-
-err_irq_free:
- free_irq(priv->irq, priv);
-err_clk_unprepare:
- clk_disable_unprepare(priv->clk);
-err_vref_disable:
- i = 4;
-err_regulator_disable:
- for (; i-- > 0;) {
- if (priv->vref[i])
- regulator_disable(priv->vref[i]);
- }
- return ret;
-}
-
-static void mx25_gcq_remove(struct platform_device *pdev)
-{
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct mx25_gcq_priv *priv = iio_priv(indio_dev);
- int i;
-
- iio_device_unregister(indio_dev);
- free_irq(priv->irq, priv);
- clk_disable_unprepare(priv->clk);
- for (i = 4; i-- > 0;) {
- if (priv->vref[i])
- regulator_disable(priv->vref[i]);
- }
}
static const struct of_device_id mx25_gcq_ids[] = {
@@ -411,7 +384,6 @@ static struct platform_driver mx25_gcq_driver = {
.of_match_table = mx25_gcq_ids,
},
.probe = mx25_gcq_probe,
- .remove_new = mx25_gcq_remove,
};
module_platform_driver(mx25_gcq_driver);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index c80c55fb8c6ce..fef97c1d226a5 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -7,7 +7,7 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
@@ -459,7 +459,6 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
static int hx711_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct hx711_data *hx711_data;
struct iio_dev *indio_dev;
int ret;
@@ -533,7 +532,7 @@ static int hx711_probe(struct platform_device *pdev)
hx711_data->gain_chan_a = 128;
hx711_data->clock_frequency = 400000;
- ret = of_property_read_u32(np, "clock-frequency",
+ ret = device_property_read_u32(&pdev->dev, "clock-frequency",
&hx711_data->clock_frequency);
/*
diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c
index 6af829349b4e1..45368850b2201 100644
--- a/drivers/iio/adc/max11410.c
+++ b/drivers/iio/adc/max11410.c
@@ -696,7 +696,6 @@ static int max11410_parse_channels(struct max11410_state *st,
struct device *dev = &st->spi_dev->dev;
struct max11410_channel_config *cfg;
struct iio_chan_spec *channels;
- struct fwnode_handle *child;
u32 reference, sig_path;
const char *node_name;
u32 inputs[2], scale;
@@ -720,7 +719,7 @@ static int max11410_parse_channels(struct max11410_state *st,
if (!st->channels)
return -ENOMEM;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
node_name = fwnode_get_name(child);
if (fwnode_property_present(child, "diff-channels")) {
ret = fwnode_property_read_u32_array(child,
@@ -735,47 +734,37 @@ static int max11410_parse_channels(struct max11410_state *st,
inputs[1] = 0;
chanspec.differential = 0;
}
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
if (inputs[0] > MAX11410_CHANNEL_INDEX_MAX ||
- inputs[1] > MAX11410_CHANNEL_INDEX_MAX) {
- fwnode_handle_put(child);
+ inputs[1] > MAX11410_CHANNEL_INDEX_MAX)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid channel index for %s, should be less than %d\n",
node_name,
MAX11410_CHANNEL_INDEX_MAX + 1);
- }
cfg = &st->channels[chan_idx];
reference = MAX11410_REFSEL_AVDD_AGND;
fwnode_property_read_u32(child, "adi,reference", &reference);
- if (reference > MAX11410_REFSEL_MAX) {
- fwnode_handle_put(child);
+ if (reference > MAX11410_REFSEL_MAX)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid adi,reference value for %s, should be less than %d.\n",
node_name, MAX11410_REFSEL_MAX + 1);
- }
if (!max11410_get_vrefp(st, reference) ||
- (!max11410_get_vrefn(st, reference) && reference <= 2)) {
- fwnode_handle_put(child);
+ (!max11410_get_vrefn(st, reference) && reference <= 2))
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid VREF configuration for %s, either specify corresponding VREF regulators or change adi,reference property.\n",
node_name);
- }
sig_path = MAX11410_PGA_SIG_PATH_BUFFERED;
fwnode_property_read_u32(child, "adi,input-mode", &sig_path);
- if (sig_path > MAX11410_SIG_PATH_MAX) {
- fwnode_handle_put(child);
+ if (sig_path > MAX11410_SIG_PATH_MAX)
return dev_err_probe(&indio_dev->dev, -EINVAL,
"Invalid adi,input-mode value for %s, should be less than %d.\n",
node_name, MAX11410_SIG_PATH_MAX + 1);
- }
fwnode_property_read_u32(child, "settling-time-us",
&cfg->settling_time_us);
@@ -793,10 +782,8 @@ static int max11410_parse_channels(struct max11410_state *st,
cfg->scale_avail = devm_kcalloc(dev, MAX11410_SCALE_AVAIL_SIZE * 2,
sizeof(*cfg->scale_avail),
GFP_KERNEL);
- if (!cfg->scale_avail) {
- fwnode_handle_put(child);
+ if (!cfg->scale_avail)
return -ENOMEM;
- }
scale = max11410_get_scale(st, *cfg);
for (i = 0; i < MAX11410_SCALE_AVAIL_SIZE; i++) {
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index e0c2742da5236..f751260605e4e 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -1079,8 +1079,8 @@ static int pac1934_chip_identify(struct pac1934_chip_info *info)
* documentation related to the ACPI device definition
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
*/
-static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
- struct pac1934_chip_info *info)
+static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
+ struct pac1934_chip_info *info)
{
acpi_handle handle;
union acpi_object *rez;
@@ -1095,7 +1095,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL);
if (!rez)
- return false;
+ return -EINVAL;
for (i = 0; i < rez->package.count; i += 2) {
idx = i / 2;
@@ -1118,7 +1118,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
* and assign the default sampling rate
*/
info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ;
- return true;
+ return 0;
}
for (i = 0; i < rez->package.count; i++) {
@@ -1131,7 +1131,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL);
if (!rez)
- return false;
+ return -EINVAL;
bi_dir_mask = rez->package.elements[0].integer.value;
info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
@@ -1143,19 +1143,18 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL);
if (!rez)
- return false;
+ return -EINVAL;
info->sample_rate_value = rez->package.elements[0].integer.value;
ACPI_FREE(rez);
- return true;
+ return 0;
}
-static bool pac1934_of_parse_channel_config(struct i2c_client *client,
- struct pac1934_chip_info *info)
+static int pac1934_fw_parse_channel_config(struct i2c_client *client,
+ struct pac1934_chip_info *info)
{
- struct fwnode_handle *node, *fwnode;
struct device *dev = &client->dev;
unsigned int current_channel;
int idx, ret;
@@ -1163,46 +1162,38 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client,
info->sample_rate_value = 1024;
current_channel = 1;
- fwnode = dev_fwnode(dev);
- fwnode_for_each_available_child_node(fwnode, node) {
+ device_for_each_child_node_scoped(dev, node) {
ret = fwnode_property_read_u32(node, "reg", &idx);
- if (ret) {
- dev_err_probe(dev, ret,
- "reading invalid channel index\n");
- goto err_fwnode;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reading invalid channel index\n");
+
/* adjust idx to match channel index (1 to 4) from the datasheet */
idx--;
if (current_channel >= (info->phys_channels + 1) ||
- idx >= info->phys_channels || idx < 0) {
- dev_err_probe(dev, -EINVAL,
- "%s: invalid channel_index %d value\n",
- fwnode_get_name(node), idx);
- goto err_fwnode;
- }
+ idx >= info->phys_channels || idx < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "%s: invalid channel_index %d value\n",
+ fwnode_get_name(node), idx);
/* enable channel */
info->active_channels[idx] = true;
ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms",
&info->shunts[idx]);
- if (ret) {
- dev_err_probe(dev, ret,
- "%s: invalid shunt-resistor value: %d\n",
- fwnode_get_name(node), info->shunts[idx]);
- goto err_fwnode;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "%s: invalid shunt-resistor value: %d\n",
+ fwnode_get_name(node), info->shunts[idx]);
if (fwnode_property_present(node, "label")) {
ret = fwnode_property_read_string(node, "label",
(const char **)&info->labels[idx]);
- if (ret) {
- dev_err_probe(dev, ret,
- "%s: invalid rail-name value\n",
- fwnode_get_name(node));
- goto err_fwnode;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "%s: invalid rail-name value\n",
+ fwnode_get_name(node));
}
info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar");
@@ -1210,12 +1201,7 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client,
current_channel++;
}
- return true;
-
-err_fwnode:
- fwnode_handle_put(node);
-
- return false;
+ return 0;
}
static void pac1934_cancel_delayed_work(void *dwork)
@@ -1485,7 +1471,6 @@ static int pac1934_probe(struct i2c_client *client)
const struct pac1934_features *chip;
struct iio_dev *indio_dev;
int cnt, ret;
- bool match = false;
struct device *dev = &client->dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
@@ -1519,16 +1504,16 @@ static int pac1934_probe(struct i2c_client *client)
}
if (acpi_match_device(dev->driver->acpi_match_table, dev))
- match = pac1934_acpi_parse_channel_config(client, info);
+ ret = pac1934_acpi_parse_channel_config(client, info);
else
/*
* This makes it possible to use also ACPI PRP0001 for
* registering the device using device tree properties.
*/
- match = pac1934_of_parse_channel_config(client, info);
+ ret = pac1934_fw_parse_channel_config(client, info);
- if (!match)
- return dev_err_probe(dev, -EINVAL,
+ if (ret)
+ return dev_err_probe(dev, ret,
"parameter parsing returned an error\n");
mutex_init(&info->lock);
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index b6b612d733ff2..9b69f40beed8e 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -825,7 +825,6 @@ static int adc5_get_fw_data(struct adc5_chip *adc)
const struct adc5_channels *adc_chan;
struct iio_chan_spec *iio_chan;
struct adc5_channel_prop prop, *chan_props;
- struct fwnode_handle *child;
unsigned int index = 0;
int ret;
@@ -849,12 +848,10 @@ static int adc5_get_fw_data(struct adc5_chip *adc)
if (!adc->data)
adc->data = &adc5_data_pmic;
- device_for_each_child_node(adc->dev, child) {
+ device_for_each_child_node_scoped(adc->dev, child) {
ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
prop.scale_fn_type =
adc->data->adc_chans[prop.channel].scale_fn_type;
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index d524f2e8e9277..15a21d2860e75 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -318,7 +318,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
struct rcar_gyroadc *priv = iio_priv(indio_dev);
struct device *dev = priv->dev;
struct device_node *np = dev->of_node;
- struct device_node *child;
struct regulator *vref;
unsigned int reg;
unsigned int adcmode = -1, childmode;
@@ -326,7 +325,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
unsigned int num_channels;
int ret, first = 1;
- for_each_child_of_node(np, child) {
+ for_each_available_child_of_node_scoped(np, child) {
of_id = of_match_node(rcar_gyroadc_child_match, child);
if (!of_id) {
dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
@@ -352,7 +351,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
break;
default:
- goto err_e_inval;
+ return -EINVAL;
}
/*
@@ -369,7 +368,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
dev_err(dev,
"Failed to get child reg property of ADC \"%pOFn\".\n",
child);
- goto err_of_node_put;
+ return ret;
}
/* Channel number is too high. */
@@ -377,7 +376,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
dev_err(dev,
"Only %i channels supported with %pOFn, but reg = <%i>.\n",
num_channels, child, reg);
- goto err_e_inval;
+ return -EINVAL;
}
}
@@ -386,7 +385,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
dev_err(dev,
"Channel %i uses different ADC mode than the rest.\n",
reg);
- goto err_e_inval;
+ return -EINVAL;
}
/* Channel is valid, grab the regulator. */
@@ -396,8 +395,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
if (IS_ERR(vref)) {
dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
reg);
- ret = PTR_ERR(vref);
- goto err_of_node_put;
+ return PTR_ERR(vref);
}
priv->vref[reg] = vref;
@@ -422,7 +420,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
* we can stop parsing here.
*/
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
- of_node_put(child);
break;
}
}
@@ -433,12 +430,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
}
return 0;
-
-err_e_inval:
- ret = -EINVAL;
-err_of_node_put:
- of_node_put(child);
- return ret;
}
static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c
index 0921ff2d9b3a1..cd3a7e46ea53c 100644
--- a/drivers/iio/adc/rzg2l_adc.c
+++ b/drivers/iio/adc/rzg2l_adc.c
@@ -302,7 +302,6 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc)
{
struct iio_chan_spec *chan_array;
- struct fwnode_handle *fwnode;
struct rzg2l_adc_data *data;
unsigned int channel;
int num_channels;
@@ -330,17 +329,13 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l
return -ENOMEM;
i = 0;
- device_for_each_child_node(&pdev->dev, fwnode) {
+ device_for_each_child_node_scoped(&pdev->dev, fwnode) {
ret = fwnode_property_read_u32(fwnode, "reg", &channel);
- if (ret) {
- fwnode_handle_put(fwnode);
+ if (ret)
return ret;
- }
- if (channel >= RZG2L_ADC_MAX_CHANNELS) {
- fwnode_handle_put(fwnode);
+ if (channel >= RZG2L_ADC_MAX_CHANNELS)
return -EINVAL;
- }
chan_array[i].type = IIO_VOLTAGE;
chan_array[i].indexed = 1;
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
index 71362c2ddf897..b6dd096391c11 100644
--- a/drivers/iio/adc/spear_adc.c
+++ b/drivers/iio/adc/spear_adc.c
@@ -5,8 +5,10 @@
* Copyright 2012 Stefan Roese <sr@denx.de>
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -15,8 +17,6 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/completion.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -70,7 +70,7 @@ struct adc_regs_spear6xx {
};
struct spear_adc_state {
- struct device_node *np;
+ struct device *dev;
struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
struct clk *clk;
@@ -123,7 +123,7 @@ static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
static u32 spear_adc_get_average(struct spear_adc_state *st)
{
- if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ if (device_is_compatible(st->dev, "st,spear600-adc")) {
return __raw_readl(&st->adc_base_spear6xx->average.msb) &
SPEAR_ADC_DATA_MASK;
} else {
@@ -134,7 +134,7 @@ static u32 spear_adc_get_average(struct spear_adc_state *st)
static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
{
- if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ if (device_is_compatible(st->dev, "st,spear600-adc")) {
__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
&st->adc_base_spear6xx->scan_rate_lo);
__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
@@ -266,7 +266,6 @@ static const struct iio_info spear_adc_info = {
static int spear_adc_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct spear_adc_state *st;
struct iio_dev *indio_dev = NULL;
@@ -279,11 +278,10 @@ static int spear_adc_probe(struct platform_device *pdev)
"failed allocating iio device\n");
st = iio_priv(indio_dev);
+ st->dev = dev;
mutex_init(&st->lock);
- st->np = np;
-
/*
* SPEAr600 has a different register layout than other SPEAr SoC's
* (e.g. SPEAr3xx). Let's provide two register base addresses
@@ -310,8 +308,7 @@ static int spear_adc_probe(struct platform_device *pdev)
if (ret < 0)
return dev_err_probe(dev, ret, "failed requesting interrupt\n");
- if (of_property_read_u32(np, "sampling-frequency",
- &st->sampling_freq))
+ if (device_property_read_u32(dev, "sampling-frequency", &st->sampling_freq))
return dev_err_probe(dev, -EINVAL,
"sampling-frequency missing in DT\n");
@@ -319,13 +316,13 @@ static int spear_adc_probe(struct platform_device *pdev)
* Optional avg_samples defaults to 0, resulting in single data
* conversion
*/
- of_property_read_u32(np, "average-samples", &st->avg_samples);
+ device_property_read_u32(dev, "average-samples", &st->avg_samples);
/*
* Optional vref_external defaults to 0, resulting in internal vref
* selection
*/
- of_property_read_u32(np, "vref-external", &st->vref_external);
+ device_property_read_u32(dev, "vref-external", &st->vref_external);
spear_adc_configure(st);
@@ -346,19 +343,17 @@ static int spear_adc_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id spear_adc_dt_ids[] = {
{ .compatible = "st,spear600-adc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
-#endif
static struct platform_driver spear_adc_driver = {
.probe = spear_adc_probe,
.driver = {
.name = SPEAR_ADC_MOD_NAME,
- .of_match_table = of_match_ptr(spear_adc_dt_ids),
+ .of_match_table = spear_adc_dt_ids,
},
};
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index b5d3c9cea5c4e..36add95212c37 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -2187,58 +2187,52 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
struct iio_chan_spec *channels)
{
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
- struct fwnode_handle *child;
+ struct device *dev = &indio_dev->dev;
const char *name;
int val, scan_index = 0, ret;
bool differential;
u32 vin[2];
- device_for_each_child_node(&indio_dev->dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &val);
- if (ret) {
- dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
- goto err;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Missing channel index\n");
ret = fwnode_property_read_string(child, "label", &name);
/* label is optional */
if (!ret) {
- if (strlen(name) >= STM32_ADC_CH_SZ) {
- dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
- name, STM32_ADC_CH_SZ);
- ret = -EINVAL;
- goto err;
- }
+ if (strlen(name) >= STM32_ADC_CH_SZ)
+ return dev_err_probe(dev, -EINVAL,
+ "Label %s exceeds %d characters\n",
+ name, STM32_ADC_CH_SZ);
+
strscpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
ret = stm32_adc_populate_int_ch(indio_dev, name, val);
if (ret == -ENOENT)
continue;
else if (ret)
- goto err;
+ return ret;
} else if (ret != -EINVAL) {
- dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
- goto err;
+ return dev_err_probe(dev, ret, "Invalid label\n");
}
- if (val >= adc_info->max_channels) {
- dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
- ret = -EINVAL;
- goto err;
- }
+ if (val >= adc_info->max_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid channel %d\n", val);
differential = false;
ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2);
/* diff-channels is optional */
if (!ret) {
differential = true;
- if (vin[0] != val || vin[1] >= adc_info->max_channels) {
- dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
- vin[0], vin[1]);
- goto err;
- }
+ if (vin[0] != val || vin[1] >= adc_info->max_channels)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid channel in%d-in%d\n",
+ vin[0], vin[1]);
} else if (ret != -EINVAL) {
- dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
- goto err;
+ return dev_err_probe(dev, ret,
+ "Invalid diff-channels property\n");
}
stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
@@ -2247,11 +2241,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
val = 0;
ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val);
/* st,min-sample-time-ns is optional */
- if (ret && ret != -EINVAL) {
- dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n",
- ret);
- goto err;
- }
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "Invalid st,min-sample-time-ns property\n");
stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
if (differential)
@@ -2261,11 +2253,6 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
}
return scan_index;
-
-err:
- fwnode_handle_put(child);
-
- return ret;
}
static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping)
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index fcfc462543133..cb04a29b3dbab 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -694,7 +694,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
struct ads131e08_channel_config *channel_config;
struct device *dev = &st->spi->dev;
struct iio_chan_spec *channels;
- struct fwnode_handle *node;
unsigned int channel, tmp;
int num_channels, i, ret;
@@ -736,10 +735,10 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
return -ENOMEM;
i = 0;
- device_for_each_child_node(dev, node) {
+ device_for_each_child_node_scoped(dev, node) {
ret = fwnode_property_read_u32(node, "reg", &channel);
if (ret)
- goto err_child_out;
+ return ret;
ret = fwnode_property_read_u32(node, "ti,gain", &tmp);
if (ret) {
@@ -747,7 +746,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
} else {
ret = ads131e08_pga_gain_to_field_value(st, tmp);
if (ret < 0)
- goto err_child_out;
+ return ret;
channel_config[i].pga_gain = tmp;
}
@@ -758,7 +757,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
} else {
ret = ads131e08_validate_channel_mux(st, tmp);
if (ret)
- goto err_child_out;
+ return ret;
channel_config[i].mux = tmp;
}
@@ -785,9 +784,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
return 0;
-err_child_out:
- fwnode_handle_put(node);
- return ret;
}
static void ads131e08_regulator_disable(void *data)
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 4a247ca25a449..0253064fadec8 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -19,10 +19,12 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/mfd/twl.h>
-#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/mutex.h>
#include <linux/bitops.h>
@@ -30,7 +32,6 @@
#include <linux/types.h>
#include <linux/gfp.h>
#include <linux/err.h>
-#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
@@ -744,14 +745,14 @@ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
*/
static int twl4030_madc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct twl4030_madc_platform_data *pdata = dev_get_platdata(dev);
struct twl4030_madc_data *madc;
- struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct device_node *np = pdev->dev.of_node;
int irq, ret;
u8 regval;
struct iio_dev *iio_dev = NULL;
- if (!pdata && !np) {
+ if (!pdata && !dev_fwnode(dev)) {
dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
return -EINVAL;
}
@@ -779,7 +780,7 @@ static int twl4030_madc_probe(struct platform_device *pdev)
if (pdata)
madc->use_second_irq = (pdata->irq_line != 1);
else
- madc->use_second_irq = of_property_read_bool(np,
+ madc->use_second_irq = device_property_read_bool(dev,
"ti,system-uses-second-madc-irq");
madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
@@ -905,20 +906,18 @@ static void twl4030_madc_remove(struct platform_device *pdev)
regulator_disable(madc->usb3v1);
}
-#ifdef CONFIG_OF
static const struct of_device_id twl_madc_of_match[] = {
{ .compatible = "ti,twl4030-madc", },
- { },
+ { }
};
MODULE_DEVICE_TABLE(of, twl_madc_of_match);
-#endif
static struct platform_driver twl4030_madc_driver = {
.probe = twl4030_madc_probe,
.remove_new = twl4030_madc_remove,
.driver = {
.name = "twl4030_madc",
- .of_match_table = of_match_ptr(twl_madc_of_match),
+ .of_match_table = twl_madc_of_match,
},
};
diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c
index 7af3e4b8fe3bb..cd26a16dc0ff8 100644
--- a/drivers/iio/addac/ad74413r.c
+++ b/drivers/iio/addac/ad74413r.c
@@ -1255,21 +1255,15 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev,
static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev)
{
struct ad74413r_state *st = iio_priv(indio_dev);
- struct fwnode_handle *channel_node = NULL;
int ret;
- fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) {
+ device_for_each_child_node_scoped(st->dev, channel_node) {
ret = ad74413r_parse_channel_config(indio_dev, channel_node);
if (ret)
- goto put_channel_node;
+ return ret;
}
return 0;
-
-put_channel_node:
- fwnode_handle_put(channel_node);
-
- return ret;
}
static int ad74413r_setup_channels(struct iio_dev *indio_dev)
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index 5610ba67925ef..13b1a858969e9 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
block->state = IIO_BLOCK_STATE_DONE;
}
+static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
+{
+ __poll_t flags;
+
+ if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
+ flags = EPOLLIN | EPOLLRDNORM;
+ else
+ flags = EPOLLOUT | EPOLLWRNORM;
+
+ wake_up_interruptible_poll(&queue->buffer.pollq, flags);
+}
+
/**
* iio_dma_buffer_block_done() - Indicate that a block has been completed
* @block: The completed block
@@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
spin_unlock_irqrestore(&queue->list_lock, flags);
iio_buffer_block_put_atomic(block);
- wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+ iio_dma_buffer_queue_wake(queue);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
@@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
}
spin_unlock_irqrestore(&queue->list_lock, flags);
- wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
+ iio_dma_buffer_queue_wake(queue);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
@@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
queue->fileio.blocks[i] = block;
}
- block->state = IIO_BLOCK_STATE_QUEUED;
- list_add_tail(&block->head, &queue->incoming);
+ /*
+ * block->bytes_used may have been modified previously, e.g. by
+ * iio_dma_buffer_block_list_abort(). Reset it here to the
+ * block's so that iio_dma_buffer_io() will work.
+ */
+ block->bytes_used = block->size;
+
+ /*
+ * If it's an input buffer, mark the block as queued, and
+ * iio_dma_buffer_enable() will submit it. Otherwise mark it as
+ * done, which means it's ready to be dequeued.
+ */
+ if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
+ block->state = IIO_BLOCK_STATE_QUEUED;
+ list_add_tail(&block->head, &queue->incoming);
+ } else {
+ block->state = IIO_BLOCK_STATE_DONE;
+ }
}
out_unlock:
@@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
return block;
}
-/**
- * iio_dma_buffer_read() - DMA buffer read callback
- * @buffer: Buffer to read form
- * @n: Number of bytes to read
- * @user_buffer: Userspace buffer to copy the data to
- *
- * Should be used as the read callback for iio_buffer_access_ops
- * struct for DMA buffers.
- */
-int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
- char __user *user_buffer)
+static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
+ char __user *user_buffer, bool is_from_user)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block;
+ void *addr;
int ret;
if (n < buffer->bytes_per_datum)
@@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
n = rounddown(n, buffer->bytes_per_datum);
if (n > block->bytes_used - queue->fileio.pos)
n = block->bytes_used - queue->fileio.pos;
+ addr = block->vaddr + queue->fileio.pos;
- if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
+ if (is_from_user)
+ ret = copy_from_user(addr, user_buffer, n);
+ else
+ ret = copy_to_user(user_buffer, addr, n);
+ if (ret) {
ret = -EFAULT;
goto out_unlock;
}
@@ -544,16 +569,49 @@ out_unlock:
return ret;
}
+
+/**
+ * iio_dma_buffer_read() - DMA buffer read callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data to
+ *
+ * Should be used as the read callback for iio_buffer_access_ops
+ * struct for DMA buffers.
+ */
+int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
+ char __user *user_buffer)
+{
+ return iio_dma_buffer_io(buffer, n, user_buffer, false);
+}
EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
/**
- * iio_dma_buffer_data_available() - DMA buffer data_available callback
- * @buf: Buffer to check for data availability
+ * iio_dma_buffer_write() - DMA buffer write callback
+ * @buffer: Buffer to read form
+ * @n: Number of bytes to read
+ * @user_buffer: Userspace buffer to copy the data from
*
- * Should be used as the data_available callback for iio_buffer_access_ops
+ * Should be used as the write callback for iio_buffer_access_ops
* struct for DMA buffers.
*/
-size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+ const char __user *user_buffer)
+{
+ return iio_dma_buffer_io(buffer, n,
+ (__force __user char *)user_buffer, true);
+}
+EXPORT_SYMBOL_GPL(iio_dma_buffer_write);
+
+/**
+ * iio_dma_buffer_usage() - DMA buffer data_available and
+ * space_available callback
+ * @buf: Buffer to check for data availability
+ *
+ * Should be used as the data_available and space_available callbacks for
+ * iio_buffer_access_ops struct for DMA buffers.
+ */
+size_t iio_dma_buffer_usage(struct iio_buffer *buf)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
struct iio_dma_buffer_block *block;
@@ -586,7 +644,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
return data_available;
}
-EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
+EXPORT_SYMBOL_GPL(iio_dma_buffer_usage);
/**
* iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index a18c1da292af2..918f6f8d65b6c 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
struct dma_async_tx_descriptor *desc;
+ enum dma_transfer_direction dma_dir;
+ size_t max_size;
dma_cookie_t cookie;
- block->bytes_used = min(block->size, dmaengine_buffer->max_size);
- block->bytes_used = round_down(block->bytes_used,
- dmaengine_buffer->align);
+ 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) {
+ block->bytes_used = max_size;
+ dma_dir = DMA_DEV_TO_MEM;
+ } else {
+ dma_dir = DMA_MEM_TO_DEV;
+ }
+
+ if (!block->bytes_used || block->bytes_used > max_size)
+ return -EINVAL;
desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
- block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
+ block->phys_addr, block->bytes_used, dma_dir,
DMA_PREP_INTERRUPT);
if (!desc)
return -ENOMEM;
@@ -112,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
.read = iio_dma_buffer_read,
+ .write = iio_dma_buffer_write,
.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
.set_length = iio_dma_buffer_set_length,
.request_update = iio_dma_buffer_request_update,
.enable = iio_dma_buffer_enable,
.disable = iio_dma_buffer_disable,
- .data_available = iio_dma_buffer_data_available,
+ .data_available = iio_dma_buffer_usage,
+ .space_available = iio_dma_buffer_usage,
.release = iio_dmaengine_buffer_release,
.modes = INDIO_BUFFER_HARDWARE,
@@ -159,7 +172,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = {
* Once done using the buffer iio_dmaengine_buffer_free() should be used to
* release it.
*/
-struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
+static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel)
{
struct dmaengine_buffer *dmaengine_buffer;
@@ -210,7 +223,6 @@ err_free:
kfree(dmaengine_buffer);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER);
/**
* iio_dmaengine_buffer_free() - Free dmaengine buffer
@@ -230,66 +242,64 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
}
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER);
-static void __devm_iio_dmaengine_buffer_free(void *buffer)
-{
- iio_dmaengine_buffer_free(buffer);
-}
-
-/**
- * devm_iio_dmaengine_buffer_alloc() - Resource-managed iio_dmaengine_buffer_alloc()
- * @dev: Parent device for the buffer
- * @channel: DMA channel name, typically "rx".
- *
- * This allocates a new IIO buffer which internally uses the DMAengine framework
- * to perform its transfers. The parent device will be used to request the DMA
- * channel.
- *
- * The buffer will be automatically de-allocated once the device gets destroyed.
- */
-static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
- const char *channel)
+struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir)
{
struct iio_buffer *buffer;
int ret;
buffer = iio_dmaengine_buffer_alloc(dev, channel);
if (IS_ERR(buffer))
- return buffer;
+ return ERR_CAST(buffer);
- ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
- buffer);
- if (ret)
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ buffer->direction = dir;
+
+ ret = iio_device_attach_buffer(indio_dev, buffer);
+ if (ret) {
+ iio_dmaengine_buffer_free(buffer);
return ERR_PTR(ret);
+ }
return buffer;
}
+EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER);
+
+static void __devm_iio_dmaengine_buffer_free(void *buffer)
+{
+ iio_dmaengine_buffer_free(buffer);
+}
/**
- * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device
+ * devm_iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device
* @dev: Parent device for the buffer
* @indio_dev: IIO device to which to attach this buffer.
* @channel: DMA channel name, typically "rx".
+ * @dir: Direction of buffer (in or out)
*
* This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc()
* and attaches it to an IIO device with iio_device_attach_buffer().
* It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the
* IIO device.
*/
-int devm_iio_dmaengine_buffer_setup(struct device *dev,
- struct iio_dev *indio_dev,
- const char *channel)
+int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir)
{
struct iio_buffer *buffer;
- buffer = devm_iio_dmaengine_buffer_alloc(dev, channel);
+ buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
- indio_dev->modes |= INDIO_BUFFER_HARDWARE;
-
- return iio_device_attach_buffer(indio_dev, buffer);
+ return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
+ buffer);
}
-EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER);
+EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 34eb40bb95291..3c2bf620f00fb 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -131,6 +131,43 @@ config AD5624R_SPI
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config AD9739A
+ tristate "Analog Devices AD9739A RF DAC spi driver"
+ depends on SPI || COMPILE_TEST
+ select REGMAP_SPI
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices AD9739A Digital-to
+ Analog Converter.
+
+ The driver requires the assistance of the AXI DAC IP core to operate,
+ since SPI is used for configuration only, while data has to be
+ streamed into memory via DMA.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad9739a.
+
+config ADI_AXI_DAC
+ tristate "Analog Devices Generic AXI DAC IP core driver"
+ select IIO_BUFFER
+ select IIO_BUFFER_DMAENGINE
+ select REGMAP_MMIO
+ select IIO_BACKEND
+ help
+ Say yes here to build support for Analog Devices Generic
+ AXI DAC IP core. The IP core is used for interfacing with
+ digital-to-analog (DAC) converters that require either a high-speed
+ serial interface (JESD204B/C) or a source synchronous parallel
+ interface (LVDS/CMOS).
+ Typically (for such devices) SPI will be used for configuration only,
+ while this IP core handles the streaming of data into memory via DMA.
+
+ Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called adi-axi-dac.
+
config LTC2688
tristate "Analog Devices LTC2688 DAC spi driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 55bf89739d14b..8432a81a19dc2 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
obj-$(CONFIG_AD7293) += ad7293.o
obj-$(CONFIG_AD7303) += ad7303.o
obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_AD9739A) += ad9739a.o
+obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_DS4424) += ds4424.o
diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c
index a492e8f2fc0fb..8aa942896b5bc 100644
--- a/drivers/iio/dac/ad3552r.c
+++ b/drivers/iio/dac/ad3552r.c
@@ -801,51 +801,45 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
u32 ch)
{
struct device *dev = &dac->spi->dev;
- struct fwnode_handle *gain_child;
u32 val;
int err;
u8 addr;
u16 reg = 0, offset;
- gain_child = fwnode_get_named_child_node(child,
- "custom-output-range-config");
- if (!gain_child) {
- dev_err(dev,
- "mandatory custom-output-range-config property missing\n");
- return -EINVAL;
- }
+ struct fwnode_handle *gain_child __free(fwnode_handle)
+ = fwnode_get_named_child_node(child,
+ "custom-output-range-config");
+ if (!gain_child)
+ return dev_err_probe(dev, -EINVAL,
+ "mandatory custom-output-range-config property missing\n");
dac->ch_data[ch].range_override = 1;
reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE);
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
- if (err) {
- dev_err(dev, "mandatory adi,gain-scaling-p property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,gain-scaling-p property missing\n");
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P);
dac->ch_data[ch].p = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
- if (err) {
- dev_err(dev, "mandatory adi,gain-scaling-n property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,gain-scaling-n property missing\n");
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N);
dac->ch_data[ch].n = val;
err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
- if (err) {
- dev_err(dev, "mandatory adi,rfb-ohms property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,rfb-ohms property missing\n");
dac->ch_data[ch].rfb = val;
err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
- if (err) {
- dev_err(dev, "mandatory adi,gain-offset property missing\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory adi,gain-offset property missing\n");
dac->ch_data[ch].gain_offset = val;
offset = abs((s32)val);
@@ -855,21 +849,14 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
err = ad3552r_write_reg(dac, addr,
offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
- if (err) {
- dev_err(dev, "Error writing register\n");
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err, "Error writing register\n");
err = ad3552r_write_reg(dac, addr, reg);
- if (err) {
- dev_err(dev, "Error writing register\n");
- goto put_child;
- }
-
-put_child:
- fwnode_handle_put(gain_child);
+ if (err)
+ return dev_err_probe(dev, err, "Error writing register\n");
- return err;
+ return 0;
}
static void ad3552r_reg_disable(void *reg)
@@ -880,7 +867,6 @@ static void ad3552r_reg_disable(void *reg)
static int ad3552r_configure_device(struct ad3552r_desc *dac)
{
struct device *dev = &dac->spi->dev;
- struct fwnode_handle *child;
struct regulator *vref;
int err, cnt = 0, voltage, delta = 100000;
u32 vals[2], val, ch;
@@ -949,53 +935,45 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
return -ENODEV;
}
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
err = fwnode_property_read_u32(child, "reg", &ch);
- if (err) {
- dev_err(dev, "mandatory reg property missing\n");
- goto put_child;
- }
- if (ch >= AD3552R_NUM_CH) {
- dev_err(dev, "reg must be less than %d\n",
- AD3552R_NUM_CH);
- err = -EINVAL;
- goto put_child;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "mandatory reg property missing\n");
+ if (ch >= AD3552R_NUM_CH)
+ return dev_err_probe(dev, -EINVAL,
+ "reg must be less than %d\n",
+ AD3552R_NUM_CH);
if (fwnode_property_present(child, "adi,output-range-microvolt")) {
err = fwnode_property_read_u32_array(child,
"adi,output-range-microvolt",
vals,
2);
- if (err) {
- dev_err(dev,
+ if (err)
+ return dev_err_probe(dev, err,
"adi,output-range-microvolt property could not be parsed\n");
- goto put_child;
- }
err = ad3552r_find_range(dac->chip_id, vals);
- if (err < 0) {
- dev_err(dev,
- "Invalid adi,output-range-microvolt value\n");
- goto put_child;
- }
+ if (err < 0)
+ return dev_err_probe(dev, err,
+ "Invalid adi,output-range-microvolt value\n");
+
val = err;
err = ad3552r_set_ch_value(dac,
AD3552R_CH_OUTPUT_RANGE_SEL,
ch, val);
if (err)
- goto put_child;
+ return err;
dac->ch_data[ch].range = val;
} else if (dac->chip_id == AD3542R_ID) {
- dev_err(dev,
- "adi,output-range-microvolt is required for ad3542r\n");
- err = -EINVAL;
- goto put_child;
+ return dev_err_probe(dev, -EINVAL,
+ "adi,output-range-microvolt is required for ad3542r\n");
} else {
err = ad3552r_configure_custom_gain(dac, child, ch);
if (err)
- goto put_child;
+ return err;
}
ad3552r_calc_gain_and_offset(dac, ch);
@@ -1003,7 +981,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1);
if (err < 0)
- goto put_child;
+ return err;
dac->channels[cnt] = AD3552R_CH_DAC(ch);
++cnt;
@@ -1021,10 +999,6 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
dac->num_ch = cnt;
return 0;
-put_child:
- fwnode_handle_put(child);
-
- return err;
}
static int ad3552r_init(struct ad3552r_desc *dac)
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
index 404865e354602..0b24cb19ac9d1 100644
--- a/drivers/iio/dac/ad5755.c
+++ b/drivers/iio/dac/ad5755.c
@@ -809,7 +809,6 @@ static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev)
static int ad5755_probe(struct spi_device *spi)
{
- enum ad5755_type type = spi_get_device_id(spi)->driver_data;
const struct ad5755_platform_data *pdata;
struct iio_dev *indio_dev;
struct ad5755_state *st;
@@ -824,7 +823,7 @@ static int ad5755_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
- st->chip_info = &ad5755_chip_info_tbl[type];
+ st->chip_info = spi_get_device_match_data(spi);
st->spi = spi;
st->pwr_down = 0xf;
@@ -854,21 +853,21 @@ static int ad5755_probe(struct spi_device *spi)
}
static const struct spi_device_id ad5755_id[] = {
- { "ad5755", ID_AD5755 },
- { "ad5755-1", ID_AD5755 },
- { "ad5757", ID_AD5757 },
- { "ad5735", ID_AD5735 },
- { "ad5737", ID_AD5737 },
+ { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
+ { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
+ { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] },
+ { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] },
+ { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] },
{}
};
MODULE_DEVICE_TABLE(spi, ad5755_id);
static const struct of_device_id ad5755_of_match[] = {
- { .compatible = "adi,ad5755" },
- { .compatible = "adi,ad5755-1" },
- { .compatible = "adi,ad5757" },
- { .compatible = "adi,ad5735" },
- { .compatible = "adi,ad5737" },
+ { .compatible = "adi,ad5755", &ad5755_chip_info_tbl[ID_AD5755] },
+ { .compatible = "adi,ad5755-1", &ad5755_chip_info_tbl[ID_AD5755] },
+ { .compatible = "adi,ad5757", &ad5755_chip_info_tbl[ID_AD5757] },
+ { .compatible = "adi,ad5735", &ad5755_chip_info_tbl[ID_AD5735] },
+ { .compatible = "adi,ad5737", &ad5755_chip_info_tbl[ID_AD5737] },
{ }
};
MODULE_DEVICE_TABLE(of, ad5755_of_match);
@@ -876,6 +875,7 @@ MODULE_DEVICE_TABLE(of, ad5755_of_match);
static struct spi_driver ad5755_driver = {
.driver = {
.name = "ad5755",
+ .of_match_table = ad5755_of_match,
},
.probe = ad5755_probe,
.id_table = ad5755_id,
diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c
index f66d67402e436..c360ebf5297ab 100644
--- a/drivers/iio/dac/ad5770r.c
+++ b/drivers/iio/dac/ad5770r.c
@@ -515,39 +515,32 @@ static int ad5770r_channel_config(struct ad5770r_state *st)
{
int ret, tmp[2], min, max;
unsigned int num;
- struct fwnode_handle *child;
num = device_get_child_node_count(&st->spi->dev);
if (num != AD5770R_MAX_CHANNELS)
return -EINVAL;
- device_for_each_child_node(&st->spi->dev, child) {
+ device_for_each_child_node_scoped(&st->spi->dev, child) {
ret = fwnode_property_read_u32(child, "reg", &num);
if (ret)
- goto err_child_out;
- if (num >= AD5770R_MAX_CHANNELS) {
- ret = -EINVAL;
- goto err_child_out;
- }
+ return ret;
+ if (num >= AD5770R_MAX_CHANNELS)
+ return -EINVAL;
ret = fwnode_property_read_u32_array(child,
"adi,range-microamp",
tmp, 2);
if (ret)
- goto err_child_out;
+ return ret;
min = tmp[0] / 1000;
max = tmp[1] / 1000;
ret = ad5770r_store_output_range(st, min, max, num);
if (ret)
- goto err_child_out;
+ return ret;
}
return 0;
-
-err_child_out:
- fwnode_handle_put(child);
- return ret;
}
static int ad5770r_init(struct ad5770r_state *st)
diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
new file mode 100644
index 0000000000000..ff33120075bf6
--- /dev/null
+++ b/drivers/iio/dac/ad9739a.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices AD9739a SPI DAC driver
+ *
+ * Copyright 2015-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define AD9739A_REG_MODE 0
+#define AD9739A_RESET_MASK BIT(5)
+#define AD9739A_REG_FSC_1 0x06
+#define AD9739A_REG_FSC_2 0x07
+#define AD9739A_FSC_MSB GENMASK(1, 0)
+#define AD9739A_REG_DEC_CNT 0x8
+#define AD9739A_NORMAL_MODE 0
+#define AD9739A_MIXED_MODE 2
+#define AD9739A_DAC_DEC GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT1 0x10
+#define AD9739A_RCVR_LOOP_EN_MASK GENMASK(1, 0)
+#define AD9739A_REG_LVDS_REC_CNT4 0x13
+#define AD9739A_FINE_DEL_SKW_MASK GENMASK(3, 0)
+#define AD9739A_REG_LVDS_REC_STAT9 0x21
+#define AD9739A_RCVR_TRACK_AND_LOCK (BIT(3) | BIT(0))
+#define AD9739A_REG_CROSS_CNT1 0x22
+#define AD9739A_REG_CROSS_CNT2 0x23
+#define AD9739A_REG_PHS_DET 0x24
+#define AD9739A_REG_MU_DUTY 0x25
+#define AD9739A_REG_MU_CNT1 0x26
+#define AD9739A_MU_EN_MASK BIT(0)
+#define AD9739A_REG_MU_CNT2 0x27
+#define AD9739A_REG_MU_CNT3 0x28
+#define AD9739A_REG_MU_CNT4 0x29
+#define AD9739A_MU_CNT4_DEFAULT 0xcb
+#define AD9739A_REG_MU_STAT1 0x2A
+#define AD9739A_MU_LOCK_MASK BIT(0)
+#define AD9739A_REG_ANA_CNT_1 0x32
+#define AD9739A_REG_ID 0x35
+
+#define AD9739A_ID 0x24
+#define AD9739A_REG_IS_RESERVED(reg) \
+ ((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \
+ (reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34)
+
+#define AD9739A_FSC_MIN 8580
+#define AD9739A_FSC_MAX 31700
+#define AD9739A_FSC_RANGE (AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1)
+
+#define AD9739A_MIN_DAC_CLK (1600 * MEGA)
+#define AD9739A_MAX_DAC_CLK (2500 * MEGA)
+#define AD9739A_DAC_CLK_RANGE (AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1)
+/* as recommended by the datasheet */
+#define AD9739A_LOCK_N_TRIES 3
+
+struct ad9739a_state {
+ struct iio_backend *back;
+ struct regmap *regmap;
+ unsigned long sample_rate;
+};
+
+static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+ u32 mode;
+ int ret;
+
+ ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
+ if (ret)
+ return ret;
+
+ mode = FIELD_GET(AD9739A_DAC_DEC, mode);
+ /* sanity check we get valid values from the HW */
+ if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
+ return -EIO;
+ if (!mode)
+ return AD9739A_NORMAL_MODE;
+
+ /*
+ * We get 2 from the device but for IIO modes, that means 1. Hence the
+ * minus 1.
+ */
+ return AD9739A_MIXED_MODE - 1;
+}
+
+static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, u32 mode)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ /*
+ * On the IIO interface we have 0 and 1 for mode. But for mixed_mode, we
+ * need to write 2 in the device. That's what the below check is about.
+ */
+ if (mode == AD9739A_MIXED_MODE - 1)
+ mode = AD9739A_MIXED_MODE;
+
+ return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
+ AD9739A_DAC_DEC, mode);
+}
+
+static int ad9739a_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->sample_rate;
+ *val2 = 0;
+ return IIO_VAL_INT_64;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad9739a_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
+}
+
+static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad9739a_state *st = iio_priv(indio_dev);
+
+ return iio_backend_data_source_set(st->back, 0,
+ IIO_BACKEND_INTERNAL_CONTINUOS_WAVE);
+}
+
+static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg)
+{
+ if (AD9739A_REG_IS_RESERVED(reg))
+ return false;
+ if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1)
+ return false;
+
+ return true;
+}
+
+static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st)
+{
+ struct gpio_desc *gpio;
+ int ret;
+
+ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ if (gpio) {
+ /* minimum pulse width of 40ns */
+ ndelay(40);
+ gpiod_set_value_cansleep(gpio, 0);
+ return 0;
+ }
+
+ /* bring all registers to their default state */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK);
+ if (ret)
+ return ret;
+
+ ndelay(40);
+
+ return regmap_clear_bits(st->regmap, AD9739A_REG_MODE,
+ AD9739A_RESET_MASK);
+}
+
+/*
+ * Recommended values (as per datasheet) for the dac clk common mode voltage
+ * and Mu controller. Look at table 29.
+ */
+static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
+ /* DAC clk common mode voltage */
+ { AD9739A_REG_CROSS_CNT1, 0x0f },
+ { AD9739A_REG_CROSS_CNT2, 0x0f },
+ /* Mu controller configuration */
+ { AD9739A_REG_PHS_DET, 0x30 },
+ { AD9739A_REG_MU_DUTY, 0x80 },
+ { AD9739A_REG_MU_CNT2, 0x44 },
+ { AD9739A_REG_MU_CNT3, 0x6c },
+};
+
+static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
+{
+ unsigned int i = 0, lock, fsc;
+ u32 fsc_raw;
+ int ret;
+
+ ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
+ ARRAY_SIZE(ad9739a_clk_mu_ctrl));
+ if (ret)
+ return ret;
+
+ /*
+ * Try to get the Mu lock. Repeat the below steps AD9739A_LOCK_N_TRIES
+ * (as specified by the datasheet) until we get the lock.
+ */
+ do {
+ ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
+ AD9739A_MU_CNT4_DEFAULT);
+ if (ret)
+ return ret;
+
+ /* Enable the Mu controller search and track mode. */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
+ AD9739A_MU_EN_MASK);
+ if (ret)
+ return ret;
+
+ /* Ensure the DLL loop is locked */
+ ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
+ lock, lock & AD9739A_MU_LOCK_MASK,
+ 0, 1000);
+ if (ret && ret != -ETIMEDOUT)
+ return ret;
+ } while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+ if (i == AD9739A_LOCK_N_TRIES)
+ return dev_err_probe(dev, ret, "Mu lock timeout\n");
+
+ /* Receiver tracking and lock. Same deal as the Mu controller */
+ i = 0;
+ do {
+ ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
+ AD9739A_FINE_DEL_SKW_MASK,
+ FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
+ if (ret)
+ return ret;
+
+ /* Disable the receiver and the loop. */
+ ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Re-enable the loop so it falls out of lock and begins the
+ * search/track routine again.
+ */
+ ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
+ AD9739A_RCVR_LOOP_EN_MASK);
+ if (ret)
+ return ret;
+
+ /* Ensure the DLL loop is locked */
+ ret = regmap_read_poll_timeout(st->regmap,
+ AD9739A_REG_LVDS_REC_STAT9, lock,
+ lock == AD9739A_RCVR_TRACK_AND_LOCK,
+ 0, 1000);
+ if (ret && ret != -ETIMEDOUT)
+ return ret;
+ } while (ret && ++i < AD9739A_LOCK_N_TRIES);
+
+ if (i == AD9739A_LOCK_N_TRIES)
+ return dev_err_probe(dev, ret, "Receiver lock timeout\n");
+
+ ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
+ if (ret && ret == -EINVAL)
+ return 0;
+ if (ret)
+ return ret;
+ if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid full scale current(%u) [%u %u]\n",
+ fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
+ /*
+ * IOUTFS is given by
+ * Ioutfs = 0.0226 * FSC + 8.58
+ * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
+ * order to get rid of the fractional.
+ */
+ fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
+
+ ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD9739A_REG_FSC_2,
+ AD9739A_FSC_MSB, fsc_raw >> 8);
+}
+
+static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" };
+
+static const struct iio_enum ad9739a_modes = {
+ .items = ad9739a_modes_avail,
+ .num_items = ARRAY_SIZE(ad9739a_modes_avail),
+ .get = ad9739a_oper_mode_get,
+ .set = ad9739a_oper_mode_set,
+};
+
+static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = {
+ IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+ IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes),
+ { }
+};
+
+/*
+ * The reason for having two different channels is because we have, in reality,
+ * two sources of data:
+ * ALTVOLTAGE: It's a Continuous Wave that's internally generated by the
+ * backend device.
+ * VOLTAGE: It's the typical data we can have in a DAC device and the source
+ * of it has nothing to do with the backend. The backend will only
+ * forward it into our data interface to be sent out.
+ */
+static struct iio_chan_spec ad9739a_channels[] = {
+ {
+ .type = IIO_ALTVOLTAGE,
+ .indexed = 1,
+ .output = 1,
+ .scan_index = -1,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .output = 1,
+ .ext_info = ad9739a_ext_info,
+ .scan_type = {
+ .sign = 's',
+ .storagebits = 16,
+ .realbits = 16,
+ },
+ }
+};
+
+static const struct iio_info ad9739a_info = {
+ .read_raw = ad9739a_read_raw,
+};
+
+static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = {
+ .preenable = &ad9739a_buffer_preenable,
+ .postdisable = &ad9739a_buffer_postdisable,
+};
+
+static const struct regmap_config ad9739a_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .readable_reg = ad9739a_reg_accessible,
+ .writeable_reg = ad9739a_reg_accessible,
+ .max_register = AD9739A_REG_ID,
+};
+
+static int ad9739a_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad9739a_state *st;
+ unsigned int id;
+ struct clk *clk;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
+
+ st->sample_rate = clk_get_rate(clk);
+ if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
+ AD9739A_DAC_CLK_RANGE))
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid dac clk range(%lu) [%lu %lu]\n",
+ st->sample_rate, AD9739A_MIN_DAC_CLK,
+ AD9739A_MAX_DAC_CLK);
+
+ st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
+ if (ret)
+ return ret;
+
+ if (id != AD9739A_ID)
+ dev_warn(dev, "Unrecognized CHIP_ID 0x%X", id);
+
+ ret = ad9739a_reset(dev, st);
+ if (ret)
+ return ret;
+
+ ret = ad9739a_init(dev, st);
+ if (ret)
+ return ret;
+
+ st->back = devm_iio_backend_get(dev, NULL);
+ if (IS_ERR(st->back))
+ return PTR_ERR(st->back);
+
+ ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_extend_chan_spec(indio_dev, st->back,
+ &ad9739a_channels[0]);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_backend_enable(dev, st->back);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "ad9739a";
+ indio_dev->info = &ad9739a_info;
+ indio_dev->channels = ad9739a_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
+ indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ad9739a_of_match[] = {
+ { .compatible = "adi,ad9739a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad9739a_of_match);
+
+static const struct spi_device_id ad9739a_id[] = {
+ {"ad9739a"},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9739a_id);
+
+static struct spi_driver ad9739a_driver = {
+ .driver = {
+ .name = "ad9739a",
+ .of_match_table = ad9739a_of_match,
+ },
+ .probe = ad9739a_probe,
+ .id_table = ad9739a_id,
+};
+module_spi_driver(ad9739a_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD9739 DAC");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_BACKEND);
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
new file mode 100644
index 0000000000000..9047c5aec0ff9
--- /dev/null
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -0,0 +1,635 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices Generic AXI DAC IP core
+ * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
+ *
+ * Copyright 2016-2024 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/kstrtox.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/iio/backend.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+
+/*
+ * Register definitions:
+ * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
+ */
+
+/* Base controls */
+#define AXI_DAC_REG_CONFIG 0x0c
+#define AXI_DDS_DISABLE BIT(6)
+
+ /* DAC controls */
+#define AXI_DAC_REG_RSTN 0x0040
+#define AXI_DAC_RSTN_CE_N BIT(2)
+#define AXI_DAC_RSTN_MMCM_RSTN BIT(1)
+#define AXI_DAC_RSTN_RSTN BIT(0)
+#define AXI_DAC_REG_CNTRL_1 0x0044
+#define AXI_DAC_SYNC BIT(0)
+#define AXI_DAC_REG_CNTRL_2 0x0048
+#define ADI_DAC_R1_MODE BIT(4)
+#define AXI_DAC_DRP_STATUS 0x0074
+#define AXI_DAC_DRP_LOCKED BIT(17)
+/* DAC Channel controls */
+#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0408 + (c) * 0x40)
+#define AXI_DAC_SCALE_SIGN BIT(15)
+#define AXI_DAC_SCALE_INT BIT(14)
+#define AXI_DAC_SCALE GENMASK(14, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40)
+#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40)
+#define AXI_DAC_PHASE GENMASK(31, 16)
+#define AXI_DAC_FREQUENCY GENMASK(15, 0)
+#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40)
+#define AXI_DAC_DATA_SEL GENMASK(3, 0)
+
+/* 360 degrees in rad */
+#define AXI_DAC_2_PI_MEGA 6283190
+enum {
+ AXI_DAC_DATA_INTERNAL_TONE,
+ AXI_DAC_DATA_DMA = 2,
+};
+
+struct axi_dac_state {
+ struct regmap *regmap;
+ struct device *dev;
+ /*
+ * lock to protect multiple accesses to the device registers and global
+ * data/variables.
+ */
+ struct mutex lock;
+ u64 dac_clk;
+ u32 reg_config;
+ bool int_tone;
+};
+
+static int axi_dac_enable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int __val;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_MMCM_RSTN);
+ if (ret)
+ return ret;
+ /*
+ * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
+ * designs really use it but if they don't we still get the lock bit
+ * set. So let's do it all the time so the code is generic.
+ */
+ ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
+ __val & AXI_DAC_DRP_LOCKED, 100, 1000);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
+ AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
+}
+
+static void axi_dac_disable(struct iio_backend *back)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ guard(mutex)(&st->lock);
+ regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+}
+
+static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
+ struct iio_dev *indio_dev)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ const char *dma_name;
+
+ if (device_property_read_string(st->dev, "dma-names", &dma_name))
+ dma_name = "tx";
+
+ return iio_dmaengine_buffer_setup_ext(st->dev, indio_dev, dma_name,
+ IIO_BUFFER_DIRECTION_OUT);
+}
+
+static void axi_dac_free_buffer(struct iio_backend *back,
+ struct iio_buffer *buffer)
+{
+ iio_dmaengine_buffer_free(buffer);
+}
+
+enum {
+ AXI_DAC_FREQ_TONE_1,
+ AXI_DAC_FREQ_TONE_2,
+ AXI_DAC_SCALE_TONE_1,
+ AXI_DAC_SCALE_TONE_2,
+ AXI_DAC_PHASE_TONE_1,
+ AXI_DAC_PHASE_TONE_2,
+};
+
+static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
+ unsigned int tone_2, unsigned int *freq)
+{
+ u32 reg, raw;
+ int ret;
+
+ if (!st->dac_clk) {
+ dev_err(st->dev, "Sampling rate is 0...\n");
+ return -EINVAL;
+ }
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
+ *freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
+
+ return 0;
+}
+
+static int axi_dac_frequency_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone_2)
+{
+ unsigned int freq;
+ int ret;
+
+ scoped_guard(mutex, &st->lock) {
+ ret = __axi_dac_frequency_get(st, chan->channel, tone_2, &freq);
+ if (ret)
+ return ret;
+ }
+
+ return sysfs_emit(buf, "%u\n", freq);
+}
+
+static int axi_dac_scale_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone_2)
+{
+ unsigned int scale, sign;
+ int ret, vals[2];
+ u32 reg, raw;
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw);
+ raw = FIELD_GET(AXI_DAC_SCALE, raw);
+ scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT);
+
+ vals[0] = scale / MEGA;
+ vals[1] = scale % MEGA;
+
+ if (sign) {
+ vals[0] *= -1;
+ if (!vals[0])
+ vals[1] *= -1;
+ }
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+ vals);
+}
+
+static int axi_dac_phase_get(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan, char *buf,
+ unsigned int tone_2)
+{
+ u32 reg, raw, phase;
+ int ret, vals[2];
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ ret = regmap_read(st->regmap, reg, &raw);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(AXI_DAC_PHASE, raw);
+ phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX);
+
+ vals[0] = phase / MEGA;
+ vals[1] = phase % MEGA;
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
+ vals);
+}
+
+static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan,
+ u64 sample_rate, unsigned int freq,
+ unsigned int tone_2)
+{
+ u32 reg;
+ u16 raw;
+ int ret;
+
+ if (!sample_rate || freq > sample_rate / 2) {
+ dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n",
+ freq, sample_rate);
+ return -EINVAL;
+ }
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
+
+ raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate);
+
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, raw);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+}
+
+static int axi_dac_frequency_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone_2)
+{
+ unsigned int freq;
+ int ret;
+
+ ret = kstrtou32(buf, 10, &freq);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+ ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq,
+ tone_2);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_scale_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone_2)
+{
+ int integer, frac, scale;
+ u32 raw = 0, reg;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+ if (ret)
+ return ret;
+
+ scale = integer * MEGA + frac;
+ if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
+ return -EINVAL;
+
+ /* format is 1.1.14 (sign, integer and fractional bits) */
+ if (scale < 0) {
+ raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
+ scale *= -1;
+ }
+
+ raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_write(st->regmap, reg, raw);
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_phase_set(struct axi_dac_state *st,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len, unsigned int tone_2)
+{
+ int integer, frac, phase;
+ u32 raw, reg;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
+ if (ret)
+ return ret;
+
+ phase = integer * MEGA + frac;
+ if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
+ return -EINVAL;
+
+ raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
+
+ if (tone_2)
+ reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
+ else
+ reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
+
+ guard(mutex)(&st->lock);
+ ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
+ FIELD_PREP(AXI_DAC_PHASE, raw));
+ if (ret)
+ return ret;
+
+ /* synchronize channels */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (private) {
+ case AXI_DAC_FREQ_TONE_1:
+ case AXI_DAC_FREQ_TONE_2:
+ return axi_dac_frequency_set(st, chan, buf, len,
+ private - AXI_DAC_FREQ_TONE_1);
+ case AXI_DAC_SCALE_TONE_1:
+ case AXI_DAC_SCALE_TONE_2:
+ return axi_dac_scale_set(st, chan, buf, len,
+ private - AXI_DAC_SCALE_TONE_1);
+ case AXI_DAC_PHASE_TONE_1:
+ case AXI_DAC_PHASE_TONE_2:
+ return axi_dac_phase_set(st, chan, buf, len,
+ private - AXI_DAC_PHASE_TONE_2);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (private) {
+ case AXI_DAC_FREQ_TONE_1:
+ case AXI_DAC_FREQ_TONE_2:
+ return axi_dac_frequency_get(st, chan, buf,
+ private - AXI_DAC_FREQ_TONE_1);
+ case AXI_DAC_SCALE_TONE_1:
+ case AXI_DAC_SCALE_TONE_2:
+ return axi_dac_scale_get(st, chan, buf,
+ private - AXI_DAC_SCALE_TONE_1);
+ case AXI_DAC_PHASE_TONE_1:
+ case AXI_DAC_PHASE_TONE_2:
+ return axi_dac_phase_get(st, chan, buf,
+ private - AXI_DAC_PHASE_TONE_1);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
+ IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
+ IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
+ IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
+ IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
+ IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
+ IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
+ {}
+};
+
+static int axi_dac_extend_chan(struct iio_backend *back,
+ struct iio_chan_spec *chan)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ if (chan->type != IIO_ALTVOLTAGE)
+ return -EINVAL;
+ if (st->reg_config & AXI_DDS_DISABLE)
+ /* nothing to extend */
+ return 0;
+
+ chan->ext_info = axi_dac_ext_info;
+
+ return 0;
+}
+
+static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+
+ switch (data) {
+ case IIO_BACKEND_INTERNAL_CONTINUOS_WAVE:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL,
+ AXI_DAC_DATA_INTERNAL_TONE);
+ case IIO_BACKEND_EXTERNAL:
+ return regmap_update_bits(st->regmap,
+ AXI_DAC_REG_CHAN_CNTRL_7(chan),
+ AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate)
+{
+ struct axi_dac_state *st = iio_backend_get_priv(back);
+ unsigned int freq;
+ int ret, tone;
+
+ if (!sample_rate)
+ return -EINVAL;
+ if (st->reg_config & AXI_DDS_DISABLE)
+ /* sample_rate has no meaning if DDS is disabled */
+ return 0;
+
+ guard(mutex)(&st->lock);
+ /*
+ * If dac_clk is 0 then this must be the first time we're being notified
+ * about the interface sample rate. Hence, just update our internal
+ * variable and bail... If it's not 0, then we get the current DDS
+ * frequency (for the old rate) and update the registers for the new
+ * sample rate.
+ */
+ if (!st->dac_clk) {
+ st->dac_clk = sample_rate;
+ return 0;
+ }
+
+ for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
+ ret = __axi_dac_frequency_get(st, chan, tone, &freq);
+ if (ret)
+ return ret;
+
+ ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
+ if (ret)
+ return ret;
+ }
+
+ st->dac_clk = sample_rate;
+
+ return 0;
+}
+
+static const struct iio_backend_ops axi_dac_generic = {
+ .enable = axi_dac_enable,
+ .disable = axi_dac_disable,
+ .request_buffer = axi_dac_request_buffer,
+ .free_buffer = axi_dac_free_buffer,
+ .extend_chan_spec = axi_dac_extend_chan,
+ .ext_info_set = axi_dac_ext_info_set,
+ .ext_info_get = axi_dac_ext_info_get,
+ .data_source_set = axi_dac_data_source_set,
+ .set_sample_rate = axi_dac_set_sample_rate,
+};
+
+static const struct regmap_config axi_dac_regmap_config = {
+ .val_bits = 32,
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x0800,
+};
+
+static int axi_dac_probe(struct platform_device *pdev)
+{
+ const unsigned int *expected_ver;
+ struct axi_dac_state *st;
+ void __iomem *base;
+ unsigned int ver;
+ struct clk *clk;
+ int ret;
+
+ st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ expected_ver = device_get_match_data(&pdev->dev);
+ if (!expected_ver)
+ return -ENODEV;
+
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ st->dev = &pdev->dev;
+ st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axi_dac_regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ /*
+ * Force disable the core. Up to the frontend to enable us. And we can
+ * still read/write registers...
+ */
+ ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
+ dev_err(&pdev->dev,
+ "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
+ ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
+ ADI_AXI_PCORE_VER_MINOR(*expected_ver),
+ ADI_AXI_PCORE_VER_PATCH(*expected_ver),
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+ return -ENODEV;
+ }
+
+ /* Let's get the core read only configuration */
+ ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
+ if (ret)
+ return ret;
+
+ /*
+ * In some designs, setting the R1_MODE bit to 0 (which is the default
+ * value) causes all channels of the frontend to be routed to the same
+ * DMA (so they are sampled together). This is for things like
+ * Multiple-Input and Multiple-Output (MIMO). As most of the times we
+ * want independent channels let's override the core's default value and
+ * set the R1_MODE bit.
+ */
+ ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE);
+ if (ret)
+ return ret;
+
+ mutex_init(&st->lock);
+ ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
+ if (ret)
+ return ret;
+
+ dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
+ ADI_AXI_PCORE_VER_MAJOR(ver),
+ ADI_AXI_PCORE_VER_MINOR(ver),
+ ADI_AXI_PCORE_VER_PATCH(ver));
+
+ return 0;
+}
+
+static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
+
+static const struct of_device_id axi_dac_of_match[] = {
+ { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
+ {}
+};
+MODULE_DEVICE_TABLE(of, axi_dac_of_match);
+
+static struct platform_driver axi_dac_driver = {
+ .driver = {
+ .name = "adi-axi-dac",
+ .of_match_table = axi_dac_of_match,
+ },
+ .probe = axi_dac_probe,
+};
+module_platform_driver(axi_dac_driver);
+
+MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
+MODULE_IMPORT_NS(IIO_BACKEND);
diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c
index fc8eb53c65be3..c4b1ba30f9354 100644
--- a/drivers/iio/dac/ltc2688.c
+++ b/drivers/iio/dac/ltc2688.c
@@ -746,26 +746,21 @@ static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max)
static int ltc2688_channel_config(struct ltc2688_state *st)
{
struct device *dev = &st->spi->dev;
- struct fwnode_handle *child;
u32 reg, clk_input, val, tmp[2];
int ret, span;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
struct ltc2688_chan *chan;
ret = fwnode_property_read_u32(child, "reg", &reg);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return dev_err_probe(dev, ret,
"Failed to get reg property\n");
- }
- if (reg >= LTC2688_DAC_CHANNELS) {
- fwnode_handle_put(child);
+ if (reg >= LTC2688_DAC_CHANNELS)
return dev_err_probe(dev, -EINVAL,
"reg bigger than: %d\n",
LTC2688_DAC_CHANNELS);
- }
val = 0;
chan = &st->channels[reg];
@@ -786,12 +781,10 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
if (!ret) {
span = ltc2688_span_lookup(st, (int)tmp[0] / 1000,
tmp[1] / 1000);
- if (span < 0) {
- fwnode_handle_put(child);
- return dev_err_probe(dev, -EINVAL,
+ if (span < 0)
+ return dev_err_probe(dev, span,
"output range not valid:[%d %d]\n",
tmp[0], tmp[1]);
- }
val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span);
}
@@ -800,17 +793,14 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
&clk_input);
if (!ret) {
if (clk_input >= LTC2688_CH_TGP_MAX) {
- fwnode_handle_put(child);
return dev_err_probe(dev, -EINVAL,
"toggle-dither-input inv value(%d)\n",
clk_input);
}
ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
/*
* 0 means software toggle which is the default mode.
@@ -844,11 +834,9 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg),
val);
- if (ret) {
- fwnode_handle_put(child);
- return dev_err_probe(dev, -EINVAL,
+ if (ret)
+ return dev_err_probe(dev, ret,
"failed to set chan settings\n");
- }
}
return 0;
diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c
index efb1269a77c1e..c5162b72951af 100644
--- a/drivers/iio/dac/ti-dac5571.c
+++ b/drivers/iio/dac/ti-dac5571.c
@@ -13,6 +13,7 @@
* https://www.ti.com/lit/ds/symlink/dac5573.pdf
* https://www.ti.com/lit/ds/symlink/dac6573.pdf
* https://www.ti.com/lit/ds/symlink/dac7573.pdf
+ * https://www.ti.com/lit/ds/symlink/dac081c081.pdf
* https://www.ti.com/lit/ds/symlink/dac121c081.pdf
*/
@@ -386,6 +387,7 @@ static void dac5571_remove(struct i2c_client *i2c)
}
static const struct of_device_id dac5571_of_id[] = {
+ {.compatible = "ti,dac081c081", .data = &dac5571_spec[single_8bit] },
{.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] },
{.compatible = "ti,dac5571", .data = &dac5571_spec[single_8bit] },
{.compatible = "ti,dac6571", .data = &dac5571_spec[single_10bit] },
@@ -401,6 +403,7 @@ static const struct of_device_id dac5571_of_id[] = {
MODULE_DEVICE_TABLE(of, dac5571_of_id);
static const struct i2c_device_id dac5571_id[] = {
+ {"dac081c081", (kernel_ulong_t)&dac5571_spec[single_8bit] },
{"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] },
{"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] },
{"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] },
diff --git a/drivers/iio/frequency/admfm2000.c b/drivers/iio/frequency/admfm2000.c
index c34d79e55a7c5..b2263b9afeda3 100644
--- a/drivers/iio/frequency/admfm2000.c
+++ b/drivers/iio/frequency/admfm2000.c
@@ -160,26 +160,21 @@ static int admfm2000_channel_config(struct admfm2000_state *st,
{
struct platform_device *pdev = to_platform_device(indio_dev->dev.parent);
struct device *dev = &pdev->dev;
- struct fwnode_handle *child;
struct gpio_desc **dsa;
struct gpio_desc **sw;
int ret, i;
bool mode;
u32 reg;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &reg);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return dev_err_probe(dev, ret,
"Failed to get reg property\n");
- }
- if (reg >= indio_dev->num_channels) {
- fwnode_handle_put(child);
+ if (reg >= indio_dev->num_channels)
return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n",
indio_dev->num_channels);
- }
if (fwnode_property_present(child, "adi,mixer-mode"))
mode = ADMFM2000_MIXER_MODE;
@@ -196,36 +191,29 @@ static int admfm2000_channel_config(struct admfm2000_state *st,
dsa = st->dsa2_gpios;
break;
default:
- fwnode_handle_put(child);
return -EINVAL;
}
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch",
i, GPIOD_OUT_LOW, NULL);
- if (IS_ERR(sw[i])) {
- fwnode_handle_put(child);
+ if (IS_ERR(sw[i]))
return dev_err_probe(dev, PTR_ERR(sw[i]),
"Failed to get gpios\n");
- }
}
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) {
dsa[i] = devm_fwnode_gpiod_get_index(dev, child,
"attenuation", i,
GPIOD_OUT_LOW, NULL);
- if (IS_ERR(dsa[i])) {
- fwnode_handle_put(child);
+ if (IS_ERR(dsa[i]))
return dev_err_probe(dev, PTR_ERR(dsa[i]),
"Failed to get gpios\n");
- }
}
ret = admfm2000_mode(indio_dev, reg, mode);
- if (ret) {
- fwnode_handle_put(child);
+ if (ret)
return ret;
- }
}
return 0;
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index 37e619827e8a1..6616729af5b77 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -613,6 +613,7 @@ static void max30102_remove(struct i2c_client *client)
}
static const struct i2c_device_id max30102_id[] = {
+ { "max30101", max30105 },
{ "max30102", max30102 },
{ "max30105", max30105 },
{}
@@ -620,6 +621,7 @@ static const struct i2c_device_id max30102_id[] = {
MODULE_DEVICE_TABLE(i2c, max30102_id);
static const struct of_device_id max30102_dt_ids[] = {
+ { .compatible = "maxim,max30101" },
{ .compatible = "maxim,max30102" },
{ .compatible = "maxim,max30105" },
{ }
diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c
index 1e5d0d4797b16..cdc4789213ba7 100644
--- a/drivers/iio/humidity/hdc3020.c
+++ b/drivers/iio/humidity/hdc3020.c
@@ -15,11 +15,14 @@
#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
#include <linux/units.h>
#include <asm/unaligned.h>
@@ -68,6 +71,8 @@
struct hdc3020_data {
struct i2c_client *client;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vdd_supply;
/*
* Ensure that the sensor configuration (currently only heater is
* supported) will not be changed during the process of reading
@@ -551,9 +556,53 @@ static const struct iio_info hdc3020_info = {
.write_event_value = hdc3020_write_thresh,
};
-static void hdc3020_stop(void *data)
+static int hdc3020_power_off(struct hdc3020_data *data)
{
- hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO);
+ hdc3020_exec_cmd(data, HDC3020_EXIT_AUTO);
+
+ if (data->reset_gpio)
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
+
+ return regulator_disable(data->vdd_supply);
+}
+
+static int hdc3020_power_on(struct hdc3020_data *data)
+{
+ int ret;
+
+ ret = regulator_enable(data->vdd_supply);
+ if (ret)
+ return ret;
+
+ fsleep(5000);
+
+ if (data->reset_gpio) {
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+ fsleep(3000);
+ }
+
+ if (data->client->irq) {
+ /*
+ * The alert output is activated by default upon power up,
+ * hardware reset, and soft reset. Clear the status register.
+ */
+ ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
+ if (ret) {
+ hdc3020_power_off(data);
+ return ret;
+ }
+ }
+
+ ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
+ if (ret)
+ hdc3020_power_off(data);
+
+ return ret;
+}
+
+static void hdc3020_exit(void *data)
+{
+ hdc3020_power_off(data);
}
static int hdc3020_probe(struct i2c_client *client)
@@ -569,6 +618,8 @@ static int hdc3020_probe(struct i2c_client *client)
if (!indio_dev)
return -ENOMEM;
+ dev_set_drvdata(&client->dev, indio_dev);
+
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
@@ -580,6 +631,26 @@ static int hdc3020_probe(struct i2c_client *client)
indio_dev->info = &hdc3020_info;
indio_dev->channels = hdc3020_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
+
+ data->vdd_supply = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->vdd_supply))
+ return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply),
+ "Unable to get VDD regulator\n");
+
+ data->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio),
+ "Cannot get reset GPIO\n");
+
+ ret = hdc3020_power_on(data);
+ if (ret)
+ return dev_err_probe(&client->dev, ret, "Power on failed\n");
+
+ ret = devm_add_action_or_reset(&data->client->dev, hdc3020_exit, data);
+ if (ret)
+ return ret;
+
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, hdc3020_interrupt_handler,
@@ -588,25 +659,8 @@ static int hdc3020_probe(struct i2c_client *client)
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to request IRQ\n");
-
- /*
- * The alert output is activated by default upon power up,
- * hardware reset, and soft reset. Clear the status register.
- */
- ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
- if (ret)
- return ret;
}
- ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
- if (ret)
- return dev_err_probe(&client->dev, ret,
- "Unable to set up measurement\n");
-
- ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data);
- if (ret)
- return ret;
-
ret = devm_iio_device_register(&data->client->dev, indio_dev);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to add device");
@@ -614,6 +668,24 @@ static int hdc3020_probe(struct i2c_client *client)
return 0;
}
+static int hdc3020_suspend(struct device *dev)
+{
+ struct iio_dev *iio_dev = dev_get_drvdata(dev);
+ struct hdc3020_data *data = iio_priv(iio_dev);
+
+ return hdc3020_power_off(data);
+}
+
+static int hdc3020_resume(struct device *dev)
+{
+ struct iio_dev *iio_dev = dev_get_drvdata(dev);
+ struct hdc3020_data *data = iio_priv(iio_dev);
+
+ return hdc3020_power_on(data);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(hdc3020_pm_ops, hdc3020_suspend, hdc3020_resume);
+
static const struct i2c_device_id hdc3020_id[] = {
{ "hdc3020" },
{ "hdc3021" },
@@ -633,6 +705,7 @@ MODULE_DEVICE_TABLE(of, hdc3020_dt_ids);
static struct i2c_driver hdc3020_driver = {
.driver = {
.name = "hdc3020",
+ .pm = pm_sleep_ptr(&hdc3020_pm_ops),
.of_match_table = hdc3020_dt_ids,
},
.probe = hdc3020_probe,
diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
index 2a413da87b760..87627d116eff0 100644
--- a/drivers/iio/humidity/hts221_core.c
+++ b/drivers/iio/humidity/hts221_core.c
@@ -573,7 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name,
if (!iio_dev)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)iio_dev);
+ dev_set_drvdata(dev, iio_dev);
hw = iio_priv(iio_dev);
hw->name = name;
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
index 0e290c807b0f9..0566340b26604 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h
@@ -22,6 +22,7 @@ enum inv_icm42600_chip {
INV_CHIP_ICM42602,
INV_CHIP_ICM42605,
INV_CHIP_ICM42622,
+ INV_CHIP_ICM42688,
INV_CHIP_ICM42631,
INV_CHIP_NB,
};
@@ -304,6 +305,7 @@ struct inv_icm42600_state {
#define INV_ICM42600_WHOAMI_ICM42602 0x41
#define INV_ICM42600_WHOAMI_ICM42605 0x42
#define INV_ICM42600_WHOAMI_ICM42622 0x46
+#define INV_ICM42600_WHOAMI_ICM42688 0x47
#define INV_ICM42600_WHOAMI_ICM42631 0x5C
/* User bank 1 (MSB 0x10) */
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
index a5e81906e37ec..82e0a2e2ad706 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c
@@ -87,6 +87,11 @@ static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = {
.name = "icm42622",
.conf = &inv_icm42600_default_conf,
},
+ [INV_CHIP_ICM42688] = {
+ .whoami = INV_ICM42600_WHOAMI_ICM42688,
+ .name = "icm42688",
+ .conf = &inv_icm42600_default_conf,
+ },
[INV_CHIP_ICM42631] = {
.whoami = INV_ICM42600_WHOAMI_ICM42631,
.name = "icm42631",
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
index 1af559403ba66..ebb28f84ba985 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c
@@ -85,6 +85,9 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
.compatible = "invensense,icm42622",
.data = (void *)INV_CHIP_ICM42622,
}, {
+ .compatible = "invensense,icm42688",
+ .data = (void *)INV_CHIP_ICM42688,
+ }, {
.compatible = "invensense,icm42631",
.data = (void *)INV_CHIP_ICM42631,
},
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
index 6be4ac7949379..50217a10e0bbf 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c
@@ -81,6 +81,9 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
.compatible = "invensense,icm42622",
.data = (void *)INV_CHIP_ICM42622,
}, {
+ .compatible = "invensense,icm42688",
+ .data = (void *)INV_CHIP_ICM42688,
+ }, {
.compatible = "invensense,icm42631",
.data = (void *)INV_CHIP_ICM42631,
},
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index 0e94e5335e937..14d95f34e981c 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -15,6 +15,8 @@
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/math64.h>
+#include <linux/minmax.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -287,7 +289,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
};
static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep,
- int clock, int temp_dis)
+ bool cycle, int clock, int temp_dis)
{
u8 val;
@@ -301,6 +303,8 @@ static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep
val |= INV_MPU6050_BIT_TEMP_DIS;
if (sleep)
val |= INV_MPU6050_BIT_SLEEP;
+ if (cycle)
+ val |= INV_MPU6050_BIT_CYCLE;
dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val);
return regmap_write(st->map, st->reg->pwr_mgmt_1, val);
@@ -316,7 +320,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
case INV_MPU6000:
case INV_MPU9150:
/* old chips: switch clock manually */
- ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1);
+ ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1);
if (ret)
return ret;
st->chip_config.clk = clock;
@@ -332,7 +336,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
unsigned int mask)
{
- unsigned int sleep;
+ unsigned int sleep, val;
u8 pwr_mgmt2, user_ctrl;
int ret;
@@ -345,12 +349,20 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
mask &= ~INV_MPU6050_SENSOR_TEMP;
if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en)
mask &= ~INV_MPU6050_SENSOR_MAGN;
+ if (mask & INV_MPU6050_SENSOR_WOM && en == st->chip_config.wom_en)
+ mask &= ~INV_MPU6050_SENSOR_WOM;
+
+ /* force accel on if WoM is on and not going off */
+ if (!en && (mask & INV_MPU6050_SENSOR_ACCL) && st->chip_config.wom_en &&
+ !(mask & INV_MPU6050_SENSOR_WOM))
+ mask &= ~INV_MPU6050_SENSOR_ACCL;
+
if (mask == 0)
return 0;
/* turn on/off temperature sensor */
if (mask & INV_MPU6050_SENSOR_TEMP) {
- ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en);
+ ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en);
if (ret)
return ret;
st->chip_config.temp_en = en;
@@ -439,6 +451,16 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
}
}
+ /* enable/disable accel intelligence control */
+ if (mask & INV_MPU6050_SENSOR_WOM) {
+ val = en ? INV_MPU6500_BIT_ACCEL_INTEL_EN |
+ INV_MPU6500_BIT_ACCEL_INTEL_MODE : 0;
+ ret = regmap_write(st->map, INV_MPU6500_REG_ACCEL_INTEL_CTRL, val);
+ if (ret)
+ return ret;
+ st->chip_config.wom_en = en;
+ }
+
return 0;
}
@@ -447,7 +469,7 @@ static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
{
int result;
- result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1);
+ result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1);
if (result)
return result;
@@ -477,22 +499,9 @@ static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st,
return regmap_write(st->map, st->reg->gyro_config, data);
}
-/*
- * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
- *
- * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
- * MPU6500 and above have a dedicated register for accelerometer
- */
-static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
- enum inv_mpu6050_filter_e val)
+static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st,
+ enum inv_mpu6050_filter_e val)
{
- int result;
-
- result = regmap_write(st->map, st->reg->lpf, val);
- if (result)
- return result;
-
- /* set accel lpf */
switch (st->chip_type) {
case INV_MPU6050:
case INV_MPU6000:
@@ -512,6 +521,25 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
}
/*
+ * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
+ *
+ * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
+ * MPU6500 and above have a dedicated register for accelerometer
+ */
+static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
+ enum inv_mpu6050_filter_e val)
+{
+ int result;
+
+ result = regmap_write(st->map, st->reg->lpf, val);
+ if (result)
+ return result;
+
+ /* set accel lpf */
+ return inv_mpu6050_set_accel_lpf_regs(st, val);
+}
+
+/*
* inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
*
* Initial configuration:
@@ -893,6 +921,317 @@ error_write_raw_unlock:
return result;
}
+static u64 inv_mpu6050_convert_wom_to_roc(unsigned int threshold, unsigned int freq_div)
+{
+ /* 4mg per LSB converted in m/s² in micro (1000000) */
+ const unsigned int convert = 4U * 9807U;
+ u64 value;
+
+ value = threshold * convert;
+
+ /* compute the differential by multiplying by the frequency */
+ return div_u64(value * INV_MPU6050_INTERNAL_FREQ_HZ, freq_div);
+}
+
+static unsigned int inv_mpu6050_convert_roc_to_wom(u64 roc, unsigned int freq_div)
+{
+ /* 4mg per LSB converted in m/s² in micro (1000000) */
+ const unsigned int convert = 4U * 9807U;
+ u64 value;
+
+ /* return 0 only if roc is 0 */
+ if (roc == 0)
+ return 0;
+
+ value = div_u64(roc * freq_div, convert * INV_MPU6050_INTERNAL_FREQ_HZ);
+
+ /* limit value to 8 bits and prevent 0 */
+ return min(255, max(1, value));
+}
+
+static int inv_mpu6050_set_wom_int(struct inv_mpu6050_state *st, bool on)
+{
+ unsigned int reg_val, val;
+
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ case INV_MPU6500:
+ case INV_MPU6515:
+ case INV_MPU6880:
+ case INV_MPU6000:
+ case INV_MPU9150:
+ case INV_MPU9250:
+ case INV_MPU9255:
+ reg_val = INV_MPU6500_BIT_WOM_INT_EN;
+ break;
+ default:
+ reg_val = INV_ICM20608_BIT_WOM_INT_EN;
+ break;
+ }
+
+ val = on ? reg_val : 0;
+
+ return regmap_update_bits(st->map, st->reg->int_enable, reg_val, val);
+}
+
+static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, u64 value,
+ unsigned int freq_div)
+{
+ unsigned int threshold;
+ int result;
+
+ /* convert roc to wom threshold and convert back to handle clipping */
+ threshold = inv_mpu6050_convert_roc_to_wom(value, freq_div);
+ value = inv_mpu6050_convert_wom_to_roc(threshold, freq_div);
+
+ dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold);
+
+ switch (st->chip_type) {
+ case INV_ICM20609:
+ case INV_ICM20689:
+ case INV_ICM20600:
+ case INV_ICM20602:
+ case INV_ICM20690:
+ st->data[0] = threshold;
+ st->data[1] = threshold;
+ st->data[2] = threshold;
+ result = regmap_bulk_write(st->map, INV_ICM20609_REG_ACCEL_WOM_X_THR,
+ st->data, 3);
+ break;
+ default:
+ result = regmap_write(st->map, INV_MPU6500_REG_WOM_THRESHOLD, threshold);
+ break;
+ }
+ if (result)
+ return result;
+
+ st->chip_config.roc_threshold = value;
+
+ return 0;
+}
+
+static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div,
+ unsigned int *lp_div)
+{
+ static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256};
+ static const unsigned int reg_values[] = {
+ INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ,
+ INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ,
+ INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ,
+ INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ,
+ };
+ unsigned int val, i;
+
+ switch (st->chip_type) {
+ case INV_ICM20609:
+ case INV_ICM20689:
+ case INV_ICM20600:
+ case INV_ICM20602:
+ case INV_ICM20690:
+ /* nothing to do */
+ *lp_div = INV_MPU6050_FREQ_DIVIDER(st);
+ return 0;
+ default:
+ break;
+ }
+
+ /* found the nearest superior frequency divider */
+ i = ARRAY_SIZE(reg_values) - 1;
+ val = reg_values[i];
+ *lp_div = freq_dividers[i];
+ for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) {
+ if (freq_div <= freq_dividers[i]) {
+ val = reg_values[i];
+ *lp_div = freq_dividers[i];
+ break;
+ }
+ }
+
+ dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val);
+ return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val);
+}
+
+static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on)
+{
+ unsigned int lp_div;
+ int result;
+
+ if (on) {
+ /* set low power ODR */
+ result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div);
+ if (result)
+ return result;
+ /* disable accel low pass filter */
+ result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF);
+ if (result)
+ return result;
+ /* update wom threshold with new low-power frequency divider */
+ result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div);
+ if (result)
+ return result;
+ /* set cycle mode */
+ result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1);
+ } else {
+ /* disable cycle mode */
+ result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1);
+ if (result)
+ return result;
+ /* restore wom threshold */
+ result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
+ INV_MPU6050_FREQ_DIVIDER(st));
+ if (result)
+ return result;
+ /* restore accel low pass filter */
+ result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf);
+ }
+
+ return result;
+}
+
+static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en)
+{
+ struct device *pdev = regmap_get_device(st->map);
+ unsigned int mask;
+ int result;
+
+ if (en) {
+ result = pm_runtime_resume_and_get(pdev);
+ if (result)
+ return result;
+
+ mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_WOM;
+ result = inv_mpu6050_switch_engine(st, true, mask);
+ if (result)
+ goto error_suspend;
+
+ result = inv_mpu6050_set_wom_int(st, true);
+ if (result)
+ goto error_suspend;
+ } else {
+ result = inv_mpu6050_set_wom_int(st, false);
+ if (result)
+ dev_err(pdev, "error %d disabling WoM interrupt bit", result);
+
+ /* disable only WoM and let accel be disabled by autosuspend */
+ result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_WOM);
+ if (result) {
+ dev_err(pdev, "error %d disabling WoM force off", result);
+ /* force WoM off */
+ st->chip_config.wom_en = false;
+ }
+
+ pm_runtime_mark_last_busy(pdev);
+ pm_runtime_put_autosuspend(pdev);
+ }
+
+ return result;
+
+error_suspend:
+ pm_runtime_mark_last_busy(pdev);
+ pm_runtime_put_autosuspend(pdev);
+ return result;
+}
+
+static int inv_mpu6050_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ /* support only WoM (accel roc rising) event */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ return st->chip_config.wom_en ? 1 : 0;
+}
+
+static int inv_mpu6050_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ int enable;
+
+ /* support only WoM (accel roc rising) event */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING)
+ return -EINVAL;
+
+ enable = !!state;
+
+ guard(mutex)(&st->lock);
+
+ if (st->chip_config.wom_en == enable)
+ return 0;
+
+ return inv_mpu6050_enable_wom(st, enable);
+}
+
+static int inv_mpu6050_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ u32 rem;
+
+ /* support only WoM (accel roc rising) event value */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ /* return value in micro */
+ *val = div_u64_rem(st->chip_config.roc_threshold, 1000000U, &rem);
+ *val2 = rem;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_mpu6050_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ struct device *pdev = regmap_get_device(st->map);
+ u64 value;
+ int result;
+
+ /* support only WoM (accel roc rising) event value */
+ if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
+ dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ result = pm_runtime_resume_and_get(pdev);
+ if (result)
+ return result;
+
+ value = (u64)val * 1000000ULL + (u64)val2;
+ result = inv_mpu6050_set_wom_threshold(st, value, INV_MPU6050_FREQ_DIVIDER(st));
+
+ pm_runtime_mark_last_busy(pdev);
+ pm_runtime_put_autosuspend(pdev);
+
+ return result;
+}
+
/*
* inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
*
@@ -989,6 +1328,12 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
if (result)
goto fifo_rate_fail_power_off;
+ /* update wom threshold since roc is dependent on sampling frequency */
+ result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
+ INV_MPU6050_FREQ_DIVIDER(st));
+ if (result)
+ goto fifo_rate_fail_power_off;
+
pm_runtime_mark_last_busy(pdev);
fifo_rate_fail_power_off:
pm_runtime_put_autosuspend(pdev);
@@ -1089,6 +1434,15 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
{ }
};
+static const struct iio_event_spec inv_wom_events[] = {
+ {
+ .type = IIO_EV_TYPE_ROC,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
#define INV_MPU6050_CHAN(_type, _channel2, _index) \
{ \
.type = _type, \
@@ -1124,7 +1478,31 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
}, \
}
-static const struct iio_chan_spec inv_mpu_channels[] = {
+#define INV_MPU6050_EVENT_CHAN(_type, _channel2, _events, _events_nb) \
+{ \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = _channel2, \
+ .event_spec = _events, \
+ .num_event_specs = _events_nb, \
+ .scan_index = -1, \
+}
+
+static const struct iio_chan_spec inv_mpu6050_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
+
+ INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
+
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
+
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+};
+
+static const struct iio_chan_spec inv_mpu6500_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
@@ -1136,6 +1514,9 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
+
+ INV_MPU6050_EVENT_CHAN(IIO_ACCEL, IIO_MOD_X_OR_Y_OR_Z,
+ inv_wom_events, ARRAY_SIZE(inv_wom_events)),
};
#define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL \
@@ -1326,6 +1707,10 @@ static const struct iio_info mpu_info = {
.write_raw = &inv_mpu6050_write_raw,
.write_raw_get_fmt = &inv_write_raw_get_fmt,
.attrs = &inv_attribute_group,
+ .read_event_config = inv_mpu6050_read_event_config,
+ .write_event_config = inv_mpu6050_write_event_config,
+ .read_event_value = inv_mpu6050_read_event_value,
+ .write_event_value = inv_mpu6050_write_event_value,
.validate_trigger = inv_mpu6050_validate_trigger,
.debugfs_reg_access = &inv_mpu6050_reg_access,
};
@@ -1537,6 +1922,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
irq_type);
return -EINVAL;
}
+ device_set_wakeup_capable(dev, true);
st->vdd_supply = devm_regulator_get(dev, "vdd");
if (IS_ERR(st->vdd_supply))
@@ -1613,6 +1999,12 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
return result;
switch (chip_type) {
+ case INV_MPU6000:
+ case INV_MPU6050:
+ indio_dev->channels = inv_mpu6050_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ break;
case INV_MPU9150:
indio_dev->channels = inv_mpu9150_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels);
@@ -1626,13 +2018,13 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
break;
case INV_ICM20600:
case INV_ICM20602:
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+ indio_dev->channels = inv_mpu6500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
break;
default:
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+ indio_dev->channels = inv_mpu6500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
indio_dev->available_scan_masks = inv_mpu_scan_masks;
break;
}
@@ -1641,9 +2033,18 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
* auxiliary device in use. Otherwise Going back to 6-axis only.
*/
if (st->magn_disabled) {
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
- indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ switch (chip_type) {
+ case INV_MPU9150:
+ indio_dev->channels = inv_mpu6050_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ break;
+ default:
+ indio_dev->channels = inv_mpu6500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ break;
+ }
}
indio_dev->info = &mpu_info;
@@ -1687,16 +2088,27 @@ static int inv_mpu_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ bool wakeup;
int result;
- mutex_lock(&st->lock);
- result = inv_mpu_core_enable_regulator_vddio(st);
- if (result)
- goto out_unlock;
+ guard(mutex)(&st->lock);
- result = inv_mpu6050_set_power_itg(st, true);
- if (result)
- goto out_unlock;
+ wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
+
+ if (wakeup) {
+ enable_irq(st->irq);
+ disable_irq_wake(st->irq);
+ result = inv_mpu6050_set_wom_lp(st, false);
+ if (result)
+ return result;
+ } else {
+ result = inv_mpu_core_enable_regulator_vddio(st);
+ if (result)
+ return result;
+ result = inv_mpu6050_set_power_itg(st, true);
+ if (result)
+ return result;
+ }
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@@ -1704,14 +2116,17 @@ static int inv_mpu_resume(struct device *dev)
result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
if (result)
- goto out_unlock;
+ return result;
+
+ if (st->chip_config.wom_en && !wakeup) {
+ result = inv_mpu6050_set_wom_int(st, true);
+ if (result)
+ return result;
+ }
if (iio_buffer_enabled(indio_dev))
result = inv_mpu6050_prepare_fifo(st, true);
-out_unlock:
- mutex_unlock(&st->lock);
-
return result;
}
@@ -1719,23 +2134,30 @@ static int inv_mpu_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ bool wakeup;
int result;
- mutex_lock(&st->lock);
+ guard(mutex)(&st->lock);
st->suspended_sensors = 0;
- if (pm_runtime_suspended(dev)) {
- result = 0;
- goto out_unlock;
- }
+ if (pm_runtime_suspended(dev))
+ return 0;
if (iio_buffer_enabled(indio_dev)) {
result = inv_mpu6050_prepare_fifo(st, false);
if (result)
- goto out_unlock;
+ return result;
+ }
+
+ wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
+
+ if (st->chip_config.wom_en && !wakeup) {
+ result = inv_mpu6050_set_wom_int(st, false);
+ if (result)
+ return result;
}
- if (st->chip_config.accl_en)
+ if (st->chip_config.accl_en && !wakeup)
st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
if (st->chip_config.gyro_en)
st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
@@ -1743,19 +2165,26 @@ static int inv_mpu_suspend(struct device *dev)
st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
if (st->chip_config.magn_en)
st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
+ if (st->chip_config.wom_en && !wakeup)
+ st->suspended_sensors |= INV_MPU6050_SENSOR_WOM;
result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
if (result)
- goto out_unlock;
-
- result = inv_mpu6050_set_power_itg(st, false);
- if (result)
- goto out_unlock;
+ return result;
- inv_mpu_core_disable_regulator_vddio(st);
-out_unlock:
- mutex_unlock(&st->lock);
+ if (wakeup) {
+ result = inv_mpu6050_set_wom_lp(st, true);
+ if (result)
+ return result;
+ enable_irq_wake(st->irq);
+ disable_irq(st->irq);
+ } else {
+ result = inv_mpu6050_set_power_itg(st, false);
+ if (result)
+ return result;
+ inv_mpu_core_disable_regulator_vddio(st);
+ }
- return result;
+ return 0;
}
static int inv_mpu_runtime_suspend(struct device *dev)
@@ -1767,7 +2196,8 @@ static int inv_mpu_runtime_suspend(struct device *dev)
mutex_lock(&st->lock);
sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
- INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
+ INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN |
+ INV_MPU6050_SENSOR_WOM;
ret = inv_mpu6050_switch_engine(st, false, sensors);
if (ret)
goto out_unlock;
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
index 5950e2419ebb5..e1c0c51468761 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -88,11 +88,12 @@ enum inv_devices {
INV_NUM_PARTS
};
-/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */
+/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */
#define INV_MPU6050_SENSOR_ACCL BIT(0)
#define INV_MPU6050_SENSOR_GYRO BIT(1)
#define INV_MPU6050_SENSOR_TEMP BIT(2)
#define INV_MPU6050_SENSOR_MAGN BIT(3)
+#define INV_MPU6050_SENSOR_WOM BIT(4)
/**
* struct inv_mpu6050_chip_config - Cached chip configuration data.
@@ -104,11 +105,13 @@ enum inv_devices {
* @gyro_en: gyro engine enabled
* @temp_en: temperature sensor enabled
* @magn_en: magn engine (i2c master) enabled
+ * @wom_en: Wake-on-Motion enabled
* @accl_fifo_enable: enable accel data output
* @gyro_fifo_enable: enable gyro data output
* @temp_fifo_enable: enable temp data output
* @magn_fifo_enable: enable magn data output
* @divider: chip sample rate divider (sample rate divider - 1)
+ * @roc_threshold: save ROC threshold (WoM) set value
*/
struct inv_mpu6050_chip_config {
unsigned int clk:3;
@@ -119,12 +122,14 @@ struct inv_mpu6050_chip_config {
unsigned int gyro_en:1;
unsigned int temp_en:1;
unsigned int magn_en:1;
+ unsigned int wom_en:1;
unsigned int accl_fifo_enable:1;
unsigned int gyro_fifo_enable:1;
unsigned int temp_fifo_enable:1;
unsigned int magn_fifo_enable:1;
u8 divider;
u8 user_ctrl;
+ u64 roc_threshold;
};
/*
@@ -180,6 +185,7 @@ struct inv_mpu6050_hw {
* @magn_orient: magnetometer sensor chip orientation if available.
* @suspended_sensors: sensors mask of sensors turned off for suspend
* @data: read buffer used for bulk reads.
+ * @it_timestamp: interrupt timestamp.
*/
struct inv_mpu6050_state {
struct mutex lock;
@@ -205,6 +211,7 @@ struct inv_mpu6050_state {
unsigned int suspended_sensors;
bool level_shifter;
u8 *data;
+ s64 it_timestamp;
};
/*register and associated bit definition*/
@@ -256,12 +263,16 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_INT_ENABLE 0x38
#define INV_MPU6050_BIT_DATA_RDY_EN 0x01
#define INV_MPU6050_BIT_DMP_INT_EN 0x02
+#define INV_MPU6500_BIT_WOM_INT_EN BIT(6)
+#define INV_ICM20608_BIT_WOM_INT_EN GENMASK(7, 5)
#define INV_MPU6050_REG_RAW_ACCEL 0x3B
#define INV_MPU6050_REG_TEMPERATURE 0x41
#define INV_MPU6050_REG_RAW_GYRO 0x43
#define INV_MPU6050_REG_INT_STATUS 0x3A
+#define INV_MPU6500_BIT_WOM_INT BIT(6)
+#define INV_ICM20608_BIT_WOM_INT GENMASK(7, 5)
#define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
@@ -294,6 +305,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
#define INV_MPU6050_BIT_H_RESET 0x80
#define INV_MPU6050_BIT_SLEEP 0x40
+#define INV_MPU6050_BIT_CYCLE 0x20
#define INV_MPU6050_BIT_TEMP_DIS 0x08
#define INV_MPU6050_BIT_CLK_MASK 0x7
@@ -301,6 +313,11 @@ struct inv_mpu6050_state {
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
+/* ICM20609 registers */
+#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20
+#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21
+#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22
+
/* ICM20602 register */
#define INV_ICM20602_REG_I2C_IF 0x70
#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
@@ -320,6 +337,11 @@ struct inv_mpu6050_state {
/* mpu6500 registers */
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
#define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0
+#define INV_MPU6500_REG_LP_ODR 0x1E
+#define INV_MPU6500_REG_WOM_THRESHOLD 0x1F
+#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69
+#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7)
+#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6)
#define INV_MPU6500_REG_ACCEL_OFFSET 0x77
/* delay time in milliseconds */
@@ -432,6 +454,18 @@ enum inv_mpu6050_filter_e {
NUM_MPU6050_FILTER
};
+enum inv_mpu6050_lposc_e {
+ INV_MPU6050_LPOSC_4HZ = 4,
+ INV_MPU6050_LPOSC_8HZ,
+ INV_MPU6050_LPOSC_16HZ,
+ INV_MPU6050_LPOSC_31HZ,
+ INV_MPU6050_LPOSC_62HZ,
+ INV_MPU6050_LPOSC_125HZ,
+ INV_MPU6050_LPOSC_250HZ,
+ INV_MPU6050_LPOSC_500HZ,
+ NUM_MPU6050_LPOSC,
+};
+
/* IIO attribute address */
enum INV_MPU6050_IIO_ATTR_ADDR {
ATTR_GYRO_MATRIX,
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
index d4f9b5d8d28d6..86465226f7e10 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev)
reset_fifo_fail:
dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
- result = regmap_write(st->map, st->reg->int_enable,
- INV_MPU6050_BIT_DATA_RDY_EN);
-
- return result;
+ return regmap_update_bits(st->map, st->reg->int_enable,
+ INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
}
/*
@@ -53,21 +51,10 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
u32 fifo_period;
s64 timestamp;
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
- int int_status;
size_t i, nb;
mutex_lock(&st->lock);
- /* ack interrupt and check status */
- result = regmap_read(st->map, st->reg->int_status, &int_status);
- if (result) {
- dev_err(regmap_get_device(st->map),
- "failed to ack interrupt\n");
- goto flush_fifo;
- }
- if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT))
- goto end_session;
-
if (!(st->chip_config.accl_fifo_enable |
st->chip_config.gyro_fifo_enable |
st->chip_config.magn_fifo_enable))
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
index e6e6e94452a32..1b603567ccc80 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
@@ -6,6 +6,7 @@
#include <linux/pm_runtime.h>
#include <linux/iio/common/inv_sensors_timestamp.h>
+#include <linux/iio/events.h>
#include "inv_mpu_iio.h"
@@ -135,11 +136,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
ret = regmap_write(st->map, st->reg->user_ctrl, d);
if (ret)
return ret;
- /* enable interrupt */
- ret = regmap_write(st->map, st->reg->int_enable,
- INV_MPU6050_BIT_DATA_RDY_EN);
+ /* enable data interrupt */
+ ret = regmap_update_bits(st->map, st->reg->int_enable,
+ INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
} else {
- ret = regmap_write(st->map, st->reg->int_enable, 0);
+ /* disable data interrupt */
+ ret = regmap_update_bits(st->map, st->reg->int_enable,
+ INV_MPU6050_BIT_DATA_RDY_EN, 0);
if (ret)
return ret;
ret = regmap_write(st->map, st->reg->fifo_en, 0);
@@ -172,9 +175,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
return result;
/*
* In case autosuspend didn't trigger, turn off first not
- * required sensors.
+ * required sensors excepted WoM
*/
- result = inv_mpu6050_switch_engine(st, false, ~scan);
+ result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM);
if (result)
goto error_power_off;
result = inv_mpu6050_switch_engine(st, true, scan);
@@ -226,6 +229,65 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
};
+static irqreturn_t inv_mpu6050_interrupt_timestamp(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ st->it_timestamp = iio_get_time_ns(indio_dev);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t inv_mpu6050_interrupt_handle(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ unsigned int int_status, wom_bits;
+ u64 ev_code;
+ int result;
+
+ switch (st->chip_type) {
+ case INV_MPU6050:
+ case INV_MPU6500:
+ case INV_MPU6515:
+ case INV_MPU6880:
+ case INV_MPU6000:
+ case INV_MPU9150:
+ case INV_MPU9250:
+ case INV_MPU9255:
+ wom_bits = INV_MPU6500_BIT_WOM_INT;
+ break;
+ default:
+ wom_bits = INV_ICM20608_BIT_WOM_INT;
+ break;
+ }
+
+ scoped_guard(mutex, &st->lock) {
+ /* ack interrupt and check status */
+ result = regmap_read(st->map, st->reg->int_status, &int_status);
+ if (result) {
+ dev_err(regmap_get_device(st->map), "failed to ack interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ /* handle WoM event */
+ if (st->chip_config.wom_en && (int_status & wom_bits)) {
+ ev_code = IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING);
+ iio_push_event(indio_dev, ev_code, st->it_timestamp);
+ }
+ }
+
+ /* handle raw data interrupt */
+ if (int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT) {
+ indio_dev->pollfunc->timestamp = st->it_timestamp;
+ iio_trigger_poll_nested(st->trig);
+ }
+
+ return IRQ_HANDLED;
+}
+
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
{
int ret;
@@ -238,11 +300,10 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
if (!st->trig)
return -ENOMEM;
- ret = devm_request_irq(&indio_dev->dev, st->irq,
- &iio_trigger_generic_data_rdy_poll,
- irq_type,
- "inv_mpu",
- st->trig);
+ ret = devm_request_threaded_irq(&indio_dev->dev, st->irq,
+ &inv_mpu6050_interrupt_timestamp,
+ &inv_mpu6050_interrupt_handle,
+ irq_type, "inv_mpu", indio_dev);
if (ret)
return ret;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 0716986f98129..937ff9c5a74c7 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -2726,7 +2726,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
if (!hw)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)hw);
+ dev_set_drvdata(dev, hw);
mutex_init(&hw->fifo_lock);
mutex_init(&hw->conf_lock);
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 2fea2bbbe47fd..f08ed6d70ae59 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -43,10 +43,12 @@
#include <linux/types.h>
#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
struct iio_backend {
struct list_head entry;
const struct iio_backend_ops *ops;
+ struct device *frontend_dev;
struct device *dev;
struct module *owner;
void *priv;
@@ -186,6 +188,44 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
}
EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
+/**
+ * iio_backend_data_source_set - Select data source
+ * @back: Backend device
+ * @chan: Channel number
+ * @data: Data source
+ *
+ * A given backend may have different sources to stream/sync data. This allows
+ * to choose that source.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data)
+{
+ if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
+ return -EINVAL;
+
+ return iio_backend_op_call(back, data_source_set, chan, data);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
+
+/**
+ * iio_backend_set_sampling_freq - Set channel sampling rate
+ * @back: Backend device
+ * @chan: Channel number
+ * @sample_rate_hz: Sample rate
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate_hz)
+{
+ return iio_backend_op_call(back, set_sample_rate, chan, sample_rate_hz);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
+
static void iio_backend_free_buffer(void *arg)
{
struct iio_backend_buffer_pair *pair = arg;
@@ -231,6 +271,143 @@ int devm_iio_backend_request_buffer(struct device *dev,
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
+static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev)
+{
+ struct iio_backend *back = ERR_PTR(-ENODEV), *iter;
+
+ /*
+ * We deliberately go through all backends even after finding a match.
+ * The reason is that we want to catch frontend devices which have more
+ * than one backend in which case returning the first we find is bogus.
+ * For those cases, frontends need to explicitly define
+ * get_iio_backend() in struct iio_info.
+ */
+ guard(mutex)(&iio_back_lock);
+ list_for_each_entry(iter, &iio_back_list, entry) {
+ if (dev == iter->frontend_dev) {
+ if (!IS_ERR(back)) {
+ dev_warn(dev,
+ "Multiple backends! get_iio_backend() needs to be implemented");
+ return ERR_PTR(-ENODEV);
+ }
+
+ back = iter;
+ }
+ }
+
+ return back;
+}
+
+/**
+ * iio_backend_ext_info_get - IIO ext_info read callback
+ * @indio_dev: IIO device
+ * @private: Data private to the driver
+ * @chan: IIO channel
+ * @buf: Buffer where to place the attribute data
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (through iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not have
+ * a way to get the backend from indio_dev). This is the getter.
+ *
+ * RETURNS:
+ * Number of bytes written to buf, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct iio_backend *back;
+
+ /*
+ * The below should work for the majority of the cases. It will not work
+ * when one frontend has multiple backends in which case we'll need a
+ * new callback in struct iio_info so we can directly request the proper
+ * backend from the frontend. Anyways, let's only introduce new options
+ * when really needed...
+ */
+ back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
+ if (IS_ERR(back))
+ return PTR_ERR(back);
+
+ return iio_backend_op_call(back, ext_info_get, private, chan, buf);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
+
+/**
+ * iio_backend_ext_info_set - IIO ext_info write callback
+ * @indio_dev: IIO device
+ * @private: Data private to the driver
+ * @chan: IIO channel
+ * @buf: Buffer holding the sysfs attribute
+ * @len: Buffer length
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not have
+ * a way to get the backend from indio_dev). This is the setter.
+ *
+ * RETURNS:
+ * Buffer length on success, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct iio_backend *back;
+
+ back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
+ if (IS_ERR(back))
+ return PTR_ERR(back);
+
+ return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
+
+/**
+ * iio_backend_extend_chan_spec - Extend an IIO channel
+ * @indio_dev: IIO device
+ * @back: Backend device
+ * @chan: IIO channel
+ *
+ * Some backends may have their own functionalities and hence capable of
+ * extending a frontend's channel.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+ struct iio_backend *back,
+ struct iio_chan_spec *chan)
+{
+ const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info;
+ const struct iio_chan_spec_ext_info *back_ext_info;
+ int ret;
+
+ ret = iio_backend_op_call(back, extend_chan_spec, chan);
+ if (ret)
+ return ret;
+ /*
+ * Let's keep things simple for now. Don't allow to overwrite the
+ * frontend's extended info. If ever needed, we can support appending
+ * it.
+ */
+ if (frontend_ext_info && chan->ext_info != frontend_ext_info)
+ return -EOPNOTSUPP;
+ if (!chan->ext_info)
+ return 0;
+
+ /* Don't allow backends to get creative and force their own handlers */
+ for (back_ext_info = chan->ext_info; back_ext_info->name; back_ext_info++) {
+ if (back_ext_info->read != iio_backend_ext_info_get)
+ return -EINVAL;
+ if (back_ext_info->write != iio_backend_ext_info_set)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
+
static void iio_backend_release(void *arg)
{
struct iio_backend *back = arg;
@@ -263,6 +440,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
"Could not link to supplier(%s)\n",
dev_name(back->dev));
+ back->frontend_dev = dev;
+
dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev));
return 0;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index b581a7e805662..1d950a3e153b8 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -10,6 +10,7 @@
* - Alternative access techniques?
*/
#include <linux/anon_inodes.h>
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/device.h>
@@ -533,28 +534,26 @@ static ssize_t iio_scan_el_store(struct device *dev,
ret = kstrtobool(buf, &state);
if (ret < 0)
return ret;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- goto error_ret;
- }
+
+ guard(mutex)(&iio_dev_opaque->mlock);
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
+
ret = iio_scan_mask_query(indio_dev, buffer, this_attr->address);
if (ret < 0)
- goto error_ret;
- if (!state && ret) {
- ret = iio_scan_mask_clear(buffer, this_attr->address);
- if (ret)
- goto error_ret;
- } else if (state && !ret) {
- ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address);
- if (ret)
- goto error_ret;
- }
+ return ret;
-error_ret:
- mutex_unlock(&iio_dev_opaque->mlock);
+ if (state && ret)
+ return len;
- return ret < 0 ? ret : len;
+ if (state)
+ ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address);
+ else
+ ret = iio_scan_mask_clear(buffer, this_attr->address);
+ if (ret)
+ return ret;
+
+ return len;
}
static ssize_t iio_scan_el_ts_show(struct device *dev,
@@ -581,16 +580,13 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
if (ret < 0)
return ret;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- goto error_ret;
- }
+ guard(mutex)(&iio_dev_opaque->mlock);
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
+
buffer->scan_timestamp = state;
-error_ret:
- mutex_unlock(&iio_dev_opaque->mlock);
- return ret ? ret : len;
+ return len;
}
static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
@@ -674,21 +670,16 @@ static ssize_t length_store(struct device *dev, struct device_attribute *attr,
if (val == buffer->length)
return len;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- } else {
- buffer->access->set_length(buffer, val);
- ret = 0;
- }
- if (ret)
- goto out;
+ guard(mutex)(&iio_dev_opaque->mlock);
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
+
+ buffer->access->set_length(buffer, val);
+
if (buffer->length && buffer->length < buffer->watermark)
buffer->watermark = buffer->length;
-out:
- mutex_unlock(&iio_dev_opaque->mlock);
- return ret ? ret : len;
+ return len;
}
static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
@@ -1268,7 +1259,6 @@ int iio_update_buffers(struct iio_dev *indio_dev,
struct iio_buffer *remove_buffer)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
- int ret;
if (insert_buffer == remove_buffer)
return 0;
@@ -1277,8 +1267,8 @@ int iio_update_buffers(struct iio_dev *indio_dev,
insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT)
return -EINVAL;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->mlock);
if (insert_buffer && iio_buffer_is_active(insert_buffer))
insert_buffer = NULL;
@@ -1286,23 +1276,13 @@ int iio_update_buffers(struct iio_dev *indio_dev,
if (remove_buffer && !iio_buffer_is_active(remove_buffer))
remove_buffer = NULL;
- if (!insert_buffer && !remove_buffer) {
- ret = 0;
- goto out_unlock;
- }
-
- if (!indio_dev->info) {
- ret = -ENODEV;
- goto out_unlock;
- }
-
- ret = __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
+ if (!insert_buffer && !remove_buffer)
+ return 0;
-out_unlock:
- mutex_unlock(&iio_dev_opaque->mlock);
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ if (!indio_dev->info)
+ return -ENODEV;
- return ret;
+ return __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
@@ -1326,22 +1306,22 @@ static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
if (ret < 0)
return ret;
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->mlock);
/* Find out if it is in the list */
inlist = iio_buffer_is_active(buffer);
/* Already in desired state */
if (inlist == requested_state)
- goto done;
+ return len;
if (requested_state)
ret = __iio_update_buffers(indio_dev, buffer, NULL);
else
ret = __iio_update_buffers(indio_dev, NULL, buffer);
+ if (ret)
+ return ret;
-done:
- mutex_unlock(&iio_dev_opaque->mlock);
- return (ret < 0) ? ret : len;
+ return len;
}
static ssize_t watermark_show(struct device *dev, struct device_attribute *attr,
@@ -1368,23 +1348,17 @@ static ssize_t watermark_store(struct device *dev,
if (!val)
return -EINVAL;
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->mlock);
- if (val > buffer->length) {
- ret = -EINVAL;
- goto out;
- }
+ if (val > buffer->length)
+ return -EINVAL;
- if (iio_buffer_is_active(buffer)) {
- ret = -EBUSY;
- goto out;
- }
+ if (iio_buffer_is_active(buffer))
+ return -EBUSY;
buffer->watermark = val;
-out:
- mutex_unlock(&iio_dev_opaque->mlock);
- return ret ? ret : len;
+ return len;
}
static ssize_t data_available_show(struct device *dev,
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 4302093b92c75..fa7cc051b4c49 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -11,6 +11,7 @@
#include <linux/anon_inodes.h>
#include <linux/cdev.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -1643,19 +1644,20 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
struct iio_dev *indio_dev;
size_t alloc_size;
- alloc_size = sizeof(struct iio_dev_opaque);
- if (sizeof_priv) {
- alloc_size = ALIGN(alloc_size, IIO_DMA_MINALIGN);
- alloc_size += sizeof_priv;
- }
+ if (sizeof_priv)
+ alloc_size = ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN) + sizeof_priv;
+ else
+ alloc_size = sizeof(*iio_dev_opaque);
iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL);
if (!iio_dev_opaque)
return NULL;
indio_dev = &iio_dev_opaque->indio_dev;
- indio_dev->priv = (char *)iio_dev_opaque +
- ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
+
+ if (sizeof_priv)
+ indio_dev->priv = (char *)iio_dev_opaque +
+ ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN);
indio_dev->dev.parent = parent;
indio_dev->dev.type = &iio_device_type;
@@ -1809,31 +1811,24 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct iio_dev *indio_dev = ib->indio_dev;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_ioctl_handler *h;
- int ret = -ENODEV;
-
- mutex_lock(&iio_dev_opaque->info_exist_lock);
+ int ret;
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
/*
* The NULL check here is required to prevent crashing when a device
* is being removed while userspace would still have open file handles
* to try to access this device.
*/
if (!indio_dev->info)
- goto out_unlock;
+ return -ENODEV;
list_for_each_entry(h, &iio_dev_opaque->ioctl_handlers, entry) {
ret = h->ioctl(indio_dev, filp, cmd, arg);
if (ret != IIO_IOCTL_UNHANDLED)
- break;
+ return ret;
}
- if (ret == IIO_IOCTL_UNHANDLED)
- ret = -ENODEV;
-
-out_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
-
- return ret;
+ return -ENODEV;
}
static const struct file_operations iio_buffer_fileops = {
@@ -2061,18 +2056,16 @@ void iio_device_unregister(struct iio_dev *indio_dev)
cdev_device_del(&iio_dev_opaque->chrdev, &indio_dev->dev);
- mutex_lock(&iio_dev_opaque->info_exist_lock);
+ scoped_guard(mutex, &iio_dev_opaque->info_exist_lock) {
+ iio_device_unregister_debugfs(indio_dev);
- iio_device_unregister_debugfs(indio_dev);
+ iio_disable_all_buffers(indio_dev);
- iio_disable_all_buffers(indio_dev);
+ indio_dev->info = NULL;
- indio_dev->info = NULL;
-
- iio_device_wakeup_eventset(indio_dev);
- iio_buffer_wakeup_poll(indio_dev);
-
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ iio_device_wakeup_eventset(indio_dev);
+ iio_buffer_wakeup_poll(indio_dev);
+ }
iio_buffers_free_sysfs_and_mask(indio_dev);
}
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index 18f83158f637f..16de57846bd9c 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -4,6 +4,7 @@
* Copyright (c) 2008 Jonathan Cameron
*/
+#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/idr.h>
#include <linux/err.h>
@@ -80,19 +81,18 @@ int iio_trigger_register(struct iio_trigger *trig_info)
goto error_unregister_id;
/* Add to list of available triggers held by the IIO core */
- mutex_lock(&iio_trigger_list_lock);
- if (__iio_trigger_find_by_name(trig_info->name)) {
- pr_err("Duplicate trigger name '%s'\n", trig_info->name);
- ret = -EEXIST;
- goto error_device_del;
+ scoped_guard(mutex, &iio_trigger_list_lock) {
+ if (__iio_trigger_find_by_name(trig_info->name)) {
+ pr_err("Duplicate trigger name '%s'\n", trig_info->name);
+ ret = -EEXIST;
+ goto error_device_del;
+ }
+ list_add_tail(&trig_info->list, &iio_trigger_list);
}
- list_add_tail(&trig_info->list, &iio_trigger_list);
- mutex_unlock(&iio_trigger_list_lock);
return 0;
error_device_del:
- mutex_unlock(&iio_trigger_list_lock);
device_del(&trig_info->dev);
error_unregister_id:
ida_free(&iio_trigger_ida, trig_info->id);
@@ -102,9 +102,8 @@ EXPORT_SYMBOL(iio_trigger_register);
void iio_trigger_unregister(struct iio_trigger *trig_info)
{
- mutex_lock(&iio_trigger_list_lock);
- list_del(&trig_info->list);
- mutex_unlock(&iio_trigger_list_lock);
+ scoped_guard(mutex, &iio_trigger_list_lock)
+ list_del(&trig_info->list);
ida_free(&iio_trigger_ida, trig_info->id);
/* Possible issue in here */
@@ -120,12 +119,11 @@ int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *tri
return -EINVAL;
iio_dev_opaque = to_iio_dev_opaque(indio_dev);
- mutex_lock(&iio_dev_opaque->mlock);
+ guard(mutex)(&iio_dev_opaque->mlock);
WARN_ON(iio_dev_opaque->trig_readonly);
indio_dev->trig = iio_trigger_get(trig);
iio_dev_opaque->trig_readonly = true;
- mutex_unlock(&iio_dev_opaque->mlock);
return 0;
}
@@ -145,18 +143,14 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
{
- struct iio_trigger *trig = NULL, *iter;
+ struct iio_trigger *iter;
- mutex_lock(&iio_trigger_list_lock);
+ guard(mutex)(&iio_trigger_list_lock);
list_for_each_entry(iter, &iio_trigger_list, list)
- if (sysfs_streq(iter->name, name)) {
- trig = iter;
- iio_trigger_get(trig);
- break;
- }
- mutex_unlock(&iio_trigger_list_lock);
+ if (sysfs_streq(iter->name, name))
+ return iio_trigger_get(iter);
- return trig;
+ return NULL;
}
static void iio_reenable_work_fn(struct work_struct *work)
@@ -259,22 +253,21 @@ static int iio_trigger_get_irq(struct iio_trigger *trig)
{
int ret;
- mutex_lock(&trig->pool_lock);
- ret = bitmap_find_free_region(trig->pool,
- CONFIG_IIO_CONSUMERS_PER_TRIGGER,
- ilog2(1));
- mutex_unlock(&trig->pool_lock);
- if (ret >= 0)
- ret += trig->subirq_base;
+ scoped_guard(mutex, &trig->pool_lock) {
+ ret = bitmap_find_free_region(trig->pool,
+ CONFIG_IIO_CONSUMERS_PER_TRIGGER,
+ ilog2(1));
+ if (ret < 0)
+ return ret;
+ }
- return ret;
+ return ret + trig->subirq_base;
}
static void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
{
- mutex_lock(&trig->pool_lock);
+ guard(mutex)(&trig->pool_lock);
clear_bit(irq - trig->subirq_base, trig->pool);
- mutex_unlock(&trig->pool_lock);
}
/* Complexity in here. With certain triggers (datardy) an acknowledgement
@@ -451,16 +444,12 @@ static ssize_t current_trigger_store(struct device *dev,
struct iio_trigger *trig;
int ret;
- mutex_lock(&iio_dev_opaque->mlock);
- if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED) {
- mutex_unlock(&iio_dev_opaque->mlock);
- return -EBUSY;
- }
- if (iio_dev_opaque->trig_readonly) {
- mutex_unlock(&iio_dev_opaque->mlock);
- return -EPERM;
+ scoped_guard(mutex, &iio_dev_opaque->mlock) {
+ if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED)
+ return -EBUSY;
+ if (iio_dev_opaque->trig_readonly)
+ return -EPERM;
}
- mutex_unlock(&iio_dev_opaque->mlock);
trig = iio_trigger_acquire_by_name(buf);
if (oldtrig == trig) {
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 7a1f6713318a3..52d7732618282 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2011 Jonathan Cameron
*/
+#include <linux/cleanup.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/minmax.h>
@@ -43,13 +44,14 @@ static int iio_map_array_unregister_locked(struct iio_dev *indio_dev)
int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
{
- int i = 0, ret = 0;
struct iio_map_internal *mapi;
+ int i = 0;
+ int ret;
if (!maps)
return 0;
- mutex_lock(&iio_map_list_lock);
+ guard(mutex)(&iio_map_list_lock);
while (maps[i].consumer_dev_name) {
mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
if (!mapi) {
@@ -61,11 +63,10 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
list_add_tail(&mapi->l, &iio_map_list);
i++;
}
-error_ret:
- if (ret)
- iio_map_array_unregister_locked(indio_dev);
- mutex_unlock(&iio_map_list_lock);
+ return 0;
+error_ret:
+ iio_map_array_unregister_locked(indio_dev);
return ret;
}
EXPORT_SYMBOL_GPL(iio_map_array_register);
@@ -75,13 +76,8 @@ EXPORT_SYMBOL_GPL(iio_map_array_register);
*/
int iio_map_array_unregister(struct iio_dev *indio_dev)
{
- int ret;
-
- mutex_lock(&iio_map_list_lock);
- ret = iio_map_array_unregister_locked(indio_dev);
- mutex_unlock(&iio_map_list_lock);
-
- return ret;
+ guard(mutex)(&iio_map_list_lock);
+ return iio_map_array_unregister_locked(indio_dev);
}
EXPORT_SYMBOL_GPL(iio_map_array_unregister);
@@ -183,25 +179,21 @@ err_put:
static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode,
int index)
{
- struct iio_channel *channel;
int err;
if (index < 0)
return ERR_PTR(-EINVAL);
- channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ struct iio_channel *channel __free(kfree) =
+ kzalloc(sizeof(*channel), GFP_KERNEL);
if (!channel)
return ERR_PTR(-ENOMEM);
err = __fwnode_iio_channel_get(channel, fwnode, index);
if (err)
- goto err_free_channel;
+ return ERR_PTR(err);
- return channel;
-
-err_free_channel:
- kfree(channel);
- return ERR_PTR(err);
+ return_ptr(channel);
}
static struct iio_channel *
@@ -291,7 +283,6 @@ EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name);
static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
{
struct fwnode_handle *fwnode = dev_fwnode(dev);
- struct iio_channel *chans;
int i, mapind, nummaps = 0;
int ret;
@@ -307,7 +298,8 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
return ERR_PTR(-ENODEV);
/* NULL terminated array to save passing size */
- chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
+ struct iio_channel *chans __free(kfree) =
+ kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
if (!chans)
return ERR_PTR(-ENOMEM);
@@ -317,12 +309,11 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
if (ret)
goto error_free_chans;
}
- return chans;
+ return_ptr(chans);
error_free_chans:
for (i = 0; i < mapind; i++)
iio_device_put(chans[i].indio_dev);
- kfree(chans);
return ERR_PTR(ret);
}
@@ -330,28 +321,28 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
const char *channel_name)
{
struct iio_map_internal *c_i = NULL, *c = NULL;
- struct iio_channel *channel;
int err;
if (!(name || channel_name))
return ERR_PTR(-ENODEV);
/* first find matching entry the channel map */
- mutex_lock(&iio_map_list_lock);
- list_for_each_entry(c_i, &iio_map_list, l) {
- if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
- (channel_name &&
- strcmp(channel_name, c_i->map->consumer_channel) != 0))
- continue;
- c = c_i;
- iio_device_get(c->indio_dev);
- break;
+ scoped_guard(mutex, &iio_map_list_lock) {
+ list_for_each_entry(c_i, &iio_map_list, l) {
+ if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
+ (channel_name &&
+ strcmp(channel_name, c_i->map->consumer_channel) != 0))
+ continue;
+ c = c_i;
+ iio_device_get(c->indio_dev);
+ break;
+ }
}
- mutex_unlock(&iio_map_list_lock);
if (!c)
return ERR_PTR(-ENODEV);
- channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ struct iio_channel *channel __free(kfree) =
+ kzalloc(sizeof(*channel), GFP_KERNEL);
if (!channel) {
err = -ENOMEM;
goto error_no_mem;
@@ -366,14 +357,12 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
if (!channel->channel) {
err = -EINVAL;
- goto error_no_chan;
+ goto error_no_mem;
}
}
- return channel;
+ return_ptr(channel);
-error_no_chan:
- kfree(channel);
error_no_mem:
iio_device_put(c->indio_dev);
return ERR_PTR(err);
@@ -450,8 +439,8 @@ EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name);
struct iio_channel *iio_channel_get_all(struct device *dev)
{
const char *name;
- struct iio_channel *chans;
struct iio_map_internal *c = NULL;
+ struct iio_channel *fw_chans;
int nummaps = 0;
int mapind = 0;
int i, ret;
@@ -459,17 +448,17 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
if (!dev)
return ERR_PTR(-EINVAL);
- chans = fwnode_iio_channel_get_all(dev);
+ fw_chans = fwnode_iio_channel_get_all(dev);
/*
* We only want to carry on if the error is -ENODEV. Anything else
* should be reported up the stack.
*/
- if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV)
- return chans;
+ if (!IS_ERR(fw_chans) || PTR_ERR(fw_chans) != -ENODEV)
+ return fw_chans;
name = dev_name(dev);
- mutex_lock(&iio_map_list_lock);
+ guard(mutex)(&iio_map_list_lock);
/* first count the matching maps */
list_for_each_entry(c, &iio_map_list, l)
if (name && strcmp(name, c->map->consumer_dev_name) != 0)
@@ -477,17 +466,14 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
else
nummaps++;
- if (nummaps == 0) {
- ret = -ENODEV;
- goto error_ret;
- }
+ if (nummaps == 0)
+ return ERR_PTR(-ENODEV);
/* NULL terminated array to save passing size */
- chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
- if (!chans) {
- ret = -ENOMEM;
- goto error_ret;
- }
+ struct iio_channel *chans __free(kfree) =
+ kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
+ if (!chans)
+ return ERR_PTR(-ENOMEM);
/* for each map fill in the chans element */
list_for_each_entry(c, &iio_map_list, l) {
@@ -509,17 +495,12 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
ret = -ENODEV;
goto error_free_chans;
}
- mutex_unlock(&iio_map_list_lock);
- return chans;
+ return_ptr(chans);
error_free_chans:
for (i = 0; i < nummaps; i++)
iio_device_put(chans[i].indio_dev);
- kfree(chans);
-error_ret:
- mutex_unlock(&iio_map_list_lock);
-
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(iio_channel_get_all);
@@ -590,38 +571,24 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
int iio_read_channel_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_channel_raw);
int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
@@ -708,20 +675,13 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
int *processed, unsigned int scale)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
- scale);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_convert_raw_to_processed_unlocked(chan, raw, processed,
+ scale);
}
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
@@ -729,19 +689,12 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
enum iio_chan_info_enum attribute)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, val2, attribute);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read(chan, val, val2, attribute);
}
EXPORT_SYMBOL_GPL(iio_read_channel_attribute);
@@ -757,30 +710,26 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val,
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
ret = iio_channel_read(chan, val, NULL,
IIO_CHAN_INFO_PROCESSED);
if (ret < 0)
- goto err_unlock;
+ return ret;
*val *= scale;
+
+ return 0;
} else {
ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
if (ret < 0)
- goto err_unlock;
- ret = iio_convert_raw_to_processed_unlocked(chan, *val, val,
- scale);
- }
+ return ret;
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
-
- return ret;
+ return iio_convert_raw_to_processed_unlocked(chan, *val, val,
+ scale);
+ }
}
EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale);
@@ -813,19 +762,12 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan,
enum iio_chan_info_enum attribute)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
-
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
- ret = iio_channel_read_avail(chan, vals, type, length, attribute);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read_avail(chan, vals, type, length, attribute);
}
EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute);
@@ -892,20 +834,13 @@ static int iio_channel_read_max(struct iio_channel *chan,
int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
int type;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
@@ -955,40 +890,27 @@ static int iio_channel_read_min(struct iio_channel *chan,
int iio_read_min_channel_raw(struct iio_channel *chan, int *val)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
int type;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- return ret;
+ return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
}
EXPORT_SYMBOL_GPL(iio_read_min_channel_raw);
int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret = 0;
- /* Need to verify underlying driver has not gone away */
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
*type = chan->channel->type;
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(iio_get_channel_type);
@@ -1003,19 +925,12 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
enum iio_chan_info_enum attribute)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
- int ret;
- mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (!chan->indio_dev->info) {
- ret = -ENODEV;
- goto err_unlock;
- }
+ guard(mutex)(&iio_dev_opaque->info_exist_lock);
+ if (!chan->indio_dev->info)
+ return -ENODEV;
- ret = iio_channel_write(chan, val, val2, attribute);
-err_unlock:
- mutex_unlock(&iio_dev_opaque->info_exist_lock);
-
- return ret;
+ return iio_channel_write(chan, val, val2, attribute);
}
EXPORT_SYMBOL_GPL(iio_write_channel_attribute);
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index fd5a9879a582c..9a587d4031180 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -73,6 +73,18 @@ config APDS9300
To compile this driver as a module, choose M here: the
module will be called apds9300.
+config APDS9306
+ tristate "Avago APDS9306 Ambient Light Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_GTS_HELPER
+ help
+ If you say Y or M here, you get support for Avago APDS9306
+ Ambient Light Sensor.
+
+ If built as a dynamically linked module, it will be called
+ apds9306.
+
config APDS9960
tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
select REGMAP_I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 2e5fdb33e0e97..a30f906e91ba0 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_ADUX1020) += adux1020.o
obj-$(CONFIG_AL3010) += al3010.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
+obj-$(CONFIG_APDS9306) += apds9306.o
obj-$(CONFIG_APDS9960) += apds9960.o
obj-$(CONFIG_AS73211) += as73211.o
obj-$(CONFIG_BH1750) += bh1750.o
diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c
new file mode 100644
index 0000000000000..46c647ccd44c5
--- /dev/null
+++ b/drivers/iio/light/apds9306.c
@@ -0,0 +1,1361 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * APDS-9306/APDS-9306-065 Ambient Light Sensor
+ * I2C Address: 0x52
+ * Datasheet: https://docs.broadcom.com/doc/AV02-4755EN
+ *
+ * Copyright (C) 2024 Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/iio-gts-helper.h>
+#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
+
+#include <asm/unaligned.h>
+
+#define APDS9306_MAIN_CTRL_REG 0x00
+#define APDS9306_ALS_MEAS_RATE_REG 0x04
+#define APDS9306_ALS_GAIN_REG 0x05
+#define APDS9306_PART_ID_REG 0x06
+#define APDS9306_MAIN_STATUS_REG 0x07
+#define APDS9306_CLEAR_DATA_0_REG 0x0A
+#define APDS9306_CLEAR_DATA_1_REG 0x0B
+#define APDS9306_CLEAR_DATA_2_REG 0x0C
+#define APDS9306_ALS_DATA_0_REG 0x0D
+#define APDS9306_ALS_DATA_1_REG 0x0E
+#define APDS9306_ALS_DATA_2_REG 0x0F
+#define APDS9306_INT_CFG_REG 0x19
+#define APDS9306_INT_PERSISTENCE_REG 0x1A
+#define APDS9306_ALS_THRES_UP_0_REG 0x21
+#define APDS9306_ALS_THRES_UP_1_REG 0x22
+#define APDS9306_ALS_THRES_UP_2_REG 0x23
+#define APDS9306_ALS_THRES_LOW_0_REG 0x24
+#define APDS9306_ALS_THRES_LOW_1_REG 0x25
+#define APDS9306_ALS_THRES_LOW_2_REG 0x26
+#define APDS9306_ALS_THRES_VAR_REG 0x27
+
+#define APDS9306_ALS_INT_STAT_MASK BIT(4)
+#define APDS9306_ALS_DATA_STAT_MASK BIT(3)
+
+#define APDS9306_ALS_THRES_VAL_MAX (BIT(20) - 1)
+#define APDS9306_ALS_THRES_VAR_VAL_MAX (BIT(3) - 1)
+#define APDS9306_ALS_PERSIST_VAL_MAX (BIT(4) - 1)
+#define APDS9306_ALS_READ_DATA_DELAY_US (20 * USEC_PER_MSEC)
+#define APDS9306_NUM_REPEAT_RATES 7
+#define APDS9306_INT_SRC_CLEAR 0
+#define APDS9306_INT_SRC_ALS 1
+#define APDS9306_SAMP_FREQ_10HZ 0
+
+/**
+ * struct part_id_gts_multiplier - Part no. and corresponding gts multiplier
+ *
+ * GTS (Gain Time Scale) are helper functions for Light sensors which along
+ * with hardware gains also has gains associated with Integration times.
+ *
+ * There are two variants of the device with slightly different characteristics,
+ * they have same ADC count for different Lux levels as mentioned in the
+ * datasheet. This multiplier array is used to store the derived Lux per count
+ * value for the two variants to be used by the GTS helper functions.
+ *
+ * @part_id: Part ID of the device
+ * @max_scale_int: Multiplier for iio_init_iio_gts()
+ * @max_scale_nano: Multiplier for iio_init_iio_gts()
+ */
+struct part_id_gts_multiplier {
+ int part_id;
+ int max_scale_int;
+ int max_scale_nano;
+};
+
+/*
+ * As per the datasheet, at HW Gain = 3x, Integration time 100mS (32x),
+ * typical 2000 ADC counts are observed for 49.8 uW per sq cm (340.134 lux)
+ * for apds9306 and 43 uW per sq cm (293.69 lux) for apds9306-065.
+ * Assuming lux per count is linear across all integration time ranges.
+ *
+ * Lux = (raw + offset) * scale; offset can be any value by userspace.
+ * HG = Hardware Gain; ITG = Gain by changing integration time.
+ * Scale table by IIO GTS Helpers = (1 / HG) * (1 / ITG) * Multiplier.
+ *
+ * The Lux values provided in the datasheet are at ITG=32x and HG=3x,
+ * at typical 2000 count for both variants of the device.
+ *
+ * Lux per ADC count at 3x and 32x for apds9306 = 340.134 / 2000
+ * Lux per ADC count at 3x and 32x for apds9306-065 = 293.69 / 2000
+ *
+ * The Multiplier for the scale table provided to userspace:
+ * IIO GTS scale Multiplier for apds9306 = (340.134 / 2000) * 32 * 3 = 16.326432
+ * and for apds9306-065 = (293.69 / 2000) * 32 * 3 = 14.09712
+ */
+static const struct part_id_gts_multiplier apds9306_gts_mul[] = {
+ {
+ .part_id = 0xB1,
+ .max_scale_int = 16,
+ .max_scale_nano = 3264320,
+ }, {
+ .part_id = 0xB3,
+ .max_scale_int = 14,
+ .max_scale_nano = 9712000,
+ },
+};
+
+static const int apds9306_repeat_rate_freq[APDS9306_NUM_REPEAT_RATES][2] = {
+ { 40, 0 },
+ { 20, 0 },
+ { 10, 0 },
+ { 5, 0 },
+ { 2, 0 },
+ { 1, 0 },
+ { 0, 500000 },
+};
+
+static const int apds9306_repeat_rate_period[APDS9306_NUM_REPEAT_RATES] = {
+ 25000, 50000, 100000, 200000, 500000, 1000000, 2000000,
+};
+
+/**
+ * struct apds9306_regfields - apds9306 regmap fields definitions
+ *
+ * @sw_reset: Software reset regfield
+ * @en: Enable regfield
+ * @intg_time: Resolution regfield
+ * @repeat_rate: Measurement Rate regfield
+ * @gain: Hardware gain regfield
+ * @int_src: Interrupt channel regfield
+ * @int_thresh_var_en: Interrupt variance threshold regfield
+ * @int_en: Interrupt enable regfield
+ * @int_persist_val: Interrupt persistence regfield
+ * @int_thresh_var_val: Interrupt threshold variance value regfield
+ */
+struct apds9306_regfields {
+ struct regmap_field *sw_reset;
+ struct regmap_field *en;
+ struct regmap_field *intg_time;
+ struct regmap_field *repeat_rate;
+ struct regmap_field *gain;
+ struct regmap_field *int_src;
+ struct regmap_field *int_thresh_var_en;
+ struct regmap_field *int_en;
+ struct regmap_field *int_persist_val;
+ struct regmap_field *int_thresh_var_val;
+};
+
+/**
+ * struct apds9306_data - apds9306 private data and registers definitions
+ *
+ * @dev: Pointer to the device structure
+ * @gts: IIO Gain Time Scale structure
+ * @mutex: Lock for protecting adc reads, device settings changes where
+ * some calculations are required before or after setting or
+ * getting the raw settings values from regmap writes or reads
+ * respectively.
+ * @regmap: Regmap structure pointer
+ * @rf: Regmap register fields structure
+ * @nlux_per_count: Nano lux per ADC count for a particular model
+ * @read_data_available: Flag set by IRQ handler for ADC data available
+ */
+struct apds9306_data {
+ struct device *dev;
+ struct iio_gts gts;
+
+ struct mutex mutex;
+
+ struct regmap *regmap;
+ struct apds9306_regfields rf;
+
+ int nlux_per_count;
+ int read_data_available;
+};
+
+/*
+ * Available scales with gain 1x - 18x, timings 3.125, 25, 50, 100, 200, 400 ms
+ * Time impacts to gain: 1x, 8x, 16x, 32x, 64x, 128x
+ */
+#define APDS9306_GSEL_1X 0x00
+#define APDS9306_GSEL_3X 0x01
+#define APDS9306_GSEL_6X 0x02
+#define APDS9306_GSEL_9X 0x03
+#define APDS9306_GSEL_18X 0x04
+
+static const struct iio_gain_sel_pair apds9306_gains[] = {
+ GAIN_SCALE_GAIN(1, APDS9306_GSEL_1X),
+ GAIN_SCALE_GAIN(3, APDS9306_GSEL_3X),
+ GAIN_SCALE_GAIN(6, APDS9306_GSEL_6X),
+ GAIN_SCALE_GAIN(9, APDS9306_GSEL_9X),
+ GAIN_SCALE_GAIN(18, APDS9306_GSEL_18X),
+};
+
+#define APDS9306_MEAS_MODE_400MS 0x00
+#define APDS9306_MEAS_MODE_200MS 0x01
+#define APDS9306_MEAS_MODE_100MS 0x02
+#define APDS9306_MEAS_MODE_50MS 0x03
+#define APDS9306_MEAS_MODE_25MS 0x04
+#define APDS9306_MEAS_MODE_3125US 0x05
+
+static const struct iio_itime_sel_mul apds9306_itimes[] = {
+ GAIN_SCALE_ITIME_US(400000, APDS9306_MEAS_MODE_400MS, BIT(7)),
+ GAIN_SCALE_ITIME_US(200000, APDS9306_MEAS_MODE_200MS, BIT(6)),
+ GAIN_SCALE_ITIME_US(100000, APDS9306_MEAS_MODE_100MS, BIT(5)),
+ GAIN_SCALE_ITIME_US(50000, APDS9306_MEAS_MODE_50MS, BIT(4)),
+ GAIN_SCALE_ITIME_US(25000, APDS9306_MEAS_MODE_25MS, BIT(3)),
+ GAIN_SCALE_ITIME_US(3125, APDS9306_MEAS_MODE_3125US, BIT(0)),
+};
+
+static const struct iio_event_spec apds9306_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD),
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH_ADAPTIVE,
+ .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec apds9306_channels_with_events[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = apds9306_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9306_event_spec),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = 1,
+ .event_spec = apds9306_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9306_event_spec),
+ },
+};
+
+static const struct iio_chan_spec apds9306_channels_without_events[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ },
+};
+
+/* INT_PERSISTENCE available */
+static IIO_CONST_ATTR(thresh_either_period_available, "[0 1 15]");
+
+/* ALS_THRESH_VAR available */
+static IIO_CONST_ATTR(thresh_adaptive_either_values_available, "[0 1 7]");
+
+static struct attribute *apds9306_event_attributes[] = {
+ &iio_const_attr_thresh_either_period_available.dev_attr.attr,
+ &iio_const_attr_thresh_adaptive_either_values_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group apds9306_event_attr_group = {
+ .attrs = apds9306_event_attributes,
+};
+
+static const struct regmap_range apds9306_readable_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_THRES_VAR_REG)
+};
+
+static const struct regmap_range apds9306_writable_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_GAIN_REG),
+ regmap_reg_range(APDS9306_INT_CFG_REG, APDS9306_ALS_THRES_VAR_REG)
+};
+
+static const struct regmap_range apds9306_volatile_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG),
+ regmap_reg_range(APDS9306_CLEAR_DATA_0_REG, APDS9306_ALS_DATA_2_REG)
+};
+
+static const struct regmap_range apds9306_precious_ranges[] = {
+ regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG)
+};
+
+static const struct regmap_access_table apds9306_readable_table = {
+ .yes_ranges = apds9306_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_readable_ranges)
+};
+
+static const struct regmap_access_table apds9306_writable_table = {
+ .yes_ranges = apds9306_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_writable_ranges)
+};
+
+static const struct regmap_access_table apds9306_volatile_table = {
+ .yes_ranges = apds9306_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_volatile_ranges)
+};
+
+static const struct regmap_access_table apds9306_precious_table = {
+ .yes_ranges = apds9306_precious_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9306_precious_ranges)
+};
+
+static const struct regmap_config apds9306_regmap = {
+ .name = "apds9306_regmap",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &apds9306_readable_table,
+ .wr_table = &apds9306_writable_table,
+ .volatile_table = &apds9306_volatile_table,
+ .precious_table = &apds9306_precious_table,
+ .max_register = APDS9306_ALS_THRES_VAR_REG,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct reg_field apds9306_rf_sw_reset =
+ REG_FIELD(APDS9306_MAIN_CTRL_REG, 4, 4);
+
+static const struct reg_field apds9306_rf_en =
+ REG_FIELD(APDS9306_MAIN_CTRL_REG, 1, 1);
+
+static const struct reg_field apds9306_rf_intg_time =
+ REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 4, 6);
+
+static const struct reg_field apds9306_rf_repeat_rate =
+ REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 0, 2);
+
+static const struct reg_field apds9306_rf_gain =
+ REG_FIELD(APDS9306_ALS_GAIN_REG, 0, 2);
+
+static const struct reg_field apds9306_rf_int_src =
+ REG_FIELD(APDS9306_INT_CFG_REG, 4, 5);
+
+static const struct reg_field apds9306_rf_int_thresh_var_en =
+ REG_FIELD(APDS9306_INT_CFG_REG, 3, 3);
+
+static const struct reg_field apds9306_rf_int_en =
+ REG_FIELD(APDS9306_INT_CFG_REG, 2, 2);
+
+static const struct reg_field apds9306_rf_int_persist_val =
+ REG_FIELD(APDS9306_INT_PERSISTENCE_REG, 4, 7);
+
+static const struct reg_field apds9306_rf_int_thresh_var_val =
+ REG_FIELD(APDS9306_ALS_THRES_VAR_REG, 0, 2);
+
+static int apds9306_regfield_init(struct apds9306_data *data)
+{
+ struct device *dev = data->dev;
+ struct regmap *regmap = data->regmap;
+ struct regmap_field *tmp;
+ struct apds9306_regfields *rf = &data->rf;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_sw_reset);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->sw_reset = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_intg_time);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->intg_time = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_repeat_rate);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->repeat_rate = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_gain);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->gain = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_src);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_src = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_thresh_var_en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_en);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_en = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_persist_val);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_persist_val = tmp;
+
+ tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_val);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ rf->int_thresh_var_val = tmp;
+
+ return 0;
+}
+
+static int apds9306_power_state(struct apds9306_data *data, bool state)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ /* Reset not included as it causes ugly I2C bus error */
+ if (state) {
+ ret = regmap_field_write(rf->en, 1);
+ if (ret)
+ return ret;
+ /* 5ms wake up time */
+ fsleep(5000);
+ return 0;
+ }
+
+ return regmap_field_write(rf->en, 0);
+}
+
+static int apds9306_read_data(struct apds9306_data *data, int *val, int reg)
+{
+ struct device *dev = data->dev;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct apds9306_regfields *rf = &data->rf;
+ u64 ev_code;
+ int ret, delay, intg_time, intg_time_idx, repeat_rate_idx, int_src;
+ int status = 0;
+ u8 buff[3];
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ intg_time = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg_time < 0)
+ return intg_time;
+
+ /* Whichever is greater - integration time period or sampling period. */
+ delay = max(intg_time, apds9306_repeat_rate_period[repeat_rate_idx]);
+
+ /*
+ * Clear stale data flag that might have been set by the interrupt
+ * handler if it got data available flag set in the status reg.
+ */
+ data->read_data_available = 0;
+
+ /*
+ * If this function runs parallel with the interrupt handler, either
+ * this reads and clears the status registers or the interrupt handler
+ * does. The interrupt handler sets a flag for read data available
+ * in our private structure which we read here.
+ */
+ ret = regmap_read_poll_timeout(data->regmap, APDS9306_MAIN_STATUS_REG,
+ status, data->read_data_available ||
+ (status & (APDS9306_ALS_DATA_STAT_MASK |
+ APDS9306_ALS_INT_STAT_MASK)),
+ APDS9306_ALS_READ_DATA_DELAY_US, delay * 2);
+ if (ret)
+ return ret;
+
+ /* If we reach here before the interrupt handler we push an event */
+ if ((status & APDS9306_ALS_INT_STAT_MASK)) {
+ if (int_src == APDS9306_INT_SRC_ALS)
+ ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ else
+ ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_MOD_LIGHT_CLEAR,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+
+ iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
+ }
+
+ ret = regmap_bulk_read(data->regmap, reg, buff, sizeof(buff));
+ if (ret) {
+ dev_err_ratelimited(dev, "read data failed\n");
+ return ret;
+ }
+
+ *val = get_unaligned_le24(&buff);
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+}
+
+static int apds9306_intg_time_get(struct apds9306_data *data, int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, intg_time_idx;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (ret < 0)
+ return ret;
+
+ *val2 = ret;
+
+ return 0;
+}
+
+static int apds9306_intg_time_set(struct apds9306_data *data, int val2)
+{
+ struct device *dev = data->dev;
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, intg_old, gain_old, gain_new, gain_new_closest, intg_time_idx;
+ int gain_idx;
+ bool ok;
+
+ if (!iio_gts_valid_time(&data->gts, val2)) {
+ dev_err_ratelimited(dev, "Unsupported integration time %u\n", val2);
+ return -EINVAL;
+ }
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ intg_old = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (ret < 0)
+ return ret;
+
+ if (intg_old == val2)
+ return 0;
+
+ gain_old = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (gain_old < 0)
+ return gain_old;
+
+ iio_gts_find_new_gain_by_old_gain_time(&data->gts, gain_old, intg_old,
+ val2, &gain_new);
+
+ if (gain_new < 0) {
+ dev_err_ratelimited(dev, "Unsupported gain with time\n");
+ return gain_new;
+ }
+
+ gain_new_closest = iio_find_closest_gain_low(&data->gts, gain_new, &ok);
+ if (gain_new_closest < 0) {
+ gain_new_closest = iio_gts_get_min_gain(&data->gts);
+ if (gain_new_closest < 0)
+ return gain_new_closest;
+ }
+ if (!ok)
+ dev_dbg(dev, "Unable to find optimum gain, setting minimum");
+
+ ret = iio_gts_find_sel_by_int_time(&data->gts, val2);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_field_write(rf->intg_time, ret);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_sel_by_gain(&data->gts, gain_new_closest);
+ if (ret < 0)
+ return ret;
+
+ return regmap_field_write(rf->gain, ret);
+}
+
+static int apds9306_sampling_freq_get(struct apds9306_data *data, int *val,
+ int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, repeat_rate_idx;
+
+ ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx);
+ if (ret)
+ return ret;
+
+ if (repeat_rate_idx >= ARRAY_SIZE(apds9306_repeat_rate_freq))
+ return -EINVAL;
+
+ *val = apds9306_repeat_rate_freq[repeat_rate_idx][0];
+ *val2 = apds9306_repeat_rate_freq[repeat_rate_idx][1];
+
+ return 0;
+}
+
+static int apds9306_sampling_freq_set(struct apds9306_data *data, int val,
+ int val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(apds9306_repeat_rate_freq); i++) {
+ if (apds9306_repeat_rate_freq[i][0] == val &&
+ apds9306_repeat_rate_freq[i][1] == val2)
+ return regmap_field_write(rf->repeat_rate, i);
+ }
+
+ return -EINVAL;
+}
+
+static int apds9306_scale_get(struct apds9306_data *data, int *val, int *val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int gain, intg, ret, intg_time_idx, gain_idx;
+
+ ret = regmap_field_read(rf->gain, &gain_idx);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx);
+ if (gain < 0)
+ return gain;
+
+ intg = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx);
+ if (intg < 0)
+ return intg;
+
+ return iio_gts_get_scale(&data->gts, gain, intg, val, val2);
+}
+
+static int apds9306_scale_set(struct apds9306_data *data, int val, int val2)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int i, ret, time_sel, gain_sel, intg_time_idx;
+
+ ret = regmap_field_read(rf->intg_time, &intg_time_idx);
+ if (ret)
+ return ret;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ intg_time_idx, val, val2, &gain_sel);
+ if (ret) {
+ for (i = 0; i < data->gts.num_itime; i++) {
+ time_sel = data->gts.itime_table[i].sel;
+
+ if (time_sel == intg_time_idx)
+ continue;
+
+ ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
+ time_sel, val, val2, &gain_sel);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ return -EINVAL;
+
+ ret = regmap_field_write(rf->intg_time, time_sel);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_field_write(rf->gain, gain_sel);
+}
+
+static int apds9306_event_period_get(struct apds9306_data *data, int *val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int period, ret;
+
+ ret = regmap_field_read(rf->int_persist_val, &period);
+ if (ret)
+ return ret;
+
+ if (!in_range(period, 0, APDS9306_ALS_PERSIST_VAL_MAX))
+ return -EINVAL;
+
+ *val = period;
+
+ return ret;
+}
+
+static int apds9306_event_period_set(struct apds9306_data *data, int val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+
+ if (!in_range(val, 0, APDS9306_ALS_PERSIST_VAL_MAX))
+ return -EINVAL;
+
+ return regmap_field_write(rf->int_persist_val, val);
+}
+
+static int apds9306_event_thresh_get(struct apds9306_data *data, int dir,
+ int *val)
+{
+ int var, ret;
+ u8 buff[3];
+
+ if (dir == IIO_EV_DIR_RISING)
+ var = APDS9306_ALS_THRES_UP_0_REG;
+ else if (dir == IIO_EV_DIR_FALLING)
+ var = APDS9306_ALS_THRES_LOW_0_REG;
+ else
+ return -EINVAL;
+
+ ret = regmap_bulk_read(data->regmap, var, buff, sizeof(buff));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_le24(&buff);
+
+ return 0;
+}
+
+static int apds9306_event_thresh_set(struct apds9306_data *data, int dir,
+ int val)
+{
+ int var;
+ u8 buff[3];
+
+ if (dir == IIO_EV_DIR_RISING)
+ var = APDS9306_ALS_THRES_UP_0_REG;
+ else if (dir == IIO_EV_DIR_FALLING)
+ var = APDS9306_ALS_THRES_LOW_0_REG;
+ else
+ return -EINVAL;
+
+ if (!in_range(val, 0, APDS9306_ALS_THRES_VAL_MAX))
+ return -EINVAL;
+
+ put_unaligned_le24(val, buff);
+
+ return regmap_bulk_write(data->regmap, var, buff, sizeof(buff));
+}
+
+static int apds9306_event_thresh_adaptive_get(struct apds9306_data *data, int *val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int thr_adpt, ret;
+
+ ret = regmap_field_read(rf->int_thresh_var_val, &thr_adpt);
+ if (ret)
+ return ret;
+
+ if (!in_range(thr_adpt, 0, APDS9306_ALS_THRES_VAR_VAL_MAX))
+ return -EINVAL;
+
+ *val = thr_adpt;
+
+ return ret;
+}
+
+static int apds9306_event_thresh_adaptive_set(struct apds9306_data *data, int val)
+{
+ struct apds9306_regfields *rf = &data->rf;
+
+ if (!in_range(val, 0, APDS9306_ALS_THRES_VAR_VAL_MAX))
+ return -EINVAL;
+
+ return regmap_field_write(rf->int_thresh_var_val, val);
+}
+
+static int apds9306_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ int ret, reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
+ reg = APDS9306_CLEAR_DATA_0_REG;
+ else
+ reg = APDS9306_ALS_DATA_0_REG;
+ /*
+ * Changing device parameters during adc operation, resets
+ * the ADC which has to avoided.
+ */
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = apds9306_read_data(data, val, reg);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ ret = apds9306_intg_time_get(data, val2);
+ if (ret)
+ return ret;
+ *val = 0;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = apds9306_sampling_freq_get(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ ret = apds9306_scale_get(data, val, val2);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+};
+
+static int apds9306_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ return iio_gts_avail_times(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SCALE:
+ return iio_gts_all_avail_scales(&data->gts, vals, type, length);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *length = ARRAY_SIZE(apds9306_repeat_rate_freq) * 2;
+ *vals = (const int *)apds9306_repeat_rate_freq;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_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;
+ case IIO_CHAN_INFO_INT_TIME:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->mutex);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val)
+ return -EINVAL;
+ return apds9306_intg_time_set(data, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return apds9306_scale_set(data, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return apds9306_sampling_freq_set(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t apds9306_irq_handler(int irq, void *priv)
+{
+ struct iio_dev *indio_dev = priv;
+ struct apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ u64 ev_code;
+ int ret, status, int_src;
+
+ /*
+ * The interrupt line is released and the interrupt flag is
+ * cleared as a result of reading the status register. All the
+ * status flags are cleared as a result of this read.
+ */
+ ret = regmap_read(data->regmap, APDS9306_MAIN_STATUS_REG, &status);
+ if (ret < 0) {
+ dev_err_ratelimited(data->dev, "status reg read failed\n");
+ return IRQ_HANDLED;
+ }
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ if ((status & APDS9306_ALS_INT_STAT_MASK)) {
+ if (int_src == APDS9306_INT_SRC_ALS)
+ ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+ else
+ ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_MOD_LIGHT_CLEAR,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER);
+
+ iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev));
+ }
+
+ /*
+ * If a one-shot read through sysfs is underway at the same time
+ * as this interrupt handler is executing and a read data available
+ * flag was set, this flag is set to inform read_poll_timeout()
+ * to exit.
+ */
+ if ((status & APDS9306_ALS_DATA_STAT_MASK))
+ data->read_data_available = 1;
+
+ return IRQ_HANDLED;
+}
+
+static int apds9306_read_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+ ret = apds9306_event_period_get(data, val);
+ else
+ ret = apds9306_event_thresh_get(data, dir, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ ret = apds9306_event_thresh_adaptive_get(data, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD)
+ return apds9306_event_period_set(data, val);
+
+ return apds9306_event_thresh_set(data, dir, val);
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ return apds9306_event_thresh_adaptive_set(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ int int_en, int_src, ret;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH: {
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_field_read(rf->int_src, &int_src);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_read(rf->int_en, &int_en);
+ if (ret)
+ return ret;
+
+ if (chan->type == IIO_LIGHT)
+ return int_en && (int_src == APDS9306_INT_SRC_ALS);
+
+ if (chan->type == IIO_INTENSITY)
+ return int_en && (int_src == APDS9306_INT_SRC_CLEAR);
+
+ return -EINVAL;
+ }
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ ret = regmap_field_read(rf->int_thresh_var_en, &int_en);
+ if (ret)
+ return ret;
+
+ return int_en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apds9306_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct apds9306_data *data = iio_priv(indio_dev);
+ struct apds9306_regfields *rf = &data->rf;
+ int ret, enabled;
+
+ switch (type) {
+ case IIO_EV_TYPE_THRESH: {
+ guard(mutex)(&data->mutex);
+
+ ret = regmap_field_read(rf->int_en, &enabled);
+ if (ret)
+ return ret;
+
+ /*
+ * If interrupt is enabled, the channel is set before enabling
+ * the interrupt. In case of disable, no need to switch
+ * channels. In case of different channel is selected while
+ * interrupt in on, just change the channel.
+ */
+ if (state) {
+ if (chan->type == IIO_LIGHT)
+ ret = regmap_field_write(rf->int_src, 1);
+ else if (chan->type == IIO_INTENSITY)
+ ret = regmap_field_write(rf->int_src, 0);
+ else
+ return -EINVAL;
+
+ if (ret)
+ return ret;
+
+ if (enabled)
+ return 0;
+
+ ret = regmap_field_write(rf->int_en, 1);
+ if (ret)
+ return ret;
+
+ return pm_runtime_resume_and_get(data->dev);
+ } else {
+ if (!enabled)
+ return 0;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+ }
+ }
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ if (state)
+ return regmap_field_write(rf->int_thresh_var_en, 1);
+ else
+ return regmap_field_write(rf->int_thresh_var_en, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info apds9306_info_no_events = {
+ .read_avail = apds9306_read_avail,
+ .read_raw = apds9306_read_raw,
+ .write_raw = apds9306_write_raw,
+ .write_raw_get_fmt = apds9306_write_raw_get_fmt,
+};
+
+static const struct iio_info apds9306_info = {
+ .read_avail = apds9306_read_avail,
+ .read_raw = apds9306_read_raw,
+ .write_raw = apds9306_write_raw,
+ .write_raw_get_fmt = apds9306_write_raw_get_fmt,
+ .read_event_value = apds9306_read_event,
+ .write_event_value = apds9306_write_event,
+ .read_event_config = apds9306_read_event_config,
+ .write_event_config = apds9306_write_event_config,
+ .event_attrs = &apds9306_event_attr_group,
+};
+
+static int apds9306_init_iio_gts(struct apds9306_data *data)
+{
+ int i, ret, part_id;
+
+ ret = regmap_read(data->regmap, APDS9306_PART_ID_REG, &part_id);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(apds9306_gts_mul); i++)
+ if (part_id == apds9306_gts_mul[i].part_id)
+ break;
+
+ if (i == ARRAY_SIZE(apds9306_gts_mul))
+ return -ENOENT;
+
+ return devm_iio_init_iio_gts(data->dev,
+ apds9306_gts_mul[i].max_scale_int,
+ apds9306_gts_mul[i].max_scale_nano,
+ apds9306_gains, ARRAY_SIZE(apds9306_gains),
+ apds9306_itimes, ARRAY_SIZE(apds9306_itimes),
+ &data->gts);
+}
+
+static void apds9306_powerdown(void *ptr)
+{
+ struct apds9306_data *data = (struct apds9306_data *)ptr;
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ ret = regmap_field_write(rf->int_thresh_var_en, 0);
+ if (ret)
+ return;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return;
+
+ apds9306_power_state(data, false);
+}
+
+static int apds9306_device_init(struct apds9306_data *data)
+{
+ struct apds9306_regfields *rf = &data->rf;
+ int ret;
+
+ ret = apds9306_init_iio_gts(data);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->intg_time, APDS9306_MEAS_MODE_100MS);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->repeat_rate, APDS9306_SAMP_FREQ_10HZ);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->gain, APDS9306_GSEL_3X);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->int_src, APDS9306_INT_SRC_ALS);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(rf->int_en, 0);
+ if (ret)
+ return ret;
+
+ return regmap_field_write(rf->int_thresh_var_en, 0);
+}
+
+static int apds9306_pm_init(struct apds9306_data *data)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ ret = apds9306_power_state(data, true);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 5000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_get(dev);
+
+ return 0;
+}
+
+static int apds9306_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct apds9306_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ mutex_init(&data->mutex);
+
+ data->regmap = devm_regmap_init_i2c(client, &apds9306_regmap);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "regmap initialization failed\n");
+
+ data->dev = dev;
+ i2c_set_clientdata(client, indio_dev);
+
+ ret = apds9306_regfield_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "regfield initialization failed\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+ indio_dev->name = "apds9306";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ if (client->irq) {
+ indio_dev->info = &apds9306_info;
+ indio_dev->channels = apds9306_channels_with_events;
+ indio_dev->num_channels = ARRAY_SIZE(apds9306_channels_with_events);
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ apds9306_irq_handler, IRQF_ONESHOT,
+ "apds9306_event", indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to assign interrupt.\n");
+ } else {
+ indio_dev->info = &apds9306_info_no_events;
+ indio_dev->channels = apds9306_channels_without_events;
+ indio_dev->num_channels =
+ ARRAY_SIZE(apds9306_channels_without_events);
+ }
+
+ ret = apds9306_pm_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed pm init\n");
+
+ ret = apds9306_device_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init device\n");
+
+ ret = devm_add_action_or_reset(dev, apds9306_powerdown, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action or reset\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed iio device registration\n");
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int apds9306_runtime_suspend(struct device *dev)
+{
+ struct apds9306_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return apds9306_power_state(data, false);
+}
+
+static int apds9306_runtime_resume(struct device *dev)
+{
+ struct apds9306_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return apds9306_power_state(data, true);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(apds9306_pm_ops,
+ apds9306_runtime_suspend,
+ apds9306_runtime_resume,
+ NULL);
+
+static const struct of_device_id apds9306_of_match[] = {
+ { .compatible = "avago,apds9306" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, apds9306_of_match);
+
+static struct i2c_driver apds9306_driver = {
+ .driver = {
+ .name = "apds9306",
+ .pm = pm_ptr(&apds9306_pm_ops),
+ .of_match_table = apds9306_of_match,
+ },
+ .probe = apds9306_probe,
+};
+module_i2c_driver(apds9306_driver);
+
+MODULE_AUTHOR("Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>");
+MODULE_DESCRIPTION("APDS9306 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(IIO_GTS_HELPER);
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 50f95c5d2060a..d4e17079b2f43 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -291,7 +291,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
if (!iio_dev)
return -ENOMEM;
- dev_set_drvdata(dev, (void *)iio_dev);
+ dev_set_drvdata(dev, iio_dev);
hw = iio_priv(iio_dev);
hw->irq = irq;
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index fe8734468ed35..871b2214121b0 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -27,20 +27,20 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/nvmem-provider.h>
-#include <linux/regmap.h>
+#include <linux/completion.h>
#include <linux/delay.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
+#include <linux/device.h>
#include <linux/gpio/consumer.h>
-#include <linux/regulator/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h> /* For irq_get_irq_data() */
-#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
#include <asm/unaligned.h>
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
index 1ff091b2f764d..7d882e15e5564 100644
--- a/drivers/iio/pressure/dps310.c
+++ b/drivers/iio/pressure/dps310.c
@@ -171,7 +171,7 @@ static int dps310_temp_workaround(struct dps310_data *data)
int reg;
rc = regmap_read(data->regmap, 0x32, &reg);
- if (rc)
+ if (rc < 0)
return rc;
/*
@@ -256,24 +256,24 @@ static int dps310_startup(struct dps310_data *data)
return dps310_temp_workaround(data);
}
-static int dps310_get_pres_precision(struct dps310_data *data)
+static int dps310_get_pres_precision(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
if (rc < 0)
return rc;
- return BIT(val & GENMASK(2, 0));
+ *val = BIT(reg_val & GENMASK(2, 0));
+
+ return 0;
}
-static int dps310_get_temp_precision(struct dps310_data *data)
+static int dps310_get_temp_precision(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
if (rc < 0)
return rc;
@@ -281,7 +281,9 @@ static int dps310_get_temp_precision(struct dps310_data *data)
* Scale factor is bottom 4 bits of the register, but 1111 is
* reserved so just grab bottom three
*/
- return BIT(val & GENMASK(2, 0));
+ *val = BIT(reg_val & GENMASK(2, 0));
+
+ return 0;
}
/* Called with lock held */
@@ -350,48 +352,56 @@ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq)
DPS310_TMP_RATE_BITS, val);
}
-static int dps310_get_pres_samp_freq(struct dps310_data *data)
+static int dps310_get_pres_samp_freq(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
if (rc < 0)
return rc;
- return BIT((val & DPS310_PRS_RATE_BITS) >> 4);
+ *val = BIT((reg_val & DPS310_PRS_RATE_BITS) >> 4);
+
+ return 0;
}
-static int dps310_get_temp_samp_freq(struct dps310_data *data)
+static int dps310_get_temp_samp_freq(struct dps310_data *data, int *val)
{
- int rc;
- int val;
+ int reg_val, rc;
- rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
if (rc < 0)
return rc;
- return BIT((val & DPS310_TMP_RATE_BITS) >> 4);
+ *val = BIT((reg_val & DPS310_TMP_RATE_BITS) >> 4);
+
+ return 0;
}
-static int dps310_get_pres_k(struct dps310_data *data)
+static int dps310_get_pres_k(struct dps310_data *data, int *val)
{
- int rc = dps310_get_pres_precision(data);
+ int reg_val, rc;
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
if (rc < 0)
return rc;
- return scale_factors[ilog2(rc)];
+ *val = scale_factors[reg_val & GENMASK(2, 0)];
+
+ return 0;
}
-static int dps310_get_temp_k(struct dps310_data *data)
+static int dps310_get_temp_k(struct dps310_data *data, int *val)
{
- int rc = dps310_get_temp_precision(data);
+ int reg_val, rc;
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
if (rc < 0)
return rc;
- return scale_factors[ilog2(rc)];
+ *val = scale_factors[reg_val & GENMASK(2, 0)];
+
+ return 0;
}
static int dps310_reset_wait(struct dps310_data *data)
@@ -464,7 +474,10 @@ static int dps310_read_pres_raw(struct dps310_data *data)
if (mutex_lock_interruptible(&data->lock))
return -EINTR;
- rate = dps310_get_pres_samp_freq(data);
+ rc = dps310_get_pres_samp_freq(data, &rate);
+ if (rc)
+ goto done;
+
timeout = DPS310_POLL_TIMEOUT_US(rate);
/* Poll for sensor readiness; base the timeout upon the sample rate. */
@@ -510,7 +523,10 @@ static int dps310_read_temp_raw(struct dps310_data *data)
if (mutex_lock_interruptible(&data->lock))
return -EINTR;
- rate = dps310_get_temp_samp_freq(data);
+ rc = dps310_get_temp_samp_freq(data, &rate);
+ if (rc)
+ goto done;
+
timeout = DPS310_POLL_TIMEOUT_US(rate);
/* Poll for sensor readiness; base the timeout upon the sample rate. */
@@ -612,13 +628,13 @@ static int dps310_write_raw(struct iio_dev *iio,
return rc;
}
-static int dps310_calculate_pressure(struct dps310_data *data)
+static int dps310_calculate_pressure(struct dps310_data *data, int *val)
{
int i;
int rc;
int t_ready;
- int kpi = dps310_get_pres_k(data);
- int kti = dps310_get_temp_k(data);
+ int kpi;
+ int kti;
s64 rem = 0ULL;
s64 pressure = 0ULL;
s64 p;
@@ -629,11 +645,13 @@ static int dps310_calculate_pressure(struct dps310_data *data)
s64 kp;
s64 kt;
- if (kpi < 0)
- return kpi;
+ rc = dps310_get_pres_k(data, &kpi);
+ if (rc)
+ return rc;
- if (kti < 0)
- return kti;
+ rc = dps310_get_temp_k(data, &kti);
+ if (rc)
+ return rc;
kp = (s64)kpi;
kt = (s64)kti;
@@ -687,7 +705,9 @@ static int dps310_calculate_pressure(struct dps310_data *data)
if (pressure < 0LL)
return -ERANGE;
- return (int)min_t(s64, pressure, INT_MAX);
+ *val = (int)min_t(s64, pressure, INT_MAX);
+
+ return 0;
}
static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
@@ -697,11 +717,10 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- rc = dps310_get_pres_samp_freq(data);
- if (rc < 0)
+ rc = dps310_get_pres_samp_freq(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
@@ -709,20 +728,17 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
if (rc)
return rc;
- rc = dps310_calculate_pressure(data);
- if (rc < 0)
+ rc = dps310_calculate_pressure(data, val);
+ if (rc)
return rc;
- *val = rc;
*val2 = 1000; /* Convert Pa to KPa per IIO ABI */
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- rc = dps310_get_pres_precision(data);
- if (rc < 0)
+ rc = dps310_get_pres_precision(data, val);
+ if (rc)
return rc;
-
- *val = rc;
return IIO_VAL_INT;
default:
@@ -730,14 +746,15 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
}
}
-static int dps310_calculate_temp(struct dps310_data *data)
+static int dps310_calculate_temp(struct dps310_data *data, int *val)
{
s64 c0;
s64 t;
- int kt = dps310_get_temp_k(data);
+ int kt, rc;
- if (kt < 0)
- return kt;
+ rc = dps310_get_temp_k(data, &kt);
+ if (rc)
+ return rc;
/* Obtain inverse-scaled offset */
c0 = div_s64((s64)kt * (s64)data->c0, 2);
@@ -746,7 +763,9 @@ static int dps310_calculate_temp(struct dps310_data *data)
t = c0 + ((s64)data->temp_raw * (s64)data->c1);
/* Convert to milliCelsius and scale the temperature */
- return (int)div_s64(t * 1000LL, kt);
+ *val = (int)div_s64(t * 1000LL, kt);
+
+ return 0;
}
static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
@@ -756,11 +775,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
- rc = dps310_get_temp_samp_freq(data);
- if (rc < 0)
+ rc = dps310_get_temp_samp_freq(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
@@ -768,19 +786,17 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
if (rc)
return rc;
- rc = dps310_calculate_temp(data);
- if (rc < 0)
+ rc = dps310_calculate_temp(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- rc = dps310_get_temp_precision(data);
- if (rc < 0)
+ rc = dps310_get_temp_precision(data, val);
+ if (rc)
return rc;
- *val = rc;
return IIO_VAL_INT;
default:
diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c
index 818fa63034545..337eecc577d2a 100644
--- a/drivers/iio/pressure/hsc030pa_spi.c
+++ b/drivers/iio/pressure/hsc030pa_spi.c
@@ -23,14 +23,9 @@
static int hsc_spi_recv(struct hsc_data *data)
{
struct spi_device *spi = to_spi_device(data->dev);
- struct spi_transfer xfer = {
- .tx_buf = NULL,
- .rx_buf = data->buffer,
- .len = HSC_REG_MEASUREMENT_RD_SIZE,
- };
msleep_interruptible(HSC_RESP_TIME_MS);
- return spi_sync_transfer(spi, &xfer, 1);
+ return spi_read(spi, data->buffer, HSC_REG_MEASUREMENT_RD_SIZE);
}
static int hsc_spi_probe(struct spi_device *spi)
diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c
index 39447c786af34..24d19f3c72927 100644
--- a/drivers/iio/temperature/ltc2983.c
+++ b/drivers/iio/temperature/ltc2983.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <asm/byteorder.h>
@@ -657,7 +658,6 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
const struct ltc2983_sensor *sensor)
{
struct ltc2983_thermocouple *thermo;
- struct fwnode_handle *ref;
u32 oc_current;
int ret;
@@ -704,7 +704,8 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
return ERR_PTR(-EINVAL);
}
- ref = fwnode_find_reference(child, "adi,cold-junction-handle", 0);
+ struct fwnode_handle *ref __free(fwnode_handle) =
+ fwnode_find_reference(child, "adi,cold-junction-handle", 0);
if (IS_ERR(ref)) {
ref = NULL;
} else {
@@ -715,7 +716,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
* the error right away.
*/
dev_err(&st->spi->dev, "Property reg must be given\n");
- goto fail;
+ return ERR_PTR(ret);
}
}
@@ -726,22 +727,15 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
thermo->custom = __ltc2983_custom_sensor_new(st, child,
propname, false,
16384, true);
- if (IS_ERR(thermo->custom)) {
- ret = PTR_ERR(thermo->custom);
- goto fail;
- }
+ if (IS_ERR(thermo->custom))
+ return ERR_CAST(thermo->custom);
}
/* set common parameters */
thermo->sensor.fault_handler = ltc2983_thermocouple_fault_handler;
thermo->sensor.assign_chan = ltc2983_thermocouple_assign_chan;
- fwnode_handle_put(ref);
return &thermo->sensor;
-
-fail:
- fwnode_handle_put(ref);
- return ERR_PTR(ret);
}
static struct ltc2983_sensor *
@@ -751,14 +745,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
struct ltc2983_rtd *rtd;
int ret = 0;
struct device *dev = &st->spi->dev;
- struct fwnode_handle *ref;
u32 excitation_current = 0, n_wires = 0;
rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
if (!rtd)
return ERR_PTR(-ENOMEM);
- ref = fwnode_find_reference(child, "adi,rsense-handle", 0);
+ struct fwnode_handle *ref __free(fwnode_handle) =
+ fwnode_find_reference(child, "adi,rsense-handle", 0);
if (IS_ERR(ref)) {
dev_err(dev, "Property adi,rsense-handle missing or invalid");
return ERR_CAST(ref);
@@ -767,7 +761,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
ret = fwnode_property_read_u32(ref, "reg", &rtd->r_sense_chan);
if (ret) {
dev_err(dev, "Property reg must be given\n");
- goto fail;
+ return ERR_PTR(ret);
}
ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires);
@@ -788,8 +782,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
break;
default:
dev_err(dev, "Invalid number of wires:%u\n", n_wires);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
@@ -799,8 +792,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
if (n_wires == 2 || n_wires == 3) {
dev_err(dev,
"Rotation not allowed for 2/3 Wire RTDs");
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
rtd->sensor_config |= LTC2983_RTD_C_ROTATE(1);
} else {
@@ -830,16 +822,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
"Invalid rsense chann:%d to use in kelvin rsense",
rtd->r_sense_chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
if (sensor->chan < min || sensor->chan > max) {
dev_err(dev, "Invalid chann:%d for the rtd config",
sensor->chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
} else {
/* same as differential case */
@@ -847,8 +837,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
dev_err(&st->spi->dev,
"Invalid chann:%d for RTD", sensor->chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
@@ -857,10 +846,8 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
rtd->custom = __ltc2983_custom_sensor_new(st, child,
"adi,custom-rtd",
false, 2048, false);
- if (IS_ERR(rtd->custom)) {
- ret = PTR_ERR(rtd->custom);
- goto fail;
- }
+ if (IS_ERR(rtd->custom))
+ return ERR_CAST(rtd->custom);
}
/* set common parameters */
@@ -902,18 +889,13 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
dev_err(&st->spi->dev,
"Invalid value for excitation current(%u)",
excitation_current);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
fwnode_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve);
- fwnode_handle_put(ref);
return &rtd->sensor;
-fail:
- fwnode_handle_put(ref);
- return ERR_PTR(ret);
}
static struct ltc2983_sensor *
@@ -922,7 +904,6 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
{
struct ltc2983_thermistor *thermistor;
struct device *dev = &st->spi->dev;
- struct fwnode_handle *ref;
u32 excitation_current = 0;
int ret = 0;
@@ -930,7 +911,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
if (!thermistor)
return ERR_PTR(-ENOMEM);
- ref = fwnode_find_reference(child, "adi,rsense-handle", 0);
+ struct fwnode_handle *ref __free(fwnode_handle) =
+ fwnode_find_reference(child, "adi,rsense-handle", 0);
if (IS_ERR(ref)) {
dev_err(dev, "Property adi,rsense-handle missing or invalid");
return ERR_CAST(ref);
@@ -939,7 +921,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
ret = fwnode_property_read_u32(ref, "reg", &thermistor->r_sense_chan);
if (ret) {
dev_err(dev, "rsense channel must be configured...\n");
- goto fail;
+ return ERR_PTR(ret);
}
if (fwnode_property_read_bool(child, "adi,single-ended")) {
@@ -959,8 +941,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
dev_err(&st->spi->dev,
"Invalid chann:%d for differential thermistor",
sensor->chan);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
/* check custom sensor */
@@ -979,10 +960,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
propname,
steinhart,
64, false);
- if (IS_ERR(thermistor->custom)) {
- ret = PTR_ERR(thermistor->custom);
- goto fail;
- }
+ if (IS_ERR(thermistor->custom))
+ return ERR_CAST(thermistor->custom);
}
/* set common parameters */
thermistor->sensor.fault_handler = ltc2983_common_fault_handler;
@@ -1006,8 +985,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
LTC2983_SENSOR_THERMISTOR_STEINHART) {
dev_err(&st->spi->dev,
"Auto Range not allowed for custom sensors\n");
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
thermistor->excitation_current = 0x0c;
break;
@@ -1048,16 +1026,11 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
dev_err(&st->spi->dev,
"Invalid value for excitation current(%u)",
excitation_current);
- ret = -EINVAL;
- goto fail;
+ return ERR_PTR(-EINVAL);
}
}
- fwnode_handle_put(ref);
return &thermistor->sensor;
-fail:
- fwnode_handle_put(ref);
- return ERR_PTR(ret);
}
static struct ltc2983_sensor *
@@ -1350,8 +1323,7 @@ static irqreturn_t ltc2983_irq_handler(int irq, void *data)
static int ltc2983_parse_fw(struct ltc2983_data *st)
{
struct device *dev = &st->spi->dev;
- struct fwnode_handle *child;
- int ret = 0, chan = 0, channel_avail_mask = 0;
+ int ret, chan = 0, channel_avail_mask = 0;
device_property_read_u32(dev, "adi,mux-delay-config-us", &st->mux_delay_config);
@@ -1369,38 +1341,35 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
return -ENOMEM;
st->iio_channels = st->num_channels;
- device_for_each_child_node(dev, child) {
+ device_for_each_child_node_scoped(dev, child) {
struct ltc2983_sensor sensor;
ret = fwnode_property_read_u32(child, "reg", &sensor.chan);
- if (ret) {
- dev_err(dev, "reg property must given for child nodes\n");
- goto put_child;
- }
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reg property must given for child nodes\n");
/* check if we have a valid channel */
if (sensor.chan < LTC2983_MIN_CHANNELS_NR ||
- sensor.chan > st->info->max_channels_nr) {
- ret = -EINVAL;
- dev_err(dev, "chan:%d must be from %u to %u\n", sensor.chan,
- LTC2983_MIN_CHANNELS_NR, st->info->max_channels_nr);
- goto put_child;
- } else if (channel_avail_mask & BIT(sensor.chan)) {
- ret = -EINVAL;
- dev_err(dev, "chan:%d already in use\n", sensor.chan);
- goto put_child;
- }
+ sensor.chan > st->info->max_channels_nr)
+ return dev_err_probe(dev, -EINVAL,
+ "chan:%d must be from %u to %u\n",
+ sensor.chan,
+ LTC2983_MIN_CHANNELS_NR,
+ st->info->max_channels_nr);
+
+ if (channel_avail_mask & BIT(sensor.chan))
+ return dev_err_probe(dev, -EINVAL,
+ "chan:%d already in use\n",
+ sensor.chan);
ret = fwnode_property_read_u32(child, "adi,sensor-type", &sensor.type);
- if (ret) {
- dev_err(dev,
+ if (ret)
+ return dev_err_probe(dev, ret,
"adi,sensor-type property must given for child nodes\n");
- goto put_child;
- }
dev_dbg(dev, "Create new sensor, type %u, chann %u",
- sensor.type,
- sensor.chan);
+ sensor.type, sensor.chan);
if (sensor.type >= LTC2983_SENSOR_THERMOCOUPLE &&
sensor.type <= LTC2983_SENSOR_THERMOCOUPLE_CUSTOM) {
@@ -1427,17 +1396,15 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) {
st->sensors[chan] = ltc2983_temp_new(child, st, &sensor);
} else {
- dev_err(dev, "Unknown sensor type %d\n", sensor.type);
- ret = -EINVAL;
- goto put_child;
+ return dev_err_probe(dev, -EINVAL,
+ "Unknown sensor type %d\n",
+ sensor.type);
}
- if (IS_ERR(st->sensors[chan])) {
- dev_err(dev, "Failed to create sensor %ld",
- PTR_ERR(st->sensors[chan]));
- ret = PTR_ERR(st->sensors[chan]);
- goto put_child;
- }
+ if (IS_ERR(st->sensors[chan]))
+ return dev_err_probe(dev, PTR_ERR(st->sensors[chan]),
+ "Failed to create sensor\n");
+
/* set generic sensor parameters */
st->sensors[chan]->chan = sensor.chan;
st->sensors[chan]->type = sensor.type;
@@ -1447,9 +1414,6 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
}
return 0;
-put_child:
- fwnode_handle_put(child);
- return ret;
}
static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd,
@@ -1634,6 +1598,10 @@ static int ltc2983_probe(struct spi_device *spi)
if (ret)
return ret;
+ ret = devm_regulator_get_enable(&spi->dev, "vdd");
+ if (ret)
+ return ret;
+
gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h
index 719cf9cc6e1ac..383614ebd760f 100644
--- a/include/linux/iio/adc/ad_sigma_delta.h
+++ b/include/linux/iio/adc/ad_sigma_delta.h
@@ -48,6 +48,7 @@ struct iio_dev;
* be used.
* @irq_flags: flags for the interrupt used by the triggered buffer
* @num_slots: Number of sequencer slots
+ * @irq_line: IRQ for reading conversions. If 0, spi->irq will be used
*/
struct ad_sigma_delta_info {
int (*set_channel)(struct ad_sigma_delta *, unsigned int channel);
@@ -62,6 +63,7 @@ struct ad_sigma_delta_info {
unsigned int data_reg;
unsigned long irq_flags;
unsigned int num_slots;
+ int irq_line;
};
/**
@@ -89,6 +91,7 @@ struct ad_sigma_delta {
unsigned int active_slots;
unsigned int current_slot;
unsigned int num_slots;
+ int irq_line;
bool status_appended;
/* map slots to channels in order to know what to expect from devices */
unsigned int *slots;
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index a6d79381866ec..9d144631134db 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -4,6 +4,7 @@
#include <linux/types.h>
+struct iio_chan_spec;
struct fwnode_handle;
struct iio_backend;
struct device;
@@ -15,6 +16,26 @@ enum iio_backend_data_type {
IIO_BACKEND_DATA_TYPE_MAX
};
+enum iio_backend_data_source {
+ IIO_BACKEND_INTERNAL_CONTINUOS_WAVE,
+ IIO_BACKEND_EXTERNAL,
+ IIO_BACKEND_DATA_SOURCE_MAX
+};
+
+/**
+ * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
+ * @_name: Attribute name
+ * @_shared: Whether the attribute is shared between all channels
+ * @_what: Data private to the driver
+ */
+#define IIO_BACKEND_EX_INFO(_name, _shared, _what) { \
+ .name = (_name), \
+ .shared = (_shared), \
+ .read = iio_backend_ext_info_get, \
+ .write = iio_backend_ext_info_set, \
+ .private = (_what), \
+}
+
/**
* struct iio_backend_data_fmt - Backend data format
* @type: Data type.
@@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
* @chan_enable: Enable one channel.
* @chan_disable: Disable one channel.
* @data_format_set: Configure the data format for a specific channel.
+ * @data_source_set: Configure the data source for a specific channel.
+ * @set_sample_rate: Configure the sampling rate for a specific channel.
* @request_buffer: Request an IIO buffer.
* @free_buffer: Free an IIO buffer.
+ * @extend_chan_spec: Extend an IIO channel.
+ * @ext_info_set: Extended info setter.
+ * @ext_info_get: Extended info getter.
**/
struct iio_backend_ops {
int (*enable)(struct iio_backend *back);
@@ -45,10 +71,21 @@ struct iio_backend_ops {
int (*chan_disable)(struct iio_backend *back, unsigned int chan);
int (*data_format_set)(struct iio_backend *back, unsigned int chan,
const struct iio_backend_data_fmt *data);
+ int (*data_source_set)(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data);
+ int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate_hz);
struct iio_buffer *(*request_buffer)(struct iio_backend *back,
struct iio_dev *indio_dev);
void (*free_buffer)(struct iio_backend *back,
struct iio_buffer *buffer);
+ int (*extend_chan_spec)(struct iio_backend *back,
+ struct iio_chan_spec *chan);
+ int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len);
+ int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf);
};
int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
@@ -56,10 +93,22 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
const struct iio_backend_data_fmt *data);
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+ enum iio_backend_data_source data);
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+ u64 sample_rate_hz);
int devm_iio_backend_request_buffer(struct device *dev,
struct iio_backend *back,
struct iio_dev *indio_dev);
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len);
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf);
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+ struct iio_backend *back,
+ struct iio_chan_spec *chan);
void *iio_backend_get_priv(const struct iio_backend *conv);
struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
struct iio_backend *
diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h
index 18d3702fa95d1..6e27e47077d56 100644
--- a/include/linux/iio/buffer-dma.h
+++ b/include/linux/iio/buffer-dma.h
@@ -132,7 +132,9 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
struct iio_dev *indio_dev);
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
char __user *user_buffer);
-size_t iio_dma_buffer_data_available(struct iio_buffer *buffer);
+int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
+ const char __user *user_buffer);
+size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);
int iio_dma_buffer_request_update(struct iio_buffer *buffer);
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index cbb8ba957fade..81d9a19aeb919 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -7,14 +7,28 @@
#ifndef __IIO_DMAENGINE_H__
#define __IIO_DMAENGINE_H__
+#include <linux/iio/buffer.h>
+
struct iio_dev;
struct device;
-struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
- const char *channel);
void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
-int devm_iio_dmaengine_buffer_setup(struct device *dev,
- struct iio_dev *indio_dev,
- const char *channel);
+struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir);
+
+#define iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
+ iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
+ IIO_BUFFER_DIRECTION_IN)
+
+int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel,
+ enum iio_buffer_direction dir);
+
+#define devm_iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
+ devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
+ IIO_BUFFER_DIRECTION_IN)
#endif
diff --git a/include/linux/property.h b/include/linux/property.h
index 3a1045eb786c4..61fc20e5f81f8 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -13,6 +13,7 @@
#include <linux/args.h>
#include <linux/array_size.h>
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/fwnode.h>
#include <linux/stddef.h>
#include <linux/types.h>
@@ -174,13 +175,32 @@ struct fwnode_handle *device_get_next_child_node(const struct device *dev,
for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child))
+#define device_for_each_child_node_scoped(dev, child) \
+ for (struct fwnode_handle *child __free(fwnode_handle) = \
+ device_get_next_child_node(dev, NULL); \
+ child; child = device_get_next_child_node(dev, child))
+
struct fwnode_handle *fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
const char *childname);
struct fwnode_handle *device_get_named_child_node(const struct device *dev,
const char *childname);
struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode);
-void fwnode_handle_put(struct fwnode_handle *fwnode);
+
+/**
+ * fwnode_handle_put - Drop reference to a device node
+ * @fwnode: Pointer to the device node to drop the reference to.
+ *
+ * This has to be used when terminating device_for_each_child_node() iteration
+ * with break or return to prevent stale device node references from being left
+ * behind.
+ */
+static inline void fwnode_handle_put(struct fwnode_handle *fwnode)
+{
+ fwnode_call_void_op(fwnode, put);
+}
+
+DEFINE_FREE(fwnode_handle, struct fwnode_handle *, fwnode_handle_put(_T))
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name);