diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-07-29 09:52:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-07-29 09:52:01 -0700 |
commit | 0d5ec7919f3747193f051036b2301734a4b5e1d6 (patch) | |
tree | d09d4ccb7f2625813aede8d3bd49638a0f1e5166 | |
parent | 86aa721820952b793a12fc6e5a01734186c0c238 (diff) | |
parent | fa3f79e82dce7b04f7b8cf1791268a775b3d6f9f (diff) |
Merge tag 'char-misc-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char / misc / IIO / other driver updates from Greg KH:
"Here is the big set of char/misc/iio and other smaller driver
subsystems for 6.17-rc1. It's a big set this time around, with the
huge majority being in the iio subsystem with new drivers and dts
files being added there.
Highlights include:
- IIO driver updates, additions, and changes making more code const
and cleaning up some init logic
- bus_type constant conversion changes
- misc device test functions added
- rust miscdevice minor fixup
- unused function removals for some drivers
- mei driver updates
- mhi driver updates
- interconnect driver updates
- Android binder updates and test infrastructure added
- small cdx driver updates
- small comedi fixes
- small nvmem driver updates
- small pps driver updates
- some acrn virt driver fixes for printk messages
- other small driver updates
All of these have been in linux-next with no reported issues"
* tag 'char-misc-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (292 commits)
binder: Use seq_buf in binder_alloc kunit tests
binder: Add copyright notice to new kunit files
misc: ti_fpc202: Switch to of_fwnode_handle()
bus: moxtet: Use dev_fwnode()
pc104: move PC104 option to drivers/Kconfig
drivers: virt: acrn: Don't use %pK through printk
comedi: fix race between polling and detaching
interconnect: qcom: Add Milos interconnect provider driver
dt-bindings: interconnect: document the RPMh Network-On-Chip Interconnect in Qualcomm Milos SoC
mei: more prints with client prefix
mei: bus: use cldev in prints
bus: mhi: host: pci_generic: Add Telit FN990B40 modem support
bus: mhi: host: Detect events pointing to unexpected TREs
bus: mhi: host: pci_generic: Add Foxconn T99W696 modem
bus: mhi: host: Use str_true_false() helper
bus: mhi: host: pci_generic: Add support for EM929x and set MRU to 32768 for better performance.
bus: mhi: host: Fix endianness of BHI vector table
bus: mhi: host: pci_generic: Disable runtime PM for QDU100
bus: mhi: host: pci_generic: Fix the modem name of Foxconn T99W640
dt-bindings: interconnect: qcom,msm8998-bwmon: Allow 'nonposted-mmio'
...
281 files changed, 15252 insertions, 2526 deletions
diff --git a/Documentation/ABI/obsolete/sysfs-bus-iio b/Documentation/ABI/obsolete/sysfs-bus-iio index b64394b0b374..a13523561958 100644 --- a/Documentation/ABI/obsolete/sysfs-bus-iio +++ b/Documentation/ABI/obsolete/sysfs-bus-iio @@ -48,10 +48,6 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en -What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en -What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en @@ -73,10 +69,6 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_type What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type What: /sys/.../iio:deviceX/scan_elements/in_voltage_type What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type -What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type -What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type What: /sys/.../iio:deviceX/scan_elements/in_pressure_type @@ -110,10 +102,6 @@ Description: What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index -What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index -What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index -What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 190bfcc1e836..7e31b8cd49b3 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -141,8 +141,6 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -417,18 +415,14 @@ What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_offset +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_q_offset +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_currentY_offset What: /sys/bus/iio/devices/iio:deviceX/in_current_offset -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_offset -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_current_q_offset -What: /sys/bus/iio/devices/iio:deviceX/in_current_i_offset What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset @@ -456,21 +450,15 @@ Description: to the _raw output. What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_scale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_scale What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_scale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_scale -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_scale -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_scale -What: /sys/bus/iio/devices/iio:deviceX/in_current_i_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_q_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale @@ -559,6 +547,30 @@ Description: - a small discrete set of values like "0 2 4 6 8" - a range specified as "[min step max]" +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_convdelay +KernelVersion: 6.17 +Contact: linux-iio@vger.kernel.org +Description: + Delay of start of conversion from common reference point shared + by all channels. Can be writable when used to compensate for + delay variation introduced by external filters feeding a + simultaneous sampling ADC. + + E.g., for the ad7606 ADC series, this value is intended as a + configurable time delay in seconds, to correct delay introduced + by an optional external filtering circuit. + +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_convdelay_available +KernelVersion: 6.16 +Contact: linux-iio@vger.kernel.org +Description: + Available values of convdelay. Maybe expressed as: + + - a range specified as "[min step max]" + + If shared across all channels, <type>_convdelay_available + is used. + What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale @@ -579,11 +591,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale -What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibscale What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibscale @@ -805,7 +813,11 @@ Description: all the other channels, since it involves changing the VCO fundamental output frequency. +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_i_phase +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_q_phase What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_phase +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_i_phase +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_q_phase KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org Description: @@ -1434,10 +1446,6 @@ What: /sys/.../iio:deviceX/bufferY/in_timestamp_en What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_en What: /sys/.../iio:deviceX/bufferY/in_voltageY_en What: /sys/.../iio:deviceX/bufferY/in_voltageY-voltageZ_en -What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_en -What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_en -What: /sys/.../iio:deviceX/bufferY/in_voltage_i_en -What: /sys/.../iio:deviceX/bufferY/in_voltage_q_en What: /sys/.../iio:deviceX/bufferY/in_incli_x_en What: /sys/.../iio:deviceX/bufferY/in_incli_y_en What: /sys/.../iio:deviceX/bufferY/in_pressureY_en @@ -1458,10 +1466,6 @@ What: /sys/.../iio:deviceX/bufferY/in_incli_type What: /sys/.../iio:deviceX/bufferY/in_voltageY_type What: /sys/.../iio:deviceX/bufferY/in_voltage_type What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_type -What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_type -What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_type -What: /sys/.../iio:deviceX/bufferY/in_voltage_i_type -What: /sys/.../iio:deviceX/bufferY/in_voltage_q_type What: /sys/.../iio:deviceX/bufferY/in_timestamp_type What: /sys/.../iio:deviceX/bufferY/in_pressureY_type What: /sys/.../iio:deviceX/bufferY/in_pressure_type @@ -1499,10 +1503,6 @@ Description: What: /sys/.../iio:deviceX/bufferY/in_voltageY_index What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_index -What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_index -What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_index -What: /sys/.../iio:deviceX/bufferY/in_voltage_i_index -What: /sys/.../iio:deviceX/bufferY/in_voltage_q_index What: /sys/.../iio:deviceX/bufferY/in_accel_x_index What: /sys/.../iio:deviceX/bufferY/in_accel_y_index What: /sys/.../iio:deviceX/bufferY/in_accel_z_index @@ -1692,8 +1692,6 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_raw -What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_raw KernelVersion: 3.17 Contact: linux-iio@vger.kernel.org Description: @@ -2278,6 +2276,9 @@ Description: Reading returns a list with the possible filter modes. Options for the attribute: + * "none" - Filter is disabled/bypassed. + * "sinc1" - The digital sinc1 filter. Fast 1st + conversion time. Poor noise performance. * "sinc3" - The digital sinc3 filter. Moderate 1st conversion time. Good noise performance. * "sinc4" - Sinc 4. Excellent noise performance. Long @@ -2293,6 +2294,8 @@ Description: * "sinc3+pf2" - Sinc3 + device specific Post Filter 2. * "sinc3+pf3" - Sinc3 + device specific Post Filter 3. * "sinc3+pf4" - Sinc3 + device specific Post Filter 4. + * "sinc5+pf1" - Sinc5 + device specific Post Filter 1. + * "sinc5+avg" - Sinc5 + averaging by 4. * "wideband" - filter with wideband low ripple passband and sharp transition band. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 index de1e323e5d47..9cf8cd0dd2df 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 @@ -1,10 +1,10 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_i_calibphase +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-altvoltage1_i_calibphase KernelVersion: Contact: linux-iio@vger.kernel.org Description: Read/write unscaled value for the Local Oscillatior path quadrature I phase shift. -What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_q_calibphase +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-altvoltage1_q_calibphase KernelVersion: Contact: linux-iio@vger.kernel.org Description: diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml new file mode 100644 index 000000000000..ed849ba1b77b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4080.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4080 20-Bit, 40 MSPS, Differential SAR ADC + +maintainers: + - Antoniu Miclaus <antoniu.miclaus@analog.com> + +description: | + The AD4080 is a high speed, low noise, low distortion, 20-bit, Easy Drive, + successive approximation register (SAR) analog-to-digital converter (ADC). + Maintaining high performance (signal-to-noise and distortion (SINAD) ratio + > 90 dBFS) at signal frequencies in excess of 1 MHz enables the AD4080 to + service a wide variety of precision, wide bandwidth data acquisition + applications. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4080.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,ad4080 + + reg: + maxItems: 1 + + spi-max-frequency: + description: Configuration of the SPI bus. + maximum: 50000000 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: cnv + + vdd33-supply: true + + vdd11-supply: true + + vddldo-supply: true + + iovdd-supply: true + + vrefin-supply: true + + io-backends: + maxItems: 1 + + adi,lvds-cnv-enable: + description: Enable the LVDS signal type on the CNV pin. Default is CMOS. + type: boolean + + adi,num-lanes: + description: + Number of lanes on which the data is sent on the output (DA, DB pins). + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + default: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - vdd33-supply + - vrefin-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4080"; + reg = <0>; + spi-max-frequency = <10000000>; + vdd33-supply = <&vdd33>; + vddldo-supply = <&vddldo>; + vrefin-supply = <&vrefin>; + clocks = <&cnv>; + clock-names = "cnv"; + io-backends = <&iio_backend>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml new file mode 100644 index 000000000000..da93213d12d4 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml @@ -0,0 +1,554 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad4170-4.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD4170-4 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt <marcelo.schmitt@analog.com> + +description: | + Analog Devices AD4170-4 series of Sigma-delta Analog to Digital Converters. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4170-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4190-4.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4195-4.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +$defs: + reference-buffer: + description: | + Enable precharge buffer, full buffer, or skip reference buffering of + the positive/negative voltage reference. Because the output impedance + of the source driving the voltage reference inputs may be dynamic, + resistive/capacitive combinations of those inputs can cause DC gain + errors if the reference inputs go unbuffered into the ADC. Enable + reference buffering if the provided reference source has dynamic high + impedance output. Note the absolute voltage allowed on REFINn+ and REFINn- + inputs is from AVSS - 50 mV to AVDD + 50 mV when the reference buffers are + disabled but narrows to AVSS to AVDD when reference buffering is enabled + or in precharge mode. + $ref: /schemas/types.yaml#/definitions/string + enum: [ precharge, full, disabled ] + default: full + +properties: + compatible: + enum: + - adi,ad4170-4 + - adi,ad4190-4 + - adi,ad4195-4 + + avss-supply: + description: + Reference voltage supply for AVSS. A −2.625V minimum and 0V maximum supply + that powers the chip. If not provided, AVSS is assumed to be at system + ground (0V). + + avdd-supply: + description: + A supply of 4.75V to 5.25V relative to AVSS that powers the chip (AVDD). + + iovdd-supply: + description: 1.7V to 5.25V reference supply to the serial interface (IOVDD). + + refin1p-supply: + description: REFIN+ supply that can be used as reference for conversion. + + refin1n-supply: + description: REFIN- supply that can be used as reference for conversion. + + refin2p-supply: + description: REFIN2+ supply that can be used as reference for conversion. + + refin2n-supply: + description: REFIN2- supply that can be used as reference for conversion. + + spi-cpol: true + + spi-cpha: true + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The data + ready signal (RDY) used as interrupt is by default provided on the SDO + pin. Alternatively, it can be provided on the DIG_AUX1 pin in which case + the chip disables the RDY function on SDO. Thus, there can be only one + data ready interrupt enabled at a time. + + interrupt-names: + description: + Specify which pin should be configured as Data Ready interrupt. + enum: + - sdo + - dig_aux1 + + clocks: + maxItems: 1 + description: + Optional external clock source. Can specify either an external clock or + external crystal. + + clock-names: + enum: + - ext-clk + - xtal + default: ext-clk + + '#clock-cells': + const: 0 + + clock-output-names: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is for the GPIO number: 0 to 3. + The second cell takes standard GPIO flags. + + ldac-gpios: + description: + GPIO connected to DIG_AUX2 pin to be used as LDAC toggle to control the + transfer of data from the DAC_INPUT_A register to the DAC. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + adi,vbias-pins: + description: Analog inputs to apply a voltage bias of (AVDD − AVSS) / 2 to. + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 9 + items: + minimum: 0 + maximum: 8 + +allOf: + # Some devices don't have integrated DAC + - if: + properties: + compatible: + contains: + enum: + - adi,ad4190-4 + - adi,ad4195-4 + then: + properties: + ldac-gpios: false + + # Require to specify the interrupt pin when using interrupts + - if: + required: + - interrupts + then: + required: + - interrupt-names + + # If an external clock is set, the internal clock cannot go out and vice versa + - oneOf: + - required: [clocks] + properties: + '#clock-cells': false + - required: ['#clock-cells'] + properties: + clocks: false + +required: + - compatible + - reg + - avdd-supply + - iovdd-supply + - spi-cpol + - spi-cpha + +unevaluatedProperties: false + +patternProperties: + "^channel@[0-9a-f]$": + $ref: /schemas/iio/adc/adc.yaml# + unevaluatedProperties: false + description: + Represents the external channels which are connected to the ADC. + + properties: + reg: + description: + The channel number. + minimum: 0 + maximum: 15 + + diff-channels: + description: | + This property is used for defining the inputs of a differential + voltage channel. The first value is the positive input and the second + value is the negative input of the channel. + + Besides the analog input pins AIN0 to AIN8, there are special inputs + that can be selected with the following values: + 17: Internal temperature sensor + 18: (AVDD-AVSS)/5 + 19: (IOVDD-DGND)/5 + 20: DAC output + 21: ALDO + 22: DLDO + 23: AVSS + 24: DGND + 25: REFIN+ + 26: REFIN- + 27: REFIN2+ + 28: REFIN2- + 29: REFOUT + For the internal temperature sensor, use the input number for both + inputs (i.e. diff-channels = <17 17>). + items: + enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29] + + adi,reference-select: + description: | + Select the reference source to use when converting on the + specific channel. Valid values are: + 0: REFIN+/REFIN- + 1: REFIN2+/REFIN2− + 2: REFOUT/AVSS (internal reference) + 3: AVDD/AVSS + If not specified, REFOUT/AVSS is used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + default: 1 + + adi,positive-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,negative-reference-buffer: + $ref: '#/$defs/reference-buffer' + + adi,sensor-type: + description: + The AD4170-4 and similar designs have features to aid interfacing with + load cell weigh scale, RTD, and thermocouple sensors. Each of those + sensor types requires either distinct wiring configuration or + external circuitry for proper sensor operation and can use different + ADC chip functionality on their setups. A key characteristic of those + external sensors is that they must be excited either by voltage supply + or by ADC chip excitation signals. The sensor can then be read through + a pair of analog inputs. This property specifies which particular + sensor type is connected to the ADC so it can be properly setup and + handled. Omit this property for conventional (not weigh scale, RTD, or + thermocouple) ADC channel setups. + $ref: /schemas/types.yaml#/definitions/string + enum: [ weighscale, rtd, thermocouple ] + + adi,excitation-pin-0: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-1: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-2: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-pin-3: + description: + Analog input to apply excitation current to while the channel + is active. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 20 + default: 0 + + adi,excitation-current-0-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-0 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-1-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-1 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-2-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-2 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-current-3-microamp: + description: + Excitation current in microamperes to be applied to pin specified in + adi,excitation-pin-3 while this channel is active. + enum: [0, 10, 50, 100, 250, 500, 1000, 1500] + default: 0 + + adi,excitation-ac: + type: boolean + description: + Whether the external sensor has to be AC or DC excited. When omitted, + it is DC excited. + + allOf: + - oneOf: + - required: [single-channel, common-mode-channel] + properties: + diff-channels: false + - required: [diff-channels] + properties: + single-channel: false + common-mode-channel: false + # Usual ADC channels don't need external circuitry excitation. + - if: + not: + required: + - adi,sensor-type + then: + properties: + adi,excitation-pin-0: false + adi,excitation-pin-1: false + adi,excitation-pin-2: false + adi,excitation-pin-3: false + adi,excitation-current-0-microamp: false + adi,excitation-current-1-microamp: false + adi,excitation-current-2-microamp: false + adi,excitation-current-3-microamp: false + adi,excitation-ac: false + # Weigh scale bridge AC excited with one pair of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + # Weigh scale bridge AC excited with two pairs of predefined signals. + - if: + allOf: + - properties: + adi,sensor-type: + contains: + const: weighscale + - required: + - adi,excitation-ac + - adi,excitation-pin-0 + - adi,excitation-pin-1 + - adi,excitation-pin-2 + - adi,excitation-pin-3 + - not: + required: + - adi,excitation-current-0-microamp + - adi,excitation-current-1-microamp + - adi,excitation-current-2-microamp + - adi,excitation-current-3-microamp + then: + properties: + adi,excitation-pin-0: + const: 17 + adi,excitation-pin-1: + const: 18 + adi,excitation-pin-2: + const: 19 + adi,excitation-pin-3: + const: 20 + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170-4"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + clocks = <&clk>; + clock-names = "xtal"; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "dig_aux1"; + adi,vbias-pins = <8>; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@0 { + reg = <0>; + single-channel = <0>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Weigh scale sensor + channel@1 { + reg = <1>; + bipolar; + diff-channels = <1 2>; + adi,reference-select = <0>; + adi,positive-reference-buffer = "precharge"; + adi,negative-reference-buffer = "precharge"; + adi,sensor-type = "weighscale"; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + // RTD sensor + channel@2 { + reg = <2>; + bipolar; + diff-channels = <3 4>; + adi,reference-select = <0>; + adi,sensor-type = "rtd"; + adi,excitation-pin-0 = <5>; + adi,excitation-pin-1 = <6>; + adi,excitation-current-0-microamp = <500>; + adi,excitation-current-1-microamp = <500>; + adi,excitation-ac; + }; + // Thermocouple sensor + channel@3 { + reg = <3>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "thermocouple"; + adi,excitation-pin-0 = <18>; + adi,excitation-current-0-microamp = <500>; + }; + }; + }; + - | + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4170-4"; + reg = <0>; + spi-max-frequency = <20000000>; + spi-cpol; + spi-cpha; + avdd-supply = <&avdd>; + iovdd-supply = <&iovdd>; + #clock-cells = <0>; + clock-output-names = "ad4170-clk16mhz"; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "dig_aux1"; + #address-cells = <1>; + #size-cells = <0>; + + // Sample AIN0 with respect to AIN1 throughout AVDD/AVSS input range + // Differential bipolar. If AVSS < 0V, differential true bipolar + channel@0 { + reg = <0>; + bipolar; + diff-channels = <0 1>; + adi,reference-select = <3>; + }; + // Sample AIN2 with respect to DGND throughout AVDD/DGND input range + // Pseudo-differential unipolar + channel@1 { + reg = <1>; + single-channel = <2>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN3 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@2 { + reg = <2>; + bipolar; + single-channel = <3>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN4 with respect to DGND throughout AVDD/AVSS input range + // Pseudo-differential bipolar + channel@3 { + reg = <3>; + bipolar; + single-channel = <4>; + common-mode-channel = <24>; + adi,reference-select = <3>; + }; + // Sample AIN5 with respect to 2.5V throughout AVDD/AVSS input range + // Pseudo-differential unipolar (AD4170-4 datasheet page 46 example) + channel@4 { + reg = <4>; + single-channel = <5>; + common-mode-channel = <29>; + adi,reference-select = <3>; + }; + // Sample AIN6 with respect to 2.5V throughout REFIN+/REFIN- input range + // Pseudo-differential bipolar + channel@5 { + reg = <5>; + bipolar; + single-channel = <6>; + common-mode-channel = <29>; + adi,reference-select = <0>; + }; + // Weigh scale sensor + channel@6 { + reg = <6>; + bipolar; + diff-channels = <7 8>; + adi,reference-select = <0>; + adi,sensor-type = "weighscale"; + adi,excitation-pin-0 = <17>; + adi,excitation-pin-1 = <18>; + adi,excitation-pin-2 = <19>; + adi,excitation-pin-3 = <20>; + adi,excitation-ac; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml index c6676d91b4e6..b107322e0ea3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml @@ -69,6 +69,8 @@ properties: spi-max-frequency: maximum: 25000000 + spi-3wire: true + '#address-cells': const: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7405.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7405.yaml new file mode 100644 index 000000000000..57f097025705 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7405.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2025 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad7405.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD7405 family + +maintainers: + - Dragos Bogdan <dragos.bogdan@analog.com> + - Pop Ioan Daniel <pop.ioan-daniel@analog.com> + +description: | + Analog Devices AD7405 is a high performance isolated ADC, 1-channel, + 16-bit with a second-order Σ-Δ modulator that converts an analog input signal + into a high speed, single-bit data stream. + + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7405.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adum7701.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/adum7702.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ADuM7703.pdf + +properties: + compatible: + enum: + - adi,ad7405 + - adi,adum7701 + - adi,adum7702 + - adi,adum7703 + + clocks: + maxItems: 1 + + vdd1-supply: true + + vdd2-supply: true + + io-backends: + maxItems: 1 + +required: + - compatible + - clocks + - vdd1-supply + - vdd2-supply + - io-backends + +additionalProperties: false + +examples: + - | + adc { + compatible = "adi,ad7405"; + clocks = <&axi_clk_gen 0>; + vdd1-supply = <&vdd1>; + vdd2-supply = <&vdd2>; + io-backends = <&axi_adc>; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index 1a5209139e13..1180d2ffbf84 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -204,6 +204,15 @@ patternProperties: considered a bipolar differential channel. Otherwise it is bipolar single-ended. + adi,rfilter-ohms: + description: + For ADCs that supports gain calibration, this property must be set to + the value of the external RFilter resistor. Proper gain error + correction is applied based on this value. + default: 0 + minimum: 0 + maximum: 64512 + required: - reg - bipolar @@ -262,6 +271,25 @@ allOf: - adi,ad7607 - adi,ad7608 - adi,ad7609 + - adi,ad7616 + then: + patternProperties: + "^channel@[0-9a-f]+$": + properties: + adi,rfilter-ohms: false + + - if: + properties: + compatible: + contains: + enum: + - adi,ad7605-4 + - adi,ad7606-4 + - adi,ad7606-6 + - adi,ad7606-8 + - adi,ad7607 + - adi,ad7608 + - adi,ad7609 then: properties: adi,sw-mode: false @@ -392,6 +420,7 @@ examples: reg = <8>; diff-channels = <8 8>; bipolar; + adi,rfilter-ohms = <2048>; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml index 3ce59d4d065f..c06d0fc791d3 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml @@ -26,7 +26,26 @@ properties: clock-names: const: mclk + trigger-sources: + $ref: /schemas/types.yaml#/definitions/phandle-array + minItems: 1 + maxItems: 2 + description: | + A list of phandles referencing trigger source providers. Each entry + represents a trigger source for the ADC: + + - First entry specifies the device responsible for driving the + synchronization (SYNC_IN) pin, as an alternative to adi,sync-in-gpios. + This can be a `gpio-trigger` or another `ad7768-1` device. If the + device's own SYNC_OUT pin is internally connected to its SYNC_IN pin, + reference the device itself or omit this property. + - Second entry optionally defines a GPIO3 pin used as a START signal trigger. + + Use the accompanying trigger source cell to identify the type of each entry. + interrupts: + description: + DRDY (Data Ready) pin, which signals conversion results are available. maxItems: 1 '#address-cells': @@ -47,6 +66,19 @@ properties: in any way, for example if the filter decimation rate changes. As the line is active low, it should be marked GPIO_ACTIVE_LOW. + regulators: + type: object + description: + list of regulators provided by this controller. + + properties: + vcm-output: + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + additionalProperties: false + reset-gpios: maxItems: 1 @@ -57,6 +89,23 @@ properties: "#io-channel-cells": const: 1 + "#trigger-source-cells": + description: | + Cell indicates the trigger output signal: 0 = SYNC_OUT, 1 = GPIO3, + 2 = DRDY. + + For better readability, macros for these values are available in + dt-bindings/iio/adc/adi,ad7768-1.h. + const: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + description: | + The first cell is for the GPIO number: 0 to 3. + The second cell takes standard GPIO flags. + required: - compatible - reg @@ -65,7 +114,16 @@ required: - vref-supply - spi-cpol - spi-cpha - - adi,sync-in-gpios + +dependencies: + adi,sync-in-gpios: + not: + required: + - trigger-sources + trigger-sources: + not: + required: + - adi,sync-in-gpios patternProperties: "^channel@([0-9]|1[0-5])$": @@ -105,6 +163,8 @@ examples: spi-max-frequency = <2000000>; spi-cpol; spi-cpha; + gpio-controller; + #gpio-cells = <2>; vref-supply = <&adc_vref>; interrupts = <25 IRQ_TYPE_EDGE_RISING>; interrupt-parent = <&gpio>; @@ -120,6 +180,12 @@ examples: reg = <0>; label = "channel_0"; }; + + regulators { + vcm_reg: vcm-output { + regulator-name = "ad7768-1-vcm"; + }; + }; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml index cf74f84d6103..e91e421a3d6b 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml @@ -27,6 +27,7 @@ description: | the ad7606 family. https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + https://analogdevicesinc.github.io/hdl/library/axi_ad408x/index.html https://analogdevicesinc.github.io/hdl/library/axi_ad485x/index.html http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html @@ -34,6 +35,7 @@ properties: compatible: enum: - adi,axi-adc-10.0.a + - adi,axi-ad408x - adi,axi-ad7606x - adi,axi-ad485x diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml index b489c984c1bb..14363389f30a 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml @@ -34,6 +34,10 @@ properties: - const: mediatek,mt2701-auxadc - items: - enum: + - mediatek,mt7981-auxadc + - const: mediatek,mt7986-auxadc + - items: + - enum: - mediatek,mt6893-auxadc - mediatek,mt8183-auxadc - mediatek,mt8186-auxadc diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml index 6497c416094d..5d4ab701f51a 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt6359-auxadc.yaml @@ -22,6 +22,8 @@ properties: - mediatek,mt6357-auxadc - mediatek,mt6358-auxadc - mediatek,mt6359-auxadc + - mediatek,mt6363-auxadc + - mediatek,mt6373-auxadc "#io-channel-cells": const: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml index 2c5032be83bd..fd815ab30df1 100644 --- a/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/nxp,lpc3220-adc.yaml @@ -22,6 +22,9 @@ properties: interrupts: maxItems: 1 + clocks: + maxItems: 1 + vref-supply: true "#io-channel-cells": diff --git a/Documentation/devicetree/bindings/iio/adc/st,spear600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,spear600-adc.yaml new file mode 100644 index 000000000000..dd9ec3038703 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/st,spear600-adc.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/st,spear600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST SPEAr ADC device driver + +maintainers: + - Jonathan Cameron <jic23@kernel.org> + +description: | + Integrated ADC inside the ST SPEAr SoC, SPEAr600, supporting + 10-bit resolution. Datasheet can be found here: + https://www.st.com/resource/en/datasheet/spear600.pdf + +properties: + compatible: + enum: + - st,spear600-adc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + sampling-frequency: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2500000 + maximum: 20000000 + description: + Default sampling frequency of the ADC in Hz. + + vref-external: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1000 + maximum: 2800 + description: + External voltage reference in milli-volts. If omitted the internal voltage + reference will be used. + + average-samples: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + default: 0 + description: + Number of samples to generate an average value. If omitted, single data + conversion will be used. + +required: + - compatible + - reg + - interrupts + - sampling-frequency + +additionalProperties: false + +examples: + - | + adc@d8200000 { + compatible = "st,spear600-adc"; + reg = <0xd8200000 0x1000>; + interrupt-parent = <&vic1>; + interrupts = <6>; + sampling-frequency = <5000000>; + vref-external = <2500>; /* 2.5V VRef */ + }; diff --git a/Documentation/devicetree/bindings/iio/gyroscope/invensense,itg3200.yaml b/Documentation/devicetree/bindings/iio/gyroscope/invensense,itg3200.yaml new file mode 100644 index 000000000000..4d8abf8ac2c8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/gyroscope/invensense,itg3200.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/gyroscope/invensense,itg3200.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Invensense ITG-3200 Gyroscope + +maintainers: + - Jonathan Cameron <jic23@kernel.org> + +description: | + Triple-axis, digital output gyroscope with a three 16-bit analog-to-digital + converters (ADCs) for digitizing the gyro outputs, a user-selectable internal + low-pass filter bandwidth, and a Fast-Mode I2C. + +properties: + compatible: + const: invensense,itg3200 + + reg: + maxItems: 1 + + vdd-supply: true + + vlogic-supply: true + + interrupts: + maxItems: 1 + + mount-matrix: + description: an optional 3x3 mounting rotation matrix. + + clocks: + maxItems: 1 + + clock-names: + items: + - const: ext_clock + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + gyroscope@68 { + compatible = "invensense,itg3200"; + reg = <0x68>; + interrupt-parent = <&gpio2>; + interrupts = <24 IRQ_TYPE_EDGE_FALLING>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/proximity/nicera,d3323aa.yaml b/Documentation/devicetree/bindings/iio/proximity/nicera,d3323aa.yaml new file mode 100644 index 000000000000..65d9b44fcd5e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/nicera,d3323aa.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/proximity/nicera,d3323aa.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nicera D3-323-AA PIR sensor + +maintainers: + - Waqar Hameed <waqar.hameed@axis.com> + +description: | + PIR sensor for human detection. + Datasheet: https://www.endrich.com/Datenbl%C3%A4tter/Sensoren/D3-323-AA_e.pdf + +properties: + compatible: + const: nicera,d3323aa + + vdd-supply: + description: + Supply voltage (1.8 to 5.5 V). + + vout-clk-gpios: + maxItems: 1 + description: + GPIO for clock and detection. + After reset, the device signals with two falling edges on this pin that it + is ready for configuration (within 1.2 s). + During configuration, it is used as clock for data reading and writing (on + data-gpios). + After all this, when device is in operational mode, it signals on this pin + for any detections. + + data-gpios: + maxItems: 1 + description: + GPIO for data reading and writing. This is denoted "DO (SI)" in datasheet. + During configuration, this pin is used for writing and reading + configuration data (together with vout-clk-gpios as clock). + After this, during operational mode, the device will output serial data on + this GPIO. + +required: + - compatible + - vdd-supply + - vout-clk-gpios + - data-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + proximity { + compatible = "nicera,d3323aa"; + vdd-supply = <®ulator_3v3>; + vout-clk-gpios = <&gpio 78 GPIO_ACTIVE_HIGH>; + data-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>; + }; +... diff --git a/Documentation/devicetree/bindings/interconnect/qcom,milos-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,milos-rpmh.yaml new file mode 100644 index 000000000000..00b7a4108d45 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,milos-rpmh.yaml @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,milos-rpmh.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm RPMh Network-On-Chip Interconnect on Milos SoC + +maintainers: + - Luca Weiss <luca.weiss@fairphone.com> + +description: | + RPMh interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + + See also: include/dt-bindings/interconnect/qcom,milos-rpmh.h + +properties: + compatible: + enum: + - qcom,milos-aggre1-noc + - qcom,milos-aggre2-noc + - qcom,milos-clk-virt + - qcom,milos-cnoc-cfg + - qcom,milos-cnoc-main + - qcom,milos-gem-noc + - qcom,milos-lpass-ag-noc + - qcom,milos-mc-virt + - qcom,milos-mmss-noc + - qcom,milos-nsp-noc + - qcom,milos-pcie-anoc + - qcom,milos-system-noc + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + +required: + - compatible + +allOf: + - $ref: qcom,rpmh-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-clk-virt + - qcom,milos-mc-virt + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-pcie-anoc + then: + properties: + clocks: + items: + - description: aggre-NOC PCIe AXI clock + - description: cfg-NOC PCIe a-NOC AHB clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-aggre1-noc + then: + properties: + clocks: + items: + - description: aggre USB3 PRIM AXI clock + - description: aggre UFS PHY AXI clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-aggre2-noc + then: + properties: + clocks: + items: + - description: RPMH CC IPA clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,milos-aggre1-noc + - qcom,milos-aggre2-noc + - qcom,milos-pcie-anoc + then: + required: + - clocks + else: + properties: + clocks: false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,milos-gcc.h> + + interconnect-0 { + compatible = "qcom,milos-clk-virt"; + #interconnect-cells = <2>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + interconnect@16e0000 { + compatible = "qcom,milos-aggre1-noc"; + reg = <0x016e0000 0x16400>; + #interconnect-cells = <2>; + clocks = <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 83bcf0575cd3..256de140c03d 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -76,6 +76,8 @@ properties: minItems: 1 maxItems: 2 + nonposted-mmio: true + required: - compatible - interconnects diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index cd4bb912e0dc..ab5a921c3495 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -36,6 +36,11 @@ properties: - qcom,sm8350-epss-l3 - qcom,sm8650-epss-l3 - const: qcom,epss-l3 + - items: + - enum: + - qcom,qcs8300-epss-l3 + - const: qcom,sa8775p-epss-l3 + - const: qcom,epss-l3 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml index 2e0c0bc7a376..db19fd5c5708 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sa8775p-rpmh.yaml @@ -13,7 +13,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sa8775p.h + See also: include/dt-bindings/interconnect/qcom,sa8775p.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml index 4647dac740e9..f5d3d0c5df73 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sar2130p-rpmh.yaml @@ -18,7 +18,7 @@ description: | least one RPMh device child node pertaining to their RSC and each provider can map to multiple RPMh resources. - See also:: include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml index 78210791496f..81c3dff53992 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc7280-rpmh.yaml @@ -14,7 +14,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sc7280.h + See also: include/dt-bindings/interconnect/qcom,sc7280.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml index 100c68636909..2a5a7594bafd 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc8280xp-rpmh.yaml @@ -14,7 +14,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sc8280xp.h + See also: include/dt-bindings/interconnect/qcom,sc8280xp.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml index b565d1a382f6..978930324bbf 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm7150-rpmh.yaml @@ -13,7 +13,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h allOf: - $ref: qcom,rpmh-common.yaml# diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml index 300640a533dd..6a46dc7d473e 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8450-rpmh.yaml @@ -14,7 +14,7 @@ description: | RPMh interconnect providers support system bandwidth requirements through RPMh hardware accelerators known as Bus Clock Manager (BCM). - See also:: include/dt-bindings/interconnect/qcom,sm8450.h + See also: include/dt-bindings/interconnect/qcom,sm8450.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml index 716bd21f6041..5325ebe23c77 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8550-rpmh.yaml @@ -18,7 +18,7 @@ description: | least one RPMh device child node pertaining to their RSC and each provider can map to multiple RPMh resources. - See also:: include/dt-bindings/interconnect/qcom,sm8550-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm8550-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml index f9322de7cd61..199fe7b232af 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8650-rpmh.yaml @@ -18,7 +18,7 @@ description: | least one RPMh device child node pertaining to their RSC and each provider can map to multiple RPMh resources. - See also:: include/dt-bindings/interconnect/qcom,sm8650-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm8650-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml index a816acc301e1..366f40d980c2 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,sm8750-rpmh.yaml @@ -18,7 +18,7 @@ description: | least one RPMh device child node pertaining to their RSC and each provider can map to multiple RPMh resources. - See also:: include/dt-bindings/interconnect/qcom,sm8750-rpmh.h + See also: include/dt-bindings/interconnect/qcom,sm8750-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml index 08b0210e0e59..0840b0ec6e27 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,x1e80100-rpmh.yaml @@ -18,7 +18,7 @@ description: | least one RPMh device child node pertaining to their RSC and each provider can map to multiple RPMh resources. - See also:: include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h + See also: include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h properties: compatible: diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml index 4424c3c5e75c..f67470b8a2ed 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml @@ -27,6 +27,7 @@ properties: - enum: - allwinner,sun50i-a100-sid - allwinner,sun50i-h616-sid + - allwinner,sun55i-a523-sid - const: allwinner,sun50i-a64-sid - const: allwinner,sun50i-h5-sid - const: allwinner,sun50i-h6-sid diff --git a/Documentation/devicetree/bindings/nvmem/fsl,vf610-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/fsl,vf610-ocotp.yaml new file mode 100644 index 000000000000..5aef86a752a6 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/fsl,vf610-ocotp.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/fsl,vf610-ocotp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: On-Chip OTP Memory for Freescale Vybrid + +maintainers: + - Frank Li <Frank.Li@nxp.com> + +allOf: + - $ref: nvmem.yaml# + - $ref: nvmem-deprecated-cells.yaml + +properties: + compatible: + items: + - enum: + - fsl,vf610-ocotp + - const: syscon + + reg: + maxItems: 1 + + clocks: + items: + - description: ipg clock we associate with the OCOTP peripheral + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/vf610-clock.h> + + ocotp@400a5000 { + compatible = "fsl,vf610-ocotp", "syscon"; + reg = <0x400a5000 0xcf0>; + #address-cells = <1>; + #size-cells = <1>; + clocks = <&clks VF610_CLK_OCOTP>; + }; diff --git a/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml b/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml index 9bd34bd5af30..b01567f99284 100644 --- a/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml +++ b/Documentation/devicetree/bindings/nvmem/layouts/fixed-layout.yaml @@ -27,7 +27,7 @@ properties: const: 1 patternProperties: - "@[a-f0-9]+$": + "@[a-f0-9]+(,[0-7])?$": type: object $ref: fixed-cell.yaml unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt b/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt deleted file mode 100644 index 809df68f6e14..000000000000 --- a/Documentation/devicetree/bindings/nvmem/lpc1857-eeprom.txt +++ /dev/null @@ -1,28 +0,0 @@ -* NXP LPC18xx EEPROM memory NVMEM driver - -Required properties: - - compatible: Should be "nxp,lpc1857-eeprom" - - reg: Must contain an entry with the physical base address and length - for each entry in reg-names. - - reg-names: Must include the following entries. - - reg: EEPROM registers. - - mem: EEPROM address space. - - clocks: Must contain an entry for each entry in clock-names. - - clock-names: Must include the following entries. - - eeprom: EEPROM operating clock. - - resets: Should contain a reference to the reset controller asserting - the EEPROM in reset. - - interrupts: Should contain EEPROM interrupt. - -Example: - - eeprom: eeprom@4000e000 { - compatible = "nxp,lpc1857-eeprom"; - reg = <0x4000e000 0x1000>, - <0x20040000 0x4000>; - reg-names = "reg", "mem"; - clocks = <&ccu1 CLK_CPU_EEPROM>; - clock-names = "eeprom"; - resets = <&rgu 27>; - interrupts = <4>; - }; diff --git a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml index 32b8c1eb4e80..4dc0d42df3e6 100644 --- a/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml +++ b/Documentation/devicetree/bindings/nvmem/mediatek,efuse.yaml @@ -25,6 +25,21 @@ properties: compatible: oneOf: - items: + - const: mediatek,mt8188-efuse + - const: mediatek,mt8186-efuse + - const: mediatek,mt8186-efuse + + - items: + - enum: + - mediatek,mt8186-efuse + - mediatek,mt8188-efuse + - const: mediatek,efuse + deprecated: true + description: Some compatibles also imply a decoding scheme for the + "gpu-speedbin" cell, and thus are not backward compatible to the + generic "mediatek,efuse" compatible. + + - items: - enum: - mediatek,mt7622-efuse - mediatek,mt7623-efuse @@ -33,8 +48,6 @@ properties: - mediatek,mt7988-efuse - mediatek,mt8173-efuse - mediatek,mt8183-efuse - - mediatek,mt8186-efuse - - mediatek,mt8188-efuse - mediatek,mt8192-efuse - mediatek,mt8195-efuse - mediatek,mt8516-efuse diff --git a/Documentation/devicetree/bindings/nvmem/nxp,lpc1857-eeprom.yaml b/Documentation/devicetree/bindings/nvmem/nxp,lpc1857-eeprom.yaml new file mode 100644 index 000000000000..24c71252846f --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/nxp,lpc1857-eeprom.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/nxp,lpc1857-eeprom.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC18xx EEPROM memory + +maintainers: + - Frank Li <Frank.Li@nxp.com> + +properties: + compatible: + const: nxp,lpc1857-eeprom + + reg: + maxItems: 2 + + reg-names: + items: + - const: reg + - const: mem + + clocks: + maxItems: 1 + + clock-names: + items: + - const: eeprom + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - interrupts + - resets + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/lpc18xx-ccu.h> + + eeprom@4000e000 { + compatible = "nxp,lpc1857-eeprom"; + reg = <0x4000e000 0x1000>, + <0x20040000 0x4000>; + reg-names = "reg", "mem"; + clocks = <&ccu1 CLK_CPU_EEPROM>; + clock-names = "eeprom"; + resets = <&rgu 27>; + interrupts = <4>; + }; diff --git a/Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt b/Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt deleted file mode 100644 index 72ba628f6d0b..000000000000 --- a/Documentation/devicetree/bindings/nvmem/vf610-ocotp.txt +++ /dev/null @@ -1,19 +0,0 @@ -On-Chip OTP Memory for Freescale Vybrid - -Required Properties: - compatible: - - "fsl,vf610-ocotp", "syscon" for VF5xx/VF6xx - #address-cells : Should be 1 - #size-cells : Should be 1 - reg : Address and length of OTP controller and fuse map registers - clocks : ipg clock we associate with the OCOTP peripheral - -Example for Vybrid VF5xx/VF6xx: - - ocotp: ocotp@400a5000 { - compatible = "fsl,vf610-ocotp", "syscon"; - #address-cells = <1>; - #size-cells = <1>; - reg = <0x400a5000 0xCF0>; - clocks = <&clks VF610_CLK_OCOTP>; - }; diff --git a/Documentation/devicetree/bindings/staging/iio/adc/spear-adc.txt b/Documentation/devicetree/bindings/staging/iio/adc/spear-adc.txt deleted file mode 100644 index 88bc94fe1f6d..000000000000 --- a/Documentation/devicetree/bindings/staging/iio/adc/spear-adc.txt +++ /dev/null @@ -1,24 +0,0 @@ -* ST SPEAr ADC device driver - -Required properties: -- compatible: Should be "st,spear600-adc" -- reg: Address and length of the register set for the device -- interrupts: Should contain the ADC interrupt -- sampling-frequency: Default sampling frequency - -Optional properties: -- vref-external: External voltage reference in milli-volts. If omitted - the internal voltage reference will be used. -- average-samples: Number of samples to generate an average value. If - omitted, single data conversion will be used. - -Examples: - - adc: adc@d8200000 { - compatible = "st,spear600-adc"; - reg = <0xd8200000 0x1000>; - interrupt-parent = <&vic1>; - interrupts = <6>; - sampling-frequency = <5000000>; - vref-external = <2500>; /* 2.5V VRef */ - }; diff --git a/Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml b/Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml new file mode 100644 index 000000000000..1331d153ee82 --- /dev/null +++ b/Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/trigger-source/gpio-trigger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic trigger source using GPIO + +description: A GPIO used as a trigger source. + +maintainers: + - Jonathan Santos <Jonathan.Santos@analog.com> + +properties: + compatible: + const: gpio-trigger + + '#trigger-source-cells': + const: 0 + + gpios: + maxItems: 1 + description: GPIO to be used as a trigger source. + +required: + - compatible + - '#trigger-source-cells' + - gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + trigger { + compatible = "gpio-trigger"; + #trigger-source-cells = <0>; + gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index e5e5bd34136a..be6abf8fad36 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1069,6 +1069,8 @@ patternProperties: description: Next Thing Co. "^ni,.*": description: National Instruments + "^nicera,.*": + description: Nippon Ceramic Co., Ltd. "^nintendo,.*": description: Nintendo "^nlt,.*": diff --git a/Documentation/iio/adxl313.rst b/Documentation/iio/adxl313.rst new file mode 100644 index 000000000000..966e72c0109a --- /dev/null +++ b/Documentation/iio/adxl313.rst @@ -0,0 +1,293 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============== +ADXL313 driver +=============== + +This driver supports Analog Device's ADXL313 on SPI/I2C bus. + +1. Supported devices +==================== + +* `ADXL313 <https://www.analog.com/ADXL313>`_ + +The ADXL313is a low noise density, low power, 3-axis accelerometer with +selectable measurement ranges. The ADXL313 supports the ±0.5 g, ±1 g, ±2 g and +±4 g ranges. + +2. Device attributes +==================== + +Accelerometer measurements are always provided. + +Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``, +where X is the IIO index of the device. Under these folders reside a set of +device files, depending on the characteristics and features of the hardware +device in questions. These files are consistently generalized and documented in +the IIO ABI documentation. + +The following tables show the adxl313 related device files, found in the +specific device folder path ``/sys/bus/iio/devices/iio:deviceX``. + ++---------------------------------------------------+----------------------------------------------------------+ +| 3-Axis Accelerometer related device files | Description | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_scale | Scale for the accelerometer channels. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x_raw | Raw X-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_y_calibbias | y-axis acceleration offset correction | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_y_raw | Raw Y-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_z_raw | Raw Z-axis accelerometer channel value. | ++---------------------------------------------------+----------------------------------------------------------+ + ++---------------------------------------+----------------------------------------------+ +| Miscellaneous device files | Description | ++---------------------------------------+----------------------------------------------+ +| name | Name of the IIO device. | ++---------------------------------------+----------------------------------------------+ +| in_accel_sampling_frequency | Currently selected sample rate. | ++---------------------------------------+----------------------------------------------+ +| in_accel_sampling_frequency_available | Available sampling frequency configurations. | ++---------------------------------------+----------------------------------------------+ + +The iio event related settings, found in ``/sys/bus/iio/devices/iio:deviceX/events``. + ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_adaptive_falling_period | AC coupled inactivity time. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_adaptive_rising_value | AC coupled activity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_falling_period | Inactivity time. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_falling_value | Inactivity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_mag_rising_value | Activity threshold. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\&y\&z_mag_adaptive_falling_en | Enable or disable AC coupled inactivity events. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\|y\|z_mag_adaptive_rising_en | Enable or disable AC coupled activity events. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\&y\&z_mag_falling_en | Enable or disable inactivity events. | ++---------------------------------------------------+----------------------------------------------------------+ +| in_accel_x\|y\|z_mag_rising_en | Enable or disable activity events. | ++---------------------------------------------------+----------------------------------------------------------+ + +The default coupling is DC coupled events. In this case the threshold will +be in place as such, where for the AC coupled case an adaptive threshold +(described in the datasheet) will be applied by the sensor. In general activity, +i.e. ``ACTIVITY`` or ``ACTIVITY_AC`` and inactivity i.e. ``INACTIVITY`` or +``INACTIVITY_AC``, will be linked with auto-sleep enabled when both are enabled. +This means in particular ``ACTIVITY`` can also be linked to ``INACTIVITY_AC`` +and vice versa, without problem. + +Note here, that ``ACTIVITY`` and ``ACTIVITY_AC`` are mutually exclusive. This +means, that the most recent configuration will be set. For instance, if +``ACTIVITY`` is enabled, and ``ACTIVITY_AC`` will be enabled, the sensor driver +will have ``ACTIVITY`` disabled, but ``ACTIVITY_AC`` enabled. The same is valid +for inactivity. In case of turning off an event, it has to match to what is +actually enabled, i.e. enabling ``ACTIVITY_AC`` and then disabling ``ACTIVITY`` +is simply ignored as it is already disabled. Or, as if it was any other not +enabled event, too. + +Channels processed values +------------------------- + +A channel value can be read from its _raw attribute. The value returned is the +raw value as reported by the devices. To get the processed value of the channel, +apply the following formula: + +.. code-block:: + + processed value = (_raw + _offset) * _scale + +Where _offset and _scale are device attributes. If no _offset attribute is +present, simply assume its value is 0. + +The ADXL313 driver offers data for a single types of channels, the table below +shows the measurement units for the processed value, which are defined by the +IIO framework: + ++-------------------------------------+---------------------------+ +| Channel type | Measurement unit | ++-------------------------------------+---------------------------+ +| Acceleration on X, Y, and Z axis | Meters per Second squared | ++-------------------------------------+---------------------------+ + +Usage examples +-------------- + +Show device name: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat name + adxl313 + +Show accelerometer channels value: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw + 2 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw + -57 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw + 2 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale + 0.009576806 + +The accelerometer values will be: + +- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.0191536 m/s^2 +- Y-axis acceleration = in_accel_y_raw * in_accel_scale = -0.5458779 m/s^2 +- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 0.0191536 m/s^2 + +Set calibration offset for accelerometer channels. Note, that the calibration +will be rounded according to the graduation of LSB units: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias + 0 + + root:/sys/bus/iio/devices/iio:device0> echo 50 > in_accel_x_calibbias + root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias + 48 + +Set sampling frequency: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency + 100.000000 + root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency_available + 6.250000 12.500000 25.000000 50.000000 100.000000 200.000000 400.000000 800.000000 1600.000000 3200.000000 + + root:/sys/bus/iio/devices/iio:device0> echo 400 > in_accel_sampling_frequency + root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency + 400.000000 + +3. Device buffers and triggers +============================== + +This driver supports IIO buffers. + +All devices support retrieving the raw acceleration measurements using buffers. + +Usage examples +-------------- + +Select channels for buffer read: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en + root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_en + +Set the number of samples to be stored in the buffer: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length + +Enable buffer readings: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable + +Obtain buffered data: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0 + ... + 000000d0 01 fc 31 00 c7 ff 03 fc 31 00 c7 ff 04 fc 33 00 |..1.....1.....3.| + 000000e0 c8 ff 03 fc 32 00 c5 ff ff fc 32 00 c7 ff 0a fc |....2.....2.....| + 000000f0 30 00 c8 ff 06 fc 33 00 c7 ff 01 fc 2f 00 c8 ff |0.....3...../...| + 00000100 02 fc 32 00 c6 ff 04 fc 33 00 c8 ff 05 fc 33 00 |..2.....3.....3.| + 00000110 ca ff 02 fc 31 00 c7 ff 02 fc 30 00 c9 ff 09 fc |....1.....0.....| + 00000120 35 00 c9 ff 08 fc 35 00 c8 ff 02 fc 31 00 c5 ff |5.....5.....1...| + 00000130 03 fc 32 00 c7 ff 04 fc 32 00 c7 ff 02 fc 31 00 |..2.....2.....1.| + 00000140 c7 ff 08 fc 30 00 c7 ff 02 fc 32 00 c5 ff ff fc |....0.....2.....| + 00000150 31 00 c5 ff 04 fc 31 00 c8 ff 03 fc 32 00 c8 ff |1.....1.....2...| + 00000160 01 fc 31 00 c7 ff 05 fc 31 00 c3 ff 04 fc 31 00 |..1.....1.....1.| + 00000170 c5 ff 04 fc 30 00 c7 ff 03 fc 31 00 c9 ff 03 fc |....0.....1.....| + ... + +Enabling activity detection: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1.28125 > ./events/in_accel_mag_rising_value + root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\|y\|z_mag_rising_en + + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + Found IIO device with name adxl313 with device number 0 + <only while moving the sensor> + Event: time: 1748795762298351281, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising + Event: time: 1748795762302653704, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising + Event: time: 1748795762304340726, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising + ... + +Disabling activity detection: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 0 > ./events/in_accel_x\|y\|z_mag_rising_en + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + <nothing> + +Enabling inactivity detection: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1.234375 > ./events/in_accel_mag_falling_value + root:/sys/bus/iio/devices/iio:device0> echo 5 > ./events/in_accel_mag_falling_period + root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\&y\&z_mag_falling_en + + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + Found IIO device with name adxl313 with device number 0 + Event: time: 1748796324115962975, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + Event: time: 1748796329329981772, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + Event: time: 1748796334543399706, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + ... + <every 5s now indicates inactivity> + +Now, enabling activity, e.g. the AC coupled counter-part ``ACTIVITY_AC`` + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 1.28125 > ./events/in_accel_mag_rising_value + root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\|y\|z_mag_rising_en + + root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313 + Found IIO device with name adxl313 with device number 0 + <some activity with the sensor> + Event: time: 1748796880354686777, type: accel(x|y|z), channel: 0, evtype: mag_adaptive, direction: rising + <5s of inactivity, then> + Event: time: 1748796885543252017, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + <some other activity detected by accelerating the sensor> + Event: time: 1748796887756634678, type: accel(x|y|z), channel: 0, evtype: mag_adaptive, direction: rising + <again, 5s of inactivity> + Event: time: 1748796892964368352, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling + <stays like this until next activity in auto-sleep> + +Note, when AC coupling is in place, the event type will be of ``mag_adaptive``. +AC- or DC-coupled (the default) events are used similarly. + +4. IIO Interfacing Tools +======================== + +See Documentation/iio/iio_tools.rst for the description of the available IIO +interfacing tools. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index 2d6afc5a8ed5..c106402a91f7 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers adis16475 adis16480 adis16550 + adxl313 adxl380 bno055 ep93xx_adc diff --git a/MAINTAINERS b/MAINTAINERS index 7a4e63bacaa4..cc774d6699b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1385,6 +1385,14 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml F: Documentation/iio/ad4030.rst F: drivers/iio/adc/ad4030.c +ANALOG DEVICES INC AD4080 DRIVER +M: Antoniu Miclaus <antoniu.miclaus@analog.com> +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +F: drivers/iio/adc/ad4080.c + ANALOG DEVICES INC AD4130 DRIVER M: Cosmin Tanislav <cosmin.tanislav@analog.com> L: linux-iio@vger.kernel.org @@ -1394,6 +1402,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml F: drivers/iio/adc/ad4130.c +ANALOG DEVICES INC AD4170-4 DRIVER +M: Marcelo Schmitt <marcelo.schmitt@analog.com> +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml +F: drivers/iio/adc/ad4170-4.c + ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich <michael.hennerich@analog.com> M: Nuno Sá <nuno.sa@analog.com> @@ -1492,6 +1508,7 @@ S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml F: drivers/iio/adc/ad7768-1.c +F: include/dt-bindings/iio/adc/adi,ad7768-1.h ANALOG DEVICES INC AD7780 DRIVER M: Michael Hennerich <Michael.Hennerich@analog.com> @@ -9443,7 +9460,7 @@ K: \bunsafe_memcpy\b K: \b__NO_FORTIFY\b FPGA DFL DRIVERS -M: Wu Hao <hao.wu@intel.com> +M: Xu Yilun <yilun.xu@intel.com> R: Tom Rix <trix@redhat.com> L: linux-fpga@vger.kernel.org S: Maintained @@ -9456,7 +9473,6 @@ F: include/uapi/linux/fpga-dfl.h FPGA MANAGER FRAMEWORK M: Moritz Fischer <mdf@kernel.org> -M: Wu Hao <hao.wu@intel.com> M: Xu Yilun <yilun.xu@intel.com> R: Tom Rix <trix@redhat.com> L: linux-fpga@vger.kernel.org @@ -23611,7 +23627,6 @@ STAGING - INDUSTRIAL IO M: Jonathan Cameron <jic23@kernel.org> L: linux-iio@vger.kernel.org S: Odd Fixes -F: Documentation/devicetree/bindings/staging/iio/ F: drivers/staging/iio/ STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec) @@ -25278,9 +25293,10 @@ M: David Lechner <dlechner@baylibre.com> S: Maintained F: Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml -TRIGGER SOURCE - PWM +TRIGGER SOURCE M: David Lechner <dlechner@baylibre.com> S: Maintained +F: Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml F: Documentation/devicetree/bindings/trigger-source/pwm-trigger.yaml TRUSTED SECURITY MODULE (TSM) INFRASTRUCTURE diff --git a/drivers/Kconfig b/drivers/Kconfig index 7c556c5ac4fd..5b85e21b55d9 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -10,6 +10,12 @@ source "drivers/cxl/Kconfig" source "drivers/pcmcia/Kconfig" source "drivers/rapidio/Kconfig" +config PC104 + bool "PC/104 support" if EXPERT + help + Expose PC/104 form factor device drivers and options available for + selection and configuration. Enable this option if your target + machine has a PC/104 bus. source "drivers/base/Kconfig" diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 07aa8ae0a058..5b3b8041f827 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -37,14 +37,15 @@ config ANDROID_BINDER_DEVICES created. Each binder device has its own context manager, and is therefore logically separated from the other devices. -config ANDROID_BINDER_IPC_SELFTEST - bool "Android Binder IPC Driver Selftest" - depends on ANDROID_BINDER_IPC +config ANDROID_BINDER_ALLOC_KUNIT_TEST + tristate "KUnit Tests for Android Binder Alloc" if !KUNIT_ALL_TESTS + depends on ANDROID_BINDER_IPC && KUNIT + default KUNIT_ALL_TESTS help - This feature allows binder selftest to run. + This feature builds the binder alloc KUnit tests. - Binder selftest checks the allocation and free of binder buffers - exhaustively with combinations of various buffer sizes and - alignments. + Each test case runs using a pared-down binder_alloc struct and + test-specific freelist, which allows this KUnit module to be loaded + for testing without interfering with a running system. endmenu diff --git a/drivers/android/Makefile b/drivers/android/Makefile index c9d3d0c99c25..c5d47be0276c 100644 --- a/drivers/android/Makefile +++ b/drivers/android/Makefile @@ -3,4 +3,4 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o -obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o +obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += tests/ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 1ba2192ae667..312b462e349d 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -68,6 +68,8 @@ #include <linux/sizes.h> #include <linux/ktime.h> +#include <kunit/visibility.h> + #include <uapi/linux/android/binder.h> #include <linux/cacheflush.h> @@ -1585,11 +1587,10 @@ static struct binder_thread *binder_get_txn_from( { struct binder_thread *from; - spin_lock(&t->lock); + guard(spinlock)(&t->lock); from = t->from; if (from) atomic_inc(&from->tmp_ref); - spin_unlock(&t->lock); return from; } @@ -3144,10 +3145,8 @@ static void binder_transaction(struct binder_proc *proc, } if (!target_node) { binder_txn_error("%d:%d cannot find target node\n", - thread->pid, proc->pid); - /* - * return_error is set above - */ + proc->pid, thread->pid); + /* return_error is set above */ return_error_param = -EINVAL; return_error_line = __LINE__; goto err_dead_binder; @@ -5437,32 +5436,28 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp, struct binder_node *new_node; kuid_t curr_euid = current_euid(); - mutex_lock(&context->context_mgr_node_lock); + guard(mutex)(&context->context_mgr_node_lock); if (context->binder_context_mgr_node) { pr_err("BINDER_SET_CONTEXT_MGR already set\n"); - ret = -EBUSY; - goto out; + return -EBUSY; } ret = security_binder_set_context_mgr(proc->cred); if (ret < 0) - goto out; + return ret; if (uid_valid(context->binder_context_mgr_uid)) { if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) { pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n", from_kuid(&init_user_ns, curr_euid), from_kuid(&init_user_ns, context->binder_context_mgr_uid)); - ret = -EPERM; - goto out; + return -EPERM; } } else { context->binder_context_mgr_uid = curr_euid; } new_node = binder_new_node(proc, fbo); - if (!new_node) { - ret = -ENOMEM; - goto out; - } + if (!new_node) + return -ENOMEM; binder_node_lock(new_node); new_node->local_weak_refs++; new_node->local_strong_refs++; @@ -5471,8 +5466,6 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp, context->binder_context_mgr_node = new_node; binder_node_unlock(new_node); binder_put_node(new_node); -out: - mutex_unlock(&context->context_mgr_node_lock); return ret; } @@ -5708,11 +5701,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct binder_thread *thread; void __user *ubuf = (void __user *)arg; - /*pr_info("binder_ioctl: %d:%d %x %lx\n", - proc->pid, current->pid, cmd, arg);*/ - - binder_selftest_alloc(&proc->alloc); - trace_binder_ioctl(cmd, arg); ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); @@ -5948,10 +5936,11 @@ static void binder_vma_close(struct vm_area_struct *vma) binder_alloc_vma_close(&proc->alloc); } -static vm_fault_t binder_vm_fault(struct vm_fault *vmf) +VISIBLE_IF_KUNIT vm_fault_t binder_vm_fault(struct vm_fault *vmf) { return VM_FAULT_SIGBUS; } +EXPORT_SYMBOL_IF_KUNIT(binder_vm_fault); static const struct vm_operations_struct binder_vm_ops = { .open = binder_vma_open, @@ -6314,14 +6303,13 @@ static DECLARE_WORK(binder_deferred_work, binder_deferred_func); static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) { - mutex_lock(&binder_deferred_lock); + guard(mutex)(&binder_deferred_lock); proc->deferred_work |= defer; if (hlist_unhashed(&proc->deferred_work_node)) { hlist_add_head(&proc->deferred_work_node, &binder_deferred_list); schedule_work(&binder_deferred_work); } - mutex_unlock(&binder_deferred_lock); } static void print_binder_transaction_ilocked(struct seq_file *m, @@ -6863,14 +6851,13 @@ static int proc_show(struct seq_file *m, void *unused) struct binder_proc *itr; int pid = (unsigned long)m->private; - mutex_lock(&binder_procs_lock); + guard(mutex)(&binder_procs_lock); hlist_for_each_entry(itr, &binder_procs, proc_node) { if (itr->pid == pid) { seq_puts(m, "binder proc state:\n"); print_binder_proc(m, itr, true, false); } } - mutex_unlock(&binder_procs_lock); return 0; } @@ -6988,16 +6975,14 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = { void binder_add_device(struct binder_device *device) { - spin_lock(&binder_devices_lock); + guard(spinlock)(&binder_devices_lock); hlist_add_head(&device->hlist, &binder_devices); - spin_unlock(&binder_devices_lock); } void binder_remove_device(struct binder_device *device) { - spin_lock(&binder_devices_lock); + guard(spinlock)(&binder_devices_lock); hlist_del_init(&device->hlist); - spin_unlock(&binder_devices_lock); } static int __init init_binder_device(const char *name) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index fcfaf1b899c8..979c96b74cad 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -23,10 +23,11 @@ #include <linux/uaccess.h> #include <linux/highmem.h> #include <linux/sizes.h> +#include <kunit/visibility.h> #include "binder_alloc.h" #include "binder_trace.h" -struct list_lru binder_freelist; +static struct list_lru binder_freelist; static DEFINE_MUTEX(binder_alloc_mmap_lock); @@ -57,13 +58,14 @@ static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer) return list_entry(buffer->entry.prev, struct binder_buffer, entry); } -static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, - struct binder_buffer *buffer) +VISIBLE_IF_KUNIT size_t binder_alloc_buffer_size(struct binder_alloc *alloc, + struct binder_buffer *buffer) { if (list_is_last(&buffer->entry, &alloc->buffers)) return alloc->vm_start + alloc->buffer_size - buffer->user_data; return binder_buffer_next(buffer)->user_data - buffer->user_data; } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_buffer_size); static void binder_insert_free_buffer(struct binder_alloc *alloc, struct binder_buffer *new_buffer) @@ -167,12 +169,8 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked( struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, unsigned long user_ptr) { - struct binder_buffer *buffer; - - mutex_lock(&alloc->mutex); - buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); - mutex_unlock(&alloc->mutex); - return buffer; + guard(mutex)(&alloc->mutex); + return binder_alloc_prepare_to_free_locked(alloc, user_ptr); } static inline void @@ -210,7 +208,7 @@ static void binder_lru_freelist_add(struct binder_alloc *alloc, trace_binder_free_lru_start(alloc, index); - ret = list_lru_add(&binder_freelist, + ret = list_lru_add(alloc->freelist, page_to_lru(page), page_to_nid(page), NULL); @@ -409,7 +407,7 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc, if (page) { trace_binder_alloc_lru_start(alloc, index); - on_lru = list_lru_del(&binder_freelist, + on_lru = list_lru_del(alloc->freelist, page_to_lru(page), page_to_nid(page), NULL); @@ -699,6 +697,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, out: return buffer; } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_new_buf); static unsigned long buffer_start_page(struct binder_buffer *buffer) { @@ -877,6 +876,7 @@ void binder_alloc_free_buf(struct binder_alloc *alloc, binder_free_buf_locked(alloc, buffer); mutex_unlock(&alloc->mutex); } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_buf); /** * binder_alloc_mmap_handler() - map virtual address space for proc @@ -959,7 +959,7 @@ err_invalid_mm: failure_string, ret); return ret; } - +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_mmap_handler); void binder_alloc_deferred_release(struct binder_alloc *alloc) { @@ -1007,7 +1007,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) if (!page) continue; - on_lru = list_lru_del(&binder_freelist, + on_lru = list_lru_del(alloc->freelist, page_to_lru(page), page_to_nid(page), NULL); @@ -1028,6 +1028,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc) "%s: %d buffers %d, pages %d\n", __func__, alloc->pid, buffers, page_count); } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_deferred_release); /** * binder_alloc_print_allocated() - print buffer info @@ -1043,7 +1044,7 @@ void binder_alloc_print_allocated(struct seq_file *m, struct binder_buffer *buffer; struct rb_node *n; - mutex_lock(&alloc->mutex); + guard(mutex)(&alloc->mutex); for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) { buffer = rb_entry(n, struct binder_buffer, rb_node); seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n", @@ -1053,7 +1054,6 @@ void binder_alloc_print_allocated(struct seq_file *m, buffer->extra_buffers_size, buffer->transaction ? "active" : "delivered"); } - mutex_unlock(&alloc->mutex); } /** @@ -1102,10 +1102,9 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc) struct rb_node *n; int count = 0; - mutex_lock(&alloc->mutex); + guard(mutex)(&alloc->mutex); for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) count++; - mutex_unlock(&alloc->mutex); return count; } @@ -1122,6 +1121,7 @@ void binder_alloc_vma_close(struct binder_alloc *alloc) { binder_alloc_set_mapped(alloc, false); } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_vma_close); /** * binder_alloc_free_page() - shrinker callback to free pages @@ -1213,6 +1213,7 @@ err_mmap_read_lock_failed: err_mmget: return LRU_SKIP; } +EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_page); static unsigned long binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) @@ -1229,6 +1230,18 @@ binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) static struct shrinker *binder_shrinker; +VISIBLE_IF_KUNIT void __binder_alloc_init(struct binder_alloc *alloc, + struct list_lru *freelist) +{ + alloc->pid = current->group_leader->pid; + alloc->mm = current->mm; + mmgrab(alloc->mm); + mutex_init(&alloc->mutex); + INIT_LIST_HEAD(&alloc->buffers); + alloc->freelist = freelist; +} +EXPORT_SYMBOL_IF_KUNIT(__binder_alloc_init); + /** * binder_alloc_init() - called by binder_open() for per-proc initialization * @alloc: binder_alloc for this proc @@ -1238,11 +1251,7 @@ static struct shrinker *binder_shrinker; */ void binder_alloc_init(struct binder_alloc *alloc) { - alloc->pid = current->group_leader->pid; - alloc->mm = current->mm; - mmgrab(alloc->mm); - mutex_init(&alloc->mutex); - INIT_LIST_HEAD(&alloc->buffers); + __binder_alloc_init(alloc, &binder_freelist); } int binder_alloc_shrinker_init(void) diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index feecd7414241..d6f1f6f2d00e 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -15,7 +15,6 @@ #include <linux/list_lru.h> #include <uapi/linux/android/binder.h> -extern struct list_lru binder_freelist; struct binder_transaction; /** @@ -91,6 +90,7 @@ static inline struct list_head *page_to_lru(struct page *p) * @free_async_space: VA space available for async buffers. This is * initialized at mmap time to 1/2 the full VA space * @pages: array of struct page * + * @freelist: lru list to use for free pages (invariant after init) * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) * @pages_high: high watermark of offset in @pages @@ -113,6 +113,7 @@ struct binder_alloc { struct rb_root allocated_buffers; size_t free_async_space; struct page **pages; + struct list_lru *freelist; size_t buffer_size; int pid; size_t pages_high; @@ -120,11 +121,6 @@ struct binder_alloc { bool oneway_spam_detected; }; -#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST -void binder_selftest_alloc(struct binder_alloc *alloc); -#else -static inline void binder_selftest_alloc(struct binder_alloc *alloc) {} -#endif enum lru_status binder_alloc_free_page(struct list_head *item, struct list_lru_one *lru, void *cb_arg); @@ -160,12 +156,8 @@ void binder_alloc_print_pages(struct seq_file *m, static inline size_t binder_alloc_get_free_async_space(struct binder_alloc *alloc) { - size_t free_async_space; - - mutex_lock(&alloc->mutex); - free_async_space = alloc->free_async_space; - mutex_unlock(&alloc->mutex); - return free_async_space; + guard(mutex)(&alloc->mutex); + return alloc->free_async_space; } unsigned long @@ -187,5 +179,11 @@ int binder_alloc_copy_from_buffer(struct binder_alloc *alloc, binder_size_t buffer_offset, size_t bytes); +#if IS_ENABLED(CONFIG_KUNIT) +void __binder_alloc_init(struct binder_alloc *alloc, struct list_lru *freelist); +size_t binder_alloc_buffer_size(struct binder_alloc *alloc, + struct binder_buffer *buffer); +#endif + #endif /* _LINUX_BINDER_ALLOC_H */ diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c deleted file mode 100644 index c88735c54848..000000000000 --- a/drivers/android/binder_alloc_selftest.c +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* binder_alloc_selftest.c - * - * Android IPC Subsystem - * - * Copyright (C) 2017 Google, Inc. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/mm_types.h> -#include <linux/err.h> -#include "binder_alloc.h" - -#define BUFFER_NUM 5 -#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) - -static bool binder_selftest_run = true; -static int binder_selftest_failures; -static DEFINE_MUTEX(binder_selftest_lock); - -/** - * enum buf_end_align_type - Page alignment of a buffer - * end with regard to the end of the previous buffer. - * - * In the pictures below, buf2 refers to the buffer we - * are aligning. buf1 refers to previous buffer by addr. - * Symbol [ means the start of a buffer, ] means the end - * of a buffer, and | means page boundaries. - */ -enum buf_end_align_type { - /** - * @SAME_PAGE_UNALIGNED: The end of this buffer is on - * the same page as the end of the previous buffer and - * is not page aligned. Examples: - * buf1 ][ buf2 ][ ... - * buf1 ]|[ buf2 ][ ... - */ - SAME_PAGE_UNALIGNED = 0, - /** - * @SAME_PAGE_ALIGNED: When the end of the previous buffer - * is not page aligned, the end of this buffer is on the - * same page as the end of the previous buffer and is page - * aligned. When the previous buffer is page aligned, the - * end of this buffer is aligned to the next page boundary. - * Examples: - * buf1 ][ buf2 ]| ... - * buf1 ]|[ buf2 ]| ... - */ - SAME_PAGE_ALIGNED, - /** - * @NEXT_PAGE_UNALIGNED: The end of this buffer is on - * the page next to the end of the previous buffer and - * is not page aligned. Examples: - * buf1 ][ buf2 | buf2 ][ ... - * buf1 ]|[ buf2 | buf2 ][ ... - */ - NEXT_PAGE_UNALIGNED, - /** - * @NEXT_PAGE_ALIGNED: The end of this buffer is on - * the page next to the end of the previous buffer and - * is page aligned. Examples: - * buf1 ][ buf2 | buf2 ]| ... - * buf1 ]|[ buf2 | buf2 ]| ... - */ - NEXT_PAGE_ALIGNED, - /** - * @NEXT_NEXT_UNALIGNED: The end of this buffer is on - * the page that follows the page after the end of the - * previous buffer and is not page aligned. Examples: - * buf1 ][ buf2 | buf2 | buf2 ][ ... - * buf1 ]|[ buf2 | buf2 | buf2 ][ ... - */ - NEXT_NEXT_UNALIGNED, - /** - * @LOOP_END: The number of enum values in &buf_end_align_type. - * It is used for controlling loop termination. - */ - LOOP_END, -}; - -static void pr_err_size_seq(size_t *sizes, int *seq) -{ - int i; - - pr_err("alloc sizes: "); - for (i = 0; i < BUFFER_NUM; i++) - pr_cont("[%zu]", sizes[i]); - pr_cont("\n"); - pr_err("free seq: "); - for (i = 0; i < BUFFER_NUM; i++) - pr_cont("[%d]", seq[i]); - pr_cont("\n"); -} - -static bool check_buffer_pages_allocated(struct binder_alloc *alloc, - struct binder_buffer *buffer, - size_t size) -{ - unsigned long page_addr; - unsigned long end; - int page_index; - - end = PAGE_ALIGN(buffer->user_data + size); - page_addr = buffer->user_data; - for (; page_addr < end; page_addr += PAGE_SIZE) { - page_index = (page_addr - alloc->vm_start) / PAGE_SIZE; - if (!alloc->pages[page_index] || - !list_empty(page_to_lru(alloc->pages[page_index]))) { - pr_err("expect alloc but is %s at page index %d\n", - alloc->pages[page_index] ? - "lru" : "free", page_index); - return false; - } - } - return true; -} - -static void binder_selftest_alloc_buf(struct binder_alloc *alloc, - struct binder_buffer *buffers[], - size_t *sizes, int *seq) -{ - int i; - - for (i = 0; i < BUFFER_NUM; i++) { - buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); - if (IS_ERR(buffers[i]) || - !check_buffer_pages_allocated(alloc, buffers[i], - sizes[i])) { - pr_err_size_seq(sizes, seq); - binder_selftest_failures++; - } - } -} - -static void binder_selftest_free_buf(struct binder_alloc *alloc, - struct binder_buffer *buffers[], - size_t *sizes, int *seq, size_t end) -{ - int i; - - for (i = 0; i < BUFFER_NUM; i++) - binder_alloc_free_buf(alloc, buffers[seq[i]]); - - for (i = 0; i < end / PAGE_SIZE; i++) { - /** - * Error message on a free page can be false positive - * if binder shrinker ran during binder_alloc_free_buf - * calls above. - */ - if (list_empty(page_to_lru(alloc->pages[i]))) { - pr_err_size_seq(sizes, seq); - pr_err("expect lru but is %s at page index %d\n", - alloc->pages[i] ? "alloc" : "free", i); - binder_selftest_failures++; - } - } -} - -static void binder_selftest_free_page(struct binder_alloc *alloc) -{ - int i; - unsigned long count; - - while ((count = list_lru_count(&binder_freelist))) { - list_lru_walk(&binder_freelist, binder_alloc_free_page, - NULL, count); - } - - for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { - if (alloc->pages[i]) { - pr_err("expect free but is %s at page index %d\n", - list_empty(page_to_lru(alloc->pages[i])) ? - "alloc" : "lru", i); - binder_selftest_failures++; - } - } -} - -static void binder_selftest_alloc_free(struct binder_alloc *alloc, - size_t *sizes, int *seq, size_t end) -{ - struct binder_buffer *buffers[BUFFER_NUM]; - - binder_selftest_alloc_buf(alloc, buffers, sizes, seq); - binder_selftest_free_buf(alloc, buffers, sizes, seq, end); - - /* Allocate from lru. */ - binder_selftest_alloc_buf(alloc, buffers, sizes, seq); - if (list_lru_count(&binder_freelist)) - pr_err("lru list should be empty but is not\n"); - - binder_selftest_free_buf(alloc, buffers, sizes, seq, end); - binder_selftest_free_page(alloc); -} - -static bool is_dup(int *seq, int index, int val) -{ - int i; - - for (i = 0; i < index; i++) { - if (seq[i] == val) - return true; - } - return false; -} - -/* Generate BUFFER_NUM factorial free orders. */ -static void binder_selftest_free_seq(struct binder_alloc *alloc, - size_t *sizes, int *seq, - int index, size_t end) -{ - int i; - - if (index == BUFFER_NUM) { - binder_selftest_alloc_free(alloc, sizes, seq, end); - return; - } - for (i = 0; i < BUFFER_NUM; i++) { - if (is_dup(seq, index, i)) - continue; - seq[index] = i; - binder_selftest_free_seq(alloc, sizes, seq, index + 1, end); - } -} - -static void binder_selftest_alloc_size(struct binder_alloc *alloc, - size_t *end_offset) -{ - int i; - int seq[BUFFER_NUM] = {0}; - size_t front_sizes[BUFFER_NUM]; - size_t back_sizes[BUFFER_NUM]; - size_t last_offset, offset = 0; - - for (i = 0; i < BUFFER_NUM; i++) { - last_offset = offset; - offset = end_offset[i]; - front_sizes[i] = offset - last_offset; - back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; - } - /* - * Buffers share the first or last few pages. - * Only BUFFER_NUM - 1 buffer sizes are adjustable since - * we need one giant buffer before getting to the last page. - */ - back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; - binder_selftest_free_seq(alloc, front_sizes, seq, 0, - end_offset[BUFFER_NUM - 1]); - binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size); -} - -static void binder_selftest_alloc_offset(struct binder_alloc *alloc, - size_t *end_offset, int index) -{ - int align; - size_t end, prev; - - if (index == BUFFER_NUM) { - binder_selftest_alloc_size(alloc, end_offset); - return; - } - prev = index == 0 ? 0 : end_offset[index - 1]; - end = prev; - - BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); - - for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { - if (align % 2) - end = ALIGN(end, PAGE_SIZE); - else - end += BUFFER_MIN_SIZE; - end_offset[index] = end; - binder_selftest_alloc_offset(alloc, end_offset, index + 1); - } -} - -/** - * binder_selftest_alloc() - Test alloc and free of buffer pages. - * @alloc: Pointer to alloc struct. - * - * Allocate BUFFER_NUM buffers to cover all page alignment cases, - * then free them in all orders possible. Check that pages are - * correctly allocated, put onto lru when buffers are freed, and - * are freed when binder_alloc_free_page is called. - */ -void binder_selftest_alloc(struct binder_alloc *alloc) -{ - size_t end_offset[BUFFER_NUM]; - - if (!binder_selftest_run) - return; - mutex_lock(&binder_selftest_lock); - if (!binder_selftest_run || !alloc->mapped) - goto done; - pr_info("STARTED\n"); - binder_selftest_alloc_offset(alloc, end_offset, 0); - binder_selftest_run = false; - if (binder_selftest_failures > 0) - pr_info("%d tests FAILED\n", binder_selftest_failures); - else - pr_info("PASSED\n"); - -done: - mutex_unlock(&binder_selftest_lock); -} diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index a5fd23dcafcd..8b08976146ba 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -590,4 +590,8 @@ void binder_add_device(struct binder_device *device); */ void binder_remove_device(struct binder_device *device); +#if IS_ENABLED(CONFIG_KUNIT) +vm_fault_t binder_vm_fault(struct vm_fault *vmf); +#endif + #endif /* _LINUX_BINDER_INTERNAL_H */ diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 16de1b9e72f7..97a78e5623db 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -34,27 +34,6 @@ TRACE_EVENT(binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); -DECLARE_EVENT_CLASS(binder_lock_class, - TP_PROTO(const char *tag), - TP_ARGS(tag), - TP_STRUCT__entry( - __field(const char *, tag) - ), - TP_fast_assign( - __entry->tag = tag; - ), - TP_printk("tag=%s", __entry->tag) -); - -#define DEFINE_BINDER_LOCK_EVENT(name) \ -DEFINE_EVENT(binder_lock_class, name, \ - TP_PROTO(const char *func), \ - TP_ARGS(func)) - -DEFINE_BINDER_LOCK_EVENT(binder_lock); -DEFINE_BINDER_LOCK_EVENT(binder_locked); -DEFINE_BINDER_LOCK_EVENT(binder_unlock); - DECLARE_EVENT_CLASS(binder_function_return_class, TP_PROTO(int ret), TP_ARGS(ret), diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index acc946bb1457..0d9d95a7fb60 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -117,7 +117,6 @@ static int binderfs_binder_device_create(struct inode *ref_inode, struct dentry *dentry, *root; struct binder_device *device; char *name = NULL; - size_t name_len; struct inode *inode = NULL; struct super_block *sb = ref_inode->i_sb; struct binderfs_info *info = sb->s_fs_info; @@ -161,9 +160,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode, inode->i_gid = info->root_gid; req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */ - name_len = strlen(req->name); - /* Make sure to include terminating NUL byte */ - name = kmemdup(req->name, name_len + 1, GFP_KERNEL); + name = kstrdup(req->name, GFP_KERNEL); if (!name) goto err; diff --git a/drivers/android/tests/.kunitconfig b/drivers/android/tests/.kunitconfig new file mode 100644 index 000000000000..39b76bab9d9a --- /dev/null +++ b/drivers/android/tests/.kunitconfig @@ -0,0 +1,7 @@ +# +# Copyright 2025 Google LLC. +# + +CONFIG_KUNIT=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST=y diff --git a/drivers/android/tests/Makefile b/drivers/android/tests/Makefile new file mode 100644 index 000000000000..27268418eb03 --- /dev/null +++ b/drivers/android/tests/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright 2025 Google LLC. +# + +obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += binder_alloc_kunit.o diff --git a/drivers/android/tests/binder_alloc_kunit.c b/drivers/android/tests/binder_alloc_kunit.c new file mode 100644 index 000000000000..9b884d977f76 --- /dev/null +++ b/drivers/android/tests/binder_alloc_kunit.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for binder allocator code. + * + * Copyright 2025 Google LLC. + * Author: Tiffany Yang <ynaffit@google.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <kunit/test.h> +#include <linux/anon_inodes.h> +#include <linux/err.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/seq_buf.h> +#include <linux/sizes.h> + +#include "../binder_alloc.h" +#include "../binder_internal.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +#define BINDER_MMAP_SIZE SZ_128K + +#define BUFFER_NUM 5 +#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) + +#define FREESEQ_BUFLEN ((3 * BUFFER_NUM) + 1) + +#define ALIGN_TYPE_STRLEN (12) + +#define ALIGNMENTS_BUFLEN (((ALIGN_TYPE_STRLEN + 6) * BUFFER_NUM) + 1) + +#define PRINT_ALL_CASES (0) + +/* 5^5 alignment combinations * 2 places to share pages * 5! free sequences */ +#define TOTAL_EXHAUSTIVE_CASES (3125 * 2 * 120) + +/** + * enum buf_end_align_type - Page alignment of a buffer + * end with regard to the end of the previous buffer. + * + * In the pictures below, buf2 refers to the buffer we + * are aligning. buf1 refers to previous buffer by addr. + * Symbol [ means the start of a buffer, ] means the end + * of a buffer, and | means page boundaries. + */ +enum buf_end_align_type { + /** + * @SAME_PAGE_UNALIGNED: The end of this buffer is on + * the same page as the end of the previous buffer and + * is not page aligned. Examples: + * buf1 ][ buf2 ][ ... + * buf1 ]|[ buf2 ][ ... + */ + SAME_PAGE_UNALIGNED = 0, + /** + * @SAME_PAGE_ALIGNED: When the end of the previous buffer + * is not page aligned, the end of this buffer is on the + * same page as the end of the previous buffer and is page + * aligned. When the previous buffer is page aligned, the + * end of this buffer is aligned to the next page boundary. + * Examples: + * buf1 ][ buf2 ]| ... + * buf1 ]|[ buf2 ]| ... + */ + SAME_PAGE_ALIGNED, + /** + * @NEXT_PAGE_UNALIGNED: The end of this buffer is on + * the page next to the end of the previous buffer and + * is not page aligned. Examples: + * buf1 ][ buf2 | buf2 ][ ... + * buf1 ]|[ buf2 | buf2 ][ ... + */ + NEXT_PAGE_UNALIGNED, + /** + * @NEXT_PAGE_ALIGNED: The end of this buffer is on + * the page next to the end of the previous buffer and + * is page aligned. Examples: + * buf1 ][ buf2 | buf2 ]| ... + * buf1 ]|[ buf2 | buf2 ]| ... + */ + NEXT_PAGE_ALIGNED, + /** + * @NEXT_NEXT_UNALIGNED: The end of this buffer is on + * the page that follows the page after the end of the + * previous buffer and is not page aligned. Examples: + * buf1 ][ buf2 | buf2 | buf2 ][ ... + * buf1 ]|[ buf2 | buf2 | buf2 ][ ... + */ + NEXT_NEXT_UNALIGNED, + /** + * @LOOP_END: The number of enum values in &buf_end_align_type. + * It is used for controlling loop termination. + */ + LOOP_END, +}; + +static const char *const buf_end_align_type_strs[LOOP_END] = { + [SAME_PAGE_UNALIGNED] = "SP_UNALIGNED", + [SAME_PAGE_ALIGNED] = " SP_ALIGNED ", + [NEXT_PAGE_UNALIGNED] = "NP_UNALIGNED", + [NEXT_PAGE_ALIGNED] = " NP_ALIGNED ", + [NEXT_NEXT_UNALIGNED] = "NN_UNALIGNED", +}; + +struct binder_alloc_test_case_info { + char alignments[ALIGNMENTS_BUFLEN]; + struct seq_buf alignments_sb; + size_t *buffer_sizes; + int *free_sequence; + bool front_pages; +}; + +static void stringify_free_seq(struct kunit *test, int *seq, struct seq_buf *sb) +{ + int i; + + for (i = 0; i < BUFFER_NUM; i++) + seq_buf_printf(sb, "[%d]", seq[i]); + + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb)); +} + +static void stringify_alignments(struct kunit *test, int *alignments, + struct seq_buf *sb) +{ + int i; + + for (i = 0; i < BUFFER_NUM; i++) + seq_buf_printf(sb, "[ %d:%s ]", i, + buf_end_align_type_strs[alignments[i]]); + + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb)); +} + +static bool check_buffer_pages_allocated(struct kunit *test, + struct binder_alloc *alloc, + struct binder_buffer *buffer, + size_t size) +{ + unsigned long page_addr; + unsigned long end; + int page_index; + + end = PAGE_ALIGN(buffer->user_data + size); + page_addr = buffer->user_data; + for (; page_addr < end; page_addr += PAGE_SIZE) { + page_index = (page_addr - alloc->vm_start) / PAGE_SIZE; + if (!alloc->pages[page_index] || + !list_empty(page_to_lru(alloc->pages[page_index]))) { + kunit_err(test, "expect alloc but is %s at page index %d\n", + alloc->pages[page_index] ? + "lru" : "free", page_index); + return false; + } + } + return true; +} + +static unsigned long binder_alloc_test_alloc_buf(struct kunit *test, + struct binder_alloc *alloc, + struct binder_buffer *buffers[], + size_t *sizes, int *seq) +{ + unsigned long failures = 0; + int i; + + for (i = 0; i < BUFFER_NUM; i++) { + buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); + if (IS_ERR(buffers[i]) || + !check_buffer_pages_allocated(test, alloc, buffers[i], sizes[i])) + failures++; + } + + return failures; +} + +static unsigned long binder_alloc_test_free_buf(struct kunit *test, + struct binder_alloc *alloc, + struct binder_buffer *buffers[], + size_t *sizes, int *seq, size_t end) +{ + unsigned long failures = 0; + int i; + + for (i = 0; i < BUFFER_NUM; i++) + binder_alloc_free_buf(alloc, buffers[seq[i]]); + + for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) { + if (list_empty(page_to_lru(alloc->pages[i]))) { + kunit_err(test, "expect lru but is %s at page index %d\n", + alloc->pages[i] ? "alloc" : "free", i); + failures++; + } + } + + return failures; +} + +static unsigned long binder_alloc_test_free_page(struct kunit *test, + struct binder_alloc *alloc) +{ + unsigned long failures = 0; + unsigned long count; + int i; + + while ((count = list_lru_count(alloc->freelist))) { + list_lru_walk(alloc->freelist, binder_alloc_free_page, + NULL, count); + } + + for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { + if (alloc->pages[i]) { + kunit_err(test, "expect free but is %s at page index %d\n", + list_empty(page_to_lru(alloc->pages[i])) ? + "alloc" : "lru", i); + failures++; + } + } + + return failures; +} + +/* Executes one full test run for the given test case. */ +static bool binder_alloc_test_alloc_free(struct kunit *test, + struct binder_alloc *alloc, + struct binder_alloc_test_case_info *tc, + size_t end) +{ + unsigned long pages = PAGE_ALIGN(end) / PAGE_SIZE; + struct binder_buffer *buffers[BUFFER_NUM]; + unsigned long failures; + bool failed = false; + + failures = binder_alloc_test_alloc_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Initial allocation failed: %lu/%u buffers with errors", + failures, BUFFER_NUM); + + failures = binder_alloc_test_free_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence, end); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Initial buffers not freed correctly: %lu/%lu pages not on lru list", + failures, pages); + + /* Allocate from lru. */ + failures = binder_alloc_test_alloc_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Reallocation failed: %lu/%u buffers with errors", + failures, BUFFER_NUM); + + failures = list_lru_count(alloc->freelist); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "lru list should be empty after reallocation but still has %lu pages", + failures); + + failures = binder_alloc_test_free_buf(test, alloc, buffers, + tc->buffer_sizes, + tc->free_sequence, end); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Reallocated buffers not freed correctly: %lu/%lu pages not on lru list", + failures, pages); + + failures = binder_alloc_test_free_page(test, alloc); + failed = failed || failures; + KUNIT_EXPECT_EQ_MSG(test, failures, 0, + "Failed to clean up allocated pages: %lu/%lu pages still installed", + failures, (alloc->buffer_size / PAGE_SIZE)); + + return failed; +} + +static bool is_dup(int *seq, int index, int val) +{ + int i; + + for (i = 0; i < index; i++) { + if (seq[i] == val) + return true; + } + return false; +} + +/* Generate BUFFER_NUM factorial free orders. */ +static void permute_frees(struct kunit *test, struct binder_alloc *alloc, + struct binder_alloc_test_case_info *tc, + unsigned long *runs, unsigned long *failures, + int index, size_t end) +{ + bool case_failed; + int i; + + if (index == BUFFER_NUM) { + DECLARE_SEQ_BUF(freeseq_sb, FREESEQ_BUFLEN); + + case_failed = binder_alloc_test_alloc_free(test, alloc, tc, end); + *runs += 1; + *failures += case_failed; + + if (case_failed || PRINT_ALL_CASES) { + stringify_free_seq(test, tc->free_sequence, + &freeseq_sb); + kunit_err(test, "case %lu: [%s] | %s - %s - %s", *runs, + case_failed ? "FAILED" : "PASSED", + tc->front_pages ? "front" : "back ", + seq_buf_str(&tc->alignments_sb), + seq_buf_str(&freeseq_sb)); + } + + return; + } + for (i = 0; i < BUFFER_NUM; i++) { + if (is_dup(tc->free_sequence, index, i)) + continue; + tc->free_sequence[index] = i; + permute_frees(test, alloc, tc, runs, failures, index + 1, end); + } +} + +static void gen_buf_sizes(struct kunit *test, + struct binder_alloc *alloc, + struct binder_alloc_test_case_info *tc, + size_t *end_offset, unsigned long *runs, + unsigned long *failures) +{ + size_t last_offset, offset = 0; + size_t front_sizes[BUFFER_NUM]; + size_t back_sizes[BUFFER_NUM]; + int seq[BUFFER_NUM] = {0}; + int i; + + tc->free_sequence = seq; + for (i = 0; i < BUFFER_NUM; i++) { + last_offset = offset; + offset = end_offset[i]; + front_sizes[i] = offset - last_offset; + back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; + } + back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; + + /* + * Buffers share the first or last few pages. + * Only BUFFER_NUM - 1 buffer sizes are adjustable since + * we need one giant buffer before getting to the last page. + */ + tc->front_pages = true; + tc->buffer_sizes = front_sizes; + permute_frees(test, alloc, tc, runs, failures, 0, + end_offset[BUFFER_NUM - 1]); + + tc->front_pages = false; + tc->buffer_sizes = back_sizes; + permute_frees(test, alloc, tc, runs, failures, 0, alloc->buffer_size); +} + +static void gen_buf_offsets(struct kunit *test, struct binder_alloc *alloc, + size_t *end_offset, int *alignments, + unsigned long *runs, unsigned long *failures, + int index) +{ + size_t end, prev; + int align; + + if (index == BUFFER_NUM) { + struct binder_alloc_test_case_info tc = {0}; + + seq_buf_init(&tc.alignments_sb, tc.alignments, + ALIGNMENTS_BUFLEN); + stringify_alignments(test, alignments, &tc.alignments_sb); + + gen_buf_sizes(test, alloc, &tc, end_offset, runs, failures); + return; + } + prev = index == 0 ? 0 : end_offset[index - 1]; + end = prev; + + BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); + + for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { + if (align % 2) + end = ALIGN(end, PAGE_SIZE); + else + end += BUFFER_MIN_SIZE; + end_offset[index] = end; + alignments[index] = align; + gen_buf_offsets(test, alloc, end_offset, alignments, runs, + failures, index + 1); + } +} + +struct binder_alloc_test { + struct binder_alloc alloc; + struct list_lru binder_test_freelist; + struct file *filp; + unsigned long mmap_uaddr; +}; + +static void binder_alloc_test_init_freelist(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + + KUNIT_EXPECT_PTR_EQ(test, priv->alloc.freelist, + &priv->binder_test_freelist); +} + +static void binder_alloc_test_mmap(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + struct binder_alloc *alloc = &priv->alloc; + struct binder_buffer *buf; + struct rb_node *n; + + KUNIT_EXPECT_EQ(test, alloc->mapped, true); + KUNIT_EXPECT_EQ(test, alloc->buffer_size, BINDER_MMAP_SIZE); + + n = rb_first(&alloc->allocated_buffers); + KUNIT_EXPECT_PTR_EQ(test, n, NULL); + + n = rb_first(&alloc->free_buffers); + buf = rb_entry(n, struct binder_buffer, rb_node); + KUNIT_EXPECT_EQ(test, binder_alloc_buffer_size(alloc, buf), + BINDER_MMAP_SIZE); + KUNIT_EXPECT_TRUE(test, list_is_last(&buf->entry, &alloc->buffers)); +} + +/** + * binder_alloc_exhaustive_test() - Exhaustively test alloc and free of buffer pages. + * @test: The test context object. + * + * Allocate BUFFER_NUM buffers to cover all page alignment cases, + * then free them in all orders possible. Check that pages are + * correctly allocated, put onto lru when buffers are freed, and + * are freed when binder_alloc_free_page() is called. + */ +static void binder_alloc_exhaustive_test(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + size_t end_offset[BUFFER_NUM]; + int alignments[BUFFER_NUM]; + unsigned long failures = 0; + unsigned long runs = 0; + + gen_buf_offsets(test, &priv->alloc, end_offset, alignments, &runs, + &failures, 0); + + KUNIT_EXPECT_EQ(test, runs, TOTAL_EXHAUSTIVE_CASES); + KUNIT_EXPECT_EQ(test, failures, 0); +} + +/* ===== End test cases ===== */ + +static void binder_alloc_test_vma_close(struct vm_area_struct *vma) +{ + struct binder_alloc *alloc = vma->vm_private_data; + + binder_alloc_vma_close(alloc); +} + +static const struct vm_operations_struct binder_alloc_test_vm_ops = { + .close = binder_alloc_test_vma_close, + .fault = binder_vm_fault, +}; + +static int binder_alloc_test_mmap_handler(struct file *filp, + struct vm_area_struct *vma) +{ + struct binder_alloc *alloc = filp->private_data; + + vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE); + + vma->vm_ops = &binder_alloc_test_vm_ops; + vma->vm_private_data = alloc; + + return binder_alloc_mmap_handler(alloc, vma); +} + +static const struct file_operations binder_alloc_test_fops = { + .mmap = binder_alloc_test_mmap_handler, +}; + +static int binder_alloc_test_init(struct kunit *test) +{ + struct binder_alloc_test *priv; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + test->priv = priv; + + ret = list_lru_init(&priv->binder_test_freelist); + if (ret) { + kunit_err(test, "Failed to initialize test freelist\n"); + return ret; + } + + /* __binder_alloc_init requires mm to be attached */ + ret = kunit_attach_mm(); + if (ret) { + kunit_err(test, "Failed to attach mm\n"); + return ret; + } + __binder_alloc_init(&priv->alloc, &priv->binder_test_freelist); + + priv->filp = anon_inode_getfile("binder_alloc_kunit", + &binder_alloc_test_fops, &priv->alloc, + O_RDWR | O_CLOEXEC); + if (IS_ERR_OR_NULL(priv->filp)) { + kunit_err(test, "Failed to open binder alloc test driver file\n"); + return priv->filp ? PTR_ERR(priv->filp) : -ENOMEM; + } + + priv->mmap_uaddr = kunit_vm_mmap(test, priv->filp, 0, BINDER_MMAP_SIZE, + PROT_READ, MAP_PRIVATE | MAP_NORESERVE, + 0); + if (!priv->mmap_uaddr) { + kunit_err(test, "Could not map the test's transaction memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void binder_alloc_test_exit(struct kunit *test) +{ + struct binder_alloc_test *priv = test->priv; + + /* Close the backing file to make sure binder_alloc_vma_close runs */ + if (!IS_ERR_OR_NULL(priv->filp)) + fput(priv->filp); + + if (priv->alloc.mm) + binder_alloc_deferred_release(&priv->alloc); + + /* Make sure freelist is empty */ + KUNIT_EXPECT_EQ(test, list_lru_count(&priv->binder_test_freelist), 0); + list_lru_destroy(&priv->binder_test_freelist); +} + +static struct kunit_case binder_alloc_test_cases[] = { + KUNIT_CASE(binder_alloc_test_init_freelist), + KUNIT_CASE(binder_alloc_test_mmap), + KUNIT_CASE(binder_alloc_exhaustive_test), + {} +}; + +static struct kunit_suite binder_alloc_test_suite = { + .name = "binder_alloc", + .test_cases = binder_alloc_test_cases, + .init = binder_alloc_test_init, + .exit = binder_alloc_test_exit, +}; + +kunit_test_suite(binder_alloc_test_suite); + +MODULE_AUTHOR("Tiffany Yang <ynaffit@google.com>"); +MODULE_DESCRIPTION("Binder Alloc KUnit tests"); +MODULE_LICENSE("GPL"); diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index efa3b6dddf4d..205d83ac069f 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -31,8 +31,8 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, int ret; for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) { - bhi_vec->dma_addr = mhi_buf->dma_addr; - bhi_vec->size = mhi_buf->len; + bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr); + bhi_vec->size = cpu_to_le64(mhi_buf->len); } dev_dbg(dev, "BHIe programming for RDDM\n"); @@ -431,8 +431,8 @@ static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl, while (remainder) { to_cpy = min(remainder, mhi_buf->len); memcpy(mhi_buf->buf, buf, to_cpy); - bhi_vec->dma_addr = mhi_buf->dma_addr; - bhi_vec->size = to_cpy; + bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr); + bhi_vec->size = cpu_to_le64(to_cpy); buf += to_cpy; remainder -= to_cpy; diff --git a/drivers/bus/mhi/host/debugfs.c b/drivers/bus/mhi/host/debugfs.c index cfec7811dfbb..39e45748a24c 100644 --- a/drivers/bus/mhi/host/debugfs.c +++ b/drivers/bus/mhi/host/debugfs.c @@ -10,6 +10,7 @@ #include <linux/list.h> #include <linux/mhi.h> #include <linux/module.h> +#include <linux/string_choices.h> #include "internal.h" static int mhi_debugfs_states_show(struct seq_file *m, void *d) @@ -22,7 +23,7 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d) mhi_is_active(mhi_cntrl) ? "Active" : "Inactive", mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(mhi_cntrl->ee), - mhi_cntrl->wake_set ? "true" : "false"); + str_true_false(mhi_cntrl->wake_set)); /* counters */ seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2, diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 13e7a55f54ff..7f72aab38ce9 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -176,7 +176,7 @@ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl, return 0; } -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) +static void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) { int i; struct mhi_event *mhi_event = mhi_cntrl->mhi_event; @@ -191,7 +191,7 @@ void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) free_irq(mhi_cntrl->irq[0], mhi_cntrl); } -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) +static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) { struct mhi_event *mhi_event = mhi_cntrl->mhi_event; struct device *dev = &mhi_cntrl->mhi_dev->dev; @@ -254,7 +254,7 @@ error_request: return ret; } -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) +static void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) { int i; struct mhi_ctxt *mhi_ctxt = mhi_cntrl->mhi_ctxt; @@ -299,7 +299,7 @@ void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) mhi_cntrl->mhi_ctxt = NULL; } -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) +static int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) { struct mhi_ctxt *mhi_ctxt; struct mhi_chan_ctxt *chan_ctxt; diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index ce566f7d2e92..034be33565b7 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -25,8 +25,8 @@ struct mhi_ctxt { }; struct bhi_vec_entry { - u64 dma_addr; - u64 size; + __le64 dma_addr; + __le64 size; }; enum mhi_fw_load_type { @@ -383,19 +383,12 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, /* Initialization methods */ int mhi_init_mmio(struct mhi_controller *mhi_cntrl); -int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl); -void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl); -int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl); -void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl); int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, struct image_info *img_info); void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl); /* Automatically allocate and queue inbound buffers */ #define MHI_CH_INBOUND_ALLOC_BUFS BIT(0) -int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan, unsigned int flags); - int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 9bb0df43ceef..52bef663e182 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -602,7 +602,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, { dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event); struct mhi_ring_element *local_rp, *ev_tre; - void *dev_rp; + void *dev_rp, *next_rp; struct mhi_buf_info *buf_info; u16 xfer_len; @@ -621,6 +621,16 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, result.dir = mhi_chan->dir; local_rp = tre_ring->rp; + + next_rp = local_rp + 1; + if (next_rp >= tre_ring->base + tre_ring->len) + next_rp = tre_ring->base; + if (dev_rp != next_rp && !MHI_TRE_DATA_GET_CHAIN(local_rp)) { + dev_err(&mhi_cntrl->mhi_dev->dev, + "Event element points to an unexpected TRE\n"); + break; + } + while (local_rp != dev_rp) { buf_info = buf_ring->rp; /* If it's the last TRE, get length from the event */ @@ -1435,7 +1445,7 @@ exit_unprepare_channel: mutex_unlock(&mhi_chan->mutex); } -int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, +static int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, unsigned int flags) { int ret = 0; diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index 589cb6722316..4edb5bb476ba 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -43,6 +43,7 @@ * @mru_default: default MRU size for MBIM network packets * @sideband_wake: Devices using dedicated sideband GPIO for wakeup instead * of inband wake support (such as sdx24) + * @no_m3: M3 not supported */ struct mhi_pci_dev_info { const struct mhi_controller_config *config; @@ -54,6 +55,7 @@ struct mhi_pci_dev_info { unsigned int dma_data_width; unsigned int mru_default; bool sideband_wake; + bool no_m3; }; #define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \ @@ -295,6 +297,7 @@ static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = { .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, .sideband_wake = false, + .no_m3 = true, }; static const struct mhi_channel_config mhi_qcom_sa8775p_channels[] = { @@ -490,6 +493,23 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = { MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), }; +static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = { + MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0), + MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1), + MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1), + MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0), + MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0), + MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0), + MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), + MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0), + MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0), + MHI_CHANNEL_CONFIG_UL(50, "NMEA", 32, 0), + MHI_CHANNEL_CONFIG_DL(51, "NMEA", 32, 0), + MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2), + MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3), +}; + static struct mhi_event_config mhi_foxconn_sdx55_events[] = { MHI_EVENT_CONFIG_CTRL(0, 128), MHI_EVENT_CONFIG_DATA(1, 128), @@ -506,6 +526,15 @@ static const struct mhi_controller_config modem_foxconn_sdx55_config = { .event_cfg = mhi_foxconn_sdx55_events, }; +static const struct mhi_controller_config modem_foxconn_sdx61_config = { + .max_channels = 128, + .timeout_ms = 20000, + .num_channels = ARRAY_SIZE(mhi_foxconn_sdx61_channels), + .ch_cfg = mhi_foxconn_sdx61_channels, + .num_events = ARRAY_SIZE(mhi_foxconn_sdx55_events), + .event_cfg = mhi_foxconn_sdx55_events, +}; + static const struct mhi_controller_config modem_foxconn_sdx72_config = { .max_channels = 128, .timeout_ms = 20000, @@ -593,8 +622,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5932e_info = { .sideband_wake = false, }; -static const struct mhi_pci_dev_info mhi_foxconn_t99w515_info = { - .name = "foxconn-t99w515", +static const struct mhi_pci_dev_info mhi_foxconn_t99w640_info = { + .name = "foxconn-t99w640", .edl = "qcom/sdx72m/foxconn/edl.mbn", .edl_trigger = true, .config = &modem_foxconn_sdx72_config, @@ -615,6 +644,17 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5934e_info = { .sideband_wake = false, }; +static const struct mhi_pci_dev_info mhi_foxconn_t99w696_info = { + .name = "foxconn-t99w696", + .edl = "qcom/sdx61/foxconn/prog_firehose_lite.elf", + .edl_trigger = true, + .config = &modem_foxconn_sdx61_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .mru_default = 32768, + .sideband_wake = false, +}; + static const struct mhi_channel_config mhi_mv3x_channels[] = { MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 64, 0), MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 64, 0), @@ -695,6 +735,7 @@ static const struct mhi_pci_dev_info mhi_sierra_em919x_info = { .config = &modem_sierra_em919x_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, + .mru_default = 32768, .sideband_wake = false, }; @@ -818,6 +859,16 @@ static const struct mhi_pci_dev_info mhi_telit_fn920c04_info = { .edl_trigger = true, }; +static const struct mhi_pci_dev_info mhi_telit_fn990b40_info = { + .name = "telit-fn990b40", + .config = &modem_telit_fn920c04_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, + .mru_default = 32768, + .edl_trigger = true, +}; + static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = { .name = "netprisma-lcur57", .edl = "qcom/prog_firehose_sdx24.mbn", @@ -852,6 +903,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200), .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info }, + /* EM929x (sdx65), use the same configuration as EM919x */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x18d7, 0x0301), + .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info }, /* Telit FN980 hardware revision v1 */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x1C5D, 0x2000), .driver_data = (kernel_ulong_t) &mhi_telit_fn980_hw_v1_info }, @@ -863,8 +917,26 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* Telit FE990A */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015), .driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info }, + /* Foxconn T99W696.01, Lenovo Generic SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe142), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.02, Lenovo X1 Carbon SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe143), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.03, Lenovo X1 2in1 SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe144), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.04, Lenovo PRC SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe145), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, + /* Foxconn T99W696.00, Foxconn SKU */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe146), + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info }, + /* Telit FN990B40 (sdx72) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0309, 0x1c5d, 0x201a), + .driver_data = (kernel_ulong_t) &mhi_telit_fn990b40_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info }, /* QDU100, x100-DU */ @@ -920,9 +992,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* DW5932e (sdx62), Non-eSIM */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f9), .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5932e_info }, - /* T99W515 (sdx72) */ + /* T99W640 (sdx72) */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe118), - .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w515_info }, + .driver_data = (kernel_ulong_t) &mhi_foxconn_t99w640_info }, /* DW5934e(sdx72), With eSIM */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe11d), .driver_data = (kernel_ulong_t) &mhi_foxconn_dw5934e_info }, @@ -1306,8 +1378,8 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* start health check */ mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); - /* Only allow runtime-suspend if PME capable (for wakeup) */ - if (pci_pme_capable(pdev, PCI_D3hot)) { + /* Allow runtime suspend only if both PME from D3Hot and M3 are supported */ + if (pci_pme_capable(pdev, PCI_D3hot) && !(info->no_m3)) { pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_mark_last_busy(&pdev->dev); diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c index 6c3e5c5dae10..7ce61d629a87 100644 --- a/drivers/bus/moxtet.c +++ b/drivers/bus/moxtet.c @@ -737,8 +737,7 @@ static int moxtet_irq_setup(struct moxtet *moxtet) { int i, ret; - moxtet->irq.domain = irq_domain_create_simple(of_fwnode_handle(moxtet->dev->of_node), - MOXTET_NIRQS, 0, + moxtet->irq.domain = irq_domain_create_simple(dev_fwnode(moxtet->dev), MOXTET_NIRQS, 0, &moxtet_irq_domain, moxtet); if (moxtet->irq.domain == NULL) { dev_err(moxtet->dev, "Could not add IRQ domain\n"); diff --git a/drivers/cdx/Kconfig b/drivers/cdx/Kconfig index a08958485e31..3af41f51cf38 100644 --- a/drivers/cdx/Kconfig +++ b/drivers/cdx/Kconfig @@ -7,7 +7,8 @@ config CDX_BUS bool "CDX Bus driver" - depends on OF && ARM64 + depends on OF && ARM64 || COMPILE_TEST + select GENERIC_MSI_IRQ help Driver to enable Composable DMA Transfer(CDX) Bus. CDX bus exposes Fabric devices which uses composable DMA IP to the diff --git a/drivers/cdx/controller/Kconfig b/drivers/cdx/controller/Kconfig index f8e729761aee..0641a4c21e66 100644 --- a/drivers/cdx/controller/Kconfig +++ b/drivers/cdx/controller/Kconfig @@ -9,6 +9,7 @@ if CDX_BUS config CDX_CONTROLLER tristate "CDX bus controller" + depends on HAS_DMA select GENERIC_MSI_IRQ select REMOTEPROC select RPMSG diff --git a/drivers/cdx/controller/cdx_controller.c b/drivers/cdx/controller/cdx_controller.c index d623f9c7517a..fca83141e3e6 100644 --- a/drivers/cdx/controller/cdx_controller.c +++ b/drivers/cdx/controller/cdx_controller.c @@ -195,19 +195,16 @@ static int xlnx_cdx_probe(struct platform_device *pdev) /* Create MSI domain */ cdx->msi_domain = cdx_msi_domain_init(&pdev->dev); if (!cdx->msi_domain) { - dev_err(&pdev->dev, "cdx_msi_domain_init() failed"); - ret = -ENODEV; + ret = dev_err_probe(&pdev->dev, -ENODEV, "cdx_msi_domain_init() failed"); goto cdx_msi_fail; } ret = cdx_setup_rpmsg(pdev); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n"); + dev_err_probe(&pdev->dev, ret, "Failed to register CDX RPMsg transport\n"); goto cdx_rpmsg_fail; } - dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n"); return 0; cdx_rpmsg_fail: @@ -246,31 +243,13 @@ MODULE_DEVICE_TABLE(of, cdx_match_table); static struct platform_driver cdx_pdriver = { .driver = { .name = "cdx-controller", - .pm = NULL, .of_match_table = cdx_match_table, }, .probe = xlnx_cdx_probe, .remove = xlnx_cdx_remove, }; -static int __init cdx_controller_init(void) -{ - int ret; - - ret = platform_driver_register(&cdx_pdriver); - if (ret) - pr_err("platform_driver_register() failed: %d\n", ret); - - return ret; -} - -static void __exit cdx_controller_exit(void) -{ - platform_driver_unregister(&cdx_pdriver); -} - -module_init(cdx_controller_init); -module_exit(cdx_controller_exit); +module_platform_driver(cdx_pdriver); MODULE_AUTHOR("AMD Inc."); MODULE_DESCRIPTION("CDX controller for AMD devices"); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index ae6196760556..d2cfc584e202 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -237,7 +237,7 @@ config APPLICOM config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" - depends on X86_32 && PCI && INPUT + depends on X86_32 && PCI && INPUT && HAS_IOPORT depends on ACPI_EC || !ACPI help This driver enables access to the Sony Programmable I/O Control diff --git a/drivers/char/misc.c b/drivers/char/misc.c index d5accc10a110..558302a64dd9 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -289,15 +289,15 @@ EXPORT_SYMBOL(misc_deregister); static int __init misc_init(void) { int err; - struct proc_dir_entry *ret; + struct proc_dir_entry *misc_proc_file; - ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops); + misc_proc_file = proc_create_seq("misc", 0, NULL, &misc_seq_ops); err = class_register(&misc_class); if (err) goto fail_remove; - err = -EIO; - if (__register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops)) + err = __register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops); + if (err < 0) goto fail_printk; return 0; @@ -305,7 +305,7 @@ fail_printk: pr_err("unable to get major %d for misc devices\n", MISC_MAJOR); class_unregister(&misc_class); fail_remove: - if (ret) + if (misc_proc_file) remove_proc_entry("misc", NULL); return err; } diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index c83fd14dd7ad..23b7178522ae 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -787,6 +787,7 @@ static int is_device_busy(struct comedi_device *dev) struct comedi_subdevice *s; int i; + lockdep_assert_held_write(&dev->attach_lock); lockdep_assert_held(&dev->mutex); if (!dev->attached) return 0; @@ -795,7 +796,16 @@ static int is_device_busy(struct comedi_device *dev) s = &dev->subdevices[i]; if (s->busy) return 1; - if (s->async && comedi_buf_is_mmapped(s)) + if (!s->async) + continue; + if (comedi_buf_is_mmapped(s)) + return 1; + /* + * There may be tasks still waiting on the subdevice's wait + * queue, although they should already be about to be removed + * from it since the subdevice has no active async command. + */ + if (wq_has_sleeper(&s->async->wait_head)) return 1; } @@ -825,15 +835,22 @@ static int do_devconfig_ioctl(struct comedi_device *dev, return -EPERM; if (!arg) { - if (is_device_busy(dev)) - return -EBUSY; + int rc = 0; + if (dev->attached) { - struct module *driver_module = dev->driver->module; + down_write(&dev->attach_lock); + if (is_device_busy(dev)) { + rc = -EBUSY; + } else { + struct module *driver_module = + dev->driver->module; - comedi_device_detach(dev); - module_put(driver_module); + comedi_device_detach_locked(dev); + module_put(driver_module); + } + up_write(&dev->attach_lock); } - return 0; + return rc; } if (copy_from_user(&it, arg, sizeof(it))) diff --git a/drivers/comedi/comedi_internal.h b/drivers/comedi/comedi_internal.h index 9b3631a654c8..cf10ba016ebc 100644 --- a/drivers/comedi/comedi_internal.h +++ b/drivers/comedi/comedi_internal.h @@ -50,6 +50,7 @@ extern struct mutex comedi_drivers_list_lock; int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data); +void comedi_device_detach_locked(struct comedi_device *dev); void comedi_device_detach(struct comedi_device *dev); int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it); diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index 9e4b7c840a8f..f1dc854928c1 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -158,7 +158,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev) int i; struct comedi_subdevice *s; - lockdep_assert_held(&dev->attach_lock); + lockdep_assert_held_write(&dev->attach_lock); lockdep_assert_held(&dev->mutex); if (dev->subdevices) { for (i = 0; i < dev->n_subdevices; i++) { @@ -196,16 +196,23 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev) comedi_clear_hw_dev(dev); } -void comedi_device_detach(struct comedi_device *dev) +void comedi_device_detach_locked(struct comedi_device *dev) { + lockdep_assert_held_write(&dev->attach_lock); lockdep_assert_held(&dev->mutex); comedi_device_cancel_all(dev); - down_write(&dev->attach_lock); dev->attached = false; dev->detach_count++; if (dev->driver) dev->driver->detach(dev); comedi_device_detach_cleanup(dev); +} + +void comedi_device_detach(struct comedi_device *dev) +{ + lockdep_assert_held(&dev->mutex); + down_write(&dev->attach_lock); + comedi_device_detach_locked(dev); up_write(&dev->attach_lock); } diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index f7e08f7ea9ef..0be0d569589d 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -406,7 +406,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt) } priv->dma_nelms = - dma_map_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + dma_map_sgtable(mgr->dev.parent, sgt, DMA_TO_DEVICE, 0); if (priv->dma_nelms == 0) { dev_err(&mgr->dev, "Unable to DMA map (TO_DEVICE)\n"); return -ENOMEM; @@ -478,7 +478,7 @@ out_clk: clk_disable(priv->clk); out_free: - dma_unmap_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE); + dma_unmap_sgtable(mgr->dev.parent, sgt, DMA_TO_DEVICE, 0); return err; } diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 50e8736039fe..ee39d1699387 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -1404,7 +1404,7 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv) } EXPORT_SYMBOL_GPL(fsi_driver_unregister); -struct bus_type fsi_bus_type = { +const struct bus_type fsi_bus_type = { .name = "fsi", .match = fsi_bus_match, }; diff --git a/drivers/fsi/fsi-master-ast-cf.c b/drivers/fsi/fsi-master-ast-cf.c index 9f2fd444ceb6..e67d7cd30fca 100644 --- a/drivers/fsi/fsi-master-ast-cf.c +++ b/drivers/fsi/fsi-master-ast-cf.c @@ -13,13 +13,13 @@ #include <linux/irqflags.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/regmap.h> #include <linux/firmware.h> #include <linux/gpio/aspeed.h> #include <linux/mfd/syscon.h> -#include <linux/of_address.h> #include <linux/genalloc.h> #include "fsi-master.h" @@ -1285,14 +1285,7 @@ static int fsi_master_acf_probe(struct platform_device *pdev) master->gpio_mux = gpio; /* Grab the reserved memory region (use DMA API instead ?) */ - np = of_parse_phandle(mnode, "memory-region", 0); - if (!np) { - dev_err(&pdev->dev, "Didn't find reserved memory\n"); - rc = -EINVAL; - goto err_free; - } - rc = of_address_to_resource(np, 0, &res); - of_node_put(np); + rc = of_reserved_mem_region_to_resource(mnode, 0, &res); if (rc) { dev_err(&pdev->dev, "Couldn't address to resource for reserved memory\n"); rc = -ENOMEM; diff --git a/drivers/greybus/gb-beagleplay.c b/drivers/greybus/gb-beagleplay.c index da31f1131afc..9610f878da1b 100644 --- a/drivers/greybus/gb-beagleplay.c +++ b/drivers/greybus/gb-beagleplay.c @@ -1039,9 +1039,12 @@ static const struct fw_upload_ops cc1352_bootloader_ops = { .cleanup = cc1352_cleanup }; +/* + * Must only be called from probe() as the devres resources allocated here + * will only be released on driver detach. + */ static int gb_fw_init(struct gb_beagleplay *bg) { - int ret; struct fw_upload *fwl; struct gpio_desc *desc; @@ -1060,29 +1063,17 @@ static int gb_fw_init(struct gb_beagleplay *bg) bg->bootloader_backdoor_gpio = desc; desc = devm_gpiod_get(&bg->sd->dev, "reset", GPIOD_IN); - if (IS_ERR(desc)) { - ret = PTR_ERR(desc); - goto free_boot; - } + if (IS_ERR(desc)) + return PTR_ERR(desc); bg->rst_gpio = desc; fwl = firmware_upload_register(THIS_MODULE, &bg->sd->dev, "cc1352p7", &cc1352_bootloader_ops, bg); - if (IS_ERR(fwl)) { - ret = PTR_ERR(fwl); - goto free_reset; - } + if (IS_ERR(fwl)) + return PTR_ERR(fwl); bg->fwl = fwl; return 0; - -free_reset: - devm_gpiod_put(&bg->sd->dev, bg->rst_gpio); - bg->rst_gpio = NULL; -free_boot: - devm_gpiod_put(&bg->sd->dev, bg->bootloader_backdoor_gpio); - bg->bootloader_backdoor_gpio = NULL; - return ret; } static void gb_fw_deinit(struct gb_beagleplay *bg) diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h index 72f624af4686..75ef54b60f75 100644 --- a/drivers/iio/accel/adxl313.h +++ b/drivers/iio/accel/adxl313.h @@ -18,10 +18,14 @@ #define ADXL313_REG_SOFT_RESET 0x18 #define ADXL313_REG_OFS_AXIS(index) (0x1E + (index)) #define ADXL313_REG_THRESH_ACT 0x24 +#define ADXL313_REG_THRESH_INACT 0x25 +#define ADXL313_REG_TIME_INACT 0x26 #define ADXL313_REG_ACT_INACT_CTL 0x27 #define ADXL313_REG_BW_RATE 0x2C #define ADXL313_REG_POWER_CTL 0x2D +#define ADXL313_REG_INT_ENABLE 0x2E #define ADXL313_REG_INT_MAP 0x2F +#define ADXL313_REG_INT_SOURCE 0x30 #define ADXL313_REG_DATA_FORMAT 0x31 #define ADXL313_REG_DATA_AXIS(index) (0x32 + ((index) * 2)) #define ADXL313_REG_FIFO_CTL 0x38 @@ -36,8 +40,10 @@ #define ADXL313_RATE_MSK GENMASK(3, 0) #define ADXL313_RATE_BASE 6 -#define ADXL313_POWER_CTL_MSK GENMASK(3, 2) -#define ADXL313_MEASUREMENT_MODE BIT(3) +#define ADXL313_POWER_CTL_MSK BIT(3) +#define ADXL313_POWER_CTL_INACT_MSK GENMASK(5, 4) +#define ADXL313_POWER_CTL_LINK BIT(5) +#define ADXL313_POWER_CTL_AUTO_SLEEP BIT(4) #define ADXL313_RANGE_MSK GENMASK(1, 0) #define ADXL313_RANGE_MAX 3 @@ -46,6 +52,25 @@ #define ADXL313_SPI_3WIRE BIT(6) #define ADXL313_I2C_DISABLE BIT(6) +#define ADXL313_INT_OVERRUN BIT(0) +#define ADXL313_INT_WATERMARK BIT(1) +#define ADXL313_INT_INACTIVITY BIT(3) +#define ADXL313_INT_ACTIVITY BIT(4) +#define ADXL313_INT_DREADY BIT(7) + +/* FIFO entries: how many values are stored in the FIFO */ +#define ADXL313_REG_FIFO_STATUS_ENTRIES_MSK GENMASK(5, 0) +/* FIFO samples: number of samples needed for watermark (FIFO mode) */ +#define ADXL313_REG_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0) +#define ADXL313_REG_FIFO_CTL_MODE_MSK GENMASK(7, 6) + +#define ADXL313_FIFO_BYPASS 0 +#define ADXL313_FIFO_STREAM 2 + +#define ADXL313_FIFO_SIZE 32 + +#define ADXL313_NUM_AXIS 3 + extern const struct regmap_access_table adxl312_readable_regs_table; extern const struct regmap_access_table adxl313_readable_regs_table; extern const struct regmap_access_table adxl314_readable_regs_table; @@ -54,6 +79,8 @@ extern const struct regmap_access_table adxl312_writable_regs_table; extern const struct regmap_access_table adxl313_writable_regs_table; extern const struct regmap_access_table adxl314_writable_regs_table; +bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg); + enum adxl313_device_type { ADXL312, ADXL313, @@ -64,7 +91,9 @@ struct adxl313_data { struct regmap *regmap; const struct adxl313_chip_info *chip_info; struct mutex lock; /* lock to protect transf_buf */ + u8 watermark; __le16 transf_buf __aligned(IIO_DMA_MINALIGN); + __le16 fifo_buf[ADXL313_NUM_AXIS * ADXL313_FIFO_SIZE + 1]; }; struct adxl313_chip_info { diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c index 46cca10e776f..9f5d4d2cb325 100644 --- a/drivers/iio/accel/adxl313_core.c +++ b/drivers/iio/accel/adxl313_core.c @@ -8,11 +8,62 @@ */ #include <linux/bitfield.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/overflow.h> +#include <linux/property.h> #include <linux/regmap.h> +#include <linux/units.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/kfifo_buf.h> #include "adxl313.h" +#define ADXL313_INT_NONE U8_MAX +#define ADXL313_INT1 1 +#define ADXL313_INT2 2 + +#define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0) + +#define ADXL313_ACT_XYZ_EN GENMASK(6, 4) +#define ADXL313_INACT_XYZ_EN GENMASK(2, 0) + +#define ADXL313_REG_ACT_ACDC_MSK BIT(7) +#define ADXL313_REG_INACT_ACDC_MSK BIT(3) +#define ADXL313_COUPLING_DC 0 +#define ADXL313_COUPLING_AC 1 + +/* activity/inactivity */ +enum adxl313_activity_type { + ADXL313_ACTIVITY, + ADXL313_INACTIVITY, + ADXL313_ACTIVITY_AC, + ADXL313_INACTIVITY_AC, +}; + +static const unsigned int adxl313_act_int_reg[] = { + [ADXL313_ACTIVITY] = ADXL313_INT_ACTIVITY, + [ADXL313_INACTIVITY] = ADXL313_INT_INACTIVITY, + [ADXL313_ACTIVITY_AC] = ADXL313_INT_ACTIVITY, + [ADXL313_INACTIVITY_AC] = ADXL313_INT_INACTIVITY, +}; + +static const unsigned int adxl313_act_thresh_reg[] = { + [ADXL313_ACTIVITY] = ADXL313_REG_THRESH_ACT, + [ADXL313_INACTIVITY] = ADXL313_REG_THRESH_INACT, + [ADXL313_ACTIVITY_AC] = ADXL313_REG_THRESH_ACT, + [ADXL313_INACTIVITY_AC] = ADXL313_REG_THRESH_INACT, +}; + +static const unsigned int adxl313_act_acdc_msk[] = { + [ADXL313_ACTIVITY] = ADXL313_REG_ACT_ACDC_MSK, + [ADXL313_INACTIVITY] = ADXL313_REG_INACT_ACDC_MSK, + [ADXL313_ACTIVITY_AC] = ADXL313_REG_ACT_ACDC_MSK, + [ADXL313_INACTIVITY_AC] = ADXL313_REG_INACT_ACDC_MSK, +}; + static const struct regmap_range adxl312_readable_reg_range[] = { regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0), regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), @@ -46,6 +97,30 @@ const struct regmap_access_table adxl314_readable_regs_table = { }; EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, "IIO_ADXL313"); +bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADXL313_REG_DATA_AXIS(0): + case ADXL313_REG_DATA_AXIS(1): + case ADXL313_REG_DATA_AXIS(2): + case ADXL313_REG_DATA_AXIS(3): + case ADXL313_REG_DATA_AXIS(4): + case ADXL313_REG_DATA_AXIS(5): + case ADXL313_REG_FIFO_STATUS: + case ADXL313_REG_INT_SOURCE: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(adxl313_is_volatile_reg, "IIO_ADXL313"); + +static int adxl313_set_measure_en(struct adxl313_data *data, bool en) +{ + return regmap_assign_bits(data->regmap, ADXL313_REG_POWER_CTL, + ADXL313_POWER_CTL_MSK, en); +} + static int adxl312_check_id(struct device *dev, struct adxl313_data *data) { @@ -171,9 +246,10 @@ static const int adxl313_odr_freqs[][2] = { [9] = { 3200, 0 }, }; -#define ADXL313_ACCEL_CHANNEL(index, axis) { \ +#define ADXL313_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ - .address = index, \ + .scan_index = (index), \ + .address = (reg), \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ @@ -183,14 +259,77 @@ static const int adxl313_odr_freqs[][2] = { .info_mask_shared_by_type_available = \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_type = { \ + .sign = 's', \ .realbits = 13, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ }, \ } +static const struct iio_event_spec adxl313_activity_events[] = { + { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, + { + /* activity, AC bit set */ + .type = IIO_EV_TYPE_MAG_ADAPTIVE, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_event_spec adxl313_inactivity_events[] = { + { + /* inactivity */ + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD), + }, + { + /* inactivity, AC bit set */ + .type = IIO_EV_TYPE_MAG_ADAPTIVE, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD), + }, +}; + +enum adxl313_chans { + chan_x, chan_y, chan_z, +}; + static const struct iio_chan_spec adxl313_channels[] = { - ADXL313_ACCEL_CHANNEL(0, X), - ADXL313_ACCEL_CHANNEL(1, Y), - ADXL313_ACCEL_CHANNEL(2, Z), + ADXL313_ACCEL_CHANNEL(0, chan_x, X), + ADXL313_ACCEL_CHANNEL(1, chan_y, Y), + ADXL313_ACCEL_CHANNEL(2, chan_z, Z), + { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_X_OR_Y_OR_Z, + .scan_index = -1, /* Fake channel for axis OR'ing */ + .event_spec = adxl313_activity_events, + .num_event_specs = ARRAY_SIZE(adxl313_activity_events), + }, + { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_X_AND_Y_AND_Z, + .scan_index = -1, /* Fake channel for axis AND'ing */ + .event_spec = adxl313_inactivity_events, + .num_event_specs = ARRAY_SIZE(adxl313_inactivity_events), + }, +}; + +static const unsigned long adxl313_scan_masks[] = { + BIT(chan_x) | BIT(chan_y) | BIT(chan_z), + 0 }; static int adxl313_set_odr(struct adxl313_data *data, @@ -248,6 +387,230 @@ static int adxl313_read_freq_avail(struct iio_dev *indio_dev, } } +static int adxl313_set_inact_time_s(struct adxl313_data *data, + unsigned int val_s) +{ + unsigned int max_boundary = U8_MAX; /* by register size */ + unsigned int val = min(val_s, max_boundary); + + return regmap_write(data->regmap, ADXL313_REG_TIME_INACT, val); +} + +/** + * adxl313_is_act_inact_ac() - Check if AC coupling is enabled. + * @data: The device data. + * @type: The activity or inactivity type. + * + * Provide a type of activity or inactivity, combined with either AC coupling + * set, or default to DC coupling. This function verifies if the combination is + * currently enabled or not. + * + * Return: if the provided activity type has AC coupling enabled or a negative + * error value. + */ +static int adxl313_is_act_inact_ac(struct adxl313_data *data, + enum adxl313_activity_type type) +{ + unsigned int regval; + bool coupling; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, ®val); + if (ret) + return ret; + + coupling = adxl313_act_acdc_msk[type] & regval; + + switch (type) { + case ADXL313_ACTIVITY: + case ADXL313_INACTIVITY: + return coupling == ADXL313_COUPLING_DC; + case ADXL313_ACTIVITY_AC: + case ADXL313_INACTIVITY_AC: + return coupling == ADXL313_COUPLING_AC; + default: + return -EINVAL; + } +} + +static int adxl313_set_act_inact_ac(struct adxl313_data *data, + enum adxl313_activity_type type, + bool cmd_en) +{ + unsigned int act_inact_ac; + + switch (type) { + case ADXL313_ACTIVITY_AC: + case ADXL313_INACTIVITY_AC: + act_inact_ac = ADXL313_COUPLING_AC && cmd_en; + break; + case ADXL313_ACTIVITY: + case ADXL313_INACTIVITY: + act_inact_ac = ADXL313_COUPLING_DC && cmd_en; + break; + default: + return -EINVAL; + } + + return regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL, + adxl313_act_acdc_msk[type], act_inact_ac); +} + +static int adxl313_is_act_inact_en(struct adxl313_data *data, + enum adxl313_activity_type type) +{ + unsigned int axis_ctrl; + unsigned int regval; + bool int_en; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl); + if (ret) + return ret; + + /* Check if axis for activity are enabled */ + switch (type) { + case ADXL313_ACTIVITY: + case ADXL313_ACTIVITY_AC: + if (!FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl)) + return false; + break; + case ADXL313_INACTIVITY: + case ADXL313_INACTIVITY_AC: + if (!FIELD_GET(ADXL313_INACT_XYZ_EN, axis_ctrl)) + return false; + break; + default: + return -EINVAL; + } + + /* Check if specific interrupt is enabled */ + ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, ®val); + if (ret) + return ret; + + int_en = adxl313_act_int_reg[type] & regval; + if (!int_en) + return false; + + /* Check if configured coupling matches provided type */ + return adxl313_is_act_inact_ac(data, type); +} + +static int adxl313_set_act_inact_linkbit(struct adxl313_data *data, bool en) +{ + int act_ac_en, inact_ac_en; + int act_en, inact_en; + + act_en = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY); + if (act_en < 0) + return act_en; + + act_ac_en = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY_AC); + if (act_ac_en < 0) + return act_ac_en; + + inact_en = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY); + if (inact_en < 0) + return inact_en; + + inact_ac_en = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY_AC); + if (inact_ac_en < 0) + return inact_ac_en; + + act_en = act_en || act_ac_en; + + inact_en = inact_en || inact_ac_en; + + return regmap_assign_bits(data->regmap, ADXL313_REG_POWER_CTL, + ADXL313_POWER_CTL_AUTO_SLEEP | ADXL313_POWER_CTL_LINK, + en && act_en && inact_en); +} + +static int adxl313_set_act_inact_en(struct adxl313_data *data, + enum adxl313_activity_type type, + bool cmd_en) +{ + unsigned int axis_ctrl; + unsigned int threshold; + unsigned int inact_time_s; + int ret; + + if (cmd_en) { + /* When turning on, check if threshold is valid */ + ret = regmap_read(data->regmap, adxl313_act_thresh_reg[type], + &threshold); + if (ret) + return ret; + + if (!threshold) /* Just ignore the command if threshold is 0 */ + return 0; + + /* When turning on inactivity, check if inact time is valid */ + if (type == ADXL313_INACTIVITY || type == ADXL313_INACTIVITY_AC) { + ret = regmap_read(data->regmap, + ADXL313_REG_TIME_INACT, + &inact_time_s); + if (ret) + return ret; + + if (!inact_time_s) + return 0; + } + } else { + /* + * When turning off an activity, ensure that the correct + * coupling event is specified. This step helps prevent misuse - + * for example, if an AC-coupled activity is active and the + * current call attempts to turn off a DC-coupled activity, this + * inconsistency should be detected here. + */ + if (adxl313_is_act_inact_ac(data, type) <= 0) + return 0; + } + + /* Start modifying configuration registers */ + ret = adxl313_set_measure_en(data, false); + if (ret) + return ret; + + /* Enable axis according to the command */ + switch (type) { + case ADXL313_ACTIVITY: + case ADXL313_ACTIVITY_AC: + axis_ctrl = ADXL313_ACT_XYZ_EN; + break; + case ADXL313_INACTIVITY: + case ADXL313_INACTIVITY_AC: + axis_ctrl = ADXL313_INACT_XYZ_EN; + break; + default: + return -EINVAL; + } + ret = regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL, + axis_ctrl, cmd_en); + if (ret) + return ret; + + /* Update AC/DC-coupling according to the command */ + ret = adxl313_set_act_inact_ac(data, type, cmd_en); + if (ret) + return ret; + + /* Enable the interrupt line, according to the command */ + ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_ENABLE, + adxl313_act_int_reg[type], cmd_en); + if (ret) + return ret; + + /* Set link-bit and auto-sleep only when ACT and INACT are enabled */ + ret = adxl313_set_act_inact_linkbit(data, cmd_en); + if (ret) + return ret; + + return adxl313_set_measure_en(data, true); +} + static int adxl313_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -321,10 +684,474 @@ static int adxl313_write_raw(struct iio_dev *indio_dev, } } +static int adxl313_read_mag_config(struct adxl313_data *data, + enum iio_event_direction dir, + enum adxl313_activity_type type_act, + enum adxl313_activity_type type_inact) +{ + switch (dir) { + case IIO_EV_DIR_RISING: + return !!adxl313_is_act_inact_en(data, type_act); + case IIO_EV_DIR_FALLING: + return !!adxl313_is_act_inact_en(data, type_inact); + default: + return -EINVAL; + } +} + +static int adxl313_write_mag_config(struct adxl313_data *data, + enum iio_event_direction dir, + enum adxl313_activity_type type_act, + enum adxl313_activity_type type_inact, + bool state) +{ + switch (dir) { + case IIO_EV_DIR_RISING: + return adxl313_set_act_inact_en(data, type_act, state); + case IIO_EV_DIR_FALLING: + return adxl313_set_act_inact_en(data, type_inact, state); + default: + return -EINVAL; + } +} + +static int adxl313_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 adxl313_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_MAG: + return adxl313_read_mag_config(data, dir, + ADXL313_ACTIVITY, + ADXL313_INACTIVITY); + case IIO_EV_TYPE_MAG_ADAPTIVE: + return adxl313_read_mag_config(data, dir, + ADXL313_ACTIVITY_AC, + ADXL313_INACTIVITY_AC); + default: + return -EINVAL; + } +} + +static int adxl313_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct adxl313_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_MAG: + return adxl313_write_mag_config(data, dir, + ADXL313_ACTIVITY, + ADXL313_INACTIVITY, + state); + case IIO_EV_TYPE_MAG_ADAPTIVE: + return adxl313_write_mag_config(data, dir, + ADXL313_ACTIVITY_AC, + ADXL313_INACTIVITY_AC, + state); + default: + return -EINVAL; + } +} + +static int adxl313_read_mag_value(struct adxl313_data *data, + enum iio_event_direction dir, + enum iio_event_info info, + enum adxl313_activity_type type_act, + enum adxl313_activity_type type_inact, + int *val, int *val2) +{ + unsigned int threshold; + unsigned int period; + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = regmap_read(data->regmap, + adxl313_act_thresh_reg[type_act], + &threshold); + if (ret) + return ret; + *val = threshold * 15625; + *val2 = MICRO; + return IIO_VAL_FRACTIONAL; + case IIO_EV_DIR_FALLING: + ret = regmap_read(data->regmap, + adxl313_act_thresh_reg[type_inact], + &threshold); + if (ret) + return ret; + *val = threshold * 15625; + *val2 = MICRO; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + ret = regmap_read(data->regmap, ADXL313_REG_TIME_INACT, + &period); + if (ret) + return ret; + *val = period; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int adxl313_write_mag_value(struct adxl313_data *data, + enum iio_event_direction dir, + enum iio_event_info info, + enum adxl313_activity_type type_act, + enum adxl313_activity_type type_inact, + int val, int val2) +{ + unsigned int regval; + + switch (info) { + case IIO_EV_INFO_VALUE: + /* Scale factor 15.625 mg/LSB */ + regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625); + switch (dir) { + case IIO_EV_DIR_RISING: + return regmap_write(data->regmap, + adxl313_act_thresh_reg[type_act], + regval); + case IIO_EV_DIR_FALLING: + return regmap_write(data->regmap, + adxl313_act_thresh_reg[type_inact], + regval); + default: + return -EINVAL; + } + case IIO_EV_INFO_PERIOD: + return adxl313_set_inact_time_s(data, val); + default: + return -EINVAL; + } +} + +static int adxl313_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 adxl313_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_MAG: + return adxl313_read_mag_value(data, dir, info, + ADXL313_ACTIVITY, + ADXL313_INACTIVITY, + val, val2); + case IIO_EV_TYPE_MAG_ADAPTIVE: + return adxl313_read_mag_value(data, dir, info, + ADXL313_ACTIVITY_AC, + ADXL313_INACTIVITY_AC, + val, val2); + default: + return -EINVAL; + } +} + +static int adxl313_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 adxl313_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_MAG: + return adxl313_write_mag_value(data, dir, info, + ADXL313_ACTIVITY, + ADXL313_INACTIVITY, + val, val2); + case IIO_EV_TYPE_MAG_ADAPTIVE: + return adxl313_write_mag_value(data, dir, info, + ADXL313_ACTIVITY_AC, + ADXL313_INACTIVITY_AC, + val, val2); + default: + return -EINVAL; + } +} + +static int adxl313_set_watermark(struct iio_dev *indio_dev, unsigned int value) +{ + struct adxl313_data *data = iio_priv(indio_dev); + int ret; + + value = min(value, ADXL313_FIFO_SIZE - 1); + + ret = adxl313_set_measure_en(data, false); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADXL313_REG_FIFO_CTL, + ADXL313_REG_FIFO_CTL_MODE_MSK, value); + if (ret) + return ret; + + data->watermark = value; + + ret = regmap_set_bits(data->regmap, ADXL313_REG_INT_ENABLE, + ADXL313_INT_WATERMARK); + if (ret) + return ret; + + return adxl313_set_measure_en(data, true); +} + +static int adxl313_get_samples(struct adxl313_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL313_REG_FIFO_STATUS, ®val); + if (ret) + return ret; + + return FIELD_GET(ADXL313_REG_FIFO_STATUS_ENTRIES_MSK, regval); +} + +static int adxl313_fifo_transfer(struct adxl313_data *data, int samples) +{ + unsigned int i; + int ret; + + for (i = 0; i < samples; i++) { + ret = regmap_bulk_read(data->regmap, ADXL313_REG_XYZ_BASE, + data->fifo_buf + (i * ADXL313_NUM_AXIS), + sizeof(data->fifo_buf[0]) * ADXL313_NUM_AXIS); + if (ret) + return ret; + } + + return 0; +} + +/** + * adxl313_fifo_reset() - Reset the FIFO and interrupt status registers. + * @data: The device data. + * + * Reset the FIFO status registers. Reading out status registers clears the + * FIFO and interrupt configuration. Thus do not evaluate regmap return values. + * Ignore particular read register content. Register content is not processed + * any further. Therefore the function returns void. + */ +static void adxl313_fifo_reset(struct adxl313_data *data) +{ + unsigned int regval; + int samples; + + adxl313_set_measure_en(data, false); + + samples = adxl313_get_samples(data); + if (samples > 0) + adxl313_fifo_transfer(data, samples); + + regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, ®val); + + adxl313_set_measure_en(data, true); +} + +static int adxl313_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl313_data *data = iio_priv(indio_dev); + int ret; + + /* Set FIFO modes with measurement turned off, according to datasheet */ + ret = adxl313_set_measure_en(data, false); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL, + FIELD_PREP(ADXL313_REG_FIFO_CTL_SAMPLES_MSK, data->watermark) | + FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_STREAM)); + if (ret) + return ret; + + return adxl313_set_measure_en(data, true); +} + +static int adxl313_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl313_data *data = iio_priv(indio_dev); + int ret; + + ret = adxl313_set_measure_en(data, false); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL, + FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_BYPASS)); + + ret = regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0); + if (ret) + return ret; + + return adxl313_set_measure_en(data, true); +} + +static const struct iio_buffer_setup_ops adxl313_buffer_ops = { + .postenable = adxl313_buffer_postenable, + .predisable = adxl313_buffer_predisable, +}; + +static int adxl313_fifo_push(struct iio_dev *indio_dev, int samples) +{ + struct adxl313_data *data = iio_priv(indio_dev); + unsigned int i; + int ret; + + ret = adxl313_fifo_transfer(data, samples); + if (ret) + return ret; + + for (i = 0; i < ADXL313_NUM_AXIS * samples; i += ADXL313_NUM_AXIS) + iio_push_to_buffers(indio_dev, &data->fifo_buf[i]); + + return 0; +} + +static int adxl313_push_events(struct iio_dev *indio_dev, int int_stat) +{ + s64 ts = iio_get_time_ns(indio_dev); + struct adxl313_data *data = iio_priv(indio_dev); + unsigned int regval; + int ret = -ENOENT; + + if (FIELD_GET(ADXL313_INT_ACTIVITY, int_stat)) { + ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, ®val); + if (ret) + return ret; + + if (FIELD_GET(ADXL313_REG_ACT_ACDC_MSK, regval)) { + /* AC coupled */ + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG_ADAPTIVE, + IIO_EV_DIR_RISING), + ts); + if (ret) + return ret; + } else { + /* DC coupled, relying on THRESH */ + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + ts); + if (ret) + return ret; + } + } + + if (FIELD_GET(ADXL313_INT_INACTIVITY, int_stat)) { + ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, ®val); + if (ret) + return ret; + + if (FIELD_GET(ADXL313_REG_INACT_ACDC_MSK, regval)) { + /* AC coupled */ + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_AND_Y_AND_Z, + IIO_EV_TYPE_MAG_ADAPTIVE, + IIO_EV_DIR_FALLING), + ts); + if (ret) + return ret; + } else { + /* DC coupled, relying on THRESH */ + ret = iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, + IIO_MOD_X_AND_Y_AND_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_FALLING), + ts); + if (ret) + return ret; + } + } + + return ret; +} + +static irqreturn_t adxl313_irq_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adxl313_data *data = iio_priv(indio_dev); + int samples, int_stat; + + if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat)) + return IRQ_NONE; + + /* + * In cases of sensor events not handled (still not implemented) by + * this driver, the FIFO needs to be drained to become operational + * again. In general the sensor configuration only should issue events + * which were configured by this driver. Anyway a miss-configuration + * easily might end up in a hanging sensor FIFO. + */ + if (adxl313_push_events(indio_dev, int_stat)) + goto err_reset_fifo; + + if (FIELD_GET(ADXL313_INT_WATERMARK, int_stat)) { + samples = adxl313_get_samples(data); + if (samples < 0) + goto err_reset_fifo; + + if (adxl313_fifo_push(indio_dev, samples)) + goto err_reset_fifo; + } + + if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat)) + goto err_reset_fifo; + + return IRQ_HANDLED; + +err_reset_fifo: + adxl313_fifo_reset(data); + + return IRQ_HANDLED; +} + +static int adxl313_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adxl313_data *data = iio_priv(indio_dev); + + if (readval) + return regmap_read(data->regmap, reg, readval); + return regmap_write(data->regmap, reg, writeval); +} + static const struct iio_info adxl313_info = { .read_raw = adxl313_read_raw, .write_raw = adxl313_write_raw, + .read_event_config = adxl313_read_event_config, + .write_event_config = adxl313_write_event_config, + .read_event_value = adxl313_read_event_value, + .write_event_value = adxl313_write_event_value, .read_avail = adxl313_read_freq_avail, + .hwfifo_set_watermark = adxl313_set_watermark, + .debugfs_reg_access = &adxl313_reg_access, }; static int adxl313_setup(struct device *dev, struct adxl313_data *data, @@ -369,9 +1196,20 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data, } /* Enables measurement mode */ - return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL, - ADXL313_POWER_CTL_MSK, - ADXL313_MEASUREMENT_MODE); + return adxl313_set_measure_en(data, true); +} + +static unsigned int adxl313_get_int_type(struct device *dev, int *irq) +{ + *irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1"); + if (*irq > 0) + return ADXL313_INT1; + + *irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); + if (*irq > 0) + return ADXL313_INT2; + + return ADXL313_INT_NONE; } /** @@ -391,7 +1229,9 @@ int adxl313_core_probe(struct device *dev, { struct adxl313_data *data; struct iio_dev *indio_dev; - int ret; + u8 int_line; + u8 int_map_msk; + int irq, ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) @@ -408,6 +1248,7 @@ int adxl313_core_probe(struct device *dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl313_channels; indio_dev->num_channels = ARRAY_SIZE(adxl313_channels); + indio_dev->available_scan_masks = adxl313_scan_masks; ret = adxl313_setup(dev, data, setup); if (ret) { @@ -415,6 +1256,70 @@ int adxl313_core_probe(struct device *dev, return ret; } + int_line = adxl313_get_int_type(dev, &irq); + if (int_line == ADXL313_INT_NONE) { + /* + * FIFO_BYPASSED mode + * + * When no interrupt lines are specified, the driver falls back + * to use the sensor in FIFO_BYPASS mode. This means turning off + * internal FIFO and interrupt generation (since there is no + * line specified). Unmaskable interrupts such as overrun or + * data ready won't interfere. Even that a FIFO_STREAM mode w/o + * connected interrupt line might allow for obtaining raw + * measurements, a fallback to disable interrupts when no + * interrupt lines are connected seems to be the cleaner + * solution. + */ + ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL, + FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, + ADXL313_FIFO_BYPASS)); + if (ret) + return ret; + } else { + /* FIFO_STREAM mode */ + int_map_msk = ADXL313_INT_DREADY | ADXL313_INT_ACTIVITY | + ADXL313_INT_INACTIVITY | ADXL313_INT_WATERMARK | + ADXL313_INT_OVERRUN; + ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_MAP, + int_map_msk, int_line == ADXL313_INT2); + if (ret) + return ret; + + /* + * Reset or configure the registers with reasonable default + * values. As having 0 in most cases may result in undesirable + * behavior if the interrupts are enabled. + */ + ret = regmap_write(data->regmap, ADXL313_REG_ACT_INACT_CTL, 0x00); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADXL313_REG_TIME_INACT, 5); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADXL313_REG_THRESH_INACT, 0x4f); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADXL313_REG_THRESH_ACT, 0x52); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + &adxl313_buffer_ops); + if (ret) + return ret; + + ret = devm_request_threaded_irq(dev, irq, NULL, + &adxl313_irq_handler, + IRQF_SHARED | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, "IIO_ADXL313"); diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c index dfa51860cd83..b67ff0b4dc54 100644 --- a/drivers/iio/accel/adxl313_i2c.c +++ b/drivers/iio/accel/adxl313_i2c.c @@ -21,6 +21,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = { .rd_table = &adxl312_readable_regs_table, .wr_table = &adxl312_writable_regs_table, .max_register = 0x39, + .volatile_reg = adxl313_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }, [ADXL313] = { .reg_bits = 8, @@ -28,6 +30,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = { .rd_table = &adxl313_readable_regs_table, .wr_table = &adxl313_writable_regs_table, .max_register = 0x39, + .volatile_reg = adxl313_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }, [ADXL314] = { .reg_bits = 8, @@ -35,6 +39,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = { .rd_table = &adxl314_readable_regs_table, .wr_table = &adxl314_writable_regs_table, .max_register = 0x39, + .volatile_reg = adxl313_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }, }; diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c index ebc5d09f108d..dedb0885c277 100644 --- a/drivers/iio/accel/adxl313_spi.c +++ b/drivers/iio/accel/adxl313_spi.c @@ -24,6 +24,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = { .max_register = 0x39, /* Setting bits 7 and 6 enables multiple-byte read */ .read_flag_mask = BIT(7) | BIT(6), + .volatile_reg = adxl313_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }, [ADXL313] = { .reg_bits = 8, @@ -33,6 +35,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = { .max_register = 0x39, /* Setting bits 7 and 6 enables multiple-byte read */ .read_flag_mask = BIT(7) | BIT(6), + .volatile_reg = adxl313_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }, [ADXL314] = { .reg_bits = 8, @@ -42,6 +46,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = { .max_register = 0x39, /* Setting bits 7 and 6 enables multiple-byte read */ .read_flag_mask = BIT(7) | BIT(6), + .volatile_reg = adxl313_is_volatile_reg, + .cache_type = REGCACHE_MAPLE, }, }; diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 7d482dd595fa..9385affdefe3 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -69,11 +69,10 @@ * BW_RATE bits - Bandwidth and output data rate. The default value is * 0x0A, which translates to a 100 Hz output data rate */ -#define ADXL345_BW_RATE GENMASK(3, 0) +#define ADXL345_BW_RATE_MSK GENMASK(3, 0) #define ADXL345_BW_LOW_POWER BIT(4) #define ADXL345_BASE_RATE_NANO_HZ 97656250LL -#define ADXL345_POWER_CTL_STANDBY 0x00 #define ADXL345_POWER_CTL_WAKEUP GENMASK(1, 0) #define ADXL345_POWER_CTL_SLEEP BIT(2) #define ADXL345_POWER_CTL_MEASURE BIT(3) diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 2e5fdd479e8d..b7dfd0007aa0 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -64,11 +64,75 @@ static const unsigned int adxl345_tap_time_reg[] = { [ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR, }; +enum adxl345_odr { + ADXL345_ODR_0P10HZ = 0, + ADXL345_ODR_0P20HZ, + ADXL345_ODR_0P39HZ, + ADXL345_ODR_0P78HZ, + ADXL345_ODR_1P56HZ, + ADXL345_ODR_3P13HZ, + ADXL345_ODR_6P25HZ, + ADXL345_ODR_12P50HZ, + ADXL345_ODR_25HZ, + ADXL345_ODR_50HZ, + ADXL345_ODR_100HZ, + ADXL345_ODR_200HZ, + ADXL345_ODR_400HZ, + ADXL345_ODR_800HZ, + ADXL345_ODR_1600HZ, + ADXL345_ODR_3200HZ, +}; + +enum adxl345_range { + ADXL345_2G_RANGE = 0, + ADXL345_4G_RANGE, + ADXL345_8G_RANGE, + ADXL345_16G_RANGE, +}; + +/* Certain features recommend 12.5 Hz - 400 Hz ODR */ +static const int adxl345_odr_tbl[][2] = { + [ADXL345_ODR_0P10HZ] = { 0, 97000 }, + [ADXL345_ODR_0P20HZ] = { 0, 195000 }, + [ADXL345_ODR_0P39HZ] = { 0, 390000 }, + [ADXL345_ODR_0P78HZ] = { 0, 781000 }, + [ADXL345_ODR_1P56HZ] = { 1, 562000 }, + [ADXL345_ODR_3P13HZ] = { 3, 125000 }, + [ADXL345_ODR_6P25HZ] = { 6, 250000 }, + [ADXL345_ODR_12P50HZ] = { 12, 500000 }, + [ADXL345_ODR_25HZ] = { 25, 0 }, + [ADXL345_ODR_50HZ] = { 50, 0 }, + [ADXL345_ODR_100HZ] = { 100, 0 }, + [ADXL345_ODR_200HZ] = { 200, 0 }, + [ADXL345_ODR_400HZ] = { 400, 0 }, + [ADXL345_ODR_800HZ] = { 800, 0 }, + [ADXL345_ODR_1600HZ] = { 1600, 0 }, + [ADXL345_ODR_3200HZ] = { 3200, 0 }, +}; + +/* + * Full resolution frequency table: + * (g * 2 * 9.80665) / (2^(resolution) - 1) + * + * resolution := 13 (full) + * g := 2|4|8|16 + * + * 2g at 13bit: 0.004789 + * 4g at 13bit: 0.009578 + * 8g at 13bit: 0.019156 + * 16g at 16bit: 0.038312 + */ +static const int adxl345_fullres_range_tbl[][2] = { + [ADXL345_2G_RANGE] = { 0, 4789 }, + [ADXL345_4G_RANGE] = { 0, 9578 }, + [ADXL345_8G_RANGE] = { 0, 19156 }, + [ADXL345_16G_RANGE] = { 0, 38312 }, +}; + struct adxl345_state { const struct adxl345_chip_info *info; struct regmap *regmap; bool fifo_delay; /* delay: delay is needed for SPI */ - int irq; u8 watermark; u8 fifo_mode; @@ -79,7 +143,7 @@ struct adxl345_state { __le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN); }; -static struct iio_event_spec adxl345_events[] = { +static const struct iio_event_spec adxl345_events[] = { { /* single tap */ .type = IIO_EV_TYPE_GESTURE, @@ -107,6 +171,8 @@ static struct iio_event_spec adxl345_events[] = { BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = (index), \ .scan_type = { \ .sign = 's', \ @@ -167,9 +233,8 @@ EXPORT_SYMBOL_NS_GPL(adxl345_is_volatile_reg, "IIO_ADXL345"); */ static int adxl345_set_measure_en(struct adxl345_state *st, bool en) { - unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY; - - return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val); + return regmap_assign_bits(st->regmap, ADXL345_REG_POWER_CTL, + ADXL345_POWER_CTL_MEASURE, en); } /* tap */ @@ -383,14 +448,82 @@ static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int, return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us); } +static int adxl345_find_odr(struct adxl345_state *st, int val, + int val2, enum adxl345_odr *odr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adxl345_odr_tbl); i++) { + if (val == adxl345_odr_tbl[i][0] && + val2 == adxl345_odr_tbl[i][1]) { + *odr = i; + return 0; + } + } + + return -EINVAL; +} + +static int adxl345_set_odr(struct adxl345_state *st, enum adxl345_odr odr) +{ + return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE, + ADXL345_BW_RATE_MSK, + FIELD_PREP(ADXL345_BW_RATE_MSK, odr)); +} + +static int adxl345_find_range(struct adxl345_state *st, int val, int val2, + enum adxl345_range *range) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adxl345_fullres_range_tbl); i++) { + if (val == adxl345_fullres_range_tbl[i][0] && + val2 == adxl345_fullres_range_tbl[i][1]) { + *range = i; + return 0; + } + } + + return -EINVAL; +} + +static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range) +{ + return regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT, + ADXL345_DATA_FORMAT_RANGE, + FIELD_PREP(ADXL345_DATA_FORMAT_RANGE, range)); +} + +static int adxl345_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)adxl345_fullres_range_tbl; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(adxl345_fullres_range_tbl) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)adxl345_odr_tbl; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = ARRAY_SIZE(adxl345_odr_tbl) * 2; + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + static int adxl345_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct adxl345_state *st = iio_priv(indio_dev); __le16 accel; - long long samp_freq_nhz; unsigned int regval; + enum adxl345_odr odr; + enum adxl345_range range; int ret; switch (mask) { @@ -409,8 +542,12 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, *val = sign_extend32(le16_to_cpu(accel), 12); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = st->info->uscale; + ret = regmap_read(st->regmap, ADXL345_REG_DATA_FORMAT, ®val); + if (ret) + return ret; + range = FIELD_GET(ADXL345_DATA_FORMAT_RANGE, regval); + *val = adxl345_fullres_range_tbl[range][0]; + *val2 = adxl345_fullres_range_tbl[range][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_CALIBBIAS: ret = regmap_read(st->regmap, @@ -428,12 +565,10 @@ static int adxl345_read_raw(struct iio_dev *indio_dev, ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, ®val); if (ret) return ret; - - samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ << - (regval & ADXL345_BW_RATE); - *val = div_s64_rem(samp_freq_nhz, NANOHZ_PER_HZ, val2); - - return IIO_VAL_INT_PLUS_NANO; + odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval); + *val = adxl345_odr_tbl[odr][0]; + *val2 = adxl345_odr_tbl[odr][1]; + return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; @@ -444,7 +579,13 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct adxl345_state *st = iio_priv(indio_dev); - s64 n; + enum adxl345_range range; + enum adxl345_odr odr; + int ret; + + ret = adxl345_set_measure_en(st, false); + if (ret) + return ret; switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: @@ -452,20 +593,35 @@ static int adxl345_write_raw(struct iio_dev *indio_dev, * 8-bit resolution at +/- 2g, that is 4x accel data scale * factor */ - return regmap_write(st->regmap, - ADXL345_REG_OFS_AXIS(chan->address), - val / 4); + ret = regmap_write(st->regmap, + ADXL345_REG_OFS_AXIS(chan->address), + val / 4); + if (ret) + return ret; + break; case IIO_CHAN_INFO_SAMP_FREQ: - n = div_s64(val * NANOHZ_PER_HZ + val2, - ADXL345_BASE_RATE_NANO_HZ); + ret = adxl345_find_odr(st, val, val2, &odr); + if (ret) + return ret; - return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE, - ADXL345_BW_RATE, - clamp_val(ilog2(n), 0, - ADXL345_BW_RATE)); + ret = adxl345_set_odr(st, odr); + if (ret) + return ret; + break; + case IIO_CHAN_INFO_SCALE: + ret = adxl345_find_range(st, val, val2, &range); + if (ret) + return ret; + + ret = adxl345_set_range(st, range); + if (ret) + return ret; + break; + default: + return -EINVAL; } - return -EINVAL; + return adxl345_set_measure_en(st, true); } static int adxl345_read_event_config(struct iio_dev *indio_dev, @@ -552,15 +708,15 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_EV_INFO_TIMEOUT: *val = st->tap_duration_us; - *val2 = 1000000; + *val2 = MICRO; return IIO_VAL_FRACTIONAL; case IIO_EV_INFO_RESET_TIMEOUT: *val = st->tap_window_us; - *val2 = 1000000; + *val2 = MICRO; return IIO_VAL_FRACTIONAL; case IIO_EV_INFO_TAP2_MIN_DELAY: *val = st->tap_latent_us; - *val2 = 1000000; + *val2 = MICRO; return IIO_VAL_FRACTIONAL; default: return -EINVAL; @@ -653,8 +809,10 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - return IIO_VAL_INT_PLUS_NANO; + return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -667,19 +825,6 @@ static void adxl345_powerdown(void *ptr) adxl345_set_measure_en(st, false); } -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( -"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200" -); - -static struct attribute *adxl345_attrs[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - NULL -}; - -static const struct attribute_group adxl345_attrs_group = { - .attrs = adxl345_attrs, -}; - static int adxl345_set_fifo(struct adxl345_state *st) { unsigned int intio; @@ -740,15 +885,12 @@ static int adxl345_get_samples(struct adxl345_state *st) */ static int adxl345_fifo_transfer(struct adxl345_state *st, int samples) { - size_t count; int i, ret = 0; - /* count is the 3x the fifo_buf element size, hence 6B */ - count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS; for (i = 0; i < samples; i++) { - /* read 3x 2 byte elements from base address into next fifo_buf position */ ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE, - st->fifo_buf + (i * count / 2), count); + st->fifo_buf + (i * ADXL345_DIRS), + sizeof(st->fifo_buf[0]) * ADXL345_DIRS); if (ret) return ret; @@ -931,9 +1073,9 @@ err: } static const struct iio_info adxl345_info = { - .attrs = &adxl345_attrs_group, .read_raw = adxl345_read_raw, .write_raw = adxl345_write_raw, + .read_avail = adxl345_read_avail, .write_raw_get_fmt = adxl345_write_raw_get_fmt, .read_event_config = adxl345_read_event_config, .write_event_config = adxl345_write_event_config, @@ -943,6 +1085,19 @@ static const struct iio_info adxl345_info = { .hwfifo_set_watermark = adxl345_set_watermark, }; +static int adxl345_get_int_line(struct device *dev, int *irq) +{ + *irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1"); + if (*irq > 0) + return ADXL345_INT1; + + *irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); + if (*irq > 0) + return ADXL345_INT2; + + return ADXL345_INT_NONE; +} + /** * adxl345_core_probe() - Probe and setup for the accelerometer. * @dev: Driver model representation of the device @@ -973,6 +1128,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, ADXL345_DATA_FORMAT_FULL_RES | ADXL345_DATA_FORMAT_SELF_TEST); unsigned int tap_threshold; + int irq; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); @@ -999,6 +1155,19 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); indio_dev->available_scan_masks = adxl345_scan_masks; + /* + * Using I2C at 100kHz would limit the maximum ODR to 200Hz, operation + * at an output rate above the recommended maximum may result in + * undesired behavior. + */ + ret = adxl345_set_odr(st, ADXL345_ODR_200HZ); + if (ret) + return ret; + + ret = adxl345_set_range(st, ADXL345_16G_RANGE); + if (ret) + return ret; + /* Reset interrupts at start up */ ret = regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, 0x00); if (ret) @@ -1044,23 +1213,16 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; - st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1"); - if (st->irq < 0) { - intio = ADXL345_INT2; - st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); - if (st->irq < 0) - intio = ADXL345_INT_NONE; - } - + intio = adxl345_get_int_line(dev, &irq); if (intio != ADXL345_INT_NONE) { /* - * Any bits set to 0 in the INT map register send their respective - * interrupts to the INT1 pin, whereas bits set to 1 send their respective - * interrupts to the INT2 pin. The intio shall convert this accordingly. + * In the INT map register, bits set to 0 route their + * corresponding interrupts to the INT1 pin, while bits set to 1 + * route them to the INT2 pin. The intio should handle this + * mapping accordingly. */ - regval = intio ? 0xff : 0; - - ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, regval); + ret = regmap_assign_bits(st->regmap, ADXL345_REG_INT_MAP, + U8_MAX, intio); if (ret) return ret; @@ -1073,7 +1235,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; - ret = devm_request_threaded_irq(dev, st->irq, NULL, + ret = devm_request_threaded_irq(dev, irq, NULL, &adxl345_irq_handler, IRQF_SHARED | IRQF_ONESHOT, indio_dev->name, indio_dev); diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 961145b50293..46d518a2a029 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -600,10 +600,9 @@ static int adxl372_get_status(struct adxl372_state *st, static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample) { - __be16 axis_sample[3]; + __be16 axis_sample[3] = { }; int i = 0; - memset(axis_sample, 0, 3 * sizeof(__be16)); if (ADXL372_X_AXIS_EN(st->fifo_axis_mask)) axis_sample[i++] = sample[0]; if (ADXL372_Y_AXIS_EN(st->fifo_axis_mask)) diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 93a868678722..4fccbcb76e04 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -29,9 +29,6 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> -#define BMA180_DRV_NAME "bma180" -#define BMA180_IRQ_NAME "bma180_event" - enum chip_ids { BMA023, BMA150, diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 744a034bb8b5..be5fbb0c5d29 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -25,9 +25,6 @@ #include "bmc150-accel.h" -#define BMC150_ACCEL_DRV_NAME "bmc150_accel" -#define BMC150_ACCEL_IRQ_NAME "bmc150_accel_event" - #define BMC150_ACCEL_REG_CHIP_ID 0x00 #define BMC150_ACCEL_REG_INT_STATUS_2 0x0B @@ -1706,7 +1703,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, bmc150_accel_irq_handler, bmc150_accel_irq_thread_handler, IRQF_TRIGGER_RISING, - BMC150_ACCEL_IRQ_NAME, + "bmc150_accel_event", indio_dev); if (ret) goto err_buffer_cleanup; diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c index 07dcf5f0599f..39485572a76b 100644 --- a/drivers/iio/accel/kionix-kx022a.c +++ b/drivers/iio/accel/kionix-kx022a.c @@ -5,27 +5,37 @@ * ROHM/KIONIX accelerometer driver */ +#include <linux/array_size.h> +#include <linux/bitmap.h> #include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/errno.h> +#include <linux/export.h> #include <linux/interrupt.h> +#include <linux/math64.h> +#include <linux/minmax.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/mutex.h> #include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/string_choices.h> +#include <linux/sysfs.h> +#include <linux/time64.h> #include <linux/types.h> #include <linux/units.h> #include <linux/iio/iio.h> +#include <linux/iio/buffer.h> #include <linux/iio/sysfs.h> #include <linux/iio/trigger.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +#include <asm/byteorder.h> + #include "kionix-kx022a.h" /* diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 910d7b5716e1..6aefe8221296 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -26,9 +26,6 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/accel/kxcjk_1013.h> -#define KXCJK1013_DRV_NAME "kxcjk1013" -#define KXCJK1013_IRQ_NAME "kxcjk1013_event" - #define KXTF9_REG_HP_XOUT_L 0x00 #define KXTF9_REG_HP_XOUT_H 0x01 #define KXTF9_REG_HP_YOUT_L 0x02 @@ -1464,7 +1461,7 @@ static int kxcjk1013_probe(struct i2c_client *client) kxcjk1013_data_rdy_trig_poll, kxcjk1013_event_handler, IRQF_TRIGGER_RISING, - KXCJK1013_IRQ_NAME, + "kxcjk1013_event", indio_dev); if (ret) goto err_poweroff; @@ -1674,7 +1671,7 @@ MODULE_DEVICE_TABLE(acpi, kx_acpi_match); static struct i2c_driver kxcjk1013_driver = { .driver = { - .name = KXCJK1013_DRV_NAME, + .name = "kxcjk1013", .acpi_match_table = kx_acpi_match, .of_match_table = kxcjk1013_of_match, .pm = pm_ptr(&kxcjk1013_pm_ops), diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index b89bad9e6fe6..02195deada49 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -17,8 +17,6 @@ #include <linux/pm_runtime.h> #include "mma9551_core.h" -#define MMA9551_DRV_NAME "mma9551" -#define MMA9551_IRQ_NAME "mma9551_event" #define MMA9551_GPIO_COUNT 4 /* Tilt application (inclination in IIO terms). */ @@ -422,7 +420,7 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev) ret = devm_request_threaded_irq(dev, data->irqs[i], NULL, mma9551_event_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - MMA9551_IRQ_NAME, indio_dev); + "mma9551_event", indio_dev); if (ret < 0) { dev_err(dev, "request irq %d failed\n", data->irqs[i]); return ret; @@ -592,7 +590,7 @@ MODULE_DEVICE_TABLE(i2c, mma9551_id); static struct i2c_driver mma9551_driver = { .driver = { - .name = MMA9551_DRV_NAME, + .name = "mma9551", .acpi_match_table = mma9551_acpi_match, .pm = pm_ptr(&mma9551_pm_ops), }, diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 1bbe660b254f..f85384a7908f 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -15,9 +15,6 @@ #include <linux/pm_runtime.h> #include "mma9551_core.h" -#define MMA9553_DRV_NAME "mma9553" -#define MMA9553_IRQ_NAME "mma9553_event" - /* Pedometer configuration registers (R/W) */ #define MMA9553_REG_CONF_SLEEPMIN 0x00 #define MMA9553_REG_CONF_SLEEPMAX 0x02 @@ -100,7 +97,7 @@ enum activity_level { ACTIVITY_RUNNING, }; -static struct mma9553_event_info { +static const struct mma9553_event_info { enum iio_chan_type type; enum iio_modifier mod; enum iio_event_direction dir; @@ -155,7 +152,7 @@ static struct mma9553_event_info { #define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info) struct mma9553_event { - struct mma9553_event_info *info; + const struct mma9553_event_info *info; bool enabled; }; @@ -1102,7 +1099,7 @@ static int mma9553_probe(struct i2c_client *client) mma9553_irq_handler, mma9553_event_handler, IRQF_TRIGGER_RISING, - MMA9553_IRQ_NAME, indio_dev); + "mma9553_event", indio_dev); if (ret < 0) { dev_err(&client->dev, "request irq %d failed\n", client->irq); @@ -1230,7 +1227,7 @@ MODULE_DEVICE_TABLE(i2c, mma9553_id); static struct i2c_driver mma9553_driver = { .driver = { - .name = MMA9553_DRV_NAME, + .name = "mma9553", .acpi_match_table = mma9553_acpi_match, .pm = pm_ptr(&mma9553_pm_ops), }, diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c index c31c53abc3d0..3e10225410e8 100644 --- a/drivers/iio/accel/msa311.c +++ b/drivers/iio/accel/msa311.c @@ -897,9 +897,7 @@ static irqreturn_t msa311_buffer_thread(int irq, void *p) struct { __le16 channels[MSA311_SI_Z + 1]; aligned_s64 ts; - } buf; - - memset(&buf, 0, sizeof(buf)); + } buf = { }; mutex_lock(&msa311->lock); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 1075c8ce0e37..ac973d871c8b 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -19,8 +19,6 @@ #include <linux/iio/trigger_consumer.h> #define MXC4005_DRV_NAME "mxc4005" -#define MXC4005_IRQ_NAME "mxc4005_event" -#define MXC4005_REGMAP_NAME "mxc4005_regmap" #define MXC4005_REG_XOUT_UPPER 0x03 #define MXC4005_REG_XOUT_LOWER 0x04 @@ -138,7 +136,7 @@ static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg) } static const struct regmap_config mxc4005_regmap_config = { - .name = MXC4005_REGMAP_NAME, + .name = "mxc4005_regmap", .reg_bits = 8, .val_bits = 8, @@ -493,7 +491,7 @@ static int mxc4005_probe(struct i2c_client *client) NULL, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - MXC4005_IRQ_NAME, + "mxc4005_event", data->dready_trig); if (ret) { dev_err(&client->dev, diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c index a8abda7b2a63..fc3ed84d1933 100644 --- a/drivers/iio/accel/mxc6255.c +++ b/drivers/iio/accel/mxc6255.c @@ -17,7 +17,6 @@ #include <linux/iio/sysfs.h> #define MXC6255_DRV_NAME "mxc6255" -#define MXC6255_REGMAP_NAME "mxc6255_regmap" #define MXC6255_REG_XOUT 0x00 #define MXC6255_REG_YOUT 0x01 @@ -105,7 +104,7 @@ static bool mxc6255_is_readable_reg(struct device *dev, unsigned int reg) } static const struct regmap_config mxc6255_regmap_config = { - .name = MXC6255_REGMAP_NAME, + .name = "mxc6255_regmap", .reg_bits = 8, .val_bits = 8, diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index aabe4491efd7..bfa8a3f5a92f 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -369,23 +369,20 @@ static int sca3000_write_ctrl_reg(struct sca3000_state *st, ret = sca3000_reg_lock_on(st); if (ret < 0) - goto error_ret; + return ret; if (ret) { ret = __sca3000_unlock_reg_lock(st); if (ret) - goto error_ret; + return ret; } /* Set the control select register */ ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, sel); if (ret) - goto error_ret; + return ret; /* Write the actual value into the register */ - ret = sca3000_write_reg(st, SCA3000_REG_CTRL_DATA_ADDR, val); - -error_ret: - return ret; + return sca3000_write_reg(st, SCA3000_REG_CTRL_DATA_ADDR, val); } /** @@ -402,22 +399,20 @@ static int sca3000_read_ctrl_reg(struct sca3000_state *st, ret = sca3000_reg_lock_on(st); if (ret < 0) - goto error_ret; + return ret; if (ret) { ret = __sca3000_unlock_reg_lock(st); if (ret) - goto error_ret; + return ret; } /* Set the control select register */ ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, ctrl_reg); if (ret) - goto error_ret; + return ret; ret = sca3000_read_data_short(st, SCA3000_REG_CTRL_DATA_ADDR, 1); if (ret) - goto error_ret; + return ret; return st->rx[0]; -error_ret: - return ret; } /** @@ -577,7 +572,8 @@ static inline int __sca3000_get_base_freq(struct sca3000_state *st, ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1); if (ret) - goto error_ret; + return ret; + switch (SCA3000_REG_MODE_MODE_MASK & st->rx[0]) { case SCA3000_REG_MODE_MEAS_MODE_NORMAL: *base_freq = info->measurement_mode_freq; @@ -591,7 +587,6 @@ static inline int __sca3000_get_base_freq(struct sca3000_state *st, default: ret = -EINVAL; } -error_ret: return ret; } @@ -834,7 +829,7 @@ static ssize_t sca3000_read_av_freq(struct device *dev, val = st->rx[0]; mutex_unlock(&st->lock); if (ret) - goto error_ret; + return ret; switch (val & SCA3000_REG_MODE_MODE_MASK) { case SCA3000_REG_MODE_MEAS_MODE_NORMAL: @@ -857,8 +852,6 @@ static ssize_t sca3000_read_av_freq(struct device *dev, break; } return len; -error_ret: - return ret; } /* diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c index 67416a406e2f..bda370c0f660 100644 --- a/drivers/iio/accel/sca3300.c +++ b/drivers/iio/accel/sca3300.c @@ -20,8 +20,6 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> -#define SCA3300_ALIAS "sca3300" - #define SCA3300_CRC8_POLYNOMIAL 0x1d /* Device mode register */ @@ -674,7 +672,7 @@ MODULE_DEVICE_TABLE(spi, sca3300_ids); static struct spi_driver sca3300_driver = { .driver = { - .name = SCA3300_ALIAS, + .name = "sca3300", .of_match_table = sca3300_dt_ids, }, .probe = sca3300_probe, diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index dfac2e44191f..89569ce221d7 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -46,7 +46,6 @@ #define STK8312_ALL_CHANNEL_SIZE 3 #define STK8312_DRIVER_NAME "stk8312" -#define STK8312_IRQ_NAME "stk8312_event" /* * The accelerometer has two measurement ranges: @@ -543,7 +542,7 @@ static int stk8312_probe(struct i2c_client *client) NULL, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - STK8312_IRQ_NAME, + "stk8312_event", indio_dev); if (ret < 0) { dev_err(&client->dev, "request irq %d failed\n", diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 05d4fd540eb2..c1d7e7dcb09b 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -42,7 +42,6 @@ #define STK8BA50_ALL_CHANNEL_SIZE 6 #define STK8BA50_DRIVER_NAME "stk8ba50" -#define STK8BA50_IRQ_NAME "stk8ba50_event" #define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069" @@ -436,7 +435,7 @@ static int stk8ba50_probe(struct i2c_client *client) NULL, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - STK8BA50_IRQ_NAME, + "stk8ba50_event", indio_dev); if (ret < 0) { dev_err(&client->dev, "request irq %d failed\n", diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ea3ba1397392..6de2abad0197 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -22,7 +22,9 @@ config AB8500_GPADC config AD_SIGMA_DELTA tristate select IIO_BUFFER + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD config AD4000 tristate "Analog Devices AD4000 ADC Driver" @@ -55,6 +57,20 @@ config AD4030 To compile this driver as a module, choose M here: the module will be called ad4030. +config AD4080 + tristate "Analog Devices AD4080 high speed ADC" + depends on SPI + select REGMAP_SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD4080 + high speed, low noise, low distortion, 20-bit, Easy Drive, + successive approximation register (SAR) analog-to-digital + converter (ADC). Supports iio_backended devices for AD4080. + + To compile this driver as a module, choose M here: the module will be + called ad4080. + config AD4130 tristate "Analog Device AD4130 ADC Driver" depends on SPI @@ -70,6 +86,22 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. + +config AD4170_4 + tristate "Analog Device AD4170-4 ADC Driver" + depends on SPI + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on COMMON_CLK + depends on GPIOLIB + help + Say yes here to build support for Analog Devices AD4170-4 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4170-4. + config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI @@ -252,6 +284,16 @@ config AD7380 To compile this driver as a module, choose M here: the module will be called ad7380. +config AD7405 + tristate "Analog Device AD7405 ADC Driver" + depends on IIO_BACKEND + help + Say yes here to build support for Analog Devices AD7405, ADUM7701, + ADUM7702, ADUM7703 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7405. + config AD7476 tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD and TI" depends on SPI @@ -330,6 +372,7 @@ config AD7766 config AD7768_1 tristate "Analog Devices AD7768-1 ADC driver" depends on SPI + select REGULATOR select REGMAP_SPI select IIO_BUFFER select IIO_TRIGGER diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 09ae6edb2650..1c6ca5fd4b6d 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -10,7 +10,9 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o obj-$(CONFIG_AD4000) += ad4000.o obj-$(CONFIG_AD4030) += ad4030.o +obj-$(CONFIG_AD4080) += ad4080.o obj-$(CONFIG_AD4130) += ad4130.o +obj-$(CONFIG_AD4170_4) += ad4170-4.o obj-$(CONFIG_AD4695) += ad4695.o obj-$(CONFIG_AD4851) += ad4851.o obj-$(CONFIG_AD7091R) += ad7091r-base.o @@ -26,6 +28,7 @@ obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7292) += ad7292.o obj-$(CONFIG_AD7298) += ad7298.o obj-$(CONFIG_AD7380) += ad7380.o +obj-$(CONFIG_AD7405) += ad7405.o obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c index 5609a7845b6f..fd3d79fca785 100644 --- a/drivers/iio/adc/ad4000.c +++ b/drivers/iio/adc/ad4000.c @@ -554,7 +554,7 @@ static void ad4000_fill_scale_tbl(struct ad4000_state *st, val = mult_frac(st->vref_mv, MICRO, st->gain_milli); /* Would multiply by NANO here but we multiplied by extra MILLI */ - tmp2 = shift_right((u64)val * MICRO, scale_bits); + tmp2 = (u64)val * MICRO >> scale_bits; tmp0 = div_s64_rem(tmp2, NANO, &tmp1); /* Store scale for when span compression is disabled */ diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c new file mode 100644 index 000000000000..6e61787ed321 --- /dev/null +++ b/drivers/iio/adc/ad4080.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4080 SPI ADC driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iio/backend.h> +#include <linux/iio/iio.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <linux/units.h> + +/* Register Definition */ +#define AD4080_REG_INTERFACE_CONFIG_A 0x00 +#define AD4080_REG_INTERFACE_CONFIG_B 0x01 +#define AD4080_REG_DEVICE_CONFIG 0x02 +#define AD4080_REG_CHIP_TYPE 0x03 +#define AD4080_REG_PRODUCT_ID_L 0x04 +#define AD4080_REG_PRODUCT_ID_H 0x05 +#define AD4080_REG_CHIP_GRADE 0x06 +#define AD4080_REG_SCRATCH_PAD 0x0A +#define AD4080_REG_SPI_REVISION 0x0B +#define AD4080_REG_VENDOR_L 0x0C +#define AD4080_REG_VENDOR_H 0x0D +#define AD4080_REG_STREAM_MODE 0x0E +#define AD4080_REG_TRANSFER_CONFIG 0x0F +#define AD4080_REG_INTERFACE_CONFIG_C 0x10 +#define AD4080_REG_INTERFACE_STATUS_A 0x11 +#define AD4080_REG_DEVICE_STATUS 0x14 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_A 0x15 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_B 0x16 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_C 0x17 +#define AD4080_REG_PWR_CTRL 0x18 +#define AD4080_REG_GPIO_CONFIG_A 0x19 +#define AD4080_REG_GPIO_CONFIG_B 0x1A +#define AD4080_REG_GPIO_CONFIG_C 0x1B +#define AD4080_REG_GENERAL_CONFIG 0x1C +#define AD4080_REG_FIFO_WATERMARK_LSB 0x1D +#define AD4080_REG_FIFO_WATERMARK_MSB 0x1E +#define AD4080_REG_EVENT_HYSTERESIS_LSB 0x1F +#define AD4080_REG_EVENT_HYSTERESIS_MSB 0x20 +#define AD4080_REG_EVENT_DETECTION_HI_LSB 0x21 +#define AD4080_REG_EVENT_DETECTION_HI_MSB 0x22 +#define AD4080_REG_EVENT_DETECTION_LO_LSB 0x23 +#define AD4080_REG_EVENT_DETECTION_LO_MSB 0x24 +#define AD4080_REG_OFFSET_LSB 0x25 +#define AD4080_REG_OFFSET_MSB 0x26 +#define AD4080_REG_GAIN_LSB 0x27 +#define AD4080_REG_GAIN_MSB 0x28 +#define AD4080_REG_FILTER_CONFIG 0x29 + +/* AD4080_REG_INTERFACE_CONFIG_A Bit Definition */ +#define AD4080_INTERFACE_CONFIG_A_SW_RESET (BIT(7) | BIT(0)) +#define AD4080_INTERFACE_CONFIG_A_ADDR_ASC BIT(5) +#define AD4080_INTERFACE_CONFIG_A_SDO_ENABLE BIT(4) + +/* AD4080_REG_INTERFACE_CONFIG_B Bit Definition */ +#define AD4080_INTERFACE_CONFIG_B_SINGLE_INST BIT(7) +#define AD4080_INTERFACE_CONFIG_B_SHORT_INST BIT(3) + +/* AD4080_REG_DEVICE_CONFIG Bit Definition */ +#define AD4080_DEVICE_CONFIG_OPERATING_MODES_MSK GENMASK(1, 0) + +/* AD4080_REG_TRANSFER_CONFIG Bit Definition */ +#define AD4080_TRANSFER_CONFIG_KEEP_STREAM_LENGTH_VAL BIT(2) + +/* AD4080_REG_INTERFACE_CONFIG_C Bit Definition */ +#define AD4080_INTERFACE_CONFIG_C_STRICT_REG_ACCESS BIT(5) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_A Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A BIT(6) +#define AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN BIT(4) +#define AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES BIT(2) +#define AD4080_ADC_DATA_INTF_CONFIG_A_DATA_INTF_MODE BIT(0) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_B Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK GENMASK(7, 4) +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_SELF_CLK_MODE BIT(3) +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN BIT(0) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_C Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_C_LVDS_VOD_MSK GENMASK(6, 4) + +/* AD4080_REG_PWR_CTRL Bit Definition */ +#define AD4080_PWR_CTRL_ANA_DIG_LDO_PD BIT(1) +#define AD4080_PWR_CTRL_INTF_LDO_PD BIT(0) + +/* AD4080_REG_GPIO_CONFIG_A Bit Definition */ +#define AD4080_GPIO_CONFIG_A_GPO_1_EN BIT(1) +#define AD4080_GPIO_CONFIG_A_GPO_0_EN BIT(0) + +/* AD4080_REG_GPIO_CONFIG_B Bit Definition */ +#define AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK GENMASK(7, 4) +#define AD4080_GPIO_CONFIG_B_GPIO_0_SEL_MSK GENMASK(3, 0) +#define AD4080_GPIO_CONFIG_B_GPIO_SPI_SDO 0 +#define AD4080_GPIO_CONFIG_B_GPIO_FIFO_FULL 1 +#define AD4080_GPIO_CONFIG_B_GPIO_FIFO_READ_DONE 2 +#define AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY 3 +#define AD4080_GPIO_CONFIG_B_GPIO_H_THRESH 4 +#define AD4080_GPIO_CONFIG_B_GPIO_L_THRESH 5 +#define AD4080_GPIO_CONFIG_B_GPIO_STATUS_ALERT 6 +#define AD4080_GPIO_CONFIG_B_GPIO_GPIO_DATA 7 +#define AD4080_GPIO_CONFIG_B_GPIO_FILTER_SYNC 8 +#define AD4080_GPIO_CONFIG_B_GPIO_EXTERNAL_EVENT 9 + +/* AD4080_REG_FIFO_CONFIG Bit Definition */ +#define AD4080_FIFO_CONFIG_FIFO_MODE_MSK GENMASK(1, 0) + +/* AD4080_REG_FILTER_CONFIG Bit Definition */ +#define AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK GENMASK(6, 3) +#define AD4080_FILTER_CONFIG_FILTER_SEL_MSK GENMASK(1, 0) + +/* Miscellaneous Definitions */ +#define AD4080_SPI_READ BIT(7) +#define AD4080_CHIP_ID GENMASK(2, 0) + +#define AD4080_LVDS_CNV_CLK_CNT_MAX 7 + +#define AD4080_MAX_SAMP_FREQ 40000000 +#define AD4080_MIN_SAMP_FREQ 1250000 + +enum ad4080_filter_type { + FILTER_NONE, + SINC_1, + SINC_5, + SINC_5_COMP +}; + +static const unsigned int ad4080_scale_table[][2] = { + { 6000, 0 }, +}; + +static const char *const ad4080_filter_type_iio_enum[] = { + [FILTER_NONE] = "none", + [SINC_1] = "sinc1", + [SINC_5] = "sinc5", + [SINC_5_COMP] = "sinc5+pf1", +}; + +static const int ad4080_dec_rate_avail[] = { + 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +static const int ad4080_dec_rate_none[] = { 1 }; + +static const char * const ad4080_power_supplies[] = { + "vdd33", "vdd11", "vddldo", "iovdd", "vrefin", +}; + +struct ad4080_chip_info { + const char *name; + unsigned int product_id; + int num_scales; + const unsigned int (*scale_table)[2]; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ad4080_state { + struct regmap *regmap; + struct iio_backend *back; + const struct ad4080_chip_info *info; + /* + * Synchronize access to members the of driver state, and ensure + * atomicity of consecutive regmap operations. + */ + struct mutex lock; + unsigned int num_lanes; + unsigned int dec_rate; + unsigned long clk_rate; + enum ad4080_filter_type filter_type; + bool lvds_cnv_en; +}; + +static const struct regmap_config ad4080_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), + .max_register = 0x29, +}; + +static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4080_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2) +{ + unsigned int tmp; + + tmp = (st->info->scale_table[0][0] * 1000000ULL) >> + st->info->channels[0].scan_type.realbits; + *val = tmp / 1000000; + *val2 = tmp % 1000000; + + return IIO_VAL_INT_PLUS_NANO; +} + +static unsigned int ad4080_get_dec_rate(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4080_state *st = iio_priv(dev); + int ret; + unsigned int data; + + ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + if (ret) + return ret; + + return 1 << (FIELD_GET(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, data) + 1); +} + +static int ad4080_set_dec_rate(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad4080_state *st = iio_priv(dev); + + guard(mutex)(&st->lock); + + if ((st->filter_type >= SINC_5 && mode >= 512) || mode < 2) + return -EINVAL; + + return regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, + FIELD_PREP(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, + (ilog2(mode) - 1))); +} + +static int ad4080_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int dec_rate; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + return ad4080_get_scale(st, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + dec_rate = ad4080_get_dec_rate(indio_dev, chan); + if (dec_rate < 0) + return dec_rate; + if (st->filter_type == SINC_5_COMP) + dec_rate *= 2; + if (st->filter_type) + *val = DIV_ROUND_CLOSEST(st->clk_rate, dec_rate); + else + *val = st->clk_rate; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (st->filter_type == FILTER_NONE) { + *val = 1; + } else { + *val = ad4080_get_dec_rate(indio_dev, chan); + if (*val < 0) + return *val; + } + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4080_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (st->filter_type == FILTER_NONE && val > 1) + return -EINVAL; + + return ad4080_set_dec_rate(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad4080_lvds_sync_write(struct ad4080_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + int ret; + + ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); + if (ret) + return ret; + + ret = iio_backend_interface_data_align(st->back, 10000); + if (ret) + return dev_err_probe(dev, ret, + "Data alignment process failed\n"); + + dev_dbg(dev, "Success: Pattern correct and Locked!\n"); + return regmap_clear_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); +} + +static int ad4080_get_filter_type(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4080_state *st = iio_priv(dev); + unsigned int data; + int ret; + + ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + if (ret) + return ret; + + return FIELD_GET(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, data); +} + +static int ad4080_set_filter_type(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad4080_state *st = iio_priv(dev); + int dec_rate; + int ret; + + guard(mutex)(&st->lock); + + dec_rate = ad4080_get_dec_rate(dev, chan); + if (dec_rate < 0) + return dec_rate; + + if (mode >= SINC_5 && dec_rate >= 512) + return -EINVAL; + + ret = iio_backend_filter_type_set(st->back, mode); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + AD4080_FILTER_CONFIG_FILTER_SEL_MSK, + FIELD_PREP(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, + mode)); + if (ret) + return ret; + + st->filter_type = mode; + + return 0; +} + +static int ad4080_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (st->filter_type) { + case FILTER_NONE: + *vals = ad4080_dec_rate_none; + *length = ARRAY_SIZE(ad4080_dec_rate_none); + break; + default: + *vals = ad4080_dec_rate_avail; + *length = st->filter_type >= SINC_5 ? + (ARRAY_SIZE(ad4080_dec_rate_avail) - 2) : + ARRAY_SIZE(ad4080_dec_rate_avail); + break; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info ad4080_iio_info = { + .debugfs_reg_access = ad4080_reg_access, + .read_raw = ad4080_read_raw, + .write_raw = ad4080_write_raw, + .read_avail = ad4080_read_avail, +}; + +static const struct iio_enum ad4080_filter_type_enum = { + .items = ad4080_filter_type_iio_enum, + .num_items = ARRAY_SIZE(ad4080_filter_type_iio_enum), + .set = ad4080_set_filter_type, + .get = ad4080_get_filter_type, +}; + +static struct iio_chan_spec_ext_info ad4080_ext_info[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad4080_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, + &ad4080_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4080_channel = { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = ad4080_ext_info, + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 20, + .storagebits = 32, + }, +}; + +static const struct ad4080_chip_info ad4080_chip_info = { + .name = "ad4080", + .product_id = AD4080_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 1, + .channels = &ad4080_channel, +}; + +static int ad4080_setup(struct iio_dev *indio_dev) +{ + struct ad4080_state *st = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->regmap); + unsigned int id; + int ret; + + ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + AD4080_INTERFACE_CONFIG_A_SW_RESET); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + AD4080_INTERFACE_CONFIG_A_SDO_ENABLE); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD4080_REG_CHIP_TYPE, &id); + if (ret) + return ret; + + if (id != AD4080_CHIP_ID) + dev_info(dev, "Unrecognized CHIP_ID 0x%X\n", id); + + ret = regmap_set_bits(st->regmap, AD4080_REG_GPIO_CONFIG_A, + AD4080_GPIO_CONFIG_A_GPO_1_EN); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4080_REG_GPIO_CONFIG_B, + FIELD_PREP(AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK, + AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY)); + if (ret) + return ret; + + ret = iio_backend_num_lanes_set(st->back, st->num_lanes); + if (ret) + return ret; + + if (!st->lvds_cnv_en) + return 0; + + /* Set maximum LVDS Data Transfer Latency */ + ret = regmap_update_bits(st->regmap, + AD4080_REG_ADC_DATA_INTF_CONFIG_B, + AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, + FIELD_PREP(AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, + AD4080_LVDS_CNV_CLK_CNT_MAX)); + if (ret) + return ret; + + if (st->num_lanes > 1) { + ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES); + if (ret) + return ret; + } + + ret = regmap_set_bits(st->regmap, + AD4080_REG_ADC_DATA_INTF_CONFIG_B, + AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN); + if (ret) + return ret; + + return ad4080_lvds_sync_write(st); +} + +static int ad4080_properties_parse(struct ad4080_state *st) +{ + struct device *dev = regmap_get_device(st->regmap); + + st->lvds_cnv_en = device_property_read_bool(dev, "adi,lvds-cnv-enable"); + + st->num_lanes = 1; + device_property_read_u32(dev, "adi,num-lanes", &st->num_lanes); + if (!st->num_lanes || st->num_lanes > 2) + return dev_err_probe(dev, -EINVAL, + "Invalid 'adi,num-lanes' value: %u", + st->num_lanes); + + return 0; +} + +static int ad4080_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct device *dev = &spi->dev; + struct ad4080_state *st; + struct clk *clk; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4080_power_supplies), + ad4080_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + st->regmap = devm_regmap_init_spi(spi, &ad4080_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + indio_dev->info = &ad4080_iio_info; + + ret = ad4080_properties_parse(st); + if (ret) + return ret; + + clk = devm_clk_get_enabled(&spi->dev, "cnv"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + st->clk_rate = clk_get_rate(clk); + + 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 = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + ret = ad4080_setup(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad4080_id[] = { + { "ad4080", (kernel_ulong_t)&ad4080_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4080_id); + +static const struct of_device_id ad4080_of_match[] = { + { .compatible = "adi,ad4080", &ad4080_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4080_of_match); + +static struct spi_driver ad4080_driver = { + .driver = { + .name = "ad4080", + .of_match_table = ad4080_of_match, + }, + .probe = ad4080_probe, + .id_table = ad4080_id, +}; +module_spi_driver(ad4080_driver); + +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com"); +MODULE_DESCRIPTION("Analog Devices AD4080"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad4170-4.c b/drivers/iio/adc/ad4170-4.c new file mode 100644 index 000000000000..6cd84d6fb08b --- /dev/null +++ b/drivers/iio/adc/ad4170-4.c @@ -0,0 +1,3027 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Analog Devices AD4170-4 ADC driver + * + * Copyright (C) 2025 Analog Devices, Inc. + * Author: Ana-Maria Cusco <ana-maria.cusco@analog.com> + * Author: Marcelo Schmitt <marcelo.schmitt@analog.com> + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/cleanup.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/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/unaligned.h> +#include <linux/units.h> +#include <linux/util_macros.h> + +/* + * AD4170 registers + * Multibyte register addresses point to the most significant byte which is the + * address to use to get the most significant byte first (address accessed is + * decremented by one for each data byte) + * + * Each register address define follows the AD4170_<REG_NAME>_REG format. + * Each mask follows the AD4170_<REG_NAME>_<FIELD_NAME> format. + * E.g. AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK is for accessing DIG_AUX1_CTRL field + * of PIN_MUXING_REG. + * Each constant follows the AD4170_<REG_NAME>_<FIELD_NAME>_<FUNCTION> format. + * E.g. AD4170_PIN_MUXING_DIG_AUX1_DISABLED is the value written to + * DIG_AUX1_CTRL field of PIN_MUXING register to disable DIG_AUX1 pin. + * Some register names and register field names are shortened versions of + * their datasheet counterpart names to provide better code readability. + */ +#define AD4170_CONFIG_A_REG 0x00 +#define AD4170_DATA_24B_REG 0x1E +#define AD4170_PIN_MUXING_REG 0x69 +#define AD4170_CLOCK_CTRL_REG 0x6B +#define AD4170_ADC_CTRL_REG 0x71 +#define AD4170_CHAN_EN_REG 0x79 +#define AD4170_CHAN_SETUP_REG(x) (0x81 + 4 * (x)) +#define AD4170_CHAN_MAP_REG(x) (0x83 + 4 * (x)) +#define AD4170_MISC_REG(x) (0xC1 + 14 * (x)) +#define AD4170_AFE_REG(x) (0xC3 + 14 * (x)) +#define AD4170_FILTER_REG(x) (0xC5 + 14 * (x)) +#define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) +#define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) +#define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) +#define AD4170_V_BIAS_REG 0x135 +#define AD4170_CURRENT_SRC_REG(x) (0x139 + 2 * (x)) +#define AD4170_GPIO_MODE_REG 0x191 +#define AD4170_GPIO_OUTPUT_REG 0x193 +#define AD4170_GPIO_INPUT_REG 0x195 +#define AD4170_ADC_CTRL_CONT_READ_EXIT_REG 0x200 /* virtual reg */ + +#define AD4170_REG_READ_MASK BIT(14) + +/* AD4170_CONFIG_A_REG - INTERFACE_CONFIG_A REGISTER */ +#define AD4170_SW_RESET_MSK (BIT(7) | BIT(0)) + +/* AD4170_PIN_MUXING_REG */ +#define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) + +/* AD4170_CLOCK_CTRL_REG */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_MSK GENMASK(1, 0) + +/* AD4170_ADC_CTRL_REG */ +#define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) +#define AD4170_ADC_CTRL_CONT_READ_MSK GENMASK(5, 4) +#define AD4170_ADC_CTRL_MODE_MSK GENMASK(3, 0) + +/* AD4170_CHAN_EN_REG */ +#define AD4170_CHAN_EN(ch) BIT(ch) + +/* AD4170_CHAN_SETUP_REG */ +#define AD4170_CHAN_SETUP_SETUP_MSK GENMASK(2, 0) + +/* AD4170_CHAN_MAP_REG */ +#define AD4170_CHAN_MAP_AINP_MSK GENMASK(12, 8) +#define AD4170_CHAN_MAP_AINM_MSK GENMASK(4, 0) + +/* AD4170_MISC_REG */ +#define AD4170_MISC_CHOP_IEXC_MSK GENMASK(15, 14) +#define AD4170_MISC_CHOP_ADC_MSK GENMASK(9, 8) + +/* AD4170_AFE_REG */ +#define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10) +#define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8) +#define AD4170_AFE_REF_SELECT_MSK GENMASK(6, 5) +#define AD4170_AFE_BIPOLAR_MSK BIT(4) +#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) + +/* AD4170_FILTER_REG */ +#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) + +/* AD4170_CURRENT_SRC_REG */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_MSK GENMASK(12, 8) +#define AD4170_CURRENT_SRC_I_OUT_VAL_MSK GENMASK(2, 0) + +/* AD4170_GPIO_MODE_REG */ +#define AD4170_GPIO_MODE_GPIO0_MSK GENMASK(1, 0) +#define AD4170_GPIO_MODE_GPIO1_MSK GENMASK(3, 2) +#define AD4170_GPIO_MODE_GPIO2_MSK GENMASK(5, 4) +#define AD4170_GPIO_MODE_GPIO3_MSK GENMASK(7, 6) + +/* AD4170_GPIO_OUTPUT_REG */ +#define AD4170_GPIO_OUTPUT_GPIO_MSK(x) BIT(x) + +/* AD4170 register constants */ + +/* AD4170_CLOCK_CTRL_REG constants */ +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT 0x0 +#define AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT 0x1 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT 0x2 +#define AD4170_CLOCK_CTRL_CLOCKSEL_EXT_XTAL 0x3 + +/* AD4170_CHAN_MAP_REG constants */ +#define AD4170_CHAN_MAP_AIN(x) (x) +#define AD4170_CHAN_MAP_TEMP_SENSOR 17 +#define AD4170_CHAN_MAP_AVDD_AVSS_P 18 +#define AD4170_CHAN_MAP_AVDD_AVSS_N 18 +#define AD4170_CHAN_MAP_IOVDD_DGND_P 19 +#define AD4170_CHAN_MAP_IOVDD_DGND_N 19 +#define AD4170_CHAN_MAP_AVSS 23 +#define AD4170_CHAN_MAP_DGND 24 +#define AD4170_CHAN_MAP_REFIN1_P 25 +#define AD4170_CHAN_MAP_REFIN1_N 26 +#define AD4170_CHAN_MAP_REFIN2_P 27 +#define AD4170_CHAN_MAP_REFIN2_N 28 +#define AD4170_CHAN_MAP_REFOUT 29 + +/* AD4170_MISC_REG constants */ +#define AD4170_MISC_CHOP_IEXC_PAIR1 0x1 +#define AD4170_MISC_CHOP_IEXC_PAIR2 0x2 +#define AD4170_MISC_CHOP_IEXC_BOTH 0x3 + +/* AD4170_PIN_MUXING_REG constants */ +#define AD4170_PIN_MUXING_DIG_AUX1_DISABLED 0x0 +#define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1 + +/* AD4170_ADC_CTRL_REG constants */ +#define AD4170_ADC_CTRL_MODE_CONT 0x0 +#define AD4170_ADC_CTRL_MODE_SINGLE 0x4 +#define AD4170_ADC_CTRL_MODE_IDLE 0x7 + +#define AD4170_ADC_CTRL_CONT_READ_DISABLE 0x0 +#define AD4170_ADC_CTRL_CONT_READ_ENABLE 0x1 + +/* AD4170_FILTER_REG constants */ +#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0 +#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 +#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 + +/* AD4170_CURRENT_SRC_REG constants */ +#define AD4170_CURRENT_SRC_I_OUT_PIN_AIN(x) (x) +#define AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(x) ((x) + 17) + +/* AD4170_GPIO_MODE_REG constants */ +#define AD4170_GPIO_MODE_GPIO_INPUT 1 +#define AD4170_GPIO_MODE_GPIO_OUTPUT 2 + +/* Device properties and auxiliary constants */ + +#define AD4170_NUM_ANALOG_PINS 9 +#define AD4170_NUM_GPIO_PINS 4 +#define AD4170_MAX_ADC_CHANNELS 16 +#define AD4170_MAX_IIO_CHANNELS (AD4170_MAX_ADC_CHANNELS + 1) +#define AD4170_MAX_ANALOG_PINS 8 +#define AD4170_MAX_SETUPS 8 +#define AD4170_INVALID_SETUP 9 +#define AD4170_SPI_INST_PHASE_LEN 2 +#define AD4170_SPI_MAX_XFER_LEN 6 +#define AD4170_NUM_CURRENT_SRC 4 +#define AD4170_DEFAULT_SAMP_RATE (125 * HZ_PER_KHZ) + +#define AD4170_INT_REF_2_5V 2500000 + +/* Internal and external clock properties */ +#define AD4170_INT_CLOCK_16MHZ (16 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MIN (1 * HZ_PER_MHZ) +#define AD4170_EXT_CLOCK_MHZ_MAX (17 * HZ_PER_MHZ) + +#define AD4170_NUM_PGA_OPTIONS 10 + +/* Digital filter properties */ +#define AD4170_SINC3_MIN_FS 4 +#define AD4170_SINC3_MAX_FS 65532 +#define AD4170_SINC5_MIN_FS 1 +#define AD4170_SINC5_MAX_FS 256 + +#define AD4170_GAIN_REG_DEFAULT 0x555555 + +#define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 + +/* Analog pin functions */ +#define AD4170_PIN_UNASSIGNED 0x00 +#define AD4170_PIN_ANALOG_IN 0x01 +#define AD4170_PIN_CURRENT_OUT 0x02 +#define AD4170_PIN_VBIAS 0x04 + +/* GPIO pin functions */ +#define AD4170_GPIO_UNASSIGNED 0x00 +#define AD4170_GPIO_AC_EXCITATION 0x02 +#define AD4170_GPIO_OUTPUT 0x04 + +/* Current source */ +#define AD4170_CURRENT_SRC_DISABLED 0xFF + +static const unsigned int ad4170_reg_size[] = { + [AD4170_CONFIG_A_REG] = 1, + [AD4170_DATA_24B_REG] = 3, + [AD4170_PIN_MUXING_REG] = 2, + [AD4170_CLOCK_CTRL_REG] = 2, + [AD4170_ADC_CTRL_REG] = 2, + [AD4170_CHAN_EN_REG] = 2, + /* + * CHANNEL_SETUP and CHANNEL_MAP register are all 2 byte size each and + * their addresses are interleaved such that we have CHANNEL_SETUP0 + * address followed by CHANNEL_MAP0 address, followed by CHANNEL_SETUP1, + * and so on until CHANNEL_MAP15. + * Thus, initialize the register size for them only once. + */ + [AD4170_CHAN_SETUP_REG(0) ... AD4170_CHAN_MAP_REG(AD4170_MAX_ADC_CHANNELS - 1)] = 2, + /* + * MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN register addresses are + * also interleaved but MISC, AFE, FILTER, FILTER_FS, OFFSET are 16-bit + * while OFFSET, GAIN are 24-bit registers so we can't init them all to + * the same size. + */ + [AD4170_MISC_REG(0) ... AD4170_FILTER_FS_REG(0)] = 2, + [AD4170_MISC_REG(1) ... AD4170_FILTER_FS_REG(1)] = 2, + [AD4170_MISC_REG(2) ... AD4170_FILTER_FS_REG(2)] = 2, + [AD4170_MISC_REG(3) ... AD4170_FILTER_FS_REG(3)] = 2, + [AD4170_MISC_REG(4) ... AD4170_FILTER_FS_REG(4)] = 2, + [AD4170_MISC_REG(5) ... AD4170_FILTER_FS_REG(5)] = 2, + [AD4170_MISC_REG(6) ... AD4170_FILTER_FS_REG(6)] = 2, + [AD4170_MISC_REG(7) ... AD4170_FILTER_FS_REG(7)] = 2, + [AD4170_OFFSET_REG(0) ... AD4170_GAIN_REG(0)] = 3, + [AD4170_OFFSET_REG(1) ... AD4170_GAIN_REG(1)] = 3, + [AD4170_OFFSET_REG(2) ... AD4170_GAIN_REG(2)] = 3, + [AD4170_OFFSET_REG(3) ... AD4170_GAIN_REG(3)] = 3, + [AD4170_OFFSET_REG(4) ... AD4170_GAIN_REG(4)] = 3, + [AD4170_OFFSET_REG(5) ... AD4170_GAIN_REG(5)] = 3, + [AD4170_OFFSET_REG(6) ... AD4170_GAIN_REG(6)] = 3, + [AD4170_OFFSET_REG(7) ... AD4170_GAIN_REG(7)] = 3, + [AD4170_V_BIAS_REG] = 2, + [AD4170_CURRENT_SRC_REG(0) ... AD4170_CURRENT_SRC_REG(3)] = 2, + [AD4170_GPIO_MODE_REG] = 2, + [AD4170_GPIO_OUTPUT_REG] = 2, + [AD4170_GPIO_INPUT_REG] = 2, + [AD4170_ADC_CTRL_CONT_READ_EXIT_REG] = 0, +}; + +enum ad4170_ref_buf { + AD4170_REF_BUF_PRE, /* Pre-charge referrence buffer */ + AD4170_REF_BUF_FULL, /* Full referrence buffering */ + AD4170_REF_BUF_BYPASS, /* Bypass referrence buffering */ +}; + +/* maps adi,positive/negative-reference-buffer property values to enum */ +static const char * const ad4170_ref_buf_str[] = { + [AD4170_REF_BUF_PRE] = "precharge", + [AD4170_REF_BUF_FULL] = "full", + [AD4170_REF_BUF_BYPASS] = "disabled", +}; + +enum ad4170_ref_select { + AD4170_REF_REFIN1, + AD4170_REF_REFIN2, + AD4170_REF_REFOUT, + AD4170_REF_AVDD, +}; + +enum ad4170_filter_type { + AD4170_SINC5_AVG, + AD4170_SINC5, + AD4170_SINC3, +}; + +enum ad4170_regulator { + AD4170_AVDD_SUP, + AD4170_AVSS_SUP, + AD4170_IOVDD_SUP, + AD4170_REFIN1P_SUP, + AD4170_REFIN1N_SUP, + AD4170_REFIN2P_SUP, + AD4170_REFIN2N_SUP, + AD4170_MAX_SUP, +}; + +static const char *const ad4170_clk_sel[] = { + "ext-clk", "xtal", +}; + +enum ad4170_int_pin_sel { + AD4170_INT_PIN_SDO, + AD4170_INT_PIN_DIG_AUX1, +}; + +static const char * const ad4170_int_pin_names[] = { + [AD4170_INT_PIN_SDO] = "sdo", + [AD4170_INT_PIN_DIG_AUX1] = "dig_aux1", +}; + +static const unsigned int ad4170_sinc3_filt_fs_tbl[] = { + 4, 8, 12, 16, 20, 40, 48, 80, /* 0 - 7 */ + 100, 256, 500, 1000, 5000, 8332, 10000, 25000, /* 8 - 15 */ + 50000, 65532, /* 16 - 17 */ +}; + +#define AD4170_MAX_FS_TBL_SIZE ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) + +static const unsigned int ad4170_sinc5_filt_fs_tbl[] = { + 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256, +}; + +static const unsigned int ad4170_iout_pin_tbl[] = { + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(1), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(2), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(3), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(4), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(5), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(6), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(7), + AD4170_CURRENT_SRC_I_OUT_PIN_AIN(8), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(1), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(2), + AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(3), +}; + +static const unsigned int ad4170_iout_current_ua_tbl[] = { + 0, 10, 50, 100, 250, 500, 1000, 1500, +}; + +enum ad4170_sensor_enum { + AD4170_ADC_SENSOR = 0, + AD4170_WEIGH_SCALE_SENSOR = 1, + AD4170_RTD_SENSOR = 2, + AD4170_THERMOCOUPLE_SENSOR = 3, +}; + +/* maps adi,sensor-type property value to enum */ +static const char * const ad4170_sensor_type[] = { + [AD4170_ADC_SENSOR] = "adc", + [AD4170_WEIGH_SCALE_SENSOR] = "weighscale", + [AD4170_RTD_SENSOR] = "rtd", + [AD4170_THERMOCOUPLE_SENSOR] = "thermocouple", +}; + +struct ad4170_chip_info { + const char *name; +}; + +static const struct ad4170_chip_info ad4170_chip_info = { + .name = "ad4170-4", +}; + +static const struct ad4170_chip_info ad4190_chip_info = { + .name = "ad4190-4", +}; + +static const struct ad4170_chip_info ad4195_chip_info = { + .name = "ad4195-4", +}; + +/* + * There are 8 of each MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN + * configuration registers. That is, there are 8 miscellaneous registers, MISC0 + * to MISC7. Each MISC register is associated with a setup; MISCN is associated + * with setup number N. The other 5 above mentioned types of registers have + * analogous structure. A setup is a set of those registers. For example, + * setup 1 comprises of MISC1, AFE1, FILTER1, FILTER_FS1, OFFSET1, and GAIN1 + * registers. Also, there are 16 CHANNEL_SETUP registers (CHANNEL_SETUP0 to + * CHANNEL_SETUP15). Each channel setup is associated with one of the 8 possible + * setups. Thus, AD4170 can support up to 16 channels but, since there are only + * 8 available setups, channels must share settings if more than 8 channels are + * configured. + * + * If this struct is modified, ad4170_setup_eq() will probably need to be + * updated too. + */ +struct ad4170_setup { + u16 misc; + u16 afe; + u16 filter; + u16 filter_fs; + u32 offset; /* For calibration purposes */ + u32 gain; /* For calibration purposes */ +}; + +struct ad4170_setup_info { + struct ad4170_setup setup; + unsigned int enabled_channels; + unsigned int channels; +}; + +struct ad4170_chan_info { + unsigned int input_range_uv; + unsigned int setup_num; /* Index to access state setup_infos array */ + struct ad4170_setup setup; /* cached setup */ + int offset_tbl[10]; + u32 scale_tbl[10][2]; + bool initialized; + bool enabled; +}; + +static const char * const ad4170_filt_names[] = { + [AD4170_SINC5_AVG] = "sinc5+avg", + [AD4170_SINC5] = "sinc5", + [AD4170_SINC3] = "sinc3", +}; + +struct ad4170_state { + struct mutex lock; /* Protect read-modify-write and multi write sequences */ + int vrefs_uv[AD4170_MAX_SUP]; + u32 mclk_hz; + struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS]; + struct ad4170_chan_info chan_infos[AD4170_MAX_ADC_CHANNELS]; + struct completion completion; + struct iio_chan_spec chans[AD4170_MAX_IIO_CHANNELS]; + struct spi_device *spi; + struct regmap *regmap; + int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2]; + __be32 bounce_buffer[AD4170_MAX_ADC_CHANNELS]; + struct spi_message msg; + struct spi_transfer xfer; + struct iio_trigger *trig; + struct clk_hw int_clk_hw; + unsigned int clock_ctrl; + unsigned int pins_fn[AD4170_NUM_ANALOG_PINS]; + int gpio_fn[AD4170_NUM_GPIO_PINS]; + unsigned int cur_src_pins[AD4170_NUM_CURRENT_SRC]; + struct gpio_chip gpiochip; + /* + * DMA (thus cache coherency maintenance) requires the transfer buffers + * to live in their own cache lines. + */ + u8 rx_buf[4] __aligned(IIO_DMA_MINALIGN); +}; + +static void ad4170_fill_sps_tbl(struct ad4170_state *st) +{ + unsigned int tmp0, tmp1, i; + + /* + * The ODR can be calculated the same way for sinc5+avg, sinc5, and + * sinc3 filter types with the exception that sinc5 filter has a + * narrowed range of allowed FILTER_FS values. + */ + for (i = 0; i < ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); i++) { + tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc3_filt_fs_tbl[i], + &tmp1); + tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc3_filt_fs_tbl[i]); + /* Fill sinc5+avg filter SPS table */ + st->sps_tbl[AD4170_SINC5_AVG][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5_AVG][i][1] = tmp1; /* Fractional part */ + + /* Fill sinc3 filter SPS table */ + st->sps_tbl[AD4170_SINC3][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC3][i][1] = tmp1; /* Fractional part */ + } + /* Sinc5 filter ODR doesn't use all FILTER_FS bits */ + for (i = 0; i < ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); i++) { + tmp0 = div_u64_rem(st->mclk_hz, 32 * ad4170_sinc5_filt_fs_tbl[i], + &tmp1); + tmp1 = mult_frac(tmp1, MICRO, 32 * ad4170_sinc5_filt_fs_tbl[i]); + /* Fill sinc5 filter SPS table */ + st->sps_tbl[AD4170_SINC5][i][0] = tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5][i][1] = tmp1; /* Fractional part */ + } +} + +static int ad4170_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4170_get_reg_size(struct ad4170_state *st, unsigned int reg, + unsigned int *size) +{ + if (reg >= ARRAY_SIZE(ad4170_reg_size)) + return -EINVAL; + + *size = ad4170_reg_size[reg]; + + return 0; +} + +static int ad4170_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad4170_state *st = context; + u8 tx_buf[AD4170_SPI_MAX_XFER_LEN]; + unsigned int size; + int ret; + + ret = ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + put_unaligned_be16(reg, tx_buf); + switch (size) { + case 3: + put_unaligned_be24(val, &tx_buf[AD4170_SPI_INST_PHASE_LEN]); + break; + case 2: + put_unaligned_be16(val, &tx_buf[AD4170_SPI_INST_PHASE_LEN]); + break; + case 1: + tx_buf[AD4170_SPI_INST_PHASE_LEN] = val; + break; + case 0: + /* Write continuous read exit code */ + tx_buf[0] = AD4170_ADC_CTRL_CONT_READ_EXIT; + return spi_write_then_read(st->spi, tx_buf, 1, NULL, 0); + default: + return -EINVAL; + } + + return spi_write_then_read(st->spi, tx_buf, + AD4170_SPI_INST_PHASE_LEN + size, NULL, 0); +} + +static int ad4170_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad4170_state *st = context; + u8 tx_buf[AD4170_SPI_INST_PHASE_LEN]; + unsigned int size; + int ret; + + put_unaligned_be16(AD4170_REG_READ_MASK | reg, tx_buf); + + ret = ad4170_get_reg_size(st, reg, &size); + if (ret) + return ret; + + ret = spi_write_then_read(st->spi, tx_buf, ARRAY_SIZE(tx_buf), + st->rx_buf, size); + if (ret) + return ret; + + switch (size) { + case 3: + *val = get_unaligned_be24(st->rx_buf); + return 0; + case 2: + *val = get_unaligned_be16(st->rx_buf); + return 0; + case 1: + *val = st->rx_buf[0]; + return 0; + default: + return -EINVAL; + } +} + +static const struct regmap_config ad4170_regmap_config = { + .reg_read = ad4170_reg_read, + .reg_write = ad4170_reg_write, +}; + +static bool ad4170_setup_eq(struct ad4170_setup *a, struct ad4170_setup *b) +{ + if (a->misc != b->misc || + a->afe != b->afe || + a->filter != b->filter || + a->filter_fs != b->filter_fs || + a->offset != b->offset || + a->gain != b->gain) + return false; + + return true; +} + +static int ad4170_find_setup(struct ad4170_state *st, + struct ad4170_setup *target_setup, + unsigned int *setup_num, bool *overwrite) +{ + unsigned int i; + + *setup_num = AD4170_INVALID_SETUP; + *overwrite = false; + + for (i = 0; i < AD4170_MAX_SETUPS; i++) { + struct ad4170_setup_info *setup_info = &st->setup_infos[i]; + + /* Immediately accept a matching setup. */ + if (ad4170_setup_eq(target_setup, &setup_info->setup)) { + *setup_num = i; + return 0; + } + + /* Ignore all setups which are used by enabled channels. */ + if (setup_info->enabled_channels) + continue; + + /* Find the least used slot. */ + if (*setup_num == AD4170_INVALID_SETUP || + setup_info->channels < st->setup_infos[*setup_num].channels) + *setup_num = i; + } + + if (*setup_num == AD4170_INVALID_SETUP) + return -EINVAL; + + *overwrite = true; + return 0; +} + +static void ad4170_unlink_channel(struct ad4170_state *st, unsigned int channel) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[channel]; + struct ad4170_setup_info *setup_info = &st->setup_infos[chan_info->setup_num]; + + chan_info->setup_num = AD4170_INVALID_SETUP; + setup_info->channels--; +} + +static int ad4170_unlink_setup(struct ad4170_state *st, unsigned int setup_num) +{ + unsigned int i; + + for (i = 0; i < AD4170_MAX_ADC_CHANNELS; i++) { + struct ad4170_chan_info *chan_info = &st->chan_infos[i]; + + if (!chan_info->initialized || chan_info->setup_num != setup_num) + continue; + + ad4170_unlink_channel(st, i); + } + return 0; +} + +static int ad4170_link_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, + unsigned int setup_num) +{ + struct ad4170_setup_info *setup_info = &st->setup_infos[setup_num]; + struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr]; + int ret; + + ret = regmap_update_bits(st->regmap, AD4170_CHAN_SETUP_REG(chan_addr), + AD4170_CHAN_SETUP_SETUP_MSK, + FIELD_PREP(AD4170_CHAN_SETUP_SETUP_MSK, setup_num)); + if (ret) + return ret; + + chan_info->setup_num = setup_num; + setup_info->channels++; + return 0; +} + +static int ad4170_write_setup(struct ad4170_state *st, unsigned int setup_num, + struct ad4170_setup *setup) +{ + int ret; + + /* + * It is recommended to place the ADC in standby mode or idle mode to + * write to OFFSET and GAIN registers. + */ + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_MISC_REG(setup_num), setup->misc); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_AFE_REG(setup_num), setup->afe); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_FILTER_REG(setup_num), + setup->filter); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_FILTER_FS_REG(setup_num), + setup->filter_fs); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_OFFSET_REG(setup_num), + setup->offset); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD4170_GAIN_REG(setup_num), setup->gain); + if (ret) + return ret; + + memcpy(&st->setup_infos[setup_num].setup, setup, sizeof(*setup)); + return 0; +} + +static int ad4170_write_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, bool on_enable) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr]; + bool overwrite; + int setup_num; + int ret; + + /* + * Similar to AD4130 driver, the following cases need to be handled. + * + * 1. Enabled and linked channel with setup changes: + * - Find a setup. If not possible, return error. + * - Unlink channel from current setup. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to new setup. + * + * 2. Soon to be enabled and unlinked channel: + * - Find a setup. If not possible, return error. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to the setup. + * + * 3. Disabled and linked channel with setup changes: + * - Unlink channel from current setup. + * + * 4. Soon to be enabled and linked channel: + * 5. Disabled and unlinked channel with setup changes: + * - Do nothing. + */ + + /* Cases 3, 4, and 5 */ + if (chan_info->setup_num != AD4170_INVALID_SETUP) { + /* Case 4 */ + if (on_enable) + return 0; + + /* Case 3 */ + if (!chan_info->enabled) { + ad4170_unlink_channel(st, chan_addr); + return 0; + } + } else if (!on_enable && !chan_info->enabled) { + /* Case 5 */ + return 0; + } + + /* Cases 1 & 2 */ + ret = ad4170_find_setup(st, &chan_info->setup, &setup_num, &overwrite); + if (ret) + return ret; + + if (chan_info->setup_num != AD4170_INVALID_SETUP) + /* Case 1 */ + ad4170_unlink_channel(st, chan_addr); + + if (overwrite) { + ret = ad4170_unlink_setup(st, setup_num); + if (ret) + return ret; + + ret = ad4170_write_setup(st, setup_num, &chan_info->setup); + if (ret) + return ret; + } + + return ad4170_link_channel_setup(st, chan_addr, setup_num); +} + +static int ad4170_set_channel_enable(struct ad4170_state *st, + unsigned int chan_addr, bool status) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan_addr]; + struct ad4170_setup_info *setup_info; + int ret; + + if (chan_info->enabled == status) + return 0; + + if (status) { + ret = ad4170_write_channel_setup(st, chan_addr, true); + if (ret) + return ret; + } + + setup_info = &st->setup_infos[chan_info->setup_num]; + + ret = regmap_update_bits(st->regmap, AD4170_CHAN_EN_REG, + AD4170_CHAN_EN(chan_addr), + status ? AD4170_CHAN_EN(chan_addr) : 0); + if (ret) + return ret; + + setup_info->enabled_channels += status ? 1 : -1; + chan_info->enabled = status; + return 0; +} + +static int __ad4170_get_filter_type(unsigned int filter) +{ + u16 f_conf = FIELD_GET(AD4170_FILTER_FILTER_TYPE_MSK, filter); + + switch (f_conf) { + case AD4170_FILTER_FILTER_TYPE_SINC5_AVG: + return AD4170_SINC5_AVG; + case AD4170_FILTER_FILTER_TYPE_SINC5: + return AD4170_SINC5; + case AD4170_FILTER_FILTER_TYPE_SINC3: + return AD4170_SINC3; + default: + return -EINVAL; + } +} + +static int ad4170_set_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + unsigned int filter_type_conf; + int ret; + + switch (val) { + case AD4170_SINC5_AVG: + filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5_AVG; + break; + case AD4170_SINC5: + filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC5; + break; + case AD4170_SINC3: + filter_type_conf = AD4170_FILTER_FILTER_TYPE_SINC3; + break; + default: + return -EINVAL; + } + + /* + * The filters provide the same ODR for a given filter_fs value but + * there are different minimum and maximum filter_fs limits for each + * filter. The filter_fs value will be adjusted if the current filter_fs + * is out of the limits of the just requested filter. Since the + * filter_fs value affects the ODR (sampling_frequency), changing the + * filter may lead to a change in the sampling frequency. + */ + scoped_guard(mutex, &st->lock) { + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (val == AD4170_SINC5_AVG || val == AD4170_SINC3) + setup->filter_fs = clamp(val, AD4170_SINC3_MIN_FS, + AD4170_SINC3_MAX_FS); + else + setup->filter_fs = clamp(val, AD4170_SINC5_MIN_FS, + AD4170_SINC5_MAX_FS); + + setup->filter &= ~AD4170_FILTER_FILTER_TYPE_MSK; + setup->filter |= FIELD_PREP(AD4170_FILTER_FILTER_TYPE_MSK, + filter_type_conf); + + ret = ad4170_write_channel_setup(st, chan->address, false); + iio_device_release_direct(indio_dev); + } + + return ret; +} + +static int ad4170_get_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + + return __ad4170_get_filter_type(setup->filter); +} + +static const struct iio_enum ad4170_filter_type_enum = { + .items = ad4170_filt_names, + .num_items = ARRAY_SIZE(ad4170_filt_names), + .get = ad4170_get_filter_type, + .set = ad4170_set_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4170_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4170_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4170_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad4170_filter_type_ext_info, + .scan_type = { + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, +}; + +static const struct iio_chan_spec ad4170_temp_channel_template = { + .type = IIO_TEMP, + .indexed = 0, + .channel = 17, + .channel2 = 17, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, +}; + +/* + * Receives the number of a multiplexed AD4170 input (ain_n), and stores the + * voltage (in µV) of the specified input into ain_voltage. If the input number + * is a ordinary analog input (AIN0 to AIN8), stores zero into ain_voltage. + * If a voltage regulator required by a special input is unavailable, return + * error code. Return 0 on success. + */ +static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n, + int *ain_voltage) +{ + struct device *dev = &st->spi->dev; + int v_diff; + + *ain_voltage = 0; + /* + * The voltage bias (vbias) sets the common-mode voltage of the channel + * to (AVDD + AVSS)/2. If provided, AVSS supply provides the magnitude + * (absolute value) of the negative voltage supplied to the AVSS pin. + * So, we do AVDD - AVSS to compute the DC voltage generated by the bias + * voltage generator. + */ + if (st->pins_fn[ain_n] & AD4170_PIN_VBIAS) { + int v_diff = st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP]; + *ain_voltage = v_diff / 2; + return 0; + } + + if (ain_n <= AD4170_CHAN_MAP_TEMP_SENSOR) + return 0; + + switch (ain_n) { + case AD4170_CHAN_MAP_AVDD_AVSS_N: + v_diff = st->vrefs_uv[AD4170_AVDD_SUP] - st->vrefs_uv[AD4170_AVSS_SUP]; + *ain_voltage = v_diff / 5; + return 0; + case AD4170_CHAN_MAP_IOVDD_DGND_N: + *ain_voltage = st->vrefs_uv[AD4170_IOVDD_SUP] / 5; + return 0; + case AD4170_CHAN_MAP_AVSS: + *ain_voltage = st->vrefs_uv[AD4170_AVSS_SUP]; + return 0; + case AD4170_CHAN_MAP_DGND: + *ain_voltage = 0; + return 0; + case AD4170_CHAN_MAP_REFIN1_P: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN+ but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN1P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN1_N: + if (st->vrefs_uv[AD4170_REFIN1N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN- but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN1N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_P: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2+ but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN2P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_N: + if (st->vrefs_uv[AD4170_REFIN2N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2- but ref not provided\n"); + + *ain_voltage = st->vrefs_uv[AD4170_REFIN2N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFOUT: + /* REFOUT is 2.5V relative to AVSS so take that into account */ + *ain_voltage = st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + return 0; + default: + return -EINVAL; + } +} + +static int ad4170_validate_analog_input(struct ad4170_state *st, int pin) +{ + if (pin <= AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] & AD4170_PIN_CURRENT_OUT) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Pin %d already used with fn %u.\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4170_PIN_ANALOG_IN; + } + return 0; +} + +static int ad4170_validate_channel_input(struct ad4170_state *st, int pin, bool com) +{ + /* Check common-mode input pin is mapped to a special input. */ + if (com && (pin < AD4170_CHAN_MAP_AVDD_AVSS_P || pin > AD4170_CHAN_MAP_REFOUT)) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid common-mode input pin number. %d\n", + pin); + + /* Check differential input pin is mapped to a analog input pin. */ + if (!com && pin > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid analog input pin number. %d\n", + pin); + + return ad4170_validate_analog_input(st, pin); +} + +/* + * Verifies whether the channel input configuration is valid by checking the + * input numbers. + * Returns 0 on valid channel input configuration. -EINVAL otherwise. + */ +static int ad4170_validate_channel(struct ad4170_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + ret = ad4170_validate_channel_input(st, chan->channel, false); + if (ret) + return ret; + + return ad4170_validate_channel_input(st, chan->channel2, + !chan->differential); +} + +/* + * Verifies whether the channel configuration is valid by checking the provided + * input type, polarity, and voltage references result in a sane input range. + * Returns negative error code on failure. + */ +static int ad4170_get_input_range(struct ad4170_state *st, + struct iio_chan_spec const *chan, + unsigned int ch_reg, unsigned int ref_sel) +{ + bool bipolar = chan->scan_type.sign == 's'; + struct device *dev = &st->spi->dev; + int refp, refn, ain_voltage, ret; + + switch (ref_sel) { + case AD4170_REF_REFIN1: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] == -ENODEV || + st->vrefs_uv[AD4170_REFIN1N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN± selected but not provided\n"); + + refp = st->vrefs_uv[AD4170_REFIN1P_SUP]; + refn = st->vrefs_uv[AD4170_REFIN1N_SUP]; + break; + case AD4170_REF_REFIN2: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] == -ENODEV || + st->vrefs_uv[AD4170_REFIN2N_SUP] == -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN2± selected but not provided\n"); + + refp = st->vrefs_uv[AD4170_REFIN2P_SUP]; + refn = st->vrefs_uv[AD4170_REFIN2N_SUP]; + break; + case AD4170_REF_AVDD: + refp = st->vrefs_uv[AD4170_AVDD_SUP]; + refn = st->vrefs_uv[AD4170_AVSS_SUP]; + break; + case AD4170_REF_REFOUT: + /* REFOUT is 2.5 V relative to AVSS */ + refp = st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + refn = st->vrefs_uv[AD4170_AVSS_SUP]; + break; + default: + return -EINVAL; + } + + /* + * Find out the analog input range from the channel type, polarity, and + * voltage reference selection. + * AD4170 channels are either differential or pseudo-differential. + * Diff input voltage range: −VREF/gain to +VREF/gain (datasheet page 6) + * Pseudo-diff input voltage range: 0 to VREF/gain (datasheet page 6) + */ + if (chan->differential) { + if (!bipolar) + return dev_err_probe(dev, -EINVAL, + "Channel %u differential unipolar\n", + ch_reg); + + /* + * Differential bipolar channel. + * avss-supply is never above 0V. + * Assuming refin1n-supply not above 0V. + * Assuming refin2n-supply not above 0V. + */ + return refp + abs(refn); + } + /* + * Some configurations can lead to invalid setups. + * For example, if AVSS = -2.5V, REF_SELECT set to REFOUT (REFOUT/AVSS), + * and pseudo-diff channel configuration set, then the input range + * should go from 0V to +VREF (single-ended - datasheet pg 10), but + * REFOUT/AVSS range would be -2.5V to 0V. + * Check the positive reference is higher than 0V for pseudo-diff + * channels. + * Note that at this point in the code, refp can only be >= 0 since all + * error codes from reading the regulator voltage have been checked + * either at ad4170_regulator_setup() or above in this function. + */ + if (refp == 0) + return dev_err_probe(dev, -EINVAL, + "REF+ == GND for pseudo-diff chan %u\n", + ch_reg); + + if (bipolar) + return refp; + + /* + * Pseudo-differential unipolar channel. + * Input expected to swing from IN- to +VREF. + */ + ret = ad4170_get_ain_voltage_uv(st, chan->channel2, &ain_voltage); + if (ret) + return ret; + + if (refp - ain_voltage <= 0) + return dev_err_probe(dev, -EINVAL, + "Negative input >= REF+ for pseudo-diff chan %u\n", + ch_reg); + + return refp - ain_voltage; +} + +static int __ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned long settling_time_ms; + int ret; + + reinit_completion(&st->completion); + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_SINGLE)); + if (ret) + return ret; + + /* + * When a channel is manually selected by the user, the ADC needs an + * extra time to provide the first stable conversion. The ADC settling + * time depends on the filter type, filter frequency, and ADC clock + * frequency (see datasheet page 53). The maximum settling time among + * all filter configurations is 6291164 / fCLK. Use that formula to wait + * for sufficient time whatever the filter configuration may be. + */ + settling_time_ms = DIV_ROUND_UP(6291164 * MILLI, st->mclk_hz); + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(settling_time_ms)); + if (!ret) + dev_dbg(&st->spi->dev, + "No Data Ready signal. Reading after delay.\n"); + + ret = regmap_read(st->regmap, AD4170_DATA_24B_REG, val); + if (ret) + return ret; + + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, chan->scan_type.realbits - 1); + + return 0; +} + +static int ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret, ret2; + + /* + * The ADC sequences through all enabled channels. That can lead to + * incorrect channel being sampled if a previous read would have left a + * different channel enabled. Thus, always enable and disable the + * channel on single-shot read. + */ + ret = ad4170_set_channel_enable(st, chan->address, true); + if (ret) + return ret; + + ret = __ad4170_read_sample(indio_dev, chan, val); + if (ret) { + dev_err(dev, "failed to read sample: %d\n", ret); + + ret2 = ad4170_set_channel_enable(st, chan->address, false); + if (ret2) + dev_err(dev, "failed to disable channel: %d\n", ret2); + + return ret; + } + + ret = ad4170_set_channel_enable(st, chan->address, false); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4170_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + enum ad4170_filter_type f_type; + unsigned int pga, fs_idx; + int ret; + + guard(mutex)(&st->lock); + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = ad4170_read_sample(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + switch (chan->type) { + case IIO_VOLTAGE: + *val = chan_info->scale_tbl[pga][0]; + *val2 = chan_info->scale_tbl[pga][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + /* + * The scale_tbl converts output codes to mV units so + * multiply by MILLI to make the factor convert to µV. + * Then, apply the temperature sensor change sensitivity + * of 477 μV/K. Finally, multiply the result by MILLI + * again to comply with milli degrees Celsius IIO ABI. + */ + *val = 0; + *val2 = DIV_ROUND_CLOSEST(chan_info->scale_tbl[pga][1] * MILLI, 477) * + MILLI; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val = chan_info->offset_tbl[pga]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type = __ad4170_get_filter_type(setup->filter); + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + fs_idx = find_closest(setup->filter_fs, + ad4170_sinc3_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0]; + *val2 = st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + case AD4170_SINC5: + fs_idx = find_closest(setup->filter_fs, + ad4170_sinc5_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl)); + *val = st->sps_tbl[f_type][fs_idx][0]; + *val2 = st->sps_tbl[f_type][fs_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + *val = setup->offset; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = setup->gain; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_fill_scale_tbl(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct device *dev = &st->spi->dev; + int bipolar = chan->scan_type.sign == 's' ? 1 : 0; + int precision_bits = chan->scan_type.realbits; + int pga, ainm_voltage, ret; + unsigned long long offset; + + ainm_voltage = 0; + ret = ad4170_get_ain_voltage_uv(st, chan->channel2, &ainm_voltage); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to fill scale table\n"); + + for (pga = 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + u64 nv; + unsigned int lshift, rshift; + + /* + * The PGA options are numbered from 0 to 9, with option 0 being + * a gain of 2^0 (no actual gain), and 7 meaning a gain of 2^7. + * Option 8, though, sets a gain of 0.5, so the input signal can + * be attenuated by 2 rather than amplified. Option 9, allows + * the signal to bypass the PGA circuitry (no gain). + * + * The scale factor to get ADC output codes to values in mV + * units is given by: + * _scale = (input_range / gain) / 2^precision + * AD4170 gain is a power of 2 so the above can be written as + * _scale = input_range / 2^(precision + gain) + * Keep the input range in µV to avoid truncating the less + * significant bits when right shifting it so to preserve scale + * precision. + */ + nv = (u64)chan_info->input_range_uv * NANO; + lshift = !!(pga & BIT(3)); /* handle PGA options 8 and 9 */ + rshift = precision_bits - bipolar + (pga & GENMASK(2, 0)) - lshift; + chan_info->scale_tbl[pga][0] = 0; + chan_info->scale_tbl[pga][1] = div_u64(nv >> rshift, MILLI); + + /* + * If the negative input is not at GND, the conversion result + * (which is relative to IN-) will be offset by the level at IN-. + * Use the scale factor the other way around to go from a known + * voltage to the corresponding ADC output code. + * With that, we are able to get to what would be the output + * code for the voltage at the negative input. + * If the negative input is not fixed, there is no offset. + */ + offset = ((unsigned long long)abs(ainm_voltage)) * MICRO; + offset = DIV_ROUND_CLOSEST_ULL(offset, chan_info->scale_tbl[pga][1]); + + /* + * After divided by the scale, offset will always fit into 31 + * bits. For _raw + _offset to be relative to GND, the value + * provided as _offset is of opposite sign than the real offset. + */ + if (ainm_voltage > 0) + chan_info->offset_tbl[pga] = -(int)(offset); + else + chan_info->offset_tbl[pga] = (int)(offset); + } + return 0; +} + +static int ad4170_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + enum ad4170_filter_type f_type; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)chan_info->scale_tbl; + *length = ARRAY_SIZE(chan_info->scale_tbl) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + f_type = ad4170_get_filter_type(indio_dev, chan); + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + /* Read sps_tbl here to ensure in bounds array access */ + *vals = (int *)st->sps_tbl[f_type]; + *length = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) * 2; + return IIO_AVAIL_LIST; + case AD4170_SINC5: + /* Read sps_tbl here to ensure in bounds array access */ + *vals = (int *)st->sps_tbl[f_type]; + *length = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad4170_set_pga(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, int val2) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + unsigned int pga; + + for (pga = 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + if (val == chan_info->scale_tbl[pga][0] && + val2 == chan_info->scale_tbl[pga][1]) + break; + } + + if (pga == AD4170_NUM_PGA_OPTIONS) + return -EINVAL; + + guard(mutex)(&st->lock); + setup->afe &= ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |= FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, pga); + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int ad4170_set_channel_freq(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, + int val2) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + enum ad4170_filter_type f_type = __ad4170_get_filter_type(setup->filter); + unsigned int filt_fs_tbl_size, i; + + switch (f_type) { + case AD4170_SINC5_AVG: + case AD4170_SINC3: + filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); + break; + case AD4170_SINC5: + filt_fs_tbl_size = ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); + break; + } + + for (i = 0; i < filt_fs_tbl_size; i++) { + if (st->sps_tbl[f_type][i][0] == val && + st->sps_tbl[f_type][i][1] == val2) + break; + } + if (i == filt_fs_tbl_size) + return -EINVAL; + + guard(mutex)(&st->lock); + if (f_type == AD4170_SINC5) + setup->filter_fs = ad4170_sinc5_filt_fs_tbl[i]; + else + setup->filter_fs = ad4170_sinc3_filt_fs_tbl[i]; + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int ad4170_set_calib_offset(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + + guard(mutex)(&st->lock); + setup->offset = val; + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int ad4170_set_calib_gain(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info = &st->chan_infos[chan->address]; + struct ad4170_setup *setup = &chan_info->setup; + + guard(mutex)(&st->lock); + setup->gain = val; + + return ad4170_write_channel_setup(st, chan->address, false); +} + +static int __ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4170_set_pga(st, chan, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4170_set_channel_freq(st, chan, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4170_set_calib_offset(st, chan, val); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4170_set_calib_gain(st, chan, val); + default: + return -EINVAL; + } +} + +static int ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad4170_write_raw(indio_dev, chan, val, val2, info); + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int chan_index; + int ret; + + iio_for_each_active_channel(indio_dev, chan_index) { + ret = ad4170_set_channel_enable(st, chan_index, true); + if (ret) + return ret; + } + return 0; +} + +static const struct iio_info ad4170_info = { + .read_raw = ad4170_read_raw, + .read_avail = ad4170_read_avail, + .write_raw = ad4170_write_raw, + .write_raw_get_fmt = ad4170_write_raw_get_fmt, + .update_scan_mode = ad4170_update_scan_mode, + .debugfs_reg_access = ad4170_debugfs_reg_access, +}; + +static int ad4170_soft_reset(struct ad4170_state *st) +{ + int ret; + + ret = regmap_write(st->regmap, AD4170_CONFIG_A_REG, + AD4170_SW_RESET_MSK); + if (ret) + return ret; + + /* AD4170-4 requires 1 ms between reset and any register access. */ + fsleep(1 * USEC_PER_MSEC); + + return 0; +} + +static int ad4170_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val); + if (ret) + goto err_release; + + /* + * If the GPIO is configured as an input, read the current value from + * AD4170_GPIO_INPUT_REG. Otherwise, read the input value from + * AD4170_GPIO_OUTPUT_REG. + */ + if (val & BIT(offset * 2)) + ret = regmap_read(st->regmap, AD4170_GPIO_INPUT_REG, &val); + else + ret = regmap_read(st->regmap, AD4170_GPIO_OUTPUT_REG, &val); + if (ret) + goto err_release; + + ret = !!(val & BIT(offset)); +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_assign_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, + BIT(offset), !!value); + + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD4170_GPIO_MODE_REG, &val); + if (ret) + goto err_release; + + if (val & BIT(offset * 2 + 1)) + ret = GPIO_LINE_DIRECTION_OUT; + else + ret = GPIO_LINE_DIRECTION_IN; + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + unsigned long gpio_mask; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + switch (offset) { + case 0: + gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK; + break; + case 1: + gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK; + break; + case 2: + gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK; + break; + case 3: + gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK; + break; + default: + ret = -EINVAL; + goto err_release; + } + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + AD4170_GPIO_MODE_GPIO_INPUT << (2 * offset)); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(gc); + struct ad4170_state *st = iio_priv(indio_dev); + unsigned long gpio_mask; + int ret; + + ret = ad4170_gpio_set(gc, offset, value); + if (ret) + return ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + switch (offset) { + case 0: + gpio_mask = AD4170_GPIO_MODE_GPIO0_MSK; + break; + case 1: + gpio_mask = AD4170_GPIO_MODE_GPIO1_MSK; + break; + case 2: + gpio_mask = AD4170_GPIO_MODE_GPIO2_MSK; + break; + case 3: + gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK; + break; + default: + ret = -EINVAL; + goto err_release; + } + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + AD4170_GPIO_MODE_GPIO_OUTPUT << (2 * offset)); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad4170_gpio_init_valid_mask(struct gpio_chip *gc, + unsigned long *valid_mask, + unsigned int ngpios) +{ + struct ad4170_state *st = gpiochip_get_data(gc); + unsigned int i; + + /* Only expose GPIOs that were not assigned any other function. */ + for (i = 0; i < ngpios; i++) { + bool valid = st->gpio_fn[i] == AD4170_GPIO_UNASSIGNED; + + __assign_bit(i, valid_mask, valid); + } + + return 0; +} + +static int ad4170_gpio_init(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + + st->gpiochip.label = "ad4170_gpios"; + st->gpiochip.base = -1; + st->gpiochip.ngpio = AD4170_NUM_GPIO_PINS; + st->gpiochip.parent = &st->spi->dev; + st->gpiochip.can_sleep = true; + st->gpiochip.init_valid_mask = ad4170_gpio_init_valid_mask; + st->gpiochip.get_direction = ad4170_gpio_get_direction; + st->gpiochip.direction_input = ad4170_gpio_direction_input; + st->gpiochip.direction_output = ad4170_gpio_direction_output; + st->gpiochip.get = ad4170_gpio_get; + st->gpiochip.set_rv = ad4170_gpio_set; + st->gpiochip.owner = THIS_MODULE; + + return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev); +} + +static int ad4170_validate_excitation_pin(struct ad4170_state *st, u32 pin) +{ + struct device *dev = &st->spi->dev; + unsigned int i; + + /* Check the pin number is valid */ + for (i = 0; i < ARRAY_SIZE(ad4170_iout_pin_tbl); i++) + if (ad4170_iout_pin_tbl[i] == pin) + break; + + if (i == ARRAY_SIZE(ad4170_iout_pin_tbl)) + return dev_err_probe(dev, -EINVAL, + "Invalid excitation pin: %u\n", + pin); + + /* Check the pin is available */ + if (pin <= AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] != AD4170_PIN_UNASSIGNED) + return dev_err_probe(dev, -EINVAL, + "Pin %u already used with fn %u\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |= AD4170_PIN_CURRENT_OUT; + } else { + unsigned int gpio = pin - AD4170_CURRENT_SRC_I_OUT_PIN_GPIO(0); + + if (st->gpio_fn[gpio] != AD4170_GPIO_UNASSIGNED) + return dev_err_probe(dev, -EINVAL, + "GPIO %u already used with fn %u\n", + gpio, st->gpio_fn[gpio]); + + st->gpio_fn[gpio] |= AD4170_GPIO_AC_EXCITATION; + } + + return 0; +} + +static int ad4170_validate_excitation_pins(struct ad4170_state *st, + u32 *exc_pins, int num_exc_pins) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_exc_pins; i++) { + ret = ad4170_validate_excitation_pin(st, exc_pins[i]); + if (ret) + return ret; + } + return 0; +} + +static const char *const ad4170_i_out_pin_dt_props[] = { + "adi,excitation-pin-0", + "adi,excitation-pin-1", + "adi,excitation-pin-2", + "adi,excitation-pin-3", +}; + +static const char *const ad4170_i_out_val_dt_props[] = { + "adi,excitation-current-0-microamp", + "adi,excitation-current-1-microamp", + "adi,excitation-current-2-microamp", + "adi,excitation-current-3-microamp", +}; + +/* + * Parses firmware data describing output current source setup. There are 4 + * excitation currents (IOUT0 to IOUT3) that can be configured independently. + * Excitation currents are added if they are output on the same pin. + */ +static int ad4170_parse_exc_current(struct ad4170_state *st, + struct fwnode_handle *child, + unsigned int *exc_pins, + unsigned int *exc_curs, + unsigned int *num_exc_pins) +{ + struct device *dev = &st->spi->dev; + unsigned int num_pins, i, j; + u32 pin, val; + int ret; + + num_pins = 0; + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) { + /* Parse excitation current output pin properties. */ + pin = AD4170_CURRENT_SRC_I_OUT_PIN_AIN(0); + ret = fwnode_property_read_u32(child, ad4170_i_out_pin_dt_props[i], + &pin); + if (ret) + continue; + + exc_pins[num_pins] = pin; + + /* Parse excitation current value properties. */ + val = ad4170_iout_current_ua_tbl[0]; + fwnode_property_read_u32(child, + ad4170_i_out_val_dt_props[i], &val); + + for (j = 0; j < ARRAY_SIZE(ad4170_iout_current_ua_tbl); j++) + if (ad4170_iout_current_ua_tbl[j] == val) + break; + + if (j == ARRAY_SIZE(ad4170_iout_current_ua_tbl)) + return dev_err_probe(dev, -EINVAL, "Invalid %s: %uuA\n", + ad4170_i_out_val_dt_props[i], val); + + exc_curs[num_pins] = j; + num_pins++; + } + *num_exc_pins = num_pins; + + return 0; +} + +static int ad4170_setup_current_src(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, + bool ac_excited) +{ + unsigned int exc_cur_pair, i, j; + int ret; + + for (i = 0; i < num_exc_pins; i++) { + unsigned int exc_cur = exc_curs[i]; + unsigned int pin = exc_pins[i]; + unsigned int current_src = 0; + + for (j = 0; j < AD4170_NUM_CURRENT_SRC; j++) + if (st->cur_src_pins[j] == AD4170_CURRENT_SRC_DISABLED) + break; + + if (j == AD4170_NUM_CURRENT_SRC) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Too many excitation current sources\n"); + + current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_PIN_MSK, pin); + current_src |= FIELD_PREP(AD4170_CURRENT_SRC_I_OUT_VAL_MSK, exc_cur); + st->cur_src_pins[j] = pin; + ret = regmap_write(st->regmap, AD4170_CURRENT_SRC_REG(j), + current_src); + if (ret) + return ret; + } + + if (!ac_excited) + return 0; + + if (num_exc_pins < 2) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Current chopping requested but only one pin provided: %u\n", + exc_pins[0]); + + /* + * Two use cases to handle here: + * - 2 pairs of excitation currents; + * - 1 pair of excitation currents. + */ + if (num_exc_pins == 4) { + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) + if (st->cur_src_pins[i] != exc_pins[i]) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Unable to use 4 exc pins\n"); + } else { + /* + * Excitation current chopping is configured in pairs. Current + * sources IOUT0 and IOUT1 form pair 1, IOUT2 and IOUT3 make up + * pair 2. So, if current chopping was requested, check if the + * first end of the first pair of excitation currents is + * available. Try the next pair if IOUT0 has already been + * configured for another channel. + */ + i = st->cur_src_pins[0] == exc_pins[0] ? 0 : 2; + + if (st->cur_src_pins[i] != exc_pins[0] || + st->cur_src_pins[i + 1] != exc_pins[1]) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Failed to setup current chopping\n"); + + st->cur_src_pins[i] = exc_pins[0]; + st->cur_src_pins[i + 1] = exc_pins[1]; + + if (i == 0) + exc_cur_pair = AD4170_MISC_CHOP_IEXC_PAIR1; + else + exc_cur_pair = AD4170_MISC_CHOP_IEXC_PAIR2; + } + + /* + * Configure excitation current chopping. + * Chop both pairs if using four excitation pins. + */ + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_IEXC_MSK, + num_exc_pins == 2 ? + exc_cur_pair : + AD4170_MISC_CHOP_IEXC_BOTH); + + return 0; +} + +static int ad4170_setup_bridge(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, + bool ac_excited) +{ + unsigned long gpio_mask; + unsigned int i; + int ret; + + /* + * If a specific current is provided through + * adi,excitation-current-n-microamp, set excitation pins provided + * through adi,excitation-pin-n to excite the bridge circuit. + */ + for (i = 0; i < num_exc_pins; i++) + if (exc_curs[i] > 0) + return ad4170_setup_current_src(st, child, setup, exc_pins, + exc_curs, num_exc_pins, + ac_excited); + + /* + * Else, use predefined ACX1, ACX1 negated, ACX2, ACX2 negated signals + * to AC excite the bridge. Those signals are output on GPIO2, GPIO0, + * GPIO3, and GPIO1, respectively. If only two pins are specified for AC + * excitation, use ACX1 and ACX2 (GPIO2 and GPIO3). + * + * Also, to avoid any short-circuit condition when more than one channel + * is enabled, set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to + * DC excite the bridge whenever a channel without AC excitation is + * selected. That is needed because GPIO pins are controlled by the next + * highest priority GPIO function when a channel doesn't enable AC + * excitation. See datasheet Figure 113 Weigh Scale (AC Excitation) for + * the reference circuit diagram. + */ + if (num_exc_pins == 2) { + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x3); + + gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK; + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT)); + if (ret) + return ret; + + /* + * Set GPIO2 high and GPIO3 low to DC excite the bridge when + * a different channel is selected. + */ + gpio_mask = AD4170_GPIO_OUTPUT_GPIO_MSK(3) | + AD4170_GPIO_OUTPUT_GPIO_MSK(2); + ret = regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1)); + if (ret) + return ret; + + st->gpio_fn[3] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[2] |= AD4170_GPIO_OUTPUT; + } else { + setup->misc |= FIELD_PREP(AD4170_MISC_CHOP_ADC_MSK, 0x2); + + gpio_mask = AD4170_GPIO_MODE_GPIO3_MSK | AD4170_GPIO_MODE_GPIO2_MSK | + AD4170_GPIO_MODE_GPIO1_MSK | AD4170_GPIO_MODE_GPIO0_MSK; + ret = regmap_update_bits(st->regmap, AD4170_GPIO_MODE_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_MODE_GPIO3_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO2_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO1_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT) | + FIELD_PREP(AD4170_GPIO_MODE_GPIO0_MSK, + AD4170_GPIO_MODE_GPIO_OUTPUT)); + if (ret) + return ret; + + /* + * Set GPIO2 and GPIO0 high, and set GPIO1 and GPIO3 low to DC + * excite the bridge when a different channel is selected. + */ + gpio_mask = AD4170_GPIO_OUTPUT_GPIO_MSK(3) | + AD4170_GPIO_OUTPUT_GPIO_MSK(2) | + AD4170_GPIO_OUTPUT_GPIO_MSK(1) | + AD4170_GPIO_OUTPUT_GPIO_MSK(0); + ret = regmap_update_bits(st->regmap, AD4170_GPIO_OUTPUT_REG, gpio_mask, + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(3), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(2), 1) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(1), 0) | + FIELD_PREP(AD4170_GPIO_OUTPUT_GPIO_MSK(0), 1)); + if (ret) + return ret; + + st->gpio_fn[3] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[2] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[1] |= AD4170_GPIO_OUTPUT; + st->gpio_fn[0] |= AD4170_GPIO_OUTPUT; + } + + return 0; +} + +static int ad4170_setup_rtd(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, u32 *exc_pins, + unsigned int *exc_curs, int num_exc_pins, bool ac_excited) +{ + return ad4170_setup_current_src(st, child, setup, exc_pins, + exc_curs, num_exc_pins, ac_excited); +} + +static int ad4170_parse_external_sensor(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup, + struct iio_chan_spec *chan, + unsigned int s_type) +{ + unsigned int num_exc_pins, reg_val; + struct device *dev = &st->spi->dev; + u32 pins[2], exc_pins[4], exc_curs[4]; + bool ac_excited; + int ret; + + ret = fwnode_property_read_u32_array(child, "diff-channels", pins, + ARRAY_SIZE(pins)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read sensor diff-channels\n"); + + chan->differential = true; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + + ret = ad4170_parse_exc_current(st, child, exc_pins, exc_curs, &num_exc_pins); + if (ret) + return ret; + + /* The external sensor may not need excitation from the ADC chip. */ + if (num_exc_pins == 0) + return 0; + + ret = ad4170_validate_excitation_pins(st, exc_pins, num_exc_pins); + if (ret) + return ret; + + ac_excited = fwnode_property_read_bool(child, "adi,excitation-ac"); + + if (s_type == AD4170_THERMOCOUPLE_SENSOR) { + if (st->pins_fn[chan->channel2] & AD4170_PIN_VBIAS) { + reg_val = BIT(chan->channel2); + ret = regmap_write(st->regmap, AD4170_V_BIAS_REG, reg_val); + if (ret) + dev_err_probe(dev, ret, "Failed to set vbias\n"); + } + } + if (s_type == AD4170_WEIGH_SCALE_SENSOR) + ret = ad4170_setup_bridge(st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + else + ret = ad4170_setup_rtd(st, child, setup, exc_pins, exc_curs, + num_exc_pins, ac_excited); + + return ret; +} + +static int ad4170_parse_reference(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup) +{ + struct device *dev = &st->spi->dev; + const char *propname; + u32 aux; + int ret; + + /* Optional positive reference buffering */ + propname = "adi,positive-reference-buffer"; + ret = device_property_match_property_string(dev, propname, + ad4170_ref_buf_str, + ARRAY_SIZE(ad4170_ref_buf_str)); + + /* Default to full precharge buffer enabled. */ + setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK, + ret >= 0 ? ret : AD4170_REF_BUF_FULL); + + /* Optional negative reference buffering */ + propname = "adi,negative-reference-buffer"; + ret = device_property_match_property_string(dev, propname, + ad4170_ref_buf_str, + ARRAY_SIZE(ad4170_ref_buf_str)); + + /* Default to full precharge buffer enabled. */ + setup->afe |= FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK, + ret >= 0 ? ret : AD4170_REF_BUF_FULL); + + /* Optional voltage reference selection */ + propname = "adi,reference-select"; + aux = AD4170_REF_REFOUT; /* Default reference selection. */ + fwnode_property_read_u32(child, propname, &aux); + if (aux > AD4170_REF_AVDD) + return dev_err_probe(dev, -EINVAL, "Invalid %s: %u\n", + propname, aux); + + setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, aux); + + return 0; +} + +static int ad4170_parse_adc_channel_type(struct device *dev, + struct fwnode_handle *child, + struct iio_chan_spec *chan) +{ + const char *propname, *propname2; + int ret, ret2; + u32 pins[2]; + + propname = "single-channel"; + propname2 = "diff-channels"; + if (!fwnode_property_present(child, propname) && + !fwnode_property_present(child, propname2)) + return dev_err_probe(dev, -EINVAL, + "Channel must define one of %s or %s.\n", + propname, propname2); + + /* Parse differential channel configuration */ + ret = fwnode_property_read_u32_array(child, propname2, pins, + ARRAY_SIZE(pins)); + if (!ret) { + chan->differential = true; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + return 0; + } + /* Failed to parse diff chan so try pseudo-diff chan props */ + + propname2 = "common-mode-channel"; + if (fwnode_property_present(child, propname) && + !fwnode_property_present(child, propname2)) + return dev_err_probe(dev, -EINVAL, + "When %s is defined, %s must be defined too\n", + propname, propname2); + + /* Parse pseudo-differential channel configuration */ + ret = fwnode_property_read_u32(child, propname, &pins[0]); + ret2 = fwnode_property_read_u32(child, propname2, &pins[1]); + + if (!ret && !ret2) { + chan->differential = false; + chan->channel = pins[0]; + chan->channel2 = pins[1]; + return 0; + } + return dev_err_probe(dev, -EINVAL, + "Failed to parse channel %lu input. %d, %d\n", + chan->address, ret, ret2); +} + +static int ad4170_parse_channel_node(struct iio_dev *indio_dev, + struct fwnode_handle *child, + unsigned int chan_num) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int s_type = AD4170_ADC_SENSOR; + struct device *dev = &st->spi->dev; + struct ad4170_chan_info *chan_info; + struct ad4170_setup *setup; + struct iio_chan_spec *chan; + unsigned int ref_select; + unsigned int ch_reg; + bool bipolar; + int ret; + + ret = fwnode_property_read_u32(child, "reg", &ch_reg); + if (ret) + return dev_err_probe(dev, ret, "Failed to read channel reg\n"); + + if (ch_reg >= AD4170_MAX_ADC_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Channel idx greater than no of channels\n"); + + chan = &st->chans[chan_num]; + *chan = ad4170_channel_template; + + chan->address = ch_reg; + chan->scan_index = ch_reg; + chan_info = &st->chan_infos[chan->address]; + + chan_info->setup_num = AD4170_INVALID_SETUP; + chan_info->initialized = true; + + setup = &chan_info->setup; + ret = ad4170_parse_reference(st, child, setup); + if (ret) + return ret; + + ret = fwnode_property_match_property_string(child, "adi,sensor-type", + ad4170_sensor_type, + ARRAY_SIZE(ad4170_sensor_type)); + + /* Default to conventional ADC channel if sensor type not present */ + s_type = ret < 0 ? AD4170_ADC_SENSOR : ret; + switch (s_type) { + case AD4170_ADC_SENSOR: + ret = ad4170_parse_adc_channel_type(dev, child, chan); + if (ret) + return ret; + + break; + case AD4170_WEIGH_SCALE_SENSOR: + case AD4170_THERMOCOUPLE_SENSOR: + case AD4170_RTD_SENSOR: + ret = ad4170_parse_external_sensor(st, child, setup, chan, s_type); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + bipolar = fwnode_property_read_bool(child, "bipolar"); + setup->afe |= FIELD_PREP(AD4170_AFE_BIPOLAR_MSK, bipolar); + if (bipolar) + chan->scan_type.sign = 's'; + else + chan->scan_type.sign = 'u'; + + ret = ad4170_validate_channel(st, chan); + if (ret) + return ret; + + ref_select = FIELD_GET(AD4170_AFE_REF_SELECT_MSK, setup->afe); + ret = ad4170_get_input_range(st, chan, ch_reg, ref_select); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + chan_info->input_range_uv = ret; + return 0; +} + +static int ad4170_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int num_channels; + unsigned int chan_num; + int ret; + + num_channels = device_get_child_node_count(dev); + + if (num_channels > AD4170_MAX_ADC_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels\n"); + + /* Add one for temperature */ + num_channels = min(num_channels + 1, AD4170_MAX_ADC_CHANNELS); + + chan_num = 0; + device_for_each_child_node_scoped(dev, child) { + ret = ad4170_parse_channel_node(indio_dev, child, chan_num++); + if (ret) + return ret; + } + + /* + * Add internal temperature sensor channel if the maximum number of + * channels has not been reached. + */ + if (num_channels < AD4170_MAX_ADC_CHANNELS) { + struct ad4170_setup *setup = &st->chan_infos[chan_num].setup; + + st->chans[chan_num] = ad4170_temp_channel_template; + st->chans[chan_num].address = chan_num; + st->chans[chan_num].scan_index = chan_num; + + st->chan_infos[chan_num].setup_num = AD4170_INVALID_SETUP; + st->chan_infos[chan_num].initialized = true; + + setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, + AD4170_REF_AVDD); + + ret = ad4170_get_input_range(st, &st->chans[chan_num], chan_num, + AD4170_REF_AVDD); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + st->chan_infos[chan_num].input_range_uv = ret; + chan_num++; + } + + /* Add timestamp channel */ + struct iio_chan_spec ts_chan = IIO_CHAN_SOFT_TIMESTAMP(chan_num); + + st->chans[chan_num] = ts_chan; + num_channels = num_channels + 1; + + indio_dev->num_channels = num_channels; + indio_dev->channels = st->chans; + + return 0; +} + +static struct ad4170_state *clk_hw_to_ad4170(struct clk_hw *hw) +{ + return container_of(hw, struct ad4170_state, int_clk_hw); +} + +static unsigned long ad4170_sel_clk(struct ad4170_state *st, + unsigned int clk_sel) +{ + st->clock_ctrl &= ~AD4170_CLOCK_CTRL_CLOCKSEL_MSK; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, clk_sel); + return regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); +} + +static unsigned long ad4170_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return AD4170_INT_CLOCK_16MHZ; +} + +static int ad4170_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + u32 clk_sel; + + clk_sel = FIELD_GET(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, st->clock_ctrl); + return clk_sel == AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT; +} + +static int ad4170_clk_output_prepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + return ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); +} + +static void ad4170_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad4170_state *st = clk_hw_to_ad4170(hw); + + ad4170_sel_clk(st, AD4170_CLOCK_CTRL_CLOCKSEL_INT); +} + +static const struct clk_ops ad4170_int_clk_ops = { + .recalc_rate = ad4170_clk_recalc_rate, + .is_enabled = ad4170_clk_output_is_enabled, + .prepare = ad4170_clk_output_prepare, + .unprepare = ad4170_clk_output_unprepare, +}; + +static int ad4170_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct clk_init_data init = {}; + int ret; + + if (device_property_read_string(dev, "clock-output-names", &init.name)) { + init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw", + dev_fwnode(dev)); + if (!init.name) + return -ENOMEM; + } + + init.ops = &ad4170_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 ad4170_clock_select(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + struct clk *ext_clk; + int ret; + + ext_clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(ext_clk)) + return dev_err_probe(dev, PTR_ERR(ext_clk), + "Failed to get external clock\n"); + + if (!ext_clk) { + /* Use internal clock reference */ + st->mclk_hz = AD4170_INT_CLOCK_16MHZ; + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_INT_OUT); + + if (!device_property_present(&st->spi->dev, "#clock-cells")) + return 0; + + return ad4170_register_clk_provider(indio_dev); + } + + /* Read optional clock-names prop to specify the external clock type */ + ret = device_property_match_property_string(dev, "clock-names", + ad4170_clk_sel, + ARRAY_SIZE(ad4170_clk_sel)); + + ret = ret < 0 ? 0 : ret; /* Default to external clock if no clock-names */ + st->clock_ctrl |= FIELD_PREP(AD4170_CLOCK_CTRL_CLOCKSEL_MSK, + AD4170_CLOCK_CTRL_CLOCKSEL_EXT + ret); + + st->mclk_hz = clk_get_rate(ext_clk); + if (st->mclk_hz < AD4170_EXT_CLOCK_MHZ_MIN || + st->mclk_hz > AD4170_EXT_CLOCK_MHZ_MAX) { + return dev_err_probe(dev, -EINVAL, + "Invalid external clock frequency %u\n", + st->mclk_hz); + } + + return 0; +} + +static int ad4170_parse_firmware(struct iio_dev *indio_dev) +{ + unsigned int vbias_pins[AD4170_MAX_ANALOG_PINS]; + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int num_vbias_pins; + int reg_data, ret; + u32 int_pin_sel; + unsigned int i; + + ret = ad4170_clock_select(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device clock\n"); + + ret = regmap_write(st->regmap, AD4170_CLOCK_CTRL_REG, st->clock_ctrl); + if (ret) + return ret; + + for (i = 0; i < AD4170_NUM_CURRENT_SRC; i++) + st->cur_src_pins[i] = AD4170_CURRENT_SRC_DISABLED; + + /* On power on, device defaults to using SDO pin for data ready signal */ + int_pin_sel = AD4170_INT_PIN_SDO; + ret = device_property_match_property_string(dev, "interrupt-names", + ad4170_int_pin_names, + ARRAY_SIZE(ad4170_int_pin_names)); + if (ret >= 0) + int_pin_sel = ret; + + reg_data = FIELD_PREP(AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, + int_pin_sel == AD4170_INT_PIN_DIG_AUX1 ? + AD4170_PIN_MUXING_DIG_AUX1_RDY : + AD4170_PIN_MUXING_DIG_AUX1_DISABLED); + + ret = regmap_update_bits(st->regmap, AD4170_PIN_MUXING_REG, + AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, reg_data); + if (ret) + return ret; + + ret = device_property_count_u32(dev, "adi,vbias-pins"); + if (ret > 0) { + if (ret > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(dev, -EINVAL, + "Too many vbias pins %u\n", ret); + + num_vbias_pins = ret; + + ret = device_property_read_u32_array(dev, "adi,vbias-pins", + vbias_pins, + num_vbias_pins); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read vbias pins\n"); + + for (i = 0; i < num_vbias_pins; i++) + st->pins_fn[vbias_pins[i]] |= AD4170_PIN_VBIAS; + } + + ret = ad4170_parse_channels(indio_dev); + if (ret) + return ret; + + /* Only create a GPIO chip if flagged for it */ + if (device_property_read_bool(dev, "gpio-controller")) { + ret = ad4170_gpio_init(indio_dev); + if (ret) + return ret; + } + + return 0; +} + +static int ad4170_initial_config(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + unsigned int i; + int ret; + + ad4170_fill_sps_tbl(st); + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set ADC mode to idle\n"); + + for (i = 0; i < indio_dev->num_channels; i++) { + struct ad4170_chan_info *chan_info; + struct iio_chan_spec const *chan; + struct ad4170_setup *setup; + unsigned int val; + + chan = &indio_dev->channels[i]; + if (chan->type == IIO_TIMESTAMP) + continue; + + chan_info = &st->chan_infos[chan->address]; + + setup = &chan_info->setup; + setup->gain = AD4170_GAIN_REG_DEFAULT; + ret = ad4170_write_channel_setup(st, chan->address, false); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write channel setup\n"); + + val = FIELD_PREP(AD4170_CHAN_MAP_AINP_MSK, chan->channel) | + FIELD_PREP(AD4170_CHAN_MAP_AINM_MSK, chan->channel2); + + ret = regmap_write(st->regmap, AD4170_CHAN_MAP_REG(i), val); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write CHAN_MAP_REG\n"); + + ret = ad4170_set_channel_freq(st, chan, + AD4170_DEFAULT_SAMP_RATE, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set channel freq\n"); + + ret = ad4170_fill_scale_tbl(indio_dev, chan); + if (ret) + return dev_err_probe(dev, ret, + "Failed to fill scale tbl\n"); + } + + /* Disable all channels to avoid reading from unexpected channel */ + ret = regmap_write(st->regmap, AD4170_CHAN_EN_REG, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to disable channels\n"); + + /* + * Configure channels to share the same data output register, i.e. data + * can be read from the same register address regardless of channel + * number. + */ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK); +} + +static int ad4170_prepare_spi_message(struct ad4170_state *st) +{ + /* + * Continuous data register read is enabled on buffer postenable so + * no instruction phase is needed meaning we don't need to send the + * register address to read data. Transfer only needs the read buffer. + */ + st->xfer.rx_buf = &st->rx_buf; + st->xfer.len = BITS_TO_BYTES(ad4170_channel_template.scan_type.realbits); + + spi_message_init_with_transfers(&st->msg, &st->xfer, 1); + + return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->msg); +} + +static int ad4170_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_CONT)); + if (ret) + return ret; + + /* + * This enables continuous read of the ADC data register. The ADC must + * be in continuous conversion mode. + */ + return regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_ADC_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_ENABLE)); +} + +static int ad4170_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int i; + int ret; + + /* + * Use a high register address (virtual register) to request a write of + * 0xA5 to the ADC during the first 8 SCLKs of the ADC data read cycle, + * thus exiting continuous read. + */ + ret = regmap_write(st->regmap, AD4170_ADC_CTRL_CONT_READ_EXIT_REG, 0); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_CONT_READ_MSK, + FIELD_PREP(AD4170_ADC_CTRL_CONT_READ_MSK, + AD4170_ADC_CTRL_CONT_READ_DISABLE)); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, + AD4170_ADC_CTRL_MODE_IDLE)); + if (ret) + return ret; + + /* + * The ADC sequences through all the enabled channels (see datasheet + * page 95). That can lead to incorrect channel being read if a + * single-shot read (or buffered read with different active_scan_mask) + * is done after buffer disable. Disable all channels so only requested + * channels will be read. + */ + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].type == IIO_TIMESTAMP) + continue; + + ret = ad4170_set_channel_enable(st, i, false); + if (ret) + return ret; + } + + return 0; +} + +static bool ad4170_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + unsigned int masklength = iio_get_masklength(indio_dev); + unsigned int enabled; + + /* + * The channel sequencer cycles through the enabled channels in + * sequential order, from channel 0 to channel 15, bypassing disabled + * channels. When more than one channel is enabled, channel 0 must + * always be enabled. See datasheet channel_en register description at + * page 95. + */ + enabled = bitmap_weight(scan_mask, masklength); + if (enabled > 1) + return test_bit(0, scan_mask); + + return enabled == 1; +} + +static const struct iio_buffer_setup_ops ad4170_buffer_ops = { + .postenable = ad4170_buffer_postenable, + .predisable = ad4170_buffer_predisable, + .validate_scan_mask = ad4170_validate_scan_mask, +}; + +static irqreturn_t ad4170_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad4170_state *st = iio_priv(indio_dev); + unsigned int chan_index; + unsigned int i = 0; + int ret; + + iio_for_each_active_channel(indio_dev, chan_index) { + ret = spi_sync(st->spi, &st->msg); + if (ret) + goto err_out; + + memcpy(&st->bounce_buffer[i++], st->rx_buf, ARRAY_SIZE(st->rx_buf)); + } + + iio_push_to_buffers_with_ts(indio_dev, st->bounce_buffer, + sizeof(st->bounce_buffer), + iio_get_time_ns(indio_dev)); +err_out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops ad4170_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static irqreturn_t ad4170_irq_handler(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad4170_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad4170_trigger_setup(struct iio_dev *indio_dev) +{ + struct ad4170_state *st = iio_priv(indio_dev); + struct device *dev = &st->spi->dev; + int ret; + + st->trig = devm_iio_trigger_alloc(dev, "%s-trig%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad4170_trigger_ops; + + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(dev, st->trig); + if (ret) + return dev_err_probe(dev, ret, "Failed to register trigger\n"); + + indio_dev->trig = iio_trigger_get(st->trig); + + return 0; +} + +static int ad4170_regulator_setup(struct ad4170_state *st) +{ + struct device *dev = &st->spi->dev; + int ret; + + /* Required regulators */ + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get AVDD voltage.\n"); + + st->vrefs_uv[AD4170_AVDD_SUP] = ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get IOVDD voltage.\n"); + + st->vrefs_uv[AD4170_IOVDD_SUP] = ret; + + /* Optional regulators */ + ret = devm_regulator_get_enable_read_voltage(dev, "avss"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get AVSS voltage.\n"); + + /* + * Assume AVSS at GND (0V) if not provided. + * REVISIT: AVSS is never above system ground level (i.e. AVSS is either + * GND or a negative voltage). But we currently don't have support for + * reading negative voltages with the regulator framework. So, the + * current AD4170 support reads a positive value from the regulator, + * then inverts sign to make that negative. + */ + st->vrefs_uv[AD4170_AVSS_SUP] = ret == -ENODEV ? 0 : -ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin1p"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN1P_SUP] = ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin1n"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN- voltage.\n"); + + /* + * Negative supplies are assumed to provide negative voltage. + * REVISIT when support for negative regulator voltage read be available + * in the regulator framework. + */ + st->vrefs_uv[AD4170_REFIN1N_SUP] = ret == -ENODEV ? -ENODEV : -ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin2p"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN2P_SUP] = ret; + + ret = devm_regulator_get_enable_read_voltage(dev, "refin2n"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2- voltage.\n"); + + /* + * Negative supplies are assumed to provide negative voltage. + * REVISIT when support for negative regulator voltage read be available + * in the regulator framework. + */ + st->vrefs_uv[AD4170_REFIN2N_SUP] = ret == -ENODEV ? -ENODEV : -ret; + + return 0; +} + +static int ad4170_probe(struct spi_device *spi) +{ + const struct ad4170_chip_info *chip; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad4170_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + chip = spi_get_device_match_data(spi); + if (!chip) + return -EINVAL; + + indio_dev->name = chip->name; + indio_dev->info = &ad4170_info; + + st->regmap = devm_regmap_init(dev, NULL, st, &ad4170_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to initialize regmap\n"); + + ret = ad4170_regulator_setup(st); + if (ret) + return ret; + + ret = ad4170_soft_reset(st); + if (ret) + return ret; + + ret = ad4170_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse firmware\n"); + + ret = ad4170_initial_config(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device\n"); + + init_completion(&st->completion); + + if (spi->irq) { + ret = devm_request_irq(dev, spi->irq, &ad4170_irq_handler, + IRQF_ONESHOT, indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = ad4170_trigger_setup(indio_dev); + if (ret) + return ret; + } + + ret = ad4170_prepare_spi_message(st); + if (ret) + return dev_err_probe(dev, ret, "Failed to prepare SPI message\n"); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &ad4170_trigger_handler, + &ad4170_buffer_ops); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup read buffer\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4170_id_table[] = { + { "ad4170-4", (kernel_ulong_t)&ad4170_chip_info }, + { "ad4190-4", (kernel_ulong_t)&ad4190_chip_info }, + { "ad4195-4", (kernel_ulong_t)&ad4195_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4170_id_table); + +static const struct of_device_id ad4170_of_match[] = { + { .compatible = "adi,ad4170-4", .data = &ad4170_chip_info }, + { .compatible = "adi,ad4190-4", .data = &ad4190_chip_info }, + { .compatible = "adi,ad4195-4", .data = &ad4195_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4170_of_match); + +static struct spi_driver ad4170_driver = { + .driver = { + .name = "ad4170-4", + .of_match_table = ad4170_of_match, + }, + .probe = ad4170_probe, + .id_table = ad4170_id_table, +}; +module_spi_driver(ad4170_driver); + +MODULE_AUTHOR("Ana-Maria Cusco <ana-maria.cusco@analog.com>"); +MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD4170 SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad4851.c b/drivers/iio/adc/ad4851.c index f1d2e2896f2a..1ad77f2a4580 100644 --- a/drivers/iio/adc/ad4851.c +++ b/drivers/iio/adc/ad4851.c @@ -294,7 +294,6 @@ static int ad4851_scale_fill(struct iio_dev *indio_dev) } static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int osr) { struct ad4851_state *st = iio_priv(indio_dev); @@ -321,7 +320,8 @@ static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev, return ret; } - ret = iio_backend_oversampling_ratio_set(st->back, osr); + /* Channel is ignored by the backend being used here */ + ret = iio_backend_oversampling_ratio_set(st->back, 0, osr); if (ret) return ret; @@ -444,10 +444,12 @@ static int ad4851_setup(struct ad4851_state *st) if (ret) return ret; - ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A, - AD4851_SDO_ENABLE); - if (ret) - return ret; + if (!(st->spi->mode & SPI_3WIRE)) { + ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A, + AD4851_SDO_ENABLE); + if (ret) + return ret; + } ret = regmap_read(st->regmap, AD4851_REG_PRODUCT_ID_L, &product_id); if (ret) @@ -831,7 +833,7 @@ static int ad4851_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_CALIBBIAS: return ad4851_set_calibbias(st, chan->channel, val); case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - return ad4851_set_oversampling_ratio(indio_dev, chan, val); + return ad4851_set_oversampling_ratio(indio_dev, val); default: return -EINVAL; } diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c index b472b9498fd1..bd4877268689 100644 --- a/drivers/iio/adc/ad7091r5.c +++ b/drivers/iio/adc/ad7091r5.c @@ -92,7 +92,7 @@ static void ad7091r5_regmap_init(struct ad7091r_state *st, st->map = devm_regmap_init_i2c(i2c, regmap_conf); } -static struct ad7091r_init_info ad7091r5_init_info = { +static const struct ad7091r_init_info ad7091r5_init_info = { .info_irq = &ad7091r5_chip_info_irq, .info_no_irq = &ad7091r5_chip_info_noirq, .regmap_config = &ad7091r_regmap_config, diff --git a/drivers/iio/adc/ad7091r8.c b/drivers/iio/adc/ad7091r8.c index cebade4c2d49..e93b8bb60e8e 100644 --- a/drivers/iio/adc/ad7091r8.c +++ b/drivers/iio/adc/ad7091r8.c @@ -206,14 +206,14 @@ static int ad7091r8_gpio_setup(struct ad7091r_state *st) return 0; } -static struct ad7091r_init_info ad7091r2_init_info = { +static const struct ad7091r_init_info ad7091r2_init_info = { .info_no_irq = &ad7091r8_infos[AD7091R2_INFO], .regmap_config = &ad7091r2_reg_conf, .init_adc_regmap = &ad7091r8_regmap_init, .setup = &ad7091r8_gpio_setup }; -static struct ad7091r_init_info ad7091r4_init_info = { +static const struct ad7091r_init_info ad7091r4_init_info = { .info_no_irq = &ad7091r8_infos[AD7091R4_INFO], .info_irq = &ad7091r8_infos[AD7091R4_INFO_IRQ], .regmap_config = &ad7091r4_reg_conf, @@ -221,7 +221,7 @@ static struct ad7091r_init_info ad7091r4_init_info = { .setup = &ad7091r8_gpio_setup }; -static struct ad7091r_init_info ad7091r8_init_info = { +static const struct ad7091r_init_info ad7091r8_init_info = { .info_no_irq = &ad7091r8_infos[AD7091R8_INFO], .info_irq = &ad7091r8_infos[AD7091R8_INFO_IRQ], .regmap_config = &ad7091r8_reg_conf, diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 92596f15e797..9808df2e9242 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -94,11 +94,6 @@ /* AD7124 input sources */ -enum ad7124_ids { - ID_AD7124_4, - ID_AD7124_8, -}; - enum ad7124_ref_sel { AD7124_REFIN1, AD7124_REFIN2, @@ -193,17 +188,16 @@ struct ad7124_state { DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS); }; -static struct ad7124_chip_info ad7124_chip_info_tbl[] = { - [ID_AD7124_4] = { - .name = "ad7124-4", - .chip_id = AD7124_ID_DEVICE_ID_AD7124_4, - .num_inputs = 8, - }, - [ID_AD7124_8] = { - .name = "ad7124-8", - .chip_id = AD7124_ID_DEVICE_ID_AD7124_8, - .num_inputs = 16, - }, +static const struct ad7124_chip_info ad7124_4_chip_info = { + .name = "ad7124-4", + .chip_id = AD7124_ID_DEVICE_ID_AD7124_4, + .num_inputs = 8, +}; + +static const struct ad7124_chip_info ad7124_8_chip_info = { + .name = "ad7124-8", + .chip_id = AD7124_ID_DEVICE_ID_AD7124_8, + .num_inputs = 16, }; static int ad7124_find_closest_match(const int *array, @@ -1341,17 +1335,15 @@ static int ad7124_probe(struct spi_device *spi) } static const struct of_device_id ad7124_of_match[] = { - { .compatible = "adi,ad7124-4", - .data = &ad7124_chip_info_tbl[ID_AD7124_4], }, - { .compatible = "adi,ad7124-8", - .data = &ad7124_chip_info_tbl[ID_AD7124_8], }, + { .compatible = "adi,ad7124-4", .data = &ad7124_4_chip_info }, + { .compatible = "adi,ad7124-8", .data = &ad7124_8_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad7124_of_match); static const struct spi_device_id ad71124_ids[] = { - { "ad7124-4", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_4] }, - { "ad7124-8", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_8] }, + { "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info }, + { "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad71124_ids); diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index b3e6bd2a55d7..4413207be28f 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -228,7 +228,6 @@ struct ad7173_state { 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; struct regmap *reg_gpiocon_regmap; struct gpio_regmap *gpio_regmap; @@ -319,7 +318,7 @@ static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev, { struct ad7173_state *st = iio_priv(indio_dev); - st->channels[chan->channel].syscalib_mode = mode; + st->channels[chan->address].syscalib_mode = mode; return 0; } @@ -329,7 +328,7 @@ static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev, { struct ad7173_state *st = iio_priv(indio_dev); - return st->channels[chan->channel].syscalib_mode; + return st->channels[chan->address].syscalib_mode; } static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, @@ -348,7 +347,7 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - mode = st->channels[chan->channel].syscalib_mode; + mode = st->channels[chan->address].syscalib_mode; if (sys_calib) { if (mode == AD7173_SYSCALIB_ZERO_SCALE) ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO, @@ -392,13 +391,12 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d if (indio_dev->channels[i].type != IIO_VOLTAGE) continue; - ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain); + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, i); if (ret < 0) return ret; if (st->info->has_internal_fs_calibration) { - ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, - st->channels[i].ain); + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, i); if (ret < 0) return ret; } @@ -772,10 +770,26 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = { .num_slots = 8, }; +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 16, +}; + static const struct ad7173_device_info ad4111_device_info = { .name = "ad4111", .id = AD4111_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 8, .num_channels = 16, .num_configs = 8, @@ -797,7 +811,7 @@ static const struct ad7173_device_info ad4111_device_info = { static const struct ad7173_device_info ad4112_device_info = { .name = "ad4112", .id = AD4112_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 8, .num_channels = 16, .num_configs = 8, @@ -818,7 +832,7 @@ static const struct ad7173_device_info ad4112_device_info = { static const struct ad7173_device_info ad4113_device_info = { .name = "ad4113", .id = AD4113_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 8, .num_channels = 16, .num_configs = 8, @@ -837,7 +851,7 @@ static const struct ad7173_device_info ad4113_device_info = { static const struct ad7173_device_info ad4114_device_info = { .name = "ad4114", .id = AD4114_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 16, .num_channels = 16, .num_configs = 8, @@ -856,7 +870,7 @@ static const struct ad7173_device_info ad4114_device_info = { static const struct ad7173_device_info ad4115_device_info = { .name = "ad4115", .id = AD4115_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 16, .num_channels = 16, .num_configs = 8, @@ -875,7 +889,7 @@ static const struct ad7173_device_info ad4115_device_info = { static const struct ad7173_device_info ad4116_device_info = { .name = "ad4116", .id = AD4116_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 11, .num_channels = 16, .num_configs = 8, @@ -894,7 +908,7 @@ static const struct ad7173_device_info ad4116_device_info = { static const struct ad7173_device_info ad7172_2_device_info = { .name = "ad7172-2", .id = AD7172_2_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_4_slots, .num_voltage_in = 5, .num_channels = 4, .num_configs = 4, @@ -927,7 +941,7 @@ static const struct ad7173_device_info ad7172_4_device_info = { static const struct ad7173_device_info ad7173_8_device_info = { .name = "ad7173-8", .id = AD7173_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in = 17, .num_channels = 16, .num_configs = 8, @@ -944,7 +958,7 @@ static const struct ad7173_device_info ad7173_8_device_info = { static const struct ad7173_device_info ad7175_2_device_info = { .name = "ad7175-2", .id = AD7175_2_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_4_slots, .num_voltage_in = 5, .num_channels = 4, .num_configs = 4, @@ -961,7 +975,7 @@ static const struct ad7173_device_info ad7175_2_device_info = { static const struct ad7173_device_info ad7175_8_device_info = { .name = "ad7175-8", .id = AD7175_8_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in = 17, .num_channels = 16, .num_configs = 8, @@ -1344,11 +1358,6 @@ static void ad7173_disable_regulators(void *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) { @@ -1580,6 +1589,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.bipolar = false; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + chan_st_priv->cfg.odr = st->info->odr_start_value; chan_st_priv->cfg.openwire_comp_chan = -1; st->adc_mode |= AD7173_ADC_MODE_REF_EN; if (st->info->data_reg_only_16bit) @@ -1646,7 +1656,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->scan_index = chan_index; chan->channel = ain[0]; chan_st_priv->cfg.input_buf = st->info->has_input_buf; - chan_st_priv->cfg.odr = 0; + chan_st_priv->cfg.odr = st->info->odr_start_value; chan_st_priv->cfg.openwire_comp_chan = -1; chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); @@ -1718,22 +1728,14 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) AD7173_ADC_MODE_CLOCKSEL_INT); ad7173_register_clk_provider(indio_dev); } else { + struct clk *clk; + 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), + clk = devm_clk_get_enabled(dev, ad7173_clk_sel[ret]); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(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; } return ad7173_fw_parse_channel_config(indio_dev); @@ -1765,7 +1767,9 @@ static int ad7173_probe(struct spi_device *spi) indio_dev->info = &ad7173_info; spi->mode = SPI_MODE_3; - spi_setup(spi); + ret = spi_setup(spi); + if (ret) + return ret; ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info); if (ret) diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c index cabf5511d116..6f7034b6c266 100644 --- a/drivers/iio/adc/ad7380.c +++ b/drivers/iio/adc/ad7380.c @@ -1165,7 +1165,6 @@ static int ad7380_init_offload_msg(struct ad7380_state *st, struct spi_transfer *xfer = &st->offload_xfer; struct device *dev = &st->spi->dev; const struct iio_scan_type *scan_type; - int oversampling_ratio; int ret; scan_type = iio_get_current_scan_type(indio_dev, @@ -1195,10 +1194,6 @@ static int ad7380_init_offload_msg(struct ad7380_state *st, } } - ret = ad7380_get_osr(st, &oversampling_ratio); - if (ret) - return ret; - xfer->bits_per_word = scan_type->realbits; xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels; diff --git a/drivers/iio/adc/ad7405.c b/drivers/iio/adc/ad7405.c new file mode 100644 index 000000000000..9adf85a732ce --- /dev/null +++ b/drivers/iio/adc/ad7405.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD7405 driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/util_macros.h> + +#include <linux/iio/backend.h> +#include <linux/iio/iio.h> + +static const unsigned int ad7405_dec_rates_range[] = { + 32, 1, 4096, +}; + +struct ad7405_chip_info { + const char *name; + const unsigned int full_scale_mv; +}; + +struct ad7405_state { + struct iio_backend *back; + const struct ad7405_chip_info *info; + unsigned int ref_frequency; + unsigned int dec_rate; +}; + +static int ad7405_set_dec_rate(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int dec_rate) +{ + struct ad7405_state *st = iio_priv(indio_dev); + int ret; + + if (dec_rate > 4096 || dec_rate < 32) + return -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = iio_backend_oversampling_ratio_set(st->back, chan->scan_index, dec_rate); + iio_device_release_direct(indio_dev); + + if (ret < 0) + return ret; + + st->dec_rate = dec_rate; + + return 0; +} + +static int ad7405_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long info) +{ + struct ad7405_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *val = st->info->full_scale_mv; + *val2 = indio_dev->channels[0].scan_type.realbits - 1; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->dec_rate; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = DIV_ROUND_CLOSEST_ULL(st->ref_frequency, st->dec_rate); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = -(1 << (indio_dev->channels[0].scan_type.realbits - 1)); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7405_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val < 0) + return -EINVAL; + return ad7405_set_dec_rate(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad7405_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = ad7405_dec_rates_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static const struct iio_info ad7405_iio_info = { + .read_raw = &ad7405_read_raw, + .write_raw = &ad7405_write_raw, + .read_avail = &ad7405_read_avail, +}; + +static const struct iio_chan_spec ad7405_channel = { + .type = IIO_VOLTAGE, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_all = IIO_CHAN_INFO_SAMP_FREQ | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .indexed = 1, + .channel = 0, + .channel2 = 1, + .differential = 1, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + }, +}; + +static const struct ad7405_chip_info ad7405_chip_info = { + .name = "ad7405", + .full_scale_mv = 320, +}; + +static const struct ad7405_chip_info adum7701_chip_info = { + .name = "adum7701", + .full_scale_mv = 320, +}; + +static const struct ad7405_chip_info adum7702_chip_info = { + .name = "adum7702", + .full_scale_mv = 64, +}; + +static const struct ad7405_chip_info adum7703_chip_info = { + .name = "adum7703", + .full_scale_mv = 320, +}; + +static const char * const ad7405_power_supplies[] = { + "vdd1", "vdd2", +}; + +static int ad7405_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct ad7405_state *st; + struct clk *clk; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->info = device_get_match_data(dev); + if (!st->info) + return dev_err_probe(dev, -EINVAL, "no chip info\n"); + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad7405_power_supplies), + ad7405_power_supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get and enable supplies"); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + st->ref_frequency = clk_get_rate(clk); + if (!st->ref_frequency) + return -EINVAL; + + indio_dev->name = st->info->name; + indio_dev->channels = &ad7405_channel; + indio_dev->num_channels = 1; + indio_dev->info = &ad7405_iio_info; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return dev_err_probe(dev, PTR_ERR(st->back), + "failed to get IIO backend"); + + ret = iio_backend_chan_enable(st->back, 0); + if (ret) + return ret; + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + /* + * Set 256 decimation rate. The default value in the AXI_ADC register + * is 0, so we set the register with a decimation rate value that is + * functional for all parts. + */ + ret = ad7405_set_dec_rate(indio_dev, &indio_dev->channels[0], 256); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7405_of_match[] = { + { .compatible = "adi,ad7405", .data = &ad7405_chip_info, }, + { .compatible = "adi,adum7701", .data = &adum7701_chip_info, }, + { .compatible = "adi,adum7702", .data = &adum7702_chip_info, }, + { .compatible = "adi,adum7703", .data = &adum7703_chip_info, }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7405_of_match); + +static struct platform_driver ad7405_driver = { + .driver = { + .name = "ad7405", + .of_match_table = ad7405_of_match, + }, + .probe = ad7405_probe, +}; +module_platform_driver(ad7405_driver); + +MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>"); +MODULE_AUTHOR("Pop Ioan Daniel <pop.ioan-daniel@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7405 driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_BACKEND"); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index ddb607ac1860..aea734aa06bd 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -435,6 +435,13 @@ static const struct spi_device_id ad7476_id[] = { { "ads7866", ID_ADS7866 }, { "ads7867", ID_ADS7867 }, { "ads7868", ID_ADS7868 }, + /* + * The ROHM BU79100G is identical to the TI's ADS7866 from the software + * point of view. The binding document mandates the ADS7866 to be + * marked as a fallback for the BU79100G, but we still need the SPI ID + * here to make the module loading work. + */ + { "bu79100g", ID_ADS7866 }, { "ltc2314-14", ID_LTC2314_14 }, { } }; diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 185243dee86e..d9271894f091 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -33,6 +33,10 @@ #include "ad7606.h" +#define AD7606_CALIB_GAIN_MIN 0 +#define AD7606_CALIB_GAIN_STEP 1024 +#define AD7606_CALIB_GAIN_MAX (63 * AD7606_CALIB_GAIN_STEP) + /* * Scales are computed as 5000/32768 and 10000/32768 respectively, * so that when applied to the raw values they provide mV values. @@ -95,6 +99,22 @@ static const unsigned int ad7616_oversampling_avail[8] = { 1, 2, 4, 8, 16, 32, 64, 128, }; +static const int ad7606_calib_offset_avail[3] = { + -128, 1, 127, +}; + +static const int ad7606c_18bit_calib_offset_avail[3] = { + -512, 4, 508, +}; + +static const int ad7606b_calib_phase_avail[][2] = { + { 0, 0 }, { 0, 1250 }, { 0, 318750 }, +}; + +static const int ad7606c_calib_phase_avail[][2] = { + { 0, 0 }, { 0, 1000 }, { 0, 255000 }, +}; + static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan); static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, @@ -164,6 +184,9 @@ const struct ad7606_chip_info ad7606b_info = { .scale_setup_cb = ad7606_16bit_chan_scale_setup, .sw_setup_cb = ad7606b_sw_mode_setup, .offload_storagebits = 32, + .calib_gain_avail = true, + .calib_offset_avail = ad7606_calib_offset_avail, + .calib_phase_avail = ad7606b_calib_phase_avail, }; EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606"); @@ -177,6 +200,9 @@ const struct ad7606_chip_info ad7606c_16_info = { .scale_setup_cb = ad7606c_16bit_chan_scale_setup, .sw_setup_cb = ad7606b_sw_mode_setup, .offload_storagebits = 32, + .calib_gain_avail = true, + .calib_offset_avail = ad7606_calib_offset_avail, + .calib_phase_avail = ad7606c_calib_phase_avail, }; EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606"); @@ -226,6 +252,9 @@ const struct ad7606_chip_info ad7606c_18_info = { .scale_setup_cb = ad7606c_18bit_chan_scale_setup, .sw_setup_cb = ad7606b_sw_mode_setup, .offload_storagebits = 32, + .calib_gain_avail = true, + .calib_offset_avail = ad7606c_18bit_calib_offset_avail, + .calib_phase_avail = ad7606c_calib_phase_avail, }; EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606"); @@ -261,21 +290,21 @@ static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; if (!st->sw_mode_en) { /* tied to logic low, analog input range is +/- 5V */ - cs->range = 0; - cs->scale_avail = ad7606_16bit_hw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); + ci->range = 0; + ci->scale_avail = ad7606_16bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); return 0; } /* Scale of 0.076293 is only available in sw mode */ /* After reset, in software mode, ±10 V is set by default */ - cs->range = 2; - cs->scale_avail = ad7606_16bit_sw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail); + ci->range = 2; + ci->scale_avail = ad7606_16bit_sw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail); return 0; } @@ -284,6 +313,7 @@ static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, bool *bipolar, bool *differential) { struct ad7606_state *st = iio_priv(indio_dev); + struct ad7606_chan_info *ci; unsigned int num_channels = st->chip_info->num_adc_channels; struct device *dev = st->dev; int ret; @@ -297,15 +327,13 @@ static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, ret = fwnode_property_read_u32(child, "reg", ®); if (ret) - continue; + return ret; /* channel number (here) is from 1 to num_channels */ - if (reg < 1 || reg > num_channels) { - dev_warn(dev, - "Invalid channel number (ignoring): %d\n", reg); - continue; - } + if (reg < 1 || reg > num_channels) + return -EINVAL; + /* Loop until we are in the right channel. */ if (reg != (ch + 1)) continue; @@ -329,6 +357,14 @@ static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch, return -EINVAL; } + ci = &st->chan_info[reg - 1]; + + ci->r_gain = 0; + ret = fwnode_property_read_u32(child, "adi,rfilter-ohms", + &ci->r_gain); + if (ret == 0 && ci->r_gain > AD7606_CALIB_GAIN_MAX) + return -EINVAL; + return 0; } @@ -339,14 +375,14 @@ static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; bool bipolar, differential; int ret; if (!st->sw_mode_en) { - cs->range = 0; - cs->scale_avail = ad7606_18bit_hw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); + ci->range = 0; + ci->scale_avail = ad7606_18bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); return 0; } @@ -356,12 +392,12 @@ static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, return ret; if (differential) { - cs->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail; - cs->num_scales = + ci->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606c_18bit_differential_bipolar_scale_avail); /* Bipolar differential ranges start at 8 (b1000) */ - cs->reg_offset = 8; - cs->range = 1; + ci->reg_offset = 8; + ci->range = 1; chan->differential = 1; chan->channel2 = chan->channel; @@ -371,23 +407,23 @@ static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev, chan->differential = 0; if (bipolar) { - cs->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail; - cs->num_scales = + ci->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606c_18bit_single_ended_bipolar_scale_avail); /* Bipolar single-ended ranges start at 0 (b0000) */ - cs->reg_offset = 0; - cs->range = 3; + ci->reg_offset = 0; + ci->range = 3; chan->scan_type.sign = 's'; return 0; } - cs->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail; - cs->num_scales = + ci->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606c_18bit_single_ended_unipolar_scale_avail); /* Unipolar single-ended ranges start at 5 (b0101) */ - cs->reg_offset = 5; - cs->range = 1; + ci->reg_offset = 5; + ci->range = 1; chan->scan_type.sign = 'u'; return 0; @@ -397,14 +433,14 @@ static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; bool bipolar, differential; int ret; if (!st->sw_mode_en) { - cs->range = 0; - cs->scale_avail = ad7606_16bit_hw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); + ci->range = 0; + ci->scale_avail = ad7606_16bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail); return 0; } @@ -414,12 +450,12 @@ static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, return ret; if (differential) { - cs->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail; - cs->num_scales = + ci->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606c_16bit_differential_bipolar_scale_avail); /* Bipolar differential ranges start at 8 (b1000) */ - cs->reg_offset = 8; - cs->range = 1; + ci->reg_offset = 8; + ci->range = 1; chan->differential = 1; chan->channel2 = chan->channel; chan->scan_type.sign = 's'; @@ -430,23 +466,23 @@ static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev, chan->differential = 0; if (bipolar) { - cs->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail; - cs->num_scales = + ci->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606c_16bit_single_ended_bipolar_scale_avail); /* Bipolar single-ended ranges start at 0 (b0000) */ - cs->reg_offset = 0; - cs->range = 3; + ci->reg_offset = 0; + ci->range = 3; chan->scan_type.sign = 's'; return 0; } - cs->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail; - cs->num_scales = + ci->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606c_16bit_single_ended_unipolar_scale_avail); /* Unipolar single-ended ranges start at 5 (b0101) */ - cs->reg_offset = 5; - cs->range = 1; + ci->reg_offset = 5; + ci->range = 1; chan->scan_type.sign = 'u'; return 0; @@ -456,11 +492,11 @@ static int ad7607_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; - cs->range = 0; - cs->scale_avail = ad7607_hw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail); + ci->range = 0; + ci->scale_avail = ad7607_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail); return 0; } @@ -468,11 +504,11 @@ static int ad7608_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; - cs->range = 0; - cs->scale_avail = ad7606_18bit_hw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); + ci->range = 0; + ci->scale_avail = ad7606_18bit_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail); return 0; } @@ -480,11 +516,11 @@ static int ad7609_chan_scale_setup(struct iio_dev *indio_dev, struct iio_chan_spec *chan) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index]; + struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index]; - cs->range = 0; - cs->scale_avail = ad7609_hw_scale_avail; - cs->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail); + ci->range = 0; + ci->scale_avail = ad7609_hw_scale_avail; + ci->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail); return 0; } @@ -681,6 +717,40 @@ error_ret: return ret; } +static int ad7606_get_calib_offset(struct ad7606_state *st, int ch, int *val) +{ + int ret; + + ret = st->bops->reg_read(st, AD7606_CALIB_OFFSET(ch)); + if (ret < 0) + return ret; + + *val = st->chip_info->calib_offset_avail[0] + + ret * st->chip_info->calib_offset_avail[1]; + + return 0; +} + +static int ad7606_get_calib_phase(struct ad7606_state *st, int ch, int *val, + int *val2) +{ + int ret; + + ret = st->bops->reg_read(st, AD7606_CALIB_PHASE(ch)); + if (ret < 0) + return ret; + + *val = 0; + + /* + * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs. + * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs. + */ + *val2 = ret * st->chip_info->calib_phase_avail[1][1]; + + return 0; +} + static int ad7606_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -689,7 +759,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, { int ret, ch = 0; struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs; + struct ad7606_chan_info *ci; struct pwm_state cnvst_pwm_state; switch (m) { @@ -704,9 +774,9 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: if (st->sw_mode_en) ch = chan->scan_index; - cs = &st->chan_scales[ch]; - *val = cs->scale_avail[cs->range][0]; - *val2 = cs->scale_avail[cs->range][1]; + ci = &st->chan_info[ch]; + *val = ci->scale_avail[ci->range][0]; + *val2 = ci->scale_avail[ci->range][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = st->oversampling; @@ -715,6 +785,22 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state); *val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period); return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_get_calib_offset(st, chan->scan_index, val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CONVDELAY: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_get_calib_phase(st, chan->scan_index, val, val2); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + return IIO_VAL_INT_PLUS_NANO; } return -EINVAL; } @@ -725,12 +811,12 @@ static ssize_t in_voltage_scale_available_show(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs = &st->chan_scales[0]; - const unsigned int (*vals)[2] = cs->scale_avail; + struct ad7606_chan_info *ci = &st->chan_info[0]; + const unsigned int (*vals)[2] = ci->scale_avail; unsigned int i; size_t len = 0; - for (i = 0; i < cs->num_scales; i++) + for (i = 0; i < ci->num_scales; i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", vals[i][0], vals[i][1]); buf[len - 1] = '\n'; @@ -765,6 +851,64 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val) return 0; } +static int ad7606_set_calib_offset(struct ad7606_state *st, int ch, int val) +{ + int start_val, step_val, stop_val; + int offset; + + start_val = st->chip_info->calib_offset_avail[0]; + step_val = st->chip_info->calib_offset_avail[1]; + stop_val = st->chip_info->calib_offset_avail[2]; + + if (val < start_val || val > stop_val) + return -EINVAL; + + offset = (val - start_val) / step_val; + + return st->bops->reg_write(st, AD7606_CALIB_OFFSET(ch), offset); +} + +static int ad7606_set_calib_phase(struct ad7606_state *st, int ch, int val, + int val2) +{ + int wreg, start_ns, step_ns, stop_ns; + + if (val != 0) + return -EINVAL; + + start_ns = st->chip_info->calib_phase_avail[0][1]; + step_ns = st->chip_info->calib_phase_avail[1][1]; + stop_ns = st->chip_info->calib_phase_avail[2][1]; + + /* + * ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs. + * ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs. + */ + if (val2 < start_ns || val2 > stop_ns) + return -EINVAL; + + wreg = val2 / step_ns; + + return st->bops->reg_write(st, AD7606_CALIB_PHASE(ch), wreg); +} + +static int ad7606_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + case IIO_CHAN_INFO_CALIBBIAS: + return IIO_VAL_INT; + case IIO_CHAN_INFO_CONVDELAY: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + static int ad7606_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, @@ -773,7 +917,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, { struct ad7606_state *st = iio_priv(indio_dev); unsigned int scale_avail_uv[AD760X_MAX_SCALES]; - struct ad7606_chan_scale *cs; + struct ad7606_chan_info *ci; int i, ret, ch = 0; guard(mutex)(&st->lock); @@ -782,21 +926,21 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: if (st->sw_mode_en) ch = chan->scan_index; - cs = &st->chan_scales[ch]; - for (i = 0; i < cs->num_scales; i++) { - scale_avail_uv[i] = cs->scale_avail[i][0] * MICRO + - cs->scale_avail[i][1]; + ci = &st->chan_info[ch]; + for (i = 0; i < ci->num_scales; i++) { + scale_avail_uv[i] = ci->scale_avail[i][0] * MICRO + + ci->scale_avail[i][1]; } val = (val * MICRO) + val2; - i = find_closest(val, scale_avail_uv, cs->num_scales); + i = find_closest(val, scale_avail_uv, ci->num_scales); if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - ret = st->write_scale(indio_dev, ch, i + cs->reg_offset); + ret = st->write_scale(indio_dev, ch, i + ci->reg_offset); iio_device_release_direct(indio_dev); if (ret < 0) return ret; - cs->range = i; + ci->range = i; return 0; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -818,6 +962,18 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, if (val < 0 && val2 != 0) return -EINVAL; return ad7606_set_sampling_freq(st, val); + case IIO_CHAN_INFO_CALIBBIAS: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_set_calib_offset(st, chan->scan_index, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_CONVDELAY: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + ret = ad7606_set_calib_phase(st, chan->scan_index, val, val2); + iio_device_release_direct(indio_dev); + return ret; default: return -EINVAL; } @@ -975,7 +1131,7 @@ static int ad7606_read_avail(struct iio_dev *indio_dev, long info) { struct ad7606_state *st = iio_priv(indio_dev); - struct ad7606_chan_scale *cs; + struct ad7606_chan_info *ci; unsigned int ch = 0; switch (info) { @@ -990,12 +1146,20 @@ static int ad7606_read_avail(struct iio_dev *indio_dev, if (st->sw_mode_en) ch = chan->scan_index; - cs = &st->chan_scales[ch]; - *vals = (int *)cs->scale_avail; - *length = cs->num_scales * 2; + ci = &st->chan_info[ch]; + *vals = (int *)ci->scale_avail; + *length = ci->num_scales * 2; *type = IIO_VAL_INT_PLUS_MICRO; return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals = st->chip_info->calib_offset_avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_CONVDELAY: + *vals = (const int *)st->chip_info->calib_phase_avail; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_RANGE; } return -EINVAL; } @@ -1058,6 +1222,7 @@ static const struct iio_info ad7606_info_sw_mode = { .read_raw = &ad7606_read_raw, .write_raw = &ad7606_write_raw, .read_avail = &ad7606_read_avail, + .write_raw_get_fmt = ad7606_write_raw_get_fmt, .debugfs_reg_access = &ad7606_reg_access, .validate_trigger = &ad7606_validate_trigger, .update_scan_mode = &ad7606_update_scan_mode, @@ -1203,6 +1368,23 @@ static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev) return st->bops->sw_mode_config(indio_dev); } +static int ad7606_set_gain_calib(struct ad7606_state *st) +{ + struct ad7606_chan_info *ci; + int i, ret; + + for (i = 0; i < st->chip_info->num_adc_channels; i++) { + ci = &st->chan_info[i]; + ret = st->bops->reg_write(st, AD7606_CALIB_GAIN(i), + DIV_ROUND_CLOSEST(ci->r_gain, + AD7606_CALIB_GAIN_STEP)); + if (ret) + return ret; + } + + return 0; +} + static int ad7606_probe_channels(struct iio_dev *indio_dev) { struct ad7606_state *st = iio_priv(indio_dev); @@ -1250,6 +1432,15 @@ static int ad7606_probe_channels(struct iio_dev *indio_dev) chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_SCALE); + if (st->chip_info->calib_offset_avail) { + chan->info_mask_separate |= + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CONVDELAY); + chan->info_mask_separate_available |= + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CONVDELAY); + } + /* * All chips with software mode support oversampling, * so we skip the oversampling_available check. And the @@ -1330,6 +1521,16 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return dev_err_probe(dev, ret, "Failed to enable specified AVcc supply\n"); + ret = devm_regulator_get_enable(dev, "vdrive"); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable Vdrive supply\n"); + + ret = devm_regulator_get_enable_optional(dev, "refin"); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to enable REFIN supply\n"); + st->chip_info = chip_info; if (st->chip_info->oversampling_num) { @@ -1462,6 +1663,12 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, st->chip_info->sw_setup_cb(indio_dev); } + if (st->sw_mode_en && st->chip_info->calib_gain_avail) { + ret = ad7606_set_gain_calib(st); + if (ret) + return ret; + } + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606"); @@ -1487,7 +1694,7 @@ static int ad7606_resume(struct device *dev) struct ad7606_state *st = iio_priv(indio_dev); if (st->gpio_standby) { - gpiod_set_value(st->gpio_range, st->chan_scales[0].range); + gpiod_set_value(st->gpio_range, st->chan_info[0].range); gpiod_set_value(st->gpio_standby, 1); ad7606_reset(st); } diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 441e62c521bc..2951bb731354 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -40,6 +40,11 @@ #define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1)) #define AD7606_OS_MODE 0x08 +#define AD7606_CALIB_GAIN(ch) (0x09 + (ch)) +#define AD7606_CALIB_GAIN_MASK GENMASK(5, 0) +#define AD7606_CALIB_OFFSET(ch) (0x11 + (ch)) +#define AD7606_CALIB_PHASE(ch) (0x19 + (ch)) + struct ad7606_state; typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev, @@ -61,6 +66,9 @@ typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev); * @init_delay_ms: required delay in milliseconds for initialization * after a restart * @offload_storagebits: storage bits used by the offload hw implementation + * @calib_gain_avail: chip supports gain calibration + * @calib_offset_avail: pointer to offset calibration range/limits array + * @calib_phase_avail: pointer to phase calibration range/limits array */ struct ad7606_chip_info { unsigned int max_samplerate; @@ -74,22 +82,28 @@ struct ad7606_chip_info { bool os_req_reset; unsigned long init_delay_ms; u8 offload_storagebits; + bool calib_gain_avail; + const int *calib_offset_avail; + const int (*calib_phase_avail)[2]; }; /** - * struct ad7606_chan_scale - channel scale configuration + * struct ad7606_chan_info - channel configuration * @scale_avail: pointer to the array which stores the available scales * @num_scales: number of elements stored in the scale_avail array * @range: voltage range selection, selects which scale to apply * @reg_offset: offset for the register value, to be applied when * writing the value of 'range' to the register value + * @r_gain: gain resistor value in ohms, to be set to match the + * external r_filter value */ -struct ad7606_chan_scale { +struct ad7606_chan_info { #define AD760X_MAX_SCALES 16 const unsigned int (*scale_avail)[2]; unsigned int num_scales; unsigned int range; unsigned int reg_offset; + unsigned int r_gain; }; /** @@ -97,7 +111,7 @@ struct ad7606_chan_scale { * @dev: pointer to kernel device * @chip_info: entry in the table of chips that describes this device * @bops: bus operations (SPI or parallel) - * @chan_scales: scale configuration for channels + * @chan_info: scale configuration for channels * @oversampling: oversampling selection * @cnvst_pwm: pointer to the PWM device connected to the cnvst pin * @base_address: address from where to read data in parallel operation @@ -128,7 +142,7 @@ struct ad7606_state { struct device *dev; const struct ad7606_chip_info *chip_info; const struct ad7606_bus_ops *bops; - struct ad7606_chan_scale chan_scales[AD760X_MAX_CHANNELS]; + struct ad7606_chan_info chan_info[AD760X_MAX_CHANNELS]; unsigned int oversampling; struct pwm_device *cnvst_pwm; void __iomem *base_address; diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 51134023534a..a2e061f0cb08 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -11,13 +11,19 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/gpio/driver.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> #include <linux/sysfs.h> #include <linux/spi/spi.h> +#include <linux/unaligned.h> +#include <linux/units.h> +#include <linux/util_macros.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> @@ -26,6 +32,8 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> +#include <dt-bindings/iio/adc/adi,ad7768-1.h> + /* AD7768 registers definition */ #define AD7768_REG_CHIP_TYPE 0x3 #define AD7768_REG_PROD_ID_L 0x4 @@ -73,6 +81,7 @@ #define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x) /* AD7768_REG_DIGITAL_FILTER */ +#define AD7768_DIG_FIL_EN_60HZ_REJ BIT(7) #define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4) #define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x) #define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0) @@ -82,6 +91,26 @@ #define AD7768_CONV_MODE_MSK GENMASK(2, 0) #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) +/* AD7768_REG_ANALOG2 */ +#define AD7768_REG_ANALOG2_VCM_MSK GENMASK(2, 0) +#define AD7768_REG_ANALOG2_VCM(x) FIELD_PREP(AD7768_REG_ANALOG2_VCM_MSK, (x)) + +/* AD7768_REG_GPIO_CONTROL */ +#define AD7768_GPIO_UNIVERSAL_EN BIT(7) +#define AD7768_GPIO_CONTROL_MSK GENMASK(3, 0) + +/* AD7768_REG_GPIO_WRITE */ +#define AD7768_GPIO_WRITE_MSK GENMASK(3, 0) + +/* AD7768_REG_GPIO_READ */ +#define AD7768_GPIO_READ_MSK GENMASK(3, 0) + +#define AD7768_VCM_OFF 0x07 + +#define AD7768_TRIGGER_SOURCE_SYNC_IDX 0 + +#define AD7768_MAX_CHANNELS 1 + enum ad7768_conv_mode { AD7768_CONTINUOUS, AD7768_ONE_SHOT, @@ -103,54 +132,84 @@ enum ad7768_mclk_div { AD7768_MCLK_DIV_2 }; -enum ad7768_dec_rate { - AD7768_DEC_RATE_32 = 0, - AD7768_DEC_RATE_64 = 1, - AD7768_DEC_RATE_128 = 2, - AD7768_DEC_RATE_256 = 3, - AD7768_DEC_RATE_512 = 4, - AD7768_DEC_RATE_1024 = 5, - AD7768_DEC_RATE_8 = 9, - AD7768_DEC_RATE_16 = 10 +enum ad7768_filter_type { + AD7768_FILTER_SINC5, + AD7768_FILTER_SINC3, + AD7768_FILTER_WIDEBAND, + AD7768_FILTER_SINC3_REJ60, }; -struct ad7768_clk_configuration { - enum ad7768_mclk_div mclk_div; - enum ad7768_dec_rate dec_rate; - unsigned int clk_div; - enum ad7768_pwrmode pwrmode; +enum ad7768_filter_regval { + AD7768_FILTER_REGVAL_SINC5 = 0, + AD7768_FILTER_REGVAL_SINC5_X8 = 1, + AD7768_FILTER_REGVAL_SINC5_X16 = 2, + AD7768_FILTER_REGVAL_SINC3 = 3, + AD7768_FILTER_REGVAL_WIDEBAND = 4, + AD7768_FILTER_REGVAL_SINC3_REJ60 = 11, }; -static const struct ad7768_clk_configuration ad7768_clk_config[] = { - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE }, - { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE }, +enum ad7768_scan_type { + AD7768_SCAN_TYPE_NORMAL, + AD7768_SCAN_TYPE_HIGH_SPEED, }; -static const struct iio_chan_spec ad7768_channels[] = { - { - .type = IIO_VOLTAGE, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .indexed = 1, - .channel = 0, - .scan_index = 0, - .scan_type = { - .sign = 's', - .realbits = 24, - .storagebits = 32, - .shift = 8, - .endianness = IIO_BE, - }, +/* -3dB cutoff frequency multipliers (relative to ODR) for each filter type. */ +static const int ad7768_filter_3db_odr_multiplier[] = { + [AD7768_FILTER_SINC5] = 204, /* 0.204 */ + [AD7768_FILTER_SINC3] = 262, /* 0.2617 */ + [AD7768_FILTER_SINC3_REJ60] = 262, /* 0.2617 */ + [AD7768_FILTER_WIDEBAND] = 433, /* 0.433 */ +}; + +static const int ad7768_mclk_div_rates[] = { + 16, 8, 4, 2, +}; + +static const int ad7768_dec_rate_values[8] = { + 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +/* Decimation rate range for sinc3 filter */ +static const int ad7768_sinc3_dec_rate_range[3] = { + 32, 32, 163840, +}; + +/* + * The AD7768-1 supports three primary filter types: + * Sinc5, Sinc3, and Wideband. + * However, the filter register values can also encode additional parameters + * such as decimation rates and 60Hz rejection. This utility array separates + * the filter type from these parameters. + */ +static const int ad7768_filter_regval_to_type[] = { + [AD7768_FILTER_REGVAL_SINC5] = AD7768_FILTER_SINC5, + [AD7768_FILTER_REGVAL_SINC5_X8] = AD7768_FILTER_SINC5, + [AD7768_FILTER_REGVAL_SINC5_X16] = AD7768_FILTER_SINC5, + [AD7768_FILTER_REGVAL_SINC3] = AD7768_FILTER_SINC3, + [AD7768_FILTER_REGVAL_WIDEBAND] = AD7768_FILTER_WIDEBAND, + [AD7768_FILTER_REGVAL_SINC3_REJ60] = AD7768_FILTER_SINC3_REJ60, +}; + +static const char * const ad7768_filter_enum[] = { + [AD7768_FILTER_SINC5] = "sinc5", + [AD7768_FILTER_SINC3] = "sinc3", + [AD7768_FILTER_WIDEBAND] = "wideband", + [AD7768_FILTER_SINC3_REJ60] = "sinc3+rej60", +}; + +static const struct iio_scan_type ad7768_scan_type[] = { + [AD7768_SCAN_TYPE_NORMAL] = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, + [AD7768_SCAN_TYPE_HIGH_SPEED] = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, }, }; @@ -159,14 +218,23 @@ struct ad7768_state { struct regmap *regmap; struct regmap *regmap24; struct regulator *vref; + struct regulator_dev *vcm_rdev; + unsigned int vcm_output_sel; struct clk *mclk; unsigned int mclk_freq; + unsigned int mclk_div; + unsigned int oversampling_ratio; + enum ad7768_filter_type filter_type; unsigned int samp_freq; + unsigned int samp_freq_avail[ARRAY_SIZE(ad7768_mclk_div_rates)]; + unsigned int samp_freq_avail_len; struct completion completion; struct iio_trigger *trig; struct gpio_desc *gpio_sync_in; struct gpio_desc *gpio_reset; - const char *labels[ARRAY_SIZE(ad7768_channels)]; + const char *labels[AD7768_MAX_CHANNELS]; + struct gpio_chip gpiochip; + bool en_spi_sync; /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. @@ -252,6 +320,63 @@ static const struct regmap_config ad7768_regmap24_config = { .max_register = AD7768_REG24_COEFF_DATA, }; +static int ad7768_send_sync_pulse(struct ad7768_state *st) +{ + if (st->en_spi_sync) + return regmap_write(st->regmap, AD7768_REG_SYNC_RESET, 0x00); + + /* + * The datasheet specifies a minimum SYNC_IN pulse width of 1.5 × Tmclk, + * where Tmclk is the MCLK period. The supported MCLK frequencies range + * from 0.6 MHz to 17 MHz, which corresponds to a minimum SYNC_IN pulse + * width of approximately 2.5 µs in the worst-case scenario (0.6 MHz). + * + * Add a delay to ensure the pulse width is always sufficient to + * trigger synchronization. + */ + gpiod_set_value_cansleep(st->gpio_sync_in, 1); + fsleep(3); + gpiod_set_value_cansleep(st->gpio_sync_in, 0); + + return 0; +} + +static void ad7768_fill_samp_freq_tbl(struct ad7768_state *st) +{ + unsigned int i, samp_freq_avail, freq_filtered; + unsigned int len = 0; + + freq_filtered = DIV_ROUND_CLOSEST(st->mclk_freq, st->oversampling_ratio); + for (i = 0; i < ARRAY_SIZE(ad7768_mclk_div_rates); i++) { + samp_freq_avail = DIV_ROUND_CLOSEST(freq_filtered, ad7768_mclk_div_rates[i]); + /* Sampling frequency cannot be lower than the minimum of 50 SPS */ + if (samp_freq_avail < 50) + continue; + + st->samp_freq_avail[len++] = samp_freq_avail; + } + + st->samp_freq_avail_len = len; +} + +static int ad7768_set_mclk_div(struct ad7768_state *st, unsigned int mclk_div) +{ + unsigned int mclk_div_value; + + mclk_div_value = AD7768_PWR_MCLK_DIV(mclk_div); + /* + * Set power mode based on mclk_div value. + * ECO_MODE is only recommended for MCLK_DIV = 16. + */ + mclk_div_value |= mclk_div > AD7768_MCLK_DIV_16 ? + AD7768_PWR_PWRMODE(AD7768_FAST_MODE) : + AD7768_PWR_PWRMODE(AD7768_ECO_MODE); + + return regmap_update_bits(st->regmap, AD7768_REG_POWER_CLOCK, + AD7768_PWR_MCLK_DIV_MSK | AD7768_PWR_PWRMODE_MSK, + mclk_div_value); +} + static int ad7768_set_mode(struct ad7768_state *st, enum ad7768_conv_mode mode) { @@ -280,6 +405,15 @@ static int ad7768_scan_direct(struct iio_dev *indio_dev) return ret; /* + * When the decimation rate is set to x8, the ADC data precision is + * reduced from 24 bits to 16 bits. Since the AD7768_REG_ADC_DATA + * register provides 24-bit data, the precision is reduced by + * right-shifting the read value by 8 bits. + */ + if (st->oversampling_ratio == 8) + readval >>= 8; + + /* * Any SPI configuration of the AD7768-1 can only be * performed in continuous conversion mode. */ @@ -323,96 +457,330 @@ static int ad7768_reg_access(struct iio_dev *indio_dev, return ret; } -static int ad7768_set_dig_fil(struct ad7768_state *st, - enum ad7768_dec_rate dec_rate) +static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st, + unsigned int dec_rate) { - unsigned int mode; + unsigned int max_dec_rate; + u8 dec_rate_reg[2]; + u16 regval; int ret; - if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16) - mode = AD7768_DIG_FIL_FIL(dec_rate); - else - mode = AD7768_DIG_FIL_DEC_RATE(dec_rate); + /* + * Maximum dec_rate is limited by the MCLK_DIV value and by the ODR. + * The edge case is for MCLK_DIV = 2, ODR = 50 SPS. + * max_dec_rate <= MCLK / (2 * 50) + */ + max_dec_rate = st->mclk_freq / 100; + dec_rate = clamp(dec_rate, 32, max_dec_rate); + /* + * Calculate the equivalent value to sinc3 decimation ratio + * to be written on the SINC3_DEC_RATE register: + * Value = (DEC_RATE / 32) - 1 + */ + dec_rate = DIV_ROUND_UP(dec_rate, 32) - 1; - ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, mode); - if (ret < 0) + /* + * The SINC3_DEC_RATE value is a 13-bit value split across two + * registers: MSB [12:8] and LSB [7:0]. Prepare the 13-bit value using + * FIELD_PREP() and store it with the right endianness in dec_rate_reg. + */ + regval = FIELD_PREP(GENMASK(12, 0), dec_rate); + put_unaligned_be16(regval, dec_rate_reg); + ret = regmap_bulk_write(st->regmap, AD7768_REG_SINC3_DEC_RATE_MSB, + dec_rate_reg, 2); + if (ret) return ret; - /* A sync-in pulse is required every time the filter dec rate changes */ - gpiod_set_value(st->gpio_sync_in, 1); - gpiod_set_value(st->gpio_sync_in, 0); + st->oversampling_ratio = (dec_rate + 1) * 32; return 0; } -static int ad7768_set_freq(struct ad7768_state *st, - unsigned int freq) +static int ad7768_configure_dig_fil(struct iio_dev *dev, + enum ad7768_filter_type filter_type, + unsigned int dec_rate) { - unsigned int diff_new, diff_old, pwr_mode, i, idx; - int res, ret; + struct ad7768_state *st = iio_priv(dev); + unsigned int dec_rate_idx, dig_filter_regval; + int ret; - diff_old = U32_MAX; - idx = 0; + switch (filter_type) { + case AD7768_FILTER_SINC3: + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC3); + break; + case AD7768_FILTER_SINC3_REJ60: + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC3) | + AD7768_DIG_FIL_EN_60HZ_REJ; + break; + case AD7768_FILTER_WIDEBAND: + /* Skip decimations 8 and 16, not supported by the wideband filter */ + dec_rate_idx = find_closest(dec_rate, &ad7768_dec_rate_values[2], + ARRAY_SIZE(ad7768_dec_rate_values) - 2); + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_WIDEBAND) | + AD7768_DIG_FIL_DEC_RATE(dec_rate_idx); + /* Correct the index offset */ + dec_rate_idx += 2; + break; + case AD7768_FILTER_SINC5: + dec_rate_idx = find_closest(dec_rate, ad7768_dec_rate_values, + ARRAY_SIZE(ad7768_dec_rate_values)); - res = DIV_ROUND_CLOSEST(st->mclk_freq, freq); + /* + * Decimations 8 (idx 0) and 16 (idx 1) are set in the + * FILTER[6:4] field. The other decimations are set in the + * DEC_RATE[2:0] field, and the idx needs to be offsetted by two. + */ + if (dec_rate_idx == 0) + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC5_X8); + else if (dec_rate_idx == 1) + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC5_X16); + else + dig_filter_regval = AD7768_DIG_FIL_FIL(AD7768_FILTER_REGVAL_SINC5) | + AD7768_DIG_FIL_DEC_RATE(dec_rate_idx - 2); + break; + } - /* Find the closest match for the desired sampling frequency */ - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { - diff_new = abs(res - ad7768_clk_config[i].clk_div); - if (diff_new < diff_old) { - diff_old = diff_new; - idx = i; - } + ret = regmap_write(st->regmap, AD7768_REG_DIGITAL_FILTER, dig_filter_regval); + if (ret) + return ret; + + st->filter_type = filter_type; + /* + * The decimation for SINC3 filters are configured in different + * registers. + */ + if (filter_type == AD7768_FILTER_SINC3 || + filter_type == AD7768_FILTER_SINC3_REJ60) { + ret = ad7768_set_sinc3_dec_rate(st, dec_rate); + if (ret) + return ret; + } else { + st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_idx]; } + ad7768_fill_samp_freq_tbl(st); + + /* A sync-in pulse is required after every configuration change */ + return ad7768_send_sync_pulse(st); +} + +static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_clear_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + BIT(offset)); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_set_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + BIT(offset)); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + goto err_release; + /* - * Set both the mclk_div and pwrmode with a single write to the - * POWER_CLOCK register + * If the GPIO is configured as an output, read the current value from + * AD7768_REG_GPIO_WRITE. Otherwise, read the input value from + * AD7768_REG_GPIO_READ. */ - pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) | - AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode); - ret = regmap_write(st->regmap, AD7768_REG_POWER_CLOCK, pwr_mode); - if (ret < 0) + if (val & BIT(offset)) + ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val); + else + ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val); + if (ret) + goto err_release; + + ret = !!(val & BIT(offset)); +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct iio_dev *indio_dev = gpiochip_get_data(chip); + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int val; + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + goto err_release; + + if (val & BIT(offset)) + ret = regmap_assign_bits(st->regmap, AD7768_REG_GPIO_WRITE, + BIT(offset), value); + +err_release: + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_gpio_init(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + ret = regmap_write(st->regmap, AD7768_REG_GPIO_CONTROL, + AD7768_GPIO_UNIVERSAL_EN); + if (ret) return ret; - ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate); - if (ret < 0) + st->gpiochip = (struct gpio_chip) { + .label = "ad7768_1_gpios", + .base = -1, + .ngpio = 4, + .parent = &st->spi->dev, + .can_sleep = true, + .direction_input = ad7768_gpio_direction_input, + .direction_output = ad7768_gpio_direction_output, + .get = ad7768_gpio_get, + .set_rv = ad7768_gpio_set, + .owner = THIS_MODULE, + }; + + return devm_gpiochip_add_data(&st->spi->dev, &st->gpiochip, indio_dev); +} + +static int ad7768_set_freq(struct ad7768_state *st, + unsigned int freq) +{ + unsigned int idx, mclk_div; + int ret; + + freq = clamp(freq, 50, 1024000); + if (freq == 0) + return -EINVAL; + + mclk_div = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->oversampling_ratio); + /* Find the closest match for the desired sampling frequency */ + idx = find_closest_descending(mclk_div, ad7768_mclk_div_rates, + ARRAY_SIZE(ad7768_mclk_div_rates)); + /* Set both the mclk_div and pwrmode */ + ret = ad7768_set_mclk_div(st, idx); + if (ret) return ret; st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq, - ad7768_clk_config[idx].clk_div); + ad7768_mclk_div_rates[idx] * st->oversampling_ratio); - return 0; + /* A sync-in pulse is required after every configuration change */ + return ad7768_send_sync_pulse(st); } -static ssize_t ad7768_sampling_freq_avail(struct device *dev, - struct device_attribute *attr, - char *buf) +static int ad7768_set_filter_type_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int filter) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7768_state *st = iio_priv(indio_dev); - unsigned int freq; - int i, len = 0; + struct ad7768_state *st = iio_priv(dev); + int ret; - for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { - freq = DIV_ROUND_CLOSEST(st->mclk_freq, - ad7768_clk_config[i].clk_div); - len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq); - } + ret = ad7768_configure_dig_fil(dev, filter, st->oversampling_ratio); + if (ret) + return ret; + + /* Update sampling frequency */ + return ad7768_set_freq(st, st->samp_freq); +} + +static int ad7768_get_filter_type_attr(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(dev); + int ret; + unsigned int mode, mask; - buf[len - 1] = '\n'; + ret = regmap_read(st->regmap, AD7768_REG_DIGITAL_FILTER, &mode); + if (ret) + return ret; - return len; + mask = AD7768_DIG_FIL_EN_60HZ_REJ | AD7768_DIG_FIL_FIL_MSK; + /* From the register value, get the corresponding filter type */ + return ad7768_filter_regval_to_type[FIELD_GET(mask, mode)]; } -static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail); +static const struct iio_enum ad7768_filter_type_iio_enum = { + .items = ad7768_filter_enum, + .num_items = ARRAY_SIZE(ad7768_filter_enum), + .set = ad7768_set_filter_type_attr, + .get = ad7768_get_filter_type_attr, +}; + +static const struct iio_chan_spec_ext_info ad7768_ext_info[] = { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad7768_filter_type_iio_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, &ad7768_filter_type_iio_enum), + { } +}; + +static const struct iio_chan_spec ad7768_channels[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info = ad7768_ext_info, + .indexed = 1, + .channel = 0, + .scan_index = 0, + .has_ext_scan_type = 1, + .ext_scan_type = ad7768_scan_type, + .num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), + }, +}; static int ad7768_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) { struct ad7768_state *st = iio_priv(indio_dev); - int scale_uv, ret; + const struct iio_scan_type *scan_type; + int scale_uv, ret, temp; + + scan_type = iio_get_current_scan_type(indio_dev, chan); + if (IS_ERR(scan_type)) + return PTR_ERR(scan_type); switch (info) { case IIO_CHAN_INFO_RAW: @@ -424,7 +792,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, iio_device_release_direct(indio_dev); if (ret < 0) return ret; - *val = sign_extend32(ret, chan->scan_type.realbits - 1); + *val = sign_extend32(ret, scan_type->realbits - 1); return IIO_VAL_INT; @@ -434,7 +802,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, return scale_uv; *val = (scale_uv * 2) / 1000; - *val2 = chan->scan_type.realbits; + *val2 = scan_type->realbits; return IIO_VAL_FRACTIONAL_LOG2; @@ -442,25 +810,96 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, *val = st->samp_freq; return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling_ratio; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + temp = st->samp_freq * ad7768_filter_3db_odr_multiplier[st->filter_type]; + *val = DIV_ROUND_CLOSEST(temp, MILLI); + + return IIO_VAL_INT; } return -EINVAL; } -static int ad7768_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long info) +static int ad7768_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) { struct ad7768_state *st = iio_priv(indio_dev); + unsigned int shift; + + switch (info) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + /* + * Sinc3 filter allows a wider range of OSR values, so show + * the available values in range format. + */ + if (st->filter_type == AD7768_FILTER_SINC3 || + st->filter_type == AD7768_FILTER_SINC3_REJ60) { + *vals = (int *)ad7768_sinc3_dec_rate_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + } + + shift = st->filter_type == AD7768_FILTER_SINC5 ? 0 : 2; + *vals = (int *)&ad7768_dec_rate_values[shift]; + *length = ARRAY_SIZE(ad7768_dec_rate_values) - shift; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)st->samp_freq_avail; + *length = st->samp_freq_avail_len; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int __ad7768_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: return ad7768_set_freq(st, val); + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val); + if (ret) + return ret; + + /* Update sampling frequency */ + return ad7768_set_freq(st, st->samp_freq); default: return -EINVAL; } } +static int ad7768_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ad7768_write_raw(indio_dev, chan, val, val2, info); + iio_device_release_direct(indio_dev); + + return ret; +} + static int ad7768_read_label(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, char *label) { @@ -469,25 +908,97 @@ static int ad7768_read_label(struct iio_dev *indio_dev, return sprintf(label, "%s\n", st->labels[chan->channel]); } -static struct attribute *ad7768_attributes[] = { - &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - NULL -}; +static int ad7768_get_current_scan_type(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7768_state *st = iio_priv(indio_dev); -static const struct attribute_group ad7768_group = { - .attrs = ad7768_attributes, -}; + return st->oversampling_ratio == 8 ? + AD7768_SCAN_TYPE_HIGH_SPEED : AD7768_SCAN_TYPE_NORMAL; +} static const struct iio_info ad7768_info = { - .attrs = &ad7768_group, .read_raw = &ad7768_read_raw, + .read_avail = &ad7768_read_avail, .write_raw = &ad7768_write_raw, .read_label = ad7768_read_label, + .get_current_scan_type = &ad7768_get_current_scan_type, .debugfs_reg_access = &ad7768_reg_access, }; -static int ad7768_setup(struct ad7768_state *st) +static struct fwnode_handle * +ad7768_fwnode_find_reference_args(const struct fwnode_handle *fwnode, + const char *name, const char *nargs_prop, + unsigned int nargs, unsigned int index, + struct fwnode_reference_args *args) +{ + int ret; + + ret = fwnode_property_get_reference_args(fwnode, name, nargs_prop, + nargs, index, args); + return ret ? ERR_PTR(ret) : args->fwnode; +} + +static int ad7768_trigger_sources_sync_setup(struct device *dev, + struct fwnode_handle *fwnode, + struct ad7768_state *st) +{ + struct fwnode_reference_args args; + + struct fwnode_handle *ref __free(fwnode_handle) = + ad7768_fwnode_find_reference_args(fwnode, "trigger-sources", + "#trigger-source-cells", 0, + AD7768_TRIGGER_SOURCE_SYNC_IDX, + &args); + if (IS_ERR(ref)) + return PTR_ERR(ref); + + ref = args.fwnode; + /* First, try getting the GPIO trigger source */ + if (fwnode_device_is_compatible(ref, "gpio-trigger")) { + st->gpio_sync_in = devm_fwnode_gpiod_get_index(dev, ref, NULL, 0, + GPIOD_OUT_LOW, + "sync-in"); + return PTR_ERR_OR_ZERO(st->gpio_sync_in); + } + + /* + * TODO: Support the other cases when we have a trigger subsystem + * to reliably handle other types of devices as trigger sources. + * + * For now, return an error message. For self triggering, omit the + * trigger-sources property. + */ + return dev_err_probe(dev, -EOPNOTSUPP, "Invalid synchronization trigger source\n"); +} + +static int ad7768_trigger_sources_get_sync(struct device *dev, + struct ad7768_state *st) { + struct fwnode_handle *fwnode = dev_fwnode(dev); + + /* + * The AD7768-1 allows two primary methods for driving the SYNC_IN pin + * to synchronize one or more devices: + * 1. Using an external GPIO. + * 2. Using a SPI command, where the SYNC_OUT pin generates a + * synchronization pulse that drives the SYNC_IN pin. + */ + if (fwnode_property_present(fwnode, "trigger-sources")) + return ad7768_trigger_sources_sync_setup(dev, fwnode, st); + + /* + * In the absence of trigger-sources property, enable self + * synchronization over SPI (SYNC_OUT). + */ + st->en_spi_sync = true; + + return 0; +} + +static int ad7768_setup(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); int ret; st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", @@ -515,11 +1026,37 @@ static int ad7768_setup(struct ad7768_state *st) return ret; } - st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in", - GPIOD_OUT_LOW); + /* For backwards compatibility, try the adi,sync-in-gpios property */ + st->gpio_sync_in = devm_gpiod_get_optional(&st->spi->dev, "adi,sync-in", + GPIOD_OUT_LOW); if (IS_ERR(st->gpio_sync_in)) return PTR_ERR(st->gpio_sync_in); + /* + * If the synchronization is not defined by adi,sync-in-gpios, try the + * trigger-sources. + */ + if (!st->gpio_sync_in) { + ret = ad7768_trigger_sources_get_sync(&st->spi->dev, st); + if (ret) + return ret; + } + + /* Only create a Chip GPIO if flagged for it */ + if (device_property_read_bool(&st->spi->dev, "gpio-controller")) { + ret = ad7768_gpio_init(indio_dev); + if (ret) + return ret; + } + + /* + * Set Default Digital Filter configuration: + * SINC5 filter with x32 Decimation rate + */ + ret = ad7768_configure_dig_fil(indio_dev, AD7768_FILTER_SINC5, 32); + if (ret) + return ret; + /* Set the default sampling frequency to 32000 kSPS */ return ad7768_set_freq(st, 32000); } @@ -529,9 +1066,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ad7768_state *st = iio_priv(indio_dev); + const struct iio_scan_type *scan_type; int ret; - ret = spi_read(st->spi, &st->data.scan.chan, 3); + scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]); + if (IS_ERR(scan_type)) + goto out; + + ret = spi_read(st->spi, &st->data.scan.chan, + BITS_TO_BYTES(scan_type->realbits)); if (ret < 0) goto out; @@ -647,6 +1190,150 @@ static int ad7768_triggered_buffer_alloc(struct iio_dev *indio_dev) &ad7768_buffer_ops); } +static int ad7768_vcm_enable(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret, regval; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + /* To enable, set the last selected output */ + regval = AD7768_REG_ANALOG2_VCM(st->vcm_output_sel + 1); + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, regval); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_vcm_disable(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, AD7768_VCM_OFF); + iio_device_release_direct(indio_dev); + + return ret; +} + +static int ad7768_vcm_is_enabled(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret, val; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_ANALOG2, &val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + return FIELD_GET(AD7768_REG_ANALOG2_VCM_MSK, val) != AD7768_VCM_OFF; +} + +static int ad7768_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + unsigned int regval = AD7768_REG_ANALOG2_VCM(selector + 1); + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, regval); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + st->vcm_output_sel = selector; + + return 0; +} + +static int ad7768_get_voltage_sel(struct regulator_dev *rdev) +{ + struct iio_dev *indio_dev = rdev_get_drvdata(rdev); + struct ad7768_state *st = iio_priv(indio_dev); + int ret, val; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = regmap_read(st->regmap, AD7768_REG_ANALOG2, &val); + iio_device_release_direct(indio_dev); + if (ret) + return ret; + + val = FIELD_GET(AD7768_REG_ANALOG2_VCM_MSK, val); + + return clamp(val, 1, rdev->desc->n_voltages) - 1; +} + +static const struct regulator_ops vcm_regulator_ops = { + .enable = ad7768_vcm_enable, + .disable = ad7768_vcm_disable, + .is_enabled = ad7768_vcm_is_enabled, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = ad7768_set_voltage_sel, + .get_voltage_sel = ad7768_get_voltage_sel, +}; + +static const unsigned int vcm_voltage_table[] = { + 2500000, + 2050000, + 1650000, + 1900000, + 1100000, + 900000, +}; + +static const struct regulator_desc vcm_desc = { + .name = "ad7768-1-vcm", + .of_match = "vcm-output", + .regulators_node = "regulators", + .n_voltages = ARRAY_SIZE(vcm_voltage_table), + .volt_table = vcm_voltage_table, + .ops = &vcm_regulator_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, +}; + +static int ad7768_register_regulators(struct device *dev, struct ad7768_state *st, + struct iio_dev *indio_dev) +{ + struct regulator_config config = { + .dev = dev, + .driver_data = indio_dev, + }; + int ret; + + /* Disable the regulator before registering it */ + ret = regmap_update_bits(st->regmap, AD7768_REG_ANALOG2, + AD7768_REG_ANALOG2_VCM_MSK, AD7768_VCM_OFF); + if (ret) + return ret; + + st->vcm_rdev = devm_regulator_register(dev, &vcm_desc, &config); + if (IS_ERR(st->vcm_rdev)) + return dev_err_probe(dev, PTR_ERR(st->vcm_rdev), + "failed to register VCM regulator\n"); + + return 0; +} + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; @@ -711,7 +1398,12 @@ static int ad7768_probe(struct spi_device *spi) indio_dev->info = &ad7768_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = ad7768_setup(st); + /* Register VCM output regulator */ + ret = ad7768_register_regulators(&spi->dev, st, indio_dev); + if (ret) + return ret; + + ret = ad7768_setup(indio_dev); if (ret < 0) { dev_err(&spi->dev, "AD7768 setup failed\n"); return ret; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 4c5f8d29a559..7852884703b0 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -7,24 +7,33 @@ */ #include <linux/align.h> -#include <linux/interrupt.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/completion.h> #include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> #include <linux/err.h> +#include <linux/export.h> +#include <linux/find.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/spi/offload/consumer.h> +#include <linux/spi/spi.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/unaligned.h> -#include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> +#include <linux/iio/adc/ad_sigma_delta.h> +#include <linux/iio/buffer-dmaengine.h> #include <linux/iio/buffer.h> -#include <linux/iio/trigger.h> +#include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> +#include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> -#include <linux/iio/adc/ad_sigma_delta.h> - -#include <linux/unaligned.h> - #define AD_SD_COMM_CHAN_MASK 0x3 @@ -40,7 +49,7 @@ * @sigma_delta: The sigma delta device * @comm: New value for the communications register */ -void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm) +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm) { /* Some variants use the lower two bits of the communications register * to select the channel */ @@ -61,7 +70,7 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, unsigned int val) { - uint8_t *data = sigma_delta->tx_buf; + u8 *data = sigma_delta->tx_buf; struct spi_transfer t = { .tx_buf = data, .len = size + 1, @@ -100,10 +109,18 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, } EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, "IIO_AD_SIGMA_DELTA"); +static void ad_sd_set_read_reg_addr(struct ad_sigma_delta *sigma_delta, u8 reg, + u8 *data) +{ + data[0] = reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + data[0] |= sigma_delta->comm; +} + static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, - unsigned int reg, unsigned int size, uint8_t *val) + unsigned int reg, unsigned int size, u8 *val) { - uint8_t *data = sigma_delta->tx_buf; + u8 *data = sigma_delta->tx_buf; int ret; struct spi_transfer t[] = { { @@ -120,9 +137,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, spi_message_init(&m); if (sigma_delta->info->has_registers) { - data[0] = reg << sigma_delta->info->addr_shift; - data[0] |= sigma_delta->info->read_mask; - data[0] |= sigma_delta->comm; + ad_sd_set_read_reg_addr(sigma_delta, reg, data); spi_message_add_tail(&t[0], &m); } spi_message_add_tail(&t[1], &m); @@ -187,11 +202,11 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { unsigned int reset_length = sigma_delta->info->num_resetclks; - uint8_t *buf; unsigned int size; + u8 *buf; int ret; - size = DIV_ROUND_UP(reset_length, 8); + size = BITS_TO_BYTES(reset_length); buf = kcalloc(size, sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; @@ -281,9 +296,7 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta if (sigma_delta->info->has_registers) { unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; - data[0] = data_reg << sigma_delta->info->addr_shift; - data[0] |= sigma_delta->info->read_mask; - data[0] |= sigma_delta->comm; + ad_sd_set_read_reg_addr(sigma_delta, data_reg, data); t[0].tx_buf = data; spi_message_add_tail(&t[0], &m); } @@ -420,7 +433,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, data_reg = AD_SD_REG_DATA; ret = ad_sd_read_reg(sigma_delta, data_reg, - DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8), + BITS_TO_BYTES(chan->scan_type.realbits + chan->scan_type.shift), &raw_sample); out: @@ -454,9 +467,10 @@ EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, "IIO_AD_SIGMA_DELTA"); static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - unsigned int i, slot, samples_buf_size; - unsigned int channel; - uint8_t *samples_buf; + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; + struct spi_transfer *xfer = sigma_delta->sample_xfer; + unsigned int i, slot, channel; + u8 *samples_buf; int ret; if (sigma_delta->num_slots == 1) { @@ -483,20 +497,55 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) sigma_delta->active_slots = slot; sigma_delta->current_slot = 0; - if (sigma_delta->active_slots > 1) { - ret = ad_sigma_delta_append_status(sigma_delta, true); - if (ret) - return ret; + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + xfer[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfer[1].bits_per_word = scan_type->realbits; + xfer[1].len = spi_bpw_to_bytes(scan_type->realbits); + } else { + unsigned int samples_buf_size, scan_size; + + if (sigma_delta->active_slots > 1) { + ret = ad_sigma_delta_append_status(sigma_delta, true); + if (ret) + return ret; + } + + samples_buf_size = + ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits), + sizeof(s64)); + samples_buf_size += sizeof(s64); + samples_buf = devm_krealloc(&sigma_delta->spi->dev, + sigma_delta->samples_buf, + samples_buf_size, GFP_KERNEL); + if (!samples_buf) + return -ENOMEM; + + sigma_delta->samples_buf = samples_buf; + scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); + /* For 24-bit data, there is an extra byte of padding. */ + xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0]; + xfer[1].len = scan_size + (sigma_delta->status_appended ? 1 : 0); } + xfer[1].cs_change = 1; - samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits, 8); - samples_buf_size += sizeof(int64_t); - samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf, - samples_buf_size, GFP_KERNEL); - if (!samples_buf) - return -ENOMEM; + if (sigma_delta->info->has_registers) { + xfer[0].tx_buf = &sigma_delta->sample_addr; + xfer[0].len = 1; + + ad_sd_set_read_reg_addr(sigma_delta, + sigma_delta->info->data_reg ?: AD_SD_REG_DATA, + &sigma_delta->sample_addr); + spi_message_init_with_transfers(&sigma_delta->sample_msg, xfer, 2); + } else { + spi_message_init_with_transfers(&sigma_delta->sample_msg, + &xfer[1], 1); + } - sigma_delta->samples_buf = samples_buf; + sigma_delta->sample_msg.offload = sigma_delta->offload; + + ret = spi_optimize_message(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) + return ret; spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; @@ -510,24 +559,42 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unlock; - ad_sd_enable_irq(sigma_delta); + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + + ret = spi_offload_trigger_enable(sigma_delta->offload, + sigma_delta->offload_trigger, + &config); + if (ret) + goto err_unlock; + } else { + ad_sd_enable_irq(sigma_delta); + } return 0; err_unlock: spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); return ret; } -static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) +static int ad_sd_buffer_predisable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - reinit_completion(&sigma_delta->completion); - wait_for_completion_timeout(&sigma_delta->completion, HZ); + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + spi_offload_trigger_disable(sigma_delta->offload, + sigma_delta->offload_trigger); + } else { + reinit_completion(&sigma_delta->completion); + wait_for_completion_timeout(&sigma_delta->completion, HZ); - ad_sd_disable_irq(sigma_delta); + ad_sd_disable_irq(sigma_delta); + } sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -537,61 +604,32 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) ad_sigma_delta_disable_all(sigma_delta); sigma_delta->bus_locked = false; - return spi_bus_unlock(sigma_delta->spi->controller); + spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); + + return 0; } static irqreturn_t ad_sd_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - uint8_t *data = sigma_delta->rx_buf; - unsigned int transfer_size; + u8 *data = sigma_delta->rx_buf; unsigned int sample_size; unsigned int sample_pos; unsigned int status_pos; unsigned int reg_size; - unsigned int data_reg; - - reg_size = indio_dev->channels[0].scan_type.realbits + - indio_dev->channels[0].scan_type.shift; - reg_size = DIV_ROUND_UP(reg_size, 8); - - if (sigma_delta->info->data_reg != 0) - data_reg = sigma_delta->info->data_reg; - else - data_reg = AD_SD_REG_DATA; - - /* Status word will be appended to the sample during transfer */ - if (sigma_delta->status_appended) - transfer_size = reg_size + 1; - else - transfer_size = reg_size; - - switch (reg_size) { - case 4: - case 2: - case 1: - status_pos = reg_size; - ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[0]); - break; - case 3: - /* - * Data array after transfer will look like (if status is appended): - * data[] = { [0][sample][sample][sample][status] } - * Keeping the first byte 0 shifts the status position by 1 byte to the right. - */ - status_pos = reg_size + 1; + int ret; - /* We store 24 bit samples in a 32 bit word. Keep the upper - * byte set to zero. */ - ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]); - break; + reg_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); + /* For 24-bit data, there is an extra byte of padding. */ + status_pos = reg_size + (reg_size == 3 ? 1 : 0); - default: - dev_err_ratelimited(&indio_dev->dev, "Unsupported reg_size: %u\n", reg_size); + ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) goto irq_handled; - } /* * For devices sampling only one channel at @@ -617,7 +655,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) } } - sample_size = indio_dev->channels[0].scan_type.storagebits / 8; + sample_size = BITS_TO_BYTES(scan_type->storagebits); sample_pos = sample_size * sigma_delta->current_slot; memcpy(&sigma_delta->samples_buf[sample_pos], data, sample_size); sigma_delta->current_slot++; @@ -644,7 +682,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { .postenable = &ad_sd_buffer_postenable, - .postdisable = &ad_sd_buffer_postdisable, + .predisable = &ad_sd_buffer_predisable, .validate_scan_mask = &ad_sd_validate_scan_mask, }; @@ -671,7 +709,8 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && ad_sd_disable_irq(sigma_delta)) { complete(&sigma_delta->completion); - iio_trigger_poll(sigma_delta->trig); + if (sigma_delta->trig) + iio_trigger_poll(sigma_delta->trig); return IRQ_HANDLED; } @@ -704,17 +743,6 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de unsigned long irq_flags = irq_get_trigger_type(sigma_delta->irq_line); int ret; - if (dev != &sigma_delta->spi->dev) { - dev_err(dev, "Trigger parent should be '%s', got '%s'\n", - dev_name(dev), dev_name(&sigma_delta->spi->dev)); - return -EFAULT; - } - - sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - iio_device_id(indio_dev)); - if (sigma_delta->trig == NULL) - return -ENOMEM; - init_completion(&sigma_delta->completion); sigma_delta->irq_dis = true; @@ -734,14 +762,33 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de if (ret) return ret; - iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + sigma_delta->offload_trigger = + devm_spi_offload_trigger_get(dev, sigma_delta->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(sigma_delta->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(sigma_delta->offload_trigger), + "Failed to get SPI offload trigger\n"); + } else { + if (dev != &sigma_delta->spi->dev) + return dev_err_probe(dev, -EFAULT, + "Trigger parent should be '%s', got '%s'\n", + dev_name(dev), dev_name(&sigma_delta->spi->dev)); - ret = devm_iio_trigger_register(dev, sigma_delta->trig); - if (ret) - return ret; + sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!sigma_delta->trig) + return -ENOMEM; - /* select default trigger */ - indio_dev->trig = iio_trigger_get(sigma_delta->trig); + iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); + + ret = devm_iio_trigger_register(dev, sigma_delta->trig); + if (ret) + return ret; + + /* select default trigger */ + indio_dev->trig = iio_trigger_get(sigma_delta->trig); + } return 0; } @@ -761,12 +808,29 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi if (!sigma_delta->slots) return -ENOMEM; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - &iio_pollfunc_store_time, - &ad_sd_trigger_handler, - &ad_sd_buffer_setup_ops); - if (ret) - return ret; + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + struct dma_chan *rx_dma; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + sigma_delta->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "Failed to get RX DMA channel\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "Cannot setup DMA buffer\n"); + + indio_dev->setup_ops = &ad_sd_buffer_setup_ops; + } else { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad_sd_trigger_handler, + &ad_sd_buffer_setup_ops); + if (ret) + return ret; + } return devm_ad_sd_probe_trigger(dev, indio_dev); } @@ -829,6 +893,20 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, return sigma_delta->irq_line; } + if (info->supports_spi_offload) { + struct spi_offload_config offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, + }; + int ret; + + sigma_delta->offload = devm_spi_offload_get(&spi->dev, spi, + &offload_config); + ret = PTR_ERR_OR_ZERO(sigma_delta->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, "Failed to get SPI offload\n"); + } + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; @@ -838,3 +916,4 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_init, "IIO_AD_SIGMA_DELTA"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 2dbaa0b5b3d6..4b7c472a088b 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -44,6 +44,8 @@ #define ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N BIT(7) #define ADI_AXI_ADC_REG_CTRL 0x0044 +#define ADI_AXI_ADC_CTRL_NUM_LANES_MSK GENMASK(12, 8) +#define ADI_AXI_ADC_CTRL_SYNC_MSK BIT(3) #define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) #define ADI_AXI_ADC_REG_CNTRL_3 0x004c @@ -52,6 +54,10 @@ #define AXI_AD485X_PACKET_FORMAT_20BIT 0x0 #define AXI_AD485X_PACKET_FORMAT_24BIT 0x1 #define AXI_AD485X_PACKET_FORMAT_32BIT 0x2 +#define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0) + +#define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068 +#define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0) #define ADI_AXI_ADC_REG_DRP_STATUS 0x0074 #define ADI_AXI_ADC_DRP_LOCKED BIT(17) @@ -80,6 +86,9 @@ #define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40) #define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16) +#define ADI_AXI_ADC_REG_CHAN_USR_CTRL_2(c) (0x0424 + (c) * 0x40) +#define ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK GENMASK(15, 0) + /* IO Delays */ #define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4) #define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0) @@ -242,6 +251,19 @@ static int axi_adc_test_pattern_set(struct iio_backend *back, } } +static int axi_adc_oversampling_ratio_set(struct iio_backend *back, + unsigned int chan, + unsigned int rate) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + return regmap_update_bits(st->regmap, + ADI_AXI_ADC_REG_CHAN_USR_CTRL_2(chan), + ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK, + FIELD_PREP(ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK, + rate)); +} + static int axi_adc_read_chan_status(struct adi_axi_adc_state *st, unsigned int chan, unsigned int *status) { @@ -381,7 +403,8 @@ static int axi_adc_ad485x_data_size_set(struct iio_backend *back, } static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back, - unsigned int ratio) + unsigned int chan, + unsigned int ratio) { struct adi_axi_adc_state *st = iio_backend_get_priv(back); @@ -402,6 +425,50 @@ static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back, } } +static int axi_adc_ad408x_filter_type_set(struct iio_backend *back, + enum iio_backend_filter_type type) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + if (type) + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_FILTER_EN_MSK); + + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3, + AXI_AD408X_CNTRL_3_FILTER_EN_MSK); +} + +static int axi_adc_ad408x_interface_data_align(struct iio_backend *back, + u32 timeout_us) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + u32 val; + int ret; + + ret = regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_SYNC_MSK); + if (ret) + return ret; + + return regmap_read_poll_timeout(st->regmap, ADI_AXI_ADC_REG_SYNC_STATUS, + val, + FIELD_GET(ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK, val), + 1, timeout_us); +} + +static int axi_adc_num_lanes_set(struct iio_backend *back, + unsigned int num_lanes) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + if (!num_lanes) + return -EINVAL; + + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_NUM_LANES_MSK, + FIELD_PREP(ADI_AXI_ADC_CTRL_NUM_LANES_MSK, num_lanes)); +} + static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, struct iio_dev *indio_dev) { @@ -551,6 +618,7 @@ static const struct iio_backend_ops adi_axi_adc_ops = { .test_pattern_set = axi_adc_test_pattern_set, .chan_status = axi_adc_chan_status, .interface_type_get = axi_adc_interface_type_get, + .oversampling_ratio_set = axi_adc_oversampling_ratio_set, .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), }; @@ -584,6 +652,26 @@ static const struct iio_backend_info axi_ad485x = { .ops = &adi_ad485x_ops, }; +static const struct iio_backend_ops adi_ad408x_ops = { + .enable = axi_adc_enable, + .disable = axi_adc_disable, + .chan_enable = axi_adc_chan_enable, + .chan_disable = axi_adc_chan_disable, + .request_buffer = axi_adc_request_buffer, + .free_buffer = axi_adc_free_buffer, + .data_sample_trigger = axi_adc_data_sample_trigger, + .filter_type_set = axi_adc_ad408x_filter_type_set, + .interface_data_align = axi_adc_ad408x_interface_data_align, + .num_lanes_set = axi_adc_num_lanes_set, + .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access), + .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), +}; + +static const struct iio_backend_info axi_ad408x = { + .name = "axi-ad408x", + .ops = &adi_ad408x_ops, +}; + static int adi_axi_adc_probe(struct platform_device *pdev) { struct adi_axi_adc_state *st; @@ -699,9 +787,15 @@ static const struct axi_adc_info adc_ad7606 = { .has_child_nodes = true, }; +static const struct axi_adc_info adi_axi_ad408x = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), + .backend_info = &axi_ad408x, +}; + /* Match table for of_platform binding */ static const struct of_device_id adi_axi_adc_of_match[] = { { .compatible = "adi,axi-adc-10.0.a", .data = &adc_generic }, + { .compatible = "adi,axi-ad408x", .data = &adi_axi_ad408x }, { .compatible = "adi,axi-ad485x", .data = &adi_axi_ad485x }, { .compatible = "adi,axi-ad7606x", .data = &adc_ad7606 }, { } diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 5927756b749a..920dd9ffd27a 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1226,7 +1226,7 @@ static const struct at91_adc_trigger at91sam9260_triggers[] = { { .name = "external", .value = 0xd, .is_external = true }, }; -static struct at91_adc_caps at91sam9260_caps = { +static const struct at91_adc_caps at91sam9260_caps = { .calc_startup_ticks = calc_startup_ticks_9260, .num_channels = 4, .low_res_bits = 8, @@ -1250,7 +1250,7 @@ static const struct at91_adc_trigger at91sam9x5_triggers[] = { { .name = "continuous", .value = 0x6 }, }; -static struct at91_adc_caps at91sam9rl_caps = { +static const struct at91_adc_caps at91sam9rl_caps = { .has_ts = true, .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ .num_channels = 6, @@ -1268,7 +1268,7 @@ static struct at91_adc_caps at91sam9rl_caps = { .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), }; -static struct at91_adc_caps at91sam9g45_caps = { +static const struct at91_adc_caps at91sam9g45_caps = { .has_ts = true, .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ .num_channels = 8, @@ -1286,7 +1286,7 @@ static struct at91_adc_caps at91sam9g45_caps = { .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), }; -static struct at91_adc_caps at91sam9x5_caps = { +static const struct at91_adc_caps at91sam9x5_caps = { .has_ts = true, .has_tsmr = true, .ts_filter_average = 3, @@ -1308,7 +1308,7 @@ static struct at91_adc_caps at91sam9x5_caps = { .trigger_number = ARRAY_SIZE(at91sam9x5_triggers), }; -static struct at91_adc_caps sama5d3_caps = { +static const struct at91_adc_caps sama5d3_caps = { .has_ts = true, .has_tsmr = true, .ts_filter_average = 3, diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 1b49325ec1ce..f9a60e8b05cb 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -173,7 +173,7 @@ static const struct iio_map axp22x_maps[] = { { } }; -static struct iio_map axp717_maps[] = { +static const struct iio_map axp717_maps[] = { { .consumer_dev_name = "axp20x-usb-power-supply", .consumer_channel = "vbus_v", diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 9dbd2c87938c..5aea7644780f 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -467,7 +467,7 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) struct { __le16 values[DLN2_ADC_MAX_CHANNELS]; aligned_s64 timestamp_space; - } data; + } data = { }; struct dln2_adc_get_all_vals dev_data; struct dln2_adc *dln2 = iio_priv(indio_dev); const struct dln2_adc_demux_table *t; @@ -479,8 +479,6 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p) if (ret < 0) goto done; - memset(&data, 0, sizeof(data)); - /* Demux operation */ for (i = 0; i < dln2->demux_count; ++i) { t = &dln2->demux[i]; diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index b4562aae8477..86c10ea7ded4 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -19,8 +19,6 @@ #include <linux/spi/spi.h> #include <linux/gpio/consumer.h> -#define DRV_NAME "hi8435" - /* Register offsets for HI-8435 */ #define HI8435_CTRL_REG 0x02 #define HI8435_PSEN_REG 0x04 @@ -536,7 +534,7 @@ MODULE_DEVICE_TABLE(spi, hi8435_id); static struct spi_driver hi8435_driver = { .driver = { - .name = DRV_NAME, + .name = "hi8435", .of_match_table = hi8435_dt_ids, }, .probe = hi8435_probe, diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 14fe42fc4b7d..826566d7a85e 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -25,8 +25,6 @@ #include <linux/mod_devicetable.h> #include <linux/property.h> -#define DRIVER_NAME "max9611" - /* max9611 register addresses */ #define MAX9611_REG_CSA_DATA 0x00 #define MAX9611_REG_RS_DATA 0x02 @@ -553,7 +551,7 @@ static int max9611_probe(struct i2c_client *client) static struct i2c_driver max9611_driver = { .driver = { - .name = DRIVER_NAME, + .name = "max9611", .of_match_table = max9611_of_table, }, .probe = max9611_probe, diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 1cb043b17437..5a1d516f8dad 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -44,7 +44,7 @@ struct mp2629_adc { struct device *dev; }; -static struct iio_chan_spec mp2629_channels[] = { +static const struct iio_chan_spec mp2629_channels[] = { MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE), MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE), MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE), diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index eecf88b05c6f..f426a289e867 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -7,6 +7,7 @@ * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> */ +#include <linux/bitfield.h> #include <linux/bits.h> #include <linux/cleanup.h> #include <linux/delay.h> @@ -24,11 +25,11 @@ #include <dt-bindings/iio/adc/mediatek,mt6357-auxadc.h> #include <dt-bindings/iio/adc/mediatek,mt6358-auxadc.h> #include <dt-bindings/iio/adc/mediatek,mt6359-auxadc.h> +#include <dt-bindings/iio/adc/mediatek,mt6363-auxadc.h> #define AUXADC_AVG_TIME_US 10 #define AUXADC_POLL_DELAY_US 100 #define AUXADC_TIMEOUT_US 32000 -#define AUXADC_VOLT_FULL 1800 #define IMP_STOP_DELAY_US 150 #define IMP_POLL_DELAY_US 1000 @@ -46,6 +47,12 @@ #define MT6359_IMP0_CONV_EN BIT(0) #define MT6359_IMP1_IRQ_RDY BIT(15) +#define MT6363_EXT_CHAN_MASK GENMASK(2, 0) +#define MT6363_EXT_PURES_MASK GENMASK(4, 3) + #define MT6363_PULLUP_RES_100K 0 + #define MT6363_PULLUP_RES_30K 1 + #define MT6363_PULLUP_RES_OPEN 3 + enum mtk_pmic_auxadc_regs { PMIC_AUXADC_ADC0, PMIC_AUXADC_DCM_CON, @@ -54,6 +61,8 @@ enum mtk_pmic_auxadc_regs { PMIC_AUXADC_IMP3, PMIC_AUXADC_RQST0, PMIC_AUXADC_RQST1, + PMIC_AUXADC_RQST3, + PMIC_AUXADC_SDMADC_CON0, PMIC_HK_TOP_WKEY, PMIC_HK_TOP_RST_CON0, PMIC_FGADC_R_CON0, @@ -75,7 +84,16 @@ enum mtk_pmic_auxadc_channels { PMIC_AUXADC_CHAN_TSX_TEMP, PMIC_AUXADC_CHAN_HPOFS_CAL, PMIC_AUXADC_CHAN_DCXO_TEMP, + PMIC_AUXADC_CHAN_VTREF, PMIC_AUXADC_CHAN_VBIF, + PMIC_AUXADC_CHAN_VSYSSNS, + PMIC_AUXADC_CHAN_VIN1, + PMIC_AUXADC_CHAN_VIN2, + PMIC_AUXADC_CHAN_VIN3, + PMIC_AUXADC_CHAN_VIN4, + PMIC_AUXADC_CHAN_VIN5, + PMIC_AUXADC_CHAN_VIN6, + PMIC_AUXADC_CHAN_VIN7, PMIC_AUXADC_CHAN_IBAT, PMIC_AUXADC_CHAN_VBAT, PMIC_AUXADC_CHAN_MAX @@ -101,12 +119,22 @@ struct mt6359_auxadc { * struct mtk_pmic_auxadc_chan - PMIC AUXADC channel data * @req_idx: Request register number * @req_mask: Bitmask to activate a channel + * @rdy_idx: Readiness register number + * @rdy_mask: Bitmask to determine channel readiness + * @ext_sel_idx: PMIC GPIO channel register number + * @ext_sel_ch: PMIC GPIO number + * @ext_sel_pu: PMIC GPIO channel pullup resistor selector * @num_samples: Number of AUXADC samples for averaging * @r_ratio: Resistance ratio fractional */ struct mtk_pmic_auxadc_chan { u8 req_idx; u16 req_mask; + u8 rdy_idx; + u16 rdy_mask; + s8 ext_sel_idx; + u8 ext_sel_ch; + u8 ext_sel_pu; u16 num_samples; struct u8_fract r_ratio; }; @@ -119,7 +147,10 @@ struct mtk_pmic_auxadc_chan { * @desc: PMIC AUXADC channel data * @regs: List of PMIC specific registers * @sec_unlock_key: Security unlock key for HK_TOP writes + * @vref_mV: AUXADC Reference Voltage (VREF) in millivolts * @imp_adc_num: ADC channel for battery impedance readings + * @is_spmi: Defines whether this PMIC communicates over SPMI + * @no_reset: If true, this PMIC does not support ADC reset * @read_imp: Callback to read impedance channels */ struct mtk_pmic_auxadc_info { @@ -129,18 +160,34 @@ struct mtk_pmic_auxadc_info { const struct mtk_pmic_auxadc_chan *desc; const u16 *regs; u16 sec_unlock_key; + u32 vref_mV; u8 imp_adc_num; - int (*read_imp)(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat); + bool is_spmi; + bool no_reset; + int (*read_imp)(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *vbat, int *ibat); }; -#define MTK_PMIC_ADC_CHAN(_ch_idx, _req_idx, _req_bit, _samples, _rnum, _rdiv) \ +#define MTK_PMIC_ADC_EXT_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \ + _ext_sel_idx, _ext_sel_ch, _ext_sel_pu, \ + _samples, _rnum, _rdiv) \ [PMIC_AUXADC_CHAN_##_ch_idx] = { \ .req_idx = _req_idx, \ .req_mask = BIT(_req_bit), \ + .rdy_idx = _rdy_idx, \ + .rdy_mask = BIT(_rdy_bit), \ + .ext_sel_idx = _ext_sel_idx, \ + .ext_sel_ch = _ext_sel_ch, \ + .ext_sel_pu = _ext_sel_pu, \ .num_samples = _samples, \ .r_ratio = { _rnum, _rdiv } \ } +#define MTK_PMIC_ADC_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \ + _samples, _rnum, _rdiv) \ + MTK_PMIC_ADC_EXT_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \ + -1, 0, 0, _samples, _rnum, _rdiv) + #define MTK_PMIC_IIO_CHAN(_model, _name, _ch_idx, _adc_idx, _nbits, _ch_type) \ { \ .type = _ch_type, \ @@ -177,21 +224,21 @@ static const struct iio_chan_spec mt6357_auxadc_channels[] = { }; static const struct mtk_pmic_auxadc_chan mt6357_auxadc_ch_desc[] = { - MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, 128, 3, 1), - MTK_PMIC_ADC_CHAN(ISENSE, PMIC_AUXADC_RQST0, 0, 128, 3, 1), - MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, 8, 1, 1), - MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, 8, 1, 1), - MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, 8, 1, 1), - MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, 8, 1, 1), - MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, 128, 1, 1), - MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, 256, 1, 1), - MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, 16, 1, 1), - MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 5, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 6, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), + MTK_PMIC_ADC_CHAN(ISENSE, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), + MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP0, 8, 128, 1, 1), + MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP0, 8, 256, 1, 1), + MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP0, 8, 16, 1, 1), + MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 6, PMIC_AUXADC_IMP0, 8, 8, 1, 1), /* Battery impedance channels */ - MTK_PMIC_ADC_CHAN(VBAT, 0, 0, 128, 3, 1), + MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), }; static const u16 mt6357_auxadc_regs[] = { @@ -224,22 +271,22 @@ static const struct iio_chan_spec mt6358_auxadc_channels[] = { }; static const struct mtk_pmic_auxadc_chan mt6358_auxadc_ch_desc[] = { - MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, 128, 3, 1), - MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, 8, 1, 1), - MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, 8, 2, 1), - MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, 8, 1, 1), - MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, 8, 3, 2), - MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, 128, 1, 1), - MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, 256, 1, 1), - MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, 16, 1, 1), - MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, 8, 2, 1), - MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1), + MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP0, 8, 8, 2, 1), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP0, 8, 8, 3, 2), + MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP0, 8, 128, 1, 1), + MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP0, 8, 256, 1, 1), + MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP0, 8, 16, 1, 1), + MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP0, 8, 8, 2, 1), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, PMIC_AUXADC_IMP0, 8, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, PMIC_AUXADC_IMP0, 8, 8, 1, 1), /* Battery impedance channels */ - MTK_PMIC_ADC_CHAN(VBAT, 0, 0, 128, 7, 2), + MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP0, 8, 128, 7, 2), }; static const u16 mt6358_auxadc_regs[] = { @@ -272,22 +319,22 @@ static const struct iio_chan_spec mt6359_auxadc_channels[] = { }; static const struct mtk_pmic_auxadc_chan mt6359_auxadc_ch_desc[] = { - MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, 128, 7, 2), - MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, 8, 5, 2), - MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, 8, 1, 1), - MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, 8, 3, 2), - MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, 128, 1, 1), - MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, 256, 1, 1), - MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, 16, 1, 1), - MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, 8, 5, 2), - MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, 8, 1, 1), - MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, 8, 1, 1), + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP1, 15, 8, 5, 2), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15 ,8, 1, 1), + MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP1, 15, 8, 3, 2), + MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP1, 15, 128, 1, 1), + MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP1, 15, 256, 1, 1), + MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP1, 15, 16, 1, 1), + MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP1, 15, 8, 5, 2), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, PMIC_AUXADC_IMP1, 15, 8, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, PMIC_AUXADC_IMP1, 15, 8, 1, 1), /* Battery impedance channels */ - MTK_PMIC_ADC_CHAN(VBAT, 0, 0, 128, 7, 2), - MTK_PMIC_ADC_CHAN(IBAT, 0, 0, 128, 7, 2), + MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), + MTK_PMIC_ADC_CHAN(IBAT, 0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2), }; static const u16 mt6359_auxadc_regs[] = { @@ -302,6 +349,107 @@ static const u16 mt6359_auxadc_regs[] = { [PMIC_AUXADC_IMP3] = 0x120e, }; +static const struct iio_chan_spec mt6363_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6363, bat_adc, BATADC, 0, 15, IIO_RESISTANCE), + MTK_PMIC_IIO_CHAN(MT6363, cdt_v, VCDT, 2, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, batt_temp, BAT_TEMP, 3, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, sys_sns_v, VSYSSNS, 6, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, tref_v, VTREF, 11, 12, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP), + + /* For VIN, ADC12 holds the result depending on which GPIO was activated */ + MTK_PMIC_IIO_CHAN(MT6363, in1_v, VIN1, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in2_v, VIN2, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in3_v, VIN3, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in4_v, VIN4, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in5_v, VIN5, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in6_v, VIN6, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in7_v, VIN7, 45, 15, IIO_VOLTAGE), +}; + +static const struct mtk_pmic_auxadc_chan mt6363_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_ADC0, 15, 64, 4, 1), + MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_ADC0, 15, 32, 3, 2), + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VSYSSNS, PMIC_AUXADC_RQST1, 6, PMIC_AUXADC_ADC0, 15, 64, 3, 1), + MTK_PMIC_ADC_CHAN(VTREF, PMIC_AUXADC_RQST1, 3, PMIC_AUXADC_ADC0, 15, 32, 3, 2), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST3, 0, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST3, 1, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST3, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + + MTK_PMIC_ADC_EXT_CHAN(VIN1, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 1, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN2, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 2, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN3, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 3, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN4, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 4, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN5, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 5, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN6, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 6, MT6363_PULLUP_RES_100K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN7, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 7, MT6363_PULLUP_RES_100K, 32, 1, 1), +}; + +static const u16 mt6363_auxadc_regs[] = { + [PMIC_AUXADC_RQST0] = 0x1108, + [PMIC_AUXADC_RQST1] = 0x1109, + [PMIC_AUXADC_RQST3] = 0x110c, + [PMIC_AUXADC_ADC0] = 0x1088, + [PMIC_AUXADC_IMP0] = 0x1208, + [PMIC_AUXADC_IMP1] = 0x1209, +}; + +static const struct iio_chan_spec mt6373_auxadc_channels[] = { + MTK_PMIC_IIO_CHAN(MT6363, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP), + MTK_PMIC_IIO_CHAN(MT6363, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP), + + /* For VIN, ADC12 holds the result depending on which GPIO was activated */ + MTK_PMIC_IIO_CHAN(MT6363, in1_v, VIN1, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in2_v, VIN2, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in3_v, VIN3, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in4_v, VIN4, 45, 15, IIO_VOLTAGE), + MTK_PMIC_IIO_CHAN(MT6363, in5_v, VIN5, 45, 15, IIO_VOLTAGE), +}; + +static const struct mtk_pmic_auxadc_chan mt6373_auxadc_ch_desc[] = { + MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST3, 0, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST3, 1, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST3, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1), + + MTK_PMIC_ADC_EXT_CHAN(VIN1, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 1, MT6363_PULLUP_RES_30K, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN2, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 2, MT6363_PULLUP_RES_OPEN, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN3, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 3, MT6363_PULLUP_RES_OPEN, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN4, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 4, MT6363_PULLUP_RES_OPEN, 32, 1, 1), + MTK_PMIC_ADC_EXT_CHAN(VIN5, + PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15, + PMIC_AUXADC_SDMADC_CON0, 5, MT6363_PULLUP_RES_OPEN, 32, 1, 1), +}; + static void mt6358_stop_imp_conv(struct mt6359_auxadc *adc_dev) { const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; @@ -313,9 +461,10 @@ static void mt6358_stop_imp_conv(struct mt6359_auxadc *adc_dev) regmap_clear_bits(regmap, cinfo->regs[PMIC_AUXADC_DCM_CON], MT6358_DCM_CK_SW_EN); } -static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev) +static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev, const struct iio_chan_spec *chan) { const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; struct regmap *regmap = adc_dev->regmap; u32 val; int ret; @@ -323,8 +472,8 @@ static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev) regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_DCM_CON], MT6358_DCM_CK_SW_EN); regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_IMP1], MT6358_IMP1_AUTOREPEAT_EN); - ret = regmap_read_poll_timeout(adc_dev->regmap, cinfo->regs[PMIC_AUXADC_IMP0], - val, val & MT6358_IMP0_IRQ_RDY, + ret = regmap_read_poll_timeout(regmap, cinfo->regs[desc->rdy_idx], + val, val & desc->rdy_mask, IMP_POLL_DELAY_US, AUXADC_TIMEOUT_US); if (ret) { mt6358_stop_imp_conv(adc_dev); @@ -334,7 +483,8 @@ static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev) return 0; } -static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat) +static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *vbat, int *ibat) { const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; struct regmap *regmap = adc_dev->regmap; @@ -342,7 +492,7 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat) u32 val_v; int ret; - ret = mt6358_start_imp_conv(adc_dev); + ret = mt6358_start_imp_conv(adc_dev, chan); if (ret) return ret; @@ -359,17 +509,19 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat) return 0; } -static int mt6359_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat) +static int mt6359_read_imp(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *vbat, int *ibat) { const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; struct regmap *regmap = adc_dev->regmap; u32 val, val_v, val_i; int ret; /* Start conversion */ regmap_write(regmap, cinfo->regs[PMIC_AUXADC_IMP0], MT6359_IMP0_CONV_EN); - ret = regmap_read_poll_timeout(regmap, cinfo->regs[PMIC_AUXADC_IMP1], - val, val & MT6359_IMP1_IRQ_RDY, + ret = regmap_read_poll_timeout(regmap, cinfo->regs[desc->rdy_idx], + val, val & desc->rdy_mask, IMP_POLL_DELAY_US, AUXADC_TIMEOUT_US); /* Stop conversion regardless of the result */ @@ -404,6 +556,7 @@ static const struct mtk_pmic_auxadc_info mt6357_chip_info = { .regs = mt6357_auxadc_regs, .imp_adc_num = MT6357_IMP_ADC_NUM, .read_imp = mt6358_read_imp, + .vref_mV = 1800, }; static const struct mtk_pmic_auxadc_info mt6358_chip_info = { @@ -414,6 +567,7 @@ static const struct mtk_pmic_auxadc_info mt6358_chip_info = { .regs = mt6358_auxadc_regs, .imp_adc_num = MT6358_IMP_ADC_NUM, .read_imp = mt6358_read_imp, + .vref_mV = 1800, }; static const struct mtk_pmic_auxadc_info mt6359_chip_info = { @@ -424,6 +578,29 @@ static const struct mtk_pmic_auxadc_info mt6359_chip_info = { .regs = mt6359_auxadc_regs, .sec_unlock_key = 0x6359, .read_imp = mt6359_read_imp, + .vref_mV = 1800, +}; + +static const struct mtk_pmic_auxadc_info mt6363_chip_info = { + .model_name = "MT6363", + .channels = mt6363_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6363_auxadc_channels), + .desc = mt6363_auxadc_ch_desc, + .regs = mt6363_auxadc_regs, + .is_spmi = true, + .no_reset = true, + .vref_mV = 1840, +}; + +static const struct mtk_pmic_auxadc_info mt6373_chip_info = { + .model_name = "MT6373", + .channels = mt6373_auxadc_channels, + .num_channels = ARRAY_SIZE(mt6373_auxadc_channels), + .desc = mt6373_auxadc_ch_desc, + .regs = mt6363_auxadc_regs, + .is_spmi = true, + .no_reset = true, + .vref_mV = 1840, }; static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev) @@ -431,6 +608,10 @@ static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev) const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; struct regmap *regmap = adc_dev->regmap; + /* Some PMICs do not support reset */ + if (cinfo->no_reset) + return; + /* Unlock HK_TOP writes */ if (cinfo->sec_unlock_key) regmap_write(regmap, cinfo->regs[PMIC_HK_TOP_WKEY], cinfo->sec_unlock_key); @@ -446,13 +627,29 @@ static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev) regmap_write(regmap, cinfo->regs[PMIC_HK_TOP_WKEY], 0); } -static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev, - const struct iio_chan_spec *chan, int *out) +/** + * mt6359_auxadc_sample_adc_val() - Start ADC channel sampling and read value + * @adc_dev: Main driver structure + * @chan: IIO Channel spec for requested ADC + * @out: Preallocated variable to store the value read from HW + * + * This function starts the sampling for an ADC channel, waits until all + * of the samples are averaged and then reads the value from the HW. + * + * Note that the caller must stop the ADC sampling on its own, as this + * function *never* stops it. + * + * Return: + * Negative number for error; + * Upon success returns zero and writes the read value to *out. + */ +static int mt6359_auxadc_sample_adc_val(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, u32 *out) { const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; struct regmap *regmap = adc_dev->regmap; - u32 val; + u32 reg, rdy_mask, val, lval; int ret; /* Request to start sampling for ADC channel */ @@ -463,16 +660,95 @@ static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev, /* Wait until all samples are averaged */ fsleep(desc->num_samples * AUXADC_AVG_TIME_US); - ret = regmap_read_poll_timeout(regmap, - cinfo->regs[PMIC_AUXADC_ADC0] + (chan->address << 1), - val, val & PMIC_AUXADC_RDY_BIT, + reg = cinfo->regs[PMIC_AUXADC_ADC0] + (chan->address << 1); + rdy_mask = PMIC_AUXADC_RDY_BIT; + + /* + * Even though for both PWRAP and SPMI cases the ADC HW signals that + * the data is ready by setting AUXADC_RDY_BIT, for SPMI the register + * read is only 8 bits long: for this case, the check has to be done + * on the ADC(x)_H register (high bits) and the rdy_mask needs to be + * shifted to the right by the same 8 bits. + */ + if (cinfo->is_spmi) { + rdy_mask >>= 8; + reg += 1; + } + + ret = regmap_read_poll_timeout(regmap, reg, val, val & rdy_mask, AUXADC_POLL_DELAY_US, AUXADC_TIMEOUT_US); + if (ret) { + dev_dbg(adc_dev->dev, "ADC read timeout for chan %lu\n", chan->address); + return ret; + } + + if (cinfo->is_spmi) { + ret = regmap_read(regmap, reg - 1, &lval); + if (ret) + return ret; + + val = (val << 8) | lval; + } + + *out = val; + return 0; +} + +static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev, + const struct iio_chan_spec *chan, int *out) +{ + const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info; + const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index]; + struct regmap *regmap = adc_dev->regmap; + int ret, adc_stop_err; + u8 ext_sel; + u32 val; + + if (desc->ext_sel_idx >= 0) { + ext_sel = FIELD_PREP(MT6363_EXT_PURES_MASK, desc->ext_sel_pu); + ext_sel |= FIELD_PREP(MT6363_EXT_CHAN_MASK, desc->ext_sel_ch); + + ret = regmap_update_bits(regmap, cinfo->regs[desc->ext_sel_idx], + MT6363_EXT_PURES_MASK | MT6363_EXT_CHAN_MASK, + ext_sel); + if (ret) + return ret; + } + + /* + * Get sampled value, then stop sampling unconditionally; the gathered + * value is good regardless of if the ADC could be stopped. + * + * Note that if the ADC cannot be stopped but sampling was ok, this + * function will not return any error, but will set the timed_out + * status: this is not critical, as the ADC may auto recover and auto + * stop after some time (depending on the PMIC model); if not, the next + * read attempt will return -ETIMEDOUT and, for models that support it, + * reset will be triggered. + */ + ret = mt6359_auxadc_sample_adc_val(adc_dev, chan, &val); + + adc_stop_err = regmap_write(regmap, cinfo->regs[desc->req_idx], 0); + if (adc_stop_err) { + dev_warn(adc_dev->dev, "Could not stop the ADC: %d\n,", adc_stop_err); + adc_dev->timed_out = true; + } + + /* If any sampling error occurred, the retrieved value is invalid */ if (ret) return ret; - /* Stop sampling */ - regmap_write(regmap, cinfo->regs[desc->req_idx], 0); + /* ...and deactivate the ADC GPIO if previously done */ + if (desc->ext_sel_idx >= 0) { + ext_sel = FIELD_PREP(MT6363_EXT_PURES_MASK, MT6363_PULLUP_RES_OPEN); + + ret = regmap_update_bits(regmap, cinfo->regs[desc->ext_sel_idx], + MT6363_EXT_PURES_MASK, ext_sel); + if (ret) + return ret; + } + /* Everything went fine, give back the ADC reading */ *out = val & GENMASK(chan->scan_type.realbits - 1, 0); return 0; } @@ -493,7 +769,7 @@ static int mt6359_auxadc_read_raw(struct iio_dev *indio_dev, int ret; if (mask == IIO_CHAN_INFO_SCALE) { - *val = desc->r_ratio.numerator * AUXADC_VOLT_FULL; + *val = desc->r_ratio.numerator * cinfo->vref_mV; if (desc->r_ratio.denominator > 1) { *val2 = desc->r_ratio.denominator; @@ -506,10 +782,16 @@ static int mt6359_auxadc_read_raw(struct iio_dev *indio_dev, scoped_guard(mutex, &adc_dev->lock) { switch (chan->scan_index) { case PMIC_AUXADC_CHAN_IBAT: - ret = adc_dev->chip_info->read_imp(adc_dev, NULL, val); + if (!adc_dev->chip_info->read_imp) + return -EOPNOTSUPP; + + ret = adc_dev->chip_info->read_imp(adc_dev, chan, NULL, val); break; case PMIC_AUXADC_CHAN_VBAT: - ret = adc_dev->chip_info->read_imp(adc_dev, val, NULL); + if (!adc_dev->chip_info->read_imp) + return -EOPNOTSUPP; + + ret = adc_dev->chip_info->read_imp(adc_dev, chan, val, NULL); break; default: ret = mt6359_auxadc_read_adc(adc_dev, chan, val); @@ -543,15 +825,36 @@ static const struct iio_info mt6359_auxadc_iio_info = { static int mt6359_auxadc_probe(struct platform_device *pdev) { + const struct mtk_pmic_auxadc_info *chip_info; struct device *dev = &pdev->dev; - struct device *mt6397_mfd_dev = dev->parent; + struct device *mfd_dev = dev->parent; struct mt6359_auxadc *adc_dev; struct iio_dev *indio_dev; + struct device *regmap_dev; struct regmap *regmap; int ret; + chip_info = device_get_match_data(dev); + if (!chip_info) + return -EINVAL; + /* + * The regmap for this device has to be acquired differently for + * SoC PMIC Wrapper and SPMI PMIC cases: + * + * If this is under SPMI, the regmap comes from the direct parent of + * this driver: this_device->parent(mfd). + * ... or ... + * If this is under the SoC PMIC Wrapper, the regmap comes from the + * parent of the MT6397 MFD: this_device->parent(mfd)->parent(pwrap) + */ + if (chip_info->is_spmi) + regmap_dev = mfd_dev; + else + regmap_dev = mfd_dev->parent; + + /* Regmap is from SoC PMIC Wrapper, parent of the mt6397 MFD */ - regmap = dev_get_regmap(mt6397_mfd_dev->parent, NULL); + regmap = dev_get_regmap(regmap_dev, NULL); if (!regmap) return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); @@ -562,10 +865,7 @@ static int mt6359_auxadc_probe(struct platform_device *pdev) adc_dev = iio_priv(indio_dev); adc_dev->regmap = regmap; adc_dev->dev = dev; - - adc_dev->chip_info = device_get_match_data(dev); - if (!adc_dev->chip_info) - return -EINVAL; + adc_dev->chip_info = chip_info; mutex_init(&adc_dev->lock); @@ -588,6 +888,8 @@ static const struct of_device_id mt6359_auxadc_of_match[] = { { .compatible = "mediatek,mt6357-auxadc", .data = &mt6357_chip_info }, { .compatible = "mediatek,mt6358-auxadc", .data = &mt6358_chip_info }, { .compatible = "mediatek,mt6359-auxadc", .data = &mt6359_chip_info }, + { .compatible = "mediatek,mt6363-auxadc", .data = &mt6363_chip_info }, + { .compatible = "mediatek,mt6373-auxadc", .data = &mt6373_chip_info }, { } }; MODULE_DEVICE_TABLE(of, mt6359_auxadc_of_match); diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index f8e98b6fa7e9..69b3569c90e5 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -264,10 +264,9 @@ static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p) struct { u16 values[MT6360_CHAN_MAX]; aligned_s64 timestamp; - } data; + } data = { }; int i = 0, bit, val, ret; - memset(&data, 0, sizeof(data)); iio_for_each_active_channel(indio_dev, bit) { ret = mt6360_adc_read_channel(mad, bit, &val); if (ret < 0) { diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index d5209f32adb3..b03cf584b165 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -330,7 +330,7 @@ static int qcom_vadc7_scale_hw_calib_die_temp( const struct adc5_data *data, u16 adc_code, int *result_mdec); -static struct qcom_adc5_scale_type scale_adc5_fn[] = { +static const struct qcom_adc5_scale_type scale_adc5_fn[] = { [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt}, [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm}, [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm}, diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 325e3747a134..bd62daea0a3e 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -404,12 +404,10 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) struct { u16 values[SARADC_MAX_CHANNELS]; aligned_s64 timestamp; - } data; + } data = { }; int ret; int i, j = 0; - memset(&data, 0, sizeof(data)); - mutex_lock(&info->lock); iio_for_each_active_channel(i_dev, i) { diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index 6ff47415a222..ad9738228b7f 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -645,12 +645,10 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p) struct { u16 vals[RTQ6056_MAX_CHANNEL]; aligned_s64 timestamp; - } data; + } data = { }; unsigned int raw; int i = 0, bit, ret; - memset(&data, 0, sizeof(data)); - pm_runtime_get_sync(dev); iio_for_each_active_channel(indio_dev, bit) { diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 21c04a98b3b6..3d800762c5fc 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -407,7 +407,6 @@ static const struct irq_domain_ops stm32_adc_domain_ops = { static int stm32_adc_irq_probe(struct platform_device *pdev, struct stm32_adc_priv *priv) { - struct device_node *np = pdev->dev.of_node; unsigned int i; /* @@ -421,7 +420,7 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, return priv->irq[i]; } - priv->domain = irq_domain_create_simple(of_fwnode_handle(np), + priv->domain = irq_domain_create_simple(dev_fwnode(&pdev->dev), STM32_ADC_MAX_ADCS, 0, &stm32_adc_domain_ops, priv); diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index e84babf43385..b9f93116e114 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -216,7 +216,7 @@ struct stm32_adc; struct stm32_adc_cfg { const struct stm32_adc_regspec *regs; const struct stm32_adc_info *adc_info; - struct stm32_adc_trig_info *trigs; + const struct stm32_adc_trig_info *trigs; bool clk_required; bool has_vregready; bool has_boostmode; @@ -383,7 +383,7 @@ static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = { }; /* STM32F4 external trigger sources for all instances */ -static struct stm32_adc_trig_info stm32f4_adc_trigs[] = { +static const struct stm32_adc_trig_info stm32f4_adc_trigs[] = { { TIM1_CH1, STM32_EXT0 }, { TIM1_CH2, STM32_EXT1 }, { TIM1_CH3, STM32_EXT2 }, @@ -473,7 +473,7 @@ static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = { }; /* STM32H7 external trigger sources for all instances */ -static struct stm32_adc_trig_info stm32h7_adc_trigs[] = { +static const struct stm32_adc_trig_info stm32h7_adc_trigs[] = { { TIM1_CH1, STM32_EXT0 }, { TIM1_CH2, STM32_EXT1 }, { TIM1_CH3, STM32_EXT2 }, @@ -2470,7 +2470,7 @@ static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping) static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev) { struct stm32_adc *adc = iio_priv(indio_dev); - struct dma_slave_config config; + struct dma_slave_config config = { }; int ret; adc->dma_chan = dma_request_chan(dev, "rx"); @@ -2494,7 +2494,6 @@ static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev) } /* Configure DMA channel to read data register */ - memset(&config, 0, sizeof(config)); config.src_addr = (dma_addr_t)adc->common->phys_base; config.src_addr += adc->offset + adc->cfg->regs->dr; config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index f583924eb16b..c2d21eecafe7 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -8,6 +8,7 @@ #include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/export.h> #include <linux/iio/adc/stm32-dfsdm-adc.h> #include <linux/iio/backend.h> #include <linux/iio/buffer.h> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index 041dc9ebc048..47e2d1338e9e 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -8,6 +8,7 @@ #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/export.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/interrupt.h> diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 21181cc3bd85..48549d617e5f 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -450,11 +450,9 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) struct { s16 chan; aligned_s64 timestamp; - } scan; + } scan = { }; int chan, ret, res; - memset(&scan, 0, sizeof(scan)); - mutex_lock(&data->lock); chan = find_first_bit(indio_dev->active_scan_mask, iio_get_masklength(indio_dev)); diff --git a/drivers/iio/adc/ti-ads1119.c b/drivers/iio/adc/ti-ads1119.c index d280c949cf47..d2f86e1ec656 100644 --- a/drivers/iio/adc/ti-ads1119.c +++ b/drivers/iio/adc/ti-ads1119.c @@ -507,12 +507,10 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private) struct { s16 sample; aligned_s64 timestamp; - } scan; + } scan = { }; unsigned int index; int ret; - memset(&scan, 0, sizeof(scan)); - if (!iio_trigger_using_own(indio_dev)) { index = find_first_bit(indio_dev->active_scan_mask, iio_get_masklength(indio_dev)); diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 085f0d6fb39e..b18f30d3fdbe 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -625,7 +625,7 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private) * 16 bits of data into the buffer. */ unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate); - u8 tweek_offset = num_bytes == 2 ? 1 : 0; + u8 tweak_offset = num_bytes == 2 ? 1 : 0; if (iio_trigger_using_own(indio_dev)) ret = ads131e08_read_data(st, st->readback_len); @@ -640,25 +640,25 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private) dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; /* - * Tweek offset is 0: + * Tweak offset is 0: * +---+---+---+---+ * |D0 |D1 |D2 | X | (3 data bytes) * +---+---+---+---+ * a+0 a+1 a+2 a+3 * - * Tweek offset is 1: + * Tweak offset is 1: * +---+---+---+---+ * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes) * +---+---+---+---+ * a+0 a+1 a+2 a+3 */ - memcpy(dest + tweek_offset, src, num_bytes); + memcpy(dest + tweak_offset, src, num_bytes); /* * Data conversion from 16 bits of data to 24 bits of data * is done by sign extension (properly filling padding byte). */ - if (tweek_offset) + if (tweak_offset) *dest = *src & BIT(7) ? 0xff : 0x00; i++; diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index 3f375c1f586c..7e57006a8a12 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -200,11 +200,9 @@ static irqreturn_t lmp92064_trigger_handler(int irq, void *p) struct { u16 values[2]; aligned_s64 timestamp; - } data; + } data = { }; int ret; - memset(&data, 0, sizeof(data)); - ret = lmp92064_read_meas(priv, data.values); if (ret) goto err; diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c index c2d2aada6772..74471f08662e 100644 --- a/drivers/iio/adc/ti-tsc2046.c +++ b/drivers/iio/adc/ti-tsc2046.c @@ -276,7 +276,7 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx]; unsigned int val, val_normalized = 0; int ret, i, count_skip = 0, max_count; - struct spi_transfer xfer; + struct spi_transfer xfer = { }; struct spi_message msg; u8 cmd; @@ -314,7 +314,6 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, /* automatically power down on last sample */ tx_buf[i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); - memset(&xfer, 0, sizeof(xfer)); xfer.tx_buf = tx_buf; xfer.rx_buf = rx_buf; xfer.len = sizeof(*tx_buf) * max_count; diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 6404b015234a..1b3b1843a801 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -28,9 +28,6 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> -/* This will be the driver name the kernel reports */ -#define DRIVER_NAME "vf610-adc" - /* Vybrid/IMX ADC registers */ #define VF610_REG_ADC_HC0 0x00 #define VF610_REG_ADC_HC1 0x04 @@ -952,7 +949,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, static struct platform_driver vf610_adc_driver = { .probe = vf610_adc_probe, .driver = { - .name = DRIVER_NAME, + .name = "vf610-adc", .of_match_table = vf610_adc_match, .pm = pm_sleep_ptr(&vf610_adc_pm_ops), }, diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index e73c9b983395..d06ac786501c 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -45,7 +45,7 @@ struct ad8366_state { struct gpio_desc *reset_gpio; unsigned char ch[2]; enum ad8366_type type; - struct ad8366_info *info; + const struct ad8366_info *info; /* * DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. @@ -53,7 +53,7 @@ struct ad8366_state { unsigned char data[2] __aligned(IIO_DMA_MINALIGN); }; -static struct ad8366_info ad8366_infos[] = { +static const struct ad8366_info ad8366_infos[] = { [ID_AD8366] = { .gain_min = 4500, .gain_max = 20500, @@ -163,7 +163,7 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, long mask) { struct ad8366_state *st = iio_priv(indio_dev); - struct ad8366_info *inf = st->info; + const struct ad8366_info *inf = st->info; int code = 0, gain; int ret; diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c index 74f8429d652b..40f396ea9069 100644 --- a/drivers/iio/amplifiers/ada4250.c +++ b/drivers/iio/amplifiers/ada4250.c @@ -13,8 +13,8 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> - -#include <linux/unaligned.h> +#include <linux/types.h> +#include <linux/units.h> /* ADA4250 Register Map */ #define ADA4250_REG_GAIN_MUX 0x00 @@ -56,13 +56,14 @@ enum ada4250_current_bias { struct ada4250_state { struct spi_device *spi; struct regmap *regmap; - struct regulator *reg; /* Protect against concurrent accesses to the device and data content */ struct mutex lock; + int avdd_uv; + int offset_uv; u8 bias; u8 gain; - int offset_uv; bool refbuf_en; + __le16 reg_val_16 __aligned(IIO_DMA_MINALIGN); }; /* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */ @@ -91,8 +92,7 @@ static int ada4250_set_offset_uv(struct iio_dev *indio_dev, if (st->bias == 0 || st->bias == 3) return -EINVAL; - voltage_v = regulator_get_voltage(st->reg); - voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000); + voltage_v = DIV_ROUND_CLOSEST(st->avdd_uv, MICRO); if (st->bias == ADA4250_BIAS_AVDD) x[0] = voltage_v; @@ -292,50 +292,33 @@ static const struct iio_chan_spec ada4250_channels[] = { } }; -static void ada4250_reg_disable(void *data) -{ - regulator_disable(data); -} - static int ada4250_init(struct ada4250_state *st) { + struct device *dev = &st->spi->dev; int ret; u16 chip_id; - u8 data[2] __aligned(8) = {}; - struct spi_device *spi = st->spi; - st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable"); + st->refbuf_en = device_property_read_bool(dev, "adi,refbuf-enable"); - st->reg = devm_regulator_get(&spi->dev, "avdd"); - if (IS_ERR(st->reg)) - return dev_err_probe(&spi->dev, PTR_ERR(st->reg), + st->avdd_uv = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (st->avdd_uv < 0) + return dev_err_probe(dev, st->avdd_uv, "failed to get the AVDD voltage\n"); - ret = regulator_enable(st->reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable specified AVDD supply\n"); - return ret; - } - - ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg); - if (ret) - return ret; - ret = regmap_write(st->regmap, ADA4250_REG_RESET, FIELD_PREP(ADA4250_RESET_MSK, 1)); if (ret) return ret; - ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2); + ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, &st->reg_val_16, + sizeof(st->reg_val_16)); if (ret) return ret; - chip_id = get_unaligned_le16(data); + chip_id = le16_to_cpu(st->reg_val_16); - if (chip_id != ADA4250_CHIP_ID) { - dev_err(&spi->dev, "Invalid chip ID.\n"); - return -EINVAL; - } + if (chip_id != ADA4250_CHIP_ID) + dev_info(dev, "Invalid chip ID: 0x%02X.\n", chip_id); return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN, FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en)); @@ -368,10 +351,8 @@ static int ada4250_probe(struct spi_device *spi) mutex_init(&st->lock); ret = ada4250_init(st); - if (ret) { - dev_err(&spi->dev, "ADA4250 init failed\n"); - return ret; - } + if (ret) + return dev_err_probe(&spi->dev, ret, "ADA4250 init failed\n"); return devm_iio_device_register(&spi->dev, indio_dev); } diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index c06515987e7a..9bf75dee7ff8 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only - /* +/* * Copyright (c) 2012 Analog Devices, Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> */ diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c index de0b87edd188..59f3a4fa9e9f 100644 --- a/drivers/iio/chemical/atlas-ezo-sensor.c +++ b/drivers/iio/chemical/atlas-ezo-sensor.c @@ -82,7 +82,7 @@ static const struct iio_chan_spec atlas_hum_ezo_channels[] = { }, }; -static struct atlas_ezo_device atlas_ezo_devices[] = { +static const struct atlas_ezo_device atlas_ezo_devices[] = { [ATLAS_CO2_EZO] = { .channels = atlas_co2_ezo_channels, .num_channels = 1, diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index cb6662b92137..1daaa36f87a9 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -24,7 +24,6 @@ #include <linux/iio/triggered_buffer.h> #include <linux/pm_runtime.h> -#define ATLAS_REGMAP_NAME "atlas_regmap" #define ATLAS_DRV_NAME "atlas" #define ATLAS_REG_DEV_TYPE 0x00 @@ -96,7 +95,7 @@ struct atlas_data { }; static const struct regmap_config atlas_regmap_config = { - .name = ATLAS_REGMAP_NAME, + .name = "atlas_regmap", .reg_bits = 8, .val_bits = 8, }; diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 3e850562ab00..61d446fd456c 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -158,7 +158,7 @@ const struct regmap_config bme680_regmap_config = { .val_bits = 8, .max_register = 0xef, .volatile_table = &bme680_volatile_table, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS(bme680_regmap_config, "IIO_BME680"); diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 8316720b1fa3..5df1926cd5d9 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -587,7 +587,7 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p) struct { int data[SCD30_MEAS_COUNT]; aligned_s64 ts; - } scan; + } scan = { }; int ret; mutex_lock(&state->lock); @@ -595,7 +595,6 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p) ret = scd30_read_poll(state); else ret = scd30_read_meas(state); - memset(&scan, 0, sizeof(scan)); memcpy(scan.data, state->meas, sizeof(state->meas)); mutex_unlock(&state->lock); if (ret) diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index 2463149519b6..8859f89fb2a9 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -665,10 +665,9 @@ static irqreturn_t scd4x_trigger_handler(int irq, void *p) struct { uint16_t data[3]; aligned_s64 ts; - } scan; + } scan = { }; int ret; - memset(&scan, 0, sizeof(scan)); mutex_lock(&state->lock); ret = scd4x_read_poll(state, scan.data); mutex_unlock(&state->lock); diff --git a/drivers/iio/chemical/sunrise_co2.c b/drivers/iio/chemical/sunrise_co2.c index af79efde37e8..158be9d798d2 100644 --- a/drivers/iio/chemical/sunrise_co2.c +++ b/drivers/iio/chemical/sunrise_co2.c @@ -51,13 +51,12 @@ static int sunrise_regmap_read(void *context, const void *reg_buf, { struct i2c_client *client = context; struct sunrise_dev *sunrise = i2c_get_clientdata(client); - union i2c_smbus_data data; + union i2c_smbus_data data = { }; int ret; if (reg_size != 1 || !val_size) return -EINVAL; - memset(&data, 0, sizeof(data)); data.block[0] = val_size; /* @@ -88,14 +87,13 @@ static int sunrise_regmap_write(void *context, const void *val_buf, size_t count { struct i2c_client *client = context; struct sunrise_dev *sunrise = i2c_get_clientdata(client); - union i2c_smbus_data data; + union i2c_smbus_data data = { }; /* Discard reg address from values count. */ if (!count) return -EINVAL; count--; - memset(&data, 0, sizeof(data)); data.block[0] = count; memcpy(&data.block[1], (u8 *)val_buf + 1, count); diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig index fefad9572790..394e319c9c97 100644 --- a/drivers/iio/common/cros_ec_sensors/Kconfig +++ b/drivers/iio/common/cros_ec_sensors/Kconfig @@ -30,3 +30,12 @@ config IIO_CROS_EC_SENSORS_LID_ANGLE convertible devices. This module is loaded when the EC can calculate the angle between the base and the lid. + +config IIO_CROS_EC_ACTIVITY + tristate "ChromeOS EC Activity Sensors" + depends on IIO_CROS_EC_SENSORS_CORE + help + Module to handle activity events presented by the ChromeOS EC sensor hub. + Activities can be a proximity detector (on body/off body detection) + or a significant motion detector. + Creates an IIO device to manage all activities. diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile index c358fa0328ab..a7dfb5794cae 100644 --- a/drivers/iio/common/cros_ec_sensors/Makefile +++ b/drivers/iio/common/cros_ec_sensors/Makefile @@ -7,3 +7,4 @@ cros-ec-sensors-core-objs += cros_ec_sensors_core.o cros_ec_sensors_trace.o obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros-ec-sensors-core.o obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o obj-$(CONFIG_IIO_CROS_EC_SENSORS_LID_ANGLE) += cros_ec_lid_angle.o +obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c new file mode 100644 index 000000000000..6e38d115b6fe --- /dev/null +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_activity.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cros_ec_activity - Driver for activities/gesture recognition. + * + * Copyright 2025 Google, Inc + * + * This driver uses the cros-ec interface to communicate with the ChromeOS + * EC about activity data. + */ + +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> + +#include <linux/iio/common/cros_ec_sensors_core.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> + +#define DRV_NAME "cros-ec-activity" + +/* state data for ec_sensors iio driver. */ +struct cros_ec_sensors_state { + /* Shared by all sensors */ + struct cros_ec_sensors_core_state core; + + struct iio_chan_spec *channels; + + int body_detection_channel_index; + int sig_motion_channel_index; +}; + +static const struct iio_event_spec cros_ec_activity_single_shot[] = { + { + .type = IIO_EV_TYPE_CHANGE, + /* significant motion trigger when we get out of still. */ + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_event_spec cros_ec_body_detect_events[] = { + { + .type = IIO_EV_TYPE_CHANGE, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static int cros_ec_activity_sensors_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cros_ec_sensors_state *st = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_PROXIMITY || mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + guard(mutex)(&st->core.cmd_lock); + st->core.param.cmd = MOTIONSENSE_CMD_GET_ACTIVITY; + st->core.param.get_activity.activity = + MOTIONSENSE_ACTIVITY_BODY_DETECTION; + ret = cros_ec_motion_send_host_cmd(&st->core, 0); + if (ret) + return ret; + + /* + * EC actually report if a body is near (1) or far (0). + * Units for proximity sensor after scale is in meter, + * so invert the result to return 0m when near and 1m when far. + */ + *val = !st->core.resp->get_activity.state; + return IIO_VAL_INT; +} + +static int cros_ec_activity_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 cros_ec_sensors_state *st = iio_priv(indio_dev); + int ret; + + if (chan->type != IIO_ACTIVITY && chan->type != IIO_PROXIMITY) + return -EINVAL; + + guard(mutex)(&st->core.cmd_lock); + st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES; + ret = cros_ec_motion_send_host_cmd(&st->core, 0); + if (ret) + return ret; + + switch (chan->type) { + case IIO_PROXIMITY: + return !!(st->core.resp->list_activities.enabled & + (1 << MOTIONSENSE_ACTIVITY_BODY_DETECTION)); + case IIO_ACTIVITY: + if (chan->channel2 == IIO_MOD_STILL) { + return !!(st->core.resp->list_activities.enabled & + (1 << MOTIONSENSE_ACTIVITY_SIG_MOTION)); + } + + dev_warn(&indio_dev->dev, "Unknown activity: %d\n", + chan->channel2); + return -EINVAL; + default: + dev_warn(&indio_dev->dev, "Unknown channel type: %d\n", + chan->type); + return -EINVAL; + } +} + +static int cros_ec_activity_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct cros_ec_sensors_state *st = iio_priv(indio_dev); + + guard(mutex)(&st->core.cmd_lock); + st->core.param.cmd = MOTIONSENSE_CMD_SET_ACTIVITY; + switch (chan->type) { + case IIO_PROXIMITY: + st->core.param.set_activity.activity = + MOTIONSENSE_ACTIVITY_BODY_DETECTION; + break; + case IIO_ACTIVITY: + if (chan->channel2 == IIO_MOD_STILL) { + st->core.param.set_activity.activity = + MOTIONSENSE_ACTIVITY_SIG_MOTION; + break; + } + dev_warn(&indio_dev->dev, "Unknown activity: %d\n", + chan->channel2); + return -EINVAL; + default: + dev_warn(&indio_dev->dev, "Unknown channel type: %d\n", + chan->type); + return -EINVAL; + } + st->core.param.set_activity.enable = state; + return cros_ec_motion_send_host_cmd(&st->core, 0); +} + +static int cros_ec_activity_push_data(struct iio_dev *indio_dev, + s16 *data, s64 timestamp) +{ + struct ec_response_activity_data *activity_data = + (struct ec_response_activity_data *)data; + enum motionsensor_activity activity = activity_data->activity; + u8 state = activity_data->state; + const struct cros_ec_sensors_state *st = iio_priv(indio_dev); + const struct iio_chan_spec *chan; + enum iio_event_direction dir; + int index; + + switch (activity) { + case MOTIONSENSE_ACTIVITY_BODY_DETECTION: + index = st->body_detection_channel_index; + dir = state ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; + break; + case MOTIONSENSE_ACTIVITY_SIG_MOTION: + index = st->sig_motion_channel_index; + dir = IIO_EV_DIR_FALLING; + break; + default: + dev_warn(&indio_dev->dev, "Unknown activity: %d\n", activity); + return 0; + } + chan = &st->channels[index]; + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, index, chan->event_spec[0].type, dir), + timestamp); + return 0; +} + +static irqreturn_t cros_ec_activity_capture(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + /* + * This callback would be called when a software trigger is + * used. But when this virtual sensor is present, it is guaranteed + * the sensor hub is advanced enough to not need a software trigger. + */ + dev_warn(&indio_dev->dev, "%s: Not Expected\n", __func__); + return IRQ_NONE; +} + +static const struct iio_info ec_sensors_info = { + .read_raw = &cros_ec_activity_sensors_read_raw, + .read_event_config = cros_ec_activity_read_event_config, + .write_event_config = cros_ec_activity_write_event_config, +}; + +static int cros_ec_sensors_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct cros_ec_sensors_state *st; + struct iio_chan_spec *channel; + unsigned long activities; + int i, index, ret, nb_activities; + + if (!ec_device) { + dev_warn(dev, "No CROS EC device found.\n"); + return -EINVAL; + } + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + ret = cros_ec_sensors_core_init(pdev, indio_dev, true, + cros_ec_activity_capture); + if (ret) + return ret; + + indio_dev->info = &ec_sensors_info; + st = iio_priv(indio_dev); + st->core.type = st->core.resp->info.type; + st->core.read_ec_sensors_data = cros_ec_sensors_read_cmd; + + st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES; + ret = cros_ec_motion_send_host_cmd(&st->core, 0); + if (ret) + return ret; + + activities = st->core.resp->list_activities.enabled | + st->core.resp->list_activities.disabled; + if (!activities) + return -ENODEV; + + /* Allocate a channel per activity and one for timestamp */ + nb_activities = hweight_long(activities) + 1; + st->channels = devm_kcalloc(dev, nb_activities, + sizeof(*st->channels), GFP_KERNEL); + if (!st->channels) + return -ENOMEM; + + channel = &st->channels[0]; + index = 0; + for_each_set_bit(i, &activities, BITS_PER_LONG) { + /* List all available triggers */ + if (i == MOTIONSENSE_ACTIVITY_BODY_DETECTION) { + channel->type = IIO_PROXIMITY; + channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + channel->event_spec = cros_ec_body_detect_events; + channel->num_event_specs = + ARRAY_SIZE(cros_ec_body_detect_events); + st->body_detection_channel_index = index; + } else { + channel->type = IIO_ACTIVITY; + channel->modified = 1; + channel->event_spec = cros_ec_activity_single_shot; + channel->num_event_specs = + ARRAY_SIZE(cros_ec_activity_single_shot); + if (i == MOTIONSENSE_ACTIVITY_SIG_MOTION) { + channel->channel2 = IIO_MOD_STILL; + st->sig_motion_channel_index = index; + } else { + dev_warn(dev, "Unknown activity: %d\n", i); + continue; + } + } + channel->ext_info = cros_ec_sensors_limited_info; + channel->scan_index = index++; + channel++; + } + + /* Timestamp */ + channel->scan_index = index; + channel->type = IIO_TIMESTAMP; + channel->channel = -1; + channel->scan_type.sign = 's'; + channel->scan_type.realbits = 64; + channel->scan_type.storagebits = 64; + + indio_dev->channels = st->channels; + indio_dev->num_channels = index + 1; + + return cros_ec_sensors_core_register(dev, indio_dev, + cros_ec_activity_push_data); +} + +static struct platform_driver cros_ec_sensors_platform_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_ec_sensors_probe, +}; +module_platform_driver(cros_ec_sensors_platform_driver); + +MODULE_DESCRIPTION("ChromeOS EC activity sensors driver"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index 700ebcd68ff4..9ac80e4b7d75 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -486,6 +486,16 @@ const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = { }; EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info); +const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[] = { + { + .name = "id", + .shared = IIO_SHARED_BY_ALL, + .read = cros_ec_sensors_id + }, + { } +}; +EXPORT_SYMBOL_GPL(cros_ec_sensors_limited_info); + /** * cros_ec_sensors_idx_to_reg - convert index into offset in shared memory * @st: pointer to state information for device diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 2055a03cbeb1..a61428bfdce3 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -11,7 +11,7 @@ #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -static struct { +static const struct { u32 usage_id; int unit; /* 0 for default others from HID sensor spec */ int scale_val0; /* scale, whole number */ diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index a44b163f3183..93c33bc3e1be 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -293,10 +293,9 @@ static irqreturn_t ad3552r_trigger_handler(int irq, void *p) struct iio_buffer *buf = indio_dev->buffer; struct ad3552r_desc *dac = iio_priv(indio_dev); /* Maximum size of a scan */ - u8 buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE]; + u8 buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE] = { }; int err; - memset(buff, 0, sizeof(buff)); err = iio_pop_from_buffer(buf, buff); if (err) goto end; diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index f63af704b77e..0ddce7b218e3 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -426,7 +426,7 @@ static const struct regmap_config ad5380_regmap_config = { .val_bits = 14, .max_register = AD5380_REG_DATA(40), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = ad5380_reg_false, .readable_reg = ad5380_reg_false, diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 6eb4027a44fb..cd47cb1c685c 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -155,7 +155,7 @@ struct ad5770r_output_modes { int max; }; -static struct ad5770r_output_modes ad5770r_rng_tbl[] = { +static const struct ad5770r_output_modes ad5770r_rng_tbl[] = { { 0, AD5770R_CH0_0_300, 0, 300 }, { 0, AD5770R_CH0_NEG_60_0, -60, 0 }, { 0, AD5770R_CH0_NEG_60_300, -60, 300 }, diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c index 33faba4b02c2..a0e546dba368 100644 --- a/drivers/iio/dac/adi-axi-dac.c +++ b/drivers/iio/dac/adi-axi-dac.c @@ -635,15 +635,26 @@ static int axi_dac_ddr_disable(struct iio_backend *back) AXI_DAC_CNTRL_2_SDR_DDR_N); } +static int axi_dac_wait_bus_free(struct axi_dac_state *st) +{ + u32 val; + int ret; + + ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_UI_STATUS_REG, val, + FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, val) == 0, 10, + 100 * KILO); + if (ret == -ETIMEDOUT) + dev_err(st->dev, "AXI bus timeout\n"); + + return ret; +} + static int axi_dac_data_stream_enable(struct iio_backend *back) { struct axi_dac_state *st = iio_backend_get_priv(back); - int ret, val; + int ret; - ret = regmap_read_poll_timeout(st->regmap, - AXI_DAC_UI_STATUS_REG, val, - FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, val) == 0, - 10, 100 * KILO); + ret = axi_dac_wait_bus_free(st); if (ret) return ret; @@ -734,12 +745,9 @@ static int __axi_dac_bus_reg_write(struct iio_backend *back, u32 reg, if (ret) return ret; - ret = regmap_read_poll_timeout(st->regmap, - AXI_DAC_UI_STATUS_REG, ival, - FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0, - 10, 100 * KILO); - if (ret == -ETIMEDOUT) - dev_err(st->dev, "AXI read timeout\n"); + ret = axi_dac_wait_bus_free(st); + if (ret) + return ret; /* Cleaning always AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA */ return regmap_clear_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG, @@ -760,7 +768,6 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val, { struct axi_dac_state *st = iio_backend_get_priv(back); int ret; - u32 ival; guard(mutex)(&st->lock); @@ -773,10 +780,7 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val, if (ret) return ret; - ret = regmap_read_poll_timeout(st->regmap, - AXI_DAC_UI_STATUS_REG, ival, - FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0, - 10, 100 * KILO); + ret = axi_dac_wait_bus_free(st); if (ret) return ret; @@ -787,7 +791,7 @@ static int axi_dac_bus_set_io_mode(struct iio_backend *back, enum ad3552r_io_mode mode) { struct axi_dac_state *st = iio_backend_get_priv(back); - int ival, ret; + int ret; if (mode > AD3552R_IO_MODE_QSPI) return -EINVAL; @@ -800,9 +804,7 @@ static int axi_dac_bus_set_io_mode(struct iio_backend *back, if (ret) return ret; - return regmap_read_poll_timeout(st->regmap, AXI_DAC_UI_STATUS_REG, ival, - FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0, 10, - 100 * KILO); + return axi_dac_wait_bus_free(st); } static void axi_dac_child_remove(void *data) diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index 1f24f07d1ad2..7a2ee26a7d68 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -622,7 +622,7 @@ static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = { { } }; -static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = { +static const struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = { LTC2688_CHAN_EXT_INFO("dither_raw", LTC2688_INPUT_B, IIO_SEPARATE, ltc2688_dac_input_read, ltc2688_dac_input_write), LTC2688_CHAN_EXT_INFO("dither_raw_available", LTC2688_INPUT_B_AVAIL, diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 84336736a47b..d334c67821ad 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -15,8 +15,6 @@ #include <linux/iio/sysfs.h> #include <linux/iio/dac/max517.h> -#define MAX517_DRV_NAME "max517" - /* Commands */ #define COMMAND_CHANNEL0 0x00 #define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */ @@ -200,7 +198,7 @@ MODULE_DEVICE_TABLE(i2c, max517_id); static struct i2c_driver max517_driver = { .driver = { - .name = MAX517_DRV_NAME, + .name = "max517", .pm = pm_sleep_ptr(&max517_pm_ops), }, .probe = max517_probe, diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 62972494a229..23b9e3a09ec8 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -24,8 +24,6 @@ #include <linux/iio/dac/mcp4725.h> -#define MCP4725_DRV_NAME "mcp4725" - #define MCP472X_REF_VDD 0x00 #define MCP472X_REF_VREF_UNBUFFERED 0x02 #define MCP472X_REF_VREF_BUFFERED 0x03 @@ -546,7 +544,7 @@ MODULE_DEVICE_TABLE(of, mcp4725_of_match); static struct i2c_driver mcp4725_driver = { .driver = { - .name = MCP4725_DRV_NAME, + .name = "mcp4725", .of_match_table = mcp4725_of_match, .pm = pm_sleep_ptr(&mcp4725_pm_ops), }, diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c index a35c37d2261d..e91090e4a66d 100644 --- a/drivers/iio/dac/rohm-bd79703.c +++ b/drivers/iio/dac/rohm-bd79703.c @@ -35,7 +35,7 @@ static const struct regmap_config bd79703_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = BD79703_MAX_REGISTER, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; /* Dynamic driver private data */ diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index b30ff7bb4400..93639599b2b9 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -178,10 +178,8 @@ static int vf610_dac_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_dac)); - if (!indio_dev) { - dev_err(&pdev->dev, "Failed allocating iio device\n"); + if (!indio_dev) return -ENOMEM; - } info = iio_priv(indio_dev); info->dev = &pdev->dev; @@ -190,12 +188,10 @@ static int vf610_dac_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); - info->clk = devm_clk_get(&pdev->dev, "dac"); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "Failed getting clock, err = %ld\n", - PTR_ERR(info->clk)); - return PTR_ERR(info->clk); - } + info->clk = devm_clk_get_enabled(&pdev->dev, "dac"); + if (IS_ERR(info->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), + "Failed getting clock\n"); platform_set_drvdata(pdev, indio_dev); @@ -207,13 +203,6 @@ static int vf610_dac_probe(struct platform_device *pdev) mutex_init(&info->lock); - ret = clk_prepare_enable(info->clk); - if (ret) { - dev_err(&pdev->dev, - "Could not prepare or enable the clock\n"); - return ret; - } - vf610_dac_init(info); ret = iio_device_register(indio_dev); @@ -226,7 +215,6 @@ static int vf610_dac_probe(struct platform_device *pdev) error_iio_device_register: vf610_dac_exit(info); - clk_disable_unprepare(info->clk); return ret; } @@ -238,7 +226,6 @@ static void vf610_dac_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); vf610_dac_exit(info); - clk_disable_unprepare(info->clk); } static int vf610_dac_suspend(struct device *dev) diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index deb3c6459dde..781d3e96645f 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -21,8 +21,6 @@ #include <linux/regulator/consumer.h> #include "bmg160.h" -#define BMG160_IRQ_NAME "bmg160_event" - #define BMG160_REG_CHIP_ID 0x00 #define BMG160_CHIP_ID_VAL 0x0F @@ -1099,7 +1097,7 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, bmg160_data_rdy_trig_poll, bmg160_event_handler, IRQF_TRIGGER_RISING, - BMG160_IRQ_NAME, + "bmg160_event", indio_dev); if (ret) return ret; diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 1582cfc03579..30d3f984b032 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -405,7 +405,7 @@ static const struct regmap_config afe4403_regmap_config = { .val_bits = 24, .max_register = AFE440X_PDNCYCLEENDC, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_table = &afe4403_volatile_table, }; diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 99ff68aed27c..b2727effecaa 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -413,7 +413,7 @@ static const struct regmap_config afe4404_regmap_config = { .val_bits = 24, .max_register = AFE4404_AVG_LED1_ALED1VAL, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_table = &afe4404_volatile_table, }; diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 846664a4ee90..814f521e47ae 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -22,7 +22,6 @@ #include <linux/iio/buffer.h> #include <linux/iio/kfifo_buf.h> -#define MAX30100_REGMAP_NAME "max30100_regmap" #define MAX30100_DRV_NAME "max30100" #define MAX30100_REG_INT_STATUS 0x00 @@ -94,7 +93,7 @@ static bool max30100_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config max30100_regmap_config = { - .name = MAX30100_REGMAP_NAME, + .name = "max30100_regmap", .reg_bits = 8, .val_bits = 8, diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index f5f29d2fec57..a48c0881a4c7 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -25,7 +25,6 @@ #include <linux/iio/buffer.h> #include <linux/iio/kfifo_buf.h> -#define MAX30102_REGMAP_NAME "max30102_regmap" #define MAX30102_DRV_NAME "max30102" #define MAX30102_PART_NUMBER 0x15 @@ -112,7 +111,7 @@ struct max30102_data { }; static const struct regmap_config max30102_regmap_config = { - .name = MAX30102_REGMAP_NAME, + .name = "max30102_regmap", .reg_bits = 8, .val_bits = 8, diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index 48c59d09eea7..73d2033954e7 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -27,8 +27,6 @@ #include <linux/iio/iio.h> -#define DRIVER_NAME "dht11" - #define DHT11_DATA_VALID_TIME 2000000000 /* 2s in ns */ #define DHT11_EDGES_PREAMBLE 2 @@ -331,7 +329,7 @@ static int dht11_probe(struct platform_device *pdev) static struct platform_driver dht11_driver = { .driver = { - .name = DRIVER_NAME, + .name = "dht11", .of_match_table = dht11_dt_ids, }, .probe = dht11_probe, diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 90ed3f9bb39c..36323ad149e0 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -170,7 +170,7 @@ struct adis16400_chip_info { * that must be enabled together **/ struct adis16400_state { - struct adis16400_chip_info *variant; + const struct adis16400_chip_info *variant; int filt_int; struct adis adis; @@ -289,19 +289,6 @@ static void adis16400_debugfs_init(struct iio_dev *indio_dev) d, st, &adis16400_flash_count_fops); } -enum adis16400_chip_variant { - ADIS16300, - ADIS16334, - ADIS16350, - ADIS16360, - ADIS16362, - ADIS16364, - ADIS16367, - ADIS16400, - ADIS16445, - ADIS16448, -}; - static int adis16334_get_freq(struct adis16400_state *st) { int ret; @@ -984,137 +971,142 @@ static const struct adis_timeout adis16448_timeouts = { .self_test_ms = 45, }; -static struct adis16400_chip_info adis16400_chips[] = { - [ADIS16300] = { - .channels = adis16300_channels, - .num_channels = ARRAY_SIZE(adis16300_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | - ADIS16400_HAS_SERIAL_NUMBER, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ - .accel_scale_micro = 5884, - .temp_scale_nano = 140000000, /* 0.14 C */ - .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */ - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16300_timeouts, 18), - }, - [ADIS16334] = { - .channels = adis16334_channels, - .num_channels = ARRAY_SIZE(adis16334_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_NO_BURST | - ADIS16400_HAS_SERIAL_NUMBER, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */ - .temp_scale_nano = 67850000, /* 0.06785 C */ - .temp_offset = 25000000 / 67850, /* 25 C = 0x00 */ - .set_freq = adis16334_set_freq, - .get_freq = adis16334_get_freq, - .adis_data = ADIS16400_DATA(&adis16334_timeouts, 0), - }, - [ADIS16350] = { - .channels = adis16350_channels, - .num_channels = ARRAY_SIZE(adis16350_channels), - .gyro_scale_micro = IIO_DEGREE_TO_RAD(73260), /* 0.07326 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(2522), /* 0.002522 g */ - .temp_scale_nano = 145300000, /* 0.1453 C */ - .temp_offset = 25000000 / 145300, /* 25 C = 0x00 */ - .flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE, - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16300_timeouts, 0), - }, - [ADIS16360] = { - .channels = adis16350_channels, - .num_channels = ARRAY_SIZE(adis16350_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | - ADIS16400_HAS_SERIAL_NUMBER, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */ - .temp_scale_nano = 136000000, /* 0.136 C */ - .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16300_timeouts, 28), - }, - [ADIS16362] = { - .channels = adis16350_channels, - .num_channels = ARRAY_SIZE(adis16350_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | - ADIS16400_HAS_SERIAL_NUMBER, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(333), /* 0.333 mg */ - .temp_scale_nano = 136000000, /* 0.136 C */ - .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16362_timeouts, 28), - }, - [ADIS16364] = { - .channels = adis16350_channels, - .num_channels = ARRAY_SIZE(adis16350_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | - ADIS16400_HAS_SERIAL_NUMBER, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */ - .temp_scale_nano = 136000000, /* 0.136 C */ - .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16362_timeouts, 28), - }, - [ADIS16367] = { - .channels = adis16350_channels, - .num_channels = ARRAY_SIZE(adis16350_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | - ADIS16400_HAS_SERIAL_NUMBER, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(2000), /* 0.2 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */ - .temp_scale_nano = 136000000, /* 0.136 C */ - .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16300_timeouts, 28), - }, - [ADIS16400] = { - .channels = adis16400_channels, - .num_channels = ARRAY_SIZE(adis16400_channels), - .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */ - .temp_scale_nano = 140000000, /* 0.14 C */ - .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */ - .set_freq = adis16400_set_freq, - .get_freq = adis16400_get_freq, - .adis_data = ADIS16400_DATA(&adis16400_timeouts, 24), - }, - [ADIS16445] = { - .channels = adis16445_channels, - .num_channels = ARRAY_SIZE(adis16445_channels), - .flags = ADIS16400_HAS_PROD_ID | - ADIS16400_HAS_SERIAL_NUMBER | - ADIS16400_BURST_DIAG_STAT, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(250), /* 1/4000 g */ - .temp_scale_nano = 73860000, /* 0.07386 C */ - .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ - .set_freq = adis16334_set_freq, - .get_freq = adis16334_get_freq, - .adis_data = ADIS16400_DATA(&adis16445_timeouts, 16), - }, - [ADIS16448] = { - .channels = adis16448_channels, - .num_channels = ARRAY_SIZE(adis16448_channels), - .flags = ADIS16400_HAS_PROD_ID | - ADIS16400_HAS_SERIAL_NUMBER | - ADIS16400_BURST_DIAG_STAT, - .gyro_scale_micro = IIO_DEGREE_TO_RAD(40000), /* 0.04 deg/s */ - .accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */ - .temp_scale_nano = 73860000, /* 0.07386 C */ - .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ - .set_freq = adis16334_set_freq, - .get_freq = adis16334_get_freq, - .adis_data = ADIS16400_DATA(&adis16448_timeouts, 24), - } +static const struct adis16400_chip_info adis16300_chip_info = { + .channels = adis16300_channels, + .num_channels = ARRAY_SIZE(adis16300_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | + ADIS16400_HAS_SERIAL_NUMBER, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ + .accel_scale_micro = 5884, + .temp_scale_nano = 140000000, /* 0.14 C */ + .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */ + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16300_timeouts, 18), +}; + +static const struct adis16400_chip_info adis16334_chip_info = { + .channels = adis16334_channels, + .num_channels = ARRAY_SIZE(adis16334_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_NO_BURST | + ADIS16400_HAS_SERIAL_NUMBER, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */ + .temp_scale_nano = 67850000, /* 0.06785 C */ + .temp_offset = 25000000 / 67850, /* 25 C = 0x00 */ + .set_freq = adis16334_set_freq, + .get_freq = adis16334_get_freq, + .adis_data = ADIS16400_DATA(&adis16334_timeouts, 0), +}; + +static const struct adis16400_chip_info adis16350_chip_info = { + .channels = adis16350_channels, + .num_channels = ARRAY_SIZE(adis16350_channels), + .gyro_scale_micro = IIO_DEGREE_TO_RAD(73260), /* 0.07326 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(2522), /* 0.002522 g */ + .temp_scale_nano = 145300000, /* 0.1453 C */ + .temp_offset = 25000000 / 145300, /* 25 C = 0x00 */ + .flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE, + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16300_timeouts, 0), +}; + +static const struct adis16400_chip_info adis16360_chip_info = { + .channels = adis16350_channels, + .num_channels = ARRAY_SIZE(adis16350_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | + ADIS16400_HAS_SERIAL_NUMBER, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */ + .temp_scale_nano = 136000000, /* 0.136 C */ + .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16300_timeouts, 28), +}; + +static const struct adis16400_chip_info adis16362_chip_info = { + .channels = adis16350_channels, + .num_channels = ARRAY_SIZE(adis16350_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | + ADIS16400_HAS_SERIAL_NUMBER, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(333), /* 0.333 mg */ + .temp_scale_nano = 136000000, /* 0.136 C */ + .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16362_timeouts, 28), +}; + +static const struct adis16400_chip_info adis16364_chip_info = { + .channels = adis16350_channels, + .num_channels = ARRAY_SIZE(adis16350_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | + ADIS16400_HAS_SERIAL_NUMBER, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */ + .temp_scale_nano = 136000000, /* 0.136 C */ + .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16362_timeouts, 28), +}; + +static const struct adis16400_chip_info adis16367_chip_info = { + .channels = adis16350_channels, + .num_channels = ARRAY_SIZE(adis16350_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE | + ADIS16400_HAS_SERIAL_NUMBER, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(2000), /* 0.2 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */ + .temp_scale_nano = 136000000, /* 0.136 C */ + .temp_offset = 25000000 / 136000, /* 25 C = 0x00 */ + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16300_timeouts, 28), +}; + +static const struct adis16400_chip_info adis16400_chip_info = { + .channels = adis16400_channels, + .num_channels = ARRAY_SIZE(adis16400_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */ + .temp_scale_nano = 140000000, /* 0.14 C */ + .temp_offset = 25000000 / 140000, /* 25 C = 0x00 */ + .set_freq = adis16400_set_freq, + .get_freq = adis16400_get_freq, + .adis_data = ADIS16400_DATA(&adis16400_timeouts, 24), +}; + +static const struct adis16400_chip_info adis16445_chip_info = { + .channels = adis16445_channels, + .num_channels = ARRAY_SIZE(adis16445_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SERIAL_NUMBER | + ADIS16400_BURST_DIAG_STAT, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(250), /* 1/4000 g */ + .temp_scale_nano = 73860000, /* 0.07386 C */ + .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ + .set_freq = adis16334_set_freq, + .get_freq = adis16334_get_freq, + .adis_data = ADIS16400_DATA(&adis16445_timeouts, 16), +}; + +static const struct adis16400_chip_info adis16448_chip_info = { + .channels = adis16448_channels, + .num_channels = ARRAY_SIZE(adis16448_channels), + .flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SERIAL_NUMBER | + ADIS16400_BURST_DIAG_STAT, + .gyro_scale_micro = IIO_DEGREE_TO_RAD(40000), /* 0.04 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */ + .temp_scale_nano = 73860000, /* 0.07386 C */ + .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ + .set_freq = adis16334_set_freq, + .get_freq = adis16334_get_freq, + .adis_data = ADIS16400_DATA(&adis16448_timeouts, 24), }; static const struct iio_info adis16400_info = { @@ -1157,7 +1149,7 @@ static int adis16400_probe(struct spi_device *spi) st = iio_priv(indio_dev); /* setup the industrialio driver allocated elements */ - st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data]; + st->variant = spi_get_device_match_data(spi); indio_dev->name = spi_get_device_id(spi)->name; indio_dev->channels = st->variant->channels; indio_dev->num_channels = st->variant->num_channels; @@ -1197,21 +1189,21 @@ static int adis16400_probe(struct spi_device *spi) } static const struct spi_device_id adis16400_id[] = { - {"adis16300", ADIS16300}, - {"adis16305", ADIS16300}, - {"adis16334", ADIS16334}, - {"adis16350", ADIS16350}, - {"adis16354", ADIS16350}, - {"adis16355", ADIS16350}, - {"adis16360", ADIS16360}, - {"adis16362", ADIS16362}, - {"adis16364", ADIS16364}, - {"adis16365", ADIS16360}, - {"adis16367", ADIS16367}, - {"adis16400", ADIS16400}, - {"adis16405", ADIS16400}, - {"adis16445", ADIS16445}, - {"adis16448", ADIS16448}, + { "adis16300", (kernel_ulong_t)&adis16300_chip_info }, + { "adis16305", (kernel_ulong_t)&adis16300_chip_info }, + { "adis16334", (kernel_ulong_t)&adis16334_chip_info }, + { "adis16350", (kernel_ulong_t)&adis16350_chip_info }, + { "adis16354", (kernel_ulong_t)&adis16350_chip_info }, + { "adis16355", (kernel_ulong_t)&adis16350_chip_info }, + { "adis16360", (kernel_ulong_t)&adis16360_chip_info }, + { "adis16362", (kernel_ulong_t)&adis16362_chip_info }, + { "adis16364", (kernel_ulong_t)&adis16364_chip_info }, + { "adis16365", (kernel_ulong_t)&adis16360_chip_info }, + { "adis16367", (kernel_ulong_t)&adis16367_chip_info }, + { "adis16400", (kernel_ulong_t)&adis16400_chip_info }, + { "adis16405", (kernel_ulong_t)&adis16400_chip_info }, + { "adis16445", (kernel_ulong_t)&adis16445_chip_info }, + { "adis16448", (kernel_ulong_t)&adis16448_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, adis16400_id); diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h index 32c2ea2d7112..ffbe8205e703 100644 --- a/drivers/iio/imu/bmi160/bmi160.h +++ b/drivers/iio/imu/bmi160/bmi160.h @@ -28,4 +28,6 @@ int bmi160_enable_irq(struct regmap *regmap, bool enable); int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type); +extern const struct dev_pm_ops bmi160_core_pm_ops; + #endif /* BMI160_H_ */ diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 0423ef6f9571..5f47708b4c5d 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -161,7 +161,7 @@ struct bmi160_regs { u8 pmu_cmd_suspend; }; -static struct bmi160_regs bmi160_regs[] = { +static const struct bmi160_regs bmi160_regs[] = { [BMI160_ACCEL] = { .data = BMI160_REG_DATA_ACCEL_XOUT_L, .config = BMI160_REG_ACCEL_CONFIG, @@ -890,6 +890,25 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap, } EXPORT_SYMBOL_NS_GPL(bmi160_core_probe, "IIO_BMI160"); +static int bmi160_core_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return iio_device_suspend_triggering(indio_dev); +} + +static int bmi160_core_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return iio_device_resume_triggering(indio_dev); +} + +const struct dev_pm_ops bmi160_core_pm_ops = { + RUNTIME_PM_OPS(bmi160_core_runtime_suspend, bmi160_core_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(bmi160_core_pm_ops, "IIO_BMI160"); + MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); MODULE_DESCRIPTION("Bosch BMI160 driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c index 9fa3a19a8977..3e2758f4e0d3 100644 --- a/drivers/iio/imu/bmi160/bmi160_i2c.c +++ b/drivers/iio/imu/bmi160/bmi160_i2c.c @@ -11,6 +11,7 @@ #include <linux/i2c.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pm.h> #include <linux/regmap.h> #include "bmi160.h" @@ -69,6 +70,7 @@ MODULE_DEVICE_TABLE(of, bmi160_of_match); static struct i2c_driver bmi160_i2c_driver = { .driver = { .name = "bmi160_i2c", + .pm = pm_ptr(&bmi160_core_pm_ops), .acpi_match_table = bmi160_acpi_match, .of_match_table = bmi160_of_match, }, diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c index ebb586904215..3581bd788483 100644 --- a/drivers/iio/imu/bmi160/bmi160_spi.c +++ b/drivers/iio/imu/bmi160/bmi160_spi.c @@ -7,6 +7,7 @@ */ #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pm.h> #include <linux/regmap.h> #include <linux/spi/spi.h> @@ -61,6 +62,7 @@ static struct spi_driver bmi160_spi_driver = { .acpi_match_table = bmi160_acpi_match, .of_match_table = bmi160_of_match, .name = "bmi160_spi", + .pm = pm_ptr(&bmi160_core_pm_ops), }, }; module_spi_driver(bmi160_spi_driver); diff --git a/drivers/iio/imu/bmi270/bmi270.h b/drivers/iio/imu/bmi270/bmi270.h index d94525f6aee8..a6c4204032fc 100644 --- a/drivers/iio/imu/bmi270/bmi270.h +++ b/drivers/iio/imu/bmi270/bmi270.h @@ -20,4 +20,6 @@ struct device; int bmi270_core_probe(struct device *dev, struct regmap *regmap, const struct bmi270_chip_info *chip_info); +extern const struct dev_pm_ops bmi270_core_pm_ops; + #endif /* BMI270_H_ */ diff --git a/drivers/iio/imu/bmi270/bmi270_core.c b/drivers/iio/imu/bmi270/bmi270_core.c index 2e4469f30d53..519f1c9d466d 100644 --- a/drivers/iio/imu/bmi270/bmi270_core.c +++ b/drivers/iio/imu/bmi270/bmi270_core.c @@ -8,6 +8,7 @@ #include <linux/regmap.h> #include <linux/units.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/trigger.h> @@ -28,9 +29,14 @@ #define BMI270_ACCEL_X_REG 0x0c #define BMI270_ANG_VEL_X_REG 0x12 +#define BMI270_INT_STATUS_0_REG 0x1c +#define BMI270_INT_STATUS_0_STEP_CNT_MSK BIT(1) + #define BMI270_INT_STATUS_1_REG 0x1d #define BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK GENMASK(7, 6) +#define BMI270_SC_OUT_0_REG 0x1e + #define BMI270_INTERNAL_STATUS_REG 0x21 #define BMI270_INTERNAL_STATUS_MSG_MSK GENMASK(3, 0) #define BMI270_INTERNAL_STATUS_MSG_INIT_OK 0x01 @@ -39,6 +45,8 @@ #define BMI270_TEMPERATURE_0_REG 0x22 +#define BMI270_FEAT_PAGE_REG 0x2f + #define BMI270_ACC_CONF_REG 0x40 #define BMI270_ACC_CONF_ODR_MSK GENMASK(3, 0) #define BMI270_ACC_CONF_ODR_100HZ 0x08 @@ -70,6 +78,10 @@ #define BMI270_INT_LATCH_REG 0x55 #define BMI270_INT_LATCH_REG_MSK BIT(0) +#define BMI270_INT1_MAP_FEAT_REG 0x56 +#define BMI270_INT2_MAP_FEAT_REG 0x57 +#define BMI270_INT_MAP_FEAT_STEP_CNT_WTRMRK_MSK BIT(1) + #define BMI270_INT_MAP_DATA_REG 0x58 #define BMI270_INT_MAP_DATA_DRDY_INT1_MSK BIT(2) #define BMI270_INT_MAP_DATA_DRDY_INT2_MSK BIT(6) @@ -90,10 +102,18 @@ #define BMI270_PWR_CTRL_ACCEL_EN_MSK BIT(2) #define BMI270_PWR_CTRL_TEMP_EN_MSK BIT(3) +#define BMI270_STEP_SC26_WTRMRK_MSK GENMASK(9, 0) +#define BMI270_STEP_SC26_RST_CNT_MSK BIT(10) +#define BMI270_STEP_SC26_EN_CNT_MSK BIT(12) + /* See datasheet section 4.6.14, Temperature Sensor */ #define BMI270_TEMP_OFFSET 11776 #define BMI270_TEMP_SCALE 1953125 +/* See page 90 of datasheet. The step counter "holds implicitly a 20x factor" */ +#define BMI270_STEP_COUNTER_FACTOR 20 +#define BMI270_STEP_COUNTER_MAX 20460 + #define BMI260_INIT_DATA_FILE "bmi260-init-data.fw" #define BMI270_INIT_DATA_FILE "bmi270-init-data.fw" @@ -111,6 +131,7 @@ struct bmi270_data { struct iio_trigger *trig; /* Protect device's private data from concurrent access */ struct mutex mutex; + bool steps_enabled; /* * Where IIO_DMA_MINALIGN may be larger than 8 bytes, align to @@ -120,6 +141,11 @@ struct bmi270_data { __le16 channels[6]; aligned_s64 timestamp; } buffer __aligned(IIO_DMA_MINALIGN); + /* + * Variable to access feature registers. It can be accessed concurrently + * with the 'buffer' variable + */ + __le16 regval __aligned(IIO_DMA_MINALIGN); }; enum bmi270_scan { @@ -282,6 +308,137 @@ static const struct bmi270_odr_item bmi270_odr_table[] = { }, }; +enum bmi270_feature_reg_id { + BMI270_SC_26_REG, +}; + +struct bmi270_feature_reg { + u8 page; + u8 addr; +}; + +static const struct bmi270_feature_reg bmi270_feature_regs[] = { + [BMI270_SC_26_REG] = { + .page = 6, + .addr = 0x32, + }, +}; + +static int bmi270_write_feature_reg(struct bmi270_data *data, + enum bmi270_feature_reg_id id, + u16 val) +{ + const struct bmi270_feature_reg *reg = &bmi270_feature_regs[id]; + int ret; + + ret = regmap_write(data->regmap, BMI270_FEAT_PAGE_REG, reg->page); + if (ret) + return ret; + + data->regval = cpu_to_le16(val); + return regmap_bulk_write(data->regmap, reg->addr, &data->regval, + sizeof(data->regval)); +} + +static int bmi270_read_feature_reg(struct bmi270_data *data, + enum bmi270_feature_reg_id id, + u16 *val) +{ + const struct bmi270_feature_reg *reg = &bmi270_feature_regs[id]; + int ret; + + ret = regmap_write(data->regmap, BMI270_FEAT_PAGE_REG, reg->page); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, reg->addr, &data->regval, + sizeof(data->regval)); + if (ret) + return ret; + + *val = le16_to_cpu(data->regval); + return 0; +} + +static int bmi270_update_feature_reg(struct bmi270_data *data, + enum bmi270_feature_reg_id id, + u16 mask, u16 val) +{ + u16 regval; + int ret; + + ret = bmi270_read_feature_reg(data, id, ®val); + if (ret) + return ret; + + regval = (regval & ~mask) | (val & mask); + + return bmi270_write_feature_reg(data, id, regval); +} + +static int bmi270_enable_steps(struct bmi270_data *data, int val) +{ + int ret; + + guard(mutex)(&data->mutex); + if (data->steps_enabled) + return 0; + + ret = bmi270_update_feature_reg(data, BMI270_SC_26_REG, + BMI270_STEP_SC26_EN_CNT_MSK, + FIELD_PREP(BMI270_STEP_SC26_EN_CNT_MSK, + val ? 1 : 0)); + if (ret) + return ret; + + data->steps_enabled = true; + return 0; +} + +static int bmi270_read_steps(struct bmi270_data *data, int *val) +{ + __le16 steps_count; + int ret; + + ret = regmap_bulk_read(data->regmap, BMI270_SC_OUT_0_REG, &steps_count, + sizeof(steps_count)); + if (ret) + return ret; + + *val = sign_extend32(le16_to_cpu(steps_count), 15); + return IIO_VAL_INT; +} + +static int bmi270_int_map_reg(enum bmi270_irq_pin pin) +{ + switch (pin) { + case BMI270_IRQ_INT1: + return BMI270_INT1_MAP_FEAT_REG; + case BMI270_IRQ_INT2: + return BMI270_INT2_MAP_FEAT_REG; + default: + return -EINVAL; + } +} + +static int bmi270_step_wtrmrk_en(struct bmi270_data *data, bool state) +{ + int reg; + + guard(mutex)(&data->mutex); + if (!data->steps_enabled) + return -EINVAL; + + reg = bmi270_int_map_reg(data->irq_pin); + if (reg < 0) + return reg; + + return regmap_update_bits(data->regmap, reg, + BMI270_INT_MAP_FEAT_STEP_CNT_WTRMRK_MSK, + FIELD_PREP(BMI270_INT_MAP_FEAT_STEP_CNT_WTRMRK_MSK, + state)); +} + static int bmi270_set_scale(struct bmi270_data *data, int chan_type, int uscale) { int i; @@ -438,19 +595,31 @@ static irqreturn_t bmi270_irq_thread_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct bmi270_data *data = iio_priv(indio_dev); - unsigned int status; + unsigned int status0, status1; + s64 timestamp = iio_get_time_ns(indio_dev); int ret; scoped_guard(mutex, &data->mutex) { + ret = regmap_read(data->regmap, BMI270_INT_STATUS_0_REG, + &status0); + if (ret) + return IRQ_NONE; + ret = regmap_read(data->regmap, BMI270_INT_STATUS_1_REG, - &status); + &status1); if (ret) return IRQ_NONE; } - if (FIELD_GET(BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK, status)) + if (FIELD_GET(BMI270_INT_STATUS_1_ACC_GYR_DRDY_MSK, status1)) iio_trigger_poll_nested(data->trig); + if (FIELD_GET(BMI270_INT_STATUS_0_STEP_CNT_MSK, status0)) + iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_STEPS, 0, + IIO_EV_TYPE_CHANGE, + IIO_EV_DIR_NONE), + timestamp); + return IRQ_HANDLED; } @@ -551,6 +720,8 @@ static int bmi270_read_raw(struct iio_dev *indio_dev, struct bmi270_data *data = iio_priv(indio_dev); switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + return bmi270_read_steps(data, val); case IIO_CHAN_INFO_RAW: if (!iio_device_claim_direct(indio_dev)) return -EBUSY; @@ -571,6 +742,9 @@ static int bmi270_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: ret = bmi270_get_odr(data, chan->type, val, val2); return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_ENABLE: + *val = data->steps_enabled ? 1 : 0; + return IIO_VAL_INT; default: return -EINVAL; } @@ -596,6 +770,19 @@ static int bmi270_write_raw(struct iio_dev *indio_dev, ret = bmi270_set_odr(data, chan->type, val, val2); iio_device_release_direct(indio_dev); return ret; + case IIO_CHAN_INFO_ENABLE: + return bmi270_enable_steps(data, val); + case IIO_CHAN_INFO_PROCESSED: { + if (val || !data->steps_enabled) + return -EINVAL; + + guard(mutex)(&data->mutex); + /* Clear step counter value */ + return bmi270_update_feature_reg(data, BMI270_SC_26_REG, + BMI270_STEP_SC26_RST_CNT_MSK, + FIELD_PREP(BMI270_STEP_SC26_RST_CNT_MSK, + 1)); + } default: return -EINVAL; } @@ -640,10 +827,116 @@ static int bmi270_read_avail(struct iio_dev *indio_dev, } } +static int bmi270_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, bool state) +{ + struct bmi270_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_CHANGE: + return bmi270_step_wtrmrk_en(data, state); + default: + return -EINVAL; + } +} + +static int bmi270_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 bmi270_data *data = iio_priv(indio_dev); + int ret, reg, regval; + + guard(mutex)(&data->mutex); + + switch (chan->type) { + case IIO_STEPS: + reg = bmi270_int_map_reg(data->irq_pin); + if (reg) + return reg; + + ret = regmap_read(data->regmap, reg, ®val); + if (ret) + return ret; + return FIELD_GET(BMI270_INT_MAP_FEAT_STEP_CNT_WTRMRK_MSK, + regval) ? 1 : 0; + default: + return -EINVAL; + } +} + +static int bmi270_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 bmi270_data *data = iio_priv(indio_dev); + unsigned int raw; + + guard(mutex)(&data->mutex); + + switch (type) { + case IIO_EV_TYPE_CHANGE: + if (!in_range(val, 0, BMI270_STEP_COUNTER_MAX + 1)) + return -EINVAL; + + raw = val / BMI270_STEP_COUNTER_FACTOR; + return bmi270_update_feature_reg(data, BMI270_SC_26_REG, + BMI270_STEP_SC26_WTRMRK_MSK, + FIELD_PREP(BMI270_STEP_SC26_WTRMRK_MSK, + raw)); + default: + return -EINVAL; + } +} + +static int bmi270_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 bmi270_data *data = iio_priv(indio_dev); + unsigned int raw; + u16 regval; + int ret; + + guard(mutex)(&data->mutex); + + switch (type) { + case IIO_EV_TYPE_CHANGE: + ret = bmi270_read_feature_reg(data, BMI270_SC_26_REG, ®val); + if (ret) + return ret; + + raw = FIELD_GET(BMI270_STEP_SC26_WTRMRK_MSK, regval); + *val = raw * BMI270_STEP_COUNTER_FACTOR; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_event_spec bmi270_step_wtrmrk_event = { + .type = IIO_EV_TYPE_CHANGE, + .dir = IIO_EV_DIR_NONE, + .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), +}; + static const struct iio_info bmi270_info = { .read_raw = bmi270_read_raw, .write_raw = bmi270_write_raw, .read_avail = bmi270_read_avail, + .write_event_config = bmi270_write_event_config, + .read_event_config = bmi270_read_event_config, + .write_event_value = bmi270_write_event_value, + .read_event_value = bmi270_read_event_value, }; #define BMI270_ACCEL_CHANNEL(_axis) { \ @@ -698,6 +991,14 @@ static const struct iio_chan_spec bmi270_channels[] = { BIT(IIO_CHAN_INFO_OFFSET), .scan_index = -1, /* No buffer support */ }, + { + .type = IIO_STEPS, + .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ + .event_spec = &bmi270_step_wtrmrk_event, + .num_event_specs = 1, + }, IIO_CHAN_SOFT_TIMESTAMP(BMI270_SCAN_TIMESTAMP), }; @@ -982,6 +1283,7 @@ int bmi270_core_probe(struct device *dev, struct regmap *regmap, indio_dev->available_scan_masks = bmi270_avail_scan_masks; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &bmi270_info; + dev_set_drvdata(data->dev, indio_dev); ret = bmi270_trigger_probe(data, indio_dev); if (ret) @@ -997,6 +1299,25 @@ int bmi270_core_probe(struct device *dev, struct regmap *regmap, } EXPORT_SYMBOL_NS_GPL(bmi270_core_probe, "IIO_BMI270"); +static int bmi270_core_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return iio_device_suspend_triggering(indio_dev); +} + +static int bmi270_core_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return iio_device_resume_triggering(indio_dev); +} + +const struct dev_pm_ops bmi270_core_pm_ops = { + RUNTIME_PM_OPS(bmi270_core_runtime_suspend, bmi270_core_runtime_resume, NULL) +}; +EXPORT_SYMBOL_NS_GPL(bmi270_core_pm_ops, "IIO_BMI270"); + MODULE_AUTHOR("Alex Lanzano"); MODULE_DESCRIPTION("BMI270 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bmi270/bmi270_i2c.c b/drivers/iio/imu/bmi270/bmi270_i2c.c index 44699ab58909..c77839b03a96 100644 --- a/drivers/iio/imu/bmi270/bmi270_i2c.c +++ b/drivers/iio/imu/bmi270/bmi270_i2c.c @@ -4,6 +4,7 @@ #include <linux/iio/iio.h> #include <linux/module.h> #include <linux/mod_devicetable.h> +#include <linux/pm.h> #include <linux/regmap.h> #include "bmi270.h" @@ -52,6 +53,7 @@ static const struct of_device_id bmi270_of_match[] = { static struct i2c_driver bmi270_i2c_driver = { .driver = { .name = "bmi270_i2c", + .pm = pm_ptr(&bmi270_core_pm_ops), .acpi_match_table = bmi270_acpi_match, .of_match_table = bmi270_of_match, }, diff --git a/drivers/iio/imu/bmi270/bmi270_spi.c b/drivers/iio/imu/bmi270/bmi270_spi.c index 88a77aba5e4f..19dd7734f9d0 100644 --- a/drivers/iio/imu/bmi270/bmi270_spi.c +++ b/drivers/iio/imu/bmi270/bmi270_spi.c @@ -3,6 +3,7 @@ #include <linux/iio/iio.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/pm.h> #include <linux/regmap.h> #include <linux/spi/spi.h> @@ -79,6 +80,7 @@ static const struct of_device_id bmi270_of_match[] = { static struct spi_driver bmi270_spi_driver = { .driver = { .name = "bmi270", + .pm = pm_ptr(&bmi270_core_pm_ops), .of_match_table = bmi270_of_match, }, .probe = bmi270_spi_probe, diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c index 597c402b98de..a8e71679ec21 100644 --- a/drivers/iio/imu/bno055/bno055.c +++ b/drivers/iio/imu/bno055/bno055.c @@ -114,34 +114,35 @@ #define BNO055_UID_LEN 16 struct bno055_sysfs_attr { - int *vals; + const int *vals; int len; - int *fusion_vals; - int *hw_xlate; + const int *fusion_vals; + const int *hw_xlate; + int hw_xlate_len; int type; }; -static int bno055_acc_lpf_vals[] = { +static const int bno055_acc_lpf_vals[] = { 7, 810000, 15, 630000, 31, 250000, 62, 500000, 125, 0, 250, 0, 500, 0, 1000, 0, }; -static struct bno055_sysfs_attr bno055_acc_lpf = { +static const struct bno055_sysfs_attr bno055_acc_lpf = { .vals = bno055_acc_lpf_vals, .len = ARRAY_SIZE(bno055_acc_lpf_vals), - .fusion_vals = (int[]){62, 500000}, + .fusion_vals = (const int[]){62, 500000}, .type = IIO_VAL_INT_PLUS_MICRO, }; -static int bno055_acc_range_vals[] = { +static const int bno055_acc_range_vals[] = { /* G: 2, 4, 8, 16 */ 1962, 3924, 7848, 15696 }; -static struct bno055_sysfs_attr bno055_acc_range = { +static const struct bno055_sysfs_attr bno055_acc_range = { .vals = bno055_acc_range_vals, .len = ARRAY_SIZE(bno055_acc_range_vals), - .fusion_vals = (int[]){3924}, /* 4G */ + .fusion_vals = (const int[]){3924}, /* 4G */ .type = IIO_VAL_INT, }; @@ -165,33 +166,37 @@ static struct bno055_sysfs_attr bno055_acc_range = { * = hwval * (dps_range/(2^15 * k)) * where k is rad-to-deg factor */ -static int bno055_gyr_scale_vals[] = { +static const int bno055_gyr_scale_vals[] = { 125, 1877467, 250, 1877467, 500, 1877467, 1000, 1877467, 2000, 1877467, }; -static struct bno055_sysfs_attr bno055_gyr_scale = { +static const int bno055_gyr_scale_hw_xlate[] = {0, 1, 2, 3, 4}; +static const struct bno055_sysfs_attr bno055_gyr_scale = { .vals = bno055_gyr_scale_vals, .len = ARRAY_SIZE(bno055_gyr_scale_vals), - .fusion_vals = (int[]){1, 900}, - .hw_xlate = (int[]){4, 3, 2, 1, 0}, + .fusion_vals = (const int[]){1, 900}, + .hw_xlate = bno055_gyr_scale_hw_xlate, + .hw_xlate_len = ARRAY_SIZE(bno055_gyr_scale_hw_xlate), .type = IIO_VAL_FRACTIONAL, }; -static int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523}; -static struct bno055_sysfs_attr bno055_gyr_lpf = { +static const int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523}; +static const int bno055_gyr_lpf_hw_xlate[] = {5, 4, 7, 3, 6, 2, 1, 0}; +static const struct bno055_sysfs_attr bno055_gyr_lpf = { .vals = bno055_gyr_lpf_vals, .len = ARRAY_SIZE(bno055_gyr_lpf_vals), - .fusion_vals = (int[]){32}, - .hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0}, + .fusion_vals = (const int[]){32}, + .hw_xlate = bno055_gyr_lpf_hw_xlate, + .hw_xlate_len = ARRAY_SIZE(bno055_gyr_lpf_hw_xlate), .type = IIO_VAL_INT, }; -static int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30}; -static struct bno055_sysfs_attr bno055_mag_odr = { +static const int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30}; +static const struct bno055_sysfs_attr bno055_mag_odr = { .vals = bno055_mag_odr_vals, .len = ARRAY_SIZE(bno055_mag_odr_vals), - .fusion_vals = (int[]){20}, + .fusion_vals = (const int[]){20}, .type = IIO_VAL_INT, }; @@ -290,7 +295,7 @@ const struct regmap_config bno055_regmap_config = { .max_register = 0x80 * 2, .writeable_reg = bno055_regmap_writeable, .readable_reg = bno055_regmap_readable, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(bno055_regmap_config, "IIO_BNO055"); @@ -548,7 +553,8 @@ static const struct iio_chan_spec bno055_channels[] = { }; static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2, - int reg, int mask, struct bno055_sysfs_attr *attr) + int reg, int mask, + const struct bno055_sysfs_attr *attr) { const int shift = __ffs(mask); int hwval, idx; @@ -561,7 +567,7 @@ static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2, idx = (hwval & mask) >> shift; if (attr->hw_xlate) - for (i = 0; i < attr->len; i++) + for (i = 0; i < attr->hw_xlate_len; i++) if (attr->hw_xlate[i] == idx) { idx = i; break; @@ -577,7 +583,8 @@ static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2, } static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2, - int reg, int mask, struct bno055_sysfs_attr *attr) + int reg, int mask, + const struct bno055_sysfs_attr *attr) { const int shift = __ffs(mask); int best_delta; @@ -758,7 +765,8 @@ static int bno055_read_simple_chan(struct iio_dev *indio_dev, } } -static int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr, +static int bno055_sysfs_attr_avail(struct bno055_priv *priv, + const struct bno055_sysfs_attr *attr, const int **vals, int *length) { if (priv->operation_mode != BNO055_OPR_MODE_AMG) { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index f893dbe69965..1430ab4f1dea 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -135,6 +135,14 @@ struct inv_icm42600_suspended { bool temp; }; +struct inv_icm42600_apex { + unsigned int on; + struct { + u64 value; + bool enable; + } wom; +}; + /** * struct inv_icm42600_state - driver state variables * @lock: lock for serializing multiple registers access. @@ -143,14 +151,16 @@ struct inv_icm42600_suspended { * @map: regmap pointer. * @vdd_supply: VDD voltage regulator for the chip. * @vddio_supply: I/O voltage regulator for the chip. + * @irq: chip irq, required to enable/disable and set wakeup * @orientation: sensor chip orientation relative to main hardware. * @conf: chip sensors configurations. * @suspended: suspended sensors configuration. * @indio_gyro: gyroscope IIO device. * @indio_accel: accelerometer IIO device. - * @buffer: data transfer buffer aligned for DMA. - * @fifo: FIFO management structure. * @timestamp: interrupt timestamps. + * @apex: APEX (Advanced Pedometer and Event detection) management + * @fifo: FIFO management structure. + * @buffer: data transfer buffer aligned for DMA. */ struct inv_icm42600_state { struct mutex lock; @@ -159,17 +169,19 @@ struct inv_icm42600_state { struct regmap *map; struct regulator *vdd_supply; struct regulator *vddio_supply; + int irq; struct iio_mount_matrix orientation; struct inv_icm42600_conf conf; struct inv_icm42600_suspended suspended; struct iio_dev *indio_gyro; struct iio_dev *indio_accel; - uint8_t buffer[2] __aligned(IIO_DMA_MINALIGN); - struct inv_icm42600_fifo fifo; struct { - int64_t gyro; - int64_t accel; + s64 gyro; + s64 accel; } timestamp; + struct inv_icm42600_apex apex; + struct inv_icm42600_fifo fifo; + u8 buffer[3] __aligned(IIO_DMA_MINALIGN); }; @@ -253,6 +265,18 @@ struct inv_icm42600_sensor_state { #define INV_ICM42600_REG_FIFO_COUNT 0x002E #define INV_ICM42600_REG_FIFO_DATA 0x0030 +#define INV_ICM42600_REG_INT_STATUS2 0x0037 +#define INV_ICM42600_INT_STATUS2_SMD_INT BIT(3) +#define INV_ICM42600_INT_STATUS2_WOM_INT GENMASK(2, 0) + +#define INV_ICM42600_REG_INT_STATUS3 0x0038 +#define INV_ICM42600_INT_STATUS3_STEP_DET_INT BIT(5) +#define INV_ICM42600_INT_STATUS3_STEP_CNT_OVF_INT BIT(4) +#define INV_ICM42600_INT_STATUS3_TILT_DET_INT BIT(3) +#define INV_ICM42600_INT_STATUS3_WAKE_INT BIT(2) +#define INV_ICM42600_INT_STATUS3_SLEEP_INT BIT(1) +#define INV_ICM42600_INT_STATUS3_TAP_DET_INT BIT(0) + #define INV_ICM42600_REG_SIGNAL_PATH_RESET 0x004B #define INV_ICM42600_SIGNAL_PATH_RESET_DMP_INIT_EN BIT(6) #define INV_ICM42600_SIGNAL_PATH_RESET_DMP_MEM_RESET BIT(5) @@ -309,6 +333,14 @@ struct inv_icm42600_sensor_state { #define INV_ICM42600_TMST_CONFIG_TMST_FSYNC_EN BIT(1) #define INV_ICM42600_TMST_CONFIG_TMST_EN BIT(0) +#define INV_ICM42600_REG_SMD_CONFIG 0x0057 +#define INV_ICM42600_SMD_CONFIG_WOM_INT_MODE BIT(3) +#define INV_ICM42600_SMD_CONFIG_WOM_MODE BIT(2) +#define INV_ICM42600_SMD_CONFIG_SMD_MODE_OFF 0x00 +#define INV_ICM42600_SMD_CONFIG_SMD_MODE_WOM 0x01 +#define INV_ICM42600_SMD_CONFIG_SMD_MODE_SHORT 0x02 +#define INV_ICM42600_SMD_CONFIG_SMD_MODE_LONG 0x03 + #define INV_ICM42600_REG_FIFO_CONFIG1 0x005F #define INV_ICM42600_FIFO_CONFIG1_RESUME_PARTIAL_RD BIT(6) #define INV_ICM42600_FIFO_CONFIG1_WM_GT_TH BIT(5) @@ -338,6 +370,11 @@ struct inv_icm42600_sensor_state { #define INV_ICM42600_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1) #define INV_ICM42600_INT_SOURCE0_UI_AGC_RDY_INT1_EN BIT(0) +#define INV_ICM42600_REG_INT_SOURCE1 0x0066 +#define INV_ICM42600_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6) +#define INV_ICM42600_INT_SOURCE1_SMD_INT1_EN BIT(3) +#define INV_ICM42600_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0) + #define INV_ICM42600_REG_WHOAMI 0x0075 #define INV_ICM42600_WHOAMI_ICM42600 0x40 #define INV_ICM42600_WHOAMI_ICM42602 0x41 @@ -373,6 +410,10 @@ struct inv_icm42600_sensor_state { #define INV_ICM42600_INTF_CONFIG6_I3C_SDR_EN BIT(0) /* User bank 4 (MSB 0x40) */ +#define INV_ICM42600_REG_ACCEL_WOM_X_THR 0x404A +#define INV_ICM42600_REG_ACCEL_WOM_Y_THR 0x404B +#define INV_ICM42600_REG_ACCEL_WOM_Z_THR 0x404C + #define INV_ICM42600_REG_INT_SOURCE8 0x404F #define INV_ICM42600_INT_SOURCE8_FSYNC_IBI_EN BIT(5) #define INV_ICM42600_INT_SOURCE8_PLL_RDY_IBI_EN BIT(4) @@ -410,7 +451,7 @@ const struct iio_mount_matrix * inv_icm42600_get_mount_matrix(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan); -uint32_t inv_icm42600_odr_to_period(enum inv_icm42600_odr odr); +u32 inv_icm42600_odr_to_period(enum inv_icm42600_odr odr); int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st, struct inv_icm42600_sensor_conf *conf, @@ -423,6 +464,9 @@ int inv_icm42600_set_gyro_conf(struct inv_icm42600_state *st, int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable, unsigned int *sleep_ms); +int inv_icm42600_enable_wom(struct inv_icm42600_state *st); +int inv_icm42600_disable_wom(struct inv_icm42600_state *st); + int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval); @@ -437,4 +481,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st); int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev); +void inv_icm42600_accel_handle_events(struct iio_dev *indio_dev, + unsigned int status2, unsigned int status3, + s64 timestamp); + #endif diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index e6cd9dcb0687..7a28051330b7 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -10,9 +10,12 @@ #include <linux/regmap.h> #include <linux/delay.h> #include <linux/math64.h> +#include <linux/minmax.h> +#include <linux/units.h> #include <linux/iio/buffer.h> #include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> @@ -47,6 +50,16 @@ .ext_info = _ext_info, \ } +#define INV_ICM42600_ACCEL_EVENT_CHAN(_modifier, _events, _events_nb) \ + { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = _modifier, \ + .event_spec = _events, \ + .num_event_specs = _events_nb, \ + .scan_index = -1, \ + } + enum inv_icm42600_accel_scan { INV_ICM42600_ACCEL_SCAN_X, INV_ICM42600_ACCEL_SCAN_Y, @@ -82,14 +95,15 @@ static int inv_icm42600_accel_power_mode_set(struct iio_dev *indio_dev, if (idx >= ARRAY_SIZE(inv_icm42600_accel_power_mode_values)) return -EINVAL; - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - power_mode = inv_icm42600_accel_power_mode_values[idx]; filter = inv_icm42600_accel_filter_values[idx]; guard(mutex)(&st->lock); + /* cannot change if accel sensor is on */ + if (st->conf.accel.mode != INV_ICM42600_SENSOR_MODE_OFF) + return -EBUSY; + /* prevent change if power mode is not supported by the ODR */ switch (power_mode) { case INV_ICM42600_SENSOR_MODE_LOW_NOISE: @@ -160,6 +174,16 @@ static const struct iio_chan_spec_ext_info inv_icm42600_accel_ext_infos[] = { { } }; +/* WoM event: rising ROC */ +static const struct iio_event_spec inv_icm42600_wom_events[] = { + { + .type = IIO_EV_TYPE_ROC, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE), + }, +}; + static const struct iio_chan_spec inv_icm42600_accel_channels[] = { INV_ICM42600_ACCEL_CHAN(IIO_MOD_X, INV_ICM42600_ACCEL_SCAN_X, inv_icm42600_accel_ext_infos), @@ -169,6 +193,8 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = { inv_icm42600_accel_ext_infos), INV_ICM42600_TEMP_CHAN(INV_ICM42600_ACCEL_SCAN_TEMP), IIO_CHAN_SOFT_TIMESTAMP(INV_ICM42600_ACCEL_SCAN_TIMESTAMP), + INV_ICM42600_ACCEL_EVENT_CHAN(IIO_MOD_X_OR_Y_OR_Z, inv_icm42600_wom_events, + ARRAY_SIZE(inv_icm42600_wom_events)), }; /* @@ -177,7 +203,7 @@ static const struct iio_chan_spec inv_icm42600_accel_channels[] = { */ struct inv_icm42600_accel_buffer { struct inv_icm42600_fifo_sensor_data accel; - int16_t temp; + s16 temp; aligned_s64 timestamp; }; @@ -241,7 +267,7 @@ out_unlock: static int inv_icm42600_accel_read_sensor(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, - int16_t *val) + s16 *val) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); @@ -284,7 +310,7 @@ static int inv_icm42600_accel_read_sensor(struct iio_dev *indio_dev, if (ret) goto exit; - *val = (int16_t)be16_to_cpup(data); + *val = (s16)be16_to_cpup(data); if (*val == INV_ICM42600_DATA_INVALID) ret = -EINVAL; exit: @@ -294,6 +320,180 @@ exit: return ret; } +static unsigned int inv_icm42600_accel_convert_roc_to_wom(u64 roc, + int accel_hz, int accel_uhz) +{ + /* 1000/256mg per LSB converted in µm/s² */ + const unsigned int convert = (9807U * (MICRO / MILLI)) / 256U; + u64 value; + u64 freq_uhz; + + /* return 0 only if roc is 0 */ + if (roc == 0) + return 0; + + freq_uhz = (u64)accel_hz * MICRO + (u64)accel_uhz; + value = div64_u64(roc * MICRO, freq_uhz * (u64)convert); + + /* limit value to 8 bits and prevent 0 */ + return clamp(value, 1, 255); +} + +static u64 inv_icm42600_accel_convert_wom_to_roc(unsigned int threshold, + int accel_hz, int accel_uhz) +{ + /* 1000/256mg per LSB converted in µm/s² */ + const unsigned int convert = (9807U * (MICRO / MILLI)) / 256U; + u64 value; + u64 freq_uhz; + + value = threshold * convert; + freq_uhz = (u64)accel_hz * MICRO + (u64)accel_uhz; + + /* compute the differential by multiplying by the frequency */ + return div_u64(value * freq_uhz, MICRO); +} + +static int inv_icm42600_accel_set_wom_threshold(struct inv_icm42600_state *st, + u64 value, + int accel_hz, int accel_uhz) +{ + unsigned int threshold; + int ret; + + /* convert roc to wom threshold and convert back to handle clipping */ + threshold = inv_icm42600_accel_convert_roc_to_wom(value, accel_hz, accel_uhz); + value = inv_icm42600_accel_convert_wom_to_roc(threshold, accel_hz, accel_uhz); + + dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold); + + /* set accel WoM threshold for the 3 axes */ + st->buffer[0] = threshold; + st->buffer[1] = threshold; + st->buffer[2] = threshold; + ret = regmap_bulk_write(st->map, INV_ICM42600_REG_ACCEL_WOM_X_THR, st->buffer, 3); + if (ret) + return ret; + + st->apex.wom.value = value; + + return 0; +} + +static int _inv_icm42600_accel_enable_wom(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int sleep_ms = 0; + int ret; + + scoped_guard(mutex, &st->lock) { + /* turn on accel sensor */ + conf.mode = accel_st->power_mode; + conf.filter = accel_st->filter; + ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_ms); + if (ret) + return ret; + } + + if (sleep_ms) + msleep(sleep_ms); + + scoped_guard(mutex, &st->lock) { + ret = inv_icm42600_enable_wom(st); + if (ret) + return ret; + st->apex.on++; + st->apex.wom.enable = true; + } + + return 0; +} + +static int inv_icm42600_accel_enable_wom(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct device *pdev = regmap_get_device(st->map); + int ret; + + ret = pm_runtime_resume_and_get(pdev); + if (ret) + return ret; + + ret = _inv_icm42600_accel_enable_wom(indio_dev); + if (ret) { + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + return ret; + } + + return 0; +} + +static int _inv_icm42600_accel_disable_wom(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int sleep_ms = 0; + int ret; + + scoped_guard(mutex, &st->lock) { + /* + * Consider that turning off WoM is always working to avoid + * blocking the chip in on mode and prevent going back to sleep. + * If there is an error, the chip will anyway go back to sleep + * and the feature will not work anymore. + */ + st->apex.wom.enable = false; + st->apex.on--; + ret = inv_icm42600_disable_wom(st); + if (ret) + return ret; + /* turn off accel sensor if not used */ + if (!st->apex.on && !iio_buffer_enabled(indio_dev)) { + conf.mode = INV_ICM42600_SENSOR_MODE_OFF; + ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_ms); + if (ret) + return ret; + } + } + + if (sleep_ms) + msleep(sleep_ms); + + return 0; +} + +static int inv_icm42600_accel_disable_wom(struct iio_dev *indio_dev) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct device *pdev = regmap_get_device(st->map); + int ret; + + ret = _inv_icm42600_accel_disable_wom(indio_dev); + + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + + return ret; +} + +void inv_icm42600_accel_handle_events(struct iio_dev *indio_dev, + unsigned int status2, unsigned int status3, + s64 timestamp) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + u64 ev_code; + + /* handle WoM event */ + if (st->apex.wom.enable && (status2 & INV_ICM42600_INT_STATUS2_WOM_INT)) { + 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, timestamp); + } +} + /* IIO format int + nano */ static const int inv_icm42600_accel_scale[] = { /* +/- 16G => 0.004788403 m/s-2 */ @@ -466,6 +666,10 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev, ret = inv_icm42600_set_accel_conf(st, &conf, NULL); if (ret) goto out_unlock; + /* update wom threshold since roc is dependent on sampling frequency */ + ret = inv_icm42600_accel_set_wom_threshold(st, st->apex.wom.value, val, val2); + if (ret) + goto out_unlock; inv_icm42600_buffer_update_fifo_period(st); inv_icm42600_buffer_update_watermark(st); @@ -492,11 +696,11 @@ static int inv_icm42600_accel_read_offset(struct inv_icm42600_state *st, int *val, int *val2) { struct device *dev = regmap_get_device(st->map); - int64_t val64; - int32_t bias; + s64 val64; + s32 bias; unsigned int reg; - int16_t offset; - uint8_t data[2]; + s16 offset; + u8 data[2]; int ret; if (chan->type != IIO_ACCEL) @@ -550,7 +754,7 @@ static int inv_icm42600_accel_read_offset(struct inv_icm42600_state *st, * result in micro (1000000) * (offset * 5 * 9.806650 * 1000000) / 10000 */ - val64 = (int64_t)offset * 5LL * 9806650LL; + val64 = (s64)offset * 5LL * 9806650LL; /* for rounding, add + or - divisor (10000) divided by 2 */ if (val64 >= 0) val64 += 10000LL / 2LL; @@ -568,10 +772,10 @@ static int inv_icm42600_accel_write_offset(struct inv_icm42600_state *st, int val, int val2) { struct device *dev = regmap_get_device(st->map); - int64_t val64; - int32_t min, max; + s64 val64; + s32 min, max; unsigned int reg, regval; - int16_t offset; + s16 offset; int ret; if (chan->type != IIO_ACCEL) @@ -596,7 +800,7 @@ static int inv_icm42600_accel_write_offset(struct inv_icm42600_state *st, inv_icm42600_accel_calibbias[1]; max = inv_icm42600_accel_calibbias[4] * 1000000L + inv_icm42600_accel_calibbias[5]; - val64 = (int64_t)val * 1000000LL + (int64_t)val2; + val64 = (s64)val * 1000000LL + (s64)val2; if (val64 < min || val64 > max) return -EINVAL; @@ -671,7 +875,7 @@ static int inv_icm42600_accel_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - int16_t data; + s16 data; int ret; switch (chan->type) { @@ -819,6 +1023,116 @@ static int inv_icm42600_accel_hwfifo_flush(struct iio_dev *indio_dev, return ret; } +static int inv_icm42600_accel_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_icm42600_state *st = iio_device_get_drvdata(indio_dev); + + /* handle only WoM (roc rising) event */ + if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING) + return -EINVAL; + + guard(mutex)(&st->lock); + + return st->apex.wom.enable ? 1 : 0; +} + +static int inv_icm42600_accel_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + bool state) +{ + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + + /* handle only WoM (roc rising) event */ + if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING) + return -EINVAL; + + scoped_guard(mutex, &st->lock) { + if (st->apex.wom.enable == state) + return 0; + } + + if (state) + return inv_icm42600_accel_enable_wom(indio_dev); + + return inv_icm42600_accel_disable_wom(indio_dev); +} + +static int inv_icm42600_accel_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_icm42600_state *st = iio_device_get_drvdata(indio_dev); + u32 rem; + + /* handle only WoM (roc rising) event value */ + if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING) + return -EINVAL; + + guard(mutex)(&st->lock); + + /* return value in micro */ + *val = div_u64_rem(st->apex.wom.value, MICRO, &rem); + *val2 = rem; + return IIO_VAL_INT_PLUS_MICRO; +} + +static int _inv_icm42600_accel_wom_value(struct inv_icm42600_state *st, + int val, int val2) +{ + u64 value; + unsigned int accel_hz, accel_uhz; + int ret; + + guard(mutex)(&st->lock); + + ret = inv_icm42600_accel_read_odr(st, &accel_hz, &accel_uhz); + if (ret < 0) + return ret; + + value = (u64)val * MICRO + (u64)val2; + + return inv_icm42600_accel_set_wom_threshold(st, value, + accel_hz, accel_uhz); +} + +static int inv_icm42600_accel_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_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); + int ret; + + /* handle only WoM (roc rising) event value */ + if (type != IIO_EV_TYPE_ROC || dir != IIO_EV_DIR_RISING) + return -EINVAL; + + if (val < 0 || val2 < 0) + return -EINVAL; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = _inv_icm42600_accel_wom_value(st, val, val2); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + static const struct iio_info inv_icm42600_accel_info = { .read_raw = inv_icm42600_accel_read_raw, .read_avail = inv_icm42600_accel_read_avail, @@ -828,6 +1142,10 @@ static const struct iio_info inv_icm42600_accel_info = { .update_scan_mode = inv_icm42600_accel_update_scan_mode, .hwfifo_set_watermark = inv_icm42600_accel_hwfifo_set_watermark, .hwfifo_flush_to_buffer = inv_icm42600_accel_hwfifo_flush, + .read_event_config = inv_icm42600_accel_read_event_config, + .write_event_config = inv_icm42600_accel_write_event_config, + .read_event_value = inv_icm42600_accel_read_event_value, + .write_event_value = inv_icm42600_accel_write_event_value, }; struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) @@ -888,6 +1206,11 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) if (ret) return ERR_PTR(ret); + /* accel events are wakeup capable */ + ret = devm_device_init_wakeup(&indio_dev->dev); + if (ret) + return ERR_PTR(ret); + return indio_dev; } @@ -902,7 +1225,8 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) const int8_t *temp; unsigned int odr; int64_t ts_val; - struct inv_icm42600_accel_buffer buffer; + /* buffer is copied to userspace, zeroing it to avoid any data leak */ + struct inv_icm42600_accel_buffer buffer = { }; /* parse all fifo packets */ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) { @@ -921,8 +1245,6 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) inv_sensors_timestamp_apply_odr(ts, st->fifo.period, st->fifo.nb.total, no); - /* buffer is copied to userspace, zeroing it to avoid any data leak */ - memset(&buffer, 0, sizeof(buffer)); memcpy(&buffer.accel, accel, sizeof(buffer.accel)); /* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp * 64) : 0; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index aae7c56481a3..7c4ed981db04 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -26,28 +26,28 @@ #define INV_ICM42600_FIFO_HEADER_ODR_GYRO BIT(0) struct inv_icm42600_fifo_1sensor_packet { - uint8_t header; + u8 header; struct inv_icm42600_fifo_sensor_data data; - int8_t temp; + s8 temp; } __packed; #define INV_ICM42600_FIFO_1SENSOR_PACKET_SIZE 8 struct inv_icm42600_fifo_2sensors_packet { - uint8_t header; + u8 header; struct inv_icm42600_fifo_sensor_data accel; struct inv_icm42600_fifo_sensor_data gyro; - int8_t temp; + s8 temp; __be16 timestamp; } __packed; #define INV_ICM42600_FIFO_2SENSORS_PACKET_SIZE 16 ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, - const void **gyro, const int8_t **temp, + const void **gyro, const s8 **temp, const void **timestamp, unsigned int *odr) { const struct inv_icm42600_fifo_1sensor_packet *pack1 = packet; const struct inv_icm42600_fifo_2sensors_packet *pack2 = packet; - uint8_t header = *((const uint8_t *)packet); + u8 header = *((const u8 *)packet); /* FIFO empty */ if (header & INV_ICM42600_FIFO_HEADER_MSG) { @@ -100,7 +100,7 @@ ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, void inv_icm42600_buffer_update_fifo_period(struct inv_icm42600_state *st) { - uint32_t period_gyro, period_accel, period; + u32 period_gyro, period_accel, period; if (st->fifo.en & INV_ICM42600_SENSOR_GYRO) period_gyro = inv_icm42600_odr_to_period(st->conf.gyro.odr); @@ -204,8 +204,8 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) { size_t packet_size, wm_size; unsigned int wm_gyro, wm_accel, watermark; - uint32_t period_gyro, period_accel, period; - uint32_t latency_gyro, latency_accel, latency; + u32 period_gyro, period_accel, period; + u32 latency_gyro, latency_accel, latency; bool restore; __le16 raw_wm; int ret; @@ -422,7 +422,7 @@ static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) conf.mode = INV_ICM42600_SENSOR_MODE_OFF; if (sensor == INV_ICM42600_SENSOR_GYRO) ret = inv_icm42600_set_gyro_conf(st, &conf, &sleep_sensor); - else + else if (!st->apex.on) ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_sensor); if (ret) goto out_unlock; @@ -459,7 +459,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, __be16 *raw_fifo_count; ssize_t i, size; const void *accel, *gyro, *timestamp; - const int8_t *temp; + const s8 *temp; unsigned int odr; int ret; @@ -550,7 +550,7 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); struct inv_sensors_timestamp *ts; - int64_t gyro_ts, accel_ts; + s64 gyro_ts, accel_ts; int ret; gyro_ts = iio_get_time_ns(st->indio_gyro); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index f6c85daf42b0..ffca4da1e249 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -28,7 +28,7 @@ struct inv_icm42600_state; struct inv_icm42600_fifo { unsigned int on; unsigned int en; - uint32_t period; + u32 period; struct { unsigned int gyro; unsigned int accel; @@ -41,7 +41,7 @@ struct inv_icm42600_fifo { size_t accel; size_t total; } nb; - uint8_t data[2080] __aligned(IIO_DMA_MINALIGN); + u8 data[2080] __aligned(IIO_DMA_MINALIGN); }; /* FIFO data packet */ @@ -52,7 +52,7 @@ struct inv_icm42600_fifo_sensor_data { } __packed; #define INV_ICM42600_FIFO_DATA_INVALID -32768 -static inline int16_t inv_icm42600_fifo_get_sensor_data(__be16 d) +static inline s16 inv_icm42600_fifo_get_sensor_data(__be16 d) { return be16_to_cpu(d); } @@ -60,7 +60,7 @@ static inline int16_t inv_icm42600_fifo_get_sensor_data(__be16 d) static inline bool inv_icm42600_fifo_is_data_valid(const struct inv_icm42600_fifo_sensor_data *s) { - int16_t x, y, z; + s16 x, y, z; x = inv_icm42600_fifo_get_sensor_data(s->x); y = inv_icm42600_fifo_get_sensor_data(s->y); @@ -75,7 +75,7 @@ inv_icm42600_fifo_is_data_valid(const struct inv_icm42600_fifo_sensor_data *s) } ssize_t inv_icm42600_fifo_decode_packet(const void *packet, const void **accel, - const void **gyro, const int8_t **temp, + const void **gyro, const s8 **temp, const void **timestamp, unsigned int *odr); extern const struct iio_buffer_setup_ops inv_icm42600_buffer_ops; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 63d46619ebfa..a4d42e7e2180 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -83,7 +83,7 @@ const struct regmap_config inv_icm42600_regmap_config = { .num_ranges = ARRAY_SIZE(inv_icm42600_regmap_ranges), .volatile_table = inv_icm42600_regmap_volatile_accesses, .rd_noinc_table = inv_icm42600_regmap_rd_noinc_accesses, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_NS_GPL(inv_icm42600_regmap_config, "IIO_ICM42600"); @@ -97,13 +97,13 @@ const struct regmap_config inv_icm42600_spi_regmap_config = { .num_ranges = ARRAY_SIZE(inv_icm42600_regmap_ranges), .volatile_table = inv_icm42600_regmap_volatile_accesses, .rd_noinc_table = inv_icm42600_regmap_rd_noinc_accesses, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_write = true, }; EXPORT_SYMBOL_NS_GPL(inv_icm42600_spi_regmap_config, "IIO_ICM42600"); struct inv_icm42600_hw { - uint8_t whoami; + u8 whoami; const char *name; const struct inv_icm42600_conf *conf; }; @@ -188,9 +188,9 @@ inv_icm42600_get_mount_matrix(const struct iio_dev *indio_dev, return &st->orientation; } -uint32_t inv_icm42600_odr_to_period(enum inv_icm42600_odr odr) +u32 inv_icm42600_odr_to_period(enum inv_icm42600_odr odr) { - static uint32_t odr_periods[INV_ICM42600_ODR_NB] = { + static u32 odr_periods[INV_ICM42600_ODR_NB] = { /* reserved values */ 0, 0, 0, /* 8kHz */ @@ -404,6 +404,37 @@ int inv_icm42600_set_temp_conf(struct inv_icm42600_state *st, bool enable, sleep_ms); } +int inv_icm42600_enable_wom(struct inv_icm42600_state *st) +{ + int ret; + + /* enable WoM hardware */ + ret = regmap_write(st->map, INV_ICM42600_REG_SMD_CONFIG, + INV_ICM42600_SMD_CONFIG_SMD_MODE_WOM | + INV_ICM42600_SMD_CONFIG_WOM_MODE); + if (ret) + return ret; + + /* enable WoM interrupt */ + return regmap_set_bits(st->map, INV_ICM42600_REG_INT_SOURCE1, + INV_ICM42600_INT_SOURCE1_WOM_INT1_EN); +} + +int inv_icm42600_disable_wom(struct inv_icm42600_state *st) +{ + int ret; + + /* disable WoM interrupt */ + ret = regmap_clear_bits(st->map, INV_ICM42600_REG_INT_SOURCE1, + INV_ICM42600_INT_SOURCE1_WOM_INT1_EN); + if (ret) + return ret; + + /* disable WoM hardware */ + return regmap_write(st->map, INV_ICM42600_REG_SMD_CONFIG, + INV_ICM42600_SMD_CONFIG_SMD_MODE_OFF); +} + int inv_icm42600_debugfs_reg(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { @@ -548,6 +579,19 @@ static irqreturn_t inv_icm42600_irq_handler(int irq, void *_data) mutex_lock(&st->lock); + if (st->apex.on) { + unsigned int status2, status3; + + /* read INT_STATUS2 and INT_STATUS3 in 1 operation */ + ret = regmap_bulk_read(st->map, INV_ICM42600_REG_INT_STATUS2, st->buffer, 2); + if (ret) + goto out_unlock; + status2 = st->buffer[0]; + status3 = st->buffer[1]; + inv_icm42600_accel_handle_events(st->indio_accel, status2, status3, + st->timestamp.accel); + } + ret = regmap_read(st->map, INV_ICM42600_REG_INT_STATUS, &status); if (ret) goto out_unlock; @@ -721,6 +765,7 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, mutex_init(&st->lock); st->chip = chip; st->map = regmap; + st->irq = irq; ret = iio_read_mount_matrix(dev, &st->orientation); if (ret) { @@ -799,6 +844,9 @@ EXPORT_SYMBOL_NS_GPL(inv_icm42600_core_probe, "IIO_ICM42600"); static int inv_icm42600_suspend(struct device *dev) { struct inv_icm42600_state *st = dev_get_drvdata(dev); + struct device *accel_dev; + bool wakeup; + int accel_conf; int ret; mutex_lock(&st->lock); @@ -819,13 +867,32 @@ static int inv_icm42600_suspend(struct device *dev) goto out_unlock; } + /* keep chip on and wake-up capable if APEX and wakeup on */ + accel_dev = &st->indio_accel->dev; + wakeup = st->apex.on && device_may_wakeup(accel_dev); + if (wakeup) { + /* keep accel on and setup irq for wakeup */ + accel_conf = st->conf.accel.mode; + enable_irq_wake(st->irq); + disable_irq(st->irq); + } else { + /* disable APEX features and accel if wakeup disabled */ + if (st->apex.wom.enable) { + ret = inv_icm42600_disable_wom(st); + if (ret) + goto out_unlock; + } + accel_conf = INV_ICM42600_SENSOR_MODE_OFF; + } + ret = inv_icm42600_set_pwr_mgmt0(st, INV_ICM42600_SENSOR_MODE_OFF, - INV_ICM42600_SENSOR_MODE_OFF, false, - NULL); + accel_conf, false, NULL); if (ret) goto out_unlock; - regulator_disable(st->vddio_supply); + /* disable vddio regulator if chip is sleeping */ + if (!wakeup) + regulator_disable(st->vddio_supply); out_unlock: mutex_unlock(&st->lock); @@ -841,13 +908,24 @@ static int inv_icm42600_resume(struct device *dev) struct inv_icm42600_state *st = dev_get_drvdata(dev); struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); + struct device *accel_dev; + bool wakeup; int ret; mutex_lock(&st->lock); - ret = inv_icm42600_enable_regulator_vddio(st); - if (ret) - goto out_unlock; + /* check wakeup capability */ + accel_dev = &st->indio_accel->dev; + wakeup = st->apex.on && device_may_wakeup(accel_dev); + /* restore irq state or vddio if cut off */ + if (wakeup) { + enable_irq(st->irq); + disable_irq_wake(st->irq); + } else { + ret = inv_icm42600_enable_regulator_vddio(st); + if (ret) + goto out_unlock; + } pm_runtime_disable(dev); pm_runtime_set_active(dev); @@ -860,6 +938,13 @@ static int inv_icm42600_resume(struct device *dev) if (ret) goto out_unlock; + /* restore APEX features if disabled */ + if (!wakeup && st->apex.wom.enable) { + ret = inv_icm42600_enable_wom(st); + if (ret) + goto out_unlock; + } + /* restore FIFO data streaming */ if (st->fifo.on) { inv_sensors_timestamp_reset(&gyro_st->ts); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index b4d7ce1432a4..9ba6f13628e6 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -77,7 +77,7 @@ static const struct iio_chan_spec inv_icm42600_gyro_channels[] = { */ struct inv_icm42600_gyro_buffer { struct inv_icm42600_fifo_sensor_data gyro; - int16_t temp; + s16 temp; aligned_s64 timestamp; }; @@ -139,7 +139,7 @@ out_unlock: static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st, struct iio_chan_spec const *chan, - int16_t *val) + s16 *val) { struct device *dev = regmap_get_device(st->map); struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; @@ -179,7 +179,7 @@ static int inv_icm42600_gyro_read_sensor(struct inv_icm42600_state *st, if (ret) goto exit; - *val = (int16_t)be16_to_cpup(data); + *val = (s16)be16_to_cpup(data); if (*val == INV_ICM42600_DATA_INVALID) ret = -EINVAL; exit: @@ -399,11 +399,11 @@ static int inv_icm42600_gyro_read_offset(struct inv_icm42600_state *st, int *val, int *val2) { struct device *dev = regmap_get_device(st->map); - int64_t val64; - int32_t bias; + s64 val64; + s32 bias; unsigned int reg; - int16_t offset; - uint8_t data[2]; + s16 offset; + u8 data[2]; int ret; if (chan->type != IIO_ANGL_VEL) @@ -457,7 +457,7 @@ static int inv_icm42600_gyro_read_offset(struct inv_icm42600_state *st, * result in nano (1000000000) * (offset * 64 * Pi * 1000000000) / (2048 * 180) */ - val64 = (int64_t)offset * 64LL * 3141592653LL; + val64 = (s64)offset * 64LL * 3141592653LL; /* for rounding, add + or - divisor (2048 * 180) divided by 2 */ if (val64 >= 0) val64 += 2048 * 180 / 2; @@ -475,9 +475,9 @@ static int inv_icm42600_gyro_write_offset(struct inv_icm42600_state *st, int val, int val2) { struct device *dev = regmap_get_device(st->map); - int64_t val64, min, max; + s64 val64, min, max; unsigned int reg, regval; - int16_t offset; + s16 offset; int ret; if (chan->type != IIO_ANGL_VEL) @@ -498,11 +498,11 @@ static int inv_icm42600_gyro_write_offset(struct inv_icm42600_state *st, } /* inv_icm42600_gyro_calibbias: min - step - max in nano */ - min = (int64_t)inv_icm42600_gyro_calibbias[0] * 1000000000LL + - (int64_t)inv_icm42600_gyro_calibbias[1]; - max = (int64_t)inv_icm42600_gyro_calibbias[4] * 1000000000LL + - (int64_t)inv_icm42600_gyro_calibbias[5]; - val64 = (int64_t)val * 1000000000LL + (int64_t)val2; + min = (s64)inv_icm42600_gyro_calibbias[0] * 1000000000LL + + (s64)inv_icm42600_gyro_calibbias[1]; + max = (s64)inv_icm42600_gyro_calibbias[4] * 1000000000LL + + (s64)inv_icm42600_gyro_calibbias[5]; + val64 = (s64)val * 1000000000LL + (s64)val2; if (val64 < min || val64 > max) return -EINVAL; @@ -577,7 +577,7 @@ static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - int16_t data; + s16 data; int ret; switch (chan->type) { @@ -803,10 +803,11 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; - const int8_t *temp; + const s8 *temp; unsigned int odr; - int64_t ts_val; - struct inv_icm42600_gyro_buffer buffer; + s64 ts_val; + /* buffer is copied to userspace, zeroing it to avoid any data leak */ + struct inv_icm42600_gyro_buffer buffer = { }; /* parse all fifo packets */ for (i = 0, no = 0; i < st->fifo.count; i += size, ++no) { @@ -825,8 +826,6 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) inv_sensors_timestamp_apply_odr(ts, st->fifo.period, st->fifo.nb.total, no); - /* buffer is copied to userspace, zeroing it to avoid any data leak */ - memset(&buffer, 0, sizeof(buffer)); memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro)); /* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp * 64) : 0; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c index 988f227f6563..8b15afca498c 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_temp.c @@ -13,7 +13,7 @@ #include "inv_icm42600.h" #include "inv_icm42600_temp.h" -static int inv_icm42600_temp_read(struct inv_icm42600_state *st, int16_t *temp) +static int inv_icm42600_temp_read(struct inv_icm42600_state *st, s16 *temp) { struct device *dev = regmap_get_device(st->map); __be16 *raw; @@ -31,7 +31,7 @@ static int inv_icm42600_temp_read(struct inv_icm42600_state *st, int16_t *temp) if (ret) goto exit; - *temp = (int16_t)be16_to_cpup(raw); + *temp = (s16)be16_to_cpup(raw); if (*temp == INV_ICM42600_DATA_INVALID) ret = -EINVAL; @@ -48,7 +48,7 @@ int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - int16_t temp; + s16 temp; int ret; if (chan->type != IIO_TEMP) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index a9bcf02e5b43..460792ed27e0 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -130,12 +130,10 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client) st->mux_client = NULL; if (adev) { - struct i2c_board_info info; + struct i2c_board_info info = { }; struct i2c_client *mux_client; int ret = -1; - memset(&info, 0, sizeof(info)); - dmi_check_system(inv_mpu_dev_list); switch (matched_product_name) { case INV_MPU_ASUS_T100TA: diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c index 8a7f2911905a..970cf5c47f68 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_aux.c @@ -14,6 +14,8 @@ /* * i2c master auxiliary bus transfer function. * Requires the i2c operations to be correctly setup before. + * Disables SLV0 and checks for NACK status internally. + * Assumes that only SLV0 is used for transfers. */ static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st) { @@ -23,6 +25,7 @@ static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st) uint8_t d; unsigned int user_ctrl; int ret; + unsigned int status; /* set sample rate */ d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(freq); @@ -51,12 +54,27 @@ static int inv_mpu_i2c_master_xfer(const struct inv_mpu6050_state *st) if (ret) goto error_restore_rate; + /* disable i2c slave */ + ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); + if (ret) + goto error_disable_i2c; + + /* check i2c status */ + ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); + if (ret) + return ret; + + if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) + return -EIO; + return 0; error_stop_i2c: regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl); error_restore_rate: regmap_write(st->map, st->reg->sample_rate_div, st->chip_config.divider); +error_disable_i2c: + regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); return ret; } @@ -117,7 +135,6 @@ int inv_mpu_aux_init(const struct inv_mpu6050_state *st) int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, uint8_t reg, uint8_t *val, size_t size) { - unsigned int status; int ret; if (size > 0x0F) @@ -136,30 +153,14 @@ int inv_mpu_aux_read(const struct inv_mpu6050_state *st, uint8_t addr, if (ret) return ret; - /* do i2c xfer */ + /* do i2c xfer, disable i2c slave and check status*/ ret = inv_mpu_i2c_master_xfer(st); if (ret) - goto error_disable_i2c; - - /* disable i2c slave */ - ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); - if (ret) - goto error_disable_i2c; - - /* check i2c status */ - ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); - if (ret) return ret; - if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) - return -EIO; /* read data in registers */ return regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, val, size); - -error_disable_i2c: - regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); - return ret; } /** @@ -174,7 +175,6 @@ error_disable_i2c: int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, uint8_t reg, uint8_t val) { - unsigned int status; int ret; /* setup i2c SLV0 control: i2c addr, register, value, enable + size */ @@ -192,26 +192,10 @@ int inv_mpu_aux_write(const struct inv_mpu6050_state *st, uint8_t addr, if (ret) return ret; - /* do i2c xfer */ + /* do i2c xfer, disable i2c slave and check status*/ ret = inv_mpu_i2c_master_xfer(st); if (ret) - goto error_disable_i2c; - - /* disable i2c slave */ - ret = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); - if (ret) - goto error_disable_i2c; - - /* check i2c status */ - ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status); - if (ret) return ret; - if (status & INV_MPU6050_BIT_I2C_SLV0_NACK) - return -EIO; return 0; - -error_disable_i2c: - regmap_write(st->map, INV_MPU6050_REG_I2C_SLV_CTRL(0), 0); - return ret; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index b8656c02354a..39eb516acc73 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -1382,7 +1382,7 @@ inv_fifo_rate_show(struct device *dev, struct device_attribute *attr, fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); mutex_unlock(&st->lock); - return scnprintf(buf, PAGE_SIZE, "%u\n", fifo_rate); + return sysfs_emit(buf, "%u\n", fifo_rate); } /* @@ -1409,8 +1409,7 @@ static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr, case ATTR_ACCL_MATRIX: m = st->plat_data.orientation; - return scnprintf(buf, PAGE_SIZE, - "%d, %d, %d; %d, %d, %d; %d, %d, %d\n", + return sysfs_emit(buf, "%d, %d, %d; %d, %d, %d; %d, %d, %d\n", m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); default: return -EINVAL; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 273196e647a2..c4c11124f92f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -50,7 +50,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) u16 fifo_count; u32 fifo_period; s64 timestamp; - u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(8); + /* clear internal data buffer for avoiding kernel data leak */ + u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(8) = { }; size_t i, nb; mutex_lock(&st->lock); @@ -103,9 +104,6 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) inv_sensors_timestamp_interrupt(&st->timestamp, 1, pf->timestamp); inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, 1, 0); - /* clear internal data buffer for avoiding kernel data leak */ - memset(data, 0, sizeof(data)); - /* read all data once and process every samples */ result = regmap_noinc_read(st->map, st->reg->fifo_r_w, st->data, fifo_count); if (result) diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index 2bdfb2619137..55c82891e08c 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -22,9 +22,6 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> -#define KMX61_DRV_NAME "kmx61" -#define KMX61_IRQ_NAME "kmx61_event" - #define KMX61_REG_WHO_AM_I 0x00 #define KMX61_REG_INS1 0x01 #define KMX61_REG_INS2 0x02 @@ -1312,7 +1309,7 @@ static int kmx61_probe(struct i2c_client *client) kmx61_data_rdy_trig_poll, kmx61_event_handler, IRQF_TRIGGER_RISING, - KMX61_IRQ_NAME, + "kmx61_event", data); if (ret) goto err_chip_uninit; @@ -1494,7 +1491,7 @@ MODULE_DEVICE_TABLE(i2c, kmx61_id); static struct i2c_driver kmx61_driver = { .driver = { - .name = KMX61_DRV_NAME, + .name = "kmx61", .pm = pm_ptr(&kmx61_pm_ops), }, .probe = kmx61_probe, diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 266e1b29bf91..23760652a046 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -723,9 +723,10 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_data_size_set, "IIO_BACKEND"); * 0 on success, negative error number on failure. */ int iio_backend_oversampling_ratio_set(struct iio_backend *back, + unsigned int chan, unsigned int ratio) { - return iio_backend_op_call(back, oversampling_ratio_set, ratio); + return iio_backend_op_call(back, oversampling_ratio_set, chan, ratio); } EXPORT_SYMBOL_NS_GPL(iio_backend_oversampling_ratio_set, "IIO_BACKEND"); @@ -812,6 +813,64 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) } /** + * iio_backend_filter_type_set - Set filter type + * @back: Backend device + * @type: Filter type. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_filter_type_set(struct iio_backend *back, + enum iio_backend_filter_type type) +{ + if (type >= IIO_BACKEND_FILTER_TYPE_MAX) + return -EINVAL; + + return iio_backend_op_call(back, filter_type_set, type); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_filter_type_set, "IIO_BACKEND"); + +/** + * iio_backend_interface_data_align - Perform the data alignment process. + * @back: Backend device + * @timeout_us: Timeout value in us. + * + * When activated, it initates a proccess that aligns the sample's most + * significant bit (MSB) based solely on the captured data, without + * considering any other external signals. + * + * The timeout_us value must be greater than 0. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_interface_data_align(struct iio_backend *back, u32 timeout_us) +{ + if (!timeout_us) + return -EINVAL; + + return iio_backend_op_call(back, interface_data_align, timeout_us); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_interface_data_align, "IIO_BACKEND"); + +/** + * iio_backend_num_lanes_set - Number of lanes enabled. + * @back: Backend device + * @num_lanes: Number of lanes. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_num_lanes_set(struct iio_backend *back, unsigned int num_lanes) +{ + if (!num_lanes) + return -EINVAL; + + return iio_backend_op_call(back, num_lanes_set, num_lanes); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_num_lanes_set, "IIO_BACKEND"); + +/** * iio_backend_ddr_enable - Enable interface DDR (Double Data Rate) mode * @back: Backend device * diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 5ffda104d4b2..159d6c5ca3ce 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -188,6 +188,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient", [IIO_CHAN_INFO_ZEROPOINT] = "zeropoint", [IIO_CHAN_INFO_TROUGH] = "trough_raw", + [IIO_CHAN_INFO_CONVDELAY] = "convdelay", }; /** * iio_device_id() - query the unique ID for the device diff --git a/drivers/iio/light/adux1020.c b/drivers/iio/light/adux1020.c index e321f89c5340..66ff9c5fb66a 100644 --- a/drivers/iio/light/adux1020.c +++ b/drivers/iio/light/adux1020.c @@ -23,7 +23,6 @@ #include <linux/iio/sysfs.h> #include <linux/iio/events.h> -#define ADUX1020_REGMAP_NAME "adux1020_regmap" #define ADUX1020_DRV_NAME "adux1020" /* System registers */ @@ -114,7 +113,7 @@ static const struct adux1020_mode_data adux1020_modes[] = { }; static const struct regmap_config adux1020_regmap_config = { - .name = ADUX1020_REGMAP_NAME, + .name = "adux1020_regmap", .reg_bits = 8, .val_bits = 16, .max_register = 0x6F, diff --git a/drivers/iio/light/apds9160.c b/drivers/iio/light/apds9160.c index d3f415930ec9..9b8af11b7b67 100644 --- a/drivers/iio/light/apds9160.c +++ b/drivers/iio/light/apds9160.c @@ -25,8 +25,6 @@ #include <linux/unaligned.h> -#define APDS9160_REGMAP_NAME "apds9160_regmap" - /* Main control register */ #define APDS9160_REG_CTRL 0x00 #define APDS9160_CTRL_SWRESET BIT(4) /* 1: Activate reset */ @@ -161,7 +159,7 @@ static const struct regmap_access_table apds9160_volatile_table = { }; static const struct regmap_config apds9160_regmap_config = { - .name = APDS9160_REGMAP_NAME, + .name = "apds9160_regmap", .reg_bits = 8, .val_bits = 8, .use_single_read = true, diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index 938d76f7e312..05ba21675063 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -17,7 +17,6 @@ #include <linux/iio/events.h> #define APDS9300_DRV_NAME "apds9300" -#define APDS9300_IRQ_NAME "apds9300_event" /* Command register bits */ #define APDS9300_CMD BIT(7) /* Select command register. Must write as 1 */ @@ -432,7 +431,7 @@ static int apds9300_probe(struct i2c_client *client) ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, apds9300_interrupt_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - APDS9300_IRQ_NAME, indio_dev); + "apds9300_event", indio_dev); if (ret) { dev_err(&client->dev, "irq request error %d\n", -ret); goto err; diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c index e9b237de180a..f676da245aa7 100644 --- a/drivers/iio/light/apds9306.c +++ b/drivers/iio/light/apds9306.c @@ -744,20 +744,27 @@ static int apds9306_event_period_set(struct apds9306_data *data, int val) return regmap_field_write(rf->int_persist_val, val); } -static int apds9306_event_thresh_get(struct apds9306_data *data, int dir, - int *val) +static int apds9306_get_thresh_reg(int dir) { - int var, ret; - u8 buff[3]; - if (dir == IIO_EV_DIR_RISING) - var = APDS9306_ALS_THRES_UP_0_REG; + return APDS9306_ALS_THRES_UP_0_REG; else if (dir == IIO_EV_DIR_FALLING) - var = APDS9306_ALS_THRES_LOW_0_REG; + return APDS9306_ALS_THRES_LOW_0_REG; else return -EINVAL; +} + +static int apds9306_event_thresh_get(struct apds9306_data *data, int dir, + int *val) +{ + int reg, ret; + u8 buff[3]; - ret = regmap_bulk_read(data->regmap, var, buff, sizeof(buff)); + reg = apds9306_get_thresh_reg(dir); + if (reg < 0) + return reg; + + ret = regmap_bulk_read(data->regmap, reg, buff, sizeof(buff)); if (ret) return ret; @@ -769,22 +776,19 @@ static int apds9306_event_thresh_get(struct apds9306_data *data, int dir, static int apds9306_event_thresh_set(struct apds9306_data *data, int dir, int val) { - int var; + int reg; 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; + reg = apds9306_get_thresh_reg(dir); + if (reg < 0) + return reg; 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)); + return regmap_bulk_write(data->regmap, reg, buff, sizeof(buff)); } static int apds9306_event_thresh_adaptive_get(struct apds9306_data *data, int *val) diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 0003a29bf264..b92d0fce5aec 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -25,7 +25,6 @@ #include <linux/iio/kfifo_buf.h> #include <linux/iio/sysfs.h> -#define APDS9960_REGMAP_NAME "apds9960_regmap" #define APDS9960_DRV_NAME "apds9960" #define APDS9960_REG_RAM_START 0x00 @@ -221,7 +220,7 @@ static const struct regmap_access_table apds9960_writeable_table = { }; static const struct regmap_config apds9960_regmap_config = { - .name = APDS9960_REGMAP_NAME, + .name = "apds9960_regmap", .reg_bits = 8, .val_bits = 8, .use_single_read = true, diff --git a/drivers/iio/light/bh1745.c b/drivers/iio/light/bh1745.c index 56ab5fe90ff9..4e9bd8f831f7 100644 --- a/drivers/iio/light/bh1745.c +++ b/drivers/iio/light/bh1745.c @@ -740,14 +740,12 @@ static irqreturn_t bh1745_trigger_handler(int interrupt, void *p) struct { u16 chans[4]; aligned_s64 timestamp; - } scan; + } scan = { }; u16 value; int ret; int i; int j = 0; - memset(&scan, 0, sizeof(scan)); - iio_for_each_active_channel(indio_dev, i) { ret = regmap_bulk_read(data->regmap, BH1745_RED_LSB + 2 * i, &value, 2); diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index e864d2ef036e..3a3ad6b4c468 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -54,22 +54,21 @@ static const struct { struct cm3232_als_info { u8 regs_cmd_default; u8 hw_id; - int calibscale; int mlux_per_bit; int mlux_per_bit_base_it; }; -static struct cm3232_als_info cm3232_als_info_default = { +static const struct cm3232_als_info cm3232_als_info_default = { .regs_cmd_default = CM3232_CMD_DEFAULT, .hw_id = CM3232_HW_ID, - .calibscale = CM3232_CALIBSCALE_DEFAULT, .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT, .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT, }; struct cm3232_chip { struct i2c_client *client; - struct cm3232_als_info *als_info; + const struct cm3232_als_info *als_info; + int calibscale; u8 regs_cmd; u16 regs_als; }; @@ -199,7 +198,7 @@ static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2) static int cm3232_get_lux(struct cm3232_chip *chip) { struct i2c_client *client = chip->client; - struct cm3232_als_info *als_info = chip->als_info; + const struct cm3232_als_info *als_info = chip->als_info; int ret; int val, val2; int als_it; @@ -222,7 +221,7 @@ static int cm3232_get_lux(struct cm3232_chip *chip) chip->regs_als = (u16)ret; lux *= chip->regs_als; - lux *= als_info->calibscale; + lux *= chip->calibscale; lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION); lux = div_u64(lux, CM3232_MLUX_PER_LUX); @@ -237,7 +236,6 @@ static int cm3232_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct cm3232_chip *chip = iio_priv(indio_dev); - struct cm3232_als_info *als_info = chip->als_info; int ret; switch (mask) { @@ -248,7 +246,7 @@ static int cm3232_read_raw(struct iio_dev *indio_dev, *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBSCALE: - *val = als_info->calibscale; + *val = chip->calibscale; return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: return cm3232_read_als_it(chip, val, val2); @@ -262,11 +260,10 @@ static int cm3232_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct cm3232_chip *chip = iio_priv(indio_dev); - struct cm3232_als_info *als_info = chip->als_info; switch (mask) { case IIO_CHAN_INFO_CALIBSCALE: - als_info->calibscale = val; + chip->calibscale = val; return 0; case IIO_CHAN_INFO_INT_TIME: return cm3232_write_als_it(chip, val, val2); @@ -339,6 +336,7 @@ static int cm3232_probe(struct i2c_client *client) chip = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); chip->client = client; + chip->calibscale = CM3232_CALIBSCALE_DEFAULT; indio_dev->channels = cm3232_channels; indio_dev->num_channels = ARRAY_SIZE(cm3232_channels); diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 609ebf0f7313..0e4284823d44 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -562,7 +562,7 @@ static const struct regmap_config isl29028_regmap_config = { .volatile_reg = isl29028_is_volatile_reg, .max_register = ISL29028_NUM_REGS - 1, .num_reg_defaults_raw = ISL29028_NUM_REGS, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int isl29028_probe(struct i2c_client *client) diff --git a/drivers/iio/light/isl76682.c b/drivers/iio/light/isl76682.c index cf6ddee44ffc..b6f2fc9978f6 100644 --- a/drivers/iio/light/isl76682.c +++ b/drivers/iio/light/isl76682.c @@ -59,7 +59,7 @@ struct isl76682_range { u32 ir; }; -static struct isl76682_range isl76682_range_table[] = { +static const struct isl76682_range isl76682_range_table[] = { { ISL76682_COMMAND_RANGE_LUX_1K, 15000, 10500 }, { ISL76682_COMMAND_RANGE_LUX_4K, 60000, 42000 }, { ISL76682_COMMAND_RANGE_LUX_16K, 240000, 168000 }, diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index fa4677c28931..6978d02a4df5 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -106,7 +106,6 @@ #define JSA1212_PXS_DELAY_MS 100 #define JSA1212_DRIVER_NAME "jsa1212" -#define JSA1212_REGMAP_NAME "jsa1212_regmap" enum jsa1212_op_mode { JSA1212_OPMODE_ALS_EN, @@ -300,7 +299,7 @@ static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config jsa1212_regmap_config = { - .name = JSA1212_REGMAP_NAME, + .name = "jsa1212_regmap", .reg_bits = 8, .val_bits = 8, .max_register = JSA1212_MAX_REG, diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 8d8051cf6927..debf57a52d1c 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -24,8 +24,6 @@ #include <linux/iio/buffer.h> #include <linux/iio/triggered_buffer.h> -#define LTR501_DRV_NAME "ltr501" - #define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */ #define LTR501_PS_CONTR 0x81 /* PS operation mode */ #define LTR501_PS_MEAS_RATE 0x84 /* measurement rate*/ @@ -65,8 +63,6 @@ #define LTR501_ALS_DEF_PERIOD 500000 #define LTR501_PS_DEF_PERIOD 100000 -#define LTR501_REGMAP_NAME "ltr501_regmap" - #define LTR501_LUX_CONV(vis_coeff, vis_data, ir_coeff, ir_data) \ ((vis_coeff * vis_data) - (ir_coeff * ir_data)) @@ -1283,14 +1279,12 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) struct { u16 channels[3]; aligned_s64 ts; - } scan; + } scan = { }; __le16 als_buf[2]; u8 mask = 0; int j = 0; int ret, psdata; - memset(&scan, 0, sizeof(scan)); - /* figure out which data needs to be ready */ if (test_bit(0, indio_dev->active_scan_mask) || test_bit(1, indio_dev->active_scan_mask)) @@ -1404,11 +1398,11 @@ static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config ltr501_regmap_config = { - .name = LTR501_REGMAP_NAME, + .name = "ltr501_regmap", .reg_bits = 8, .val_bits = 8, .max_register = LTR501_MAX_REG, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = ltr501_is_volatile_reg, }; @@ -1626,7 +1620,7 @@ MODULE_DEVICE_TABLE(of, ltr501_of_match); static struct i2c_driver ltr501_driver = { .driver = { - .name = LTR501_DRV_NAME, + .name = "ltr501", .of_match_table = ltr501_of_match, .pm = pm_sleep_ptr(<r501_pm_ops), .acpi_match_table = ltr_acpi_match, diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index f4085020e03e..566f1bb8fe2a 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -1063,7 +1063,7 @@ static const struct regmap_config opt4060_regmap_config = { .name = "opt4060", .reg_bits = 8, .val_bits = 16, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .max_register = OPT4060_DEVICE_ID, .readable_reg = opt4060_readable_reg, .writeable_reg = opt4060_writable_reg, @@ -1083,7 +1083,7 @@ static irqreturn_t opt4060_trigger_handler(int irq, void *p) struct { u32 chan[OPT4060_NUM_CHANS]; aligned_s64 ts; - } raw; + } raw = { }; int i = 0; int chan, ret; @@ -1091,8 +1091,6 @@ static irqreturn_t opt4060_trigger_handler(int irq, void *p) if (iio_trigger_validate_own_device(idev->trig, idev)) opt4060_trigger_new_samples(idev); - memset(&raw, 0, sizeof(raw)); - iio_for_each_active_channel(idev, chan) { if (chan == OPT4060_ILLUM) ret = opt4060_calc_illuminance(chip, &raw.chan[i++]); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 92e7552f3e39..c50183f07240 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -69,8 +69,6 @@ #define RPR0521_DEFAULT_MEAS_TIME 0x06 /* ALS - 100ms, PXS - 100ms */ #define RPR0521_DRV_NAME "RPR0521" -#define RPR0521_IRQ_NAME "rpr0521_event" -#define RPR0521_REGMAP_NAME "rpr0521_regmap" #define RPR0521_SLEEP_DELAY_MS 2000 @@ -914,7 +912,7 @@ static bool rpr0521_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config rpr0521_regmap_config = { - .name = RPR0521_REGMAP_NAME, + .name = "rpr0521_regmap", .reg_bits = 8, .val_bits = 8, @@ -991,7 +989,7 @@ static int rpr0521_probe(struct i2c_client *client) ret = devm_request_threaded_irq(&client->dev, client->irq, rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - RPR0521_IRQ_NAME, indio_dev); + "rpr0521_event", indio_dev); if (ret < 0) { dev_err(&client->dev, "request irq %d for trigger0 failed\n", client->irq); diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index deada9ba4748..81dd2bfc22c0 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -46,8 +46,6 @@ #define STK3310_PS_MAX_VAL 0xFFFF #define STK3310_DRIVER_NAME "stk3310" -#define STK3310_REGMAP_NAME "stk3310_regmap" -#define STK3310_EVENT "stk3310_event" #define STK3310_SCALE_AVAILABLE "6.4 1.6 0.4 0.1" @@ -527,7 +525,7 @@ static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config stk3310_regmap_config = { - .name = STK3310_REGMAP_NAME, + .name = "stk3310_regmap", .reg_bits = 8, .val_bits = 8, .max_register = STK3310_MAX_REG, @@ -643,7 +641,7 @@ static int stk3310_probe(struct i2c_client *client) stk3310_irq_event_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - STK3310_EVENT, indio_dev); + "stk3310_event", indio_dev); if (ret < 0) { dev_err(&client->dev, "request irq %d failed\n", client->irq); diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index b2bede9d3daa..01bc99564f98 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -23,8 +23,6 @@ #include <linux/iio/triggered_buffer.h> #define VCNL4035_DRV_NAME "vcnl4035" -#define VCNL4035_IRQ_NAME "vcnl4035_event" -#define VCNL4035_REGMAP_NAME "vcnl4035_regmap" /* Device registers */ #define VCNL4035_ALS_CONF 0x00 @@ -503,7 +501,7 @@ static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config vcnl4035_regmap_config = { - .name = VCNL4035_REGMAP_NAME, + .name = "vcnl4035_regmap", .reg_bits = 8, .val_bits = 16, .max_register = VCNL4035_DEV_ID, @@ -545,7 +543,7 @@ static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) ret = devm_request_threaded_irq(&data->client->dev, data->client->irq, NULL, vcnl4035_drdy_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, - VCNL4035_IRQ_NAME, indio_dev); + "vcnl4035_event", indio_dev); if (ret < 0) dev_err(&data->client->dev, "request irq %d for trigger0 failed\n", data->client->irq); diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index 473a9c3e32a3..0945f146bedb 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -892,9 +892,7 @@ static irqreturn_t veml6030_trigger_handler(int irq, void *p) struct { u16 chans[2]; aligned_s64 timestamp; - } scan; - - memset(&scan, 0, sizeof(scan)); + } scan = { }; iio_for_each_active_channel(iio, ch) { ret = regmap_read(data->regmap, VEML6030_REG_DATA(ch), diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c index 1e5e9bf2935f..1dba1b949cc3 100644 --- a/drivers/iio/light/zopt2201.c +++ b/drivers/iio/light/zopt2201.c @@ -119,7 +119,7 @@ struct zopt2201_scale { u8 res; /* resolution register value */ }; -static struct zopt2201_scale zopt2201_scale_als[] = { +static const struct zopt2201_scale zopt2201_scale_als[] = { { 19, 200000, 0, 5 }, { 6, 400000, 1, 5 }, { 3, 200000, 2, 5 }, @@ -144,7 +144,7 @@ static struct zopt2201_scale zopt2201_scale_als[] = { { 0, 8333, 4, 0 }, }; -static struct zopt2201_scale zopt2201_scale_uvb[] = { +static const struct zopt2201_scale zopt2201_scale_uvb[] = { { 0, 460800, 0, 5 }, { 0, 153600, 1, 5 }, { 0, 76800, 2, 5 }, @@ -347,7 +347,7 @@ static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain) } static int zopt2201_write_scale_by_idx(struct zopt2201_data *data, int idx, - struct zopt2201_scale *zopt2201_scale_array) + const struct zopt2201_scale *zopt2201_scale_array) { int ret; diff --git a/drivers/iio/magnetometer/af8133j.c b/drivers/iio/magnetometer/af8133j.c index 192ba2da94e2..b1768c3aa8f3 100644 --- a/drivers/iio/magnetometer/af8133j.c +++ b/drivers/iio/magnetometer/af8133j.c @@ -361,11 +361,9 @@ static irqreturn_t af8133j_trigger_handler(int irq, void *p) struct { __le16 values[3]; aligned_s64 timestamp; - } sample; + } sample = { }; int ret; - memset(&sample, 0, sizeof(sample)); - ret = af8133j_read_measurement(data, sample.values); if (ret) goto out_done; diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index f9c51ceae011..761daead5ada 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -28,9 +28,6 @@ #include "bmc150_magn.h" -#define BMC150_MAGN_DRV_NAME "bmc150_magn" -#define BMC150_MAGN_IRQ_NAME "bmc150_magn_event" - #define BMC150_MAGN_REG_CHIP_ID 0x40 #define BMC150_MAGN_CHIP_ID_VAL 0x32 @@ -918,7 +915,7 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, iio_trigger_generic_data_rdy_poll, NULL, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - BMC150_MAGN_IRQ_NAME, + "bmc150_magn_event", data->dready_trig); if (ret < 0) { dev_err(dev, "request irq %d failed\n", irq); diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index e08a57cd6de2..f3d48d03f7c3 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -21,7 +21,6 @@ #include <linux/iio/sysfs.h> #define MMC35240_DRV_NAME "mmc35240" -#define MMC35240_REGMAP_NAME "mmc35240_regmap" #define MMC35240_REG_XOUT_L 0x00 #define MMC35240_REG_XOUT_H 0x01 @@ -463,7 +462,7 @@ static const struct reg_default mmc35240_reg_defaults[] = { }; static const struct regmap_config mmc35240_regmap_config = { - .name = MMC35240_REGMAP_NAME, + .name = "mmc35240_regmap", .reg_bits = 8, .val_bits = 8, diff --git a/drivers/iio/potentiometer/ds1803.c b/drivers/iio/potentiometer/ds1803.c index 5761f69c538a..8a64d93f7e7b 100644 --- a/drivers/iio/potentiometer/ds1803.c +++ b/drivers/iio/potentiometer/ds1803.c @@ -13,7 +13,6 @@ */ #include <linux/err.h> -#include <linux/export.h> #include <linux/i2c.h> #include <linux/iio/iio.h> #include <linux/module.h> diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c index 9082b559d029..ad082827aad5 100644 --- a/drivers/iio/potentiometer/mcp4131.c +++ b/drivers/iio/potentiometer/mcp4131.c @@ -33,7 +33,6 @@ #include <linux/cache.h> #include <linux/err.h> -#include <linux/export.h> #include <linux/iio/iio.h> #include <linux/iio/types.h> #include <linux/module.h> diff --git a/drivers/iio/pressure/abp060mg.c b/drivers/iio/pressure/abp060mg.c index a0d956c3e254..699b0fd64985 100644 --- a/drivers/iio/pressure/abp060mg.c +++ b/drivers/iio/pressure/abp060mg.c @@ -35,7 +35,7 @@ struct abp_config { int max; }; -static struct abp_config abp_config[] = { +static const struct abp_config abp_config[] = { /* mbar & kPa variants */ [ABP006KG] = { .min = 0, .max = 6000 }, [ABP010KG] = { .min = 0, .max = 10000 }, @@ -165,7 +165,7 @@ static const struct iio_info abp060mg_info = { static void abp060mg_init_device(struct iio_dev *indio_dev, unsigned long id) { struct abp_state *state = iio_priv(indio_dev); - struct abp_config *cfg = &abp_config[id]; + const struct abp_config *cfg = &abp_config[id]; state->scale = cfg->max - cfg->min; state->offset = -ABP060MG_MIN_COUNTS; diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index f37f20776c89..74505c9ec1a0 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1234,12 +1234,9 @@ static irqreturn_t bme280_trigger_handler(int irq, void *p) s32 comp_temp; u32 comp_humidity; aligned_s64 timestamp; - } buffer; + } buffer = { }; /* Don't leak uninitialized stack to userspace. */ int ret; - /* Don't leak uninitialized stack to userspace. */ - memset(&buffer, 0, sizeof(buffer)); - guard(mutex)(&data->lock); /* Burst read data registers */ diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c index 48afe5c94000..6a13cf2eaf50 100644 --- a/drivers/iio/pressure/dlhl60d.c +++ b/drivers/iio/pressure/dlhl60d.c @@ -32,35 +32,31 @@ /* DLH timings */ #define DLH_SINGLE_DUT_MS 5 -enum dhl_ids { - dlhl60d, - dlhl60g, -}; - struct dlh_info { + const char *name; /* chip name */ u8 osdig; /* digital offset factor */ unsigned int fss; /* full scale span (inch H2O) */ }; struct dlh_state { struct i2c_client *client; - struct dlh_info info; + const struct dlh_info *info; bool use_interrupt; struct completion completion; u8 rx_buf[DLH_NUM_READ_BYTES]; }; -static struct dlh_info dlh_info_tbl[] = { - [dlhl60d] = { - .osdig = 2, - .fss = 120, - }, - [dlhl60g] = { - .osdig = 10, - .fss = 60, - }, +static const struct dlh_info dlhl60d_info = { + .name = "dlhl60d", + .osdig = 2, + .fss = 120, }; +static const struct dlh_info dlhl60g_info = { + .name = "dlhl60g", + .osdig = 10, + .fss = 60, +}; static int dlh_cmd_start_single(struct dlh_state *st) { @@ -170,7 +166,7 @@ static int dlh_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (channel->type) { case IIO_PRESSURE: - tmp = div_s64(125LL * st->info.fss * 24909 * 100, + tmp = div_s64(125LL * st->info->fss * 24909 * 100, 1 << DLH_NUM_PR_BITS); tmp = div_s64_rem(tmp, 1000000000LL, &rem); *value = tmp; @@ -188,8 +184,8 @@ static int dlh_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OFFSET: switch (channel->type) { case IIO_PRESSURE: - *value = -125 * st->info.fss * 24909; - *value2 = 100 * st->info.osdig * 100000; + *value = -125 * st->info->fss * 24909; + *value2 = 100 * st->info->osdig * 100000; return IIO_VAL_FRACTIONAL; case IIO_TEMP: @@ -281,7 +277,6 @@ static irqreturn_t dlh_interrupt(int irq, void *private) static int dlh_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct dlh_state *st; struct iio_dev *indio_dev; int ret; @@ -302,11 +297,11 @@ static int dlh_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); st = iio_priv(indio_dev); - st->info = dlh_info_tbl[id->driver_data]; + st->info = i2c_get_match_data(client); st->client = client; st->use_interrupt = false; - indio_dev->name = id->name; + indio_dev->name = st->info->name; indio_dev->info = &dlh_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = dlh_channels; @@ -316,7 +311,7 @@ static int dlh_probe(struct i2c_client *client) ret = devm_request_threaded_irq(&client->dev, client->irq, dlh_interrupt, NULL, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - id->name, indio_dev); + st->info->name, indio_dev); if (ret) { dev_err(&client->dev, "failed to allocate threaded irq"); return ret; @@ -341,15 +336,15 @@ static int dlh_probe(struct i2c_client *client) } static const struct of_device_id dlh_of_match[] = { - { .compatible = "asc,dlhl60d" }, - { .compatible = "asc,dlhl60g" }, + { .compatible = "asc,dlhl60d", .data = &dlhl60d_info }, + { .compatible = "asc,dlhl60g", .data = &dlhl60g_info }, { } }; MODULE_DEVICE_TABLE(of, dlh_of_match); static const struct i2c_device_id dlh_id[] = { - { "dlhl60d", dlhl60d }, - { "dlhl60g", dlhl60g }, + { "dlhl60d", (kernel_ulong_t)&dlhl60d_info }, + { "dlhl60g", (kernel_ulong_t)&dlhl60g_info }, { } }; MODULE_DEVICE_TABLE(i2c, dlh_id); diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index d6715997f137..579da60ef441 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -160,7 +160,7 @@ static irqreturn_t mpl3115_trigger_handler(int irq, void *p) * of the buffer may be either 16 or 32-bits. As such we cannot * use a simple structure definition to express this data layout. */ - u8 buffer[16] __aligned(8); + u8 buffer[16] __aligned(8) = { }; int ret, pos = 0; mutex_lock(&data->lock); @@ -170,7 +170,6 @@ static irqreturn_t mpl3115_trigger_handler(int irq, void *p) goto done; } - memset(buffer, 0, sizeof(buffer)); if (test_bit(0, indio_dev->active_scan_mask)) { ret = i2c_smbus_read_i2c_block_data(data->client, MPL3115_OUT_PRESS, 3, &buffer[pos]); diff --git a/drivers/iio/pressure/mprls0025pa_i2c.c b/drivers/iio/pressure/mprls0025pa_i2c.c index 1a48f8d43d71..79811fd4a02b 100644 --- a/drivers/iio/pressure/mprls0025pa_i2c.c +++ b/drivers/iio/pressure/mprls0025pa_i2c.c @@ -44,10 +44,7 @@ static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused) { int ret; struct i2c_client *client = to_i2c_client(data->dev); - u8 wdata[MPR_PKT_SYNC_LEN]; - - memset(wdata, 0, sizeof(wdata)); - wdata[0] = cmd; + u8 wdata[MPR_PKT_SYNC_LEN] = { cmd }; ret = i2c_master_send(client, wdata, MPR_PKT_SYNC_LEN); if (ret < 0) diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 1640aa3717ed..6eef37c0952d 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -583,11 +583,9 @@ static int zpa2326_fill_sample_buffer(struct iio_dev *indio_dev, u32 pressure; u16 temperature; aligned_s64 timestamp; - } sample; + } sample = { }; int err; - memset(&sample, 0, sizeof(sample)); - if (test_bit(0, indio_dev->active_scan_mask)) { /* Get current pressure from hardware FIFO. */ err = zpa2326_dequeue_pressure(indio_dev, &sample.pressure); diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index a562a78b7d0d..6070974c2c85 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -32,6 +32,15 @@ config CROS_EC_MKBP_PROXIMITY To compile this driver as a module, choose M here: the module will be called cros_ec_mkbp_proximity. +config D3323AA + tristate "Nicera (Nippon Ceramic Co.) D3-323-AA PIR sensor" + depends on GPIOLIB + help + Say Y here to build a driver for the Nicera D3-323-AA PIR sensor. + + To compile this driver as a module, choose M here: the module will be + called d3323aa. + config HX9023S tristate "TYHX HX9023S SAR sensor" select IIO_BUFFER diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index c5e76995764a..152034d38c49 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -6,6 +6,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o +obj-$(CONFIG_D3323AA) += d3323aa.o obj-$(CONFIG_HX9023S) += hx9023s.o obj-$(CONFIG_IRSD200) += irsd200.o obj-$(CONFIG_ISL29501) += isl29501.o diff --git a/drivers/iio/proximity/d3323aa.c b/drivers/iio/proximity/d3323aa.c new file mode 100644 index 000000000000..d4c3dbea9bb0 --- /dev/null +++ b/drivers/iio/proximity/d3323aa.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Nicera D3-323-AA PIR sensor. + * + * Copyright (C) 2025 Axis Communications AB + */ + +#include <linux/bitmap.h> +#include <linux/cleanup.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> + +/* + * Register bitmap. + * For some reason the first bit is denoted as F37 in the datasheet, the second + * as F38 and so on. Note the gap between F60 and F64. + */ +#define D3323AA_REG_BIT_SLAVEA1 0 /* F37. */ +#define D3323AA_REG_BIT_SLAVEA2 1 /* F38. */ +#define D3323AA_REG_BIT_SLAVEA3 2 /* F39. */ +#define D3323AA_REG_BIT_SLAVEA4 3 /* F40. */ +#define D3323AA_REG_BIT_SLAVEA5 4 /* F41. */ +#define D3323AA_REG_BIT_SLAVEA6 5 /* F42. */ +#define D3323AA_REG_BIT_SLAVEA7 6 /* F43. */ +#define D3323AA_REG_BIT_SLAVEA8 7 /* F44. */ +#define D3323AA_REG_BIT_SLAVEA9 8 /* F45. */ +#define D3323AA_REG_BIT_SLAVEA10 9 /* F46. */ +#define D3323AA_REG_BIT_DETLVLABS0 10 /* F47. */ +#define D3323AA_REG_BIT_DETLVLABS1 11 /* F48. */ +#define D3323AA_REG_BIT_DETLVLABS2 12 /* F49. */ +#define D3323AA_REG_BIT_DETLVLABS3 13 /* F50. */ +#define D3323AA_REG_BIT_DETLVLABS4 14 /* F51. */ +#define D3323AA_REG_BIT_DETLVLABS5 15 /* F52. */ +#define D3323AA_REG_BIT_DETLVLABS6 16 /* F53. */ +#define D3323AA_REG_BIT_DETLVLABS7 17 /* F54. */ +#define D3323AA_REG_BIT_DSLP 18 /* F55. */ +#define D3323AA_REG_BIT_FSTEP0 19 /* F56. */ +#define D3323AA_REG_BIT_FSTEP1 20 /* F57. */ +#define D3323AA_REG_BIT_FILSEL0 21 /* F58. */ +#define D3323AA_REG_BIT_FILSEL1 22 /* F59. */ +#define D3323AA_REG_BIT_FILSEL2 23 /* F60. */ +#define D3323AA_REG_BIT_FDSET 24 /* F64. */ +#define D3323AA_REG_BIT_F65 25 +#define D3323AA_REG_BIT_F87 (D3323AA_REG_BIT_F65 + (87 - 65)) + +#define D3323AA_REG_NR_BITS (D3323AA_REG_BIT_F87 - D3323AA_REG_BIT_SLAVEA1 + 1) +#define D3323AA_THRESH_REG_NR_BITS \ + (D3323AA_REG_BIT_DETLVLABS7 - D3323AA_REG_BIT_DETLVLABS0 + 1) +#define D3323AA_FILTER_TYPE_NR_BITS \ + (D3323AA_REG_BIT_FILSEL2 - D3323AA_REG_BIT_FILSEL0 + 1) +#define D3323AA_FILTER_GAIN_REG_NR_BITS \ + (D3323AA_REG_BIT_FSTEP1 - D3323AA_REG_BIT_FSTEP0 + 1) + +#define D3323AA_THRESH_DEFAULT_VAL 56 +#define D3323AA_FILTER_GAIN_DEFAULT_IDX 1 +#define D3323AA_LP_FILTER_FREQ_DEFAULT_IDX 1 + +/* + * The pattern is 0b01101, but store it reversed (0b10110) due to writing from + * LSB on the wire (c.f. d3323aa_write_settings()). + */ +#define D3323AA_SETTING_END_PATTERN 0x16 +#define D3323AA_SETTING_END_PATTERN_NR_BITS 5 + +/* + * Device should be ready for configuration after this many milliseconds. + * Datasheet mentions "approx. 1.2 s". Measurements show around 1.23 s, + * therefore add 100 ms of slack. + */ +#define D3323AA_RESET_TIMEOUT (1200 + 100) + +/* + * The configuration of the device (write and read) should be done within this + * many milliseconds. + */ +#define D3323AA_CONFIG_TIMEOUT 1400 + +/* Number of IRQs needed for configuration stage after reset. */ +#define D3323AA_IRQ_RESET_COUNT 2 + +/* + * High-pass filter cutoff frequency for the band-pass filter. There is a + * corresponding low-pass cutoff frequency for each of the filter types + * (denoted A, B, C and D in the datasheet). The index in this array matches + * that corresponding value in d3323aa_lp_filter_freq. + * Note that this represents a fractional value (e.g. the first value + * corresponds to 40 / 100 = 0.4 Hz). + */ +static const int d3323aa_hp_filter_freq[][2] = { + { 40, 100 }, + { 30, 100 }, + { 30, 100 }, + { 1, 100 }, +}; + +/* + * Low-pass filter cutoff frequency for the band-pass filter. There is a + * corresponding high-pass cutoff frequency for each of the filter types + * (denoted A, B, C and D in the datasheet). The index in this array matches + * that corresponding value in d3323aa_hp_filter_freq. + * Note that this represents a fractional value (e.g. the first value + * corresponds to 27 / 10 = 2.7 Hz). + */ +static const int d3323aa_lp_filter_freq[][2] = { + { 27, 10 }, + { 15, 10 }, + { 5, 1 }, + { 100, 1 }, +}; + +/* + * Register bitmap values for filter types (denoted A, B, C and D in the + * datasheet). The index in this array matches the corresponding value in + * d3323aa_lp_filter_freq (which in turn matches d3323aa_hp_filter_freq). For + * example, the first value 7 corresponds to 2.7 Hz low-pass and 0.4 Hz + * high-pass cutoff frequency. + */ +static const int d3323aa_lp_filter_regval[] = { + 7, + 0, + 1, + 2, +}; + +/* + * This is denoted as "step" in datasheet and corresponds to the gain at peak + * for the band-pass filter. The index in this array is the corresponding index + * in d3323aa_filter_gain_regval for the register bitmap value. + */ +static const int d3323aa_filter_gain[] = { 1, 2, 3 }; + +/* + * Register bitmap values for the filter gain. The index in this array is the + * corresponding index in d3323aa_filter_gain for the gain value. + */ +static const u8 d3323aa_filter_gain_regval[] = { 1, 3, 0 }; + +struct d3323aa_data { + struct completion reset_completion; + /* + * Since the setup process always requires a complete write of _all_ + * the state variables, we need to synchronize them with a lock. + */ + struct mutex statevar_lock; + + struct device *dev; + + /* Supply voltage. */ + struct regulator *regulator_vdd; + /* Input clock or output detection signal (Vout). */ + struct gpio_desc *gpiod_clkin_detectout; + /* Input (setting) or output data. */ + struct gpio_desc *gpiod_data; + + /* + * We only need the low-pass cutoff frequency to unambiguously choose + * the type of band-pass filter. For example, both filter type B and C + * have 0.3 Hz as high-pass cutoff frequency (see + * d3323aa_hp_filter_freq). + */ + size_t lp_filter_freq_idx; + size_t filter_gain_idx; + u8 detect_thresh; + u8 irq_reset_count; + + /* Indicator for operational mode (configuring or detecting). */ + bool detecting; +}; + +static int d3323aa_read_settings(struct iio_dev *indio_dev, + unsigned long *regbitmap) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + size_t i; + int ret; + + /* Bit bang the clock and data pins. */ + ret = gpiod_direction_output(data->gpiod_clkin_detectout, 0); + if (ret) + return ret; + + ret = gpiod_direction_input(data->gpiod_data); + if (ret) + return ret; + + dev_dbg(data->dev, "Reading settings...\n"); + + for (i = 0; i < D3323AA_REG_NR_BITS; ++i) { + /* Clock frequency needs to be 1 kHz. */ + gpiod_set_value(data->gpiod_clkin_detectout, 1); + udelay(500); + + /* The data seems to change when clock signal is high. */ + if (gpiod_get_value(data->gpiod_data)) + set_bit(i, regbitmap); + + gpiod_set_value(data->gpiod_clkin_detectout, 0); + udelay(500); + } + + /* The first bit (F37) is just dummy data. Discard it. */ + clear_bit(0, regbitmap); + + /* Datasheet says to wait 30 ms after reading the settings. */ + msleep(30); + + return 0; +} + +static int d3323aa_write_settings(struct iio_dev *indio_dev, + unsigned long *written_regbitmap) +{ +#define REGBITMAP_LEN \ + (D3323AA_REG_NR_BITS + D3323AA_SETTING_END_PATTERN_NR_BITS) + DECLARE_BITMAP(regbitmap, REGBITMAP_LEN); + struct d3323aa_data *data = iio_priv(indio_dev); + size_t i; + int ret; + + /* Build the register bitmap. */ + bitmap_zero(regbitmap, REGBITMAP_LEN); + bitmap_write(regbitmap, data->detect_thresh, D3323AA_REG_BIT_DETLVLABS0, + D3323AA_REG_BIT_DETLVLABS7 - D3323AA_REG_BIT_DETLVLABS0 + + 1); + bitmap_write(regbitmap, + d3323aa_filter_gain_regval[data->filter_gain_idx], + D3323AA_REG_BIT_FSTEP0, + D3323AA_REG_BIT_FSTEP1 - D3323AA_REG_BIT_FSTEP0 + 1); + bitmap_write(regbitmap, + d3323aa_lp_filter_regval[data->lp_filter_freq_idx], + D3323AA_REG_BIT_FILSEL0, + D3323AA_REG_BIT_FILSEL2 - D3323AA_REG_BIT_FILSEL0 + 1); + /* Compulsory end pattern. */ + bitmap_write(regbitmap, D3323AA_SETTING_END_PATTERN, + D3323AA_REG_NR_BITS, D3323AA_SETTING_END_PATTERN_NR_BITS); + + /* Bit bang the clock and data pins. */ + ret = gpiod_direction_output(data->gpiod_clkin_detectout, 0); + if (ret) + return ret; + + ret = gpiod_direction_output(data->gpiod_data, 0); + if (ret) + return ret; + + dev_dbg(data->dev, "Writing settings...\n"); + + /* First bit (F37) is not used when writing the register bitmap. */ + for (i = 1; i < REGBITMAP_LEN; ++i) { + gpiod_set_value(data->gpiod_data, test_bit(i, regbitmap)); + + /* Clock frequency needs to be 1 kHz. */ + gpiod_set_value(data->gpiod_clkin_detectout, 1); + udelay(500); + gpiod_set_value(data->gpiod_clkin_detectout, 0); + udelay(500); + } + + /* Datasheet says to wait 30 ms after writing the settings. */ + msleep(30); + + bitmap_copy(written_regbitmap, regbitmap, D3323AA_REG_NR_BITS); + + return 0; +} + +static irqreturn_t d3323aa_irq_handler(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct d3323aa_data *data = iio_priv(indio_dev); + enum iio_event_direction dir; + int val; + + val = gpiod_get_value(data->gpiod_clkin_detectout); + if (val < 0) { + dev_err_ratelimited(data->dev, + "Could not read from GPIO vout-clk (%d)\n", + val); + return IRQ_HANDLED; + } + + if (!data->detecting) { + /* Reset interrupt counting falling edges. */ + if (!val && ++data->irq_reset_count == D3323AA_IRQ_RESET_COUNT) + complete(&data->reset_completion); + + return IRQ_HANDLED; + } + + /* Detection interrupt. */ + dir = val ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, dir), + iio_get_time_ns(indio_dev)); + + return IRQ_HANDLED; +} + +static int d3323aa_reset(struct iio_dev *indio_dev) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + long time; + int ret; + + /* During probe() the regulator may already be disabled. */ + if (regulator_is_enabled(data->regulator_vdd)) { + ret = regulator_disable(data->regulator_vdd); + if (ret) + return ret; + } + + /* + * Datasheet says VDD needs to be low at least for 30 ms. Let's add a + * couple more to allow VDD to completely discharge as well. + */ + fsleep((30 + 5) * USEC_PER_MSEC); + + /* + * When later enabling VDD, the device will signal with + * D3323AA_IRQ_RESET_COUNT falling edges on Vout/CLK that it is now + * ready for configuration. Datasheet says that this should happen + * within D3323AA_RESET_TIMEOUT ms. Count these two edges within that + * timeout. + */ + data->irq_reset_count = 0; + reinit_completion(&data->reset_completion); + data->detecting = false; + + ret = gpiod_direction_input(data->gpiod_clkin_detectout); + if (ret) + return ret; + + dev_dbg(data->dev, "Resetting...\n"); + + ret = regulator_enable(data->regulator_vdd); + if (ret) + return ret; + + /* + * Wait for VDD to completely charge up. Measurements have shown that + * Vout/CLK signal slowly ramps up during this period. Thus, the digital + * signal will have bogus values. It is therefore necessary to wait + * before we can count the "real" falling edges. + */ + fsleep(2000); + + time = wait_for_completion_killable_timeout( + &data->reset_completion, + msecs_to_jiffies(D3323AA_RESET_TIMEOUT)); + if (time == 0) { + return -ETIMEDOUT; + } else if (time < 0) { + /* Got interrupted. */ + return time; + } + + dev_dbg(data->dev, "Reset completed\n"); + + return 0; +} + +static int d3323aa_setup(struct iio_dev *indio_dev, size_t lp_filter_freq_idx, + size_t filter_gain_idx, u8 detect_thresh) +{ + DECLARE_BITMAP(write_regbitmap, D3323AA_REG_NR_BITS); + DECLARE_BITMAP(read_regbitmap, D3323AA_REG_NR_BITS); + struct d3323aa_data *data = iio_priv(indio_dev); + unsigned long start_time; + int ret; + + ret = d3323aa_reset(indio_dev); + if (ret) { + if (ret != -ERESTARTSYS) + dev_err(data->dev, "Could not reset device (%d)\n", + ret); + + return ret; + } + + /* + * Datasheet says to wait 10 us before setting the configuration. + * Moreover, the total configuration should be done within + * D3323AA_CONFIG_TIMEOUT ms. Clock it. + */ + fsleep(10); + start_time = jiffies; + + ret = d3323aa_write_settings(indio_dev, write_regbitmap); + if (ret) { + dev_err(data->dev, "Could not write settings (%d)\n", ret); + return ret; + } + + ret = d3323aa_read_settings(indio_dev, read_regbitmap); + if (ret) { + dev_err(data->dev, "Could not read settings (%d)\n", ret); + return ret; + } + + if (time_is_before_jiffies(start_time + + msecs_to_jiffies(D3323AA_CONFIG_TIMEOUT))) { + dev_err(data->dev, "Could not set up configuration in time\n"); + return -EAGAIN; + } + + /* Check if settings were set successfully. */ + if (!bitmap_equal(write_regbitmap, read_regbitmap, + D3323AA_REG_NR_BITS)) { + dev_err(data->dev, "Settings data mismatch\n"); + return -EIO; + } + + /* Now in operational mode. */ + ret = gpiod_direction_input(data->gpiod_clkin_detectout); + if (ret) { + dev_err(data->dev, + "Could not set GPIO vout-clk as input (%d)\n", ret); + return ret; + } + + ret = gpiod_direction_input(data->gpiod_data); + if (ret) { + dev_err(data->dev, "Could not set GPIO data as input (%d)\n", + ret); + return ret; + } + + data->lp_filter_freq_idx = lp_filter_freq_idx; + data->filter_gain_idx = filter_gain_idx; + data->detect_thresh = detect_thresh; + data->detecting = true; + + dev_dbg(data->dev, "Setup done\n"); + + return 0; +} + +static int d3323aa_set_lp_filter_freq(struct iio_dev *indio_dev, const int val, + int val2) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + size_t idx; + + /* Truncate fractional part to one digit. */ + val2 /= 100000; + + for (idx = 0; idx < ARRAY_SIZE(d3323aa_lp_filter_freq); ++idx) { + int integer = d3323aa_lp_filter_freq[idx][0] / + d3323aa_lp_filter_freq[idx][1]; + int fract = d3323aa_lp_filter_freq[idx][0] % + d3323aa_lp_filter_freq[idx][1]; + + if (val == integer && val2 == fract) + break; + } + + if (idx == ARRAY_SIZE(d3323aa_lp_filter_freq)) + return -EINVAL; + + return d3323aa_setup(indio_dev, idx, data->filter_gain_idx, + data->detect_thresh); +} + +static int d3323aa_set_hp_filter_freq(struct iio_dev *indio_dev, const int val, + int val2) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + size_t idx; + + /* Truncate fractional part to two digits. */ + val2 /= 10000; + + for (idx = 0; idx < ARRAY_SIZE(d3323aa_hp_filter_freq); ++idx) { + int integer = d3323aa_hp_filter_freq[idx][0] / + d3323aa_hp_filter_freq[idx][1]; + int fract = d3323aa_hp_filter_freq[idx][0] % + d3323aa_hp_filter_freq[idx][1]; + + if (val == integer && val2 == fract) + break; + } + + if (idx == ARRAY_SIZE(d3323aa_hp_filter_freq)) + return -EINVAL; + + if (idx == data->lp_filter_freq_idx) { + /* Corresponding filter frequency already set. */ + return 0; + } + + if (idx == 1 && data->lp_filter_freq_idx == 2) { + /* + * The low-pass cutoff frequency is the only way to + * unambiguously choose the type of band-pass filter. For + * example, both filter type B (index 1) and C (index 2) have + * 0.3 Hz as high-pass cutoff frequency (see + * d3323aa_hp_filter_freq). Therefore, if one of these are + * requested _and_ the corresponding low-pass filter frequency + * is already set, we can't know which filter type is the wanted + * one. The low-pass filter frequency is the decider (i.e. in + * this case index 2). + */ + return 0; + } + + return d3323aa_setup(indio_dev, idx, data->filter_gain_idx, + data->detect_thresh); +} + +static int d3323aa_set_filter_gain(struct iio_dev *indio_dev, const int val) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + size_t idx; + + for (idx = 0; idx < ARRAY_SIZE(d3323aa_filter_gain); ++idx) { + if (d3323aa_filter_gain[idx] == val) + break; + } + + if (idx == ARRAY_SIZE(d3323aa_filter_gain)) + return -EINVAL; + + return d3323aa_setup(indio_dev, data->lp_filter_freq_idx, idx, + data->detect_thresh); +} + +static int d3323aa_set_threshold(struct iio_dev *indio_dev, const int val) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + + if (val > ((1 << D3323AA_THRESH_REG_NR_BITS) - 1)) + return -EINVAL; + + return d3323aa_setup(indio_dev, data->lp_filter_freq_idx, + data->filter_gain_idx, val); +} + +static int d3323aa_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *vals = (int *)d3323aa_hp_filter_freq; + *type = IIO_VAL_FRACTIONAL; + *length = 2 * ARRAY_SIZE(d3323aa_hp_filter_freq); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = (int *)d3323aa_lp_filter_freq; + *type = IIO_VAL_FRACTIONAL; + *length = 2 * ARRAY_SIZE(d3323aa_lp_filter_freq); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HARDWAREGAIN: + *vals = (int *)d3323aa_filter_gain; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(d3323aa_filter_gain); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int d3323aa_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->statevar_lock); + + switch (mask) { + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *val = d3323aa_hp_filter_freq[data->lp_filter_freq_idx][0]; + *val2 = d3323aa_hp_filter_freq[data->lp_filter_freq_idx][1]; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = d3323aa_lp_filter_freq[data->lp_filter_freq_idx][0]; + *val2 = d3323aa_lp_filter_freq[data->lp_filter_freq_idx][1]; + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_HARDWAREGAIN: + *val = d3323aa_filter_gain[data->filter_gain_idx]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int d3323aa_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct d3323aa_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->statevar_lock); + + switch (mask) { + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + return d3323aa_set_hp_filter_freq(indio_dev, val, val2); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return d3323aa_set_lp_filter_freq(indio_dev, val, val2); + case IIO_CHAN_INFO_HARDWAREGAIN: + return d3323aa_set_filter_gain(indio_dev, val); + default: + return -EINVAL; + } +} + +static int d3323aa_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 d3323aa_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->statevar_lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = data->detect_thresh; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int d3323aa_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 d3323aa_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->statevar_lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + return d3323aa_set_threshold(indio_dev, val); + default: + return -EINVAL; + } +} + +static const struct iio_info d3323aa_info = { + .read_avail = d3323aa_read_avail, + .read_raw = d3323aa_read_raw, + .write_raw = d3323aa_write_raw, + .read_event_value = d3323aa_read_event, + .write_event_value = d3323aa_write_event, +}; + +static const struct iio_event_spec d3323aa_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_chan_spec d3323aa_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + .event_spec = d3323aa_event_spec, + .num_event_specs = ARRAY_SIZE(d3323aa_event_spec), + }, +}; + +static void d3323aa_disable_regulator(void *indata) +{ + struct d3323aa_data *data = indata; + int ret; + + /* + * During probe() the regulator may be disabled. It is enabled during + * device setup (in d3323aa_reset(), where it is also briefly disabled). + * The check is therefore needed in order to have balanced + * regulator_enable/disable() calls. + */ + if (!regulator_is_enabled(data->regulator_vdd)) + return; + + ret = regulator_disable(data->regulator_vdd); + if (ret) + dev_err(data->dev, "Could not disable regulator (%d)\n", ret); +} + +static int d3323aa_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct d3323aa_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "Could not allocate iio device\n"); + + data = iio_priv(indio_dev); + data->dev = dev; + + init_completion(&data->reset_completion); + + ret = devm_mutex_init(dev, &data->statevar_lock); + if (ret) + return dev_err_probe(dev, ret, "Could not initialize mutex\n"); + + data->regulator_vdd = devm_regulator_get_exclusive(dev, "vdd"); + if (IS_ERR(data->regulator_vdd)) + return dev_err_probe(dev, PTR_ERR(data->regulator_vdd), + "Could not get regulator\n"); + + /* + * The regulator will be enabled for the first time during the + * device setup below (in d3323aa_reset()). However parameter changes + * from userspace can require a temporary disable of the regulator. + * To avoid complex handling of state, use a callback that will disable + * the regulator if it happens to be enabled at time of devm unwind. + */ + ret = devm_add_action_or_reset(dev, d3323aa_disable_regulator, data); + if (ret) + return ret; + + data->gpiod_clkin_detectout = + devm_gpiod_get(dev, "vout-clk", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_clkin_detectout)) + return dev_err_probe(dev, PTR_ERR(data->gpiod_clkin_detectout), + "Could not get GPIO vout-clk\n"); + + data->gpiod_data = devm_gpiod_get(dev, "data", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_data)) + return dev_err_probe(dev, PTR_ERR(data->gpiod_data), + "Could not get GPIO data\n"); + + ret = gpiod_to_irq(data->gpiod_clkin_detectout); + if (ret < 0) + return dev_err_probe(dev, ret, "Could not get IRQ\n"); + + /* + * Device signals with a rising or falling detection signal when the + * proximity data is above or below the threshold, respectively. + */ + ret = devm_request_irq(dev, ret, d3323aa_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(dev), indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Could not request IRQ\n"); + + ret = d3323aa_setup(indio_dev, D3323AA_LP_FILTER_FREQ_DEFAULT_IDX, + D3323AA_FILTER_GAIN_DEFAULT_IDX, + D3323AA_THRESH_DEFAULT_VAL); + if (ret) + return ret; + + indio_dev->info = &d3323aa_info; + indio_dev->name = "d3323aa"; + indio_dev->channels = d3323aa_channels; + indio_dev->num_channels = ARRAY_SIZE(d3323aa_channels); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "Could not register iio device\n"); + + return 0; +} + +static const struct of_device_id d3323aa_of_match[] = { + { + .compatible = "nicera,d3323aa", + }, + { } +}; +MODULE_DEVICE_TABLE(of, d3323aa_of_match); + +static struct platform_driver d3323aa_driver = { + .probe = d3323aa_probe, + .driver = { + .name = "d3323aa", + .of_match_table = d3323aa_of_match, + }, +}; +module_platform_driver(d3323aa_driver); + +MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>"); +MODULE_DESCRIPTION("Nicera D3-323-AA PIR sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/irsd200.c b/drivers/iio/proximity/irsd200.c index 0d30b91dbcbc..253e4aef22fb 100644 --- a/drivers/iio/proximity/irsd200.c +++ b/drivers/iio/proximity/irsd200.c @@ -763,10 +763,9 @@ static irqreturn_t irsd200_trigger_handler(int irq, void *pollf) struct { s16 channel; aligned_s64 ts; - } scan; + } scan = { }; int ret; - memset(&scan, 0, sizeof(scan)); ret = irsd200_read_data(data, &scan.channel); if (ret) goto end; @@ -885,9 +884,8 @@ static int irsd200_probe(struct i2c_client *client) ret = devm_regulator_get_enable(data->dev, "vdd"); if (ret) - return dev_err_probe( - data->dev, ret, - "Could not get and enable regulator (%d)\n", ret); + return dev_err_probe(data->dev, ret, + "Could not get and enable regulator\n"); ret = irsd200_setup(data); if (ret) @@ -905,17 +903,15 @@ static int irsd200_probe(struct i2c_client *client) ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, NULL, irsd200_trigger_handler, NULL); if (ret) - return dev_err_probe( - data->dev, ret, - "Could not setup iio triggered buffer (%d)\n", ret); + return dev_err_probe(data->dev, ret, + "Could not setup iio triggered buffer\n"); ret = devm_request_threaded_irq(data->dev, client->irq, NULL, irsd200_irq_thread, IRQF_TRIGGER_RISING | IRQF_ONESHOT, NULL, indio_dev); if (ret) - return dev_err_probe(data->dev, ret, - "Could not request irq (%d)\n", ret); + return dev_err_probe(data->dev, ret, "Could not request irq\n"); trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); @@ -929,14 +925,12 @@ static int irsd200_probe(struct i2c_client *client) ret = devm_iio_trigger_register(data->dev, trigger); if (ret) return dev_err_probe(data->dev, ret, - "Could not register iio trigger (%d)\n", - ret); + "Could not register iio trigger\n"); ret = devm_iio_device_register(data->dev, indio_dev); if (ret) return dev_err_probe(data->dev, ret, - "Could not register iio device (%d)\n", - ret); + "Could not register iio device\n"); return 0; } diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 8913da59dc73..05844f17a15f 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -27,7 +27,6 @@ #include <linux/iio/trigger_consumer.h> #define SX9500_DRIVER_NAME "sx9500" -#define SX9500_IRQ_NAME "sx9500_event" /* Register definitions. */ #define SX9500_REG_IRQ_SRC 0x00 @@ -938,7 +937,7 @@ static int sx9500_probe(struct i2c_client *client) ret = devm_request_threaded_irq(&client->dev, client->irq, sx9500_irq_handler, sx9500_irq_thread_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - SX9500_IRQ_NAME, indio_dev); + "sx9500_event", indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 31e77d9e0c90..7f417372566a 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -102,29 +102,29 @@ static u32 microamp_to_reg(u32 *val) return *val /= 10000; }; -static struct vcnl3020_property vcnl3020_led_current_property = { +static const struct vcnl3020_property vcnl3020_led_current_property = { .name = "vishay,led-current-microamp", .reg = VCNL_LED_CURRENT, .conversion_func = microamp_to_reg, }; static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data, - struct vcnl3020_property prop) + const struct vcnl3020_property *prop) { int rc; u32 val; - rc = device_property_read_u32(data->dev, prop.name, &val); + rc = device_property_read_u32(data->dev, prop->name, &val); if (rc) return 0; - if (prop.conversion_func) - prop.conversion_func(&val); + if (prop->conversion_func) + prop->conversion_func(&val); - rc = regmap_write(data->regmap, prop.reg, val); + rc = regmap_write(data->regmap, prop->reg, val); if (rc) { dev_err(data->dev, "Error (%d) setting property (%s)\n", - rc, prop.name); + rc, prop->name); } return rc; @@ -153,7 +153,7 @@ static int vcnl3020_init(struct vcnl3020_data *data) mutex_init(&data->lock); return vcnl3020_get_and_apply_property(data, - vcnl3020_led_current_property); + &vcnl3020_led_current_property); }; static bool vcnl3020_is_in_periodic_mode(struct vcnl3020_data *data) diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c index 0bc84f12cb34..c00a60cb31a5 100644 --- a/drivers/iio/resolver/ad2s1200.c +++ b/drivers/iio/resolver/ad2s1200.c @@ -21,7 +21,6 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#define DRV_NAME "ad2s1200" /* input clock on serial interface */ #define AD2S1200_HZ 8192000 @@ -192,7 +191,7 @@ MODULE_DEVICE_TABLE(spi, ad2s1200_id); static struct spi_driver ad2s1200_driver = { .driver = { - .name = DRV_NAME, + .name = "ad2s1200", .of_match_table = ad2s1200_of_match, }, .probe = ad2s1200_probe, diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 29bff9d8859d..10bd3f221929 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -254,11 +254,9 @@ static irqreturn_t tmp006_trigger_handler(int irq, void *p) struct { s16 channels[2]; aligned_s64 ts; - } scan; + } scan = { }; s32 ret; - memset(&scan, 0, sizeof(scan)); - ret = i2c_smbus_read_word_data(data->client, TMP006_VOBJECT); if (ret < 0) goto err; diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c index 2505ace440b4..c7bab18221c7 100644 --- a/drivers/iio/trigger/stm32-lptimer-trigger.c +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c @@ -9,6 +9,7 @@ * Inspired by Benjamin Gaignard's stm32-timer-trigger driver */ +#include <linux/export.h> #include <linux/iio/timer/stm32-lptim-trigger.h> #include <linux/mfd/stm32-lptimer.h> #include <linux/mod_devicetable.h> diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 925b864facca..3b9a3a6cbb25 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -6,6 +6,7 @@ * */ +#include <linux/export.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/timer/stm32-timer-trigger.h> diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index 1219f4f23d40..31dc4781abef 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -283,6 +283,15 @@ config INTERCONNECT_QCOM_SM7150 This is a driver for the Qualcomm Network-on-Chip on sm7150-based platforms. +config INTERCONNECT_QCOM_MILOS + tristate "Qualcomm Milos interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on Milos-based + platforms. + config INTERCONNECT_QCOM_SM8150 tristate "Qualcomm SM8150 interconnect driver" depends on INTERCONNECT_QCOM_RPMH_POSSIBLE diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 7887b1e8d69b..f16ac242eba5 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM) += interconnect_qcom.o interconnect_qcom-y := icc-common.o icc-bcm-voter-objs := bcm-voter.o +qnoc-milos-objs := milos.o qnoc-msm8909-objs := msm8909.o qnoc-msm8916-objs := msm8916.o qnoc-msm8937-objs := msm8937.o @@ -45,6 +46,7 @@ qnoc-x1e80100-objs := x1e80100.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o +obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8937) += qnoc-msm8937.o diff --git a/drivers/interconnect/qcom/milos.c b/drivers/interconnect/qcom/milos.c new file mode 100644 index 000000000000..167d479f7764 --- /dev/null +++ b/drivers/interconnect/qcom/milos.c @@ -0,0 +1,1931 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss <luca.weiss@fairphone.com> + */ + +#include <linux/device.h> +#include <linux/interconnect-provider.h> +#include <linux/interconnect.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <dt-bindings/interconnect/qcom,milos-rpmh.h> + +#include "bcm-voter.h" +#include "icc-common.h" +#include "icc-rpmh.h" + +static struct qcom_icc_node qhm_qup1; +static struct qcom_icc_node xm_ufs_mem; +static struct qcom_icc_node xm_usb3_0; +static struct qcom_icc_node qhm_qdss_bam; +static struct qcom_icc_node qhm_qspi; +static struct qcom_icc_node qhm_qup0; +static struct qcom_icc_node qxm_crypto; +static struct qcom_icc_node qxm_ipa; +static struct qcom_icc_node xm_qdss_etr_0; +static struct qcom_icc_node xm_qdss_etr_1; +static struct qcom_icc_node xm_sdc1; +static struct qcom_icc_node xm_sdc2; +static struct qcom_icc_node qup0_core_master; +static struct qcom_icc_node qup1_core_master; +static struct qcom_icc_node qsm_cfg; +static struct qcom_icc_node qnm_gemnoc_cnoc; +static struct qcom_icc_node qnm_gemnoc_pcie; +static struct qcom_icc_node alm_gpu_tcu; +static struct qcom_icc_node alm_sys_tcu; +static struct qcom_icc_node chm_apps; +static struct qcom_icc_node qnm_gpu; +static struct qcom_icc_node qnm_lpass_gemnoc; +static struct qcom_icc_node qnm_mdsp; +static struct qcom_icc_node qnm_mnoc_hf; +static struct qcom_icc_node qnm_mnoc_sf; +static struct qcom_icc_node qnm_nsp_gemnoc; +static struct qcom_icc_node qnm_pcie; +static struct qcom_icc_node qnm_snoc_gc; +static struct qcom_icc_node qnm_snoc_sf; +static struct qcom_icc_node qxm_wlan_q6; +static struct qcom_icc_node qxm_lpass_dsp; +static struct qcom_icc_node llcc_mc; +static struct qcom_icc_node qnm_camnoc_hf; +static struct qcom_icc_node qnm_camnoc_icp; +static struct qcom_icc_node qnm_camnoc_sf; +static struct qcom_icc_node qnm_mdp; +static struct qcom_icc_node qnm_video; +static struct qcom_icc_node qsm_hf_mnoc_cfg; +static struct qcom_icc_node qsm_sf_mnoc_cfg; +static struct qcom_icc_node qxm_nsp; +static struct qcom_icc_node qsm_pcie_anoc_cfg; +static struct qcom_icc_node xm_pcie3_0; +static struct qcom_icc_node xm_pcie3_1; +static struct qcom_icc_node qnm_aggre1_noc; +static struct qcom_icc_node qnm_aggre2_noc; +static struct qcom_icc_node qnm_apss_noc; +static struct qcom_icc_node qnm_cnoc_data; +static struct qcom_icc_node qxm_pimem; +static struct qcom_icc_node xm_gic; +static struct qcom_icc_node qns_a1noc_snoc; +static struct qcom_icc_node qns_a2noc_snoc; +static struct qcom_icc_node qup0_core_slave; +static struct qcom_icc_node qup1_core_slave; +static struct qcom_icc_node qhs_ahb2phy0; +static struct qcom_icc_node qhs_ahb2phy1; +static struct qcom_icc_node qhs_camera_cfg; +static struct qcom_icc_node qhs_clk_ctl; +static struct qcom_icc_node qhs_cpr_cx; +static struct qcom_icc_node qhs_cpr_mxa; +static struct qcom_icc_node qhs_crypto0_cfg; +static struct qcom_icc_node qhs_cx_rdpm; +static struct qcom_icc_node qhs_gpuss_cfg; +static struct qcom_icc_node qhs_imem_cfg; +static struct qcom_icc_node qhs_mss_cfg; +static struct qcom_icc_node qhs_mx_2_rdpm; +static struct qcom_icc_node qhs_mx_rdpm; +static struct qcom_icc_node qhs_pdm; +static struct qcom_icc_node qhs_qdss_cfg; +static struct qcom_icc_node qhs_qspi; +static struct qcom_icc_node qhs_qup0; +static struct qcom_icc_node qhs_qup1; +static struct qcom_icc_node qhs_sdc1; +static struct qcom_icc_node qhs_sdc2; +static struct qcom_icc_node qhs_tcsr; +static struct qcom_icc_node qhs_tlmm; +static struct qcom_icc_node qhs_ufs_mem_cfg; +static struct qcom_icc_node qhs_usb3_0; +static struct qcom_icc_node qhs_venus_cfg; +static struct qcom_icc_node qhs_vsense_ctrl_cfg; +static struct qcom_icc_node qhs_wlan_q6; +static struct qcom_icc_node qss_mnoc_hf_cfg; +static struct qcom_icc_node qss_mnoc_sf_cfg; +static struct qcom_icc_node qss_nsp_qtb_cfg; +static struct qcom_icc_node qss_pcie_anoc_cfg; +static struct qcom_icc_node qss_wlan_q6_throttle_cfg; +static struct qcom_icc_node srvc_cnoc_cfg; +static struct qcom_icc_node xs_qdss_stm; +static struct qcom_icc_node xs_sys_tcu_cfg; +static struct qcom_icc_node qhs_aoss; +static struct qcom_icc_node qhs_display_cfg; +static struct qcom_icc_node qhs_ipa; +static struct qcom_icc_node qhs_ipc_router; +static struct qcom_icc_node qhs_pcie0_cfg; +static struct qcom_icc_node qhs_pcie1_cfg; +static struct qcom_icc_node qhs_prng; +static struct qcom_icc_node qhs_tme_cfg; +static struct qcom_icc_node qss_apss; +static struct qcom_icc_node qss_cfg; +static struct qcom_icc_node qss_ddrss_cfg; +static struct qcom_icc_node qxs_imem; +static struct qcom_icc_node qxs_pimem; +static struct qcom_icc_node srvc_cnoc_main; +static struct qcom_icc_node xs_pcie_0; +static struct qcom_icc_node xs_pcie_1; +static struct qcom_icc_node qns_gem_noc_cnoc; +static struct qcom_icc_node qns_llcc; +static struct qcom_icc_node qns_pcie; +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc; +static struct qcom_icc_node ebi; +static struct qcom_icc_node qns_mem_noc_hf; +static struct qcom_icc_node qns_mem_noc_sf; +static struct qcom_icc_node srvc_mnoc_hf; +static struct qcom_icc_node srvc_mnoc_sf; +static struct qcom_icc_node qns_nsp_gemnoc; +static struct qcom_icc_node qns_pcie_mem_noc; +static struct qcom_icc_node srvc_pcie_aggre_noc; +static struct qcom_icc_node qns_gemnoc_gc; +static struct qcom_icc_node qns_gemnoc_sf; + +static struct qcom_icc_qosbox qhm_qup1_qos = { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qup1_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_qosbox xm_ufs_mem_qos = { + .num_ports = 1, + .port_offsets = { 0xf200 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_ufs_mem_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_qosbox xm_usb3_0_qos = { + .num_ports = 1, + .port_offsets = { 0x10000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_usb3_0_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a1noc_snoc }, +}; + +static struct qcom_icc_qosbox qhm_qdss_bam_qos = { + .num_ports = 1, + .port_offsets = { 0x14000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qdss_bam_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox qhm_qspi_qos = { + .num_ports = 1, + .port_offsets = { 0x12000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qspi_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox qhm_qup0_qos = { + .num_ports = 1, + .port_offsets = { 0x13000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .channels = 1, + .buswidth = 4, + .qosbox = &qhm_qup0_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox qxm_crypto_qos = { + .num_ports = 1, + .port_offsets = { 0x15000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .channels = 1, + .buswidth = 8, + .qosbox = &qxm_crypto_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox qxm_ipa_qos = { + .num_ports = 1, + .port_offsets = { 0x16000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .channels = 1, + .buswidth = 8, + .qosbox = &qxm_ipa_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox xm_qdss_etr_0_qos = { + .num_ports = 1, + .port_offsets = { 0x17000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_qdss_etr_0_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox xm_qdss_etr_1_qos = { + .num_ports = 1, + .port_offsets = { 0x18000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_qdss_etr_1_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox xm_sdc1_qos = { + .num_ports = 1, + .port_offsets = { 0x1a000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_sdc1 = { + .name = "xm_sdc1", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_sdc1_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_qosbox xm_sdc2_qos = { + .num_ports = 1, + .port_offsets = { 0x19000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_sdc2_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_a2noc_snoc }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qup0_core_slave }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qup1_core_slave }, +}; + +static struct qcom_icc_node qsm_cfg = { + .name = "qsm_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 35, + .link_nodes = (struct qcom_icc_node *[]) { &qhs_ahb2phy0, &qhs_ahb2phy1, + &qhs_camera_cfg, &qhs_clk_ctl, + &qhs_cpr_cx, &qhs_cpr_mxa, + &qhs_crypto0_cfg, &qhs_cx_rdpm, + &qhs_gpuss_cfg, &qhs_imem_cfg, + &qhs_mss_cfg, &qhs_mx_2_rdpm, + &qhs_mx_rdpm, &qhs_pdm, + &qhs_qdss_cfg, &qhs_qspi, + &qhs_qup0, &qhs_qup1, + &qhs_sdc1, &qhs_sdc2, + &qhs_tcsr, &qhs_tlmm, + &qhs_ufs_mem_cfg, &qhs_usb3_0, + &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, + &qhs_wlan_q6, &qss_mnoc_hf_cfg, + &qss_mnoc_sf_cfg, &qss_nsp_qtb_cfg, + &qss_pcie_anoc_cfg, &qss_wlan_q6_throttle_cfg, + &srvc_cnoc_cfg, &xs_qdss_stm, + &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 14, + .link_nodes = (struct qcom_icc_node *[]) { &qhs_aoss, &qhs_display_cfg, + &qhs_ipa, &qhs_ipc_router, + &qhs_pcie0_cfg, &qhs_pcie1_cfg, + &qhs_prng, &qhs_tme_cfg, + &qss_apss, &qss_cfg, + &qss_ddrss_cfg, &qxs_imem, + &qxs_pimem, &srvc_cnoc_main }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &xs_pcie_0, &xs_pcie_1 }, +}; + +static struct qcom_icc_qosbox alm_gpu_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0xf1000 }, + .prio = 1, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &alm_gpu_tcu_qos, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox alm_sys_tcu_qos = { + .num_ports = 1, + .port_offsets = { 0xf3000 }, + .prio = 6, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .channels = 1, + .buswidth = 8, + .qosbox = &alm_sys_tcu_qos, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .channels = 3, + .buswidth = 32, + .num_links = 3, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_gpu_qos = { + .num_ports = 2, + .port_offsets = { 0x31000, 0x71000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_gpu_qos, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_lpass_gemnoc_qos = { + .num_ports = 1, + .port_offsets = { 0xf5000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_lpass_gemnoc = { + .name = "qnm_lpass_gemnoc", + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_lpass_gemnoc_qos, + .num_links = 3, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .channels = 1, + .buswidth = 16, + .num_links = 3, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_mnoc_hf_qos = { + .num_ports = 2, + .port_offsets = { 0x33000, 0x73000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_hf_qos, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_mnoc_sf_qos = { + .num_ports = 2, + .port_offsets = { 0x35000, 0x75000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_mnoc_sf_qos, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_nsp_gemnoc_qos = { + .num_ports = 2, + .port_offsets = { 0x37000, 0x77000 }, + .prio = 0, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_nsp_gemnoc_qos, + .num_links = 3, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_qosbox qnm_pcie_qos = { + .num_ports = 1, + .port_offsets = { 0xf7000 }, + .prio = 2, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_pcie_qos, + .num_links = 2, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_snoc_gc_qos = { + .num_ports = 1, + .port_offsets = { 0xf9000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_snoc_gc = { + .name = "qnm_snoc_gc", + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_snoc_gc_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_llcc }, +}; + +static struct qcom_icc_qosbox qnm_snoc_sf_qos = { + .num_ports = 1, + .port_offsets = { 0xfb000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .channels = 1, + .buswidth = 16, + .qosbox = &qnm_snoc_sf_qos, + .num_links = 3, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qxm_wlan_q6 = { + .name = "qxm_wlan_q6", + .channels = 1, + .buswidth = 8, + .num_links = 3, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gem_noc_cnoc, &qns_llcc, + &qns_pcie }, +}; + +static struct qcom_icc_node qxm_lpass_dsp = { + .name = "qxm_lpass_dsp", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_lpass_ag_noc_gemnoc }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .channels = 2, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &ebi }, +}; + +static struct qcom_icc_qosbox qnm_camnoc_hf_qos = { + .num_ports = 2, + .port_offsets = { 0xa8000, 0xa9000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_camnoc_hf_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_qosbox qnm_camnoc_icp_qos = { + .num_ports = 1, + .port_offsets = { 0x2a000 }, + .prio = 5, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_camnoc_icp = { + .name = "qnm_camnoc_icp", + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_camnoc_icp_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_qosbox qnm_camnoc_sf_qos = { + .num_ports = 2, + .port_offsets = { 0x2b000, 0x2c000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .channels = 2, + .buswidth = 32, + .qosbox = &qnm_camnoc_sf_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_qosbox qnm_mdp_qos = { + .num_ports = 1, + .port_offsets = { 0xad000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .channels = 1, + .buswidth = 32, + .qosbox = &qnm_mdp_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_qosbox qnm_video_qos = { + .num_ports = 1, + .port_offsets = { 0x30000 }, + .prio = 0, + .urg_fwd = 1, + .prio_fwd_disable = 0, +}; + +static struct qcom_icc_node qnm_video = { + .name = "qnm_video", + .channels = 1, + .buswidth = 32, + .qosbox = &qnm_video_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_mem_noc_sf }, +}; + +static struct qcom_icc_node qsm_hf_mnoc_cfg = { + .name = "qsm_hf_mnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &srvc_mnoc_hf }, +}; + +static struct qcom_icc_node qsm_sf_mnoc_cfg = { + .name = "qsm_sf_mnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &srvc_mnoc_sf }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_node qsm_pcie_anoc_cfg = { + .name = "qsm_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &srvc_pcie_aggre_noc }, +}; + +static struct qcom_icc_qosbox xm_pcie3_0_qos = { + .num_ports = 1, + .port_offsets = { 0xb000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_pcie3_0_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_qosbox xm_pcie3_1_qos = { + .num_ports = 1, + .port_offsets = { 0xc000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_pcie3_1_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_qosbox qnm_apss_noc_qos = { + .num_ports = 1, + .port_offsets = { 0x1c000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_apss_noc = { + .name = "qnm_apss_noc", + .channels = 1, + .buswidth = 4, + .qosbox = &qnm_apss_noc_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_qosbox qnm_cnoc_data_qos = { + .num_ports = 1, + .port_offsets = { 0x1d000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qnm_cnoc_data = { + .name = "qnm_cnoc_data", + .channels = 1, + .buswidth = 8, + .qosbox = &qnm_cnoc_data_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_qosbox qxm_pimem_qos = { + .num_ports = 1, + .port_offsets = { 0x1e000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .channels = 1, + .buswidth = 8, + .qosbox = &qxm_pimem_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gemnoc_gc }, +}; + +static struct qcom_icc_qosbox xm_gic_qos = { + .num_ports = 1, + .port_offsets = { 0x1f000 }, + .prio = 2, + .urg_fwd = 0, + .prio_fwd_disable = 1, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .channels = 1, + .buswidth = 8, + .qosbox = &xm_gic_qos, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qns_gemnoc_gc }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxa = { + .name = "qhs_cpr_mxa", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cx_rdpm = { + .name = "qhs_cx_rdpm", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_2_rdpm = { + .name = "qhs_mx_2_rdpm", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_rdpm = { + .name = "qhs_mx_rdpm", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc1 = { + .name = "qhs_sdc1", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_wlan_q6 = { + .name = "qhs_wlan_q6", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_mnoc_hf_cfg = { + .name = "qss_mnoc_hf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qsm_hf_mnoc_cfg }, +}; + +static struct qcom_icc_node qss_mnoc_sf_cfg = { + .name = "qss_mnoc_sf_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qsm_sf_mnoc_cfg }, +}; + +static struct qcom_icc_node qss_nsp_qtb_cfg = { + .name = "qss_nsp_qtb_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_pcie_anoc_cfg = { + .name = "qss_pcie_anoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qsm_pcie_anoc_cfg }, +}; + +static struct qcom_icc_node qss_wlan_q6_throttle_cfg = { + .name = "qss_wlan_q6_throttle_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_cnoc_cfg = { + .name = "srvc_cnoc_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_apss = { + .name = "qss_apss", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qss_cfg = { + .name = "qss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qsm_cfg }, +}; + +static struct qcom_icc_node qss_ddrss_cfg = { + .name = "qss_ddrss_cfg", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_cnoc_main = { + .name = "srvc_cnoc_main", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_gemnoc_cnoc }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .channels = 2, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &llcc_mc }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_gemnoc_pcie }, +}; + +static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = { + .name = "qns_lpass_ag_noc_gemnoc", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_lpass_gemnoc }, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .channels = 2, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_mnoc_hf }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_mnoc_sf }, +}; + +static struct qcom_icc_node srvc_mnoc_hf = { + .name = "srvc_mnoc_hf", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_mnoc_sf = { + .name = "srvc_mnoc_sf", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .channels = 2, + .buswidth = 32, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_nsp_gemnoc }, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_pcie }, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc_gc = { + .name = "qns_gemnoc_gc", + .channels = 1, + .buswidth = 8, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_snoc_gc }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .channels = 1, + .buswidth = 16, + .num_links = 1, + .link_nodes = (struct qcom_icc_node *[]) { &qnm_snoc_sf }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .enable_mask = 0x1, + .num_nodes = 1, + .nodes = { &ebi, }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .enable_mask = 0x1, + .keepalive = true, + .num_nodes = 51, + .nodes = { &qsm_cfg, &qhs_ahb2phy0, + &qhs_ahb2phy1, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_cpr_cx, + &qhs_cpr_mxa, &qhs_crypto0_cfg, + &qhs_cx_rdpm, &qhs_gpuss_cfg, + &qhs_imem_cfg, &qhs_mss_cfg, + &qhs_mx_2_rdpm, &qhs_mx_rdpm, + &qhs_pdm, &qhs_qdss_cfg, + &qhs_qspi, &qhs_sdc1, + &qhs_sdc2, &qhs_tcsr, + &qhs_tlmm, &qhs_ufs_mem_cfg, + &qhs_usb3_0, &qhs_venus_cfg, + &qhs_vsense_ctrl_cfg, &qhs_wlan_q6, + &qss_mnoc_hf_cfg, &qss_mnoc_sf_cfg, + &qss_nsp_qtb_cfg, &qss_pcie_anoc_cfg, + &qss_wlan_q6_throttle_cfg, &srvc_cnoc_cfg, + &xs_qdss_stm, &xs_sys_tcu_cfg, + &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &qhs_aoss, &qhs_ipa, + &qhs_ipc_router, &qhs_pcie0_cfg, + &qhs_pcie1_cfg, &qhs_prng, + &qhs_tme_cfg, &qss_apss, + &qss_cfg, &qss_ddrss_cfg, + &qxs_imem, &qxs_pimem, + &srvc_cnoc_main, &xs_pcie_0, + &xs_pcie_1 }, +}; + +static struct qcom_icc_bcm bcm_cn1 = { + .name = "CN1", + .num_nodes = 3, + .nodes = { &qhs_qup0, &qhs_qup1, + &qhs_display_cfg }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .enable_mask = 0x1, + .num_nodes = 2, + .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .enable_mask = 0x1, + .num_nodes = 4, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp, + &qnm_camnoc_sf, &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .enable_mask = 0x1, + .num_nodes = 14, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &chm_apps, &qnm_gpu, + &qnm_mdsp, &qnm_mnoc_hf, + &qnm_mnoc_sf, &qnm_nsp_gemnoc, + &qnm_pcie, &qnm_snoc_gc, + &qnm_snoc_sf, &qxm_wlan_q6, + &qns_gem_noc_cnoc, &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 2, + .nodes = { &qns_gemnoc_gc, &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn1 = { + .name = "SN1", + .enable_mask = 0x1, + .num_nodes = 1, + .nodes = { &qxm_pimem }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_node * const aggre1_noc_nodes[] = { + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, +}; + +static const struct regmap_config milos_aggre1_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x16400, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_aggre1_noc = { + .config = &milos_aggre1_noc_regmap_config, + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node * const aggre2_noc_nodes[] = { + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_1] = &xm_sdc1, + [MASTER_SDCC_2] = &xm_sdc2, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, +}; + +static const struct regmap_config milos_aggre2_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f400, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_aggre2_noc = { + .config = &milos_aggre2_noc_regmap_config, + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, +}; + +static struct qcom_icc_node * const clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, +}; + +static const struct qcom_icc_desc milos_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const cnoc_cfg_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const cnoc_cfg_nodes[] = { + [MASTER_CNOC_CFG] = &qsm_cfg, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MXA_CFG] = &qhs_cpr_mxa, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CX_RDPM] = &qhs_cx_rdpm, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_MX_2_RDPM] = &qhs_mx_2_rdpm, + [SLAVE_MX_RDPM] = &qhs_mx_rdpm, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_SDC1] = &qhs_sdc1, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_WLAN] = &qhs_wlan_q6, + [SLAVE_CNOC_MNOC_HF_CFG] = &qss_mnoc_hf_cfg, + [SLAVE_CNOC_MNOC_SF_CFG] = &qss_mnoc_sf_cfg, + [SLAVE_NSP_QTB_CFG] = &qss_nsp_qtb_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg, + [SLAVE_WLAN_Q6_THROTTLE_CFG] = &qss_wlan_q6_throttle_cfg, + [SLAVE_SERVICE_CNOC_CFG] = &srvc_cnoc_cfg, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static const struct regmap_config milos_cnoc_cfg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x6e00, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_cnoc_cfg = { + .config = &milos_cnoc_cfg_regmap_config, + .nodes = cnoc_cfg_nodes, + .num_nodes = ARRAY_SIZE(cnoc_cfg_nodes), + .bcms = cnoc_cfg_bcms, + .num_bcms = ARRAY_SIZE(cnoc_cfg_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const cnoc_main_bcms[] = { + &bcm_cn0, + &bcm_cn1, +}; + +static struct qcom_icc_node * const cnoc_main_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_APPSS] = &qss_apss, + [SLAVE_CNOC_CFG] = &qss_cfg, + [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc_main, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, +}; + +static const struct regmap_config milos_cnoc_main_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x14400, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_cnoc_main = { + .config = &milos_cnoc_main_regmap_config, + .nodes = cnoc_main_nodes, + .num_nodes = ARRAY_SIZE(cnoc_main_nodes), + .bcms = cnoc_main_bcms, + .num_bcms = ARRAY_SIZE(cnoc_main_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh1, +}; + +static struct qcom_icc_node * const gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_WLAN_Q6] = &qxm_wlan_q6, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, +}; + +static const struct regmap_config milos_gem_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xff080, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_gem_noc = { + .config = &milos_gem_noc_regmap_config, + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_node * const lpass_ag_noc_nodes[] = { + [MASTER_LPASS_PROC] = &qxm_lpass_dsp, + [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc, +}; + +static const struct regmap_config milos_lpass_ag_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x17200, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_lpass_ag_noc = { + .config = &milos_lpass_ag_noc_regmap_config, + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, +}; + +static struct qcom_icc_node * const mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +static const struct qcom_icc_desc milos_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, +}; + +static struct qcom_icc_node * const mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_ICP] = &qnm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_VIDEO] = &qnm_video, + [MASTER_CNOC_MNOC_HF_CFG] = &qsm_hf_mnoc_cfg, + [MASTER_CNOC_MNOC_SF_CFG] = &qsm_sf_mnoc_cfg, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC_HF] = &srvc_mnoc_hf, + [SLAVE_SERVICE_MNOC_SF] = &srvc_mnoc_sf, +}; + +static const struct regmap_config milos_mmss_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xdb800, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_mmss_noc = { + .config = &milos_mmss_noc_regmap_config, + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node * const nsp_noc_nodes[] = { + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, +}; + +static const struct regmap_config milos_nsp_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xe080, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_nsp_noc = { + .config = &milos_nsp_noc_regmap_config, + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const pcie_anoc_bcms[] = { + &bcm_sn4, +}; + +static struct qcom_icc_node * const pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static const struct regmap_config milos_pcie_anoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x12400, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_pcie_anoc = { + .config = &milos_pcie_anoc_regmap_config, + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), + .alloc_dyn_id = true, +}; + +static struct qcom_icc_bcm * const system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn1, + &bcm_sn2, + &bcm_sn3, +}; + +static struct qcom_icc_node * const system_noc_nodes[] = { + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_APSS_NOC] = &qnm_apss_noc, + [MASTER_CNOC_SNOC] = &qnm_cnoc_data, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [SLAVE_SNOC_GEM_NOC_GC] = &qns_gemnoc_gc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, +}; + +static const struct regmap_config milos_system_noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x40000, + .fast_io = true, +}; + +static const struct qcom_icc_desc milos_system_noc = { + .config = &milos_system_noc_regmap_config, + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), + .alloc_dyn_id = true, +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,milos-aggre1-noc", .data = &milos_aggre1_noc }, + { .compatible = "qcom,milos-aggre2-noc", .data = &milos_aggre2_noc }, + { .compatible = "qcom,milos-clk-virt", .data = &milos_clk_virt }, + { .compatible = "qcom,milos-cnoc-cfg", .data = &milos_cnoc_cfg }, + { .compatible = "qcom,milos-cnoc-main", .data = &milos_cnoc_main }, + { .compatible = "qcom,milos-gem-noc", .data = &milos_gem_noc }, + { .compatible = "qcom,milos-lpass-ag-noc", .data = &milos_lpass_ag_noc }, + { .compatible = "qcom,milos-mc-virt", .data = &milos_mc_virt }, + { .compatible = "qcom,milos-mmss-noc", .data = &milos_mmss_noc }, + { .compatible = "qcom,milos-nsp-noc", .data = &milos_nsp_noc }, + { .compatible = "qcom,milos-pcie-anoc", .data = &milos_pcie_anoc }, + { .compatible = "qcom,milos-system-noc", .data = &milos_system_noc }, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qcom_icc_rpmh_probe, + .remove = qcom_icc_rpmh_remove, + .driver = { + .name = "qnoc-milos", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("Milos NoC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/interconnect/qcom/qcs615.c b/drivers/interconnect/qcom/qcs615.c index 7e59e91ce886..0549cfcbac64 100644 --- a/drivers/interconnect/qcom/qcs615.c +++ b/drivers/interconnect/qcom/qcs615.c @@ -342,15 +342,6 @@ static struct qcom_icc_node qnm_snoc_sf = { .links = { QCS615_SLAVE_LLCC }, }; -static struct qcom_icc_node ipa_core_master = { - .name = "ipa_core_master", - .id = QCS615_MASTER_IPA_CORE, - .channels = 1, - .buswidth = 8, - .num_links = 1, - .links = { QCS615_SLAVE_IPA_CORE }, -}; - static struct qcom_icc_node llcc_mc = { .name = "llcc_mc", .id = QCS615_MASTER_LLCC, @@ -942,14 +933,6 @@ static struct qcom_icc_node srvc_gemnoc = { .num_links = 0, }; -static struct qcom_icc_node ipa_core_slave = { - .name = "ipa_core_slave", - .id = QCS615_SLAVE_IPA_CORE, - .channels = 1, - .buswidth = 8, - .num_links = 0, -}; - static struct qcom_icc_node ebi = { .name = "ebi", .id = QCS615_SLAVE_EBI1, @@ -1113,12 +1096,6 @@ static struct qcom_icc_bcm bcm_cn1 = { &qhs_sdc1, &qhs_sdc2 }, }; -static struct qcom_icc_bcm bcm_ip0 = { - .name = "IP0", - .num_nodes = 1, - .nodes = { &ipa_core_slave }, -}; - static struct qcom_icc_bcm bcm_mc0 = { .name = "MC0", .keepalive = true, @@ -1260,7 +1237,6 @@ static struct qcom_icc_bcm * const aggre1_noc_bcms[] = { &bcm_qup0, &bcm_sn3, &bcm_sn14, - &bcm_ip0, }; static struct qcom_icc_node * const aggre1_noc_nodes[] = { @@ -1411,22 +1387,6 @@ static const struct qcom_icc_desc qcs615_gem_noc = { .num_bcms = ARRAY_SIZE(gem_noc_bcms), }; -static struct qcom_icc_bcm * const ipa_virt_bcms[] = { - &bcm_ip0, -}; - -static struct qcom_icc_node * const ipa_virt_nodes[] = { - [MASTER_IPA_CORE] = &ipa_core_master, - [SLAVE_IPA_CORE] = &ipa_core_slave, -}; - -static const struct qcom_icc_desc qcs615_ipa_virt = { - .nodes = ipa_virt_nodes, - .num_nodes = ARRAY_SIZE(ipa_virt_nodes), - .bcms = ipa_virt_bcms, - .num_bcms = ARRAY_SIZE(ipa_virt_bcms), -}; - static struct qcom_icc_bcm * const mc_virt_bcms[] = { &bcm_acv, &bcm_mc0, @@ -1525,8 +1485,6 @@ static const struct of_device_id qnoc_of_match[] = { .data = &qcs615_dc_noc}, { .compatible = "qcom,qcs615-gem-noc", .data = &qcs615_gem_noc}, - { .compatible = "qcom,qcs615-ipa-virt", - .data = &qcs615_ipa_virt}, { .compatible = "qcom,qcs615-mc-virt", .data = &qcs615_mc_virt}, { .compatible = "qcom,qcs615-mmss-noc", diff --git a/drivers/interconnect/qcom/sc8180x.c b/drivers/interconnect/qcom/sc8180x.c index a741badaa966..4dd1d2f2e821 100644 --- a/drivers/interconnect/qcom/sc8180x.c +++ b/drivers/interconnect/qcom/sc8180x.c @@ -1492,34 +1492,40 @@ static struct qcom_icc_bcm bcm_sh3 = { static struct qcom_icc_bcm bcm_sn0 = { .name = "SN0", + .num_nodes = 1, .nodes = { &slv_qns_gemnoc_sf } }; static struct qcom_icc_bcm bcm_sn1 = { .name = "SN1", + .num_nodes = 1, .nodes = { &slv_qxs_imem } }; static struct qcom_icc_bcm bcm_sn2 = { .name = "SN2", .keepalive = true, + .num_nodes = 1, .nodes = { &slv_qns_gemnoc_gc } }; static struct qcom_icc_bcm bcm_co2 = { .name = "CO2", + .num_nodes = 1, .nodes = { &mas_qnm_npu } }; static struct qcom_icc_bcm bcm_sn3 = { .name = "SN3", .keepalive = true, + .num_nodes = 2, .nodes = { &slv_srvc_aggre1_noc, &slv_qns_cnoc } }; static struct qcom_icc_bcm bcm_sn4 = { .name = "SN4", + .num_nodes = 1, .nodes = { &slv_qxs_pimem } }; diff --git a/drivers/interconnect/qcom/sc8280xp.c b/drivers/interconnect/qcom/sc8280xp.c index 0270f6c64481..c646cdf8a19b 100644 --- a/drivers/interconnect/qcom/sc8280xp.c +++ b/drivers/interconnect/qcom/sc8280xp.c @@ -48,6 +48,7 @@ static struct qcom_icc_node qnm_a1noc_cfg = { .id = SC8280XP_MASTER_A1NOC_CFG, .channels = 1, .buswidth = 4, + .num_links = 1, .links = { SC8280XP_SLAVE_SERVICE_A1NOC }, }; diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c index 9b8c40a6459a..c1367223e71a 100644 --- a/drivers/mcb/mcb-core.c +++ b/drivers/mcb/mcb-core.c @@ -107,7 +107,7 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr, { struct mcb_bus *bus = to_mcb_bus(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", bus->revision); + return sysfs_emit(buf, "%d\n", bus->revision); } static DEVICE_ATTR_RO(revision); @@ -116,7 +116,7 @@ static ssize_t model_show(struct device *dev, struct device_attribute *attr, { struct mcb_bus *bus = to_mcb_bus(dev); - return scnprintf(buf, PAGE_SIZE, "%c\n", bus->model); + return sysfs_emit(buf, "%c\n", bus->model); } static DEVICE_ATTR_RO(model); @@ -125,7 +125,7 @@ static ssize_t minor_show(struct device *dev, struct device_attribute *attr, { struct mcb_bus *bus = to_mcb_bus(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", bus->minor); + return sysfs_emit(buf, "%d\n", bus->minor); } static DEVICE_ATTR_RO(minor); @@ -134,7 +134,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, { struct mcb_bus *bus = to_mcb_bus(dev); - return scnprintf(buf, PAGE_SIZE, "%s\n", bus->name); + return sysfs_emit(buf, "%s\n", bus->name); } static DEVICE_ATTR_RO(name); diff --git a/drivers/misc/cardreader/rts5264.c b/drivers/misc/cardreader/rts5264.c index 06d7a8a95fd6..d050c9fff7ac 100644 --- a/drivers/misc/cardreader/rts5264.c +++ b/drivers/misc/cardreader/rts5264.c @@ -413,8 +413,8 @@ static void rts5264_init_from_hw(struct rtsx_pcr *pcr) { struct pci_dev *pdev = pcr->pci; u32 lval1, lval2, i; - u16 setting_reg1, setting_reg2; - u8 valid, efuse_valid, tmp; + u16 setting_reg1, setting_reg2, phy_val; + u8 valid, efuse_valid, tmp, efuse_len; rtsx_pci_write_register(pcr, RTS5264_REG_PME_FORCE_CTL, REG_EFUSE_POR | REG_EFUSE_POWER_MASK, @@ -433,6 +433,8 @@ static void rts5264_init_from_hw(struct rtsx_pcr *pcr) break; } rtsx_pci_read_register(pcr, RTS5264_EFUSE_READ_DATA, &tmp); + efuse_len = ((tmp & 0x70) >> 4); + pcr_dbg(pcr, "Load efuse len: 0x%x\n", efuse_len); efuse_valid = ((tmp & 0x0C) >> 2); pcr_dbg(pcr, "Load efuse valid: 0x%x\n", efuse_valid); @@ -445,6 +447,58 @@ static void rts5264_init_from_hw(struct rtsx_pcr *pcr) REG_EFUSE_POR, 0); pcr_dbg(pcr, "Disable efuse por!\n"); + if (is_version(pcr, PID_5264, RTS5264_IC_VER_B)) { + pci_write_config_dword(pdev, 0x718, 0x0007C000); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE, 0xFF, 0x88); + rtsx_pci_read_phy_register(pcr, _PHY_REV0, &phy_val); + phy_val &= 0xFFFD; + + if (efuse_len == 0) { + rtsx_pci_write_register(pcr, RTS5264_FW_CFG_INFO2, 0x0F, 0x0F); + rtsx_pci_write_register(pcr, 0xFF14, 0xFF, 0x79); + rtsx_pci_write_register(pcr, 0xFF15, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF16, 0xFF, 0x3D); + rtsx_pci_write_register(pcr, 0xFF17, 0xFF, 0xFE); + + rtsx_pci_write_register(pcr, 0xFF18, 0xFF, 0x5B); + rtsx_pci_write_register(pcr, 0xFF19, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF1A, 0xFF, 0x3E); + rtsx_pci_write_register(pcr, 0xFF1B, 0xFF, 0xFE); + + rtsx_pci_write_register(pcr, 0xFF1C, 0xFF, 0x00); + rtsx_pci_write_register(pcr, 0xFF1D, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF1E, 0xFF, 0x3F); + rtsx_pci_write_register(pcr, 0xFF1F, 0xFF, 0xFE); + + rtsx_pci_write_register(pcr, 0xFF20, 0xFF, 0x81); + rtsx_pci_write_register(pcr, 0xFF21, 0xFF, 0xFF); + rtsx_pci_write_register(pcr, 0xFF22, 0xFF, 0x3C); + rtsx_pci_write_register(pcr, 0xFF23, 0xFF, 0xFE); + } + + rtsx_pci_write_register(pcr, 0xFF24, 0xFF, 0x79); + rtsx_pci_write_register(pcr, 0xFF25, 0xFF, 0x5B); + rtsx_pci_write_register(pcr, 0xFF26, 0xFF, 0x00); + rtsx_pci_write_register(pcr, 0xFF27, 0xFF, 0x40); + + rtsx_pci_write_register(pcr, 0xFF28, 0xFF, (u8)phy_val); + rtsx_pci_write_register(pcr, 0xFF29, 0xFF, (u8)(phy_val >> 8)); + rtsx_pci_write_register(pcr, 0xFF2A, 0xFF, 0x19); + rtsx_pci_write_register(pcr, 0xFF2B, 0xFF, 0x40); + + rtsx_pci_write_register(pcr, 0xFF2C, 0xFF, 0x20); + rtsx_pci_write_register(pcr, 0xFF2D, 0xFF, 0xDA); + rtsx_pci_write_register(pcr, 0xFF2E, 0xFF, 0x0A); + rtsx_pci_write_register(pcr, 0xFF2F, 0xFF, 0x40); + + rtsx_pci_write_register(pcr, 0xFF30, 0xFF, 0x20); + rtsx_pci_write_register(pcr, 0xFF31, 0xFF, 0xD2); + rtsx_pci_write_register(pcr, 0xFF32, 0xFF, 0x0A); + rtsx_pci_write_register(pcr, 0xFF33, 0xFF, 0x40); + } else { + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE, 0x80, 0x80); + } + if (efuse_valid == 2 || efuse_valid == 3) { if (valid == 3) { /* Bypass efuse */ @@ -618,6 +672,9 @@ static int rts5264_optimize_phy(struct rtsx_pcr *pcr) rtsx_pci_update_phy(pcr, _PHY_REV0, 0x1FF, 0x3800); } + if (is_version(pcr, PID_5264, RTS5264_IC_VER_B)) + rtsx_pci_write_phy_register(pcr, 0x00, 0x5B79); + return 0; } @@ -820,7 +877,7 @@ int rts5264_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, SSC_DEPTH_MASK, ssc_depth); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); - if (is_version(pcr, 0x5264, IC_VER_A)) { + if (is_version(pcr, PID_5264, RTS5264_IC_VER_A)) { rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RTS5264_CARD_CLK_SRC2, RTS5264_REG_BIG_KVCO_A, 0); diff --git a/drivers/misc/cardreader/rts5264.h b/drivers/misc/cardreader/rts5264.h index e3cbbf2fe1a4..f3e81daa708d 100644 --- a/drivers/misc/cardreader/rts5264.h +++ b/drivers/misc/cardreader/rts5264.h @@ -61,6 +61,8 @@ /* DMACTL 0xFE2C */ #define RTS5264_DMA_PACK_SIZE_MASK 0x70 +#define RTS5264_FW_CFG_INFO2 0xFF52 + #define RTS5264_FW_CFG1 0xFF55 #define RTS5264_SYS_CLK_SEL_MCU_CLK (0x01<<7) #define RTS5264_CRC_CLK_SEL_MCU_CLK (0x01<<6) @@ -272,6 +274,10 @@ #define SD_LUN 1 #define SD_EXPRESS_LUN 2 +#define RTS5264_IC_VER_A 0 +#define RTS5264_IC_VER_B 2 +#define RTS5264_IC_VER_C 3 + int rts5264_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index a7b066c48740..f9952d76d6ed 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1236,7 +1236,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) else if (PCI_PID(pcr) == PID_5228) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, RTS5228_SSC_DEPTH_2M); - else if (is_version(pcr, 0x5264, IC_VER_A)) + else if (is_version(pcr, PID_5264, RTS5264_IC_VER_A)) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); else if (PCI_PID(pcr) == PID_5264) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index 148107a4547c..d007a4455ce5 100644 --- a/drivers/misc/cardreader/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -698,6 +698,12 @@ static void rtsx_usb_disconnect(struct usb_interface *intf) } #ifdef CONFIG_PM +static int rtsx_usb_resume_child(struct device *dev, void *data) +{ + pm_request_resume(dev); + return 0; +} + static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) { struct rtsx_ucr *ucr = @@ -713,8 +719,10 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) mutex_unlock(&ucr->dev_mutex); /* Defer the autosuspend if card exists */ - if (val & (SD_CD | MS_CD)) + if (val & (SD_CD | MS_CD)) { + device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); return -EAGAIN; + } } else { /* There is an ongoing operation*/ return -EAGAIN; @@ -724,12 +732,6 @@ static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -static int rtsx_usb_resume_child(struct device *dev, void *data) -{ - pm_request_resume(dev); - return 0; -} - static int rtsx_usb_resume(struct usb_interface *intf) { device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index cb1c4b8e7fd3..0bef5b93bd6d 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -37,6 +37,7 @@ config EEPROM_AT25 depends on SPI && SYSFS select NVMEM select NVMEM_SYSFS + select SPI_MEM help Enable this driver to get read/write support to most SPI EEPROMs and Cypress FRAMs, diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 595ceb9a7126..2d0492867054 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -7,8 +7,10 @@ */ #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/property.h> @@ -17,6 +19,7 @@ #include <linux/spi/eeprom.h> #include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> #include <linux/nvmem-provider.h> @@ -35,13 +38,12 @@ struct at25_data { struct spi_eeprom chip; - struct spi_device *spi; + struct spi_mem *spimem; struct mutex lock; unsigned addrlen; struct nvmem_config nvmem_config; struct nvmem_device *nvmem; u8 sernum[FM25_SN_LEN]; - u8 command[EE_MAXADDRLEN + 1]; }; #define AT25_WREN 0x06 /* latch the write enable */ @@ -74,20 +76,29 @@ struct at25_data { #define io_limit PAGE_SIZE /* bytes */ +/* Handle the address MSB as part of instruction byte */ +static u8 at25_instr(struct at25_data *at25, u8 instr, unsigned int off) +{ + if (!(at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)) + return instr; + if (off < BIT(at25->addrlen * 8)) + return instr; + return instr | AT25_INSTR_BIT3; +} + static int at25_ee_read(void *priv, unsigned int offset, void *val, size_t count) { + u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL); struct at25_data *at25 = priv; char *buf = val; - size_t max_chunk = spi_max_transfer_size(at25->spi); unsigned int msg_offset = offset; size_t bytes_left = count; size_t segment; - u8 *cp; - ssize_t status; - struct spi_transfer t[2]; - struct spi_message m; - u8 instr; + int status; + + if (!bounce) + return -ENOMEM; if (unlikely(offset >= at25->chip.byte_len)) return -EINVAL; @@ -97,87 +108,67 @@ static int at25_ee_read(void *priv, unsigned int offset, return -EINVAL; do { - segment = min(bytes_left, max_chunk); - cp = at25->command; - - instr = AT25_READ; - if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) - if (msg_offset >= BIT(at25->addrlen * 8)) - instr |= AT25_INSTR_BIT3; - - mutex_lock(&at25->lock); + struct spi_mem_op op; - *cp++ = instr; + segment = min(bytes_left, io_limit); - /* 8/16/24-bit address is written MSB first */ - switch (at25->addrlen) { - default: /* case 3 */ - *cp++ = msg_offset >> 16; - fallthrough; - case 2: - *cp++ = msg_offset >> 8; - fallthrough; - case 1: - case 0: /* can't happen: for better code generation */ - *cp++ = msg_offset >> 0; - } - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = at25->command; - t[0].len = at25->addrlen + 1; - spi_message_add_tail(&t[0], &m); + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_READ, + msg_offset), 1), + SPI_MEM_OP_ADDR(at25->addrlen, msg_offset, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(segment, bounce, 1)); - t[1].rx_buf = buf; - t[1].len = segment; - spi_message_add_tail(&t[1], &m); - - status = spi_sync(at25->spi, &m); + status = spi_mem_adjust_op_size(at25->spimem, &op); + if (status) + return status; + segment = op.data.nbytes; + mutex_lock(&at25->lock); + status = spi_mem_exec_op(at25->spimem, &op); mutex_unlock(&at25->lock); - if (status) return status; + memcpy(buf, bounce, segment); msg_offset += segment; buf += segment; bytes_left -= segment; } while (bytes_left > 0); - dev_dbg(&at25->spi->dev, "read %zu bytes at %d\n", + dev_dbg(&at25->spimem->spi->dev, "read %zu bytes at %d\n", count, offset); return 0; } -/* Read extra registers as ID or serial number */ +/* + * Read extra registers as ID or serial number + * + * Allow for the callers to provide @buf on stack (not necessary DMA-capable) + * by allocating a bounce buffer internally. + */ static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, int len) { + u8 *bounce __free(kfree) = kmalloc(len, GFP_KERNEL); + struct spi_mem_op op; int status; - struct spi_transfer t[2]; - struct spi_message m; - - spi_message_init(&m); - memset(t, 0, sizeof(t)); - - t[0].tx_buf = at25->command; - t[0].len = 1; - spi_message_add_tail(&t[0], &m); - t[1].rx_buf = buf; - t[1].len = len; - spi_message_add_tail(&t[1], &m); + if (!bounce) + return -ENOMEM; - mutex_lock(&at25->lock); + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(command, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(len, bounce, 1)); - at25->command[0] = command; + status = spi_mem_exec_op(at25->spimem, &op); + dev_dbg(&at25->spimem->spi->dev, "read %d aux bytes --> %d\n", len, status); + if (status) + return status; - status = spi_sync(at25->spi, &m); - dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status); + memcpy(buf, bounce, len); - mutex_unlock(&at25->lock); - return status; + return 0; } static ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -195,14 +186,47 @@ static struct attribute *sernum_attrs[] = { }; ATTRIBUTE_GROUPS(sernum); +/* + * Poll Read Status Register with timeout + * + * Return: + * 0, if the chip is ready + * [positive] Status Register value as-is, if the chip is busy + * [negative] error code in case of read failure + */ +static int at25_wait_ready(struct at25_data *at25) +{ + u8 *bounce __free(kfree) = kmalloc(1, GFP_KERNEL); + struct spi_mem_op op; + int status; + + if (!bounce) + return -ENOMEM; + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_RDSR, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, bounce, 1)); + + read_poll_timeout(spi_mem_exec_op, status, + status || !(bounce[0] & AT25_SR_nRDY), false, + USEC_PER_MSEC, USEC_PER_MSEC * EE_TIMEOUT, + at25->spimem, &op); + if (status < 0) + return status; + if (!(bounce[0] & AT25_SR_nRDY)) + return 0; + + return bounce[0]; +} + static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) { + u8 *bounce __free(kfree) = kmalloc(min(count, io_limit), GFP_KERNEL); struct at25_data *at25 = priv; - size_t maxsz = spi_max_transfer_size(at25->spi); const char *buf = val; - int status = 0; - unsigned buf_size; - u8 *bounce; + unsigned int buf_size; + int status; if (unlikely(off >= at25->chip.byte_len)) return -EFBIG; @@ -211,11 +235,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) if (unlikely(!count)) return -EINVAL; - /* Temp buffer starts with command and address */ buf_size = at25->chip.page_size; - if (buf_size > io_limit) - buf_size = io_limit; - bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL); + if (!bounce) return -ENOMEM; @@ -223,85 +244,64 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) * For write, rollover is within the page ... so we write at * most one page, then manually roll over to the next page. */ - mutex_lock(&at25->lock); + guard(mutex)(&at25->lock); do { - unsigned long timeout, retries; - unsigned segment; - unsigned offset = off; - u8 *cp = bounce; - int sr; - u8 instr; - - *cp = AT25_WREN; - status = spi_write(at25->spi, cp, 1); - if (status < 0) { - dev_dbg(&at25->spi->dev, "WREN --> %d\n", status); - break; - } - - instr = AT25_WRITE; - if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) - if (offset >= BIT(at25->addrlen * 8)) - instr |= AT25_INSTR_BIT3; - *cp++ = instr; + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(AT25_WREN, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_DATA); + unsigned int segment; - /* 8/16/24-bit address is written MSB first */ - switch (at25->addrlen) { - default: /* case 3 */ - *cp++ = offset >> 16; - fallthrough; - case 2: - *cp++ = offset >> 8; - fallthrough; - case 1: - case 0: /* can't happen: for better code generation */ - *cp++ = offset >> 0; + status = spi_mem_exec_op(at25->spimem, &op); + if (status < 0) { + dev_dbg(&at25->spimem->spi->dev, "WREN --> %d\n", status); + return status; } /* Write as much of a page as we can */ - segment = buf_size - (offset % buf_size); + segment = buf_size - (off % buf_size); if (segment > count) segment = count; - if (segment > maxsz) - segment = maxsz; - memcpy(cp, buf, segment); - status = spi_write(at25->spi, bounce, - segment + at25->addrlen + 1); - dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n", - segment, offset, status); - if (status < 0) - break; + if (segment > io_limit) + segment = io_limit; + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(at25_instr(at25, AT25_WRITE, off), + 1), + SPI_MEM_OP_ADDR(at25->addrlen, off, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(segment, bounce, 1)); + + status = spi_mem_adjust_op_size(at25->spimem, &op); + if (status) + return status; + segment = op.data.nbytes; + + memcpy(bounce, buf, segment); + + status = spi_mem_exec_op(at25->spimem, &op); + dev_dbg(&at25->spimem->spi->dev, "write %u bytes at %u --> %d\n", + segment, off, status); + if (status) + return status; /* * REVISIT this should detect (or prevent) failed writes * to read-only sections of the EEPROM... */ - /* Wait for non-busy status */ - timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); - retries = 0; - do { - - sr = spi_w8r8(at25->spi, AT25_RDSR); - if (sr < 0 || (sr & AT25_SR_nRDY)) { - dev_dbg(&at25->spi->dev, - "rdsr --> %d (%02x)\n", sr, sr); - /* at HZ=100, this is sloooow */ - msleep(1); - continue; - } - if (!(sr & AT25_SR_nRDY)) - break; - } while (retries++ < 3 || time_before_eq(jiffies, timeout)); - - if ((sr < 0) || (sr & AT25_SR_nRDY)) { - dev_err(&at25->spi->dev, + status = at25_wait_ready(at25); + if (status < 0) { + dev_err_probe(&at25->spimem->spi->dev, status, + "Read Status Redister command failed\n"); + return status; + } + if (status) { + dev_dbg(&at25->spimem->spi->dev, + "Status %02x\n", status); + dev_err(&at25->spimem->spi->dev, "write %u bytes offset %u, timeout after %u msecs\n", - segment, offset, - jiffies_to_msecs(jiffies - - (timeout - EE_TIMEOUT))); - status = -ETIMEDOUT; - break; + segment, off, EE_TIMEOUT); + return -ETIMEDOUT; } off += segment; @@ -310,9 +310,6 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) } while (count > 0); - mutex_unlock(&at25->lock); - - kfree(bounce); return status; } @@ -388,17 +385,33 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) /* Get ID of chip */ fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); + /* There are inside-out FRAM variations, detect them and reverse the ID bytes */ + if (id[6] == 0x7f && id[2] == 0xc2) + for (i = 0; i < ARRAY_SIZE(id) / 2; i++) { + u8 tmp = id[i]; + int j = ARRAY_SIZE(id) - i - 1; + + id[i] = id[j]; + id[j] = tmp; + } if (id[6] != 0xc2) { dev_err(dev, "Error: no Cypress FRAM (id %02x)\n", id[6]); return -ENODEV; } - /* Set size found in ID */ - if (id[7] < 0x21 || id[7] > 0x26) { + + switch (id[7]) { + case 0x21 ... 0x26: + chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024; + break; + case 0x2a ... 0x30: + /* CY15B116QN ... CY15B116QN */ + chip->byte_len = BIT(((id[7] >> 1) & 0xf) + 13); + break; + default: dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]); return -ENODEV; } - chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024; if (chip->byte_len > 64 * 1024) chip->flags |= EE_ADDR3; else @@ -429,31 +442,33 @@ static const struct spi_device_id at25_spi_ids[] = { }; MODULE_DEVICE_TABLE(spi, at25_spi_ids); -static int at25_probe(struct spi_device *spi) +static int at25_probe(struct spi_mem *mem) { - struct at25_data *at25 = NULL; - int err; - int sr; + struct spi_device *spi = mem->spi; struct spi_eeprom *pdata; + struct at25_data *at25; bool is_fram; + int err; + + at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL); + if (!at25) + return -ENOMEM; + + at25->spimem = mem; /* * Ping the chip ... the status register is pretty portable, - * unlike probing manufacturer IDs. We do expect that system - * firmware didn't write it in the past few milliseconds! + * unlike probing manufacturer IDs. */ - sr = spi_w8r8(spi, AT25_RDSR); - if (sr < 0 || sr & AT25_SR_nRDY) { - dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr); + err = at25_wait_ready(at25); + if (err < 0) + return dev_err_probe(&spi->dev, err, "Read Status Register command failed\n"); + if (err) { + dev_err(&spi->dev, "Not ready (%02x)\n", err); return -ENXIO; } - at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL); - if (!at25) - return -ENOMEM; - mutex_init(&at25->lock); - at25->spi = spi; spi_set_drvdata(spi, at25); is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25"); @@ -514,17 +529,19 @@ static int at25_probe(struct spi_device *spi) /*-------------------------------------------------------------------------*/ -static struct spi_driver at25_driver = { - .driver = { - .name = "at25", - .of_match_table = at25_of_match, - .dev_groups = sernum_groups, +static struct spi_mem_driver at25_driver = { + .spidrv = { + .driver = { + .name = "at25", + .of_match_table = at25_of_match, + .dev_groups = sernum_groups, + }, + .id_table = at25_spi_ids, }, .probe = at25_probe, - .id_table = at25_spi_ids, }; -module_spi_driver(at25_driver); +module_spi_mem_driver(at25_driver); MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 76511d279aff..ca4c420e4a2f 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/string_choices.h> static LIST_HEAD(container_list); static DEFINE_MUTEX(container_list_lock); @@ -592,7 +593,7 @@ static ssize_t get_component_power_status(struct device *cdev, if (ecomp->power_status == -1) return (edev->cb->get_power_status) ? -EIO : -ENOTTY; - return sysfs_emit(buf, "%s\n", ecomp->power_status ? "on" : "off"); + return sysfs_emit(buf, "%s\n", str_on_off(ecomp->power_status)); } static ssize_t set_component_power_status(struct device *cdev, diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 378923594f02..53e88a1bc430 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2262,8 +2262,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) int i, err, domain_id = -1, vmcount; const char *domain; bool secure_dsp; - struct device_node *rmem_node; - struct reserved_mem *rmem; unsigned int vmids[FASTRPC_MAX_VMIDS]; err = of_property_read_string(rdev->of_node, "label", &domain); @@ -2306,20 +2304,17 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } } - rmem_node = of_parse_phandle(rdev->of_node, "memory-region", 0); - if (domain_id == SDSP_DOMAIN_ID && rmem_node) { + if (domain_id == SDSP_DOMAIN_ID) { + struct resource res; u64 src_perms; - rmem = of_reserved_mem_lookup(rmem_node); - if (!rmem) { - err = -EINVAL; - goto err_free_data; - } + err = of_reserved_mem_region_to_resource(rdev->of_node, 0, &res); + if (!err) { + src_perms = BIT(QCOM_SCM_VMID_HLOS); - src_perms = BIT(QCOM_SCM_VMID_HLOS); - - qcom_scm_assign_mem(rmem->base, rmem->size, &src_perms, + qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, data->vmperms, data->vmcount); + } } diff --git a/drivers/misc/hi6421v600-irq.c b/drivers/misc/hi6421v600-irq.c index 187c5bc91e31..5ba40222eb12 100644 --- a/drivers/misc/hi6421v600-irq.c +++ b/drivers/misc/hi6421v600-irq.c @@ -214,7 +214,6 @@ static void hi6421v600_irq_init(struct hi6421v600_irq *priv) static int hi6421v600_irq_probe(struct platform_device *pdev) { struct device *pmic_dev = pdev->dev.parent; - struct device_node *np = pmic_dev->of_node; struct platform_device *pmic_pdev; struct device *dev = &pdev->dev; struct hi6421v600_irq *priv; @@ -254,8 +253,7 @@ static int hi6421v600_irq_probe(struct platform_device *pdev) if (!priv->irqs) return -ENOMEM; - priv->domain = irq_domain_create_simple(of_fwnode_handle(np), - PMIC_IRQ_LIST_MAX, 0, + priv->domain = irq_domain_create_simple(dev_fwnode(pmic_dev), PMIC_IRQ_LIST_MAX, 0, &hi6421v600_domain_ops, priv); if (!priv->domain) { dev_err(dev, "Failed to create IRQ domain\n"); diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 9eebeffcd8fd..90dba20b2de7 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -463,14 +463,14 @@ static void mei_nfc(struct mei_cl_device *cldev) if (IS_ERR(cl)) { ret = PTR_ERR(cl); cl = NULL; - dev_err(bus->dev, "nfc hook alloc failed %d\n", ret); + dev_err(&cldev->dev, "nfc hook alloc failed %d\n", ret); goto out; } me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid); if (!me_cl) { ret = -ENOTTY; - dev_err(bus->dev, "Cannot find nfc info %d\n", ret); + dev_err(&cldev->dev, "Cannot find nfc info %d\n", ret); goto out; } @@ -496,13 +496,13 @@ static void mei_nfc(struct mei_cl_device *cldev) goto disconnect; } - dev_dbg(bus->dev, "nfc radio %s\n", radio_name); + dev_dbg(&cldev->dev, "nfc radio %s\n", radio_name); strscpy(cldev->name, radio_name, sizeof(cldev->name)); disconnect: mutex_lock(&bus->device_lock); if (mei_cl_disconnect(cl) < 0) - dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n"); + dev_err(&cldev->dev, "Can't disconnect the NFC INFO ME\n"); mei_cl_flush_queues(cl, NULL); @@ -515,7 +515,7 @@ out: if (ret) cldev->do_match = 0; - dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match); + dev_dbg(&cldev->dev, "end of fixup match = %d\n", cldev->do_match); } /** diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 67176caf5416..5cc3ad07d5be 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -875,14 +875,14 @@ int mei_cldev_disable(struct mei_cl_device *cldev) mei_cl_bus_vtag_free(cldev); if (!mei_cl_is_connected(cl)) { - dev_dbg(bus->dev, "Already disconnected\n"); + dev_dbg(&cldev->dev, "Already disconnected\n"); err = 0; goto out; } err = mei_cl_disconnect(cl); if (err < 0) - dev_err(bus->dev, "Could not disconnect from the ME client\n"); + dev_err(&cldev->dev, "Could not disconnect from the ME client\n"); out: /* Flush queues and remove any pending read unless we have mapped DMA */ @@ -935,7 +935,7 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, cl = cldev->cl; bus = cldev->bus; - dev_dbg(bus->dev, "client_id %u, fence_id %u\n", client_id, fence_id); + dev_dbg(&cldev->dev, "client_id %u, fence_id %u\n", client_id, fence_id); if (!bus->hbm_f_gsc_supported) return -EOPNOTSUPP; @@ -983,11 +983,11 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, /* send the message to GSC */ ret = __mei_cl_send(cl, (u8 *)ext_hdr, buf_sz, 0, MEI_CL_IO_SGL); if (ret < 0) { - dev_err(bus->dev, "__mei_cl_send failed, returned %zd\n", ret); + dev_err(&cldev->dev, "__mei_cl_send failed, returned %zd\n", ret); goto end; } if (ret != buf_sz) { - dev_err(bus->dev, "__mei_cl_send returned %zd instead of expected %zd\n", + dev_err(&cldev->dev, "__mei_cl_send returned %zd instead of expected %zd\n", ret, buf_sz); ret = -EIO; goto end; @@ -997,7 +997,7 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, ret = __mei_cl_recv(cl, (u8 *)&rx_msg, sizeof(rx_msg), NULL, MEI_CL_IO_SGL, 0); if (ret != sizeof(rx_msg)) { - dev_err(bus->dev, "__mei_cl_recv returned %zd instead of expected %zd\n", + dev_err(&cldev->dev, "__mei_cl_recv returned %zd instead of expected %zd\n", ret, sizeof(rx_msg)); if (ret >= 0) ret = -EIO; @@ -1006,13 +1006,13 @@ ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, /* check rx_msg.client_id and rx_msg.fence_id match the ones we send */ if (rx_msg.client_id != client_id || rx_msg.fence_id != fence_id) { - dev_err(bus->dev, "received client_id/fence_id %u/%u instead of %u/%u sent\n", + dev_err(&cldev->dev, "received client_id/fence_id %u/%u instead of %u/%u sent\n", rx_msg.client_id, rx_msg.fence_id, client_id, fence_id); ret = -EFAULT; goto end; } - dev_dbg(bus->dev, "gsc command: successfully written %u bytes\n", rx_msg.written); + dev_dbg(&cldev->dev, "gsc command: successfully written %u bytes\n", rx_msg.written); ret = rx_msg.written; end: @@ -1156,7 +1156,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *a, { struct mei_cl_device *cldev = to_mei_cl_device(dev); - return scnprintf(buf, PAGE_SIZE, "%s", cldev->name); + return sysfs_emit(buf, "%s", cldev->name); } static DEVICE_ATTR_RO(name); @@ -1166,7 +1166,7 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); - return sprintf(buf, "%pUl", uuid); + return sysfs_emit(buf, "%pUl", uuid); } static DEVICE_ATTR_RO(uuid); @@ -1176,7 +1176,7 @@ static ssize_t version_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u8 version = mei_me_cl_ver(cldev->me_cl); - return sprintf(buf, "%02X", version); + return sysfs_emit(buf, "%02X", version); } static DEVICE_ATTR_RO(version); @@ -1187,8 +1187,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); u8 version = mei_me_cl_ver(cldev->me_cl); - return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:%02X:", - cldev->name, uuid, version); + return sysfs_emit(buf, "mei:%s:%pUl:%02X:", cldev->name, uuid, version); } static DEVICE_ATTR_RO(modalias); @@ -1198,7 +1197,7 @@ static ssize_t max_conn_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u8 maxconn = mei_me_cl_max_conn(cldev->me_cl); - return sprintf(buf, "%d", maxconn); + return sysfs_emit(buf, "%d", maxconn); } static DEVICE_ATTR_RO(max_conn); @@ -1208,7 +1207,7 @@ static ssize_t fixed_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u8 fixed = mei_me_cl_fixed(cldev->me_cl); - return sprintf(buf, "%d", fixed); + return sysfs_emit(buf, "%d", fixed); } static DEVICE_ATTR_RO(fixed); @@ -1218,7 +1217,7 @@ static ssize_t vtag_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); bool vt = mei_me_cl_vt(cldev->me_cl); - return sprintf(buf, "%d", vt); + return sysfs_emit(buf, "%d", vt); } static DEVICE_ATTR_RO(vtag); @@ -1228,7 +1227,7 @@ static ssize_t max_len_show(struct device *dev, struct device_attribute *a, struct mei_cl_device *cldev = to_mei_cl_device(dev); u32 maxlen = mei_me_cl_max_len(cldev->me_cl); - return sprintf(buf, "%u", maxlen); + return sysfs_emit(buf, "%u", maxlen); } static DEVICE_ATTR_RO(max_len); @@ -1301,10 +1300,16 @@ static void mei_dev_bus_put(struct mei_device *bus) static void mei_cl_bus_dev_release(struct device *dev) { struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_device *mdev = cldev->cl->dev; + struct mei_cl *cl; mei_cl_flush_queues(cldev->cl, NULL); mei_me_cl_put(cldev->me_cl); mei_dev_bus_put(cldev->bus); + + list_for_each_entry(cl, &mdev->file_list, link) + WARN_ON(cl == cldev->cl); + kfree(cldev->cl); kfree(cldev); } @@ -1399,7 +1404,7 @@ static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) { int ret; - dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", + dev_dbg(&cldev->dev, "adding %pUL:%02X\n", mei_me_cl_uuid(cldev->me_cl), mei_me_cl_ver(cldev->me_cl)); ret = device_add(&cldev->dev); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 026b1f686c16..4fe9a2752d43 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -510,7 +510,7 @@ int mei_hbm_cl_notify_req(struct mei_device *dev, ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) - dev_err(dev->dev, "notify request failed: ret = %d\n", ret); + cl_err(dev, cl, "notify request failed: ret = %d\n", ret); return ret; } @@ -626,7 +626,7 @@ int mei_hbm_cl_dma_map_req(struct mei_device *dev, struct mei_cl *cl) ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) - dev_err(dev->dev, "dma map request failed: ret = %d\n", ret); + cl_err(dev, cl, "dma map request failed: ret = %d\n", ret); return ret; } @@ -654,7 +654,7 @@ int mei_hbm_cl_dma_unmap_req(struct mei_device *dev, struct mei_cl *cl) ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) - dev_err(dev->dev, "dma unmap request failed: ret = %d\n", ret); + cl_err(dev, cl, "dma unmap request failed: ret = %d\n", ret); return ret; } @@ -679,10 +679,10 @@ static void mei_hbm_cl_dma_map_res(struct mei_device *dev, return; if (res->status) { - dev_err(dev->dev, "cl dma map failed %d\n", res->status); + cl_err(dev, cl, "cl dma map failed %d\n", res->status); cl->status = -EFAULT; } else { - dev_dbg(dev->dev, "cl dma map succeeded\n"); + cl_dbg(dev, cl, "cl dma map succeeded\n"); cl->dma_mapped = 1; cl->status = 0; } @@ -709,10 +709,10 @@ static void mei_hbm_cl_dma_unmap_res(struct mei_device *dev, return; if (res->status) { - dev_err(dev->dev, "cl dma unmap failed %d\n", res->status); + cl_err(dev, cl, "cl dma unmap failed %d\n", res->status); cl->status = -EFAULT; } else { - dev_dbg(dev->dev, "cl dma unmap succeeded\n"); + cl_dbg(dev, cl, "cl dma unmap succeeded\n"); cl->dma_mapped = 0; cl->status = 0; } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index c484f416fae4..d472f6bbe767 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -35,7 +35,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list) cl = cb->cl; list_del_init(&cb->list); - dev_dbg(dev->dev, "completing call back.\n"); + cl_dbg(dev, cl, "completing call back.\n"); mei_cl_complete(cl, cb); } } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 1f5aaf16e300..8a149a15b861 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -256,7 +256,7 @@ copy_buffer: length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->buf.data + *offset, length)) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto free; } @@ -379,7 +379,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = copy_from_user(cb->buf.data, ubuf, length); if (rets) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; mei_io_cb_free(cb); goto out; @@ -421,7 +421,7 @@ static int mei_ioctl_connect_client(struct file *file, /* find ME client we're trying to connect to */ me_cl = mei_me_cl_by_uuid(dev, in_client_uuid); if (!me_cl) { - dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", + cl_dbg(dev, cl, "Cannot connect to FW Client UUID = %pUl\n", in_client_uuid); rets = -ENOTTY; goto end; @@ -431,24 +431,21 @@ static int mei_ioctl_connect_client(struct file *file, bool forbidden = dev->override_fixed_address ? !dev->allow_fixed_address : !dev->hbm_f_fa_supported; if (forbidden) { - dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", + cl_dbg(dev, cl, "Connection forbidden to FW Client UUID = %pUl\n", in_client_uuid); rets = -ENOTTY; goto end; } } - dev_dbg(dev->dev, "Connect to FW Client ID = %d\n", - me_cl->client_id); - dev_dbg(dev->dev, "FW Client - Protocol Version = %d\n", - me_cl->props.protocol_version); - dev_dbg(dev->dev, "FW Client - Max Msg Len = %d\n", - me_cl->props.max_msg_length); + cl_dbg(dev, cl, "Connect to FW Client ID = %d\n", me_cl->client_id); + cl_dbg(dev, cl, "FW Client - Protocol Version = %d\n", me_cl->props.protocol_version); + cl_dbg(dev, cl, "FW Client - Max Msg Len = %d\n", me_cl->props.max_msg_length); /* prepare the output buffer */ client->max_msg_length = me_cl->props.max_msg_length; client->protocol_version = me_cl->props.protocol_version; - dev_dbg(dev->dev, "Can connect?\n"); + cl_dbg(dev, cl, "Can connect?\n"); rets = mei_cl_connect(cl, me_cl, file); @@ -515,19 +512,19 @@ static int mei_ioctl_connect_vtag(struct file *file, cl = file->private_data; dev = cl->dev; - dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag); + cl_dbg(dev, cl, "FW Client %pUl vtag %d\n", in_client_uuid, vtag); switch (cl->state) { case MEI_FILE_DISCONNECTED: if (mei_cl_vtag_by_fp(cl, file) != vtag) { - dev_err(dev->dev, "reconnect with different vtag\n"); + cl_err(dev, cl, "reconnect with different vtag\n"); return -EINVAL; } break; case MEI_FILE_INITIALIZING: /* malicious connect from another thread may push vtag */ if (!IS_ERR(mei_cl_fp_by_vtag(cl, vtag))) { - dev_err(dev->dev, "vtag already filled\n"); + cl_err(dev, cl, "vtag already filled\n"); return -EINVAL; } @@ -546,7 +543,7 @@ static int mei_ioctl_connect_vtag(struct file *file, continue; /* replace cl with acquired one */ - dev_dbg(dev->dev, "replacing with existing cl\n"); + cl_dbg(dev, cl, "replacing with existing cl\n"); mei_cl_unlink(cl); kfree(cl); file->private_data = pos; @@ -656,7 +653,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) dev = cl->dev; - dev_dbg(dev->dev, "IOCTL cmd = 0x%x", cmd); + cl_dbg(dev, cl, "IOCTL cmd = 0x%x", cmd); mutex_lock(&dev->device_lock); if (dev->dev_state != MEI_DEV_ENABLED) { @@ -666,9 +663,9 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) switch (cmd) { case IOCTL_MEI_CONNECT_CLIENT: - dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); + cl_dbg(dev, cl, "IOCTL_MEI_CONNECT_CLIENT\n"); if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -689,7 +686,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) /* if all is ok, copying the data back to user. */ if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto out; } @@ -697,10 +694,10 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; case IOCTL_MEI_CONNECT_CLIENT_VTAG: - dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n"); + cl_dbg(dev, cl, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n"); if (copy_from_user(&conn_vtag, (char __user *)data, sizeof(conn_vtag))) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -711,13 +708,13 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) rets = mei_vt_support_check(dev, cl_uuid); if (rets == -EOPNOTSUPP) - dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n", + cl_dbg(dev, cl, "FW Client %pUl does not support vtags\n", cl_uuid); if (rets) goto out; if (!vtag) { - dev_dbg(dev->dev, "vtag can't be zero\n"); + cl_dbg(dev, cl, "vtag can't be zero\n"); rets = -EINVAL; goto out; } @@ -729,7 +726,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) /* if all is ok, copying the data back to user. */ if (copy_to_user((char __user *)data, &conn_vtag, sizeof(conn_vtag))) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto out; } @@ -737,10 +734,10 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; case IOCTL_MEI_NOTIFY_SET: - dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_SET.\n"); + cl_dbg(dev, cl, "IOCTL_MEI_NOTIFY_SET\n"); if (copy_from_user(¬ify_req, (char __user *)data, sizeof(notify_req))) { - dev_dbg(dev->dev, "failed to copy data from userland\n"); + cl_dbg(dev, cl, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -748,15 +745,15 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; case IOCTL_MEI_NOTIFY_GET: - dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_GET.\n"); + cl_dbg(dev, cl, "IOCTL_MEI_NOTIFY_GET\n"); rets = mei_ioctl_client_notify_get(file, ¬ify_get); if (rets) goto out; - dev_dbg(dev->dev, "copy connect data to user\n"); + cl_dbg(dev, cl, "copy connect data to user\n"); if (copy_to_user((char __user *)data, ¬ify_get, sizeof(notify_get))) { - dev_dbg(dev->dev, "failed to copy data to userland\n"); + cl_dbg(dev, cl, "failed to copy data to userland\n"); rets = -EFAULT; goto out; diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c index 435760b1e86f..b2b5a20ae3fa 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -256,6 +256,9 @@ static int mei_vsc_hw_reset(struct mei_device *mei_dev, bool intr_enable) vsc_tp_reset(hw->tp); + if (!intr_enable) + return 0; + return vsc_tp_init(hw->tp, mei_dev->dev); } @@ -377,6 +380,8 @@ err_stop: err_cancel: mei_cancel_work(mei_dev); + vsc_tp_register_event_cb(tp, NULL, NULL); + mei_disable_interrupts(mei_dev); return ret; @@ -385,11 +390,14 @@ err_cancel: static void mei_vsc_remove(struct platform_device *pdev) { struct mei_device *mei_dev = platform_get_drvdata(pdev); + struct mei_vsc_hw *hw = mei_dev_to_vsc_hw(mei_dev); pm_runtime_disable(mei_dev->dev); mei_stop(mei_dev); + vsc_tp_register_event_cb(hw->tp, NULL, NULL); + mei_disable_interrupts(mei_dev); mei_deregister(mei_dev); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c index 267d0de5fade..5ecf99883996 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/types.h> +#include <linux/workqueue.h> #include "vsc-tp.h" @@ -76,12 +77,12 @@ struct vsc_tp { atomic_t assert_cnt; wait_queue_head_t xfer_wait; + struct work_struct event_work; vsc_tp_event_cb_t event_notify; void *event_notify_context; - - /* used to protect command download */ - struct mutex mutex; + struct mutex event_notify_mutex; /* protects event_notify + context */ + struct mutex mutex; /* protects command download */ }; /* GPIO resources */ @@ -106,17 +107,19 @@ static irqreturn_t vsc_tp_isr(int irq, void *data) wake_up(&tp->xfer_wait); - return IRQ_WAKE_THREAD; + schedule_work(&tp->event_work); + + return IRQ_HANDLED; } -static irqreturn_t vsc_tp_thread_isr(int irq, void *data) +static void vsc_tp_event_work(struct work_struct *work) { - struct vsc_tp *tp = data; + struct vsc_tp *tp = container_of(work, struct vsc_tp, event_work); + + guard(mutex)(&tp->event_notify_mutex); if (tp->event_notify) tp->event_notify(tp->event_notify_context); - - return IRQ_HANDLED; } /* wakeup firmware and wait for response */ @@ -399,6 +402,8 @@ EXPORT_SYMBOL_NS_GPL(vsc_tp_need_read, "VSC_TP"); int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, void *context) { + guard(mutex)(&tp->event_notify_mutex); + tp->event_notify = event_cb; tp->event_notify_context = context; @@ -407,37 +412,6 @@ int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, EXPORT_SYMBOL_NS_GPL(vsc_tp_register_event_cb, "VSC_TP"); /** - * vsc_tp_request_irq - request irq for vsc_tp device - * @tp: vsc_tp device handle - */ -int vsc_tp_request_irq(struct vsc_tp *tp) -{ - struct spi_device *spi = tp->spi; - struct device *dev = &spi->dev; - int ret; - - irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); - ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), tp); - if (ret) - return ret; - - return 0; -} -EXPORT_SYMBOL_NS_GPL(vsc_tp_request_irq, "VSC_TP"); - -/** - * vsc_tp_free_irq - free irq for vsc_tp device - * @tp: vsc_tp device handle - */ -void vsc_tp_free_irq(struct vsc_tp *tp) -{ - free_irq(tp->spi->irq, tp); -} -EXPORT_SYMBOL_NS_GPL(vsc_tp_free_irq, "VSC_TP"); - -/** * vsc_tp_intr_synchronize - synchronize vsc_tp interrupt * @tp: vsc_tp device handle */ @@ -523,13 +497,15 @@ static int vsc_tp_probe(struct spi_device *spi) tp->spi = spi; irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); - ret = request_threaded_irq(spi->irq, vsc_tp_isr, vsc_tp_thread_isr, + ret = request_threaded_irq(spi->irq, NULL, vsc_tp_isr, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(dev), tp); if (ret) return ret; mutex_init(&tp->mutex); + mutex_init(&tp->event_notify_mutex); + INIT_WORK(&tp->event_work, vsc_tp_event_work); /* only one child acpi device */ ret = acpi_dev_for_each_child(ACPI_COMPANION(dev), @@ -552,35 +528,27 @@ static int vsc_tp_probe(struct spi_device *spi) return 0; err_destroy_lock: - mutex_destroy(&tp->mutex); - free_irq(spi->irq, tp); + cancel_work_sync(&tp->event_work); + mutex_destroy(&tp->event_notify_mutex); + mutex_destroy(&tp->mutex); + return ret; } +/* Note this is also used for shutdown */ static void vsc_tp_remove(struct spi_device *spi) { struct vsc_tp *tp = spi_get_drvdata(spi); platform_device_unregister(tp->pdev); - mutex_destroy(&tp->mutex); - free_irq(spi->irq, tp); -} - -static void vsc_tp_shutdown(struct spi_device *spi) -{ - struct vsc_tp *tp = spi_get_drvdata(spi); - - platform_device_unregister(tp->pdev); + cancel_work_sync(&tp->event_work); + mutex_destroy(&tp->event_notify_mutex); mutex_destroy(&tp->mutex); - - vsc_tp_reset(tp); - - free_irq(spi->irq, tp); } static const struct acpi_device_id vsc_tp_acpi_ids[] = { @@ -595,7 +563,7 @@ MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); static struct spi_driver vsc_tp_driver = { .probe = vsc_tp_probe, .remove = vsc_tp_remove, - .shutdown = vsc_tp_shutdown, + .shutdown = vsc_tp_remove, .driver = { .name = "vsc-tp", .acpi_match_table = vsc_tp_acpi_ids, diff --git a/drivers/misc/mei/vsc-tp.h b/drivers/misc/mei/vsc-tp.h index 14ca195cbddc..f9513ddc3e40 100644 --- a/drivers/misc/mei/vsc-tp.h +++ b/drivers/misc/mei/vsc-tp.h @@ -37,9 +37,6 @@ int vsc_tp_xfer(struct vsc_tp *tp, u8 cmd, const void *obuf, size_t olen, int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, void *context); -int vsc_tp_request_irq(struct vsc_tp *tp); -void vsc_tp_free_irq(struct vsc_tp *tp); - void vsc_tp_intr_enable(struct vsc_tp *tp); void vsc_tp_intr_disable(struct vsc_tp *tp); void vsc_tp_intr_synchronize(struct vsc_tp *tp); diff --git a/drivers/misc/misc_minor_kunit.c b/drivers/misc/misc_minor_kunit.c index 293e0fb7e43e..30eceac5f1b6 100644 --- a/drivers/misc/misc_minor_kunit.c +++ b/drivers/misc/misc_minor_kunit.c @@ -3,6 +3,9 @@ #include <kunit/test-bug.h> #include <linux/module.h> #include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/init_syscalls.h> /* dynamic minor (2) */ static struct miscdevice dev_dynamic_minor = { @@ -51,19 +54,601 @@ static void kunit_misc_dynamic_minor(struct kunit *test) misc_deregister(&dev_misc_dynamic_minor); } +struct miscdev_test_case { + const char *str; + int minor; +}; + +static struct miscdev_test_case miscdev_test_ranges[] = { + { + .str = "lower static range, top", + .minor = 15, + }, + { + .str = "upper static range, bottom", + .minor = 130, + }, + { + .str = "lower static range, bottom", + .minor = 0, + }, + { + .str = "upper static range, top", + .minor = MISC_DYNAMIC_MINOR - 1, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(miscdev, miscdev_test_ranges, str); + +static int miscdev_find_minors(struct kunit_suite *suite) +{ + int ret; + struct miscdevice miscstat = { + .name = "miscstat", + }; + int i; + + for (i = 15; i >= 0; i--) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[0].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = 128; i < MISC_DYNAMIC_MINOR; i++) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[1].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = 0; i < miscdev_test_ranges[0].minor; i++) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[2].minor = miscstat.minor; + misc_deregister(&miscstat); + } else { + return ret; + } + + for (i = MISC_DYNAMIC_MINOR - 1; i > miscdev_test_ranges[1].minor; i--) { + miscstat.minor = i; + ret = misc_register(&miscstat); + if (ret == 0) + break; + } + + if (ret == 0) { + kunit_info(suite, "found misc device minor %d available\n", + miscstat.minor); + miscdev_test_ranges[3].minor = miscstat.minor; + misc_deregister(&miscstat); + } + + return ret; +} + +static bool is_valid_dynamic_minor(int minor) +{ + if (minor < 0) + return false; + if (minor == MISC_DYNAMIC_MINOR) + return false; + if (minor >= 0 && minor <= 15) + return false; + if (minor >= 128 && minor < MISC_DYNAMIC_MINOR) + return false; + return true; +} + +static int miscdev_test_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations miscdev_test_fops = { + .open = miscdev_test_open, +}; + +static void __init miscdev_test_can_open(struct kunit *test, struct miscdevice *misc) +{ + int ret; + struct file *filp; + char *devname; + + devname = kasprintf(GFP_KERNEL, "/dev/%s", misc->name); + ret = init_mknod(devname, S_IFCHR | 0600, + new_encode_dev(MKDEV(MISC_MAJOR, misc->minor))); + if (ret != 0) + KUNIT_FAIL(test, "failed to create node\n"); + + filp = filp_open(devname, O_RDONLY, 0); + if (IS_ERR_OR_NULL(filp)) + KUNIT_FAIL(test, "failed to open misc device: %ld\n", PTR_ERR(filp)); + else + fput(filp); + + init_unlink(devname); + kfree(devname); +} + +static void __init miscdev_test_static_basic(struct kunit *test) +{ + struct miscdevice misc_test = { + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc_test.minor = params->minor; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + + if (ret == 0) { + miscdev_test_can_open(test, &misc_test); + misc_deregister(&misc_test); + } +} + +static void __init miscdev_test_dynamic_basic(struct kunit *test) +{ + struct miscdevice misc_test = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc_test.minor)); + + if (ret == 0) { + miscdev_test_can_open(test, &misc_test); + misc_deregister(&misc_test); + } +} + +static void miscdev_test_twice(struct kunit *test) +{ + struct miscdevice misc_test = { + .name = "misc_test", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc_test.minor = params->minor; + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret == 0) + misc_deregister(&misc_test); + + ret = misc_register(&misc_test); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc_test.minor, params->minor); + if (ret == 0) + misc_deregister(&misc_test); +} + +static void miscdev_test_duplicate_minor(struct kunit *test) +{ + struct miscdevice misc1 = { + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .name = "misc2", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + misc1.minor = params->minor; + misc2.minor = params->minor; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, misc1.minor, params->minor); + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret == 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +static void miscdev_test_duplicate_name(struct kunit *test) +{ + struct miscdevice misc1 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + int ret; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&misc2); + + misc_deregister(&misc1); +} + +/* + * Test that after a duplicate name failure, the reserved minor number is + * freed to be allocated next. + */ +static void miscdev_test_duplicate_name_leak(struct kunit *test) +{ + struct miscdevice misc1 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc2 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc1", + .fops = &miscdev_test_fops, + }; + struct miscdevice misc3 = { + .minor = MISC_DYNAMIC_MINOR, + .name = "misc3", + .fops = &miscdev_test_fops, + }; + int ret; + int dyn_minor; + + ret = misc_register(&misc1); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc1.minor)); + + /* + * Find out what is the next minor number available. + */ + ret = misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + dyn_minor = misc3.minor; + misc_deregister(&misc3); + misc3.minor = MISC_DYNAMIC_MINOR; + + ret = misc_register(&misc2); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&misc2); + + /* + * Now check that we can still get the same minor we found before. + */ + ret = misc_register(&misc3); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(misc3.minor)); + KUNIT_EXPECT_EQ(test, misc3.minor, dyn_minor); + misc_deregister(&misc3); + + misc_deregister(&misc1); +} + +/* + * Try to register a static minor with a duplicate name. That might not + * deallocate the minor, preventing it from being used again. + */ +static void miscdev_test_duplicate_error(struct kunit *test) +{ + struct miscdevice miscdyn = { + .minor = MISC_DYNAMIC_MINOR, + .name = "name1", + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "name1", + .fops = &miscdev_test_fops, + }; + struct miscdevice miscnew = { + .name = "name2", + .fops = &miscdev_test_fops, + }; + int ret; + const struct miscdev_test_case *params = test->param_value; + + miscstat.minor = params->minor; + miscnew.minor = params->minor; + + ret = misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EEXIST); + if (ret == 0) + misc_deregister(&miscstat); + + ret = misc_register(&miscnew); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscnew.minor, params->minor); + if (ret == 0) + misc_deregister(&miscnew); + + misc_deregister(&miscdyn); +} + +static void __init miscdev_test_dynamic_only_range(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + /* + * This is the bug we are looking for! + * We asked for a dynamic minor and got a minor in the static range space. + */ + if (miscdev[i].minor >= 0 && miscdev[i].minor <= 15) { + KUNIT_FAIL(test, "misc_register allocated minor %d\n", miscdev[i].minor); + i++; + break; + } + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void __init miscdev_test_collision(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + miscstat.minor = miscdev_test_ranges[0].minor; + ret = misc_register(&miscstat); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } + + misc_deregister(&miscstat); + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void __init miscdev_test_collision_reverse(struct kunit *test) +{ + int ret; + struct miscdevice *miscdev; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + const int dynamic_minors = 256; + int i; + + miscdev = kunit_kmalloc_array(test, dynamic_minors, + sizeof(struct miscdevice), + GFP_KERNEL | __GFP_ZERO); + + for (i = 0; i < dynamic_minors; i++) { + miscdev[i].minor = MISC_DYNAMIC_MINOR; + miscdev[i].name = kasprintf(GFP_KERNEL, "misc_test%d", i); + miscdev[i].fops = &miscdev_test_fops; + ret = misc_register(&miscdev[i]); + if (ret != 0) + break; + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdev[i].minor)); + } + + KUNIT_EXPECT_EQ(test, ret, 0); + + miscstat.minor = miscdev_test_ranges[0].minor; + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdev_test_ranges[0].minor); + if (ret == 0) + misc_deregister(&miscstat); + + for (i--; i >= 0; i--) { + miscdev_test_can_open(test, &miscdev[i]); + misc_deregister(&miscdev[i]); + kfree_const(miscdev[i].name); + } +} + +static void __init miscdev_test_conflict(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn = { + .name = "miscdyn", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + + ret = misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + + /* + * Try to register a static minor with the same minor as the + * dynamic one. + */ + miscstat.minor = miscdyn.minor; + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + if (ret == 0) + misc_deregister(&miscstat); + + miscdev_test_can_open(test, &miscdyn); + + misc_deregister(&miscdyn); +} + +static void __init miscdev_test_conflict_reverse(struct kunit *test) +{ + int ret; + struct miscdevice miscdyn = { + .name = "miscdyn", + .minor = MISC_DYNAMIC_MINOR, + .fops = &miscdev_test_fops, + }; + struct miscdevice miscstat = { + .name = "miscstat", + .fops = &miscdev_test_fops, + }; + + /* + * Find the first available dynamic minor to use it as a static + * minor later on. + */ + ret = misc_register(&miscdyn); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + miscstat.minor = miscdyn.minor; + misc_deregister(&miscdyn); + + ret = misc_register(&miscstat); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, miscstat.minor, miscdyn.minor); + + /* + * Try to register a dynamic minor after registering a static minor + * within the dynamic range. It should work but get a different + * minor. + */ + miscdyn.minor = MISC_DYNAMIC_MINOR; + ret = misc_register(&miscdyn); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_NE(test, miscdyn.minor, miscstat.minor); + KUNIT_EXPECT_TRUE(test, is_valid_dynamic_minor(miscdyn.minor)); + if (ret == 0) + misc_deregister(&miscdyn); + + miscdev_test_can_open(test, &miscstat); + + misc_deregister(&miscstat); +} + static struct kunit_case test_cases[] = { KUNIT_CASE(kunit_dynamic_minor), KUNIT_CASE(kunit_static_minor), KUNIT_CASE(kunit_misc_dynamic_minor), + KUNIT_CASE_PARAM(miscdev_test_twice, miscdev_gen_params), + KUNIT_CASE_PARAM(miscdev_test_duplicate_minor, miscdev_gen_params), + KUNIT_CASE(miscdev_test_duplicate_name), + KUNIT_CASE(miscdev_test_duplicate_name_leak), + KUNIT_CASE_PARAM(miscdev_test_duplicate_error, miscdev_gen_params), {} }; static struct kunit_suite test_suite = { - .name = "misc_minor_test", + .name = "miscdev", + .suite_init = miscdev_find_minors, .test_cases = test_cases, }; kunit_test_suite(test_suite); +static struct kunit_case __refdata test_init_cases[] = { + KUNIT_CASE_PARAM(miscdev_test_static_basic, miscdev_gen_params), + KUNIT_CASE(miscdev_test_dynamic_basic), + KUNIT_CASE(miscdev_test_dynamic_only_range), + KUNIT_CASE(miscdev_test_collision), + KUNIT_CASE(miscdev_test_collision_reverse), + KUNIT_CASE(miscdev_test_conflict), + KUNIT_CASE(miscdev_test_conflict_reverse), + {} +}; + +static struct kunit_suite test_init_suite = { + .name = "miscdev_init", + .suite_init = miscdev_find_minors, + .test_cases = test_init_cases, +}; +kunit_test_init_section_suite(test_init_suite); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Vimal Agrawal"); -MODULE_DESCRIPTION("misc minor testing"); +MODULE_AUTHOR("Thadeu Lima de Souza Cascardo <cascardo@igalia.com>"); +MODULE_DESCRIPTION("Test module for misc character devices"); diff --git a/drivers/misc/ocxl/sysfs.c b/drivers/misc/ocxl/sysfs.c index e849641687a0..f194c159a778 100644 --- a/drivers/misc/ocxl/sysfs.c +++ b/drivers/misc/ocxl/sysfs.c @@ -16,7 +16,7 @@ static ssize_t global_mmio_size_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", afu->config.global_mmio_size); } @@ -26,7 +26,7 @@ static ssize_t pp_mmio_size_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", afu->config.pp_mmio_stride); } @@ -36,7 +36,7 @@ static ssize_t afu_version_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%hhu:%hhu\n", + return sysfs_emit(buf, "%hhu:%hhu\n", afu->config.version_major, afu->config.version_minor); } @@ -47,7 +47,7 @@ static ssize_t contexts_show(struct device *device, { struct ocxl_afu *afu = to_afu(device); - return scnprintf(buf, PAGE_SIZE, "%d/%d\n", + return sysfs_emit(buf, "%d/%d\n", afu->pasid_count, afu->pasid_max); } @@ -61,9 +61,9 @@ static ssize_t reload_on_reset_show(struct device *device, int val; if (ocxl_config_get_reset_reload(pci_dev, &val)) - return scnprintf(buf, PAGE_SIZE, "unavailable\n"); + return sysfs_emit(buf, "unavailable\n"); - return scnprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t reload_on_reset_store(struct device *device, diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index e5069882457e..c69644be4176 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -28,7 +28,8 @@ static ssize_t sram_read(struct file *filp, struct kobject *kobj, { struct sram_partition *part; - part = container_of(attr, struct sram_partition, battr); + /* Cast away the const as the attribute is part of a larger structure */ + part = (struct sram_partition *)container_of(attr, struct sram_partition, battr); mutex_lock(&part->lock); memcpy_fromio(buf, part->base + pos, count); @@ -43,7 +44,8 @@ static ssize_t sram_write(struct file *filp, struct kobject *kobj, { struct sram_partition *part; - part = container_of(attr, struct sram_partition, battr); + /* Cast away the const as the attribute is part of a larger structure */ + part = (struct sram_partition *)container_of(attr, struct sram_partition, battr); mutex_lock(&part->lock); memcpy_toio(part->base + pos, buf, count); @@ -164,8 +166,8 @@ static void sram_free_partitions(struct sram_dev *sram) static int sram_reserve_cmp(void *priv, const struct list_head *a, const struct list_head *b) { - struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); - struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); + const struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); + const struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); return ra->start - rb->start; } diff --git a/drivers/misc/ti_fpc202.c b/drivers/misc/ti_fpc202.c index f84cae50e2c9..0b1a6350c02b 100644 --- a/drivers/misc/ti_fpc202.c +++ b/drivers/misc/ti_fpc202.c @@ -281,7 +281,7 @@ static int fpc202_probe_port(struct fpc202_priv *priv, struct device_node *i2c_h desc.chan_id = port_id; desc.parent = dev; - desc.bus_handle = of_node_to_fwnode(i2c_handle); + desc.bus_handle = of_fwnode_handle(i2c_handle); desc.num_aliases = FPC202_ALIASES_PER_PORT; fpc202_fill_alias_table(priv->client, aliases, port_id); diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index f22b44827e92..8069d271ed81 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -251,6 +251,8 @@ static int ctx_fire_notification(u32 context_id, u32 priv_flags) ev.msg.hdr.src = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, VMCI_CONTEXT_RESOURCE_ID); ev.msg.hdr.payload_size = sizeof(ev) - sizeof(ev.msg.hdr); + memset((char*)&ev + sizeof(ev.msg.hdr), 0, + ev.msg.hdr.payload_size); ev.msg.event_data.event = VMCI_EVENT_CTX_REMOVED; ev.payload.context_id = context_id; @@ -269,28 +271,6 @@ static int ctx_fire_notification(u32 context_id, u32 priv_flags) } /* - * Returns the current number of pending datagrams. The call may - * also serve as a synchronization point for the datagram queue, - * as no enqueue operations can occur concurrently. - */ -int vmci_ctx_pending_datagrams(u32 cid, u32 *pending) -{ - struct vmci_ctx *context; - - context = vmci_ctx_get(cid); - if (context == NULL) - return VMCI_ERROR_INVALID_ARGS; - - spin_lock(&context->lock); - if (pending) - *pending = context->pending_datagrams; - spin_unlock(&context->lock); - vmci_ctx_put(context); - - return VMCI_SUCCESS; -} - -/* * Queues a VMCI datagram for the appropriate target VM context. */ int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg) @@ -992,38 +972,6 @@ int vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle) } /* - * Unregisters all doorbell handles that were previously - * registered with vmci_ctx_dbell_create. - */ -int vmci_ctx_dbell_destroy_all(u32 context_id) -{ - struct vmci_ctx *context; - struct vmci_handle handle; - - if (context_id == VMCI_INVALID_ID) - return VMCI_ERROR_INVALID_ARGS; - - context = vmci_ctx_get(context_id); - if (context == NULL) - return VMCI_ERROR_NOT_FOUND; - - spin_lock(&context->lock); - do { - struct vmci_handle_arr *arr = context->doorbell_array; - handle = vmci_handle_arr_remove_tail(arr); - } while (!vmci_handle_is_invalid(handle)); - do { - struct vmci_handle_arr *arr = context->pending_doorbell_array; - handle = vmci_handle_arr_remove_tail(arr); - } while (!vmci_handle_is_invalid(handle)); - spin_unlock(&context->lock); - - vmci_ctx_put(context); - - return VMCI_SUCCESS; -} - -/* * Registers a notification of a doorbell handle initiated by the * specified source context. The notification of doorbells are * subject to the same isolation rules as datagram delivery. To diff --git a/drivers/misc/vmw_vmci/vmci_context.h b/drivers/misc/vmw_vmci/vmci_context.h index 4db8701c9781..980fdece0f7d 100644 --- a/drivers/misc/vmw_vmci/vmci_context.h +++ b/drivers/misc/vmw_vmci/vmci_context.h @@ -132,7 +132,6 @@ bool vmci_ctx_supports_host_qp(struct vmci_ctx *context); int vmci_ctx_enqueue_datagram(u32 cid, struct vmci_datagram *dg); int vmci_ctx_dequeue_datagram(struct vmci_ctx *context, size_t *max_size, struct vmci_datagram **dg); -int vmci_ctx_pending_datagrams(u32 cid, u32 *pending); struct vmci_ctx *vmci_ctx_get(u32 cid); void vmci_ctx_put(struct vmci_ctx *context); bool vmci_ctx_exists(u32 cid); @@ -153,7 +152,6 @@ void vmci_ctx_unset_notify(struct vmci_ctx *context); int vmci_ctx_dbell_create(u32 context_id, struct vmci_handle handle); int vmci_ctx_dbell_destroy(u32 context_id, struct vmci_handle handle); -int vmci_ctx_dbell_destroy_all(u32 context_id); int vmci_ctx_notify_dbell(u32 cid, struct vmci_handle handle, u32 src_priv_flags); diff --git a/drivers/misc/vmw_vmci/vmci_doorbell.c b/drivers/misc/vmw_vmci/vmci_doorbell.c index fa8a7fce4481..53eeb9e6cb56 100644 --- a/drivers/misc/vmw_vmci/vmci_doorbell.c +++ b/drivers/misc/vmw_vmci/vmci_doorbell.c @@ -258,23 +258,6 @@ static int dbell_unlink(struct vmci_handle handle) } /* - * Notify another guest or the host. We send a datagram down to the - * host via the hypervisor with the notification info. - */ -static int dbell_notify_as_guest(struct vmci_handle handle, u32 priv_flags) -{ - struct vmci_doorbell_notify_msg notify_msg; - - notify_msg.hdr.dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID, - VMCI_DOORBELL_NOTIFY); - notify_msg.hdr.src = VMCI_ANON_SRC_HANDLE; - notify_msg.hdr.payload_size = sizeof(notify_msg) - VMCI_DG_HEADERSIZE; - notify_msg.handle = handle; - - return vmci_send_datagram(¬ify_msg.hdr); -} - -/* * Calls the specified callback in a delayed context. */ static void dbell_delayed_dispatch(struct work_struct *work) @@ -566,39 +549,3 @@ int vmci_doorbell_destroy(struct vmci_handle handle) return VMCI_SUCCESS; } EXPORT_SYMBOL_GPL(vmci_doorbell_destroy); - -/* - * vmci_doorbell_notify() - Ring the doorbell (and hide in the bushes). - * @dst: The handlle identifying the doorbell resource - * @priv_flags: Priviledge flags. - * - * Generates a notification on the doorbell identified by the - * handle. For host side generation of notifications, the caller - * can specify what the privilege of the calling side is. - */ -int vmci_doorbell_notify(struct vmci_handle dst, u32 priv_flags) -{ - int retval; - enum vmci_route route; - struct vmci_handle src; - - if (vmci_handle_is_invalid(dst) || - (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)) - return VMCI_ERROR_INVALID_ARGS; - - src = VMCI_INVALID_HANDLE; - retval = vmci_route(&src, &dst, false, &route); - if (retval < VMCI_SUCCESS) - return retval; - - if (VMCI_ROUTE_AS_HOST == route) - return vmci_ctx_notify_dbell(VMCI_HOST_CONTEXT_ID, - dst, priv_flags); - - if (VMCI_ROUTE_AS_GUEST == route) - return dbell_notify_as_guest(dst, priv_flags); - - pr_warn("Unknown route (%d) for doorbell\n", route); - return VMCI_ERROR_DST_UNREACHABLE; -} -EXPORT_SYMBOL_GPL(vmci_doorbell_notify); diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 73d71c4ec139..b88ac144ad32 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -3023,139 +3023,6 @@ s64 vmci_qpair_consume_buf_ready(const struct vmci_qp *qpair) EXPORT_SYMBOL_GPL(vmci_qpair_consume_buf_ready); /* - * vmci_qpair_enqueue() - Throw data on the queue. - * @qpair: Pointer to the queue pair struct. - * @buf: Pointer to buffer containing data - * @buf_size: Length of buffer. - * @buf_type: Buffer type (Unused). - * - * This is the client interface for enqueueing data into the queue. - * Returns number of bytes enqueued or < 0 on error. - */ -ssize_t vmci_qpair_enqueue(struct vmci_qp *qpair, - const void *buf, - size_t buf_size, - int buf_type) -{ - ssize_t result; - struct iov_iter from; - struct kvec v = {.iov_base = (void *)buf, .iov_len = buf_size}; - - if (!qpair || !buf) - return VMCI_ERROR_INVALID_ARGS; - - iov_iter_kvec(&from, ITER_SOURCE, &v, 1, buf_size); - - qp_lock(qpair); - - do { - result = qp_enqueue_locked(qpair->produce_q, - qpair->consume_q, - qpair->produce_q_size, - &from); - - if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && - !qp_wait_for_ready_queue(qpair)) - result = VMCI_ERROR_WOULD_BLOCK; - - } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); - - qp_unlock(qpair); - - return result; -} -EXPORT_SYMBOL_GPL(vmci_qpair_enqueue); - -/* - * vmci_qpair_dequeue() - Get data from the queue. - * @qpair: Pointer to the queue pair struct. - * @buf: Pointer to buffer for the data - * @buf_size: Length of buffer. - * @buf_type: Buffer type (Unused). - * - * This is the client interface for dequeueing data from the queue. - * Returns number of bytes dequeued or < 0 on error. - */ -ssize_t vmci_qpair_dequeue(struct vmci_qp *qpair, - void *buf, - size_t buf_size, - int buf_type) -{ - ssize_t result; - struct iov_iter to; - struct kvec v = {.iov_base = buf, .iov_len = buf_size}; - - if (!qpair || !buf) - return VMCI_ERROR_INVALID_ARGS; - - iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); - - qp_lock(qpair); - - do { - result = qp_dequeue_locked(qpair->produce_q, - qpair->consume_q, - qpair->consume_q_size, - &to, true); - - if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && - !qp_wait_for_ready_queue(qpair)) - result = VMCI_ERROR_WOULD_BLOCK; - - } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); - - qp_unlock(qpair); - - return result; -} -EXPORT_SYMBOL_GPL(vmci_qpair_dequeue); - -/* - * vmci_qpair_peek() - Peek at the data in the queue. - * @qpair: Pointer to the queue pair struct. - * @buf: Pointer to buffer for the data - * @buf_size: Length of buffer. - * @buf_type: Buffer type (Unused on Linux). - * - * This is the client interface for peeking into a queue. (I.e., - * copy data from the queue without updating the head pointer.) - * Returns number of bytes dequeued or < 0 on error. - */ -ssize_t vmci_qpair_peek(struct vmci_qp *qpair, - void *buf, - size_t buf_size, - int buf_type) -{ - struct iov_iter to; - struct kvec v = {.iov_base = buf, .iov_len = buf_size}; - ssize_t result; - - if (!qpair || !buf) - return VMCI_ERROR_INVALID_ARGS; - - iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); - - qp_lock(qpair); - - do { - result = qp_dequeue_locked(qpair->produce_q, - qpair->consume_q, - qpair->consume_q_size, - &to, false); - - if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && - !qp_wait_for_ready_queue(qpair)) - result = VMCI_ERROR_WOULD_BLOCK; - - } while (result == VMCI_ERROR_QUEUEPAIR_NOT_READY); - - qp_unlock(qpair); - - return result; -} -EXPORT_SYMBOL_GPL(vmci_qpair_peek); - -/* * vmci_qpair_enquev() - Throw data on the queue using iov. * @qpair: Pointer to the queue pair struct. * @iov: Pointer to buffer containing data diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index d370b2ad11e7..edd811444ce5 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -31,7 +31,6 @@ source "drivers/nvmem/layouts/Kconfig" config NVMEM_APPLE_EFUSES tristate "Apple eFuse support" depends on ARCH_APPLE || COMPILE_TEST - default ARCH_APPLE help Say y here to enable support for reading eFuses on Apple SoCs such as the M1. These are e.g. used to store factory programmed diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index fd2a9698d1c9..817f55f3a19e 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -544,7 +544,7 @@ static const struct device_type nvmem_provider_type = { .release = nvmem_release, }; -static struct bus_type nvmem_bus_type = { +static const struct bus_type nvmem_bus_type = { .name = "nvmem", }; @@ -1263,7 +1263,7 @@ void nvmem_device_put(struct nvmem_device *nvmem) EXPORT_SYMBOL_GPL(nvmem_device_put); /** - * devm_nvmem_device_get() - Get nvmem device of device form a given id + * devm_nvmem_device_get() - Get nvmem device of device from a given id * * @dev: Device that requests the nvmem device. * @id: name id for the requested nvmem device. @@ -1491,7 +1491,7 @@ EXPORT_SYMBOL_GPL(of_nvmem_cell_get); #endif /** - * nvmem_cell_get() - Get nvmem cell of device form a given cell name + * nvmem_cell_get() - Get nvmem cell of device from a given cell name * * @dev: Device that requests the nvmem cell. * @id: nvmem cell name to get (this corresponds with the name from the @@ -1526,7 +1526,7 @@ static void devm_nvmem_cell_release(struct device *dev, void *res) } /** - * devm_nvmem_cell_get() - Get nvmem cell of device form a given id + * devm_nvmem_cell_get() - Get nvmem cell of device from a given id * * @dev: Device that requests the nvmem cell. * @id: nvmem cell name id to get. @@ -2194,6 +2194,6 @@ static void __exit nvmem_exit(void) subsys_initcall(nvmem_init); module_exit(nvmem_exit); -MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); -MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com"); +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); MODULE_DESCRIPTION("nvmem Driver Core"); diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 47d9891de368..935da68610c7 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -210,8 +210,8 @@ static int pps_gpio_probe(struct platform_device *pdev) } /* register IRQ interrupt handler */ - ret = devm_request_irq(dev, data->irq, pps_gpio_irq_handler, - get_irqf_trigger_flags(data), data->info.name, data); + ret = request_irq(data->irq, pps_gpio_irq_handler, + get_irqf_trigger_flags(data), data->info.name, data); if (ret) { pps_unregister_source(data->pps); dev_err(dev, "failed to acquire IRQ %d\n", data->irq); @@ -228,6 +228,7 @@ static void pps_gpio_remove(struct platform_device *pdev) { struct pps_gpio_device_data *data = platform_get_drvdata(pdev); + free_irq(data->irq, data); pps_unregister_source(data->pps); timer_delete_sync(&data->echo_timer); /* reset echo pin in any case */ diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 6a02245ea35f..9463232af8d2 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -41,6 +41,9 @@ static __poll_t pps_cdev_poll(struct file *file, poll_table *wait) poll_wait(file, &pps->queue, wait); + if (pps->last_fetched_ev == pps->last_ev) + return 0; + return EPOLLIN | EPOLLRDNORM; } @@ -186,9 +189,11 @@ static long pps_cdev_ioctl(struct file *file, if (err) return err; - /* Return the fetched timestamp */ + /* Return the fetched timestamp and save last fetched event */ spin_lock_irq(&pps->lock); + pps->last_fetched_ev = pps->last_ev; + fdata.info.assert_sequence = pps->assert_sequence; fdata.info.clear_sequence = pps->clear_sequence; fdata.info.assert_tu = pps->assert_tu; @@ -272,9 +277,11 @@ static long pps_cdev_compat_ioctl(struct file *file, if (err) return err; - /* Return the fetched timestamp */ + /* Return the fetched timestamp and save last fetched event */ spin_lock_irq(&pps->lock); + pps->last_fetched_ev = pps->last_ev; + compat.info.assert_sequence = pps->assert_sequence; compat.info.clear_sequence = pps->clear_sequence; compat.info.current_mode = pps->current_mode; diff --git a/drivers/virt/acrn/ioreq.c b/drivers/virt/acrn/ioreq.c index e94358239a4b..55ddfa4840af 100644 --- a/drivers/virt/acrn/ioreq.c +++ b/drivers/virt/acrn/ioreq.c @@ -626,7 +626,7 @@ int acrn_ioreq_init(struct acrn_vm *vm, u64 buf_vma) } dev_dbg(acrn_dev.this_device, - "Init ioreq buffer %pK!\n", vm->ioreq_buf); + "Init ioreq buffer %p!\n", vm->ioreq_buf); ret = 0; free_buf: kfree(set_buffer); @@ -638,7 +638,7 @@ void acrn_ioreq_deinit(struct acrn_vm *vm) struct acrn_ioreq_client *client, *next; dev_dbg(acrn_dev.this_device, - "Deinit ioreq buffer %pK!\n", vm->ioreq_buf); + "Deinit ioreq buffer %p!\n", vm->ioreq_buf); /* Destroy all clients belonging to this VM */ list_for_each_entry_safe(client, next, &vm->ioreq_clients, list) acrn_ioreq_client_destroy(client); diff --git a/drivers/virt/acrn/mm.c b/drivers/virt/acrn/mm.c index 4c2f28715b70..bfb3031885e8 100644 --- a/drivers/virt/acrn/mm.c +++ b/drivers/virt/acrn/mm.c @@ -68,7 +68,7 @@ int acrn_mm_region_add(struct acrn_vm *vm, u64 user_gpa, u64 service_gpa, ret = modify_region(vm, region); dev_dbg(acrn_dev.this_device, - "%s: user-GPA[%pK] service-GPA[%pK] size[0x%llx].\n", + "%s: user-GPA[%p] service-GPA[%p] size[0x%llx].\n", __func__, (void *)user_gpa, (void *)service_gpa, size); kfree(region); return ret; @@ -99,7 +99,7 @@ int acrn_mm_region_del(struct acrn_vm *vm, u64 user_gpa, u64 size) ret = modify_region(vm, region); - dev_dbg(acrn_dev.this_device, "%s: user-GPA[%pK] size[0x%llx].\n", + dev_dbg(acrn_dev.this_device, "%s: user-GPA[%p] size[0x%llx].\n", __func__, (void *)user_gpa, size); kfree(region); return ret; @@ -224,7 +224,7 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap) if (ret) { dev_dbg(acrn_dev.this_device, - "Failed to lookup PFN at VMA:%pK.\n", (void *)memmap->vma_base); + "Failed to lookup PFN at VMA:%p.\n", (void *)memmap->vma_base); return ret; } @@ -326,7 +326,7 @@ int acrn_vm_ram_map(struct acrn_vm *vm, struct acrn_vm_memmap *memmap) kfree(regions_info); dev_dbg(acrn_dev.this_device, - "%s: VM[%u] service-GVA[%pK] user-GPA[%pK] size[0x%llx]\n", + "%s: VM[%u] service-GVA[%p] user-GPA[%p] size[0x%llx]\n", __func__, vm->vmid, remap_vaddr, (void *)memmap->user_vm_pa, memmap->len); return ret; diff --git a/include/dt-bindings/iio/adc/adi,ad7768-1.h b/include/dt-bindings/iio/adc/adi,ad7768-1.h new file mode 100644 index 000000000000..34d92856a50b --- /dev/null +++ b/include/dt-bindings/iio/adc/adi,ad7768-1.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ + +#ifndef _DT_BINDINGS_ADI_AD7768_1_H +#define _DT_BINDINGS_ADI_AD7768_1_H + +#define AD7768_TRIGGER_SOURCE_SYNC_OUT 0 +#define AD7768_TRIGGER_SOURCE_GPIO3 1 +#define AD7768_TRIGGER_SOURCE_DRDY 2 + +#endif /* _DT_BINDINGS_ADI_AD7768_1_H */ diff --git a/include/dt-bindings/iio/adc/mediatek,mt6363-auxadc.h b/include/dt-bindings/iio/adc/mediatek,mt6363-auxadc.h new file mode 100644 index 000000000000..92d135477d0e --- /dev/null +++ b/include/dt-bindings/iio/adc/mediatek,mt6363-auxadc.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ + +#ifndef _DT_BINDINGS_MEDIATEK_MT6363_AUXADC_H +#define _DT_BINDINGS_MEDIATEK_MT6363_AUXADC_H + +/* ADC Channel Index */ +#define MT6363_AUXADC_BATADC 0 +#define MT6363_AUXADC_VCDT 1 +#define MT6363_AUXADC_BAT_TEMP 2 +#define MT6363_AUXADC_CHIP_TEMP 3 +#define MT6363_AUXADC_VSYSSNS 4 +#define MT6363_AUXADC_VTREF 5 +#define MT6363_AUXADC_VCORE_TEMP 6 +#define MT6363_AUXADC_VPROC_TEMP 7 +#define MT6363_AUXADC_VGPU_TEMP 8 +#define MT6363_AUXADC_VIN1 9 +#define MT6363_AUXADC_VIN2 10 +#define MT6363_AUXADC_VIN3 11 +#define MT6363_AUXADC_VIN4 12 +#define MT6363_AUXADC_VIN5 13 +#define MT6363_AUXADC_VIN6 14 +#define MT6363_AUXADC_VIN7 15 + +#endif diff --git a/include/dt-bindings/iio/adc/mediatek,mt6373-auxadc.h b/include/dt-bindings/iio/adc/mediatek,mt6373-auxadc.h new file mode 100644 index 000000000000..17cab86d355e --- /dev/null +++ b/include/dt-bindings/iio/adc/mediatek,mt6373-auxadc.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ + +#ifndef _DT_BINDINGS_MEDIATEK_MT6373_AUXADC_H +#define _DT_BINDINGS_MEDIATEK_MT6373_AUXADC_H + +/* ADC Channel Index */ +#define MT6373_AUXADC_CHIP_TEMP 0 +#define MT6373_AUXADC_VCORE_TEMP 1 +#define MT6373_AUXADC_VPROC_TEMP 2 +#define MT6373_AUXADC_VGPU_TEMP 3 +#define MT6373_AUXADC_VIN1 4 +#define MT6373_AUXADC_VIN2 5 +#define MT6373_AUXADC_VIN3 6 +#define MT6373_AUXADC_VIN4 7 +#define MT6373_AUXADC_VIN5 8 +#define MT6373_AUXADC_VIN6 9 +#define MT6373_AUXADC_VIN7 10 + +#endif diff --git a/include/dt-bindings/interconnect/qcom,milos-rpmh.h b/include/dt-bindings/interconnect/qcom,milos-rpmh.h new file mode 100644 index 000000000000..9326d7d9c2a3 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,milos-rpmh.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2025, Luca Weiss <luca.weiss@fairphone.com> + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MILOS_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MILOS_H + +#define MASTER_QUP_1 0 +#define MASTER_UFS_MEM 1 +#define MASTER_USB3_0 2 +#define SLAVE_A1NOC_SNOC 3 + +#define MASTER_QDSS_BAM 0 +#define MASTER_QSPI_0 1 +#define MASTER_QUP_0 2 +#define MASTER_CRYPTO 3 +#define MASTER_IPA 4 +#define MASTER_QDSS_ETR 5 +#define MASTER_QDSS_ETR_1 6 +#define MASTER_SDCC_1 7 +#define MASTER_SDCC_2 8 +#define SLAVE_A2NOC_SNOC 9 + +#define MASTER_QUP_CORE_0 0 +#define MASTER_QUP_CORE_1 1 +#define SLAVE_QUP_CORE_0 2 +#define SLAVE_QUP_CORE_1 3 + +#define MASTER_CNOC_CFG 0 +#define SLAVE_AHB2PHY_SOUTH 1 +#define SLAVE_AHB2PHY_NORTH 2 +#define SLAVE_CAMERA_CFG 3 +#define SLAVE_CLK_CTL 4 +#define SLAVE_RBCPR_CX_CFG 5 +#define SLAVE_RBCPR_MXA_CFG 6 +#define SLAVE_CRYPTO_0_CFG 7 +#define SLAVE_CX_RDPM 8 +#define SLAVE_GFX3D_CFG 9 +#define SLAVE_IMEM_CFG 10 +#define SLAVE_CNOC_MSS 11 +#define SLAVE_MX_2_RDPM 12 +#define SLAVE_MX_RDPM 13 +#define SLAVE_PDM 14 +#define SLAVE_QDSS_CFG 15 +#define SLAVE_QSPI_0 16 +#define SLAVE_QUP_0 17 +#define SLAVE_QUP_1 18 +#define SLAVE_SDC1 19 +#define SLAVE_SDCC_2 20 +#define SLAVE_TCSR 21 +#define SLAVE_TLMM 22 +#define SLAVE_UFS_MEM_CFG 23 +#define SLAVE_USB3_0 24 +#define SLAVE_VENUS_CFG 25 +#define SLAVE_VSENSE_CTRL_CFG 26 +#define SLAVE_WLAN 27 +#define SLAVE_CNOC_MNOC_HF_CFG 28 +#define SLAVE_CNOC_MNOC_SF_CFG 29 +#define SLAVE_NSP_QTB_CFG 30 +#define SLAVE_PCIE_ANOC_CFG 31 +#define SLAVE_WLAN_Q6_THROTTLE_CFG 32 +#define SLAVE_SERVICE_CNOC_CFG 33 +#define SLAVE_QDSS_STM 34 +#define SLAVE_TCU 35 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AOSS 2 +#define SLAVE_DISPLAY_CFG 3 +#define SLAVE_IPA_CFG 4 +#define SLAVE_IPC_ROUTER_CFG 5 +#define SLAVE_PCIE_0_CFG 6 +#define SLAVE_PCIE_1_CFG 7 +#define SLAVE_PRNG 8 +#define SLAVE_TME_CFG 9 +#define SLAVE_APPSS 10 +#define SLAVE_CNOC_CFG 11 +#define SLAVE_DDRSS_CFG 12 +#define SLAVE_IMEM 13 +#define SLAVE_PIMEM 14 +#define SLAVE_SERVICE_CNOC 15 +#define SLAVE_PCIE_0 16 +#define SLAVE_PCIE_1 17 + +#define MASTER_GPU_TCU 0 +#define MASTER_SYS_TCU 1 +#define MASTER_APPSS_PROC 2 +#define MASTER_GFX3D 3 +#define MASTER_LPASS_GEM_NOC 4 +#define MASTER_MSS_PROC 5 +#define MASTER_MNOC_HF_MEM_NOC 6 +#define MASTER_MNOC_SF_MEM_NOC 7 +#define MASTER_COMPUTE_NOC 8 +#define MASTER_ANOC_PCIE_GEM_NOC 9 +#define MASTER_SNOC_GC_MEM_NOC 10 +#define MASTER_SNOC_SF_MEM_NOC 11 +#define MASTER_WLAN_Q6 12 +#define SLAVE_GEM_NOC_CNOC 13 +#define SLAVE_LLCC 14 +#define SLAVE_MEM_NOC_PCIE_SNOC 15 + +#define MASTER_LPASS_PROC 0 +#define SLAVE_LPASS_GEM_NOC 1 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_ICP 1 +#define MASTER_CAMNOC_SF 2 +#define MASTER_MDP 3 +#define MASTER_VIDEO 4 +#define MASTER_CNOC_MNOC_HF_CFG 5 +#define MASTER_CNOC_MNOC_SF_CFG 6 +#define SLAVE_MNOC_HF_MEM_NOC 7 +#define SLAVE_MNOC_SF_MEM_NOC 8 +#define SLAVE_SERVICE_MNOC_HF 9 +#define SLAVE_SERVICE_MNOC_SF 10 + +#define MASTER_CDSP_PROC 0 +#define SLAVE_CDSP_MEM_NOC 1 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_A1NOC_SNOC 0 +#define MASTER_A2NOC_SNOC 1 +#define MASTER_APSS_NOC 2 +#define MASTER_CNOC_SNOC 3 +#define MASTER_PIMEM 4 +#define MASTER_GIC 5 +#define SLAVE_SNOC_GEM_NOC_GC 6 +#define SLAVE_SNOC_GEM_NOC_SF 7 + + +#endif diff --git a/include/kunit/test.h b/include/kunit/test.h index 39c768f87dc9..d958ee53050e 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -532,6 +532,18 @@ static inline char *kunit_kstrdup(struct kunit *test, const char *str, gfp_t gfp const char *kunit_kstrdup_const(struct kunit *test, const char *str, gfp_t gfp); /** + * kunit_attach_mm() - Create and attach a new mm if it doesn't already exist. + * + * Allocates a &struct mm_struct and attaches it to @current. In most cases, call + * kunit_vm_mmap() without calling kunit_attach_mm() directly. Only necessary when + * code under test accesses the mm before executing the mmap (e.g., to perform + * additional initialization beforehand). + * + * Return: 0 on success, -errno on failure. + */ +int kunit_attach_mm(void); + +/** * kunit_vm_mmap() - Allocate KUnit-tracked vm_mmap() area * @test: The test context object. * @file: struct file pointer to map from, if any diff --git a/include/linux/fsi.h b/include/linux/fsi.h index 8c5eef808788..adea1b432f2d 100644 --- a/include/linux/fsi.h +++ b/include/linux/fsi.h @@ -68,7 +68,7 @@ extern int fsi_slave_read(struct fsi_slave *slave, uint32_t addr, extern int fsi_slave_write(struct fsi_slave *slave, uint32_t addr, const void *val, size_t size); -extern struct bus_type fsi_bus_type; +extern const struct bus_type fsi_bus_type; extern const struct device_type fsi_cdev_type; enum fsi_dev_type { diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index f242b285081b..6e70a412e218 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -31,6 +31,8 @@ struct ad_sigma_delta; struct device; struct gpio_desc; struct iio_dev; +struct spi_offload; +struct spi_offload_trigger; /** * struct ad_sigma_delta_info - Sigma Delta driver specific callbacks and options @@ -47,6 +49,10 @@ struct iio_dev; * @has_registers: true if the device has writable and readable registers, false * if there is just one read-only sample data shift register. * @has_named_irqs: Set to true if there is more than one IRQ line. + * @supports_spi_offload: Set to true if the driver supports SPI offload. Often + * special considerations are needed for scan_type and other channel + * info, so individual drivers have to set this to let the core + * code know that it can use SPI offload if it is available. * @addr_shift: Shift of the register address in the communications register. * @read_mask: Mask for the communications register having the read bit set. * @status_ch_mask: Mask for the channel number stored in status register. @@ -65,6 +71,7 @@ struct ad_sigma_delta_info { int (*postprocess_sample)(struct ad_sigma_delta *, unsigned int raw_sample); bool has_registers; bool has_named_irqs; + bool supports_spi_offload; unsigned int addr_shift; unsigned int read_mask; unsigned int status_ch_mask; @@ -94,7 +101,7 @@ struct ad_sigma_delta { bool bus_locked; bool keep_cs_asserted; - uint8_t comm; + u8 comm; const struct ad_sigma_delta_info *info; unsigned int active_slots; @@ -105,7 +112,11 @@ struct ad_sigma_delta { bool status_appended; /* map slots to channels in order to know what to expect from devices */ unsigned int *slots; - uint8_t *samples_buf; + struct spi_message sample_msg; + struct spi_transfer sample_xfer[2]; + u8 *samples_buf; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; /* * DMA (thus cache coherency maintenance) requires the @@ -114,10 +125,16 @@ struct ad_sigma_delta { * 'rx_buf' is up to 32 bits per sample + 64 bit timestamp, * rounded to 16 bytes to take into account padding. */ - uint8_t tx_buf[4] __aligned(IIO_DMA_MINALIGN); - uint8_t rx_buf[16] __aligned(8); + u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[16] __aligned(8); + u8 sample_addr; }; +static inline bool ad_sigma_delta_has_spi_offload(struct ad_sigma_delta *sd) +{ + return sd->offload != NULL; +} + static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd, unsigned int channel) { @@ -177,7 +194,7 @@ static inline int ad_sigma_delta_postprocess_sample(struct ad_sigma_delta *sd, return 0; } -void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm); +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm); int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, unsigned int val); int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index e59d909cb659..7f815f3fed6a 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -76,6 +76,14 @@ enum iio_backend_interface_type { IIO_BACKEND_INTERFACE_MAX }; +enum iio_backend_filter_type { + IIO_BACKEND_FILTER_TYPE_DISABLED, + IIO_BACKEND_FILTER_TYPE_SINC1, + IIO_BACKEND_FILTER_TYPE_SINC5, + IIO_BACKEND_FILTER_TYPE_SINC5_PLUS_COMP, + IIO_BACKEND_FILTER_TYPE_MAX +}; + /** * struct iio_backend_ops - operations structure for an iio_backend * @enable: Enable backend. @@ -101,6 +109,9 @@ enum iio_backend_interface_type { * @read_raw: Read a channel attribute from a backend device * @debugfs_print_chan_status: Print channel status into a buffer. * @debugfs_reg_access: Read or write register value of backend. + * @filter_type_set: Set filter type. + * @interface_data_align: Perform the data alignment process. + * @num_lanes_set: Set the number of lanes enabled. * @ddr_enable: Enable interface DDR (Double Data Rate) mode. * @ddr_disable: Disable interface DDR (Double Data Rate) mode. * @data_stream_enable: Enable data stream. @@ -144,7 +155,7 @@ struct iio_backend_ops { enum iio_backend_interface_type *type); int (*data_size_set)(struct iio_backend *back, unsigned int size); int (*oversampling_ratio_set)(struct iio_backend *back, - unsigned int ratio); + unsigned int chan, unsigned int ratio); int (*read_raw)(struct iio_backend *back, struct iio_chan_spec const *chan, int *val, int *val2, long mask); @@ -153,6 +164,10 @@ struct iio_backend_ops { size_t len); int (*debugfs_reg_access)(struct iio_backend *back, unsigned int reg, unsigned int writeval, unsigned int *readval); + int (*filter_type_set)(struct iio_backend *back, + enum iio_backend_filter_type type); + int (*interface_data_align)(struct iio_backend *back, u32 timeout_us); + int (*num_lanes_set)(struct iio_backend *back, unsigned int num_lanes); int (*ddr_enable)(struct iio_backend *back); int (*ddr_disable)(struct iio_backend *back); int (*data_stream_enable)(struct iio_backend *back); @@ -195,6 +210,10 @@ int iio_backend_data_sample_trigger(struct iio_backend *back, int devm_iio_backend_request_buffer(struct device *dev, struct iio_backend *back, struct iio_dev *indio_dev); +int iio_backend_filter_type_set(struct iio_backend *back, + enum iio_backend_filter_type type); +int iio_backend_interface_data_align(struct iio_backend *back, u32 timeout_us); +int iio_backend_num_lanes_set(struct iio_backend *back, unsigned int num_lanes); int iio_backend_ddr_enable(struct iio_backend *back); int iio_backend_ddr_disable(struct iio_backend *back); int iio_backend_data_stream_enable(struct iio_backend *back); @@ -209,6 +228,7 @@ int iio_backend_interface_type_get(struct iio_backend *back, enum iio_backend_interface_type *type); int iio_backend_data_size_set(struct iio_backend *back, unsigned int size); int iio_backend_oversampling_ratio_set(struct iio_backend *back, + unsigned int chan, unsigned int ratio); int iio_backend_read_raw(struct iio_backend *back, struct iio_chan_spec const *chan, int *val, int *val2, diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h index e72167b96d27..bb966abcde53 100644 --- a/include/linux/iio/common/cros_ec_sensors_core.h +++ b/include/linux/iio/common/cros_ec_sensors_core.h @@ -126,5 +126,6 @@ extern const struct dev_pm_ops cros_ec_sensors_pm_ops; /* List of extended channel specification for all sensors. */ extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; +extern const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[]; #endif /* __CROS_EC_SENSORS_CORE_H */ diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index d89982c98368..ad2761efcc83 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -69,6 +69,7 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_CALIBAMBIENT, IIO_CHAN_INFO_ZEROPOINT, IIO_CHAN_INFO_TROUGH, + IIO_CHAN_INFO_CONVDELAY, }; #endif /* _IIO_TYPES_H_ */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 69e110c2b86a..3e6deb00fc85 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -73,9 +73,6 @@ #define RFKILL_MINOR 242 #define MISC_DYNAMIC_MINOR 255 -struct device; -struct attribute_group; - struct miscdevice { int minor; const char *name; diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 1f4e4f2b89bb..c19b404e3d8d 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2388,6 +2388,12 @@ enum motionsense_command { */ MOTIONSENSE_CMD_SENSOR_SCALE = 18, + /* + * Activity management + * Retrieve current status of given activity. + */ + MOTIONSENSE_CMD_GET_ACTIVITY = 20, + /* Number of motionsense sub-commands. */ MOTIONSENSE_NUM_CMDS }; @@ -2447,6 +2453,11 @@ enum motionsensor_orientation { MOTIONSENSE_ORIENTATION_UNKNOWN = 4, }; +struct ec_response_activity_data { + uint8_t activity; /* motionsensor_activity */ + uint8_t state; +} __ec_todo_packed; + struct ec_response_motion_sensor_data { /* Flags for each sensor. */ uint8_t flags; @@ -2460,8 +2471,7 @@ struct ec_response_motion_sensor_data { uint32_t timestamp; }; struct __ec_todo_unpacked { - uint8_t activity; /* motionsensor_activity */ - uint8_t state; + struct ec_response_activity_data activity_data; int16_t add_info[2]; }; }; @@ -2494,6 +2504,7 @@ enum motionsensor_activity { MOTIONSENSE_ACTIVITY_SIG_MOTION = 1, MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2, MOTIONSENSE_ACTIVITY_ORIENTATION = 3, + MOTIONSENSE_ACTIVITY_BODY_DETECTION = 4, }; struct ec_motion_sense_activity { @@ -2671,6 +2682,7 @@ struct ec_params_motion_sense { uint32_t max_data_vector; } fifo_read; + /* Used for MOTIONSENSE_CMD_SET_ACTIVITY */ struct ec_motion_sense_activity set_activity; /* Used for MOTIONSENSE_CMD_LID_ANGLE */ @@ -2716,6 +2728,12 @@ struct ec_params_motion_sense { */ int16_t hys_degree; } tablet_mode_threshold; + + /* Used for MOTIONSENSE_CMD_GET_ACTIVITY */ + struct __ec_todo_unpacked { + uint8_t sensor_num; + uint8_t activity; /* enum motionsensor_activity */ + } get_activity; }; } __ec_todo_packed; @@ -2833,6 +2851,10 @@ struct ec_response_motion_sense { uint16_t hys_degree; } tablet_mode_threshold; + /* USED for MOTIONSENSE_CMD_GET_ACTIVITY. */ + struct __ec_todo_unpacked { + uint8_t state; + } get_activity; }; } __ec_todo_packed; diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index c7abce28ed29..aab0aebb529e 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -52,6 +52,7 @@ struct pps_device { int current_mode; /* PPS mode at event time */ unsigned int last_ev; /* last PPS event id */ + unsigned int last_fetched_ev; /* last fetched PPS event id */ wait_queue_head_t queue; /* PPS event queue */ unsigned int id; /* PPS source unique ID */ diff --git a/include/linux/vmw_vmci_api.h b/include/linux/vmw_vmci_api.h index f28907345c80..41764a684423 100644 --- a/include/linux/vmw_vmci_api.h +++ b/include/linux/vmw_vmci_api.h @@ -35,7 +35,6 @@ int vmci_doorbell_create(struct vmci_handle *handle, u32 flags, u32 priv_flags, vmci_callback notify_cb, void *client_data); int vmci_doorbell_destroy(struct vmci_handle handle); -int vmci_doorbell_notify(struct vmci_handle handle, u32 priv_flags); u32 vmci_get_context_id(void); bool vmci_is_context_owner(u32 context_id, kuid_t uid); int vmci_register_vsock_callback(vmci_vsock_cb callback); @@ -61,12 +60,6 @@ s64 vmci_qpair_produce_free_space(const struct vmci_qp *qpair); s64 vmci_qpair_produce_buf_ready(const struct vmci_qp *qpair); s64 vmci_qpair_consume_free_space(const struct vmci_qp *qpair); s64 vmci_qpair_consume_buf_ready(const struct vmci_qp *qpair); -ssize_t vmci_qpair_enqueue(struct vmci_qp *qpair, - const void *buf, size_t buf_size, int mode); -ssize_t vmci_qpair_dequeue(struct vmci_qp *qpair, - void *buf, size_t buf_size, int mode); -ssize_t vmci_qpair_peek(struct vmci_qp *qpair, void *buf, size_t buf_size, - int mode); ssize_t vmci_qpair_enquev(struct vmci_qp *qpair, struct msghdr *msg, size_t iov_size, int mode); ssize_t vmci_qpair_dequev(struct vmci_qp *qpair, diff --git a/init/Kconfig b/init/Kconfig index 80bd36d31007..173578013e59 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1881,13 +1881,6 @@ config CACHESTAT_SYSCALL If unsure say Y here. -config PC104 - bool "PC/104 support" if EXPERT - help - Expose PC/104 form factor device drivers and options available for - selection and configuration. Enable this option if your target - machine has a PC/104 bus. - config KALLSYMS bool "Load all symbols for debugging/ksymoops" if EXPERT default y diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8d969b250b18..c9eec419b40c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2515,8 +2515,8 @@ config TEST_IDA tristate "Perform selftest on IDA functions" config TEST_MISC_MINOR - tristate "miscdevice KUnit test" if !KUNIT_ALL_TESTS - depends on KUNIT + bool "miscdevice KUnit test" if !KUNIT_ALL_TESTS + depends on KUNIT=y default KUNIT_ALL_TESTS help Kunit test for miscdevice API, specially its behavior in respect to diff --git a/lib/kunit/user_alloc.c b/lib/kunit/user_alloc.c index 46951be018be..b8cac765e620 100644 --- a/lib/kunit/user_alloc.c +++ b/lib/kunit/user_alloc.c @@ -22,8 +22,7 @@ struct kunit_vm_mmap_params { unsigned long offset; }; -/* Create and attach a new mm if it doesn't already exist. */ -static int kunit_attach_mm(void) +int kunit_attach_mm(void) { struct mm_struct *mm; @@ -49,6 +48,7 @@ static int kunit_attach_mm(void) return 0; } +EXPORT_SYMBOL_GPL(kunit_attach_mm); static int kunit_vm_mmap_init(struct kunit_resource *res, void *context) { diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 939278bc7b03..f5b9a6130a7a 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -17,7 +17,6 @@ use crate::{ mm::virt::VmaNew, prelude::*, seq_file::SeqFile, - str::CStr, types::{ForeignOwnable, Opaque}, }; use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin}; diff --git a/samples/mei/mei-amt-version.c b/samples/mei/mei-amt-version.c index 867debd3b912..1d7254bcb44c 100644 --- a/samples/mei/mei-amt-version.c +++ b/samples/mei/mei-amt-version.c @@ -69,11 +69,11 @@ #include <string.h> #include <fcntl.h> #include <sys/ioctl.h> +#include <sys/time.h> #include <unistd.h> #include <errno.h> #include <stdint.h> #include <stdbool.h> -#include <bits/wordsize.h> #include <linux/mei.h> /***************************************************************************** |